aboutsummaryrefslogtreecommitdiff
path: root/sys/arm
diff options
context:
space:
mode:
Diffstat (limited to 'sys/arm')
-rw-r--r--sys/arm/allwinner/a10/a10_intc.c390
-rw-r--r--sys/arm/allwinner/a10/a10_padconf.c230
-rw-r--r--sys/arm/allwinner/a10/files.a105
-rw-r--r--sys/arm/allwinner/a10_ahci.c426
-rw-r--r--sys/arm/allwinner/a10_codec.c1211
-rw-r--r--sys/arm/allwinner/a10_dmac.c473
-rw-r--r--sys/arm/allwinner/a10_dmac.h158
-rw-r--r--sys/arm/allwinner/a10_fb.c662
-rw-r--r--sys/arm/allwinner/a10_hdmi.c725
-rw-r--r--sys/arm/allwinner/a10_hdmiaudio.c435
-rw-r--r--sys/arm/allwinner/a10_sramc.c152
-rw-r--r--sys/arm/allwinner/a10_sramc.h37
-rw-r--r--sys/arm/allwinner/a10_timer.c488
-rw-r--r--sys/arm/allwinner/a13/a13_padconf.c128
-rw-r--r--sys/arm/allwinner/a13/files.a134
-rw-r--r--sys/arm/allwinner/a20/a20_cpu_cfg.c138
-rw-r--r--sys/arm/allwinner/a20/a20_cpu_cfg.h69
-rw-r--r--sys/arm/allwinner/a20/a20_padconf.c230
-rw-r--r--sys/arm/allwinner/a20/files.a204
-rw-r--r--sys/arm/allwinner/a31/a31_padconf.c219
-rw-r--r--sys/arm/allwinner/a31/a31_r_padconf.c65
-rw-r--r--sys/arm/allwinner/a31/a31s_padconf.c199
-rw-r--r--sys/arm/allwinner/a31/files.a316
-rw-r--r--sys/arm/allwinner/a31_dmac.c559
-rw-r--r--sys/arm/allwinner/a33/a33_padconf.c148
-rw-r--r--sys/arm/allwinner/a33/files.a333
-rw-r--r--sys/arm/allwinner/a64/a64_padconf.c159
-rw-r--r--sys/arm/allwinner/a64/a64_r_padconf.c63
-rw-r--r--sys/arm/allwinner/a83t/a83t_padconf.c161
-rw-r--r--sys/arm/allwinner/a83t/a83t_r_padconf.c61
-rw-r--r--sys/arm/allwinner/a83t/files.a83t6
-rw-r--r--sys/arm/allwinner/allwinner_pinctrl.h48
-rw-r--r--sys/arm/allwinner/aw_ccu.c254
-rw-r--r--sys/arm/allwinner/aw_cir.c555
-rw-r--r--sys/arm/allwinner/aw_dwc3.c146
-rw-r--r--sys/arm/allwinner/aw_gmacclk.c279
-rw-r--r--sys/arm/allwinner/aw_gpio.c1487
-rw-r--r--sys/arm/allwinner/aw_if_dwc.c145
-rw-r--r--sys/arm/allwinner/aw_machdep.c304
-rw-r--r--sys/arm/allwinner/aw_machdep.h51
-rw-r--r--sys/arm/allwinner/aw_mmc.c1593
-rw-r--r--sys/arm/allwinner/aw_mmc.h228
-rw-r--r--sys/arm/allwinner/aw_mp.c288
-rw-r--r--sys/arm/allwinner/aw_mp.h34
-rw-r--r--sys/arm/allwinner/aw_nmi.c420
-rw-r--r--sys/arm/allwinner/aw_pwm.c405
-rw-r--r--sys/arm/allwinner/aw_reset.c165
-rw-r--r--sys/arm/allwinner/aw_rsb.c501
-rw-r--r--sys/arm/allwinner/aw_rtc.c368
-rw-r--r--sys/arm/allwinner/aw_sid.c415
-rw-r--r--sys/arm/allwinner/aw_sid.h39
-rw-r--r--sys/arm/allwinner/aw_spi.c607
-rw-r--r--sys/arm/allwinner/aw_syscon.c86
-rw-r--r--sys/arm/allwinner/aw_thermal.c732
-rw-r--r--sys/arm/allwinner/aw_ts.c229
-rw-r--r--sys/arm/allwinner/aw_usb3phy.c299
-rw-r--r--sys/arm/allwinner/aw_usbphy.c530
-rw-r--r--sys/arm/allwinner/aw_wdog.c273
-rw-r--r--sys/arm/allwinner/aw_wdog.h34
-rw-r--r--sys/arm/allwinner/axp209.c1424
-rw-r--r--sys/arm/allwinner/axp209reg.h246
-rw-r--r--sys/arm/allwinner/axp81x.c1650
-rw-r--r--sys/arm/allwinner/clkng/aw_ccung.c364
-rw-r--r--sys/arm/allwinner/clkng/aw_ccung.h110
-rw-r--r--sys/arm/allwinner/clkng/aw_clk.h604
-rw-r--r--sys/arm/allwinner/clkng/aw_clk_frac.c399
-rw-r--r--sys/arm/allwinner/clkng/aw_clk_frac.h55
-rw-r--r--sys/arm/allwinner/clkng/aw_clk_m.c290
-rw-r--r--sys/arm/allwinner/clkng/aw_clk_m.h48
-rw-r--r--sys/arm/allwinner/clkng/aw_clk_mipi.c297
-rw-r--r--sys/arm/allwinner/clkng/aw_clk_mipi.h53
-rw-r--r--sys/arm/allwinner/clkng/aw_clk_nkmp.c414
-rw-r--r--sys/arm/allwinner/clkng/aw_clk_nkmp.h56
-rw-r--r--sys/arm/allwinner/clkng/aw_clk_nm.c357
-rw-r--r--sys/arm/allwinner/clkng/aw_clk_nm.h52
-rw-r--r--sys/arm/allwinner/clkng/aw_clk_nmm.c285
-rw-r--r--sys/arm/allwinner/clkng/aw_clk_nmm.h52
-rw-r--r--sys/arm/allwinner/clkng/aw_clk_np.c267
-rw-r--r--sys/arm/allwinner/clkng/aw_clk_np.h51
-rw-r--r--sys/arm/allwinner/clkng/aw_clk_prediv_mux.c183
-rw-r--r--sys/arm/allwinner/clkng/aw_clk_prediv_mux.h48
-rw-r--r--sys/arm/allwinner/clkng/ccu_a10.c625
-rw-r--r--sys/arm/allwinner/clkng/ccu_a13.c570
-rw-r--r--sys/arm/allwinner/clkng/ccu_a31.c980
-rw-r--r--sys/arm/allwinner/clkng/ccu_a64.c843
-rw-r--r--sys/arm/allwinner/clkng/ccu_a83t.c788
-rw-r--r--sys/arm/allwinner/clkng/ccu_de2.c240
-rw-r--r--sys/arm/allwinner/clkng/ccu_h3.c794
-rw-r--r--sys/arm/allwinner/clkng/ccu_h6.c501
-rw-r--r--sys/arm/allwinner/clkng/ccu_h6_r.c172
-rw-r--r--sys/arm/allwinner/clkng/ccu_sun8i_r.c265
-rw-r--r--sys/arm/allwinner/files.allwinner47
-rw-r--r--sys/arm/allwinner/files.allwinner_up3
-rw-r--r--sys/arm/allwinner/h3/files.h36
-rw-r--r--sys/arm/allwinner/h3/h3_padconf.c150
-rw-r--r--sys/arm/allwinner/h3/h3_r_padconf.c63
-rw-r--r--sys/arm/allwinner/h6/h6_padconf.c126
-rw-r--r--sys/arm/allwinner/h6/h6_r_padconf.c60
-rw-r--r--sys/arm/allwinner/if_awg.c2025
-rw-r--r--sys/arm/allwinner/if_awgreg.h181
-rw-r--r--sys/arm/allwinner/if_emac.c1203
-rw-r--r--sys/arm/allwinner/if_emacreg.h244
-rw-r--r--sys/arm/allwinner/std.allwinner12
-rw-r--r--sys/arm/allwinner/std.allwinner_up11
-rw-r--r--sys/arm/allwinner/sunxi_dma_if.m98
-rw-r--r--sys/arm/altera/socfpga/files.socfpga20
-rw-r--r--sys/arm/altera/socfpga/socfpga_a10_manager.c443
-rw-r--r--sys/arm/altera/socfpga/socfpga_common.c44
-rw-r--r--sys/arm/altera/socfpga/socfpga_common.h38
-rw-r--r--sys/arm/altera/socfpga/socfpga_l3regs.h54
-rw-r--r--sys/arm/altera/socfpga/socfpga_machdep.c176
-rw-r--r--sys/arm/altera/socfpga/socfpga_manager.c431
-rw-r--r--sys/arm/altera/socfpga/socfpga_mp.c232
-rw-r--r--sys/arm/altera/socfpga/socfpga_mp.h35
-rw-r--r--sys/arm/altera/socfpga/socfpga_rstmgr.c260
-rw-r--r--sys/arm/altera/socfpga/socfpga_rstmgr.h50
-rw-r--r--sys/arm/altera/socfpga/std.socfpga7
-rw-r--r--sys/arm/annapurna/alpine/alpine_ccu.c130
-rw-r--r--sys/arm/annapurna/alpine/alpine_machdep.c162
-rw-r--r--sys/arm/annapurna/alpine/alpine_machdep_mp.c252
-rw-r--r--sys/arm/annapurna/alpine/alpine_mp.h35
-rw-r--r--sys/arm/annapurna/alpine/alpine_nb_service.c128
-rw-r--r--sys/arm/annapurna/alpine/alpine_pci.c159
-rw-r--r--sys/arm/annapurna/alpine/alpine_pci_msix.c394
-rw-r--r--sys/arm/annapurna/alpine/alpine_serdes.c225
-rw-r--r--sys/arm/annapurna/alpine/alpine_serdes.h61
-rw-r--r--sys/arm/annapurna/alpine/files.alpine7
-rw-r--r--sys/arm/annapurna/alpine/std.alpine13
-rw-r--r--sys/arm/arm/autoconf.c101
-rw-r--r--sys/arm/arm/bcopy_page.S142
-rw-r--r--sys/arm/arm/bcopyinout.S129
-rw-r--r--sys/arm/arm/bcopyinout_xscale.S819
-rw-r--r--sys/arm/arm/blockio.S596
-rw-r--r--sys/arm/arm/bus_space_asm_generic.S359
-rw-r--r--sys/arm/arm/bus_space_base.c158
-rw-r--r--sys/arm/arm/bus_space_generic.c134
-rw-r--r--sys/arm/arm/busdma_machdep.c1750
-rw-r--r--sys/arm/arm/copystr.S130
-rw-r--r--sys/arm/arm/cpu_asm-v6.S270
-rw-r--r--sys/arm/arm/cpufunc.c349
-rw-r--r--sys/arm/arm/cpufunc_asm.S77
-rw-r--r--sys/arm/arm/cpufunc_asm_arm11x6.S88
-rw-r--r--sys/arm/arm/cpufunc_asm_armv7.S44
-rw-r--r--sys/arm/arm/cpufunc_asm_pj4b.S98
-rw-r--r--sys/arm/arm/cpuinfo.c528
-rw-r--r--sys/arm/arm/db_disasm.c81
-rw-r--r--sys/arm/arm/db_interface.c327
-rw-r--r--sys/arm/arm/db_trace.c186
-rw-r--r--sys/arm/arm/debug_monitor.c1039
-rw-r--r--sys/arm/arm/disassem.c690
-rw-r--r--sys/arm/arm/dump_machdep.c104
-rw-r--r--sys/arm/arm/elf_machdep.c336
-rw-r--r--sys/arm/arm/elf_note.S36
-rw-r--r--sys/arm/arm/exception.S418
-rw-r--r--sys/arm/arm/fiq.c166
-rw-r--r--sys/arm/arm/fiq_subr.S94
-rw-r--r--sys/arm/arm/fusu.S304
-rw-r--r--sys/arm/arm/gdb_machdep.c130
-rw-r--r--sys/arm/arm/genassym.c144
-rw-r--r--sys/arm/arm/generic_timer.c599
-rw-r--r--sys/arm/arm/gic.c1250
-rw-r--r--sys/arm/arm/gic.h86
-rw-r--r--sys/arm/arm/gic_acpi.c360
-rw-r--r--sys/arm/arm/gic_common.h102
-rw-r--r--sys/arm/arm/gic_fdt.c363
-rw-r--r--sys/arm/arm/hypervisor-stub.S87
-rw-r--r--sys/arm/arm/identcpu-v6.c381
-rw-r--r--sys/arm/arm/in_cksum.c154
-rw-r--r--sys/arm/arm/in_cksum_arm.S282
-rw-r--r--sys/arm/arm/locore-v6.S598
-rw-r--r--sys/arm/arm/locore.S37
-rw-r--r--sys/arm/arm/machdep.c958
-rw-r--r--sys/arm/arm/machdep_boot.c499
-rw-r--r--sys/arm/arm/machdep_intr.c228
-rw-r--r--sys/arm/arm/machdep_kdb.c144
-rw-r--r--sys/arm/arm/machdep_ptrace.c323
-rw-r--r--sys/arm/arm/mem.c179
-rw-r--r--sys/arm/arm/minidump_machdep.c347
-rw-r--r--sys/arm/arm/mp_machdep.c396
-rw-r--r--sys/arm/arm/mpcore_timer.c561
-rw-r--r--sys/arm/arm/mpcore_timervar.h47
-rw-r--r--sys/arm/arm/nexus.c434
-rw-r--r--sys/arm/arm/nexusvar.h35
-rw-r--r--sys/arm/arm/ofw_machdep.c74
-rw-r--r--sys/arm/arm/pl190.c311
-rw-r--r--sys/arm/arm/pl310.c593
-rw-r--r--sys/arm/arm/platform.c244
-rw-r--r--sys/arm/arm/platform_if.m148
-rw-r--r--sys/arm/arm/platform_pl310_if.m84
-rw-r--r--sys/arm/arm/pmap-v6.c6956
-rw-r--r--sys/arm/arm/pmu.c161
-rw-r--r--sys/arm/arm/pmu.h52
-rw-r--r--sys/arm/arm/pmu_fdt.c239
-rw-r--r--sys/arm/arm/ptrace_machdep.c62
-rw-r--r--sys/arm/arm/sc_machdep.c88
-rw-r--r--sys/arm/arm/setcpsr.S82
-rw-r--r--sys/arm/arm/setstack.s95
-rw-r--r--sys/arm/arm/stack_machdep.c88
-rw-r--r--sys/arm/arm/stdatomic.c429
-rw-r--r--sys/arm/arm/support.S2122
-rw-r--r--sys/arm/arm/swtch-v6.S508
-rw-r--r--sys/arm/arm/swtch.S121
-rw-r--r--sys/arm/arm/sys_machdep.c220
-rw-r--r--sys/arm/arm/syscall.c170
-rw-r--r--sys/arm/arm/trap-v6.c678
-rw-r--r--sys/arm/arm/uio_machdep.c126
-rw-r--r--sys/arm/arm/undefined.c348
-rw-r--r--sys/arm/arm/unwind.c575
-rw-r--r--sys/arm/arm/vfp.c324
-rw-r--r--sys/arm/arm/vm_machdep.c320
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2835_audio.c968
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2835_bsc.c727
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2835_bscreg.h69
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2835_bscvar.h71
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2835_clkman.c213
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2835_clkman.h43
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2835_cpufreq.c1606
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2835_dma.c770
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2835_dma.h66
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2835_fb.c851
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2835_fbd.c287
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2835_firmware.c184
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2835_firmware.h202
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2835_ft5406.c336
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2835_gpio.c1323
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2835_intr.c453
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2835_machdep.c156
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2835_mbox.c594
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2835_mbox.h44
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2835_mbox_prop.h464
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2835_pwm.c512
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2835_rng.c467
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2835_sdhci.c869
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2835_sdhost.c1290
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2835_spi.c572
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2835_spireg.h77
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2835_spivar.h62
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2835_systimer.c317
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2835_vcbus.c280
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2835_vcbus.h81
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2835_vcio.c118
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2835_wdog.c261
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2835_wdog.h34
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2836.c745
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2836_mp.c146
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2836_mp.h37
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2838_pci.c782
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2838_xhci.c216
-rw-r--r--sys/arm/broadcom/bcm2835/bcm283x_dwc_fdt.c105
-rw-r--r--sys/arm/broadcom/bcm2835/files.bcm28353
-rw-r--r--sys/arm/broadcom/bcm2835/files.bcm28364
-rw-r--r--sys/arm/broadcom/bcm2835/files.bcm283x48
-rw-r--r--sys/arm/broadcom/bcm2835/raspberrypi_gpio.c457
-rw-r--r--sys/arm/broadcom/bcm2835/std.bcm283510
-rw-r--r--sys/arm/broadcom/bcm2835/std.bcm283610
-rw-r--r--sys/arm/broadcom/bcm2835/std.rpi3
-rw-r--r--sys/arm/broadcom/bcm2835/vc_vchi_audioserv_defs.h156
-rw-r--r--sys/arm/cloudabi32/cloudabi32_sysvec.c195
-rw-r--r--sys/arm/conf/ALPINE82
-rw-r--r--sys/arm/conf/APALIS-IMX631
-rw-r--r--sys/arm/conf/ARMADA38X104
-rw-r--r--sys/arm/conf/ARMADAXP88
-rw-r--r--sys/arm/conf/COLIBRI-VF5028
-rw-r--r--sys/arm/conf/COSMIC28
-rw-r--r--sys/arm/conf/DEFAULTS7
-rw-r--r--sys/arm/conf/EFIKA_MX135
-rw-r--r--sys/arm/conf/GENERIC301
-rw-r--r--sys/arm/conf/GENERIC-MMCCAM22
-rw-r--r--sys/arm/conf/GENERIC-NODEBUG32
-rw-r--r--sys/arm/conf/IMX53123
-rw-r--r--sys/arm/conf/IMX6139
-rw-r--r--sys/arm/conf/JETSON-TK137
-rw-r--r--sys/arm/conf/LINT4
-rw-r--r--sys/arm/conf/NOTES84
-rw-r--r--sys/arm/conf/QUARTZ28
-rw-r--r--sys/arm/conf/RPI-B101
-rw-r--r--sys/arm/conf/SOCDK30
-rw-r--r--sys/arm/conf/SOCFPGA100
-rw-r--r--sys/arm/conf/SOCKIT30
-rw-r--r--sys/arm/conf/SOCKIT-BERI37
-rw-r--r--sys/arm/conf/TEGRA124142
-rw-r--r--sys/arm/conf/VERSATILEPB75
-rw-r--r--sys/arm/conf/VYBRID110
-rw-r--r--sys/arm/conf/ZEDBOARD85
-rw-r--r--sys/arm/conf/std.armv686
-rw-r--r--sys/arm/conf/std.armv785
-rw-r--r--sys/arm/freescale/fsl_ocotp.c206
-rw-r--r--sys/arm/freescale/fsl_ocotpreg.h90
-rw-r--r--sys/arm/freescale/fsl_ocotpvar.h36
-rw-r--r--sys/arm/freescale/imx/files.imx555
-rw-r--r--sys/arm/freescale/imx/files.imx664
-rw-r--r--sys/arm/freescale/imx/imx51_ccm.c661
-rw-r--r--sys/arm/freescale/imx/imx51_ccmreg.h259
-rw-r--r--sys/arm/freescale/imx/imx51_ccmvar.h112
-rw-r--r--sys/arm/freescale/imx/imx51_dpllreg.h107
-rw-r--r--sys/arm/freescale/imx/imx51_ipuv3.c878
-rw-r--r--sys/arm/freescale/imx/imx51_ipuv3_fbd.c364
-rw-r--r--sys/arm/freescale/imx/imx51_ipuv3reg.h924
-rw-r--r--sys/arm/freescale/imx/imx51_machdep.c104
-rw-r--r--sys/arm/freescale/imx/imx51_sdmareg.h144
-rw-r--r--sys/arm/freescale/imx/imx51_ssireg.h182
-rw-r--r--sys/arm/freescale/imx/imx51_tzicreg.h87
-rw-r--r--sys/arm/freescale/imx/imx53_machdep.c100
-rw-r--r--sys/arm/freescale/imx/imx6_ahci.c363
-rw-r--r--sys/arm/freescale/imx/imx6_anatop.c818
-rw-r--r--sys/arm/freescale/imx/imx6_anatopreg.h193
-rw-r--r--sys/arm/freescale/imx/imx6_anatopvar.h49
-rw-r--r--sys/arm/freescale/imx/imx6_audmux.c157
-rw-r--r--sys/arm/freescale/imx/imx6_ccm.c520
-rw-r--r--sys/arm/freescale/imx/imx6_ccmreg.h165
-rw-r--r--sys/arm/freescale/imx/imx6_hdmi.c218
-rw-r--r--sys/arm/freescale/imx/imx6_ipu.c1272
-rw-r--r--sys/arm/freescale/imx/imx6_machdep.c396
-rw-r--r--sys/arm/freescale/imx/imx6_machdep.h38
-rw-r--r--sys/arm/freescale/imx/imx6_mp.c163
-rw-r--r--sys/arm/freescale/imx/imx6_pl310.c67
-rw-r--r--sys/arm/freescale/imx/imx6_sdma.c524
-rw-r--r--sys/arm/freescale/imx/imx6_sdma.h245
-rw-r--r--sys/arm/freescale/imx/imx6_snvs.c235
-rw-r--r--sys/arm/freescale/imx/imx6_src.c173
-rw-r--r--sys/arm/freescale/imx/imx6_src.h34
-rw-r--r--sys/arm/freescale/imx/imx6_ssi.c861
-rw-r--r--sys/arm/freescale/imx/imx6_usbphy.c209
-rw-r--r--sys/arm/freescale/imx/imx_ccmvar.h66
-rw-r--r--sys/arm/freescale/imx/imx_console.c176
-rw-r--r--sys/arm/freescale/imx/imx_epit.c491
-rw-r--r--sys/arm/freescale/imx/imx_gpio.c959
-rw-r--r--sys/arm/freescale/imx/imx_gpt.c425
-rw-r--r--sys/arm/freescale/imx/imx_gptreg.h103
-rw-r--r--sys/arm/freescale/imx/imx_i2c.c732
-rw-r--r--sys/arm/freescale/imx/imx_iomux.c330
-rw-r--r--sys/arm/freescale/imx/imx_iomuxreg.h61
-rw-r--r--sys/arm/freescale/imx/imx_iomuxvar.h49
-rw-r--r--sys/arm/freescale/imx/imx_machdep.c132
-rw-r--r--sys/arm/freescale/imx/imx_machdep.h71
-rw-r--r--sys/arm/freescale/imx/imx_nop_usbphy.c122
-rw-r--r--sys/arm/freescale/imx/imx_spi.c611
-rw-r--r--sys/arm/freescale/imx/imx_wdog.c264
-rw-r--r--sys/arm/freescale/imx/imx_wdogreg.h63
-rw-r--r--sys/arm/freescale/imx/std.imx516
-rw-r--r--sys/arm/freescale/imx/std.imx536
-rw-r--r--sys/arm/freescale/imx/std.imx67
-rw-r--r--sys/arm/freescale/imx/tzic.c312
-rw-r--r--sys/arm/freescale/vybrid/files.vybrid22
-rw-r--r--sys/arm/freescale/vybrid/std.vybrid7
-rw-r--r--sys/arm/freescale/vybrid/vf_adc.c243
-rw-r--r--sys/arm/freescale/vybrid/vf_adc.h30
-rw-r--r--sys/arm/freescale/vybrid/vf_anadig.c246
-rw-r--r--sys/arm/freescale/vybrid/vf_ccm.c501
-rw-r--r--sys/arm/freescale/vybrid/vf_common.h45
-rw-r--r--sys/arm/freescale/vybrid/vf_dcu4.c471
-rw-r--r--sys/arm/freescale/vybrid/vf_dmamux.c155
-rw-r--r--sys/arm/freescale/vybrid/vf_dmamux.h50
-rw-r--r--sys/arm/freescale/vybrid/vf_edma.c338
-rw-r--r--sys/arm/freescale/vybrid/vf_edma.h188
-rw-r--r--sys/arm/freescale/vybrid/vf_ehci.c428
-rw-r--r--sys/arm/freescale/vybrid/vf_gpio.c386
-rw-r--r--sys/arm/freescale/vybrid/vf_i2c.c633
-rw-r--r--sys/arm/freescale/vybrid/vf_iomuxc.c212
-rw-r--r--sys/arm/freescale/vybrid/vf_iomuxc.h167
-rw-r--r--sys/arm/freescale/vybrid/vf_machdep.c88
-rw-r--r--sys/arm/freescale/vybrid/vf_mscm.c126
-rw-r--r--sys/arm/freescale/vybrid/vf_port.c249
-rw-r--r--sys/arm/freescale/vybrid/vf_port.h45
-rw-r--r--sys/arm/freescale/vybrid/vf_sai.c804
-rw-r--r--sys/arm/freescale/vybrid/vf_spi.c293
-rw-r--r--sys/arm/freescale/vybrid/vf_src.c150
-rw-r--r--sys/arm/freescale/vybrid/vf_src.h32
-rw-r--r--sys/arm/freescale/vybrid/vf_tcon.c138
-rw-r--r--sys/arm/freescale/vybrid/vf_uart.c518
-rw-r--r--sys/arm/include/_align.h54
-rw-r--r--sys/arm/include/_bus.h47
-rw-r--r--sys/arm/include/_inttypes.h215
-rw-r--r--sys/arm/include/_limits.h89
-rw-r--r--sys/arm/include/_stdint.h160
-rw-r--r--sys/arm/include/_types.h114
-rw-r--r--sys/arm/include/acle-compat.h185
-rw-r--r--sys/arm/include/armreg.h458
-rw-r--r--sys/arm/include/asm.h261
-rw-r--r--sys/arm/include/asmacros.h59
-rw-r--r--sys/arm/include/atags.h130
-rw-r--r--sys/arm/include/atomic-v6.h1050
-rw-r--r--sys/arm/include/atomic.h104
-rw-r--r--sys/arm/include/blockio.h58
-rw-r--r--sys/arm/include/bus.h784
-rw-r--r--sys/arm/include/bus_dma.h78
-rw-r--r--sys/arm/include/clock.h34
-rw-r--r--sys/arm/include/counter.h91
-rw-r--r--sys/arm/include/cpu-v6.h685
-rw-r--r--sys/arm/include/cpu.h90
-rw-r--r--sys/arm/include/cpufunc.h259
-rw-r--r--sys/arm/include/cpuinfo.h129
-rw-r--r--sys/arm/include/db_machdep.h94
-rw-r--r--sys/arm/include/debug_monitor.h60
-rw-r--r--sys/arm/include/disassem.h56
-rw-r--r--sys/arm/include/dump.h70
-rw-r--r--sys/arm/include/efi.h12
-rw-r--r--sys/arm/include/elf.h115
-rw-r--r--sys/arm/include/endian.h142
-rw-r--r--sys/arm/include/exec.h39
-rw-r--r--sys/arm/include/fdt.h44
-rw-r--r--sys/arm/include/fiq.h73
-rw-r--r--sys/arm/include/float.h100
-rw-r--r--sys/arm/include/floatingpoint.h44
-rw-r--r--sys/arm/include/frame.h137
-rw-r--r--sys/arm/include/gdb_machdep.h73
-rw-r--r--sys/arm/include/ieee.h167
-rw-r--r--sys/arm/include/ieeefp.h53
-rw-r--r--sys/arm/include/in_cksum.h66
-rw-r--r--sys/arm/include/intr.h69
-rw-r--r--sys/arm/include/kdb.h57
-rw-r--r--sys/arm/include/limits.h46
-rw-r--r--sys/arm/include/machdep.h75
-rw-r--r--sys/arm/include/md_var.h71
-rw-r--r--sys/arm/include/memdev.h42
-rw-r--r--sys/arm/include/metadata.h59
-rw-r--r--sys/arm/include/minidump.h62
-rw-r--r--sys/arm/include/ofw_machdep.h49
-rw-r--r--sys/arm/include/param.h146
-rw-r--r--sys/arm/include/pcb.h89
-rw-r--r--sys/arm/include/pcpu.h146
-rw-r--r--sys/arm/include/pcpu_aux.h52
-rw-r--r--sys/arm/include/pl310.h191
-rw-r--r--sys/arm/include/platform.h66
-rw-r--r--sys/arm/include/platformvar.h124
-rw-r--r--sys/arm/include/pmap-v6.h194
-rw-r--r--sys/arm/include/pmap.h77
-rw-r--r--sys/arm/include/pmap_var.h494
-rw-r--r--sys/arm/include/pmc_mdep.h83
-rw-r--r--sys/arm/include/proc.h82
-rw-r--r--sys/arm/include/procctl.h4
-rw-r--r--sys/arm/include/profile.h124
-rw-r--r--sys/arm/include/psl.h84
-rw-r--r--sys/arm/include/pte-v6.h297
-rw-r--r--sys/arm/include/ptrace.h22
-rw-r--r--sys/arm/include/reg.h42
-rw-r--r--sys/arm/include/reloc.h55
-rw-r--r--sys/arm/include/resource.h49
-rw-r--r--sys/arm/include/runq.h48
-rw-r--r--sys/arm/include/sc_machdep.h73
-rw-r--r--sys/arm/include/setjmp.h107
-rw-r--r--sys/arm/include/sf_buf.h48
-rw-r--r--sys/arm/include/sigframe.h2
-rw-r--r--sys/arm/include/signal.h52
-rw-r--r--sys/arm/include/smp.h35
-rw-r--r--sys/arm/include/stack.h68
-rw-r--r--sys/arm/include/stdarg.h39
-rw-r--r--sys/arm/include/swi.h22
-rw-r--r--sys/arm/include/sysarch.h83
-rw-r--r--sys/arm/include/sysreg.h325
-rw-r--r--sys/arm/include/trap.h11
-rw-r--r--sys/arm/include/ucontext.h89
-rw-r--r--sys/arm/include/undefined.h93
-rw-r--r--sys/arm/include/utrap.h112
-rw-r--r--sys/arm/include/vdso.h39
-rw-r--r--sys/arm/include/vfp.h160
-rw-r--r--sys/arm/include/vm.h48
-rw-r--r--sys/arm/include/vmparam.h206
-rw-r--r--sys/arm/linux/Makefile7
-rw-r--r--sys/arm/linux/linux_proto.h1842
-rw-r--r--sys/arm/linux/linux_syscall.h326
-rw-r--r--sys/arm/linux/linux_syscalls.c417
-rw-r--r--sys/arm/linux/linux_sysent.c427
-rw-r--r--sys/arm/linux/linux_systrace_args.c7741
-rw-r--r--sys/arm/linux/syscalls.conf11
-rw-r--r--sys/arm/linux/syscalls.master1928
-rw-r--r--sys/arm/mv/a37x0_gpio.c342
-rw-r--r--sys/arm/mv/a37x0_iic.c484
-rw-r--r--sys/arm/mv/a37x0_iicreg.h69
-rw-r--r--sys/arm/mv/a37x0_spi.c494
-rw-r--r--sys/arm/mv/armada/thermal.c315
-rw-r--r--sys/arm/mv/armada/wdt.c378
-rw-r--r--sys/arm/mv/armada38x/armada38x.c226
-rw-r--r--sys/arm/mv/armada38x/armada38x_mp.c148
-rw-r--r--sys/arm/mv/armada38x/armada38x_pl310.c82
-rw-r--r--sys/arm/mv/armada38x/armada38x_pl310.h37
-rw-r--r--sys/arm/mv/armada38x/armada38x_rtc.c368
-rw-r--r--sys/arm/mv/armada38x/files.armada38x11
-rw-r--r--sys/arm/mv/armada38x/pmsu.c155
-rw-r--r--sys/arm/mv/armada38x/pmsu.h35
-rw-r--r--sys/arm/mv/armada38x/std.armada38x6
-rw-r--r--sys/arm/mv/armadaxp/armadaxp.c321
-rw-r--r--sys/arm/mv/armadaxp/armadaxp_mp.c183
-rw-r--r--sys/arm/mv/armadaxp/files.armadaxp8
-rw-r--r--sys/arm/mv/armadaxp/mptramp.S61
-rw-r--r--sys/arm/mv/armadaxp/std.armadaxp2
-rw-r--r--sys/arm/mv/armadaxp/std.mv78x604
-rw-r--r--sys/arm/mv/files.arm736
-rw-r--r--sys/arm/mv/gpio.c1213
-rw-r--r--sys/arm/mv/ic.c315
-rw-r--r--sys/arm/mv/mpic.c609
-rw-r--r--sys/arm/mv/mv_ap806_clock.c233
-rw-r--r--sys/arm/mv/mv_ap806_gicp.c328
-rw-r--r--sys/arm/mv/mv_ap806_sei.c413
-rw-r--r--sys/arm/mv/mv_armv7_machdep.c482
-rw-r--r--sys/arm/mv/mv_common.c2909
-rw-r--r--sys/arm/mv/mv_cp110_clock.c370
-rw-r--r--sys/arm/mv/mv_cp110_clock.h82
-rw-r--r--sys/arm/mv/mv_cp110_icu.c354
-rw-r--r--sys/arm/mv/mv_cp110_icu_bus.c78
-rw-r--r--sys/arm/mv/mv_pci.c1284
-rw-r--r--sys/arm/mv/mv_pci_ctrl.c349
-rw-r--r--sys/arm/mv/mv_spi.c408
-rw-r--r--sys/arm/mv/mv_thermal.c381
-rw-r--r--sys/arm/mv/mvebu_gpio.c862
-rw-r--r--sys/arm/mv/mvebu_pinctrl.c239
-rw-r--r--sys/arm/mv/mvreg.h439
-rw-r--r--sys/arm/mv/mvvar.h151
-rw-r--r--sys/arm/mv/mvwin.h392
-rw-r--r--sys/arm/mv/rtc.c195
-rw-r--r--sys/arm/mv/std-pj4b.mv12
-rw-r--r--sys/arm/mv/timer.c568
-rw-r--r--sys/arm/nvidia/as3722.c411
-rw-r--r--sys/arm/nvidia/as3722.h323
-rw-r--r--sys/arm/nvidia/as3722_gpio.c572
-rw-r--r--sys/arm/nvidia/as3722_regulators.c712
-rw-r--r--sys/arm/nvidia/as3722_rtc.c115
-rw-r--r--sys/arm/nvidia/drm2/hdmi.c1229
-rw-r--r--sys/arm/nvidia/drm2/hdmi.h333
-rw-r--r--sys/arm/nvidia/drm2/tegra_bo.c366
-rw-r--r--sys/arm/nvidia/drm2/tegra_dc.c1441
-rw-r--r--sys/arm/nvidia/drm2/tegra_dc_if.m57
-rw-r--r--sys/arm/nvidia/drm2/tegra_dc_reg.h398
-rw-r--r--sys/arm/nvidia/drm2/tegra_drm.h124
-rw-r--r--sys/arm/nvidia/drm2/tegra_drm_if.m68
-rw-r--r--sys/arm/nvidia/drm2/tegra_drm_subr.c177
-rw-r--r--sys/arm/nvidia/drm2/tegra_fb.c338
-rw-r--r--sys/arm/nvidia/drm2/tegra_hdmi.c1320
-rw-r--r--sys/arm/nvidia/drm2/tegra_hdmi_reg.h283
-rw-r--r--sys/arm/nvidia/drm2/tegra_host1x.c645
-rw-r--r--sys/arm/nvidia/tegra124/files.tegra12467
-rw-r--r--sys/arm/nvidia/tegra124/std.tegra1246
-rw-r--r--sys/arm/nvidia/tegra124/tegra124_car.c603
-rw-r--r--sys/arm/nvidia/tegra124/tegra124_car.h336
-rw-r--r--sys/arm/nvidia/tegra124/tegra124_clk_per.c825
-rw-r--r--sys/arm/nvidia/tegra124/tegra124_clk_pll.c1143
-rw-r--r--sys/arm/nvidia/tegra124/tegra124_clk_super.c264
-rw-r--r--sys/arm/nvidia/tegra124/tegra124_coretemp.c268
-rw-r--r--sys/arm/nvidia/tegra124/tegra124_cpufreq.c594
-rw-r--r--sys/arm/nvidia/tegra124/tegra124_machdep.c155
-rw-r--r--sys/arm/nvidia/tegra124/tegra124_mp.c126
-rw-r--r--sys/arm/nvidia/tegra124/tegra124_mp.h35
-rw-r--r--sys/arm/nvidia/tegra124/tegra124_pmc.c562
-rw-r--r--sys/arm/nvidia/tegra124/tegra124_xusbpadctl.c1206
-rw-r--r--sys/arm/nvidia/tegra_abpmisc.c193
-rw-r--r--sys/arm/nvidia/tegra_ahci.c785
-rw-r--r--sys/arm/nvidia/tegra_efuse.c538
-rw-r--r--sys/arm/nvidia/tegra_efuse.h61
-rw-r--r--sys/arm/nvidia/tegra_ehci.c320
-rw-r--r--sys/arm/nvidia/tegra_gpio.c889
-rw-r--r--sys/arm/nvidia/tegra_i2c.c802
-rw-r--r--sys/arm/nvidia/tegra_lic.c288
-rw-r--r--sys/arm/nvidia/tegra_mc.c311
-rw-r--r--sys/arm/nvidia/tegra_pcie.c1630
-rw-r--r--sys/arm/nvidia/tegra_pinmux.c796
-rw-r--r--sys/arm/nvidia/tegra_pmc.h119
-rw-r--r--sys/arm/nvidia/tegra_rtc.c301
-rw-r--r--sys/arm/nvidia/tegra_sdhci.c476
-rw-r--r--sys/arm/nvidia/tegra_soctherm.c844
-rw-r--r--sys/arm/nvidia/tegra_soctherm_if.m42
-rw-r--r--sys/arm/nvidia/tegra_uart.c252
-rw-r--r--sys/arm/nvidia/tegra_usbphy.c851
-rw-r--r--sys/arm/nvidia/tegra_xhci.c1125
-rw-r--r--sys/arm/qemu/files.qemu7
-rw-r--r--sys/arm/qemu/std.virt10
-rw-r--r--sys/arm/qemu/virt_machdep.c84
-rw-r--r--sys/arm/qemu/virt_mp.c74
-rw-r--r--sys/arm/qemu/virt_mp.h35
-rw-r--r--sys/arm/rockchip/files.rk32xx31
-rw-r--r--sys/arm/rockchip/rk32xx_machdep.c126
-rw-r--r--sys/arm/rockchip/rk32xx_mp.c174
-rw-r--r--sys/arm/rockchip/rk32xx_mp.h36
-rw-r--r--sys/arm/rockchip/std.rk32xx8
-rw-r--r--sys/arm/ti/aintc.c309
-rw-r--r--sys/arm/ti/am335x/am3359_cppi41.c190
-rw-r--r--sys/arm/ti/am335x/am335x_dmtimer.c408
-rw-r--r--sys/arm/ti/am335x/am335x_dmtpps.c621
-rw-r--r--sys/arm/ti/am335x/am335x_dmtreg.h88
-rw-r--r--sys/arm/ti/am335x/am335x_ecap.c199
-rw-r--r--sys/arm/ti/am335x/am335x_ehrpwm.c718
-rw-r--r--sys/arm/ti/am335x/am335x_gpio.c158
-rw-r--r--sys/arm/ti/am335x/am335x_lcd.c1104
-rw-r--r--sys/arm/ti/am335x/am335x_lcd.h60
-rw-r--r--sys/arm/ti/am335x/am335x_lcd_syscons.c771
-rw-r--r--sys/arm/ti/am335x/am335x_musb.c462
-rw-r--r--sys/arm/ti/am335x/am335x_pmic.c328
-rw-r--r--sys/arm/ti/am335x/am335x_pwm.h35
-rw-r--r--sys/arm/ti/am335x/am335x_pwmss.c179
-rw-r--r--sys/arm/ti/am335x/am335x_reg.h42
-rw-r--r--sys/arm/ti/am335x/am335x_rtc.c213
-rw-r--r--sys/arm/ti/am335x/am335x_rtcreg.h76
-rw-r--r--sys/arm/ti/am335x/am335x_rtcvar.h34
-rw-r--r--sys/arm/ti/am335x/am335x_scm.c198
-rw-r--r--sys/arm/ti/am335x/am335x_scm.h51
-rw-r--r--sys/arm/ti/am335x/am335x_scm_padconf.c303
-rw-r--r--sys/arm/ti/am335x/am335x_scm_padconf.h47
-rw-r--r--sys/arm/ti/am335x/am335x_usb_phy.c121
-rw-r--r--sys/arm/ti/am335x/files.am335x25
-rw-r--r--sys/arm/ti/am335x/std.am335x8
-rw-r--r--sys/arm/ti/am335x/tda19988.c801
-rw-r--r--sys/arm/ti/am335x/tps65217x.h114
-rw-r--r--sys/arm/ti/clk/clock_common.c152
-rw-r--r--sys/arm/ti/clk/clock_common.h43
-rw-r--r--sys/arm/ti/clk/ti_clk_clkctrl.c219
-rw-r--r--sys/arm/ti/clk/ti_clk_clkctrl.h43
-rw-r--r--sys/arm/ti/clk/ti_clk_dpll.c340
-rw-r--r--sys/arm/ti/clk/ti_clk_dpll.h96
-rw-r--r--sys/arm/ti/clk/ti_clkctrl.c352
-rw-r--r--sys/arm/ti/clk/ti_divider_clock.c264
-rw-r--r--sys/arm/ti/clk/ti_dpll_clock.c375
-rw-r--r--sys/arm/ti/clk/ti_gate_clock.c266
-rw-r--r--sys/arm/ti/clk/ti_mux_clock.c249
-rw-r--r--sys/arm/ti/cpsw/if_cpsw.c3028
-rw-r--r--sys/arm/ti/cpsw/if_cpswreg.h210
-rw-r--r--sys/arm/ti/cpsw/if_cpswvar.h151
-rw-r--r--sys/arm/ti/files.ti34
-rw-r--r--sys/arm/ti/omap4/files.omap422
-rw-r--r--sys/arm/ti/omap4/omap4_gpio.c149
-rw-r--r--sys/arm/ti/omap4/omap4_l2cache.c91
-rw-r--r--sys/arm/ti/omap4/omap4_machdep.h40
-rw-r--r--sys/arm/ti/omap4/omap4_mp.c76
-rw-r--r--sys/arm/ti/omap4/omap4_prcm_clks.c1504
-rw-r--r--sys/arm/ti/omap4/omap4_reg.h542
-rw-r--r--sys/arm/ti/omap4/omap4_scm_padconf.c304
-rw-r--r--sys/arm/ti/omap4/omap4_scm_padconf.h83
-rw-r--r--sys/arm/ti/omap4/omap4_smc.h54
-rw-r--r--sys/arm/ti/omap4/omap4_wugen.c249
-rw-r--r--sys/arm/ti/omap4/pandaboard/pandaboard.c169
-rw-r--r--sys/arm/ti/omap4/pandaboard/pandaboard.h31
-rw-r--r--sys/arm/ti/omap4/std.omap48
-rw-r--r--sys/arm/ti/std.ti6
-rw-r--r--sys/arm/ti/ti_adc.c969
-rw-r--r--sys/arm/ti/ti_adcreg.h127
-rw-r--r--sys/arm/ti/ti_adcvar.h87
-rw-r--r--sys/arm/ti/ti_cpuid.c292
-rw-r--r--sys/arm/ti/ti_cpuid.h91
-rw-r--r--sys/arm/ti/ti_edma3.c424
-rw-r--r--sys/arm/ti/ti_edma3.h83
-rw-r--r--sys/arm/ti/ti_gpio.c1125
-rw-r--r--sys/arm/ti/ti_gpio.h70
-rw-r--r--sys/arm/ti/ti_gpio_if.m49
-rw-r--r--sys/arm/ti/ti_i2c.c977
-rw-r--r--sys/arm/ti/ti_i2c.h133
-rw-r--r--sys/arm/ti/ti_machdep.c166
-rw-r--r--sys/arm/ti/ti_mbox.c268
-rw-r--r--sys/arm/ti/ti_mbox.h46
-rw-r--r--sys/arm/ti/ti_omap4_cm.c150
-rw-r--r--sys/arm/ti/ti_omap4_cm.h34
-rw-r--r--sys/arm/ti/ti_pinmux.c460
-rw-r--r--sys/arm/ti/ti_pinmux.h79
-rw-r--r--sys/arm/ti/ti_prcm.c344
-rw-r--r--sys/arm/ti/ti_prcm.h39
-rw-r--r--sys/arm/ti/ti_prm.c210
-rw-r--r--sys/arm/ti/ti_prm.h38
-rw-r--r--sys/arm/ti/ti_pruss.c854
-rw-r--r--sys/arm/ti/ti_pruss.h57
-rw-r--r--sys/arm/ti/ti_scm.c160
-rw-r--r--sys/arm/ti/ti_scm.h57
-rw-r--r--sys/arm/ti/ti_scm_syscon.c298
-rw-r--r--sys/arm/ti/ti_sdhci.c770
-rw-r--r--sys/arm/ti/ti_sdma.c1248
-rw-r--r--sys/arm/ti/ti_sdma.h113
-rw-r--r--sys/arm/ti/ti_sdmareg.h135
-rw-r--r--sys/arm/ti/ti_smc.S41
-rw-r--r--sys/arm/ti/ti_smc.h34
-rw-r--r--sys/arm/ti/ti_spi.c582
-rw-r--r--sys/arm/ti/ti_spireg.h97
-rw-r--r--sys/arm/ti/ti_spivar.h71
-rw-r--r--sys/arm/ti/ti_sysc.c622
-rw-r--r--sys/arm/ti/ti_sysc.h43
-rw-r--r--sys/arm/ti/ti_wdt.c276
-rw-r--r--sys/arm/ti/ti_wdt.h74
-rw-r--r--sys/arm/ti/tivar.h43
-rw-r--r--sys/arm/ti/twl/twl.c458
-rw-r--r--sys/arm/ti/twl/twl.h41
-rw-r--r--sys/arm/ti/twl/twl_clks.c651
-rw-r--r--sys/arm/ti/twl/twl_clks.h38
-rw-r--r--sys/arm/ti/twl/twl_vreg.c1030
-rw-r--r--sys/arm/ti/twl/twl_vreg.h37
-rw-r--r--sys/arm/ti/usb/omap_ehci.c468
-rw-r--r--sys/arm/ti/usb/omap_host.c467
-rw-r--r--sys/arm/ti/usb/omap_tll.c361
-rw-r--r--sys/arm/ti/usb/omap_usb.h50
-rw-r--r--sys/arm/versatile/files.versatile9
-rw-r--r--sys/arm/versatile/pl050.c742
-rw-r--r--sys/arm/versatile/sp804.c343
-rw-r--r--sys/arm/versatile/versatile_clcd.c921
-rw-r--r--sys/arm/versatile/versatile_machdep.c104
-rw-r--r--sys/arm/versatile/versatile_pci.c548
-rw-r--r--sys/arm/versatile/versatile_scm.c144
-rw-r--r--sys/arm/versatile/versatile_scm.h49
-rw-r--r--sys/arm/versatile/versatile_sic.c324
-rw-r--r--sys/arm/xilinx/files.zynq717
-rw-r--r--sys/arm/xilinx/std.zynq710
-rw-r--r--sys/arm/xilinx/uart_dev_cdnc.c714
-rw-r--r--sys/arm/xilinx/zy7_devcfg.c850
-rw-r--r--sys/arm/xilinx/zy7_ehci.c371
-rw-r--r--sys/arm/xilinx/zy7_gpio.c377
-rw-r--r--sys/arm/xilinx/zy7_l2cache.c53
-rw-r--r--sys/arm/xilinx/zy7_machdep.c104
-rw-r--r--sys/arm/xilinx/zy7_machdep.h38
-rw-r--r--sys/arm/xilinx/zy7_mp.c144
-rw-r--r--sys/arm/xilinx/zy7_qspi.c754
-rw-r--r--sys/arm/xilinx/zy7_reg.h73
-rw-r--r--sys/arm/xilinx/zy7_slcr.c714
-rw-r--r--sys/arm/xilinx/zy7_slcr.h329
-rw-r--r--sys/arm/xilinx/zy7_spi.c596
707 files changed, 221462 insertions, 0 deletions
diff --git a/sys/arm/allwinner/a10/a10_intc.c b/sys/arm/allwinner/a10/a10_intc.c
new file mode 100644
index 000000000000..28cc245c7254
--- /dev/null
+++ b/sys/arm/allwinner/a10/a10_intc.c
@@ -0,0 +1,390 @@
+/*-
+ * Copyright (c) 2012 Ganbold Tsagaankhuu <ganbold@freebsd.org>
+ * Copyright (c) 2016 Emmanuel Vadot <manu@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/cpuset.h>
+#include <sys/kernel.h>
+#include <sys/ktr.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/param.h>
+#include <sys/pcpu.h>
+#include <sys/proc.h>
+#include <sys/rman.h>
+#include <sys/smp.h>
+#include <sys/systm.h>
+#include <sys/sched.h>
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "pic_if.h"
+
+/**
+ * Interrupt controller registers
+ *
+ */
+#define SW_INT_VECTOR_REG 0x00
+#define SW_INT_BASE_ADR_REG 0x04
+#define SW_INT_PROTECTION_REG 0x08
+#define SW_INT_NMI_CTRL_REG 0x0c
+
+#define SW_INT_IRQ_PENDING_REG0 0x10
+#define SW_INT_IRQ_PENDING_REG1 0x14
+#define SW_INT_IRQ_PENDING_REG2 0x18
+
+#define SW_INT_FIQ_PENDING_REG0 0x20
+#define SW_INT_FIQ_PENDING_REG1 0x24
+#define SW_INT_FIQ_PENDING_REG2 0x28
+
+#define SW_INT_SELECT_REG0 0x30
+#define SW_INT_SELECT_REG1 0x34
+#define SW_INT_SELECT_REG2 0x38
+
+#define SW_INT_ENABLE_REG0 0x40
+#define SW_INT_ENABLE_REG1 0x44
+#define SW_INT_ENABLE_REG2 0x48
+
+#define SW_INT_MASK_REG0 0x50
+#define SW_INT_MASK_REG1 0x54
+#define SW_INT_MASK_REG2 0x58
+
+#define SW_INT_IRQNO_ENMI 0
+
+#define A10_INTR_MAX_NIRQS 81
+
+#define SW_INT_IRQ_PENDING_REG(_b) (0x10 + ((_b) * 4))
+#define SW_INT_FIQ_PENDING_REG(_b) (0x20 + ((_b) * 4))
+#define SW_INT_SELECT_REG(_b) (0x30 + ((_b) * 4))
+#define SW_INT_ENABLE_REG(_b) (0x40 + ((_b) * 4))
+#define SW_INT_MASK_REG(_b) (0x50 + ((_b) * 4))
+
+struct a10_intr_irqsrc {
+ struct intr_irqsrc isrc;
+ u_int irq;
+};
+
+struct a10_aintc_softc {
+ device_t sc_dev;
+ struct resource * aintc_res;
+ bus_space_tag_t aintc_bst;
+ bus_space_handle_t aintc_bsh;
+ struct mtx mtx;
+ struct a10_intr_irqsrc isrcs[A10_INTR_MAX_NIRQS];
+};
+
+#define aintc_read_4(sc, reg) \
+ bus_space_read_4(sc->aintc_bst, sc->aintc_bsh, reg)
+#define aintc_write_4(sc, reg, val) \
+ bus_space_write_4(sc->aintc_bst, sc->aintc_bsh, reg, val)
+
+static __inline void
+a10_intr_eoi(struct a10_aintc_softc *sc, u_int irq)
+{
+
+ if (irq != SW_INT_IRQNO_ENMI)
+ return;
+ mtx_lock_spin(&sc->mtx);
+ aintc_write_4(sc, SW_INT_IRQ_PENDING_REG(0),
+ (1 << SW_INT_IRQNO_ENMI));
+ mtx_unlock_spin(&sc->mtx);
+}
+
+static void
+a10_intr_unmask(struct a10_aintc_softc *sc, u_int irq)
+{
+ uint32_t bit, block, value;
+
+ bit = (irq % 32);
+ block = (irq / 32);
+
+ mtx_lock_spin(&sc->mtx);
+ value = aintc_read_4(sc, SW_INT_ENABLE_REG(block));
+ value |= (1 << bit);
+ aintc_write_4(sc, SW_INT_ENABLE_REG(block), value);
+
+ value = aintc_read_4(sc, SW_INT_MASK_REG(block));
+ value &= ~(1 << bit);
+ aintc_write_4(sc, SW_INT_MASK_REG(block), value);
+ mtx_unlock_spin(&sc->mtx);
+}
+
+static void
+a10_intr_mask(struct a10_aintc_softc *sc, u_int irq)
+{
+ uint32_t bit, block, value;
+
+ bit = (irq % 32);
+ block = (irq / 32);
+
+ mtx_lock_spin(&sc->mtx);
+ value = aintc_read_4(sc, SW_INT_ENABLE_REG(block));
+ value &= ~(1 << bit);
+ aintc_write_4(sc, SW_INT_ENABLE_REG(block), value);
+
+ value = aintc_read_4(sc, SW_INT_MASK_REG(block));
+ value |= (1 << bit);
+ aintc_write_4(sc, SW_INT_MASK_REG(block), value);
+ mtx_unlock_spin(&sc->mtx);
+}
+
+static int
+a10_pending_irq(struct a10_aintc_softc *sc)
+{
+ uint32_t value;
+ int i, b;
+
+ for (i = 0; i < 3; i++) {
+ value = aintc_read_4(sc, SW_INT_IRQ_PENDING_REG(i));
+ if (value == 0)
+ continue;
+ for (b = 0; b < 32; b++)
+ if (value & (1 << b)) {
+ return (i * 32 + b);
+ }
+ }
+
+ return (-1);
+}
+
+static int
+a10_intr(void *arg)
+{
+ struct a10_aintc_softc *sc = arg;
+ u_int irq;
+
+ irq = a10_pending_irq(sc);
+ if (irq == -1 || irq > A10_INTR_MAX_NIRQS) {
+ device_printf(sc->sc_dev, "Spurious interrupt %d\n", irq);
+ return (FILTER_HANDLED);
+ }
+
+ while (irq != -1) {
+ if (irq > A10_INTR_MAX_NIRQS) {
+ device_printf(sc->sc_dev, "Spurious interrupt %d\n",
+ irq);
+ return (FILTER_HANDLED);
+ }
+ if (intr_isrc_dispatch(&sc->isrcs[irq].isrc,
+ curthread->td_intr_frame) != 0) {
+ a10_intr_mask(sc, irq);
+ a10_intr_eoi(sc, irq);
+ device_printf(sc->sc_dev,
+ "Stray interrupt %d disabled\n", irq);
+ }
+
+ arm_irq_memory_barrier(irq);
+ irq = a10_pending_irq(sc);
+ }
+
+ return (FILTER_HANDLED);
+}
+
+static int
+a10_intr_pic_attach(struct a10_aintc_softc *sc)
+{
+ struct intr_pic *pic;
+ int error;
+ uint32_t irq;
+ const char *name;
+ intptr_t xref;
+
+ name = device_get_nameunit(sc->sc_dev);
+ for (irq = 0; irq < A10_INTR_MAX_NIRQS; irq++) {
+ sc->isrcs[irq].irq = irq;
+
+ error = intr_isrc_register(&sc->isrcs[irq].isrc,
+ sc->sc_dev, 0, "%s,%u", name, irq);
+ if (error != 0)
+ return (error);
+ }
+
+ xref = OF_xref_from_node(ofw_bus_get_node(sc->sc_dev));
+ pic = intr_pic_register(sc->sc_dev, xref);
+ if (pic == NULL)
+ return (ENXIO);
+
+ return (intr_pic_claim_root(sc->sc_dev, xref, a10_intr, sc, 0));
+}
+
+static void
+a10_intr_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct a10_aintc_softc *sc;
+ u_int irq = ((struct a10_intr_irqsrc *)isrc)->irq;
+
+ sc = device_get_softc(dev);
+ arm_irq_memory_barrier(irq);
+ a10_intr_unmask(sc, irq);
+}
+
+static void
+a10_intr_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct a10_aintc_softc *sc;
+ u_int irq = ((struct a10_intr_irqsrc *)isrc)->irq;
+
+ sc = device_get_softc(dev);
+ a10_intr_mask(sc, irq);
+}
+
+static int
+a10_intr_map_intr(device_t dev, struct intr_map_data *data,
+ struct intr_irqsrc **isrcp)
+{
+ struct intr_map_data_fdt *daf;
+ struct a10_aintc_softc *sc;
+
+ if (data->type != INTR_MAP_DATA_FDT)
+ return (ENOTSUP);
+
+ daf = (struct intr_map_data_fdt *)data;
+ if (daf->ncells != 1 || daf->cells[0] >= A10_INTR_MAX_NIRQS)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+ *isrcp = &sc->isrcs[daf->cells[0]].isrc;
+ return (0);
+}
+
+static void
+a10_intr_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct a10_aintc_softc *sc = device_get_softc(dev);
+ u_int irq = ((struct a10_intr_irqsrc *)isrc)->irq;
+
+ a10_intr_mask(sc, irq);
+ a10_intr_eoi(sc, irq);
+}
+
+static void
+a10_intr_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+
+ a10_intr_enable_intr(dev, isrc);
+}
+
+static void
+a10_intr_post_filter(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct a10_aintc_softc *sc = device_get_softc(dev);
+ u_int irq = ((struct a10_intr_irqsrc *)isrc)->irq;
+
+ a10_intr_eoi(sc, irq);
+}
+
+static int
+a10_aintc_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-a10-ic"))
+ return (ENXIO);
+ device_set_desc(dev, "A10 AINTC Interrupt Controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+a10_aintc_attach(device_t dev)
+{
+ struct a10_aintc_softc *sc = device_get_softc(dev);
+ int rid = 0;
+ int i;
+ sc->sc_dev = dev;
+
+ sc->aintc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &rid, RF_ACTIVE);
+ if (!sc->aintc_res) {
+ device_printf(dev, "could not allocate resource\n");
+ goto error;
+ }
+
+ sc->aintc_bst = rman_get_bustag(sc->aintc_res);
+ sc->aintc_bsh = rman_get_bushandle(sc->aintc_res);
+
+ mtx_init(&sc->mtx, "A10 AINTC lock", "", MTX_SPIN);
+
+ /* Disable & clear all interrupts */
+ for (i = 0; i < 3; i++) {
+ aintc_write_4(sc, SW_INT_ENABLE_REG(i), 0);
+ aintc_write_4(sc, SW_INT_MASK_REG(i), 0xffffffff);
+ }
+ /* enable protection mode*/
+ aintc_write_4(sc, SW_INT_PROTECTION_REG, 0x01);
+
+ /* config the external interrupt source type*/
+ aintc_write_4(sc, SW_INT_NMI_CTRL_REG, 0x00);
+
+ if (a10_intr_pic_attach(sc) != 0) {
+ device_printf(dev, "could not attach PIC\n");
+ return (ENXIO);
+ }
+
+ return (0);
+
+error:
+ bus_release_resource(dev, SYS_RES_MEMORY, rid,
+ sc->aintc_res);
+ return (ENXIO);
+}
+
+static device_method_t a10_aintc_methods[] = {
+ DEVMETHOD(device_probe, a10_aintc_probe),
+ DEVMETHOD(device_attach, a10_aintc_attach),
+
+ /* Interrupt controller interface */
+ DEVMETHOD(pic_disable_intr, a10_intr_disable_intr),
+ DEVMETHOD(pic_enable_intr, a10_intr_enable_intr),
+ DEVMETHOD(pic_map_intr, a10_intr_map_intr),
+ DEVMETHOD(pic_post_filter, a10_intr_post_filter),
+ DEVMETHOD(pic_post_ithread, a10_intr_post_ithread),
+ DEVMETHOD(pic_pre_ithread, a10_intr_pre_ithread),
+ { 0, 0 }
+};
+
+static driver_t a10_aintc_driver = {
+ "aintc",
+ a10_aintc_methods,
+ sizeof(struct a10_aintc_softc),
+};
+
+static devclass_t a10_aintc_devclass;
+
+EARLY_DRIVER_MODULE(aintc, simplebus, a10_aintc_driver, a10_aintc_devclass, 0, 0,
+ BUS_PASS_INTERRUPT + BUS_PASS_ORDER_FIRST);
diff --git a/sys/arm/allwinner/a10/a10_padconf.c b/sys/arm/allwinner/a10/a10_padconf.c
new file mode 100644
index 000000000000..c9bf62e002ab
--- /dev/null
+++ b/sys/arm/allwinner/a10/a10_padconf.c
@@ -0,0 +1,230 @@
+/*-
+ * Copyright (c) 2016 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/types.h>
+
+#include <arm/allwinner/allwinner_pinctrl.h>
+
+#ifdef SOC_ALLWINNER_A10
+
+const static struct allwinner_pins a10_pins[] = {
+ {"PA0", 0, 0, {"gpio_in", "gpio_out", "emac", "spi1", "uart2", NULL, NULL, NULL}},
+ {"PA1", 0, 1, {"gpio_in", "gpio_out", "emac", "spi1", "uart2", NULL, NULL, NULL}},
+ {"PA2", 0, 2, {"gpio_in", "gpio_out", "emac", "spi1", "uart2", NULL, NULL, NULL}},
+ {"PA3", 0, 3, {"gpio_in", "gpio_out", "emac", "spi1", "uart2", NULL, NULL, NULL}},
+ {"PA4", 0, 4, {"gpio_in", "gpio_out", "emac", "spi1", NULL, NULL, NULL, NULL}},
+ {"PA5", 0, 5, {"gpio_in", "gpio_out", "emac", "spi3", NULL, NULL, NULL, NULL}},
+ {"PA6", 0, 6, {"gpio_in", "gpio_out", "emac", "spi3", NULL, NULL, NULL, NULL}},
+ {"PA7", 0, 7, {"gpio_in", "gpio_out", "emac", "spi3", NULL, NULL, NULL, NULL}},
+ {"PA8", 0, 8, {"gpio_in", "gpio_out", "emac", "spi3", NULL, NULL, NULL, NULL}},
+ {"PA9", 0, 9, {"gpio_in", "gpio_out", "emac", "spi3", NULL, NULL, NULL, NULL}},
+ {"PA10", 0, 10, {"gpio_in", "gpio_out", "emac", NULL, "uart1", NULL, NULL, NULL}},
+ {"PA11", 0, 11, {"gpio_in", "gpio_out", "emac", NULL, "uart1", NULL, NULL, NULL}},
+ {"PA12", 0, 12, {"gpio_in", "gpio_out", "emac", "uart6", "uart1", NULL, NULL, NULL}},
+ {"PA13", 0, 13, {"gpio_in", "gpio_out", "emac", "uart6", "uart1", NULL, NULL, NULL}},
+ {"PA14", 0, 14, {"gpio_in", "gpio_out", "emac", "uart7", "uart1", NULL, NULL, NULL}},
+ {"PA15", 0, 15, {"gpio_in", "gpio_out", "emac", "uart7", "uart1", NULL, NULL, NULL}},
+ {"PA16", 0, 16, {"gpio_in", "gpio_out", NULL, "can", "uart1", NULL, NULL, NULL}},
+ {"PA17", 0, 17, {"gpio_in", "gpio_out", NULL, "can", "uart1", NULL, NULL, NULL}},
+
+ {"PB0", 1, 0, {"gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, NULL, NULL}},
+ {"PB1", 1, 1, {"gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, NULL, NULL}},
+ {"PB2", 1, 2, {"gpio_in", "gpio_out", "pwm", NULL, NULL, NULL, NULL, NULL}},
+ {"PB3", 1, 3, {"gpio_in", "gpio_out", "ir0", NULL, NULL, NULL, NULL, NULL}},
+ {"PB4", 1, 4, {"gpio_in", "gpio_out", "ir0", NULL, NULL, NULL, NULL, NULL}},
+ {"PB5", 1, 5, {"gpio_in", "gpio_out", "i2s", "ac97", NULL, NULL, NULL, NULL}},
+ {"PB6", 1, 6, {"gpio_in", "gpio_out", "i2s", "ac97", NULL, NULL, NULL, NULL}},
+ {"PB7", 1, 7, {"gpio_in", "gpio_out", "i2s", "ac97", NULL, NULL, NULL, NULL}},
+ {"PB8", 1, 8, {"gpio_in", "gpio_out", "i2s", "ac97", NULL, NULL, NULL, NULL}},
+ {"PB9", 1, 9, {"gpio_in", "gpio_out", "i2s", NULL, NULL, NULL, NULL, NULL}},
+ {"PB10", 1, 10, {"gpio_in", "gpio_out", "i2s", NULL, NULL, NULL, NULL, NULL}},
+ {"PB11", 1, 11, {"gpio_in", "gpio_out", "i2s", NULL, NULL, NULL, NULL, NULL}},
+ {"PB12", 1, 12, {"gpio_in", "gpio_out", "i2s", "ac97", NULL, NULL, NULL, NULL}},
+ {"PB13", 1, 13, {"gpio_in", "gpio_out", "spi2", NULL, NULL, NULL, NULL, NULL}},
+ {"PB14", 1, 14, {"gpio_in", "gpio_out", "spi2", "jtag", NULL, NULL, NULL, NULL}},
+ {"PB15", 1, 15, {"gpio_in", "gpio_out", "spi2", "jtag", NULL, NULL, NULL, NULL}},
+ {"PB16", 1, 16, {"gpio_in", "gpio_out", "spi2", "jtag", NULL, NULL, NULL, NULL}},
+ {"PB17", 1, 17, {"gpio_in", "gpio_out", "spi2", "jtag", NULL, NULL, NULL, NULL}},
+ {"PB18", 1, 18, {"gpio_in", "gpio_out", "i2c1", NULL, NULL, NULL, NULL, NULL}},
+ {"PB19", 1, 19, {"gpio_in", "gpio_out", "i2c1", NULL, NULL, NULL, NULL, NULL}},
+ {"PB20", 1, 20, {"gpio_in", "gpio_out", "i2c2", NULL, NULL, NULL, NULL, NULL}},
+ {"PB21", 1, 21, {"gpio_in", "gpio_out", "i2c2", NULL, NULL, NULL, NULL, NULL}},
+ {"PB22", 1, 22, {"gpio_in", "gpio_out", "uart0", "ir1", NULL, NULL, NULL, NULL}},
+ {"PB23", 1, 23, {"gpio_in", "gpio_out", "uart0", "ir1", NULL, NULL, NULL, NULL}},
+
+ {"PC0", 2, 0, {"gpio_in", "gpio_out", "nand", "spi0", NULL, NULL, NULL, NULL}},
+ {"PC1", 2, 1, {"gpio_in", "gpio_out", "nand", "spi0", NULL, NULL, NULL, NULL}},
+ {"PC2", 2, 2, {"gpio_in", "gpio_out", "nand", "spi0", NULL, NULL, NULL, NULL}},
+ {"PC3", 2, 3, {"gpio_in", "gpio_out", "nand", NULL, NULL, NULL, NULL, NULL}},
+ {"PC4", 2, 4, {"gpio_in", "gpio_out", "nand", NULL, NULL, NULL, NULL, NULL}},
+ {"PC5", 2, 5, {"gpio_in", "gpio_out", "nand", NULL, NULL, NULL, NULL, NULL}},
+ {"PC6", 2, 6, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC7", 2, 7, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC8", 2, 8, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC9", 2, 9, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC10", 2, 10, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC11", 2, 11, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC12", 2, 12, {"gpio_in", "gpio_out", "nand", NULL, NULL, NULL, NULL, NULL}},
+ {"PC13", 2, 13, {"gpio_in", "gpio_out", "nand", NULL, NULL, NULL, NULL, NULL}},
+ {"PC14", 2, 14, {"gpio_in", "gpio_out", "nand", NULL, NULL, NULL, NULL, NULL}},
+ {"PC15", 2, 15, {"gpio_in", "gpio_out", "nand", NULL, NULL, NULL, NULL, NULL}},
+ {"PC16", 2, 16, {"gpio_in", "gpio_out", "nand", NULL, NULL, NULL, NULL, NULL}},
+ {"PC17", 2, 17, {"gpio_in", "gpio_out", "nand", NULL, NULL, NULL, NULL, NULL}},
+ {"PC18", 2, 18, {"gpio_in", "gpio_out", "nand", NULL, NULL, NULL, NULL, NULL}},
+ {"PC19", 2, 19, {"gpio_in", "gpio_out", "nand", "spi2", NULL, NULL, NULL, NULL}},
+ {"PC20", 2, 20, {"gpio_in", "gpio_out", "nand", "spi2", NULL, NULL, NULL, NULL}},
+ {"PC21", 2, 21, {"gpio_in", "gpio_out", "nand", "spi2", NULL, NULL, NULL, NULL}},
+ {"PC22", 2, 22, {"gpio_in", "gpio_out", "nand", "spi2", NULL, NULL, NULL, NULL}},
+ {"PC23", 2, 23, {"gpio_in", "gpio_out", "spi0", NULL, NULL, NULL, NULL, NULL}},
+ {"PC24", 2, 24, {"gpio_in", "gpio_out", "nand", NULL, NULL, NULL, NULL, NULL}},
+
+ {"PD0", 3, 0, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD1", 3, 1, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD2", 3, 2, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD3", 3, 3, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD4", 3, 4, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD5", 3, 5, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD6", 3, 6, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD7", 3, 7, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD8", 3, 8, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD9", 3, 9, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD10", 3, 10, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}},
+ {"PD11", 3, 11, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}},
+ {"PD12", 3, 12, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}},
+ {"PD13", 3, 13, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}},
+ {"PD14", 3, 14, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}},
+ {"PD15", 3, 15, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}},
+ {"PD16", 3, 16, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}},
+ {"PD17", 3, 17, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}},
+ {"PD18", 3, 18, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}},
+ {"PD19", 3, 19, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}},
+ {"PD20", 3, 20, {"gpio_in", "gpio_out", "lcd0", "csi1", NULL, NULL, NULL, NULL}},
+ {"PD21", 3, 21, {"gpio_in", "gpio_out", "lcd0", "sim", NULL, NULL, NULL, NULL}},
+ {"PD22", 3, 22, {"gpio_in", "gpio_out", "lcd0", "sim", NULL, NULL, NULL, NULL}},
+ {"PD23", 3, 23, {"gpio_in", "gpio_out", "lcd0", "sim", NULL, NULL, NULL, NULL}},
+ {"PD24", 3, 24, {"gpio_in", "gpio_out", "lcd0", "sim", NULL, NULL, NULL, NULL}},
+ {"PD25", 3, 25, {"gpio_in", "gpio_out", "lcd0", "sim", NULL, NULL, NULL, NULL}},
+ {"PD26", 3, 26, {"gpio_in", "gpio_out", "lcd0", "sim", NULL, NULL, NULL, NULL}},
+ {"PD27", 3, 27, {"gpio_in", "gpio_out", "lcd0", "sim", NULL, NULL, NULL, NULL}},
+
+ {"PE0", 4, 0, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}},
+ {"PE1", 4, 1, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}},
+ {"PE2", 4, 2, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}},
+ {"PE3", 4, 3, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}},
+ {"PE4", 4, 4, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}},
+ {"PE5", 4, 5, {"gpio_in", "gpio_out", "ts0", "csi0", "sim", NULL, NULL, NULL}},
+ {"PE6", 4, 6, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}},
+ {"PE7", 4, 7, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}},
+ {"PE8", 4, 8, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}},
+ {"PE9", 4, 9, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}},
+ {"PE10", 4, 10, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}},
+ {"PE11", 4, 11, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}},
+
+ {"PF0", 5, 0, {"gpio_in", "gpio_out", "mmc0", NULL, "jtag", NULL, NULL, NULL}},
+ {"PF1", 5, 1, {"gpio_in", "gpio_out", "mmc0", NULL, "jtag", NULL, NULL, NULL}},
+ {"PF2", 5, 2, {"gpio_in", "gpio_out", "mmc0", NULL, "uart0", NULL, NULL, NULL}},
+ {"PF3", 5, 3, {"gpio_in", "gpio_out", "mmc0", NULL, "jtag", NULL, NULL, NULL}},
+ {"PF4", 5, 4, {"gpio_in", "gpio_out", "mmc0", NULL, "jtag", NULL, NULL, NULL}},
+ {"PF5", 5, 5, {"gpio_in", "gpio_out", "mmc0", NULL, "jtag", NULL, NULL, NULL}},
+
+ {"PG0", 6, 0, {"gpio_in", "gpio_out", "ts1", "csi1", "mmc1", NULL, NULL, NULL}},
+ {"PG1", 6, 1, {"gpio_in", "gpio_out", "ts1", "csi1", "mmc1", NULL, NULL, NULL}},
+ {"PG2", 6, 2, {"gpio_in", "gpio_out", "ts1", "csi1", "mmc1", NULL, NULL, NULL}},
+ {"PG3", 6, 3, {"gpio_in", "gpio_out", "ts1", "csi1", "mmc1", NULL, NULL, NULL}},
+ {"PG4", 6, 4, {"gpio_in", "gpio_out", "ts1", "csi1", "mmc1", "csi0", NULL, NULL}},
+ {"PG5", 6, 5, {"gpio_in", "gpio_out", "ts1", "csi1", "mmc1", "csi0", NULL, NULL}},
+ {"PG6", 6, 6, {"gpio_in", "gpio_out", "ts1", "csi1", "uart3", "csi0", NULL, NULL}},
+ {"PG7", 6, 7, {"gpio_in", "gpio_out", "ts1", "csi1", "uart3", "csi0", NULL, NULL}},
+ {"PG8", 6, 8, {"gpio_in", "gpio_out", "ts1", "csi1", "uart3", "csi0", NULL, NULL}},
+ {"PG9", 6, 9, {"gpio_in", "gpio_out", "ts1", "csi1", "uart3", "csi0", NULL, NULL}},
+ {"PG10", 6, 10, {"gpio_in", "gpio_out", "ts1", "csi1", "uart4", "csi0", NULL, NULL}},
+ {"PG11", 6, 11, {"gpio_in", "gpio_out", "ts1", "csi1", "uart4", "csi0", NULL, NULL}},
+
+ {"PH0", 7, 0, {"gpio_in", "gpio_out", "lcd1", "pata", "uart3", NULL, "ph_eint0", "csi1"}, 6, 0, 0},
+ {"PH1", 7, 1, {"gpio_in", "gpio_out", "lcd1", "pata", "uart3", NULL, "ph_eint1", "csi1"}, 6, 1, 0},
+ {"PH2", 7, 2, {"gpio_in", "gpio_out", "lcd1", "pata", "uart3", NULL, "ph_eint2", "csi1"}, 6, 2, 0},
+ {"PH3", 7, 3, {"gpio_in", "gpio_out", "lcd1", "pata", "uart3", NULL, "ph_eint3", "csi1"}, 6, 3, 0},
+ {"PH4", 7, 4, {"gpio_in", "gpio_out", "lcd1", "pata", "uart4", NULL, "ph_eint4", "csi1"}, 6, 4, 0},
+ {"PH5", 7, 5, {"gpio_in", "gpio_out", "lcd1", "pata", "uart4", NULL, "ph_eint5", "csi1"}, 6, 5, 0},
+ {"PH6", 7, 6, {"gpio_in", "gpio_out", "lcd1", "pata", "uart5", "ms", "ph_eint6", "csi1"}, 6, 6, 0},
+ {"PH7", 7, 7, {"gpio_in", "gpio_out", "lcd1", "pata", "uart5", "ms", "ph_eint7", "csi1"}, 6, 7, 0},
+ {"PH8", 7, 8, {"gpio_in", "gpio_out", "lcd1", "pata", "keypad", "ms", "ph_eint8", "csi1"}, 6, 8, 0},
+ {"PH9", 7, 9, {"gpio_in", "gpio_out", "lcd1", "pata", "keypad", "ms", "ph_eint9", "csi1"}, 6, 9, 0},
+ {"PH10", 7, 10, {"gpio_in", "gpio_out", "lcd1", "pata", "keypad", "ms", "ph_eint10", "csi1"}, 6, 10, 0},
+ {"PH11", 7, 11, {"gpio_in", "gpio_out", "lcd1", "pata", "keypad", "ms", "ph_eint11", "csi1"}, 6, 11, 0},
+ {"PH12", 7, 12, {"gpio_in", "gpio_out", "lcd1", "pata", "ps2", NULL, "ph_eint12", "csi1"}, 6, 12, 0},
+ {"PH13", 7, 13, {"gpio_in", "gpio_out", "lcd1", "pata", "ps2", "sim", "ph_eint13", "csi1"}, 6, 13, 0},
+ {"PH14", 7, 14, {"gpio_in", "gpio_out", "lcd1", "pata", "keypad", "sim", "ph_eint14", "csi1"}, 6, 14, 0},
+ {"PH15", 7, 15, {"gpio_in", "gpio_out", "lcd1", "pata", "keypad", "sim", "ph_eint15", "csi1"}, 6, 15, 0},
+ {"PH16", 7, 16, {"gpio_in", "gpio_out", "lcd1", "pata", "keypad", NULL, "ph_eint16", "csi1"}, 6, 16, 0},
+ {"PH17", 7, 17, {"gpio_in", "gpio_out", "lcd1", "pata", "keypad", "sim", "ph_eint17", "csi1"}, 6, 17, 0},
+ {"PH18", 7, 18, {"gpio_in", "gpio_out", "lcd1", "pata", "keypad", "sim", "ph_eint18", "csi1"}, 6, 18, 0},
+ {"PH19", 7, 19, {"gpio_in", "gpio_out", "lcd1", "pata", "keypad", "sim", "ph_eint19", "csi1"}, 6, 19, 0},
+ {"PH20", 7, 20, {"gpio_in", "gpio_out", "lcd1", "pata", "can", NULL, "ph_eint20", "csi1"}, 6, 20, 0},
+ {"PH21", 7, 21, {"gpio_in", "gpio_out", "lcd1", "pata", "can", NULL, "ph_eint21", "csi1"}, 6, 21, 0},
+ {"PH22", 7, 22, {"gpio_in", "gpio_out", "lcd1", "pata", "keypad", "mmc1", NULL, "csi1"}},
+ {"PH23", 7, 23, {"gpio_in", "gpio_out", "lcd1", "pata", "keypad", "mmc1", NULL, "csi1"}},
+ {"PH24", 7, 24, {"gpio_in", "gpio_out", "lcd1", "pata", "keypad", "mmc1", NULL, "csi1"}},
+ {"PH25", 7, 25, {"gpio_in", "gpio_out", "lcd1", "pata", "keypad", "mmc1", NULL, "csi1"}},
+ {"PH26", 7, 26, {"gpio_in", "gpio_out", "lcd1", "pata", "keypad", "mmc1", NULL, "csi1"}},
+ {"PH27", 7, 27, {"gpio_in", "gpio_out", "lcd1", "pata", "keypad", "mmc1", NULL, "csi1"}},
+
+ {"PI0", 8, 0, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}},
+ {"PI1", 8, 1, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}},
+ {"PI2", 8, 2, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}},
+ {"PI3", 8, 3, {"gpio_in", "gpio_out", "pwm", NULL, NULL, NULL, NULL, NULL}},
+ {"PI4", 8, 4, {"gpio_in", "gpio_out", "mmc3", NULL, NULL, NULL, NULL, NULL}},
+ {"PI5", 8, 5, {"gpio_in", "gpio_out", "mmc3", NULL, NULL, NULL, NULL, NULL}},
+ {"PI6", 8, 6, {"gpio_in", "gpio_out", "mmc3", NULL, NULL, NULL, NULL, NULL}},
+ {"PI7", 8, 7, {"gpio_in", "gpio_out", "mmc3", NULL, NULL, NULL, NULL, NULL}},
+ {"PI8", 8, 8, {"gpio_in", "gpio_out", "mmc3", NULL, NULL, NULL, NULL, NULL}},
+ {"PI9", 8, 9, {"gpio_in", "gpio_out", "mmc3", NULL, NULL, NULL, NULL, NULL}},
+ {"PI10", 8, 10, {"gpio_in", "gpio_out", "spi0", "uart5", NULL, NULL, "pi_eint22", NULL}, 6, 22, 0},
+ {"PI11", 8, 11, {"gpio_in", "gpio_out", "spi0", "uart5", NULL, NULL, "pi_eint23", NULL}, 6, 23, 0},
+ {"PI12", 8, 12, {"gpio_in", "gpio_out", "spi0", "uart6", NULL, NULL, "pi_eint24", NULL}, 6, 24, 0},
+ {"PI13", 8, 13, {"gpio_in", "gpio_out", "spi0", "uart6", NULL, NULL, "pi_eint25", NULL}, 6, 25, 0},
+ {"PI14", 8, 14, {"gpio_in", "gpio_out", "spi0", "ps2", "timer4", NULL, "pi_eint26", NULL}, 6, 26, 0},
+ {"PI15", 8, 15, {"gpio_in", "gpio_out", "spi1", "ps2", "timer5", NULL, "pi_eint27", NULL}, 6, 27, 0},
+ {"PI16", 8, 16, {"gpio_in", "gpio_out", "spi1", "uart2", NULL, NULL, "pi_eint28", NULL}, 6, 28, 0},
+ {"PI17", 8, 17, {"gpio_in", "gpio_out", "spi1", "uart2", NULL, NULL, "pi_eint29", NULL}, 6, 29, 0},
+ {"PI18", 8, 18, {"gpio_in", "gpio_out", "spi1", "uart2", NULL, NULL, "pi_eint30", NULL}, 6, 30, 0},
+ {"PI19", 8, 19, {"gpio_in", "gpio_out", "spi1", "uart2", NULL, NULL, "pi_eint31", NULL}, 6, 31, 0},
+ {"PI20", 8, 20, {"gpio_in", "gpio_out", "ps2", "uart7", "hdmi", NULL, NULL, NULL}},
+ {"PI21", 8, 21, {"gpio_in", "gpio_out", "ps2", "uart7", "hdmi", NULL, NULL, NULL}},
+};
+
+const struct allwinner_padconf a10_padconf = {
+ .npins = sizeof(a10_pins) / sizeof(struct allwinner_pins),
+ .pins = a10_pins,
+};
+
+#endif /* SOC_ALLWINNER_A10 */
diff --git a/sys/arm/allwinner/a10/files.a10 b/sys/arm/allwinner/a10/files.a10
new file mode 100644
index 000000000000..5fe53134f44c
--- /dev/null
+++ b/sys/arm/allwinner/a10/files.a10
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+arm/allwinner/a10/a10_intc.c standard
+arm/allwinner/a10/a10_padconf.c standard
+arm/allwinner/clkng/ccu_a10.c standard
diff --git a/sys/arm/allwinner/a10_ahci.c b/sys/arm/allwinner/a10_ahci.c
new file mode 100644
index 000000000000..399f17b079ef
--- /dev/null
+++ b/sys/arm/allwinner/a10_ahci.c
@@ -0,0 +1,426 @@
+/*-
+ * Copyright (c) 2015 Luiz Otavio O Souza <loos@freebsd.org> All rights reserved.
+ * Copyright (c) 2014-2015 M. Warner Losh <imp@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The magic-bit-bang sequence used in this code may be based on a linux
+ * platform driver in the Allwinner SDK from Allwinner Technology Co., Ltd.
+ * www.allwinnertech.com, by Daniel Wang <danielwang@allwinnertech.com>
+ * though none of the original code was copied.
+ */
+
+#include "opt_bus.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+
+#include <machine/bus.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/ahci/ahci.h>
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/regulator/regulator.h>
+
+/*
+ * Allwinner a1x/a2x/a8x SATA attachment. This is just the AHCI register
+ * set with a few extra implementation-specific registers that need to
+ * be accounted for. There's only one PHY in the system, and it needs
+ * to be trained to bring the link up. In addition, there's some DMA
+ * specific things that need to be done as well. These things are also
+ * just about completely undocumented, except in ugly code in the Linux
+ * SDK Allwinner releases.
+ */
+
+/* BITx -- Unknown bit that needs to be set/cleared at position x */
+/* UFx -- Uknown multi-bit field frobbed during init */
+#define AHCI_BISTAFR 0x00A0
+#define AHCI_BISTCR 0x00A4
+#define AHCI_BISTFCTR 0x00A8
+#define AHCI_BISTSR 0x00AC
+#define AHCI_BISTDECR 0x00B0
+#define AHCI_DIAGNR 0x00B4
+#define AHCI_DIAGNR1 0x00B8
+#define AHCI_OOBR 0x00BC
+#define AHCI_PHYCS0R 0x00C0
+/* Bits 0..17 are a mystery */
+#define PHYCS0R_BIT18 (1 << 18)
+#define PHYCS0R_POWER_ENABLE (1 << 19)
+#define PHYCS0R_UF1_MASK (7 << 20) /* Unknown Field 1 */
+#define PHYCS0R_UF1_INIT (3 << 20)
+#define PHYCS0R_BIT23 (1 << 23)
+#define PHYCS0R_UF2_MASK (7 << 24) /* Uknown Field 2 */
+#define PHYCS0R_UF2_INIT (5 << 24)
+/* Bit 27 mystery */
+#define PHYCS0R_POWER_STATUS_MASK (7 << 28)
+#define PHYCS0R_PS_GOOD (2 << 28)
+/* Bit 31 mystery */
+#define AHCI_PHYCS1R 0x00C4
+/* Bits 0..5 are a mystery */
+#define PHYCS1R_UF1_MASK (3 << 6)
+#define PHYCS1R_UF1_INIT (2 << 6)
+#define PHYCS1R_UF2_MASK (0x1f << 8)
+#define PHYCS1R_UF2_INIT (6 << 8)
+/* Bits 13..14 are a mystery */
+#define PHYCS1R_BIT15 (1 << 15)
+#define PHYCS1R_UF3_MASK (3 << 16)
+#define PHYCS1R_UF3_INIT (2 << 16)
+/* Bit 18 mystery */
+#define PHYCS1R_HIGHZ (1 << 19)
+/* Bits 20..27 mystery */
+#define PHYCS1R_BIT28 (1 << 28)
+/* Bits 29..31 mystery */
+#define AHCI_PHYCS2R 0x00C8
+/* bits 0..4 mystery */
+#define PHYCS2R_UF1_MASK (0x1f << 5)
+#define PHYCS2R_UF1_INIT (0x19 << 5)
+/* Bits 10..23 mystery */
+#define PHYCS2R_CALIBRATE (1 << 24)
+/* Bits 25..31 mystery */
+#define AHCI_TIMER1MS 0x00E0
+#define AHCI_GPARAM1R 0x00E8
+#define AHCI_GPARAM2R 0x00EC
+#define AHCI_PPARAMR 0x00F0
+#define AHCI_TESTR 0x00F4
+#define AHCI_VERSIONR 0x00F8
+#define AHCI_IDR 0x00FC
+#define AHCI_RWCR 0x00FC
+
+#define AHCI_P0DMACR 0x0070
+#define AHCI_P0PHYCR 0x0078
+#define AHCI_P0PHYSR 0x007C
+
+#define PLL_FREQ 100000000
+
+struct ahci_a10_softc {
+ struct ahci_controller ahci_ctlr;
+ regulator_t ahci_reg;
+ clk_t clk_pll;
+ clk_t clk_gate;
+};
+
+static void inline
+ahci_set(struct resource *m, bus_size_t off, uint32_t set)
+{
+ uint32_t val = ATA_INL(m, off);
+
+ val |= set;
+ ATA_OUTL(m, off, val);
+}
+
+static void inline
+ahci_clr(struct resource *m, bus_size_t off, uint32_t clr)
+{
+ uint32_t val = ATA_INL(m, off);
+
+ val &= ~clr;
+ ATA_OUTL(m, off, val);
+}
+
+static void inline
+ahci_mask_set(struct resource *m, bus_size_t off, uint32_t mask, uint32_t set)
+{
+ uint32_t val = ATA_INL(m, off);
+
+ val &= mask;
+ val |= set;
+ ATA_OUTL(m, off, val);
+}
+
+/*
+ * Should this be phy_reset or phy_init
+ */
+#define PHY_RESET_TIMEOUT 1000
+static void
+ahci_a10_phy_reset(device_t dev)
+{
+ uint32_t to, val;
+ struct ahci_controller *ctlr = device_get_softc(dev);
+
+ /*
+ * Here starts the magic -- most of the comments are based
+ * on guesswork, names of routines and printf error
+ * messages. The code works, but it will do that even if the
+ * comments are 100% BS.
+ */
+
+ /*
+ * Lock out other access while we initialize. Or at least that
+ * seems to be the case based on Linux SDK #defines. Maybe this
+ * put things into reset?
+ */
+ ATA_OUTL(ctlr->r_mem, AHCI_RWCR, 0);
+ DELAY(100);
+
+ /*
+ * Set bit 19 in PHYCS1R. Guessing this disables driving the PHY
+ * port for a bit while we reset things.
+ */
+ ahci_set(ctlr->r_mem, AHCI_PHYCS1R, PHYCS1R_HIGHZ);
+
+ /*
+ * Frob PHYCS0R...
+ */
+ ahci_mask_set(ctlr->r_mem, AHCI_PHYCS0R,
+ ~PHYCS0R_UF2_MASK,
+ PHYCS0R_UF2_INIT | PHYCS0R_BIT23 | PHYCS0R_BIT18);
+
+ /*
+ * Set three fields in PHYCS1R
+ */
+ ahci_mask_set(ctlr->r_mem, AHCI_PHYCS1R,
+ ~(PHYCS1R_UF1_MASK | PHYCS1R_UF2_MASK | PHYCS1R_UF3_MASK),
+ PHYCS1R_UF1_INIT | PHYCS1R_UF2_INIT | PHYCS1R_UF3_INIT);
+
+ /*
+ * Two more mystery bits in PHYCS1R. -- can these be combined above?
+ */
+ ahci_set(ctlr->r_mem, AHCI_PHYCS1R, PHYCS1R_BIT15 | PHYCS1R_BIT28);
+
+ /*
+ * Now clear that first mysery bit. Perhaps this starts
+ * driving the PHY again so we can power it up and start
+ * talking to the SATA drive, if any below.
+ */
+ ahci_clr(ctlr->r_mem, AHCI_PHYCS1R, PHYCS1R_HIGHZ);
+
+ /*
+ * Frob PHYCS0R again...
+ */
+ ahci_mask_set(ctlr->r_mem, AHCI_PHYCS0R,
+ ~PHYCS0R_UF1_MASK, PHYCS0R_UF1_INIT);
+
+ /*
+ * Frob PHYCS2R, because 25 means something?
+ */
+ ahci_mask_set(ctlr->r_mem, AHCI_PHYCS2R, ~PHYCS2R_UF1_MASK,
+ PHYCS2R_UF1_INIT);
+
+ DELAY(100); /* WAG */
+
+ /*
+ * Turn on the power to the PHY and wait for it to report back
+ * good?
+ */
+ ahci_set(ctlr->r_mem, AHCI_PHYCS0R, PHYCS0R_POWER_ENABLE);
+ for (to = PHY_RESET_TIMEOUT; to > 0; to--) {
+ val = ATA_INL(ctlr->r_mem, AHCI_PHYCS0R);
+ if ((val & PHYCS0R_POWER_STATUS_MASK) == PHYCS0R_PS_GOOD)
+ break;
+ DELAY(10);
+ }
+ if (to == 0 && bootverbose)
+ device_printf(dev, "PHY Power Failed PHYCS0R = %#x\n", val);
+
+ /*
+ * Calibrate the clocks between the device and the host. This appears
+ * to be an automated process that clears the bit when it is done.
+ */
+ ahci_set(ctlr->r_mem, AHCI_PHYCS2R, PHYCS2R_CALIBRATE);
+ for (to = PHY_RESET_TIMEOUT; to > 0; to--) {
+ val = ATA_INL(ctlr->r_mem, AHCI_PHYCS2R);
+ if ((val & PHYCS2R_CALIBRATE) == 0)
+ break;
+ DELAY(10);
+ }
+ if (to == 0 && bootverbose)
+ device_printf(dev, "PHY Cal Failed PHYCS2R %#x\n", val);
+
+ /*
+ * OK, let things settle down a bit.
+ */
+ DELAY(1000);
+
+ /*
+ * Go back into normal mode now that we've calibrated the PHY.
+ */
+ ATA_OUTL(ctlr->r_mem, AHCI_RWCR, 7);
+}
+
+static void
+ahci_a10_ch_start(struct ahci_channel *ch)
+{
+ uint32_t reg;
+
+ /*
+ * Magical values from Allwinner SDK, setup the DMA before start
+ * operations on this channel.
+ */
+ reg = ATA_INL(ch->r_mem, AHCI_P0DMACR);
+ reg &= ~0xff00;
+ reg |= 0x4400;
+ ATA_OUTL(ch->r_mem, AHCI_P0DMACR, reg);
+}
+
+static int
+ahci_a10_ctlr_reset(device_t dev)
+{
+
+ ahci_a10_phy_reset(dev);
+
+ return (ahci_ctlr_reset(dev));
+}
+
+static int
+ahci_a10_probe(device_t dev)
+{
+
+ if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-a10-ahci"))
+ return (ENXIO);
+ device_set_desc(dev, "Allwinner Integrated AHCI controller");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ahci_a10_attach(device_t dev)
+{
+ int error;
+ struct ahci_a10_softc *sc;
+ struct ahci_controller *ctlr;
+
+ sc = device_get_softc(dev);
+ ctlr = &sc->ahci_ctlr;
+
+ ctlr->quirks = AHCI_Q_NOPMP;
+ ctlr->vendorid = 0;
+ ctlr->deviceid = 0;
+ ctlr->subvendorid = 0;
+ ctlr->subdeviceid = 0;
+ ctlr->r_rid = 0;
+ if (!(ctlr->r_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &ctlr->r_rid, RF_ACTIVE)))
+ return (ENXIO);
+
+ /* Enable the (optional) regulator */
+ if (regulator_get_by_ofw_property(dev, 0, "target-supply",
+ &sc->ahci_reg) == 0) {
+ error = regulator_enable(sc->ahci_reg);
+ if (error != 0) {
+ device_printf(dev, "Could not enable regulator\n");
+ goto fail;
+ }
+ }
+
+ /* Enable clocks */
+ error = clk_get_by_ofw_index(dev, 0, 0, &sc->clk_gate);
+ if (error != 0) {
+ device_printf(dev, "Cannot get gate clock\n");
+ goto fail;
+ }
+ error = clk_get_by_ofw_index(dev, 0, 1, &sc->clk_pll);
+ if (error != 0) {
+ device_printf(dev, "Cannot get PLL clock\n");
+ goto fail;
+ }
+ error = clk_set_freq(sc->clk_pll, PLL_FREQ, CLK_SET_ROUND_DOWN);
+ if (error != 0) {
+ device_printf(dev, "Cannot set PLL frequency\n");
+ goto fail;
+ }
+ error = clk_enable(sc->clk_pll);
+ if (error != 0) {
+ device_printf(dev, "Cannot enable PLL\n");
+ goto fail;
+ }
+ error = clk_enable(sc->clk_gate);
+ if (error != 0) {
+ device_printf(dev, "Cannot enable clk gate\n");
+ goto fail;
+ }
+
+ /* Reset controller */
+ if ((error = ahci_a10_ctlr_reset(dev)) != 0)
+ goto fail;
+
+ /*
+ * No MSI registers on this platform.
+ */
+ ctlr->msi = 0;
+ ctlr->numirqs = 1;
+
+ /* Channel start callback(). */
+ ctlr->ch_start = ahci_a10_ch_start;
+
+ /*
+ * Note: ahci_attach will release ctlr->r_mem on errors automatically
+ */
+ return (ahci_attach(dev));
+
+fail:
+ if (sc->ahci_reg != NULL)
+ regulator_disable(sc->ahci_reg);
+ if (sc->clk_gate != NULL)
+ clk_release(sc->clk_gate);
+ if (sc->clk_pll != NULL)
+ clk_release(sc->clk_pll);
+ bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem);
+ return (error);
+}
+
+static int
+ahci_a10_detach(device_t dev)
+{
+ struct ahci_a10_softc *sc;
+ struct ahci_controller *ctlr;
+
+ sc = device_get_softc(dev);
+ ctlr = &sc->ahci_ctlr;
+
+ if (sc->ahci_reg != NULL)
+ regulator_disable(sc->ahci_reg);
+ if (sc->clk_gate != NULL)
+ clk_release(sc->clk_gate);
+ if (sc->clk_pll != NULL)
+ clk_release(sc->clk_pll);
+ bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem);
+ return (ahci_detach(dev));
+}
+
+static device_method_t ahci_ata_methods[] = {
+ DEVMETHOD(device_probe, ahci_a10_probe),
+ DEVMETHOD(device_attach, ahci_a10_attach),
+ DEVMETHOD(device_detach, ahci_a10_detach),
+ DEVMETHOD(bus_print_child, ahci_print_child),
+ DEVMETHOD(bus_alloc_resource, ahci_alloc_resource),
+ DEVMETHOD(bus_release_resource, ahci_release_resource),
+ DEVMETHOD(bus_setup_intr, ahci_setup_intr),
+ DEVMETHOD(bus_teardown_intr,ahci_teardown_intr),
+ DEVMETHOD(bus_child_location_str, ahci_child_location_str),
+ DEVMETHOD_END
+};
+
+static driver_t ahci_ata_driver = {
+ "ahci",
+ ahci_ata_methods,
+ sizeof(struct ahci_a10_softc)
+};
+
+DRIVER_MODULE(a10_ahci, simplebus, ahci_ata_driver, ahci_devclass, 0, 0);
diff --git a/sys/arm/allwinner/a10_codec.c b/sys/arm/allwinner/a10_codec.c
new file mode 100644
index 000000000000..0780a7328bbe
--- /dev/null
+++ b/sys/arm/allwinner/a10_codec.c
@@ -0,0 +1,1211 @@
+/*-
+ * Copyright (c) 2014-2016 Jared D. McNeill <jmcneill@invisible.ca>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Allwinner A10/A20 and H3 Audio Codec
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/condvar.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/gpio.h>
+
+#include <machine/bus.h>
+
+#include <dev/sound/pcm/sound.h>
+#include <dev/sound/chip.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/gpio/gpiobusvar.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+
+#include "sunxi_dma_if.h"
+#include "mixer_if.h"
+
+struct a10codec_info;
+
+struct a10codec_config {
+ /* mixer class */
+ struct kobj_class *mixer_class;
+
+ /* toggle DAC/ADC mute */
+ void (*mute)(struct a10codec_info *, int, int);
+
+ /* DRQ types */
+ u_int drqtype_codec;
+ u_int drqtype_sdram;
+
+ /* register map */
+ bus_size_t DPC,
+ DAC_FIFOC,
+ DAC_FIFOS,
+ DAC_TXDATA,
+ ADC_FIFOC,
+ ADC_FIFOS,
+ ADC_RXDATA,
+ DAC_CNT,
+ ADC_CNT;
+};
+
+#define TX_TRIG_LEVEL 0xf
+#define RX_TRIG_LEVEL 0x7
+#define DRQ_CLR_CNT 0x3
+
+#define AC_DAC_DPC(_sc) ((_sc)->cfg->DPC)
+#define DAC_DPC_EN_DA 0x80000000
+#define AC_DAC_FIFOC(_sc) ((_sc)->cfg->DAC_FIFOC)
+#define DAC_FIFOC_FS_SHIFT 29
+#define DAC_FIFOC_FS_MASK (7U << DAC_FIFOC_FS_SHIFT)
+#define DAC_FS_48KHZ 0
+#define DAC_FS_32KHZ 1
+#define DAC_FS_24KHZ 2
+#define DAC_FS_16KHZ 3
+#define DAC_FS_12KHZ 4
+#define DAC_FS_8KHZ 5
+#define DAC_FS_192KHZ 6
+#define DAC_FS_96KHZ 7
+#define DAC_FIFOC_FIFO_MODE_SHIFT 24
+#define DAC_FIFOC_FIFO_MODE_MASK (3U << DAC_FIFOC_FIFO_MODE_SHIFT)
+#define FIFO_MODE_24_31_8 0
+#define FIFO_MODE_16_31_16 0
+#define FIFO_MODE_16_15_0 1
+#define DAC_FIFOC_DRQ_CLR_CNT_SHIFT 21
+#define DAC_FIFOC_DRQ_CLR_CNT_MASK (3U << DAC_FIFOC_DRQ_CLR_CNT_SHIFT)
+#define DAC_FIFOC_TX_TRIG_LEVEL_SHIFT 8
+#define DAC_FIFOC_TX_TRIG_LEVEL_MASK (0x7f << DAC_FIFOC_TX_TRIG_LEVEL_SHIFT)
+#define DAC_FIFOC_MONO_EN (1U << 6)
+#define DAC_FIFOC_TX_BITS (1U << 5)
+#define DAC_FIFOC_DRQ_EN (1U << 4)
+#define DAC_FIFOC_FIFO_FLUSH (1U << 0)
+#define AC_DAC_FIFOS(_sc) ((_sc)->cfg->DAC_FIFOS)
+#define AC_DAC_TXDATA(_sc) ((_sc)->cfg->DAC_TXDATA)
+#define AC_ADC_FIFOC(_sc) ((_sc)->cfg->ADC_FIFOC)
+#define ADC_FIFOC_FS_SHIFT 29
+#define ADC_FIFOC_FS_MASK (7U << ADC_FIFOC_FS_SHIFT)
+#define ADC_FS_48KHZ 0
+#define ADC_FIFOC_EN_AD (1U << 28)
+#define ADC_FIFOC_RX_FIFO_MODE (1U << 24)
+#define ADC_FIFOC_RX_TRIG_LEVEL_SHIFT 8
+#define ADC_FIFOC_RX_TRIG_LEVEL_MASK (0x1f << ADC_FIFOC_RX_TRIG_LEVEL_SHIFT)
+#define ADC_FIFOC_MONO_EN (1U << 7)
+#define ADC_FIFOC_RX_BITS (1U << 6)
+#define ADC_FIFOC_DRQ_EN (1U << 4)
+#define ADC_FIFOC_FIFO_FLUSH (1U << 1)
+#define AC_ADC_FIFOS(_sc) ((_sc)->cfg->ADC_FIFOS)
+#define AC_ADC_RXDATA(_sc) ((_sc)->cfg->ADC_RXDATA)
+#define AC_DAC_CNT(_sc) ((_sc)->cfg->DAC_CNT)
+#define AC_ADC_CNT(_sc) ((_sc)->cfg->ADC_CNT)
+
+static uint32_t a10codec_fmt[] = {
+ SND_FORMAT(AFMT_S16_LE, 1, 0),
+ SND_FORMAT(AFMT_S16_LE, 2, 0),
+ 0
+};
+
+static struct pcmchan_caps a10codec_pcaps = { 8000, 192000, a10codec_fmt, 0 };
+static struct pcmchan_caps a10codec_rcaps = { 8000, 48000, a10codec_fmt, 0 };
+
+struct a10codec_info;
+
+struct a10codec_chinfo {
+ struct snd_dbuf *buffer;
+ struct pcm_channel *channel;
+ struct a10codec_info *parent;
+ bus_dmamap_t dmamap;
+ void *dmaaddr;
+ bus_addr_t physaddr;
+ bus_size_t fifo;
+ device_t dmac;
+ void *dmachan;
+
+ int dir;
+ int run;
+ uint32_t pos;
+ uint32_t format;
+ uint32_t blocksize;
+ uint32_t speed;
+};
+
+struct a10codec_info {
+ device_t dev;
+ struct resource *res[2];
+ struct mtx *lock;
+ bus_dma_tag_t dmat;
+ unsigned dmasize;
+ void *ih;
+
+ struct a10codec_config *cfg;
+
+ struct a10codec_chinfo play;
+ struct a10codec_chinfo rec;
+};
+
+static struct resource_spec a10codec_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+#define CODEC_ANALOG_READ(sc, reg) bus_read_4((sc)->res[1], (reg))
+#define CODEC_ANALOG_WRITE(sc, reg, val) bus_write_4((sc)->res[1], (reg), (val))
+
+#define CODEC_READ(sc, reg) bus_read_4((sc)->res[0], (reg))
+#define CODEC_WRITE(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val))
+
+/*
+ * A10/A20 mixer interface
+ */
+
+#define A10_DAC_ACTL 0x10
+#define A10_DACAREN (1U << 31)
+#define A10_DACALEN (1U << 30)
+#define A10_MIXEN (1U << 29)
+#define A10_DACPAS (1U << 8)
+#define A10_PAMUTE (1U << 6)
+#define A10_PAVOL_SHIFT 0
+#define A10_PAVOL_MASK (0x3f << A10_PAVOL_SHIFT)
+#define A10_ADC_ACTL 0x28
+#define A10_ADCREN (1U << 31)
+#define A10_ADCLEN (1U << 30)
+#define A10_PREG1EN (1U << 29)
+#define A10_PREG2EN (1U << 28)
+#define A10_VMICEN (1U << 27)
+#define A10_ADCG_SHIFT 20
+#define A10_ADCG_MASK (7U << A10_ADCG_SHIFT)
+#define A10_ADCIS_SHIFT 17
+#define A10_ADCIS_MASK (7U << A10_ADCIS_SHIFT)
+#define A10_ADC_IS_LINEIN 0
+#define A10_ADC_IS_FMIN 1
+#define A10_ADC_IS_MIC1 2
+#define A10_ADC_IS_MIC2 3
+#define A10_ADC_IS_MIC1_L_MIC2_R 4
+#define A10_ADC_IS_MIC1_LR_MIC2_LR 5
+#define A10_ADC_IS_OMIX 6
+#define A10_ADC_IS_LINEIN_L_MIC1_R 7
+#define A10_LNRDF (1U << 16)
+#define A10_LNPREG_SHIFT 13
+#define A10_LNPREG_MASK (7U << A10_LNPREG_SHIFT)
+#define A10_PA_EN (1U << 4)
+#define A10_DDE (1U << 3)
+
+static int
+a10_mixer_init(struct snd_mixer *m)
+{
+ struct a10codec_info *sc = mix_getdevinfo(m);
+ uint32_t val;
+
+ mix_setdevs(m, SOUND_MASK_VOLUME | SOUND_MASK_LINE | SOUND_MASK_RECLEV);
+ mix_setrecdevs(m, SOUND_MASK_LINE | SOUND_MASK_LINE1 | SOUND_MASK_MIC);
+
+ /* Unmute input source to PA */
+ val = CODEC_READ(sc, A10_DAC_ACTL);
+ val |= A10_PAMUTE;
+ CODEC_WRITE(sc, A10_DAC_ACTL, val);
+
+ /* Enable PA */
+ val = CODEC_READ(sc, A10_ADC_ACTL);
+ val |= A10_PA_EN;
+ CODEC_WRITE(sc, A10_ADC_ACTL, val);
+
+ return (0);
+}
+
+static const struct a10_mixer {
+ unsigned reg;
+ unsigned mask;
+ unsigned shift;
+} a10_mixers[SOUND_MIXER_NRDEVICES] = {
+ [SOUND_MIXER_VOLUME] = { A10_DAC_ACTL, A10_PAVOL_MASK,
+ A10_PAVOL_SHIFT },
+ [SOUND_MIXER_LINE] = { A10_ADC_ACTL, A10_LNPREG_MASK,
+ A10_LNPREG_SHIFT },
+ [SOUND_MIXER_RECLEV] = { A10_ADC_ACTL, A10_ADCG_MASK,
+ A10_ADCG_SHIFT },
+};
+
+static int
+a10_mixer_set(struct snd_mixer *m, unsigned dev, unsigned left,
+ unsigned right)
+{
+ struct a10codec_info *sc = mix_getdevinfo(m);
+ uint32_t val;
+ unsigned nvol, max;
+
+ max = a10_mixers[dev].mask >> a10_mixers[dev].shift;
+ nvol = (left * max) / 100;
+
+ val = CODEC_READ(sc, a10_mixers[dev].reg);
+ val &= ~a10_mixers[dev].mask;
+ val |= (nvol << a10_mixers[dev].shift);
+ CODEC_WRITE(sc, a10_mixers[dev].reg, val);
+
+ left = right = (left * 100) / max;
+ return (left | (right << 8));
+}
+
+static uint32_t
+a10_mixer_setrecsrc(struct snd_mixer *m, uint32_t src)
+{
+ struct a10codec_info *sc = mix_getdevinfo(m);
+ uint32_t val;
+
+ val = CODEC_READ(sc, A10_ADC_ACTL);
+
+ switch (src) {
+ case SOUND_MASK_LINE: /* line-in */
+ val &= ~A10_ADCIS_MASK;
+ val |= (A10_ADC_IS_LINEIN << A10_ADCIS_SHIFT);
+ break;
+ case SOUND_MASK_MIC: /* MIC1 */
+ val &= ~A10_ADCIS_MASK;
+ val |= (A10_ADC_IS_MIC1 << A10_ADCIS_SHIFT);
+ break;
+ case SOUND_MASK_LINE1: /* MIC2 */
+ val &= ~A10_ADCIS_MASK;
+ val |= (A10_ADC_IS_MIC2 << A10_ADCIS_SHIFT);
+ break;
+ default:
+ break;
+ }
+
+ CODEC_WRITE(sc, A10_ADC_ACTL, val);
+
+ switch ((val & A10_ADCIS_MASK) >> A10_ADCIS_SHIFT) {
+ case A10_ADC_IS_LINEIN:
+ return (SOUND_MASK_LINE);
+ case A10_ADC_IS_MIC1:
+ return (SOUND_MASK_MIC);
+ case A10_ADC_IS_MIC2:
+ return (SOUND_MASK_LINE1);
+ default:
+ return (0);
+ }
+}
+
+static void
+a10_mute(struct a10codec_info *sc, int mute, int dir)
+{
+ uint32_t val;
+
+ if (dir == PCMDIR_PLAY) {
+ val = CODEC_READ(sc, A10_DAC_ACTL);
+ if (mute) {
+ /* Disable DAC analog l/r channels and output mixer */
+ val &= ~A10_DACAREN;
+ val &= ~A10_DACALEN;
+ val &= ~A10_DACPAS;
+ } else {
+ /* Enable DAC analog l/r channels and output mixer */
+ val |= A10_DACAREN;
+ val |= A10_DACALEN;
+ val |= A10_DACPAS;
+ }
+ CODEC_WRITE(sc, A10_DAC_ACTL, val);
+ } else {
+ val = CODEC_READ(sc, A10_ADC_ACTL);
+ if (mute) {
+ /* Disable ADC analog l/r channels, MIC1 preamp,
+ * and VMIC pin voltage
+ */
+ val &= ~A10_ADCREN;
+ val &= ~A10_ADCLEN;
+ val &= ~A10_PREG1EN;
+ val &= ~A10_VMICEN;
+ } else {
+ /* Enable ADC analog l/r channels, MIC1 preamp,
+ * and VMIC pin voltage
+ */
+ val |= A10_ADCREN;
+ val |= A10_ADCLEN;
+ val |= A10_PREG1EN;
+ val |= A10_VMICEN;
+ }
+ CODEC_WRITE(sc, A10_ADC_ACTL, val);
+ }
+}
+
+static kobj_method_t a10_mixer_methods[] = {
+ KOBJMETHOD(mixer_init, a10_mixer_init),
+ KOBJMETHOD(mixer_set, a10_mixer_set),
+ KOBJMETHOD(mixer_setrecsrc, a10_mixer_setrecsrc),
+ KOBJMETHOD_END
+};
+MIXER_DECLARE(a10_mixer);
+
+/*
+ * H3 mixer interface
+ */
+
+#define H3_PR_CFG 0x00
+#define H3_AC_PR_RST (1 << 28)
+#define H3_AC_PR_RW (1 << 24)
+#define H3_AC_PR_ADDR_SHIFT 16
+#define H3_AC_PR_ADDR_MASK (0x1f << H3_AC_PR_ADDR_SHIFT)
+#define H3_ACDA_PR_WDAT_SHIFT 8
+#define H3_ACDA_PR_WDAT_MASK (0xff << H3_ACDA_PR_WDAT_SHIFT)
+#define H3_ACDA_PR_RDAT_SHIFT 0
+#define H3_ACDA_PR_RDAT_MASK (0xff << H3_ACDA_PR_RDAT_SHIFT)
+
+#define H3_LOMIXSC 0x01
+#define H3_LOMIXSC_LDAC (1 << 1)
+#define H3_ROMIXSC 0x02
+#define H3_ROMIXSC_RDAC (1 << 1)
+#define H3_DAC_PA_SRC 0x03
+#define H3_DACAREN (1 << 7)
+#define H3_DACALEN (1 << 6)
+#define H3_RMIXEN (1 << 5)
+#define H3_LMIXEN (1 << 4)
+#define H3_LINEIN_GCTR 0x05
+#define H3_LINEING_SHIFT 4
+#define H3_LINEING_MASK (0x7 << H3_LINEING_SHIFT)
+#define H3_MIC_GCTR 0x06
+#define H3_MIC1_GAIN_SHIFT 4
+#define H3_MIC1_GAIN_MASK (0x7 << H3_MIC1_GAIN_SHIFT)
+#define H3_MIC2_GAIN_SHIFT 0
+#define H3_MIC2_GAIN_MASK (0x7 << H3_MIC2_GAIN_SHIFT)
+#define H3_PAEN_CTR 0x07
+#define H3_LINEOUTEN (1 << 7)
+#define H3_LINEOUT_VOLC 0x09
+#define H3_LINEOUTVOL_SHIFT 3
+#define H3_LINEOUTVOL_MASK (0x1f << H3_LINEOUTVOL_SHIFT)
+#define H3_MIC2G_LINEOUT_CTR 0x0a
+#define H3_LINEOUT_LSEL (1 << 3)
+#define H3_LINEOUT_RSEL (1 << 2)
+#define H3_LADCMIXSC 0x0c
+#define H3_RADCMIXSC 0x0d
+#define H3_ADCMIXSC_MIC1 (1 << 6)
+#define H3_ADCMIXSC_MIC2 (1 << 5)
+#define H3_ADCMIXSC_LINEIN (1 << 2)
+#define H3_ADCMIXSC_OMIXER (3 << 0)
+#define H3_ADC_AP_EN 0x0f
+#define H3_ADCREN (1 << 7)
+#define H3_ADCLEN (1 << 6)
+#define H3_ADCG_SHIFT 0
+#define H3_ADCG_MASK (0x7 << H3_ADCG_SHIFT)
+
+static u_int
+h3_pr_read(struct a10codec_info *sc, u_int addr)
+{
+ uint32_t val;
+
+ /* Read current value */
+ val = CODEC_ANALOG_READ(sc, H3_PR_CFG);
+
+ /* De-assert reset */
+ val |= H3_AC_PR_RST;
+ CODEC_ANALOG_WRITE(sc, H3_PR_CFG, val);
+
+ /* Read mode */
+ val &= ~H3_AC_PR_RW;
+ CODEC_ANALOG_WRITE(sc, H3_PR_CFG, val);
+
+ /* Set address */
+ val &= ~H3_AC_PR_ADDR_MASK;
+ val |= (addr << H3_AC_PR_ADDR_SHIFT);
+ CODEC_ANALOG_WRITE(sc, H3_PR_CFG, val);
+
+ /* Read data */
+ return (CODEC_ANALOG_READ(sc , H3_PR_CFG) & H3_ACDA_PR_RDAT_MASK);
+}
+
+static void
+h3_pr_write(struct a10codec_info *sc, u_int addr, u_int data)
+{
+ uint32_t val;
+
+ /* Read current value */
+ val = CODEC_ANALOG_READ(sc, H3_PR_CFG);
+
+ /* De-assert reset */
+ val |= H3_AC_PR_RST;
+ CODEC_ANALOG_WRITE(sc, H3_PR_CFG, val);
+
+ /* Set address */
+ val &= ~H3_AC_PR_ADDR_MASK;
+ val |= (addr << H3_AC_PR_ADDR_SHIFT);
+ CODEC_ANALOG_WRITE(sc, H3_PR_CFG, val);
+
+ /* Write data */
+ val &= ~H3_ACDA_PR_WDAT_MASK;
+ val |= (data << H3_ACDA_PR_WDAT_SHIFT);
+ CODEC_ANALOG_WRITE(sc, H3_PR_CFG, val);
+
+ /* Write mode */
+ val |= H3_AC_PR_RW;
+ CODEC_ANALOG_WRITE(sc, H3_PR_CFG, val);
+}
+
+static void
+h3_pr_set_clear(struct a10codec_info *sc, u_int addr, u_int set, u_int clr)
+{
+ u_int old, new;
+
+ old = h3_pr_read(sc, addr);
+ new = set | (old & ~clr);
+ h3_pr_write(sc, addr, new);
+}
+
+static int
+h3_mixer_init(struct snd_mixer *m)
+{
+ int rid=1;
+ pcell_t reg[2];
+ phandle_t analogref;
+ struct a10codec_info *sc = mix_getdevinfo(m);
+
+ if (OF_getencprop(ofw_bus_get_node(sc->dev), "allwinner,codec-analog-controls",
+ &analogref, sizeof(analogref)) <= 0) {
+ return (ENXIO);
+ }
+
+ if (OF_getencprop(OF_node_from_xref(analogref), "reg",
+ reg, sizeof(reg)) <= 0) {
+ return (ENXIO);
+ }
+
+ sc->res[1] = bus_alloc_resource(sc->dev, SYS_RES_MEMORY, &rid, reg[0],
+ reg[0]+reg[1], reg[1], RF_ACTIVE );
+
+ if (sc->res[1] == NULL) {
+ return (ENXIO);
+ }
+
+ mix_setdevs(m, SOUND_MASK_PCM | SOUND_MASK_VOLUME | SOUND_MASK_RECLEV |
+ SOUND_MASK_MIC | SOUND_MASK_LINE | SOUND_MASK_LINE1);
+ mix_setrecdevs(m, SOUND_MASK_MIC | SOUND_MASK_LINE | SOUND_MASK_LINE1 |
+ SOUND_MASK_IMIX);
+
+ pcm_setflags(sc->dev, pcm_getflags(sc->dev) | SD_F_SOFTPCMVOL);
+
+ /* Right & Left LINEOUT enable */
+ h3_pr_set_clear(sc, H3_PAEN_CTR, H3_LINEOUTEN, 0);
+ h3_pr_set_clear(sc, H3_MIC2G_LINEOUT_CTR,
+ H3_LINEOUT_LSEL | H3_LINEOUT_RSEL, 0);
+
+ return (0);
+}
+
+static const struct h3_mixer {
+ unsigned reg;
+ unsigned mask;
+ unsigned shift;
+} h3_mixers[SOUND_MIXER_NRDEVICES] = {
+ [SOUND_MIXER_VOLUME] = { H3_LINEOUT_VOLC, H3_LINEOUTVOL_MASK,
+ H3_LINEOUTVOL_SHIFT },
+ [SOUND_MIXER_RECLEV] = { H3_ADC_AP_EN, H3_ADCG_MASK,
+ H3_ADCG_SHIFT },
+ [SOUND_MIXER_LINE] = { H3_LINEIN_GCTR, H3_LINEING_MASK,
+ H3_LINEING_SHIFT },
+ [SOUND_MIXER_MIC] = { H3_MIC_GCTR, H3_MIC1_GAIN_MASK,
+ H3_MIC1_GAIN_SHIFT },
+ [SOUND_MIXER_LINE1] = { H3_MIC_GCTR, H3_MIC2_GAIN_MASK,
+ H3_MIC2_GAIN_SHIFT },
+};
+
+static int
+h3_mixer_set(struct snd_mixer *m, unsigned dev, unsigned left,
+ unsigned right)
+{
+ struct a10codec_info *sc = mix_getdevinfo(m);
+ unsigned nvol, max;
+
+ max = h3_mixers[dev].mask >> h3_mixers[dev].shift;
+ nvol = (left * max) / 100;
+
+ h3_pr_set_clear(sc, h3_mixers[dev].reg,
+ nvol << h3_mixers[dev].shift, h3_mixers[dev].mask);
+
+ left = right = (left * 100) / max;
+ return (left | (right << 8));
+}
+
+static uint32_t
+h3_mixer_setrecsrc(struct snd_mixer *m, uint32_t src)
+{
+ struct a10codec_info *sc = mix_getdevinfo(m);
+ uint32_t val;
+
+ val = 0;
+ src &= (SOUND_MASK_LINE | SOUND_MASK_MIC |
+ SOUND_MASK_LINE1 | SOUND_MASK_IMIX);
+
+ if ((src & SOUND_MASK_LINE) != 0) /* line-in */
+ val |= H3_ADCMIXSC_LINEIN;
+ if ((src & SOUND_MASK_MIC) != 0) /* MIC1 */
+ val |= H3_ADCMIXSC_MIC1;
+ if ((src & SOUND_MASK_LINE1) != 0) /* MIC2 */
+ val |= H3_ADCMIXSC_MIC2;
+ if ((src & SOUND_MASK_IMIX) != 0) /* l/r output mixer */
+ val |= H3_ADCMIXSC_OMIXER;
+
+ h3_pr_write(sc, H3_LADCMIXSC, val);
+ h3_pr_write(sc, H3_RADCMIXSC, val);
+
+ return (src);
+}
+
+static void
+h3_mute(struct a10codec_info *sc, int mute, int dir)
+{
+ if (dir == PCMDIR_PLAY) {
+ if (mute) {
+ /* Mute DAC l/r channels to output mixer */
+ h3_pr_set_clear(sc, H3_LOMIXSC, 0, H3_LOMIXSC_LDAC);
+ h3_pr_set_clear(sc, H3_ROMIXSC, 0, H3_ROMIXSC_RDAC);
+ /* Disable DAC analog l/r channels and output mixer */
+ h3_pr_set_clear(sc, H3_DAC_PA_SRC,
+ 0, H3_DACAREN | H3_DACALEN | H3_RMIXEN | H3_LMIXEN);
+ } else {
+ /* Enable DAC analog l/r channels and output mixer */
+ h3_pr_set_clear(sc, H3_DAC_PA_SRC,
+ H3_DACAREN | H3_DACALEN | H3_RMIXEN | H3_LMIXEN, 0);
+ /* Unmute DAC l/r channels to output mixer */
+ h3_pr_set_clear(sc, H3_LOMIXSC, H3_LOMIXSC_LDAC, 0);
+ h3_pr_set_clear(sc, H3_ROMIXSC, H3_ROMIXSC_RDAC, 0);
+ }
+ } else {
+ if (mute) {
+ /* Disable ADC analog l/r channels */
+ h3_pr_set_clear(sc, H3_ADC_AP_EN,
+ 0, H3_ADCREN | H3_ADCLEN);
+ } else {
+ /* Enable ADC analog l/r channels */
+ h3_pr_set_clear(sc, H3_ADC_AP_EN,
+ H3_ADCREN | H3_ADCLEN, 0);
+ }
+ }
+}
+
+static kobj_method_t h3_mixer_methods[] = {
+ KOBJMETHOD(mixer_init, h3_mixer_init),
+ KOBJMETHOD(mixer_set, h3_mixer_set),
+ KOBJMETHOD(mixer_setrecsrc, h3_mixer_setrecsrc),
+ KOBJMETHOD_END
+};
+MIXER_DECLARE(h3_mixer);
+
+/*
+ * Channel interface
+ */
+
+static void
+a10codec_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+ struct a10codec_chinfo *ch = arg;
+
+ if (error != 0)
+ return;
+
+ ch->physaddr = segs[0].ds_addr;
+}
+
+static void
+a10codec_transfer(struct a10codec_chinfo *ch)
+{
+ bus_addr_t src, dst;
+ int error;
+
+ if (ch->dir == PCMDIR_PLAY) {
+ src = ch->physaddr + ch->pos;
+ dst = ch->fifo;
+ } else {
+ src = ch->fifo;
+ dst = ch->physaddr + ch->pos;
+ }
+
+ error = SUNXI_DMA_TRANSFER(ch->dmac, ch->dmachan, src, dst,
+ ch->blocksize);
+ if (error) {
+ ch->run = 0;
+ device_printf(ch->parent->dev, "DMA transfer failed: %d\n",
+ error);
+ }
+}
+
+static void
+a10codec_dmaconfig(struct a10codec_chinfo *ch)
+{
+ struct a10codec_info *sc = ch->parent;
+ struct sunxi_dma_config conf;
+
+ memset(&conf, 0, sizeof(conf));
+ conf.src_width = conf.dst_width = 16;
+ conf.src_burst_len = conf.dst_burst_len = 4;
+
+ if (ch->dir == PCMDIR_PLAY) {
+ conf.dst_noincr = true;
+ conf.src_drqtype = sc->cfg->drqtype_sdram;
+ conf.dst_drqtype = sc->cfg->drqtype_codec;
+ } else {
+ conf.src_noincr = true;
+ conf.src_drqtype = sc->cfg->drqtype_codec;
+ conf.dst_drqtype = sc->cfg->drqtype_sdram;
+ }
+
+ SUNXI_DMA_SET_CONFIG(ch->dmac, ch->dmachan, &conf);
+}
+
+static void
+a10codec_dmaintr(void *priv)
+{
+ struct a10codec_chinfo *ch = priv;
+ unsigned bufsize;
+
+ bufsize = sndbuf_getsize(ch->buffer);
+
+ ch->pos += ch->blocksize;
+ if (ch->pos >= bufsize)
+ ch->pos -= bufsize;
+
+ if (ch->run) {
+ chn_intr(ch->channel);
+ a10codec_transfer(ch);
+ }
+}
+
+static unsigned
+a10codec_fs(struct a10codec_chinfo *ch)
+{
+ switch (ch->speed) {
+ case 48000:
+ return (DAC_FS_48KHZ);
+ case 24000:
+ return (DAC_FS_24KHZ);
+ case 12000:
+ return (DAC_FS_12KHZ);
+ case 192000:
+ return (DAC_FS_192KHZ);
+ case 32000:
+ return (DAC_FS_32KHZ);
+ case 16000:
+ return (DAC_FS_16KHZ);
+ case 8000:
+ return (DAC_FS_8KHZ);
+ case 96000:
+ return (DAC_FS_96KHZ);
+ default:
+ return (DAC_FS_48KHZ);
+ }
+}
+
+static void
+a10codec_start(struct a10codec_chinfo *ch)
+{
+ struct a10codec_info *sc = ch->parent;
+ uint32_t val;
+
+ ch->pos = 0;
+
+ if (ch->dir == PCMDIR_PLAY) {
+ /* Flush DAC FIFO */
+ CODEC_WRITE(sc, AC_DAC_FIFOC(sc), DAC_FIFOC_FIFO_FLUSH);
+
+ /* Clear DAC FIFO status */
+ CODEC_WRITE(sc, AC_DAC_FIFOS(sc),
+ CODEC_READ(sc, AC_DAC_FIFOS(sc)));
+
+ /* Unmute output */
+ sc->cfg->mute(sc, 0, ch->dir);
+
+ /* Configure DAC DMA channel */
+ a10codec_dmaconfig(ch);
+
+ /* Configure DAC FIFO */
+ CODEC_WRITE(sc, AC_DAC_FIFOC(sc),
+ (AFMT_CHANNEL(ch->format) == 1 ? DAC_FIFOC_MONO_EN : 0) |
+ (a10codec_fs(ch) << DAC_FIFOC_FS_SHIFT) |
+ (FIFO_MODE_16_15_0 << DAC_FIFOC_FIFO_MODE_SHIFT) |
+ (DRQ_CLR_CNT << DAC_FIFOC_DRQ_CLR_CNT_SHIFT) |
+ (TX_TRIG_LEVEL << DAC_FIFOC_TX_TRIG_LEVEL_SHIFT));
+
+ /* Enable DAC DRQ */
+ val = CODEC_READ(sc, AC_DAC_FIFOC(sc));
+ val |= DAC_FIFOC_DRQ_EN;
+ CODEC_WRITE(sc, AC_DAC_FIFOC(sc), val);
+ } else {
+ /* Flush ADC FIFO */
+ CODEC_WRITE(sc, AC_ADC_FIFOC(sc), ADC_FIFOC_FIFO_FLUSH);
+
+ /* Clear ADC FIFO status */
+ CODEC_WRITE(sc, AC_ADC_FIFOS(sc),
+ CODEC_READ(sc, AC_ADC_FIFOS(sc)));
+
+ /* Unmute input */
+ sc->cfg->mute(sc, 0, ch->dir);
+
+ /* Configure ADC DMA channel */
+ a10codec_dmaconfig(ch);
+
+ /* Configure ADC FIFO */
+ CODEC_WRITE(sc, AC_ADC_FIFOC(sc),
+ ADC_FIFOC_EN_AD |
+ ADC_FIFOC_RX_FIFO_MODE |
+ (AFMT_CHANNEL(ch->format) == 1 ? ADC_FIFOC_MONO_EN : 0) |
+ (a10codec_fs(ch) << ADC_FIFOC_FS_SHIFT) |
+ (RX_TRIG_LEVEL << ADC_FIFOC_RX_TRIG_LEVEL_SHIFT));
+
+ /* Enable ADC DRQ */
+ val = CODEC_READ(sc, AC_ADC_FIFOC(sc));
+ val |= ADC_FIFOC_DRQ_EN;
+ CODEC_WRITE(sc, AC_ADC_FIFOC(sc), val);
+ }
+
+ /* Start DMA transfer */
+ a10codec_transfer(ch);
+}
+
+static void
+a10codec_stop(struct a10codec_chinfo *ch)
+{
+ struct a10codec_info *sc = ch->parent;
+
+ /* Disable DMA channel */
+ SUNXI_DMA_HALT(ch->dmac, ch->dmachan);
+
+ sc->cfg->mute(sc, 1, ch->dir);
+
+ if (ch->dir == PCMDIR_PLAY) {
+ /* Disable DAC DRQ */
+ CODEC_WRITE(sc, AC_DAC_FIFOC(sc), 0);
+ } else {
+ /* Disable ADC DRQ */
+ CODEC_WRITE(sc, AC_ADC_FIFOC(sc), 0);
+ }
+}
+
+static void *
+a10codec_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
+ struct pcm_channel *c, int dir)
+{
+ struct a10codec_info *sc = devinfo;
+ struct a10codec_chinfo *ch = dir == PCMDIR_PLAY ? &sc->play : &sc->rec;
+ phandle_t xref;
+ pcell_t *cells;
+ int ncells, error;
+
+ error = ofw_bus_parse_xref_list_alloc(ofw_bus_get_node(sc->dev),
+ "dmas", "#dma-cells", dir == PCMDIR_PLAY ? 1 : 0,
+ &xref, &ncells, &cells);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot parse 'dmas' property\n");
+ return (NULL);
+ }
+ OF_prop_free(cells);
+
+ ch->parent = sc;
+ ch->channel = c;
+ ch->buffer = b;
+ ch->dir = dir;
+ ch->fifo = rman_get_start(sc->res[0]) +
+ (dir == PCMDIR_REC ? AC_ADC_RXDATA(sc) : AC_DAC_TXDATA(sc));
+
+ ch->dmac = OF_device_from_xref(xref);
+ if (ch->dmac == NULL) {
+ device_printf(sc->dev, "cannot find DMA controller\n");
+ device_printf(sc->dev, "xref = 0x%x\n", (u_int)xref);
+ return (NULL);
+ }
+ ch->dmachan = SUNXI_DMA_ALLOC(ch->dmac, false, a10codec_dmaintr, ch);
+ if (ch->dmachan == NULL) {
+ device_printf(sc->dev, "cannot allocate DMA channel\n");
+ return (NULL);
+ }
+
+ error = bus_dmamem_alloc(sc->dmat, &ch->dmaaddr,
+ BUS_DMA_NOWAIT | BUS_DMA_COHERENT, &ch->dmamap);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot allocate channel buffer\n");
+ return (NULL);
+ }
+ error = bus_dmamap_load(sc->dmat, ch->dmamap, ch->dmaaddr,
+ sc->dmasize, a10codec_dmamap_cb, ch, BUS_DMA_NOWAIT);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot load DMA map\n");
+ return (NULL);
+ }
+ memset(ch->dmaaddr, 0, sc->dmasize);
+
+ if (sndbuf_setup(ch->buffer, ch->dmaaddr, sc->dmasize) != 0) {
+ device_printf(sc->dev, "cannot setup sndbuf\n");
+ return (NULL);
+ }
+
+ return (ch);
+}
+
+static int
+a10codec_chan_free(kobj_t obj, void *data)
+{
+ struct a10codec_chinfo *ch = data;
+ struct a10codec_info *sc = ch->parent;
+
+ SUNXI_DMA_FREE(ch->dmac, ch->dmachan);
+ bus_dmamap_unload(sc->dmat, ch->dmamap);
+ bus_dmamem_free(sc->dmat, ch->dmaaddr, ch->dmamap);
+
+ return (0);
+}
+
+static int
+a10codec_chan_setformat(kobj_t obj, void *data, uint32_t format)
+{
+ struct a10codec_chinfo *ch = data;
+
+ ch->format = format;
+
+ return (0);
+}
+
+static uint32_t
+a10codec_chan_setspeed(kobj_t obj, void *data, uint32_t speed)
+{
+ struct a10codec_chinfo *ch = data;
+
+ /*
+ * The codec supports full duplex operation but both DAC and ADC
+ * use the same source clock (PLL2). Limit the available speeds to
+ * those supported by a 24576000 Hz input.
+ */
+ switch (speed) {
+ case 8000:
+ case 12000:
+ case 16000:
+ case 24000:
+ case 32000:
+ case 48000:
+ ch->speed = speed;
+ break;
+ case 96000:
+ case 192000:
+ /* 96 KHz / 192 KHz mode only supported for playback */
+ if (ch->dir == PCMDIR_PLAY) {
+ ch->speed = speed;
+ } else {
+ ch->speed = 48000;
+ }
+ break;
+ case 44100:
+ ch->speed = 48000;
+ break;
+ case 22050:
+ ch->speed = 24000;
+ break;
+ case 11025:
+ ch->speed = 12000;
+ break;
+ default:
+ ch->speed = 48000;
+ break;
+ }
+
+ return (ch->speed);
+}
+
+static uint32_t
+a10codec_chan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
+{
+ struct a10codec_chinfo *ch = data;
+
+ ch->blocksize = blocksize & ~3;
+
+ return (ch->blocksize);
+}
+
+static int
+a10codec_chan_trigger(kobj_t obj, void *data, int go)
+{
+ struct a10codec_chinfo *ch = data;
+ struct a10codec_info *sc = ch->parent;
+
+ if (!PCMTRIG_COMMON(go))
+ return (0);
+
+ snd_mtxlock(sc->lock);
+ switch (go) {
+ case PCMTRIG_START:
+ ch->run = 1;
+ a10codec_stop(ch);
+ a10codec_start(ch);
+ break;
+ case PCMTRIG_STOP:
+ case PCMTRIG_ABORT:
+ ch->run = 0;
+ a10codec_stop(ch);
+ break;
+ default:
+ break;
+ }
+ snd_mtxunlock(sc->lock);
+
+ return (0);
+}
+
+static uint32_t
+a10codec_chan_getptr(kobj_t obj, void *data)
+{
+ struct a10codec_chinfo *ch = data;
+
+ return (ch->pos);
+}
+
+static struct pcmchan_caps *
+a10codec_chan_getcaps(kobj_t obj, void *data)
+{
+ struct a10codec_chinfo *ch = data;
+
+ if (ch->dir == PCMDIR_PLAY) {
+ return (&a10codec_pcaps);
+ } else {
+ return (&a10codec_rcaps);
+ }
+}
+
+static kobj_method_t a10codec_chan_methods[] = {
+ KOBJMETHOD(channel_init, a10codec_chan_init),
+ KOBJMETHOD(channel_free, a10codec_chan_free),
+ KOBJMETHOD(channel_setformat, a10codec_chan_setformat),
+ KOBJMETHOD(channel_setspeed, a10codec_chan_setspeed),
+ KOBJMETHOD(channel_setblocksize, a10codec_chan_setblocksize),
+ KOBJMETHOD(channel_trigger, a10codec_chan_trigger),
+ KOBJMETHOD(channel_getptr, a10codec_chan_getptr),
+ KOBJMETHOD(channel_getcaps, a10codec_chan_getcaps),
+ KOBJMETHOD_END
+};
+CHANNEL_DECLARE(a10codec_chan);
+
+/*
+ * Device interface
+ */
+
+static const struct a10codec_config a10_config = {
+ .mixer_class = &a10_mixer_class,
+ .mute = a10_mute,
+ .drqtype_codec = 19,
+ .drqtype_sdram = 22,
+ .DPC = 0x00,
+ .DAC_FIFOC = 0x04,
+ .DAC_FIFOS = 0x08,
+ .DAC_TXDATA = 0x0c,
+ .ADC_FIFOC = 0x1c,
+ .ADC_FIFOS = 0x20,
+ .ADC_RXDATA = 0x24,
+ .DAC_CNT = 0x30,
+ .ADC_CNT = 0x34,
+};
+
+static const struct a10codec_config h3_config = {
+ .mixer_class = &h3_mixer_class,
+ .mute = h3_mute,
+ .drqtype_codec = 15,
+ .drqtype_sdram = 1,
+ .DPC = 0x00,
+ .DAC_FIFOC = 0x04,
+ .DAC_FIFOS = 0x08,
+ .DAC_TXDATA = 0x20,
+ .ADC_FIFOC = 0x10,
+ .ADC_FIFOS = 0x14,
+ .ADC_RXDATA = 0x18,
+ .DAC_CNT = 0x40,
+ .ADC_CNT = 0x44,
+};
+
+static struct ofw_compat_data compat_data[] = {
+ { "allwinner,sun4i-a10-codec", (uintptr_t)&a10_config },
+ { "allwinner,sun7i-a20-codec", (uintptr_t)&a10_config },
+ { "allwinner,sun8i-h3-codec", (uintptr_t)&h3_config },
+ { NULL, 0 }
+};
+
+static int
+a10codec_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner Audio Codec");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+a10codec_attach(device_t dev)
+{
+ struct a10codec_info *sc;
+ char status[SND_STATUSLEN];
+ struct gpiobus_pin *pa_pin;
+ phandle_t node;
+ clk_t clk_bus, clk_codec;
+ hwreset_t rst;
+ uint32_t val;
+ int error;
+
+ node = ofw_bus_get_node(dev);
+
+ sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO);
+ sc->cfg = (void *)ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+ sc->dev = dev;
+ sc->lock = snd_mtxcreate(device_get_nameunit(dev), "a10codec softc");
+
+ if (bus_alloc_resources(dev, a10codec_spec, sc->res)) {
+ device_printf(dev, "cannot allocate resources for device\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ sc->dmasize = 131072;
+ error = bus_dma_tag_create(
+ bus_get_dma_tag(dev),
+ 4, sc->dmasize, /* alignment, boundary */
+ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ sc->dmasize, 1, /* maxsize, nsegs */
+ sc->dmasize, 0, /* maxsegsize, flags */
+ NULL, NULL, /* lockfunc, lockarg */
+ &sc->dmat);
+ if (error != 0) {
+ device_printf(dev, "cannot create DMA tag\n");
+ goto fail;
+ }
+
+ /* Get clocks */
+ if (clk_get_by_ofw_name(dev, 0, "apb", &clk_bus) != 0 &&
+ clk_get_by_ofw_name(dev, 0, "ahb", &clk_bus) != 0) {
+ device_printf(dev, "cannot find bus clock\n");
+ goto fail;
+ }
+ if (clk_get_by_ofw_name(dev, 0, "codec", &clk_codec) != 0) {
+ device_printf(dev, "cannot find codec clock\n");
+ goto fail;
+ }
+
+ /* Gating bus clock for codec */
+ if (clk_enable(clk_bus) != 0) {
+ device_printf(dev, "cannot enable bus clock\n");
+ goto fail;
+ }
+ /* Activate audio codec clock. According to the A10 and A20 user
+ * manuals, Audio_pll can be either 24.576MHz or 22.5792MHz. Most
+ * audio sampling rates require an 24.576MHz input clock with the
+ * exception of 44.1kHz, 22.05kHz, and 11.025kHz. Unfortunately,
+ * both capture and playback use the same clock source so to
+ * safely support independent full duplex operation, we use a fixed
+ * 24.576MHz clock source and don't advertise native support for
+ * the three sampling rates that require a 22.5792MHz input.
+ */
+ error = clk_set_freq(clk_codec, 24576000, CLK_SET_ROUND_DOWN);
+ if (error != 0) {
+ device_printf(dev, "cannot set codec clock frequency\n");
+ goto fail;
+ }
+ /* Enable audio codec clock */
+ error = clk_enable(clk_codec);
+ if (error != 0) {
+ device_printf(dev, "cannot enable codec clock\n");
+ goto fail;
+ }
+
+ /* De-assert hwreset */
+ if (hwreset_get_by_ofw_idx(dev, 0, 0, &rst) == 0) {
+ error = hwreset_deassert(rst);
+ if (error != 0) {
+ device_printf(dev, "cannot de-assert reset\n");
+ goto fail;
+ }
+ }
+
+ /* Enable DAC */
+ val = CODEC_READ(sc, AC_DAC_DPC(sc));
+ val |= DAC_DPC_EN_DA;
+ CODEC_WRITE(sc, AC_DAC_DPC(sc), val);
+
+ if (mixer_init(dev, sc->cfg->mixer_class, sc)) {
+ device_printf(dev, "mixer_init failed\n");
+ goto fail;
+ }
+
+ /* Unmute PA */
+ if (gpio_pin_get_by_ofw_property(dev, node, "allwinner,pa-gpios",
+ &pa_pin) == 0) {
+ error = gpio_pin_set_active(pa_pin, 1);
+ if (error != 0)
+ device_printf(dev, "failed to unmute PA\n");
+ }
+
+ pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE);
+
+ if (pcm_register(dev, sc, 1, 1)) {
+ device_printf(dev, "pcm_register failed\n");
+ goto fail;
+ }
+
+ pcm_addchan(dev, PCMDIR_PLAY, &a10codec_chan_class, sc);
+ pcm_addchan(dev, PCMDIR_REC, &a10codec_chan_class, sc);
+
+ snprintf(status, SND_STATUSLEN, "at %s", ofw_bus_get_name(dev));
+ pcm_setstatus(dev, status);
+
+ return (0);
+
+fail:
+ bus_release_resources(dev, a10codec_spec, sc->res);
+ snd_mtxfree(sc->lock);
+ free(sc, M_DEVBUF);
+
+ return (ENXIO);
+}
+
+static device_method_t a10codec_pcm_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, a10codec_probe),
+ DEVMETHOD(device_attach, a10codec_attach),
+
+ DEVMETHOD_END
+};
+
+static driver_t a10codec_pcm_driver = {
+ "pcm",
+ a10codec_pcm_methods,
+ PCM_SOFTC_SIZE,
+};
+
+DRIVER_MODULE(a10codec, simplebus, a10codec_pcm_driver, pcm_devclass, 0, 0);
+MODULE_DEPEND(a10codec, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
+MODULE_VERSION(a10codec, 1);
diff --git a/sys/arm/allwinner/a10_dmac.c b/sys/arm/allwinner/a10_dmac.c
new file mode 100644
index 000000000000..28292ba7e5a4
--- /dev/null
+++ b/sys/arm/allwinner/a10_dmac.c
@@ -0,0 +1,473 @@
+/*-
+ * Copyright (c) 2014-2016 Jared D. McNeill <jmcneill@invisible.ca>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/*
+ * Allwinner A10/A20 DMA controller
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/condvar.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+
+#include <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/allwinner/a10_dmac.h>
+#include <dev/extres/clk/clk.h>
+
+#include "sunxi_dma_if.h"
+
+#define NDMA_CHANNELS 8
+#define DDMA_CHANNELS 8
+
+enum a10dmac_type {
+ CH_NDMA,
+ CH_DDMA
+};
+
+struct a10dmac_softc;
+
+struct a10dmac_channel {
+ struct a10dmac_softc * ch_sc;
+ uint8_t ch_index;
+ enum a10dmac_type ch_type;
+ void (*ch_callback)(void *);
+ void * ch_callbackarg;
+ uint32_t ch_regoff;
+};
+
+struct a10dmac_softc {
+ struct resource * sc_res[2];
+ struct mtx sc_mtx;
+ void * sc_ih;
+
+ struct a10dmac_channel sc_ndma_channels[NDMA_CHANNELS];
+ struct a10dmac_channel sc_ddma_channels[DDMA_CHANNELS];
+};
+
+static struct resource_spec a10dmac_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+#define DMA_READ(sc, reg) bus_read_4((sc)->sc_res[0], (reg))
+#define DMA_WRITE(sc, reg, val) bus_write_4((sc)->sc_res[0], (reg), (val))
+#define DMACH_READ(ch, reg) \
+ DMA_READ((ch)->ch_sc, (reg) + (ch)->ch_regoff)
+#define DMACH_WRITE(ch, reg, val) \
+ DMA_WRITE((ch)->ch_sc, (reg) + (ch)->ch_regoff, (val))
+
+static void a10dmac_intr(void *);
+
+static int
+a10dmac_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-a10-dma"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner DMA controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+a10dmac_attach(device_t dev)
+{
+ struct a10dmac_softc *sc;
+ unsigned int index;
+ clk_t clk;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ if (bus_alloc_resources(dev, a10dmac_spec, sc->sc_res)) {
+ device_printf(dev, "cannot allocate resources for device\n");
+ return (ENXIO);
+ }
+
+ mtx_init(&sc->sc_mtx, "a10 dmac", NULL, MTX_SPIN);
+
+ /* Activate DMA controller clock */
+ error = clk_get_by_ofw_index(dev, 0, 0, &clk);
+ if (error != 0) {
+ device_printf(dev, "cannot get clock\n");
+ return (error);
+ }
+ error = clk_enable(clk);
+ if (error != 0) {
+ device_printf(dev, "cannot enable clock\n");
+ return (error);
+ }
+
+ /* Disable all interrupts and clear pending status */
+ DMA_WRITE(sc, AWIN_DMA_IRQ_EN_REG, 0);
+ DMA_WRITE(sc, AWIN_DMA_IRQ_PEND_STA_REG, ~0);
+
+ /* Initialize channels */
+ for (index = 0; index < NDMA_CHANNELS; index++) {
+ sc->sc_ndma_channels[index].ch_sc = sc;
+ sc->sc_ndma_channels[index].ch_index = index;
+ sc->sc_ndma_channels[index].ch_type = CH_NDMA;
+ sc->sc_ndma_channels[index].ch_callback = NULL;
+ sc->sc_ndma_channels[index].ch_callbackarg = NULL;
+ sc->sc_ndma_channels[index].ch_regoff = AWIN_NDMA_REG(index);
+ DMACH_WRITE(&sc->sc_ndma_channels[index], AWIN_NDMA_CTL_REG, 0);
+ }
+ for (index = 0; index < DDMA_CHANNELS; index++) {
+ sc->sc_ddma_channels[index].ch_sc = sc;
+ sc->sc_ddma_channels[index].ch_index = index;
+ sc->sc_ddma_channels[index].ch_type = CH_DDMA;
+ sc->sc_ddma_channels[index].ch_callback = NULL;
+ sc->sc_ddma_channels[index].ch_callbackarg = NULL;
+ sc->sc_ddma_channels[index].ch_regoff = AWIN_DDMA_REG(index);
+ DMACH_WRITE(&sc->sc_ddma_channels[index], AWIN_DDMA_CTL_REG, 0);
+ }
+
+ error = bus_setup_intr(dev, sc->sc_res[1], INTR_MPSAFE | INTR_TYPE_MISC,
+ NULL, a10dmac_intr, sc, &sc->sc_ih);
+ if (error != 0) {
+ device_printf(dev, "could not setup interrupt handler\n");
+ bus_release_resources(dev, a10dmac_spec, sc->sc_res);
+ mtx_destroy(&sc->sc_mtx);
+ return (ENXIO);
+ }
+
+ OF_device_register_xref(OF_xref_from_node(ofw_bus_get_node(dev)), dev);
+ return (0);
+}
+
+static void
+a10dmac_intr(void *priv)
+{
+ struct a10dmac_softc *sc = priv;
+ uint32_t sta, bit, mask;
+ uint8_t index;
+
+ sta = DMA_READ(sc, AWIN_DMA_IRQ_PEND_STA_REG);
+ DMA_WRITE(sc, AWIN_DMA_IRQ_PEND_STA_REG, sta);
+
+ while ((bit = ffs(sta & AWIN_DMA_IRQ_END_MASK)) != 0) {
+ mask = (1U << (bit - 1));
+ sta &= ~mask;
+ /*
+ * Map status bit to channel number. The status register is
+ * encoded with two bits of status per channel (lowest bit
+ * is half transfer pending, highest bit is end transfer
+ * pending). The 8 normal DMA channel status are in the lower
+ * 16 bits and the 8 dedicated DMA channel status are in
+ * the upper 16 bits. The output is a channel number from 0-7.
+ */
+ index = ((bit - 1) / 2) & 7;
+ if (mask & AWIN_DMA_IRQ_NDMA) {
+ if (sc->sc_ndma_channels[index].ch_callback == NULL)
+ continue;
+ sc->sc_ndma_channels[index].ch_callback(
+ sc->sc_ndma_channels[index].ch_callbackarg);
+ } else {
+ if (sc->sc_ddma_channels[index].ch_callback == NULL)
+ continue;
+ sc->sc_ddma_channels[index].ch_callback(
+ sc->sc_ddma_channels[index].ch_callbackarg);
+ }
+ }
+}
+
+static uint32_t
+a10dmac_read_ctl(struct a10dmac_channel *ch)
+{
+ if (ch->ch_type == CH_NDMA) {
+ return (DMACH_READ(ch, AWIN_NDMA_CTL_REG));
+ } else {
+ return (DMACH_READ(ch, AWIN_DDMA_CTL_REG));
+ }
+}
+
+static void
+a10dmac_write_ctl(struct a10dmac_channel *ch, uint32_t val)
+{
+ if (ch->ch_type == CH_NDMA) {
+ DMACH_WRITE(ch, AWIN_NDMA_CTL_REG, val);
+ } else {
+ DMACH_WRITE(ch, AWIN_DDMA_CTL_REG, val);
+ }
+}
+
+static int
+a10dmac_set_config(device_t dev, void *priv, const struct sunxi_dma_config *cfg)
+{
+ struct a10dmac_channel *ch = priv;
+ uint32_t val;
+ unsigned int dst_dw, dst_bl, dst_bs, dst_wc, dst_am;
+ unsigned int src_dw, src_bl, src_bs, src_wc, src_am;
+
+ switch (cfg->dst_width) {
+ case 8:
+ dst_dw = AWIN_DMA_CTL_DATA_WIDTH_8;
+ break;
+ case 16:
+ dst_dw = AWIN_DMA_CTL_DATA_WIDTH_16;
+ break;
+ case 32:
+ dst_dw = AWIN_DMA_CTL_DATA_WIDTH_32;
+ break;
+ default:
+ return (EINVAL);
+ }
+ switch (cfg->dst_burst_len) {
+ case 1:
+ dst_bl = AWIN_DMA_CTL_BURST_LEN_1;
+ break;
+ case 4:
+ dst_bl = AWIN_DMA_CTL_BURST_LEN_4;
+ break;
+ case 8:
+ dst_bl = AWIN_DMA_CTL_BURST_LEN_8;
+ break;
+ default:
+ return (EINVAL);
+ }
+ switch (cfg->src_width) {
+ case 8:
+ src_dw = AWIN_DMA_CTL_DATA_WIDTH_8;
+ break;
+ case 16:
+ src_dw = AWIN_DMA_CTL_DATA_WIDTH_16;
+ break;
+ case 32:
+ src_dw = AWIN_DMA_CTL_DATA_WIDTH_32;
+ break;
+ default:
+ return (EINVAL);
+ }
+ switch (cfg->src_burst_len) {
+ case 1:
+ src_bl = AWIN_DMA_CTL_BURST_LEN_1;
+ break;
+ case 4:
+ src_bl = AWIN_DMA_CTL_BURST_LEN_4;
+ break;
+ case 8:
+ src_bl = AWIN_DMA_CTL_BURST_LEN_8;
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ val = (dst_dw << AWIN_DMA_CTL_DST_DATA_WIDTH_SHIFT) |
+ (dst_bl << AWIN_DMA_CTL_DST_BURST_LEN_SHIFT) |
+ (cfg->dst_drqtype << AWIN_DMA_CTL_DST_DRQ_TYPE_SHIFT) |
+ (src_dw << AWIN_DMA_CTL_SRC_DATA_WIDTH_SHIFT) |
+ (src_bl << AWIN_DMA_CTL_SRC_BURST_LEN_SHIFT) |
+ (cfg->src_drqtype << AWIN_DMA_CTL_SRC_DRQ_TYPE_SHIFT);
+
+ if (ch->ch_type == CH_NDMA) {
+ if (cfg->dst_noincr)
+ val |= AWIN_NDMA_CTL_DST_ADDR_NOINCR;
+ if (cfg->src_noincr)
+ val |= AWIN_NDMA_CTL_SRC_ADDR_NOINCR;
+
+ DMACH_WRITE(ch, AWIN_NDMA_CTL_REG, val);
+ } else {
+ dst_am = cfg->dst_noincr ? AWIN_DDMA_CTL_DMA_ADDR_IO :
+ AWIN_DDMA_CTL_DMA_ADDR_LINEAR;
+ src_am = cfg->src_noincr ? AWIN_DDMA_CTL_DMA_ADDR_IO :
+ AWIN_DDMA_CTL_DMA_ADDR_LINEAR;
+
+ val |= (dst_am << AWIN_DDMA_CTL_DST_ADDR_MODE_SHIFT);
+ val |= (src_am << AWIN_DDMA_CTL_SRC_ADDR_MODE_SHIFT);
+
+ DMACH_WRITE(ch, AWIN_DDMA_CTL_REG, val);
+
+ dst_bs = cfg->dst_blksize - 1;
+ dst_wc = cfg->dst_wait_cyc - 1;
+ src_bs = cfg->src_blksize - 1;
+ src_wc = cfg->src_wait_cyc - 1;
+
+ DMACH_WRITE(ch, AWIN_DDMA_PARA_REG,
+ (dst_bs << AWIN_DDMA_PARA_DST_DATA_BLK_SIZ_SHIFT) |
+ (dst_wc << AWIN_DDMA_PARA_DST_WAIT_CYC_SHIFT) |
+ (src_bs << AWIN_DDMA_PARA_SRC_DATA_BLK_SIZ_SHIFT) |
+ (src_wc << AWIN_DDMA_PARA_SRC_WAIT_CYC_SHIFT));
+ }
+
+ return (0);
+}
+
+static void *
+a10dmac_alloc(device_t dev, bool dedicated, void (*cb)(void *), void *cbarg)
+{
+ struct a10dmac_softc *sc = device_get_softc(dev);
+ struct a10dmac_channel *ch_list;
+ struct a10dmac_channel *ch = NULL;
+ uint32_t irqen;
+ uint8_t ch_count, index;
+
+ if (dedicated) {
+ ch_list = sc->sc_ddma_channels;
+ ch_count = DDMA_CHANNELS;
+ } else {
+ ch_list = sc->sc_ndma_channels;
+ ch_count = NDMA_CHANNELS;
+ }
+
+ mtx_lock_spin(&sc->sc_mtx);
+ for (index = 0; index < ch_count; index++) {
+ if (ch_list[index].ch_callback == NULL) {
+ ch = &ch_list[index];
+ ch->ch_callback = cb;
+ ch->ch_callbackarg = cbarg;
+
+ irqen = DMA_READ(sc, AWIN_DMA_IRQ_EN_REG);
+ if (ch->ch_type == CH_NDMA)
+ irqen |= AWIN_DMA_IRQ_NDMA_END(index);
+ else
+ irqen |= AWIN_DMA_IRQ_DDMA_END(index);
+ DMA_WRITE(sc, AWIN_DMA_IRQ_EN_REG, irqen);
+
+ break;
+ }
+ }
+ mtx_unlock_spin(&sc->sc_mtx);
+
+ return (ch);
+}
+
+static void
+a10dmac_free(device_t dev, void *priv)
+{
+ struct a10dmac_channel *ch = priv;
+ struct a10dmac_softc *sc = ch->ch_sc;
+ uint32_t irqen, sta, cfg;
+
+ mtx_lock_spin(&sc->sc_mtx);
+
+ irqen = DMA_READ(sc, AWIN_DMA_IRQ_EN_REG);
+ cfg = a10dmac_read_ctl(ch);
+ if (ch->ch_type == CH_NDMA) {
+ sta = AWIN_DMA_IRQ_NDMA_END(ch->ch_index);
+ cfg &= ~AWIN_NDMA_CTL_DMA_LOADING;
+ } else {
+ sta = AWIN_DMA_IRQ_DDMA_END(ch->ch_index);
+ cfg &= ~AWIN_DDMA_CTL_DMA_LOADING;
+ }
+ irqen &= ~sta;
+ a10dmac_write_ctl(ch, cfg);
+ DMA_WRITE(sc, AWIN_DMA_IRQ_EN_REG, irqen);
+ DMA_WRITE(sc, AWIN_DMA_IRQ_PEND_STA_REG, sta);
+
+ ch->ch_callback = NULL;
+ ch->ch_callbackarg = NULL;
+
+ mtx_unlock_spin(&sc->sc_mtx);
+}
+
+static int
+a10dmac_transfer(device_t dev, void *priv, bus_addr_t src, bus_addr_t dst,
+ size_t nbytes)
+{
+ struct a10dmac_channel *ch = priv;
+ uint32_t cfg;
+
+ cfg = a10dmac_read_ctl(ch);
+ if (ch->ch_type == CH_NDMA) {
+ if (cfg & AWIN_NDMA_CTL_DMA_LOADING)
+ return (EBUSY);
+
+ DMACH_WRITE(ch, AWIN_NDMA_SRC_ADDR_REG, src);
+ DMACH_WRITE(ch, AWIN_NDMA_DEST_ADDR_REG, dst);
+ DMACH_WRITE(ch, AWIN_NDMA_BC_REG, nbytes);
+
+ cfg |= AWIN_NDMA_CTL_DMA_LOADING;
+ a10dmac_write_ctl(ch, cfg);
+ } else {
+ if (cfg & AWIN_DDMA_CTL_DMA_LOADING)
+ return (EBUSY);
+
+ DMACH_WRITE(ch, AWIN_DDMA_SRC_START_ADDR_REG, src);
+ DMACH_WRITE(ch, AWIN_DDMA_DEST_START_ADDR_REG, dst);
+ DMACH_WRITE(ch, AWIN_DDMA_BC_REG, nbytes);
+
+ cfg |= AWIN_DDMA_CTL_DMA_LOADING;
+ a10dmac_write_ctl(ch, cfg);
+ }
+
+ return (0);
+}
+
+static void
+a10dmac_halt(device_t dev, void *priv)
+{
+ struct a10dmac_channel *ch = priv;
+ uint32_t cfg;
+
+ cfg = a10dmac_read_ctl(ch);
+ if (ch->ch_type == CH_NDMA) {
+ cfg &= ~AWIN_NDMA_CTL_DMA_LOADING;
+ } else {
+ cfg &= ~AWIN_DDMA_CTL_DMA_LOADING;
+ }
+ a10dmac_write_ctl(ch, cfg);
+}
+
+static device_method_t a10dmac_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, a10dmac_probe),
+ DEVMETHOD(device_attach, a10dmac_attach),
+
+ /* sunxi DMA interface */
+ DEVMETHOD(sunxi_dma_alloc, a10dmac_alloc),
+ DEVMETHOD(sunxi_dma_free, a10dmac_free),
+ DEVMETHOD(sunxi_dma_set_config, a10dmac_set_config),
+ DEVMETHOD(sunxi_dma_transfer, a10dmac_transfer),
+ DEVMETHOD(sunxi_dma_halt, a10dmac_halt),
+
+ DEVMETHOD_END
+};
+
+static driver_t a10dmac_driver = {
+ "a10dmac",
+ a10dmac_methods,
+ sizeof(struct a10dmac_softc)
+};
+
+static devclass_t a10dmac_devclass;
+
+DRIVER_MODULE(a10dmac, simplebus, a10dmac_driver, a10dmac_devclass, 0, 0);
diff --git a/sys/arm/allwinner/a10_dmac.h b/sys/arm/allwinner/a10_dmac.h
new file mode 100644
index 000000000000..7b337b35bec9
--- /dev/null
+++ b/sys/arm/allwinner/a10_dmac.h
@@ -0,0 +1,158 @@
+/*-
+ * Copyright (c) 2014-2016 Jared D. McNeill <jmcneill@invisible.ca>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _A10_DMAC_H_
+#define _A10_DMAC_H_
+
+#define AWIN_DMA_IRQ_EN_REG 0x0000
+#define AWIN_DMA_IRQ_PEND_STA_REG 0x0004
+#define AWIN_NDMA_AUTO_GATE_REG 0x0008
+#define AWIN_NDMA_REG(n) (0x100+0x20*(n))
+#define AWIN_NDMA_CTL_REG 0x0000
+#define AWIN_NDMA_SRC_ADDR_REG 0x0004
+#define AWIN_NDMA_DEST_ADDR_REG 0x0008
+#define AWIN_NDMA_BC_REG 0x000c
+#define AWIN_DDMA_REG(n) (0x300+0x20*(n))
+#define AWIN_DDMA_CTL_REG 0x0000
+#define AWIN_DDMA_SRC_START_ADDR_REG 0x0004
+#define AWIN_DDMA_DEST_START_ADDR_REG 0x0008
+#define AWIN_DDMA_BC_REG 0x000c
+#define AWIN_DDMA_PARA_REG 0x0018
+#define AWIN_DMA_IRQ_END_MASK 0xaaaaaaaa
+#define AWIN_DMA_IRQ_HF_MASK 0x55555555
+#define AWIN_DMA_IRQ_DDMA 0xffff0000
+#define AWIN_DMA_IRQ_DDMA_END(n) (1U << (17+2*(n)))
+#define AWIN_DMA_IRQ_DDMA_HF(n) (1U << (16+2*(n)))
+#define AWIN_DMA_IRQ_NDMA 0x0000ffff
+#define AWIN_DMA_IRQ_NDMA_END(n) (1U << (1+2*(n)))
+#define AWIN_DMA_IRQ_NDMA_HF(n) (1U << (0+2*(n)))
+#define AWIN_NDMA_AUTO_GATING_DIS (1U << 16)
+#define AWIN_DMA_CTL_DST_DATA_WIDTH_SHIFT 25
+#define AWIN_DMA_CTL_DST_DATA_WIDTH_MASK (3U << AWIN_DMA_CTL_DST_DATA_WIDTH_SHIFT)
+#define AWIN_DMA_CTL_DATA_WIDTH_8 0
+#define AWIN_DMA_CTL_DATA_WIDTH_16 1
+#define AWIN_DMA_CTL_DATA_WIDTH_32 2
+#define AWIN_DMA_CTL_DST_BURST_LEN_SHIFT 23
+#define AWIN_DMA_CTL_DST_BURST_LEN_MASK (3 << AWIN_DMA_CTL_DST_BURST_LEN_SHIFT)
+#define AWIN_DMA_CTL_BURST_LEN_1 0
+#define AWIN_DMA_CTL_BURST_LEN_4 1
+#define AWIN_DMA_CTL_BURST_LEN_8 2
+#define AWIN_DMA_CTL_DST_DRQ_TYPE_SHIFT 16
+#define AWIN_DMA_CTL_DST_DRQ_TYPE_MASK (0x1f << AWIN_DMA_CTL_DST_DRQ_TYPE_SHIFT)
+#define AWIN_DMA_CTL_BC_REMAINING (1U << 15)
+#define AWIN_DMA_CTL_SRC_DATA_WIDTH_SHIFT 9
+#define AWIN_DMA_CTL_SRC_DATA_WIDTH_MASK (3U << AWIN_DMA_CTL_SRC_DATA_WIDTH_SHIFT)
+#define AWIN_DMA_CTL_SRC_BURST_LEN_SHIFT 7
+#define AWIN_DMA_CTL_SRC_BURST_LEN_MASK (3U << AWIN_DMA_CTL_SRC_BURST_LEN_SHIFT)
+#define AWIN_DMA_CTL_SRC_DRQ_TYPE_SHIFT 0
+#define AWIN_DMA_CTL_SRC_DRQ_TYPE_MASK (0x1f << AWIN_DMA_CTL_SRC_DRQ_TYPE_SHIFT)
+#define AWIN_NDMA_CTL_DMA_LOADING (1U << 31)
+#define AWIN_NDMA_CTL_DMA_CONTIN_MODE (1U << 30)
+#define AWIN_NDMA_CTL_WAIT_STATE_LOG2_SHIFT 27
+#define AWIN_NDMA_CTL_WAIT_STATE_LOG2_MASK (7U << AWIN_NDMA_CTL_WAIT_STATE_LOG2_SHIFT)
+#define AWIN_NDMA_CTL_DST_NON_SECURE (1U << 22)
+#define AWIN_NDMA_CTL_DST_ADDR_NOINCR (1U << 21)
+#define AWIN_NDMA_CTL_DRQ_IRO 0
+#define AWIN_NDMA_CTL_DRQ_IR1 1
+#define AWIN_NDMA_CTL_DRQ_SPDIF 2
+#define AWIN_NDMA_CTL_DRQ_IISO 3
+#define AWIN_NDMA_CTL_DRQ_IIS1 4
+#define AWIN_NDMA_CTL_DRQ_AC97 5
+#define AWIN_NDMA_CTL_DRQ_IIS2 6
+#define AWIN_NDMA_CTL_DRQ_UARTO 8
+#define AWIN_NDMA_CTL_DRQ_UART1 9
+#define AWIN_NDMA_CTL_DRQ_UART2 10
+#define AWIN_NDMA_CTL_DRQ_UART3 11
+#define AWIN_NDMA_CTL_DRQ_UART4 12
+#define AWIN_NDMA_CTL_DRQ_UART5 13
+#define AWIN_NDMA_CTL_DRQ_UART6 14
+#define AWIN_NDMA_CTL_DRQ_UART7 15
+#define AWIN_NDMA_CTL_DRQ_DDC 16
+#define AWIN_NDMA_CTL_DRQ_USB_EP1 17
+#define AWIN_NDMA_CTL_DRQ_CODEC 19
+#define AWIN_NDMA_CTL_DRQ_SRAM 21
+#define AWIN_NDMA_CTL_DRQ_SDRAM 22
+#define AWIN_NDMA_CTL_DRQ_TP_AD 23
+#define AWIN_NDMA_CTL_DRQ_SPI0 24
+#define AWIN_NDMA_CTL_DRQ_SPI1 25
+#define AWIN_NDMA_CTL_DRQ_SPI2 26
+#define AWIN_NDMA_CTL_DRQ_SPI3 27
+#define AWIN_NDMA_CTL_DRQ_USB_EP2 28
+#define AWIN_NDMA_CTL_DRQ_USB_EP3 29
+#define AWIN_NDMA_CTL_DRQ_USB_EP4 30
+#define AWIN_NDMA_CTL_DRQ_USB_EP5 31
+#define AWIN_NDMA_CTL_SRC_NON_SECURE (1U << 6)
+#define AWIN_NDMA_CTL_SRC_ADDR_NOINCR (1U << 5)
+#define AWIN_NDMA_BC_COUNT 0x0003ffff
+#define AWIN_DDMA_CTL_DMA_LOADING (1U << 31)
+#define AWIN_DDMA_CTL_BUSY (1U << 30)
+#define AWIN_DDMA_CTL_DMA_CONTIN_MODE (1U << 29)
+#define AWIN_DDMA_CTL_DST_NON_SECURE (1U << 28)
+#define AWIN_DDMA_CTL_DST_ADDR_MODE_SHIFT 21
+#define AWIN_DDMA_CTL_DST_ADDR_MODE_MASK (3U << AWIN_DDMA_CTL_DST_ADDR_MODE_SHIFT)
+#define AWIN_DDMA_CTL_DMA_ADDR_LINEAR 0
+#define AWIN_DDMA_CTL_DMA_ADDR_IO 1
+#define AWIN_DDMA_CTL_DMA_ADDR_HPAGE 2
+#define AWIN_DDMA_CTL_DMA_ADDR_VPAGE 3
+#define AWIN_DDMA_CTL_DST_DRQ_TYPE_SHIFT 16
+#define AWIN_DDMA_CTL_DST_DRQ_TYPE_MASK (0x1f << AWIN_DDMA_CTL_DST_DRQ_TYPE_SHIFT)
+#define AWIN_DDMA_CTL_DRQ_SRAM 0
+#define AWIN_DDMA_CTL_DRQ_SDRAM 1
+#define AWIN_DDMA_CTL_DRQ_NFC 3
+#define AWIN_DDMA_CTL_DRQ_USB0 4
+#define AWIN_DDMA_CTL_DRQ_EMAC_TX 6
+#define AWIN_DDMA_CTL_DRQ_EMAC_RX 7
+#define AWIN_DDMA_CTL_DRQ_SPI1_TX 8
+#define AWIN_DDMA_CTL_DRQ_SPI1_RX 9
+#define AWIN_DDMA_CTL_DRQ_SS_TX 10
+#define AWIN_DDMA_CTL_DRQ_SS_RX 11
+#define AWIN_DDMA_CTL_DRQ_TCON0 14
+#define AWIN_DDMA_CTL_DRQ_TCON1 15
+#define AWIN_DDMA_CTL_DRQ_MS_TX 23
+#define AWIN_DDMA_CTL_DRQ_MS_RX 23
+#define AWIN_DDMA_CTL_DRQ_HDMI_AUDIO 24
+#define AWIN_DDMA_CTL_DRQ_SPI0_TX 26
+#define AWIN_DDMA_CTL_DRQ_SPI0_RX 27
+#define AWIN_DDMA_CTL_DRQ_SPI2_TX 28
+#define AWIN_DDMA_CTL_DRQ_SPI2_RX 29
+#define AWIN_DDMA_CTL_DRQ_SPI3_TX 30
+#define AWIN_DDMA_CTL_DRQ_SPI3_RX 31
+#define AWIN_DDMA_CTL_SRC_NON_SECURE (1U << 12)
+#define AWIN_DDMA_CTL_SRC_ADDR_MODE_SHIFT 5
+#define AWIN_DDMA_CTL_SRC_ADDR_MODE_MASK (3U << AWIN_DDMA_CTL_SRC_ADDR_MODE_SHIFT)
+#define AWIN_DDMA_BC_COUNT 0x00003fff
+#define AWIN_DDMA_PARA_DST_DATA_BLK_SIZ_SHIFT 24
+#define AWIN_DDMA_PARA_DST_DATA_BLK_SIZ_MASK (0xff << AWIN_DDMA_PARA_DST_DATA_BLK_SIZ_SHIFT)
+#define AWIN_DDMA_PARA_DST_WAIT_CYC_SHIFT 16
+#define AWIN_DDMA_PARA_DST_WAIT_CYC_MASK (0xff << AWIN_DDMA_PARA_DST_WAIT_CYC_SHIFT)
+#define AWIN_DDMA_PARA_SRC_DATA_BLK_SIZ_SHIFT 8
+#define AWIN_DDMA_PARA_SRC_DATA_BLK_SIZ_MASK (0xff << AWIN_DDMA_PARA_SRC_DATA_BLK_SIZ_SHIFT)
+#define AWIN_DDMA_PARA_SRC_WAIT_CYC_SHIFT 0
+#define AWIN_DDMA_PARA_SRC_WAIT_CYC_MASK (0xff << AWIN_DDMA_PARA_SRC_WAIT_CYC_SHIFT)
+
+#endif /* !_A10_DMAC_H_ */
diff --git a/sys/arm/allwinner/a10_fb.c b/sys/arm/allwinner/a10_fb.c
new file mode 100644
index 000000000000..d7d37e601b7d
--- /dev/null
+++ b/sys/arm/allwinner/a10_fb.c
@@ -0,0 +1,662 @@
+/*-
+ * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Allwinner A10/A20 Framebuffer
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/condvar.h>
+#include <sys/eventhandler.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/fbio.h>
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_kern.h>
+#include <vm/pmap.h>
+
+#include <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/videomode/videomode.h>
+#include <dev/videomode/edidvar.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+
+#include "fb_if.h"
+#include "hdmi_if.h"
+
+#define FB_DEFAULT_W 800
+#define FB_DEFAULT_H 600
+#define FB_DEFAULT_REF 60
+#define FB_BPP 32
+#define FB_ALIGN 0x1000
+
+#define HDMI_ENABLE_DELAY 20000
+#define DEBE_FREQ 300000000
+
+#define DOT_CLOCK_TO_HZ(c) ((c) * 1000)
+
+/* Display backend */
+#define DEBE_REG_START 0x800
+#define DEBE_REG_END 0x1000
+#define DEBE_REG_WIDTH 4
+#define DEBE_MODCTL 0x800
+#define MODCTL_ITLMOD_EN (1 << 28)
+#define MODCTL_OUT_SEL_MASK (0x7 << 20)
+#define MODCTL_OUT_SEL(sel) ((sel) << 20)
+#define OUT_SEL_LCD 0
+#define MODCTL_LAY0_EN (1 << 8)
+#define MODCTL_START_CTL (1 << 1)
+#define MODCTL_EN (1 << 0)
+#define DEBE_DISSIZE 0x808
+#define DIS_HEIGHT(h) (((h) - 1) << 16)
+#define DIS_WIDTH(w) (((w) - 1) << 0)
+#define DEBE_LAYSIZE0 0x810
+#define LAY_HEIGHT(h) (((h) - 1) << 16)
+#define LAY_WIDTH(w) (((w) - 1) << 0)
+#define DEBE_LAYCOOR0 0x820
+#define LAY_XCOOR(x) ((x) << 16)
+#define LAY_YCOOR(y) ((y) << 0)
+#define DEBE_LAYLINEWIDTH0 0x840
+#define DEBE_LAYFB_L32ADD0 0x850
+#define LAYFB_L32ADD(pa) ((pa) << 3)
+#define DEBE_LAYFB_H4ADD 0x860
+#define LAY0FB_H4ADD(pa) ((pa) >> 29)
+#define DEBE_REGBUFFCTL 0x870
+#define REGBUFFCTL_LOAD (1 << 0)
+#define DEBE_ATTCTL1 0x8a0
+#define ATTCTL1_FBFMT(fmt) ((fmt) << 8)
+#define FBFMT_XRGB8888 9
+#define ATTCTL1_FBPS(ps) ((ps) << 0)
+#define FBPS_32BPP_ARGB 0
+
+/* Timing controller */
+#define TCON_GCTL 0x000
+#define GCTL_TCON_EN (1 << 31)
+#define GCTL_IO_MAP_SEL_TCON1 (1 << 0)
+#define TCON_GINT1 0x008
+#define GINT1_TCON1_LINENO(n) (((n) + 2) << 0)
+#define TCON0_DCLK 0x044
+#define DCLK_EN 0xf0000000
+#define TCON1_CTL 0x090
+#define TCON1_EN (1 << 31)
+#define INTERLACE_EN (1 << 20)
+#define TCON1_SRC_SEL(src) ((src) << 0)
+#define TCON1_SRC_CH1 0
+#define TCON1_SRC_CH2 1
+#define TCON1_SRC_BLUE 2
+#define TCON1_START_DELAY(sd) ((sd) << 4)
+#define TCON1_BASIC0 0x094
+#define TCON1_BASIC1 0x098
+#define TCON1_BASIC2 0x09c
+#define TCON1_BASIC3 0x0a0
+#define TCON1_BASIC4 0x0a4
+#define TCON1_BASIC5 0x0a8
+#define BASIC_X(x) (((x) - 1) << 16)
+#define BASIC_Y(y) (((y) - 1) << 0)
+#define BASIC3_HT(ht) (((ht) - 1) << 16)
+#define BASIC3_HBP(hbp) (((hbp) - 1) << 0)
+#define BASIC4_VT(vt) ((vt) << 16)
+#define BASIC4_VBP(vbp) (((vbp) - 1) << 0)
+#define BASIC5_HSPW(hspw) (((hspw) - 1) << 16)
+#define BASIC5_VSPW(vspw) (((vspw) - 1) << 0)
+#define TCON1_IO_POL 0x0f0
+#define IO_POL_IO2_INV (1 << 26)
+#define IO_POL_PHSYNC (1 << 25)
+#define IO_POL_PVSYNC (1 << 24)
+#define TCON1_IO_TRI 0x0f4
+#define IO0_OUTPUT_TRI_EN (1 << 24)
+#define IO1_OUTPUT_TRI_EN (1 << 25)
+#define IO_TRI_MASK 0xffffffff
+#define START_DELAY(vbl) (MIN(32, (vbl)) - 2)
+#define VBLANK_LEN(vt, vd, i) ((((vt) << (i)) >> 1) - (vd) - 2)
+#define VTOTAL(vt) ((vt) * 2)
+#define DIVIDE(x, y) (((x) + ((y) / 2)) / (y))
+
+struct a10fb_softc {
+ device_t dev;
+ device_t fbdev;
+ struct resource *res[2];
+
+ /* Framebuffer */
+ struct fb_info info;
+ size_t fbsize;
+ bus_addr_t paddr;
+ vm_offset_t vaddr;
+
+ /* HDMI */
+ eventhandler_tag hdmi_evh;
+};
+
+static struct resource_spec a10fb_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* DEBE */
+ { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* TCON */
+ { -1, 0 }
+};
+
+#define DEBE_READ(sc, reg) bus_read_4((sc)->res[0], (reg))
+#define DEBE_WRITE(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val))
+
+#define TCON_READ(sc, reg) bus_read_4((sc)->res[1], (reg))
+#define TCON_WRITE(sc, reg, val) bus_write_4((sc)->res[1], (reg), (val))
+
+static int
+a10fb_allocfb(struct a10fb_softc *sc)
+{
+ sc->vaddr = kmem_alloc_contig(sc->fbsize, M_NOWAIT | M_ZERO, 0, ~0,
+ FB_ALIGN, 0, VM_MEMATTR_WRITE_COMBINING);
+ if (sc->vaddr == 0) {
+ device_printf(sc->dev, "failed to allocate FB memory\n");
+ return (ENOMEM);
+ }
+ sc->paddr = pmap_kextract(sc->vaddr);
+
+ return (0);
+}
+
+static void
+a10fb_freefb(struct a10fb_softc *sc)
+{
+ kmem_free(sc->vaddr, sc->fbsize);
+}
+
+static int
+a10fb_setup_debe(struct a10fb_softc *sc, const struct videomode *mode)
+{
+ int width, height, interlace, reg;
+ clk_t clk_ahb, clk_dram, clk_debe;
+ hwreset_t rst;
+ uint32_t val;
+ int error;
+
+ interlace = !!(mode->flags & VID_INTERLACE);
+ width = mode->hdisplay;
+ height = mode->vdisplay << interlace;
+
+ /* Leave reset */
+ error = hwreset_get_by_ofw_name(sc->dev, 0, "de_be", &rst);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot find reset 'de_be'\n");
+ return (error);
+ }
+ error = hwreset_deassert(rst);
+ if (error != 0) {
+ device_printf(sc->dev, "couldn't de-assert reset 'de_be'\n");
+ return (error);
+ }
+ /* Gating AHB clock for BE */
+ error = clk_get_by_ofw_name(sc->dev, 0, "ahb_de_be", &clk_ahb);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot find clk 'ahb_de_be'\n");
+ return (error);
+ }
+ error = clk_enable(clk_ahb);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot enable clk 'ahb_de_be'\n");
+ return (error);
+ }
+ /* Enable DRAM clock to BE */
+ error = clk_get_by_ofw_name(sc->dev, 0, "dram_de_be", &clk_dram);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot find clk 'dram_de_be'\n");
+ return (error);
+ }
+ error = clk_enable(clk_dram);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot enable clk 'dram_de_be'\n");
+ return (error);
+ }
+ /* Set BE clock to 300MHz and enable */
+ error = clk_get_by_ofw_name(sc->dev, 0, "de_be", &clk_debe);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot find clk 'de_be'\n");
+ return (error);
+ }
+ error = clk_set_freq(clk_debe, DEBE_FREQ, CLK_SET_ROUND_DOWN);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot set 'de_be' frequency\n");
+ return (error);
+ }
+ error = clk_enable(clk_debe);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot enable clk 'de_be'\n");
+ return (error);
+ }
+
+ /* Initialize all registers to 0 */
+ for (reg = DEBE_REG_START; reg < DEBE_REG_END; reg += DEBE_REG_WIDTH)
+ DEBE_WRITE(sc, reg, 0);
+
+ /* Enable display backend */
+ DEBE_WRITE(sc, DEBE_MODCTL, MODCTL_EN);
+
+ /* Set display size */
+ DEBE_WRITE(sc, DEBE_DISSIZE, DIS_HEIGHT(height) | DIS_WIDTH(width));
+
+ /* Set layer 0 size, position, and stride */
+ DEBE_WRITE(sc, DEBE_LAYSIZE0, LAY_HEIGHT(height) | LAY_WIDTH(width));
+ DEBE_WRITE(sc, DEBE_LAYCOOR0, LAY_XCOOR(0) | LAY_YCOOR(0));
+ DEBE_WRITE(sc, DEBE_LAYLINEWIDTH0, width * FB_BPP);
+
+ /* Point layer 0 to FB memory */
+ DEBE_WRITE(sc, DEBE_LAYFB_L32ADD0, LAYFB_L32ADD(sc->paddr));
+ DEBE_WRITE(sc, DEBE_LAYFB_H4ADD, LAY0FB_H4ADD(sc->paddr));
+
+ /* Set backend format and pixel sequence */
+ DEBE_WRITE(sc, DEBE_ATTCTL1, ATTCTL1_FBFMT(FBFMT_XRGB8888) |
+ ATTCTL1_FBPS(FBPS_32BPP_ARGB));
+
+ /* Enable layer 0, output to LCD, setup interlace */
+ val = DEBE_READ(sc, DEBE_MODCTL);
+ val |= MODCTL_LAY0_EN;
+ val &= ~MODCTL_OUT_SEL_MASK;
+ val |= MODCTL_OUT_SEL(OUT_SEL_LCD);
+ if (interlace)
+ val |= MODCTL_ITLMOD_EN;
+ else
+ val &= ~MODCTL_ITLMOD_EN;
+ DEBE_WRITE(sc, DEBE_MODCTL, val);
+
+ /* Commit settings */
+ DEBE_WRITE(sc, DEBE_REGBUFFCTL, REGBUFFCTL_LOAD);
+
+ /* Start DEBE */
+ val = DEBE_READ(sc, DEBE_MODCTL);
+ val |= MODCTL_START_CTL;
+ DEBE_WRITE(sc, DEBE_MODCTL, val);
+
+ return (0);
+}
+
+static int
+a10fb_setup_pll(struct a10fb_softc *sc, uint64_t freq)
+{
+ clk_t clk_sclk1, clk_sclk2;
+ int error;
+
+ error = clk_get_by_ofw_name(sc->dev, 0, "lcd_ch1_sclk1", &clk_sclk1);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot find clk 'lcd_ch1_sclk1'\n");
+ return (error);
+ }
+ error = clk_get_by_ofw_name(sc->dev, 0, "lcd_ch1_sclk2", &clk_sclk2);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot find clk 'lcd_ch1_sclk2'\n");
+ return (error);
+ }
+
+ error = clk_set_freq(clk_sclk2, freq, 0);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot set lcd ch1 frequency\n");
+ return (error);
+ }
+ error = clk_enable(clk_sclk2);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot enable lcd ch1 sclk2\n");
+ return (error);
+ }
+ error = clk_enable(clk_sclk1);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot enable lcd ch1 sclk1\n");
+ return (error);
+ }
+
+ return (0);
+}
+
+static int
+a10fb_setup_tcon(struct a10fb_softc *sc, const struct videomode *mode)
+{
+ u_int interlace, hspw, hbp, vspw, vbp, vbl, width, height, start_delay;
+ u_int vtotal, framerate, clk;
+ clk_t clk_ahb;
+ hwreset_t rst;
+ uint32_t val;
+ int error;
+
+ interlace = !!(mode->flags & VID_INTERLACE);
+ width = mode->hdisplay;
+ height = mode->vdisplay;
+ hspw = mode->hsync_end - mode->hsync_start;
+ hbp = mode->htotal - mode->hsync_start;
+ vspw = mode->vsync_end - mode->vsync_start;
+ vbp = mode->vtotal - mode->vsync_start;
+ vbl = VBLANK_LEN(mode->vtotal, mode->vdisplay, interlace);
+ start_delay = START_DELAY(vbl);
+
+ /* Leave reset */
+ error = hwreset_get_by_ofw_name(sc->dev, 0, "lcd", &rst);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot find reset 'lcd'\n");
+ return (error);
+ }
+ error = hwreset_deassert(rst);
+ if (error != 0) {
+ device_printf(sc->dev, "couldn't de-assert reset 'lcd'\n");
+ return (error);
+ }
+ /* Gating AHB clock for LCD */
+ error = clk_get_by_ofw_name(sc->dev, 0, "ahb_lcd", &clk_ahb);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot find clk 'ahb_lcd'\n");
+ return (error);
+ }
+ error = clk_enable(clk_ahb);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot enable clk 'ahb_lcd'\n");
+ return (error);
+ }
+
+ /* Disable TCON and TCON1 */
+ TCON_WRITE(sc, TCON_GCTL, 0);
+ TCON_WRITE(sc, TCON1_CTL, 0);
+
+ /* Enable clocks */
+ TCON_WRITE(sc, TCON0_DCLK, DCLK_EN);
+
+ /* Disable IO and data output ports */
+ TCON_WRITE(sc, TCON1_IO_TRI, IO_TRI_MASK);
+
+ /* Disable TCON and select TCON1 */
+ TCON_WRITE(sc, TCON_GCTL, GCTL_IO_MAP_SEL_TCON1);
+
+ /* Source width and height */
+ TCON_WRITE(sc, TCON1_BASIC0, BASIC_X(width) | BASIC_Y(height));
+ /* Scaler width and height */
+ TCON_WRITE(sc, TCON1_BASIC1, BASIC_X(width) | BASIC_Y(height));
+ /* Output width and height */
+ TCON_WRITE(sc, TCON1_BASIC2, BASIC_X(width) | BASIC_Y(height));
+ /* Horizontal total and back porch */
+ TCON_WRITE(sc, TCON1_BASIC3, BASIC3_HT(mode->htotal) | BASIC3_HBP(hbp));
+ /* Vertical total and back porch */
+ vtotal = VTOTAL(mode->vtotal);
+ if (interlace) {
+ framerate = DIVIDE(DIVIDE(DOT_CLOCK_TO_HZ(mode->dot_clock),
+ mode->htotal), mode->vtotal);
+ clk = mode->htotal * (VTOTAL(mode->vtotal) + 1) * framerate;
+ if ((clk / 2) == DOT_CLOCK_TO_HZ(mode->dot_clock))
+ vtotal += 1;
+ }
+ TCON_WRITE(sc, TCON1_BASIC4, BASIC4_VT(vtotal) | BASIC4_VBP(vbp));
+ /* Horizontal and vertical sync */
+ TCON_WRITE(sc, TCON1_BASIC5, BASIC5_HSPW(hspw) | BASIC5_VSPW(vspw));
+ /* Polarity */
+ val = IO_POL_IO2_INV;
+ if (mode->flags & VID_PHSYNC)
+ val |= IO_POL_PHSYNC;
+ if (mode->flags & VID_PVSYNC)
+ val |= IO_POL_PVSYNC;
+ TCON_WRITE(sc, TCON1_IO_POL, val);
+
+ /* Set scan line for TCON1 line trigger */
+ TCON_WRITE(sc, TCON_GINT1, GINT1_TCON1_LINENO(start_delay));
+
+ /* Enable TCON1 */
+ val = TCON1_EN;
+ if (interlace)
+ val |= INTERLACE_EN;
+ val |= TCON1_START_DELAY(start_delay);
+ val |= TCON1_SRC_SEL(TCON1_SRC_CH1);
+ TCON_WRITE(sc, TCON1_CTL, val);
+
+ /* Setup PLL */
+ return (a10fb_setup_pll(sc, DOT_CLOCK_TO_HZ(mode->dot_clock)));
+}
+
+static void
+a10fb_enable_tcon(struct a10fb_softc *sc, int onoff)
+{
+ uint32_t val;
+
+ /* Enable TCON */
+ val = TCON_READ(sc, TCON_GCTL);
+ if (onoff)
+ val |= GCTL_TCON_EN;
+ else
+ val &= ~GCTL_TCON_EN;
+ TCON_WRITE(sc, TCON_GCTL, val);
+
+ /* Enable TCON1 IO0/IO1 outputs */
+ val = TCON_READ(sc, TCON1_IO_TRI);
+ if (onoff)
+ val &= ~(IO0_OUTPUT_TRI_EN | IO1_OUTPUT_TRI_EN);
+ else
+ val |= (IO0_OUTPUT_TRI_EN | IO1_OUTPUT_TRI_EN);
+ TCON_WRITE(sc, TCON1_IO_TRI, val);
+}
+
+static int
+a10fb_configure(struct a10fb_softc *sc, const struct videomode *mode)
+{
+ size_t fbsize;
+ int error;
+
+ fbsize = round_page(mode->hdisplay * mode->vdisplay * (FB_BPP / NBBY));
+
+ /* Detach the old FB device */
+ if (sc->fbdev != NULL) {
+ device_delete_child(sc->dev, sc->fbdev);
+ sc->fbdev = NULL;
+ }
+
+ /* If the FB size has changed, free the old FB memory */
+ if (sc->fbsize > 0 && sc->fbsize != fbsize) {
+ a10fb_freefb(sc);
+ sc->vaddr = 0;
+ }
+
+ /* Allocate the FB if necessary */
+ sc->fbsize = fbsize;
+ if (sc->vaddr == 0) {
+ error = a10fb_allocfb(sc);
+ if (error != 0) {
+ device_printf(sc->dev, "failed to allocate FB memory\n");
+ return (ENXIO);
+ }
+ }
+
+ /* Setup display backend */
+ error = a10fb_setup_debe(sc, mode);
+ if (error != 0)
+ return (error);
+
+ /* Setup display timing controller */
+ error = a10fb_setup_tcon(sc, mode);
+ if (error != 0)
+ return (error);
+
+ /* Attach framebuffer device */
+ sc->info.fb_name = device_get_nameunit(sc->dev);
+ sc->info.fb_vbase = (intptr_t)sc->vaddr;
+ sc->info.fb_pbase = sc->paddr;
+ sc->info.fb_size = sc->fbsize;
+ sc->info.fb_bpp = sc->info.fb_depth = FB_BPP;
+ sc->info.fb_stride = mode->hdisplay * (FB_BPP / NBBY);
+ sc->info.fb_width = mode->hdisplay;
+ sc->info.fb_height = mode->vdisplay;
+
+ sc->fbdev = device_add_child(sc->dev, "fbd", device_get_unit(sc->dev));
+ if (sc->fbdev == NULL) {
+ device_printf(sc->dev, "failed to add fbd child\n");
+ return (ENOENT);
+ }
+
+ error = device_probe_and_attach(sc->fbdev);
+ if (error != 0) {
+ device_printf(sc->dev, "failed to attach fbd device\n");
+ return (error);
+ }
+
+ return (0);
+}
+
+static void
+a10fb_hdmi_event(void *arg, device_t hdmi_dev)
+{
+ const struct videomode *mode;
+ struct videomode hdmi_mode;
+ struct a10fb_softc *sc;
+ struct edid_info ei;
+ uint8_t *edid;
+ uint32_t edid_len;
+ int error;
+
+ sc = arg;
+ edid = NULL;
+ edid_len = 0;
+ mode = NULL;
+
+ error = HDMI_GET_EDID(hdmi_dev, &edid, &edid_len);
+ if (error != 0) {
+ device_printf(sc->dev, "failed to get EDID: %d\n", error);
+ } else {
+ error = edid_parse(edid, &ei);
+ if (error != 0) {
+ device_printf(sc->dev, "failed to parse EDID: %d\n",
+ error);
+ } else {
+ if (bootverbose)
+ edid_print(&ei);
+ mode = ei.edid_preferred_mode;
+ }
+ }
+
+ /* If the preferred mode could not be determined, use the default */
+ if (mode == NULL)
+ mode = pick_mode_by_ref(FB_DEFAULT_W, FB_DEFAULT_H,
+ FB_DEFAULT_REF);
+
+ if (mode == NULL) {
+ device_printf(sc->dev, "failed to find usable video mode\n");
+ return;
+ }
+
+ if (bootverbose)
+ device_printf(sc->dev, "using %dx%d\n",
+ mode->hdisplay, mode->vdisplay);
+
+ /* Disable HDMI */
+ HDMI_ENABLE(hdmi_dev, 0);
+
+ /* Disable timing controller */
+ a10fb_enable_tcon(sc, 0);
+
+ /* Configure DEBE and TCON */
+ error = a10fb_configure(sc, mode);
+ if (error != 0) {
+ device_printf(sc->dev, "failed to configure FB: %d\n", error);
+ return;
+ }
+
+ hdmi_mode = *mode;
+ hdmi_mode.hskew = mode->hsync_end - mode->hsync_start;
+ hdmi_mode.flags |= VID_HSKEW;
+ HDMI_SET_VIDEOMODE(hdmi_dev, &hdmi_mode);
+
+ /* Enable timing controller */
+ a10fb_enable_tcon(sc, 1);
+
+ DELAY(HDMI_ENABLE_DELAY);
+
+ /* Enable HDMI */
+ HDMI_ENABLE(hdmi_dev, 1);
+}
+
+static int
+a10fb_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "allwinner,sun7i-a20-fb"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner Framebuffer");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+a10fb_attach(device_t dev)
+{
+ struct a10fb_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ sc->dev = dev;
+
+ if (bus_alloc_resources(dev, a10fb_spec, sc->res)) {
+ device_printf(dev, "cannot allocate resources for device\n");
+ return (ENXIO);
+ }
+
+ sc->hdmi_evh = EVENTHANDLER_REGISTER(hdmi_event,
+ a10fb_hdmi_event, sc, 0);
+
+ return (0);
+}
+
+static struct fb_info *
+a10fb_fb_getinfo(device_t dev)
+{
+ struct a10fb_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ return (&sc->info);
+}
+
+static device_method_t a10fb_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, a10fb_probe),
+ DEVMETHOD(device_attach, a10fb_attach),
+
+ /* FB interface */
+ DEVMETHOD(fb_getinfo, a10fb_fb_getinfo),
+
+ DEVMETHOD_END
+};
+
+static driver_t a10fb_driver = {
+ "fb",
+ a10fb_methods,
+ sizeof(struct a10fb_softc),
+};
+
+static devclass_t a10fb_devclass;
+
+DRIVER_MODULE(fb, simplebus, a10fb_driver, a10fb_devclass, 0, 0);
diff --git a/sys/arm/allwinner/a10_hdmi.c b/sys/arm/allwinner/a10_hdmi.c
new file mode 100644
index 000000000000..bc2788498004
--- /dev/null
+++ b/sys/arm/allwinner/a10_hdmi.c
@@ -0,0 +1,725 @@
+/*-
+ * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Allwinner A10/A20 HDMI TX
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/condvar.h>
+#include <sys/eventhandler.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+
+#include <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/videomode/videomode.h>
+#include <dev/videomode/edidvar.h>
+
+#include <dev/extres/clk/clk.h>
+
+#include "hdmi_if.h"
+
+#define HDMI_CTRL 0x004
+#define CTRL_MODULE_EN (1 << 31)
+#define HDMI_INT_STATUS 0x008
+#define HDMI_HPD 0x00c
+#define HPD_DET (1 << 0)
+#define HDMI_VID_CTRL 0x010
+#define VID_CTRL_VIDEO_EN (1 << 31)
+#define VID_CTRL_HDMI_MODE (1 << 30)
+#define VID_CTRL_INTERLACE (1 << 4)
+#define VID_CTRL_REPEATER_2X (1 << 0)
+#define HDMI_VID_TIMING0 0x014
+#define VID_ACT_V(v) (((v) - 1) << 16)
+#define VID_ACT_H(h) (((h) - 1) << 0)
+#define HDMI_VID_TIMING1 0x018
+#define VID_VBP(vbp) (((vbp) - 1) << 16)
+#define VID_HBP(hbp) (((hbp) - 1) << 0)
+#define HDMI_VID_TIMING2 0x01c
+#define VID_VFP(vfp) (((vfp) - 1) << 16)
+#define VID_HFP(hfp) (((hfp) - 1) << 0)
+#define HDMI_VID_TIMING3 0x020
+#define VID_VSPW(vspw) (((vspw) - 1) << 16)
+#define VID_HSPW(hspw) (((hspw) - 1) << 0)
+#define HDMI_VID_TIMING4 0x024
+#define TX_CLOCK_NORMAL 0x03e00000
+#define VID_VSYNC_ACTSEL (1 << 1)
+#define VID_HSYNC_ACTSEL (1 << 0)
+#define HDMI_AUD_CTRL 0x040
+#define AUD_CTRL_EN (1 << 31)
+#define AUD_CTRL_RST (1 << 30)
+#define HDMI_ADMA_CTRL 0x044
+#define HDMI_ADMA_MODE (1 << 31)
+#define HDMI_ADMA_MODE_DDMA (0 << 31)
+#define HDMI_ADMA_MODE_NDMA (1 << 31)
+#define HDMI_AUD_FMT 0x048
+#define AUD_FMT_CH(n) ((n) - 1)
+#define HDMI_PCM_CTRL 0x04c
+#define HDMI_AUD_CTS 0x050
+#define HDMI_AUD_N 0x054
+#define HDMI_AUD_CH_STATUS0 0x058
+#define CH_STATUS0_FS_FREQ (0xf << 24)
+#define CH_STATUS0_FS_FREQ_48 (2 << 24)
+#define HDMI_AUD_CH_STATUS1 0x05c
+#define CH_STATUS1_WORD_LEN (0x7 << 1)
+#define CH_STATUS1_WORD_LEN_16 (1 << 1)
+#define HDMI_AUDIO_RESET_RETRY 1000
+#define HDMI_AUDIO_CHANNELS 2
+#define HDMI_AUDIO_CHANNELMAP 0x76543210
+#define HDMI_AUDIO_N 6144 /* 48 kHz */
+#define HDMI_AUDIO_CTS(r, n) ((((r) * 10) * ((n) / 128)) / 480)
+#define HDMI_PADCTRL0 0x200
+#define PADCTRL0_BIASEN (1 << 31)
+#define PADCTRL0_LDOCEN (1 << 30)
+#define PADCTRL0_LDODEN (1 << 29)
+#define PADCTRL0_PWENC (1 << 28)
+#define PADCTRL0_PWEND (1 << 27)
+#define PADCTRL0_PWENG (1 << 26)
+#define PADCTRL0_CKEN (1 << 25)
+#define PADCTRL0_SEN (1 << 24)
+#define PADCTRL0_TXEN (1 << 23)
+#define HDMI_PADCTRL1 0x204
+#define PADCTRL1_AMP_OPT (1 << 23)
+#define PADCTRL1_AMPCK_OPT (1 << 22)
+#define PADCTRL1_DMP_OPT (1 << 21)
+#define PADCTRL1_EMP_OPT (1 << 20)
+#define PADCTRL1_EMPCK_OPT (1 << 19)
+#define PADCTRL1_PWSCK (1 << 18)
+#define PADCTRL1_PWSDT (1 << 17)
+#define PADCTRL1_REG_CSMPS (1 << 16)
+#define PADCTRL1_REG_DEN (1 << 15)
+#define PADCTRL1_REG_DENCK (1 << 14)
+#define PADCTRL1_REG_PLRCK (1 << 13)
+#define PADCTRL1_REG_EMP (0x7 << 10)
+#define PADCTRL1_REG_EMP_EN (0x2 << 10)
+#define PADCTRL1_REG_CD (0x3 << 8)
+#define PADCTRL1_REG_CKSS (0x3 << 6)
+#define PADCTRL1_REG_CKSS_1X (0x1 << 6)
+#define PADCTRL1_REG_CKSS_2X (0x0 << 6)
+#define PADCTRL1_REG_AMP (0x7 << 3)
+#define PADCTRL1_REG_AMP_EN (0x6 << 3)
+#define PADCTRL1_REG_PLR (0x7 << 0)
+#define HDMI_PLLCTRL0 0x208
+#define PLLCTRL0_PLL_EN (1 << 31)
+#define PLLCTRL0_BWS (1 << 30)
+#define PLLCTRL0_HV_IS_33 (1 << 29)
+#define PLLCTRL0_LDO1_EN (1 << 28)
+#define PLLCTRL0_LDO2_EN (1 << 27)
+#define PLLCTRL0_SDIV2 (1 << 25)
+#define PLLCTRL0_VCO_GAIN (0x1 << 22)
+#define PLLCTRL0_S (0x7 << 17)
+#define PLLCTRL0_CP_S (0xf << 12)
+#define PLLCTRL0_CS (0x7 << 8)
+#define PLLCTRL0_PREDIV(x) ((x) << 4)
+#define PLLCTRL0_VCO_S (0x8 << 0)
+#define HDMI_PLLDBG0 0x20c
+#define PLLDBG0_CKIN_SEL (1 << 21)
+#define PLLDBG0_CKIN_SEL_PLL3 (0 << 21)
+#define PLLDBG0_CKIN_SEL_PLL7 (1 << 21)
+#define HDMI_PKTCTRL0 0x2f0
+#define HDMI_PKTCTRL1 0x2f4
+#define PKTCTRL_PACKET(n,t) ((t) << ((n) << 2))
+#define PKT_NULL 0
+#define PKT_GC 1
+#define PKT_AVI 2
+#define PKT_AI 3
+#define PKT_SPD 5
+#define PKT_END 15
+#define DDC_CTRL 0x500
+#define CTRL_DDC_EN (1 << 31)
+#define CTRL_DDC_ACMD_START (1 << 30)
+#define CTRL_DDC_FIFO_DIR (1 << 8)
+#define CTRL_DDC_FIFO_DIR_READ (0 << 8)
+#define CTRL_DDC_FIFO_DIR_WRITE (1 << 8)
+#define CTRL_DDC_SWRST (1 << 0)
+#define DDC_SLAVE_ADDR 0x504
+#define SLAVE_ADDR_SEG_SHIFT 24
+#define SLAVE_ADDR_EDDC_SHIFT 16
+#define SLAVE_ADDR_OFFSET_SHIFT 8
+#define SLAVE_ADDR_SHIFT 0
+#define DDC_INT_STATUS 0x50c
+#define INT_STATUS_XFER_DONE (1 << 0)
+#define DDC_FIFO_CTRL 0x510
+#define FIFO_CTRL_CLEAR (1 << 31)
+#define DDC_BYTE_COUNTER 0x51c
+#define DDC_COMMAND 0x520
+#define COMMAND_EOREAD (4 << 0)
+#define DDC_CLOCK 0x528
+#define DDC_CLOCK_M (1 << 3)
+#define DDC_CLOCK_N (5 << 0)
+#define DDC_FIFO 0x518
+#define SWRST_DELAY 1000
+#define DDC_DELAY 1000
+#define DDC_RETRY 1000
+#define DDC_BLKLEN 16
+#define DDC_ADDR 0x50
+#define EDDC_ADDR 0x60
+#define EDID_LENGTH 128
+#define DDC_CTRL_LINE 0x540
+#define DDC_LINE_SCL_ENABLE (1 << 8)
+#define DDC_LINE_SDA_ENABLE (1 << 9)
+#define HDMI_ENABLE_DELAY 50000
+#define DDC_READ_RETRY 4
+#define EXT_TAG 0x00
+#define CEA_TAG_ID 0x02
+#define CEA_DTD 0x03
+#define DTD_BASIC_AUDIO (1 << 6)
+#define CEA_REV 0x02
+#define CEA_DATA_OFF 0x03
+#define CEA_DATA_START 4
+#define BLOCK_TAG(x) (((x) >> 5) & 0x7)
+#define BLOCK_TAG_VSDB 3
+#define BLOCK_LEN(x) ((x) & 0x1f)
+#define HDMI_VSDB_MINLEN 5
+#define HDMI_OUI "\x03\x0c\x00"
+#define HDMI_OUI_LEN 3
+#define HDMI_DEFAULT_FREQ 297000000
+
+struct a10hdmi_softc {
+ struct resource *res;
+
+ struct intr_config_hook mode_hook;
+
+ uint8_t edid[EDID_LENGTH];
+
+ int has_hdmi;
+ int has_audio;
+
+ clk_t clk_ahb;
+ clk_t clk_hdmi;
+ clk_t clk_lcd;
+};
+
+static struct resource_spec a10hdmi_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+#define HDMI_READ(sc, reg) bus_read_4((sc)->res, (reg))
+#define HDMI_WRITE(sc, reg, val) bus_write_4((sc)->res, (reg), (val))
+
+static void
+a10hdmi_init(struct a10hdmi_softc *sc)
+{
+ /* Enable the HDMI module */
+ HDMI_WRITE(sc, HDMI_CTRL, CTRL_MODULE_EN);
+
+ /* Configure PLL/DRV settings */
+ HDMI_WRITE(sc, HDMI_PADCTRL0, PADCTRL0_BIASEN | PADCTRL0_LDOCEN |
+ PADCTRL0_LDODEN | PADCTRL0_PWENC | PADCTRL0_PWEND |
+ PADCTRL0_PWENG | PADCTRL0_CKEN | PADCTRL0_TXEN);
+ HDMI_WRITE(sc, HDMI_PADCTRL1, PADCTRL1_AMP_OPT | PADCTRL1_AMPCK_OPT |
+ PADCTRL1_EMP_OPT | PADCTRL1_EMPCK_OPT | PADCTRL1_REG_DEN |
+ PADCTRL1_REG_DENCK | PADCTRL1_REG_EMP_EN | PADCTRL1_REG_AMP_EN);
+
+ /* Select PLL3 as input clock */
+ HDMI_WRITE(sc, HDMI_PLLDBG0, PLLDBG0_CKIN_SEL_PLL3);
+
+ DELAY(HDMI_ENABLE_DELAY);
+}
+
+static void
+a10hdmi_hpd(void *arg)
+{
+ struct a10hdmi_softc *sc;
+ device_t dev;
+ uint32_t hpd;
+
+ dev = arg;
+ sc = device_get_softc(dev);
+
+ hpd = HDMI_READ(sc, HDMI_HPD);
+ if ((hpd & HPD_DET) == HPD_DET)
+ EVENTHANDLER_INVOKE(hdmi_event, dev, HDMI_EVENT_CONNECTED);
+
+ config_intrhook_disestablish(&sc->mode_hook);
+}
+
+static int
+a10hdmi_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "allwinner,sun7i-a20-hdmi"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner HDMI TX");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+a10hdmi_attach(device_t dev)
+{
+ struct a10hdmi_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ if (bus_alloc_resources(dev, a10hdmi_spec, &sc->res)) {
+ device_printf(dev, "cannot allocate resources for device\n");
+ return (ENXIO);
+ }
+
+ /* Setup clocks */
+ error = clk_get_by_ofw_name(dev, 0, "ahb", &sc->clk_ahb);
+ if (error != 0) {
+ device_printf(dev, "cannot find ahb clock\n");
+ return (error);
+ }
+ error = clk_get_by_ofw_name(dev, 0, "hdmi", &sc->clk_hdmi);
+ if (error != 0) {
+ device_printf(dev, "cannot find hdmi clock\n");
+ return (error);
+ }
+ error = clk_get_by_ofw_name(dev, 0, "lcd", &sc->clk_lcd);
+ if (error != 0) {
+ device_printf(dev, "cannot find lcd clock\n");
+ }
+ /* Enable HDMI clock */
+ error = clk_enable(sc->clk_hdmi);
+ if (error != 0) {
+ device_printf(dev, "cannot enable hdmi clock\n");
+ return (error);
+ }
+ /* Gating AHB clock for HDMI */
+ error = clk_enable(sc->clk_ahb);
+ if (error != 0) {
+ device_printf(dev, "cannot enable ahb gate\n");
+ return (error);
+ }
+
+ a10hdmi_init(sc);
+
+ sc->mode_hook.ich_func = a10hdmi_hpd;
+ sc->mode_hook.ich_arg = dev;
+
+ error = config_intrhook_establish(&sc->mode_hook);
+ if (error != 0)
+ return (error);
+
+ return (0);
+}
+
+static int
+a10hdmi_ddc_xfer(struct a10hdmi_softc *sc, uint16_t addr, uint8_t seg,
+ uint8_t off, int len)
+{
+ uint32_t val;
+ int retry;
+
+ /* Set FIFO direction to read */
+ val = HDMI_READ(sc, DDC_CTRL);
+ val &= ~CTRL_DDC_FIFO_DIR;
+ val |= CTRL_DDC_FIFO_DIR_READ;
+ HDMI_WRITE(sc, DDC_CTRL, val);
+
+ /* Setup DDC slave address */
+ val = (addr << SLAVE_ADDR_SHIFT) | (seg << SLAVE_ADDR_SEG_SHIFT) |
+ (EDDC_ADDR << SLAVE_ADDR_EDDC_SHIFT) |
+ (off << SLAVE_ADDR_OFFSET_SHIFT);
+ HDMI_WRITE(sc, DDC_SLAVE_ADDR, val);
+
+ /* Clear FIFO */
+ val = HDMI_READ(sc, DDC_FIFO_CTRL);
+ val |= FIFO_CTRL_CLEAR;
+ HDMI_WRITE(sc, DDC_FIFO_CTRL, val);
+
+ /* Set transfer length */
+ HDMI_WRITE(sc, DDC_BYTE_COUNTER, len);
+
+ /* Set command to "Explicit Offset Address Read" */
+ HDMI_WRITE(sc, DDC_COMMAND, COMMAND_EOREAD);
+
+ /* Start transfer */
+ val = HDMI_READ(sc, DDC_CTRL);
+ val |= CTRL_DDC_ACMD_START;
+ HDMI_WRITE(sc, DDC_CTRL, val);
+
+ /* Wait for command to start */
+ retry = DDC_RETRY;
+ while (--retry > 0) {
+ val = HDMI_READ(sc, DDC_CTRL);
+ if ((val & CTRL_DDC_ACMD_START) == 0)
+ break;
+ DELAY(DDC_DELAY);
+ }
+ if (retry == 0)
+ return (ETIMEDOUT);
+
+ /* Ensure that the transfer completed */
+ val = HDMI_READ(sc, DDC_INT_STATUS);
+ if ((val & INT_STATUS_XFER_DONE) == 0)
+ return (EIO);
+
+ return (0);
+}
+
+static int
+a10hdmi_ddc_read(struct a10hdmi_softc *sc, int block, uint8_t *edid)
+{
+ int resid, off, len, error;
+ uint8_t *pbuf;
+
+ pbuf = edid;
+ resid = EDID_LENGTH;
+ off = (block & 1) ? EDID_LENGTH : 0;
+
+ while (resid > 0) {
+ len = min(resid, DDC_BLKLEN);
+ error = a10hdmi_ddc_xfer(sc, DDC_ADDR, block >> 1, off, len);
+ if (error != 0)
+ return (error);
+
+ bus_read_multi_1(sc->res, DDC_FIFO, pbuf, len);
+
+ pbuf += len;
+ off += len;
+ resid -= len;
+ }
+
+ return (0);
+}
+
+static int
+a10hdmi_detect_hdmi_vsdb(uint8_t *edid)
+{
+ int off, p, btag, blen;
+
+ if (edid[EXT_TAG] != CEA_TAG_ID)
+ return (0);
+
+ off = edid[CEA_DATA_OFF];
+
+ /* CEA data block collection starts at byte 4 */
+ if (off <= CEA_DATA_START)
+ return (0);
+
+ /* Parse the CEA data blocks */
+ for (p = CEA_DATA_START; p < off;) {
+ btag = BLOCK_TAG(edid[p]);
+ blen = BLOCK_LEN(edid[p]);
+
+ /* Make sure the length is sane */
+ if (p + blen + 1 > off)
+ break;
+
+ /* Look for a VSDB with the HDMI 24-bit IEEE registration ID */
+ if (btag == BLOCK_TAG_VSDB && blen >= HDMI_VSDB_MINLEN &&
+ memcmp(&edid[p + 1], HDMI_OUI, HDMI_OUI_LEN) == 0)
+ return (1);
+
+ /* Next data block */
+ p += (1 + blen);
+ }
+
+ return (0);
+}
+
+static void
+a10hdmi_detect_hdmi(struct a10hdmi_softc *sc, int *phdmi, int *paudio)
+{
+ struct edid_info ei;
+ uint8_t edid[EDID_LENGTH];
+ int block;
+
+ *phdmi = *paudio = 0;
+
+ if (edid_parse(sc->edid, &ei) != 0)
+ return;
+
+ /* Scan through extension blocks, looking for a CEA-861 block. */
+ for (block = 1; block <= ei.edid_ext_block_count; block++) {
+ if (a10hdmi_ddc_read(sc, block, edid) != 0)
+ return;
+
+ if (a10hdmi_detect_hdmi_vsdb(edid) != 0) {
+ *phdmi = 1;
+ *paudio = ((edid[CEA_DTD] & DTD_BASIC_AUDIO) != 0);
+ return;
+ }
+ }
+}
+
+static int
+a10hdmi_get_edid(device_t dev, uint8_t **edid, uint32_t *edid_len)
+{
+ struct a10hdmi_softc *sc;
+ int error, retry;
+
+ sc = device_get_softc(dev);
+ retry = DDC_READ_RETRY;
+
+ while (--retry > 0) {
+ /* I2C software reset */
+ HDMI_WRITE(sc, DDC_FIFO_CTRL, 0);
+ HDMI_WRITE(sc, DDC_CTRL, CTRL_DDC_EN | CTRL_DDC_SWRST);
+ DELAY(SWRST_DELAY);
+ if (HDMI_READ(sc, DDC_CTRL) & CTRL_DDC_SWRST) {
+ device_printf(dev, "DDC software reset failed\n");
+ return (ENXIO);
+ }
+
+ /* Configure DDC clock */
+ HDMI_WRITE(sc, DDC_CLOCK, DDC_CLOCK_M | DDC_CLOCK_N);
+
+ /* Enable SDA/SCL */
+ HDMI_WRITE(sc, DDC_CTRL_LINE,
+ DDC_LINE_SCL_ENABLE | DDC_LINE_SDA_ENABLE);
+
+ /* Read EDID block */
+ error = a10hdmi_ddc_read(sc, 0, sc->edid);
+ if (error == 0) {
+ *edid = sc->edid;
+ *edid_len = sizeof(sc->edid);
+ break;
+ }
+ }
+
+ if (error == 0)
+ a10hdmi_detect_hdmi(sc, &sc->has_hdmi, &sc->has_audio);
+ else
+ sc->has_hdmi = sc->has_audio = 0;
+
+ return (error);
+}
+
+static void
+a10hdmi_set_audiomode(device_t dev, const struct videomode *mode)
+{
+ struct a10hdmi_softc *sc;
+ uint32_t val;
+ int retry;
+
+ sc = device_get_softc(dev);
+
+ /* Disable and reset audio module and wait for reset bit to clear */
+ HDMI_WRITE(sc, HDMI_AUD_CTRL, AUD_CTRL_RST);
+ for (retry = HDMI_AUDIO_RESET_RETRY; retry > 0; retry--) {
+ val = HDMI_READ(sc, HDMI_AUD_CTRL);
+ if ((val & AUD_CTRL_RST) == 0)
+ break;
+ }
+ if (retry == 0) {
+ device_printf(dev, "timeout waiting for audio module\n");
+ return;
+ }
+
+ if (!sc->has_audio)
+ return;
+
+ /* DMA and FIFO control */
+ HDMI_WRITE(sc, HDMI_ADMA_CTRL, HDMI_ADMA_MODE_DDMA);
+
+ /* Audio format control (LPCM, S16LE, stereo) */
+ HDMI_WRITE(sc, HDMI_AUD_FMT, AUD_FMT_CH(HDMI_AUDIO_CHANNELS));
+
+ /* Channel mappings */
+ HDMI_WRITE(sc, HDMI_PCM_CTRL, HDMI_AUDIO_CHANNELMAP);
+
+ /* Clocks */
+ HDMI_WRITE(sc, HDMI_AUD_CTS,
+ HDMI_AUDIO_CTS(mode->dot_clock, HDMI_AUDIO_N));
+ HDMI_WRITE(sc, HDMI_AUD_N, HDMI_AUDIO_N);
+
+ /* Set sampling frequency to 48 kHz, word length to 16-bit */
+ HDMI_WRITE(sc, HDMI_AUD_CH_STATUS0, CH_STATUS0_FS_FREQ_48);
+ HDMI_WRITE(sc, HDMI_AUD_CH_STATUS1, CH_STATUS1_WORD_LEN_16);
+
+ /* Enable */
+ HDMI_WRITE(sc, HDMI_AUD_CTRL, AUD_CTRL_EN);
+}
+
+static int
+a10hdmi_get_tcon_config(struct a10hdmi_softc *sc, int *div, int *dbl)
+{
+ uint64_t lcd_fin, lcd_fout;
+ clk_t clk_lcd_parent;
+ const char *pname;
+ int error;
+
+ error = clk_get_parent(sc->clk_lcd, &clk_lcd_parent);
+ if (error != 0)
+ return (error);
+
+ /* Get the LCD CH1 special clock 2 divider */
+ error = clk_get_freq(sc->clk_lcd, &lcd_fout);
+ if (error != 0)
+ return (error);
+ error = clk_get_freq(clk_lcd_parent, &lcd_fin);
+ if (error != 0)
+ return (error);
+ *div = lcd_fin / lcd_fout;
+
+ /* Detect LCD CH1 special clock using a 1X or 2X source */
+ /* XXX */
+ pname = clk_get_name(clk_lcd_parent);
+ if (strcmp(pname, "pll3") == 0 || strcmp(pname, "pll7") == 0)
+ *dbl = 0;
+ else
+ *dbl = 1;
+
+ return (0);
+}
+
+static int
+a10hdmi_set_videomode(device_t dev, const struct videomode *mode)
+{
+ struct a10hdmi_softc *sc;
+ int error, clk_div, clk_dbl;
+ int dblscan, hfp, hspw, hbp, vfp, vspw, vbp;
+ uint32_t val;
+
+ sc = device_get_softc(dev);
+ dblscan = !!(mode->flags & VID_DBLSCAN);
+ hfp = mode->hsync_start - mode->hdisplay;
+ hspw = mode->hsync_end - mode->hsync_start;
+ hbp = mode->htotal - mode->hsync_start;
+ vfp = mode->vsync_start - mode->vdisplay;
+ vspw = mode->vsync_end - mode->vsync_start;
+ vbp = mode->vtotal - mode->vsync_start;
+
+ error = a10hdmi_get_tcon_config(sc, &clk_div, &clk_dbl);
+ if (error != 0) {
+ device_printf(dev, "couldn't get tcon config: %d\n", error);
+ return (error);
+ }
+
+ /* Clear interrupt status */
+ HDMI_WRITE(sc, HDMI_INT_STATUS, HDMI_READ(sc, HDMI_INT_STATUS));
+
+ /* Clock setup */
+ val = HDMI_READ(sc, HDMI_PADCTRL1);
+ val &= ~PADCTRL1_REG_CKSS;
+ val |= (clk_dbl ? PADCTRL1_REG_CKSS_2X : PADCTRL1_REG_CKSS_1X);
+ HDMI_WRITE(sc, HDMI_PADCTRL1, val);
+ HDMI_WRITE(sc, HDMI_PLLCTRL0, PLLCTRL0_PLL_EN | PLLCTRL0_BWS |
+ PLLCTRL0_HV_IS_33 | PLLCTRL0_LDO1_EN | PLLCTRL0_LDO2_EN |
+ PLLCTRL0_SDIV2 | PLLCTRL0_VCO_GAIN | PLLCTRL0_S |
+ PLLCTRL0_CP_S | PLLCTRL0_CS | PLLCTRL0_PREDIV(clk_div) |
+ PLLCTRL0_VCO_S);
+
+ /* Setup display settings */
+ if (bootverbose)
+ device_printf(dev, "HDMI: %s, Audio: %s\n",
+ sc->has_hdmi ? "yes" : "no", sc->has_audio ? "yes" : "no");
+ val = 0;
+ if (sc->has_hdmi)
+ val |= VID_CTRL_HDMI_MODE;
+ if (mode->flags & VID_INTERLACE)
+ val |= VID_CTRL_INTERLACE;
+ if (mode->flags & VID_DBLSCAN)
+ val |= VID_CTRL_REPEATER_2X;
+ HDMI_WRITE(sc, HDMI_VID_CTRL, val);
+
+ /* Setup display timings */
+ HDMI_WRITE(sc, HDMI_VID_TIMING0,
+ VID_ACT_V(mode->vdisplay) | VID_ACT_H(mode->hdisplay << dblscan));
+ HDMI_WRITE(sc, HDMI_VID_TIMING1,
+ VID_VBP(vbp) | VID_HBP(hbp << dblscan));
+ HDMI_WRITE(sc, HDMI_VID_TIMING2,
+ VID_VFP(vfp) | VID_HFP(hfp << dblscan));
+ HDMI_WRITE(sc, HDMI_VID_TIMING3,
+ VID_VSPW(vspw) | VID_HSPW(hspw << dblscan));
+ val = TX_CLOCK_NORMAL;
+ if (mode->flags & VID_PVSYNC)
+ val |= VID_VSYNC_ACTSEL;
+ if (mode->flags & VID_PHSYNC)
+ val |= VID_HSYNC_ACTSEL;
+ HDMI_WRITE(sc, HDMI_VID_TIMING4, val);
+
+ /* This is an ordered list of infoframe packets that the HDMI
+ * transmitter will send. Transmit packets in the following order:
+ * 1. General control packet
+ * 2. AVI infoframe
+ * 3. Audio infoframe
+ * There are 2 registers with 4 slots each. The list is terminated
+ * with the special PKT_END marker.
+ */
+ HDMI_WRITE(sc, HDMI_PKTCTRL0,
+ PKTCTRL_PACKET(0, PKT_GC) | PKTCTRL_PACKET(1, PKT_AVI) |
+ PKTCTRL_PACKET(2, PKT_AI) | PKTCTRL_PACKET(3, PKT_END));
+ HDMI_WRITE(sc, HDMI_PKTCTRL1, 0);
+
+ /* Setup audio */
+ a10hdmi_set_audiomode(dev, mode);
+
+ return (0);
+}
+
+static int
+a10hdmi_enable(device_t dev, int onoff)
+{
+ struct a10hdmi_softc *sc;
+ uint32_t val;
+
+ sc = device_get_softc(dev);
+
+ /* Enable or disable video output */
+ val = HDMI_READ(sc, HDMI_VID_CTRL);
+ if (onoff)
+ val |= VID_CTRL_VIDEO_EN;
+ else
+ val &= ~VID_CTRL_VIDEO_EN;
+ HDMI_WRITE(sc, HDMI_VID_CTRL, val);
+
+ return (0);
+}
+
+static device_method_t a10hdmi_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, a10hdmi_probe),
+ DEVMETHOD(device_attach, a10hdmi_attach),
+
+ /* HDMI interface */
+ DEVMETHOD(hdmi_get_edid, a10hdmi_get_edid),
+ DEVMETHOD(hdmi_set_videomode, a10hdmi_set_videomode),
+ DEVMETHOD(hdmi_enable, a10hdmi_enable),
+
+ DEVMETHOD_END
+};
+
+static driver_t a10hdmi_driver = {
+ "a10hdmi",
+ a10hdmi_methods,
+ sizeof(struct a10hdmi_softc),
+};
+
+static devclass_t a10hdmi_devclass;
+
+DRIVER_MODULE(a10hdmi, simplebus, a10hdmi_driver, a10hdmi_devclass, 0, 0);
+MODULE_VERSION(a10hdmi, 1);
diff --git a/sys/arm/allwinner/a10_hdmiaudio.c b/sys/arm/allwinner/a10_hdmiaudio.c
new file mode 100644
index 000000000000..13e58ce8642b
--- /dev/null
+++ b/sys/arm/allwinner/a10_hdmiaudio.c
@@ -0,0 +1,435 @@
+/*-
+ * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Allwinner A10/A20 HDMI Audio
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/condvar.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+
+#include <dev/sound/pcm/sound.h>
+#include <dev/sound/chip.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "sunxi_dma_if.h"
+#include "mixer_if.h"
+
+#define DRQTYPE_HDMIAUDIO 24
+#define DRQTYPE_SDRAM 1
+
+#define DMA_WIDTH 32
+#define DMA_BURST_LEN 8
+#define DDMA_BLKSIZE 32
+#define DDMA_WAIT_CYC 8
+
+#define DMABUF_MIN 4096
+#define DMABUF_DEFAULT 65536
+#define DMABUF_MAX 131072
+
+#define HDMI_SAMPLERATE 48000
+
+#define TX_FIFO 0x01c16400
+
+static uint32_t a10hdmiaudio_fmt[] = {
+ SND_FORMAT(AFMT_S16_LE, 2, 0),
+ 0
+};
+
+static struct pcmchan_caps a10hdmiaudio_pcaps = {
+ HDMI_SAMPLERATE, HDMI_SAMPLERATE, a10hdmiaudio_fmt, 0
+};
+
+struct a10hdmiaudio_info;
+
+struct a10hdmiaudio_chinfo {
+ struct snd_dbuf *buffer;
+ struct pcm_channel *channel;
+ struct a10hdmiaudio_info *parent;
+ bus_dmamap_t dmamap;
+ void *dmaaddr;
+ bus_addr_t physaddr;
+ device_t dmac;
+ void *dmachan;
+
+ int run;
+ uint32_t pos;
+ uint32_t blocksize;
+};
+
+struct a10hdmiaudio_info {
+ device_t dev;
+ struct mtx *lock;
+ bus_dma_tag_t dmat;
+ unsigned dmasize;
+
+ struct a10hdmiaudio_chinfo play;
+};
+
+/*
+ * Mixer interface
+ */
+
+static int
+a10hdmiaudio_mixer_init(struct snd_mixer *m)
+{
+ mix_setdevs(m, SOUND_MASK_PCM);
+
+ return (0);
+}
+
+static int
+a10hdmiaudio_mixer_set(struct snd_mixer *m, unsigned dev, unsigned left,
+ unsigned right)
+{
+ return (-1);
+}
+
+static kobj_method_t a10hdmiaudio_mixer_methods[] = {
+ KOBJMETHOD(mixer_init, a10hdmiaudio_mixer_init),
+ KOBJMETHOD(mixer_set, a10hdmiaudio_mixer_set),
+ KOBJMETHOD_END
+};
+MIXER_DECLARE(a10hdmiaudio_mixer);
+
+/*
+ * Channel interface
+ */
+
+static void
+a10hdmiaudio_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+ struct a10hdmiaudio_chinfo *ch = arg;
+
+ if (error != 0)
+ return;
+
+ ch->physaddr = segs[0].ds_addr;
+}
+
+static void
+a10hdmiaudio_transfer(struct a10hdmiaudio_chinfo *ch)
+{
+ int error;
+
+ error = SUNXI_DMA_TRANSFER(ch->dmac, ch->dmachan,
+ ch->physaddr + ch->pos, TX_FIFO, ch->blocksize);
+ if (error) {
+ ch->run = 0;
+ device_printf(ch->parent->dev, "DMA transfer failed: %d\n",
+ error);
+ }
+}
+
+static void
+a10hdmiaudio_dmaconfig(struct a10hdmiaudio_chinfo *ch)
+{
+ struct sunxi_dma_config conf;
+
+ memset(&conf, 0, sizeof(conf));
+ conf.src_width = conf.dst_width = DMA_WIDTH;
+ conf.src_burst_len = conf.dst_burst_len = DMA_BURST_LEN;
+ conf.src_blksize = conf.dst_blksize = DDMA_BLKSIZE;
+ conf.src_wait_cyc = conf.dst_wait_cyc = DDMA_WAIT_CYC;
+ conf.src_drqtype = DRQTYPE_SDRAM;
+ conf.dst_drqtype = DRQTYPE_HDMIAUDIO;
+ conf.dst_noincr = true;
+
+ SUNXI_DMA_SET_CONFIG(ch->dmac, ch->dmachan, &conf);
+}
+
+static void
+a10hdmiaudio_dmaintr(void *priv)
+{
+ struct a10hdmiaudio_chinfo *ch = priv;
+ unsigned bufsize;
+
+ bufsize = sndbuf_getsize(ch->buffer);
+
+ ch->pos += ch->blocksize;
+ if (ch->pos >= bufsize)
+ ch->pos -= bufsize;
+
+ if (ch->run) {
+ chn_intr(ch->channel);
+ a10hdmiaudio_transfer(ch);
+ }
+}
+
+static void
+a10hdmiaudio_start(struct a10hdmiaudio_chinfo *ch)
+{
+ ch->pos = 0;
+
+ /* Configure DMA channel */
+ a10hdmiaudio_dmaconfig(ch);
+
+ /* Start DMA transfer */
+ a10hdmiaudio_transfer(ch);
+}
+
+static void
+a10hdmiaudio_stop(struct a10hdmiaudio_chinfo *ch)
+{
+ /* Disable DMA channel */
+ SUNXI_DMA_HALT(ch->dmac, ch->dmachan);
+}
+
+static void *
+a10hdmiaudio_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
+ struct pcm_channel *c, int dir)
+{
+ struct a10hdmiaudio_info *sc = devinfo;
+ struct a10hdmiaudio_chinfo *ch = &sc->play;
+ int error;
+
+ ch->parent = sc;
+ ch->channel = c;
+ ch->buffer = b;
+
+ ch->dmac = devclass_get_device(devclass_find("a10dmac"), 0);
+ if (ch->dmac == NULL) {
+ device_printf(sc->dev, "cannot find DMA controller\n");
+ return (NULL);
+ }
+ ch->dmachan = SUNXI_DMA_ALLOC(ch->dmac, true, a10hdmiaudio_dmaintr, ch);
+ if (ch->dmachan == NULL) {
+ device_printf(sc->dev, "cannot allocate DMA channel\n");
+ return (NULL);
+ }
+
+ error = bus_dmamem_alloc(sc->dmat, &ch->dmaaddr,
+ BUS_DMA_NOWAIT | BUS_DMA_COHERENT, &ch->dmamap);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot allocate channel buffer\n");
+ return (NULL);
+ }
+ error = bus_dmamap_load(sc->dmat, ch->dmamap, ch->dmaaddr,
+ sc->dmasize, a10hdmiaudio_dmamap_cb, ch, BUS_DMA_NOWAIT);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot load DMA map\n");
+ return (NULL);
+ }
+ memset(ch->dmaaddr, 0, sc->dmasize);
+
+ if (sndbuf_setup(ch->buffer, ch->dmaaddr, sc->dmasize) != 0) {
+ device_printf(sc->dev, "cannot setup sndbuf\n");
+ return (NULL);
+ }
+
+ return (ch);
+}
+
+static int
+a10hdmiaudio_chan_free(kobj_t obj, void *data)
+{
+ struct a10hdmiaudio_chinfo *ch = data;
+ struct a10hdmiaudio_info *sc = ch->parent;
+
+ SUNXI_DMA_FREE(ch->dmac, ch->dmachan);
+ bus_dmamap_unload(sc->dmat, ch->dmamap);
+ bus_dmamem_free(sc->dmat, ch->dmaaddr, ch->dmamap);
+
+ return (0);
+}
+
+static int
+a10hdmiaudio_chan_setformat(kobj_t obj, void *data, uint32_t format)
+{
+ return (0);
+}
+
+static uint32_t
+a10hdmiaudio_chan_setspeed(kobj_t obj, void *data, uint32_t speed)
+{
+ return (HDMI_SAMPLERATE);
+}
+
+static uint32_t
+a10hdmiaudio_chan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
+{
+ struct a10hdmiaudio_chinfo *ch = data;
+
+ ch->blocksize = blocksize & ~3;
+
+ return (ch->blocksize);
+}
+
+static int
+a10hdmiaudio_chan_trigger(kobj_t obj, void *data, int go)
+{
+ struct a10hdmiaudio_chinfo *ch = data;
+ struct a10hdmiaudio_info *sc = ch->parent;
+
+ if (!PCMTRIG_COMMON(go))
+ return (0);
+
+ snd_mtxlock(sc->lock);
+ switch (go) {
+ case PCMTRIG_START:
+ ch->run = 1;
+ a10hdmiaudio_start(ch);
+ break;
+ case PCMTRIG_STOP:
+ case PCMTRIG_ABORT:
+ ch->run = 0;
+ a10hdmiaudio_stop(ch);
+ break;
+ default:
+ break;
+ }
+ snd_mtxunlock(sc->lock);
+
+ return (0);
+}
+
+static uint32_t
+a10hdmiaudio_chan_getptr(kobj_t obj, void *data)
+{
+ struct a10hdmiaudio_chinfo *ch = data;
+
+ return (ch->pos);
+}
+
+static struct pcmchan_caps *
+a10hdmiaudio_chan_getcaps(kobj_t obj, void *data)
+{
+ return (&a10hdmiaudio_pcaps);
+}
+
+static kobj_method_t a10hdmiaudio_chan_methods[] = {
+ KOBJMETHOD(channel_init, a10hdmiaudio_chan_init),
+ KOBJMETHOD(channel_free, a10hdmiaudio_chan_free),
+ KOBJMETHOD(channel_setformat, a10hdmiaudio_chan_setformat),
+ KOBJMETHOD(channel_setspeed, a10hdmiaudio_chan_setspeed),
+ KOBJMETHOD(channel_setblocksize, a10hdmiaudio_chan_setblocksize),
+ KOBJMETHOD(channel_trigger, a10hdmiaudio_chan_trigger),
+ KOBJMETHOD(channel_getptr, a10hdmiaudio_chan_getptr),
+ KOBJMETHOD(channel_getcaps, a10hdmiaudio_chan_getcaps),
+ KOBJMETHOD_END
+};
+CHANNEL_DECLARE(a10hdmiaudio_chan);
+
+/*
+ * Device interface
+ */
+
+static int
+a10hdmiaudio_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "allwinner,sun7i-a20-hdmiaudio"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner HDMI Audio");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+a10hdmiaudio_attach(device_t dev)
+{
+ struct a10hdmiaudio_info *sc;
+ char status[SND_STATUSLEN];
+ int error;
+
+ sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO);
+ sc->dev = dev;
+ sc->lock = snd_mtxcreate(device_get_nameunit(dev), "a10hdmiaudio softc");
+
+ sc->dmasize = pcm_getbuffersize(dev, DMABUF_MIN,
+ DMABUF_DEFAULT, DMABUF_MAX);
+ error = bus_dma_tag_create(
+ bus_get_dma_tag(dev),
+ 4, sc->dmasize, /* alignment, boundary */
+ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ sc->dmasize, 1, /* maxsize, nsegs */
+ sc->dmasize, 0, /* maxsegsize, flags */
+ NULL, NULL, /* lockfunc, lockarg */
+ &sc->dmat);
+ if (error != 0) {
+ device_printf(dev, "cannot create DMA tag\n");
+ goto fail;
+ }
+
+ if (mixer_init(dev, &a10hdmiaudio_mixer_class, sc)) {
+ device_printf(dev, "mixer_init failed\n");
+ goto fail;
+ }
+
+ pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE);
+ pcm_setflags(dev, pcm_getflags(dev) | SD_F_SOFTPCMVOL);
+
+ if (pcm_register(dev, sc, 1, 0)) {
+ device_printf(dev, "pcm_register failed\n");
+ goto fail;
+ }
+
+ pcm_addchan(dev, PCMDIR_PLAY, &a10hdmiaudio_chan_class, sc);
+
+ snprintf(status, SND_STATUSLEN, "at %s", ofw_bus_get_name(dev));
+ pcm_setstatus(dev, status);
+
+ return (0);
+
+fail:
+ snd_mtxfree(sc->lock);
+ free(sc, M_DEVBUF);
+
+ return (error);
+}
+
+static device_method_t a10hdmiaudio_pcm_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, a10hdmiaudio_probe),
+ DEVMETHOD(device_attach, a10hdmiaudio_attach),
+
+ DEVMETHOD_END
+};
+
+static driver_t a10hdmiaudio_pcm_driver = {
+ "pcm",
+ a10hdmiaudio_pcm_methods,
+ PCM_SOFTC_SIZE,
+};
+
+DRIVER_MODULE(a10hdmiaudio, simplebus, a10hdmiaudio_pcm_driver, pcm_devclass, 0, 0);
+MODULE_DEPEND(a10hdmiaudio, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
+MODULE_VERSION(a10hdmiaudio, 1);
diff --git a/sys/arm/allwinner/a10_sramc.c b/sys/arm/allwinner/a10_sramc.c
new file mode 100644
index 000000000000..2227b12c3674
--- /dev/null
+++ b/sys/arm/allwinner/a10_sramc.c
@@ -0,0 +1,152 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Ganbold Tsagaankhuu <ganbold@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/frame.h>
+#include <machine/intr.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "a10_sramc.h"
+
+#define SRAM_CTL1_CFG 0x04
+#define CTL1_CFG_SRAMD_MAP_USB0 (1 << 0)
+
+struct a10_sramc_softc {
+ struct resource *res;
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+};
+
+static struct a10_sramc_softc *a10_sramc_sc;
+
+#define sramc_read_4(sc, reg) \
+ bus_space_read_4((sc)->bst, (sc)->bsh, (reg))
+#define sramc_write_4(sc, reg, val) \
+ bus_space_write_4((sc)->bst, (sc)->bsh, (reg), (val))
+
+static int
+a10_sramc_probe(device_t dev)
+{
+
+ if (ofw_bus_is_compatible(dev, "allwinner,sun4i-a10-sram-controller")) {
+ device_set_desc(dev, "Allwinner sramc module");
+ return (BUS_PROBE_DEFAULT);
+ }
+
+ return (ENXIO);
+}
+
+static int
+a10_sramc_attach(device_t dev)
+{
+ struct a10_sramc_softc *sc = device_get_softc(dev);
+ int rid = 0;
+
+ sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
+ if (!sc->res) {
+ device_printf(dev, "could not allocate resource\n");
+ return (ENXIO);
+ }
+
+ sc->bst = rman_get_bustag(sc->res);
+ sc->bsh = rman_get_bushandle(sc->res);
+
+ a10_sramc_sc = sc;
+
+ return (0);
+}
+
+static device_method_t a10_sramc_methods[] = {
+ DEVMETHOD(device_probe, a10_sramc_probe),
+ DEVMETHOD(device_attach, a10_sramc_attach),
+ { 0, 0 }
+};
+
+static driver_t a10_sramc_driver = {
+ "a10_sramc",
+ a10_sramc_methods,
+ sizeof(struct a10_sramc_softc),
+};
+
+static devclass_t a10_sramc_devclass;
+
+EARLY_DRIVER_MODULE(a10_sramc, simplebus, a10_sramc_driver, a10_sramc_devclass,
+ 0, 0, BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_FIRST);
+
+int
+a10_map_to_emac(void)
+{
+ struct a10_sramc_softc *sc = a10_sramc_sc;
+ uint32_t reg_value;
+
+ if (sc == NULL)
+ return (ENXIO);
+
+ /* Map SRAM to EMAC, set bit 2 and 4. */
+ reg_value = sramc_read_4(sc, SRAM_CTL1_CFG);
+ reg_value |= 0x5 << 2;
+ sramc_write_4(sc, SRAM_CTL1_CFG, reg_value);
+
+ return (0);
+}
+
+int
+a10_map_to_otg(void)
+{
+ struct a10_sramc_softc *sc = a10_sramc_sc;
+ uint32_t reg_value;
+
+ if (sc == NULL)
+ return (ENXIO);
+
+ /* Map SRAM to OTG */
+ reg_value = sramc_read_4(sc, SRAM_CTL1_CFG);
+ reg_value |= CTL1_CFG_SRAMD_MAP_USB0;
+ sramc_write_4(sc, SRAM_CTL1_CFG, reg_value);
+
+ return (0);
+}
diff --git a/sys/arm/allwinner/a10_sramc.h b/sys/arm/allwinner/a10_sramc.h
new file mode 100644
index 000000000000..882091319a67
--- /dev/null
+++ b/sys/arm/allwinner/a10_sramc.h
@@ -0,0 +1,37 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Ganbold Tsagaankhuu <ganbold@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _A10_SRAMC_H_
+#define _A10_SRAMC_H_
+
+int a10_map_to_emac(void);
+int a10_map_to_otg(void);
+
+#endif
diff --git a/sys/arm/allwinner/a10_timer.c b/sys/arm/allwinner/a10_timer.c
new file mode 100644
index 000000000000..1812e9bc85ec
--- /dev/null
+++ b/sys/arm/allwinner/a10_timer.c
@@ -0,0 +1,488 @@
+/*-
+ * Copyright (c) 2012 Ganbold Tsagaankhuu <ganbold@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+#include <machine/bus.h>
+#include <machine/intr.h>
+#include <machine/machdep.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/extres/clk/clk.h>
+
+#if defined(__aarch64__)
+#include "opt_soc.h"
+#else
+#include <arm/allwinner/aw_machdep.h>
+#endif
+
+/**
+ * Timer registers addr
+ *
+ */
+#define TIMER_IRQ_EN_REG 0x00
+#define TIMER_IRQ_ENABLE(x) (1 << x)
+
+#define TIMER_IRQ_STA_REG 0x04
+#define TIMER_IRQ_PENDING(x) (1 << x)
+
+/*
+ * On A10, A13, A20 and A31/A31s 6 timers are available
+ */
+#define TIMER_CTRL_REG(x) (0x10 + 0x10 * x)
+#define TIMER_CTRL_START (1 << 0)
+#define TIMER_CTRL_AUTORELOAD (1 << 1)
+#define TIMER_CTRL_CLKSRC_MASK (3 << 2)
+#define TIMER_CTRL_OSC24M (1 << 2)
+#define TIMER_CTRL_PRESCALAR_MASK (0x7 << 4)
+#define TIMER_CTRL_PRESCALAR(x) ((x - 1) << 4)
+#define TIMER_CTRL_MODE_MASK (1 << 7)
+#define TIMER_CTRL_MODE_SINGLE (1 << 7)
+#define TIMER_CTRL_MODE_CONTINUOUS (0 << 7)
+#define TIMER_INTV_REG(x) (0x14 + 0x10 * x)
+#define TIMER_CURV_REG(x) (0x18 + 0x10 * x)
+
+/* 64 bit counter, available in A10 and A13 */
+#define CNT64_CTRL_REG 0xa0
+#define CNT64_CTRL_RL_EN 0x02 /* read latch enable */
+#define CNT64_LO_REG 0xa4
+#define CNT64_HI_REG 0xa8
+
+#define SYS_TIMER_CLKSRC 24000000 /* clock source */
+
+enum a10_timer_type {
+ A10_TIMER = 1,
+ A23_TIMER,
+};
+
+struct a10_timer_softc {
+ device_t sc_dev;
+ struct resource *res[2];
+ void *sc_ih; /* interrupt handler */
+ uint32_t sc_period;
+ uint64_t timer0_freq;
+ struct eventtimer et;
+ enum a10_timer_type type;
+};
+
+#define timer_read_4(sc, reg) \
+ bus_read_4(sc->res[A10_TIMER_MEMRES], reg)
+#define timer_write_4(sc, reg, val) \
+ bus_write_4(sc->res[A10_TIMER_MEMRES], reg, val)
+
+static u_int a10_timer_get_timecount(struct timecounter *);
+#if defined(__arm__)
+static int a10_timer_timer_start(struct eventtimer *,
+ sbintime_t first, sbintime_t period);
+static int a10_timer_timer_stop(struct eventtimer *);
+#endif
+
+static uint64_t timer_read_counter64(struct a10_timer_softc *sc);
+#if defined(__arm__)
+static void a10_timer_eventtimer_setup(struct a10_timer_softc *sc);
+#endif
+
+#if defined(__aarch64__)
+static void a23_timer_timecounter_setup(struct a10_timer_softc *sc);
+static u_int a23_timer_get_timecount(struct timecounter *tc);
+#endif
+
+static int a10_timer_irq(void *);
+static int a10_timer_probe(device_t);
+static int a10_timer_attach(device_t);
+
+#if defined(__arm__)
+static delay_func a10_timer_delay;
+#endif
+
+static struct timecounter a10_timer_timecounter = {
+ .tc_name = "a10_timer timer0",
+ .tc_get_timecount = a10_timer_get_timecount,
+ .tc_counter_mask = ~0u,
+ .tc_frequency = 0,
+ .tc_quality = 1000,
+};
+
+#if defined(__aarch64__)
+static struct timecounter a23_timer_timecounter = {
+ .tc_name = "a10_timer timer0",
+ .tc_get_timecount = a23_timer_get_timecount,
+ .tc_counter_mask = ~0u,
+ .tc_frequency = 0,
+ /* We want it to be selected over the arm generic timecounter */
+ .tc_quality = 2000,
+};
+#endif
+
+#define A10_TIMER_MEMRES 0
+#define A10_TIMER_IRQRES 1
+
+static struct resource_spec a10_timer_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"allwinner,sun4i-a10-timer", A10_TIMER},
+#if defined(__aarch64__)
+ {"allwinner,sun8i-a23-timer", A23_TIMER},
+#endif
+ {NULL, 0},
+};
+
+static int
+a10_timer_probe(device_t dev)
+{
+ struct a10_timer_softc *sc;
+#if defined(__arm__)
+ u_int soc_family;
+#endif
+
+ sc = device_get_softc(dev);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+#if defined(__arm__)
+ /* For SoC >= A10 we have the ARM Timecounter/Eventtimer */
+ soc_family = allwinner_soc_family();
+ if (soc_family != ALLWINNERSOC_SUN4I &&
+ soc_family != ALLWINNERSOC_SUN5I)
+ return (ENXIO);
+#endif
+
+ device_set_desc(dev, "Allwinner timer");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+a10_timer_attach(device_t dev)
+{
+ struct a10_timer_softc *sc;
+ clk_t clk;
+ int err;
+
+ sc = device_get_softc(dev);
+ sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+
+ if (bus_alloc_resources(dev, a10_timer_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ sc->sc_dev = dev;
+
+ /* Setup and enable the timer interrupt */
+ err = bus_setup_intr(dev, sc->res[A10_TIMER_IRQRES], INTR_TYPE_CLK,
+ a10_timer_irq, NULL, sc, &sc->sc_ih);
+ if (err != 0) {
+ bus_release_resources(dev, a10_timer_spec, sc->res);
+ device_printf(dev, "Unable to setup the clock irq handler, "
+ "err = %d\n", err);
+ return (ENXIO);
+ }
+
+ if (clk_get_by_ofw_index(dev, 0, 0, &clk) != 0)
+ sc->timer0_freq = SYS_TIMER_CLKSRC;
+ else {
+ if (clk_get_freq(clk, &sc->timer0_freq) != 0) {
+ device_printf(dev, "Cannot get clock source frequency\n");
+ return (ENXIO);
+ }
+ }
+
+#if defined(__arm__)
+ a10_timer_eventtimer_setup(sc);
+ arm_set_delay(a10_timer_delay, sc);
+ a10_timer_timecounter.tc_priv = sc;
+ a10_timer_timecounter.tc_frequency = sc->timer0_freq;
+ tc_init(&a10_timer_timecounter);
+#elif defined(__aarch64__)
+ a23_timer_timecounter_setup(sc);
+#endif
+
+ if (bootverbose) {
+ device_printf(sc->sc_dev, "clock: hz=%d stathz = %d\n", hz, stathz);
+
+ device_printf(sc->sc_dev, "event timer clock frequency %ju\n",
+ sc->timer0_freq);
+ device_printf(sc->sc_dev, "timecounter clock frequency %jd\n",
+ a10_timer_timecounter.tc_frequency);
+ }
+
+ return (0);
+}
+
+static int
+a10_timer_irq(void *arg)
+{
+ struct a10_timer_softc *sc;
+ uint32_t val;
+
+ sc = (struct a10_timer_softc *)arg;
+
+ /* Clear interrupt pending bit. */
+ timer_write_4(sc, TIMER_IRQ_STA_REG, TIMER_IRQ_PENDING(0));
+
+ val = timer_read_4(sc, TIMER_CTRL_REG(0));
+
+ /*
+ * Disabled autoreload and sc_period > 0 means
+ * timer_start was called with non NULL first value.
+ * Now we will set periodic timer with the given period
+ * value.
+ */
+ if ((val & (1<<1)) == 0 && sc->sc_period > 0) {
+ /* Update timer */
+ timer_write_4(sc, TIMER_CURV_REG(0), sc->sc_period);
+
+ /* Make periodic and enable */
+ val |= TIMER_CTRL_AUTORELOAD | TIMER_CTRL_START;
+ timer_write_4(sc, TIMER_CTRL_REG(0), val);
+ }
+
+ if (sc->et.et_active)
+ sc->et.et_event_cb(&sc->et, sc->et.et_arg);
+
+ return (FILTER_HANDLED);
+}
+
+/*
+ * Event timer function for A10 and A13
+ */
+
+#if defined(__arm__)
+static void
+a10_timer_eventtimer_setup(struct a10_timer_softc *sc)
+{
+ uint32_t val;
+
+ /* Set clock source to OSC24M, 1 pre-division, continuous mode */
+ val = timer_read_4(sc, TIMER_CTRL_REG(0));
+ val &= ~TIMER_CTRL_PRESCALAR_MASK | ~TIMER_CTRL_MODE_MASK | ~TIMER_CTRL_CLKSRC_MASK;
+ val |= TIMER_CTRL_PRESCALAR(1) | TIMER_CTRL_OSC24M;
+ timer_write_4(sc, TIMER_CTRL_REG(0), val);
+
+ /* Enable timer0 */
+ val = timer_read_4(sc, TIMER_IRQ_EN_REG);
+ val |= TIMER_IRQ_ENABLE(0);
+ timer_write_4(sc, TIMER_IRQ_EN_REG, val);
+
+ /* Set desired frequency in event timer and timecounter */
+ sc->et.et_frequency = sc->timer0_freq;
+ sc->et.et_name = "a10_timer Eventtimer";
+ sc->et.et_flags = ET_FLAGS_ONESHOT | ET_FLAGS_PERIODIC;
+ sc->et.et_quality = 1000;
+ sc->et.et_min_period = (0x00000005LLU << 32) / sc->et.et_frequency;
+ sc->et.et_max_period = (0xfffffffeLLU << 32) / sc->et.et_frequency;
+ sc->et.et_start = a10_timer_timer_start;
+ sc->et.et_stop = a10_timer_timer_stop;
+ sc->et.et_priv = sc;
+ et_register(&sc->et);
+}
+
+static int
+a10_timer_timer_start(struct eventtimer *et, sbintime_t first,
+ sbintime_t period)
+{
+ struct a10_timer_softc *sc;
+ uint32_t count;
+ uint32_t val;
+
+ sc = (struct a10_timer_softc *)et->et_priv;
+
+ if (period != 0)
+ sc->sc_period = ((uint32_t)et->et_frequency * period) >> 32;
+ else
+ sc->sc_period = 0;
+ if (first != 0)
+ count = ((uint32_t)et->et_frequency * first) >> 32;
+ else
+ count = sc->sc_period;
+
+ /* Update timer values */
+ timer_write_4(sc, TIMER_INTV_REG(0), sc->sc_period);
+ timer_write_4(sc, TIMER_CURV_REG(0), count);
+
+ val = timer_read_4(sc, TIMER_CTRL_REG(0));
+ if (period != 0) {
+ /* periodic */
+ val |= TIMER_CTRL_AUTORELOAD;
+ } else {
+ /* oneshot */
+ val &= ~TIMER_CTRL_AUTORELOAD;
+ }
+ /* Enable timer0 */
+ val |= TIMER_IRQ_ENABLE(0);
+ timer_write_4(sc, TIMER_CTRL_REG(0), val);
+
+ return (0);
+}
+
+static int
+a10_timer_timer_stop(struct eventtimer *et)
+{
+ struct a10_timer_softc *sc;
+ uint32_t val;
+
+ sc = (struct a10_timer_softc *)et->et_priv;
+
+ /* Disable timer0 */
+ val = timer_read_4(sc, TIMER_CTRL_REG(0));
+ val &= ~TIMER_CTRL_START;
+ timer_write_4(sc, TIMER_CTRL_REG(0), val);
+
+ sc->sc_period = 0;
+
+ return (0);
+}
+#endif
+
+/*
+ * Timecounter functions for A23 and above
+ */
+
+#if defined(__aarch64__)
+static void
+a23_timer_timecounter_setup(struct a10_timer_softc *sc)
+{
+ uint32_t val;
+
+ /* Set clock source to OSC24M, 1 pre-division, continuous mode */
+ val = timer_read_4(sc, TIMER_CTRL_REG(0));
+ val &= ~TIMER_CTRL_PRESCALAR_MASK | ~TIMER_CTRL_MODE_MASK | ~TIMER_CTRL_CLKSRC_MASK;
+ val |= TIMER_CTRL_PRESCALAR(1) | TIMER_CTRL_OSC24M;
+ timer_write_4(sc, TIMER_CTRL_REG(0), val);
+
+ /* Set reload value */
+ timer_write_4(sc, TIMER_INTV_REG(0), ~0);
+ val = timer_read_4(sc, TIMER_INTV_REG(0));
+
+ /* Enable timer0 */
+ val = timer_read_4(sc, TIMER_CTRL_REG(0));
+ val |= TIMER_CTRL_AUTORELOAD | TIMER_CTRL_START;
+ timer_write_4(sc, TIMER_CTRL_REG(0), val);
+
+ val = timer_read_4(sc, TIMER_CURV_REG(0));
+
+ a23_timer_timecounter.tc_priv = sc;
+ a23_timer_timecounter.tc_frequency = sc->timer0_freq;
+ tc_init(&a23_timer_timecounter);
+}
+
+static u_int
+a23_timer_get_timecount(struct timecounter *tc)
+{
+ struct a10_timer_softc *sc;
+ uint32_t val;
+
+ sc = (struct a10_timer_softc *)tc->tc_priv;
+ if (sc == NULL)
+ return (0);
+
+ val = timer_read_4(sc, TIMER_CURV_REG(0));
+ /* Counter count backwards */
+ return (~0u - val);
+}
+#endif
+
+/*
+ * Timecounter functions for A10 and A13, using the 64 bits counter
+ */
+
+static uint64_t
+timer_read_counter64(struct a10_timer_softc *sc)
+{
+ uint32_t lo, hi;
+
+ /* Latch counter, wait for it to be ready to read. */
+ timer_write_4(sc, CNT64_CTRL_REG, CNT64_CTRL_RL_EN);
+ while (timer_read_4(sc, CNT64_CTRL_REG) & CNT64_CTRL_RL_EN)
+ continue;
+
+ hi = timer_read_4(sc, CNT64_HI_REG);
+ lo = timer_read_4(sc, CNT64_LO_REG);
+
+ return (((uint64_t)hi << 32) | lo);
+}
+
+#if defined(__arm__)
+static void
+a10_timer_delay(int usec, void *arg)
+{
+ struct a10_timer_softc *sc = arg;
+ uint64_t end, now;
+
+ now = timer_read_counter64(sc);
+ end = now + (sc->timer0_freq / 1000000) * (usec + 1);
+
+ while (now < end)
+ now = timer_read_counter64(sc);
+}
+#endif
+
+static u_int
+a10_timer_get_timecount(struct timecounter *tc)
+{
+
+ if (tc->tc_priv == NULL)
+ return (0);
+
+ return ((u_int)timer_read_counter64(tc->tc_priv));
+}
+
+static device_method_t a10_timer_methods[] = {
+ DEVMETHOD(device_probe, a10_timer_probe),
+ DEVMETHOD(device_attach, a10_timer_attach),
+
+ DEVMETHOD_END
+};
+
+static driver_t a10_timer_driver = {
+ "a10_timer",
+ a10_timer_methods,
+ sizeof(struct a10_timer_softc),
+};
+
+static devclass_t a10_timer_devclass;
+
+EARLY_DRIVER_MODULE(a10_timer, simplebus, a10_timer_driver, a10_timer_devclass, 0, 0,
+ BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE);
diff --git a/sys/arm/allwinner/a13/a13_padconf.c b/sys/arm/allwinner/a13/a13_padconf.c
new file mode 100644
index 000000000000..b8a2e3a20c1a
--- /dev/null
+++ b/sys/arm/allwinner/a13/a13_padconf.c
@@ -0,0 +1,128 @@
+/*-
+ * Copyright (c) 2016 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/types.h>
+
+#include <arm/allwinner/allwinner_pinctrl.h>
+
+#ifdef SOC_ALLWINNER_A13
+
+const static struct allwinner_pins a13_pins[] = {
+ {"PB0", 1, 0, {"gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, NULL, NULL}},
+ {"PB1", 1, 1, {"gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, NULL, NULL}},
+ {"PB2", 1, 2, {"gpio_in", "gpio_out", "pwm", NULL, NULL, NULL, "pb_eint16", NULL}, 6, 16, 0},
+ {"PB3", 1, 3, {"gpio_in", "gpio_out", "ir0", NULL, NULL, NULL, "pb_eint17", NULL}, 6, 17, 0},
+ {"PB4", 1, 4, {"gpio_in", "gpio_out", "ir0", NULL, NULL, NULL, "pb_eint18", NULL}, 6, 18, 0},
+ {"PB10", 1, 10, {"gpio_in", "gpio_out", "spi2", NULL, NULL, NULL, "pb_eint24", NULL}, 6, 24, 0},
+ {"PB15", 1, 15, {"gpio_in", "gpio_out", "i2c1", NULL, NULL, NULL, NULL, NULL}},
+ {"PB16", 1, 16, {"gpio_in", "gpio_out", "i2c1", NULL, NULL, NULL, NULL, NULL}},
+ {"PB17", 1, 17, {"gpio_in", "gpio_out", "i2c2", NULL, NULL, NULL, NULL, NULL}},
+ {"PB18", 1, 18, {"gpio_in", "gpio_out", "i2c2", NULL, NULL, NULL, NULL, NULL}},
+
+ {"PC0", 2, 0, {"gpio_in", "gpio_out", "nand", "spi0", NULL, NULL, NULL, NULL}},
+ {"PC1", 2, 1, {"gpio_in", "gpio_out", "nand", "spi0", NULL, NULL, NULL, NULL}},
+ {"PC2", 2, 2, {"gpio_in", "gpio_out", "nand", "spi0", NULL, NULL, NULL, NULL}},
+ {"PC3", 2, 3, {"gpio_in", "gpio_out", "nand", "spi0", NULL, NULL, NULL, NULL}},
+ {"PC4", 2, 4, {"gpio_in", "gpio_out", "nand", NULL, NULL, NULL, NULL, NULL}},
+ {"PC5", 2, 5, {"gpio_in", "gpio_out", "nand", NULL, NULL, NULL, NULL, NULL}},
+ {"PC6", 2, 6, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC7", 2, 7, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC8", 2, 8, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC9", 2, 9, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC10", 2, 10, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC11", 2, 11, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC12", 2, 12, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC13", 2, 13, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC14", 2, 14, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC15", 2, 15, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC19", 2, 19, {"gpio_in", "gpio_out", "nand", NULL, "uart3", NULL, NULL, NULL}},
+
+ {"PD2", 3, 2, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD3", 3, 3, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD4", 3, 4, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD5", 3, 5, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD6", 3, 6, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD7", 3, 7, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD10", 3, 10, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD11", 3, 11, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD12", 3, 12, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD13", 3, 13, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD14", 3, 14, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD15", 3, 15, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD18", 3, 18, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD19", 3, 19, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD20", 3, 20, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD21", 3, 21, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD22", 3, 22, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD23", 3, 23, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD24", 3, 24, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD25", 3, 25, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD26", 3, 26, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD27", 3, 27, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+
+ {"PE0", 4, 0, {"gpio_in", NULL, NULL, "csi0", "spi2", NULL, "pe_eint14", NULL}, 6, 14, 0},
+ {"PE1", 4, 1, {"gpio_in", NULL, NULL, "csi0", "spi2", NULL, "pe_eint15", NULL}, 6, 15, 0},
+ {"PE2", 4, 2, {"gpio_in", NULL, NULL, "csi0", "spi2", NULL, NULL, NULL}},
+ {"PE3", 4, 3, {"gpio_in", "gpio_out", NULL, "csi0", "spi2", NULL, NULL, NULL}},
+ {"PE4", 4, 4, {"gpio_in", "gpio_out", NULL, "csi0", "mmc2", NULL, NULL, NULL}},
+ {"PE5", 4, 5, {"gpio_in", "gpio_out", NULL, "csi0", "mmc2", NULL, NULL, NULL}},
+ {"PE6", 4, 6, {"gpio_in", "gpio_out", NULL, "csi0", "mmc2", NULL, NULL, NULL}},
+ {"PE7", 4, 7, {"gpio_in", "gpio_out", NULL, "csi0", "mmc2", NULL, NULL, NULL}},
+ {"PE8", 4, 8, {"gpio_in", "gpio_out", NULL, "csi0", "mmc2", NULL, NULL, NULL}},
+ {"PE9", 4, 9, {"gpio_in", "gpio_out", NULL, "csi0", "mmc2", NULL, NULL, NULL}},
+ {"PE10", 4, 10, {"gpio_in", "gpio_out", NULL, "csi0", "uart1", NULL, NULL, NULL}},
+ {"PE11", 4, 11, {"gpio_in", "gpio_out", NULL, "csi0", "uart1", NULL, NULL, NULL}},
+
+ {"PF0", 5, 0, {"gpio_in", "gpio_out", "mmc0", NULL, NULL, NULL, NULL, NULL}},
+ {"PF1", 5, 1, {"gpio_in", "gpio_out", "mmc0", NULL, NULL, NULL, NULL, NULL}},
+ {"PF2", 5, 2, {"gpio_in", "gpio_out", "mmc0", NULL, NULL, NULL, NULL, NULL}},
+ {"PF3", 5, 3, {"gpio_in", "gpio_out", "mmc0", NULL, NULL, NULL, NULL, NULL}},
+ {"PF4", 5, 4, {"gpio_in", "gpio_out", "mmc0", NULL, NULL, NULL, NULL, NULL}},
+ {"PF5", 5, 5, {"gpio_in", "gpio_out", "mmc0", NULL, NULL, NULL, NULL, NULL}},
+
+ {"PG0", 6, 0, {"gpio_in", NULL, NULL, NULL, NULL, NULL, "pg_eint0", NULL}, 6, 0, 6},
+ {"PG1", 6, 1, {"gpio_in", NULL, NULL, NULL, NULL, NULL, "pg_eint1", NULL}, 6, 1, 6},
+ {"PG2", 6, 2, {"gpio_in", NULL, NULL, NULL, NULL, NULL, "pg_eint2", NULL}, 6, 2, 6},
+ {"PG3", 6, 3, {"gpio_in", "gpio_out", "mmc1", NULL, "uart1", NULL, "pg_eint3", NULL}, 6, 3, 0},
+ {"PG4", 6, 4, {"gpio_in", "gpio_out", "mmc1", NULL, "uart1", NULL, "pg_eint4", NULL}, 6, 4, 0},
+ {"PG9", 6, 9, {"gpio_in", "gpio_out", "spi1", "uart3", NULL, NULL, "pg_eint9", NULL}, 6, 9, 0},
+ {"PG10", 6, 10, {"gpio_in", "gpio_out", "spi1", "uart3", NULL, NULL, "pg_eint10", NULL}, 6, 10, 0},
+ {"PG11", 6, 11, {"gpio_in", "gpio_out", "spi1", "uart3", NULL, NULL, "pg_eint11", NULL}, 6, 11, 0},
+ {"PG12", 6, 12, {"gpio_in", "gpio_out", "spi1", "uart3", NULL, NULL, "pg_eint12", NULL}, 6, 12, 0},
+};
+
+const struct allwinner_padconf a13_padconf = {
+ .npins = sizeof(a13_pins) / sizeof(struct allwinner_pins),
+ .pins = a13_pins,
+};
+
+#endif /* SOC_ALLWINNER_A13 */
diff --git a/sys/arm/allwinner/a13/files.a13 b/sys/arm/allwinner/a13/files.a13
new file mode 100644
index 000000000000..0a84d63ef7b8
--- /dev/null
+++ b/sys/arm/allwinner/a13/files.a13
@@ -0,0 +1,4 @@
+# $FreeBSD$
+
+arm/allwinner/a13/a13_padconf.c standard
+arm/allwinner/clkng/ccu_a13.c standard
diff --git a/sys/arm/allwinner/a20/a20_cpu_cfg.c b/sys/arm/allwinner/a20/a20_cpu_cfg.c
new file mode 100644
index 000000000000..d26b30cd9449
--- /dev/null
+++ b/sys/arm/allwinner/a20/a20_cpu_cfg.c
@@ -0,0 +1,138 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Ganbold Tsagaankhuu <ganbold@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* CPU configuration module for Allwinner A20 */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+
+#include "a20_cpu_cfg.h"
+
+struct a20_cpu_cfg_softc {
+ struct resource *res;
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+};
+
+static struct a20_cpu_cfg_softc *a20_cpu_cfg_sc = NULL;
+
+#define cpu_cfg_read_4(sc, reg) \
+ bus_space_read_4((sc)->bst, (sc)->bsh, (reg))
+#define cpu_cfg_write_4(sc, reg, val) \
+ bus_space_write_4((sc)->bst, (sc)->bsh, (reg), (val))
+
+static int
+a20_cpu_cfg_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_is_compatible(dev, "allwinner,sun7i-cpu-cfg")) {
+ device_set_desc(dev, "A20 CPU Configuration Module");
+ return(BUS_PROBE_DEFAULT);
+ }
+
+ return (ENXIO);
+}
+
+static int
+a20_cpu_cfg_attach(device_t dev)
+{
+ struct a20_cpu_cfg_softc *sc = device_get_softc(dev);
+ int rid = 0;
+
+ if (a20_cpu_cfg_sc)
+ return (ENXIO);
+
+ sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
+ if (!sc->res) {
+ device_printf(dev, "could not allocate resource\n");
+ return (ENXIO);
+ }
+
+ sc->bst = rman_get_bustag(sc->res);
+ sc->bsh = rman_get_bushandle(sc->res);
+
+ a20_cpu_cfg_sc = sc;
+
+ return (0);
+}
+
+static device_method_t a20_cpu_cfg_methods[] = {
+ DEVMETHOD(device_probe, a20_cpu_cfg_probe),
+ DEVMETHOD(device_attach, a20_cpu_cfg_attach),
+ { 0, 0 }
+};
+
+static driver_t a20_cpu_cfg_driver = {
+ "a20_cpu_cfg",
+ a20_cpu_cfg_methods,
+ sizeof(struct a20_cpu_cfg_softc),
+};
+
+static devclass_t a20_cpu_cfg_devclass;
+
+EARLY_DRIVER_MODULE(a20_cpu_cfg, simplebus, a20_cpu_cfg_driver, a20_cpu_cfg_devclass, 0, 0,
+ BUS_PASS_CPU + BUS_PASS_ORDER_FIRST);
+
+uint64_t
+a20_read_counter64(void)
+{
+ uint32_t lo, hi;
+
+ /* Latch counter, wait for it to be ready to read. */
+ cpu_cfg_write_4(a20_cpu_cfg_sc, OSC24M_CNT64_CTRL_REG, CNT64_RL_EN);
+ while (cpu_cfg_read_4(a20_cpu_cfg_sc, OSC24M_CNT64_CTRL_REG) & CNT64_RL_EN)
+ continue;
+
+ hi = cpu_cfg_read_4(a20_cpu_cfg_sc, OSC24M_CNT64_HIGH_REG);
+ lo = cpu_cfg_read_4(a20_cpu_cfg_sc, OSC24M_CNT64_LOW_REG);
+
+ return (((uint64_t)hi << 32) | lo);
+}
diff --git a/sys/arm/allwinner/a20/a20_cpu_cfg.h b/sys/arm/allwinner/a20/a20_cpu_cfg.h
new file mode 100644
index 000000000000..6f3d400f0edc
--- /dev/null
+++ b/sys/arm/allwinner/a20/a20_cpu_cfg.h
@@ -0,0 +1,69 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Ganbold Tsagaankhuu <ganbold@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _A20_CPU_CFG_H_
+#define _A20_CPU_CFG_H_
+
+#define CPU_CFG_BASE 0xe1c25c00
+
+#define CPU0_RST_CTRL 0x0040
+#define CPU0_CTRL_REG 0x0044
+#define CPU0_STATUS_REG 0x0048
+
+#define CPU1_RST_CTRL 0x0080
+#define CPU1_CTRL_REG 0x0084
+#define CPU1_STATUS_REG 0x0088
+
+#define GENER_CTRL_REG 0x0184
+
+#define EVENT_IN_REG 0x0190
+#define PRIVATE_REG 0x01a4
+
+#define IDLE_CNT0_LOW_REG 0x0200
+#define IDLE_CNT0_HIGH_REG 0x0204
+#define IDLE_CNT0_CTRL_REG 0x0208
+
+#define IDLE_CNT1_LOW_REG 0x0210
+#define IDLE_CNT1_HIGH_REG 0x0214
+#define IDLE_CNT1_CTRL_REG 0x0218
+
+#define OSC24M_CNT64_CTRL_REG 0x0280
+#define OSC24M_CNT64_LOW_REG 0x0284
+#define OSC24M_CNT64_HIGH_REG 0x0288
+
+#define LOSC_CNT64_CTRL_REG 0x0290
+#define LOSC_CNT64_LOW_REG 0x0294
+#define LOSC_CNT64_HIGH_REG 0x0298
+
+#define CNT64_RL_EN 0x02 /* read latch enable */
+
+uint64_t a20_read_counter64(void);
+
+#endif /* _A20_CPU_CFG_H_ */
diff --git a/sys/arm/allwinner/a20/a20_padconf.c b/sys/arm/allwinner/a20/a20_padconf.c
new file mode 100644
index 000000000000..a6d58b40a8b1
--- /dev/null
+++ b/sys/arm/allwinner/a20/a20_padconf.c
@@ -0,0 +1,230 @@
+/*-
+ * Copyright (c) 2016 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/types.h>
+
+#include <arm/allwinner/allwinner_pinctrl.h>
+
+#ifdef SOC_ALLWINNER_A20
+
+const static struct allwinner_pins a20_pins[] = {
+ {"PA0", 0, 0, {"gpio_in", "gpio_out", "emac", "spi1", "uart2", "gmac", NULL, NULL}},
+ {"PA1", 0, 1, {"gpio_in", "gpio_out", "emac", "spi1", "uart2", "gmac", NULL, NULL}},
+ {"PA2", 0, 2, {"gpio_in", "gpio_out", "emac", "spi1", "uart2", "gmac", NULL, NULL}},
+ {"PA3", 0, 3, {"gpio_in", "gpio_out", "emac", "spi1", "uart2", "gmac", NULL, NULL}},
+ {"PA4", 0, 4, {"gpio_in", "gpio_out", "emac", "spi1", NULL, "gmac", NULL, NULL}},
+ {"PA5", 0, 5, {"gpio_in", "gpio_out", "emac", "spi3", NULL, "gmac", NULL, NULL}},
+ {"PA6", 0, 6, {"gpio_in", "gpio_out", "emac", "spi3", NULL, "gmac", NULL, NULL}},
+ {"PA7", 0, 7, {"gpio_in", "gpio_out", "emac", "spi3", NULL, "gmac", NULL, NULL}},
+ {"PA8", 0, 8, {"gpio_in", "gpio_out", "emac", "spi3", NULL, "gmac", NULL, NULL}},
+ {"PA9", 0, 9, {"gpio_in", "gpio_out", "emac", "spi3", NULL, "gmac", "i2c1", NULL}},
+ {"PA10", 0, 10, {"gpio_in", "gpio_out", "emac", NULL, "uart1", "gmac", NULL, NULL}},
+ {"PA11", 0, 11, {"gpio_in", "gpio_out", "emac", NULL, "uart1", "gmac", NULL, NULL}},
+ {"PA12", 0, 12, {"gpio_in", "gpio_out", "emac", "uart6", "uart1", "gmac", NULL, NULL}},
+ {"PA13", 0, 13, {"gpio_in", "gpio_out", "emac", "uart6", "uart1", "gmac", NULL, NULL}},
+ {"PA14", 0, 14, {"gpio_in", "gpio_out", "emac", "uart7", "uart1", "gmac", "i2c1", NULL}},
+ {"PA15", 0, 15, {"gpio_in", "gpio_out", "emac", "uart7", "uart1", "gmac", "i2c1", NULL}},
+ {"PA16", 0, 16, {"gpio_in", "gpio_out", NULL, "can", "uart1", "gmac", "i2c1", NULL}},
+ {"PA17", 0, 17, {"gpio_in", "gpio_out", NULL, "can", "uart1", "gmac", "i2c1", NULL}},
+
+ {"PB0", 1, 0, {"gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, NULL, NULL}},
+ {"PB1", 1, 1, {"gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, NULL, NULL}},
+ {"PB2", 1, 2, {"gpio_in", "gpio_out", "pwm", NULL, NULL, NULL, NULL, NULL}},
+ {"PB3", 1, 3, {"gpio_in", "gpio_out", "ir0", NULL, "spdif", NULL, NULL, NULL}},
+ {"PB4", 1, 4, {"gpio_in", "gpio_out", "ir0", NULL, NULL, NULL, NULL, NULL}},
+ {"PB5", 1, 5, {"gpio_in", "gpio_out", "i2s0", "ac97", NULL, NULL, NULL, NULL}},
+ {"PB6", 1, 6, {"gpio_in", "gpio_out", "i2c0", "ac97", NULL, NULL, NULL, NULL}},
+ {"PB7", 1, 7, {"gpio_in", "gpio_out", "i2c0", "ac97", NULL, NULL, NULL, NULL}},
+ {"PB8", 1, 8, {"gpio_in", "gpio_out", "i2c0", "ac97", NULL, NULL, NULL, NULL}},
+ {"PB9", 1, 9, {"gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, NULL, NULL}},
+ {"PB10", 1, 10, {"gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, NULL, NULL}},
+ {"PB11", 1, 11, {"gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, NULL, NULL}},
+ {"PB12", 1, 12, {"gpio_in", "gpio_out", "i2c0", "ac97", "spdif", NULL, NULL, NULL}},
+ {"PB13", 1, 13, {"gpio_in", "gpio_out", "spi2", NULL, "spdif", NULL, NULL, NULL}},
+ {"PB14", 1, 14, {"gpio_in", "gpio_out", "spi2", "jtag", NULL, NULL, NULL, NULL}},
+ {"PB15", 1, 15, {"gpio_in", "gpio_out", "spi2", "jtag", NULL, NULL, NULL, NULL}},
+ {"PB16", 1, 16, {"gpio_in", "gpio_out", "spi2", "jtag", NULL, NULL, NULL, NULL}},
+ {"PB17", 1, 17, {"gpio_in", "gpio_out", "spi2", "jtag", NULL, NULL, NULL, NULL}},
+ {"PB18", 1, 18, {"gpio_in", "gpio_out", "i2c1", NULL, NULL, NULL, NULL, NULL}},
+ {"PB19", 1, 19, {"gpio_in", "gpio_out", "i2c1", NULL, NULL, NULL, NULL, NULL}},
+ {"PB20", 1, 20, {"gpio_in", "gpio_out", "i2c2", NULL, NULL, NULL, NULL, NULL}},
+ {"PB21", 1, 21, {"gpio_in", "gpio_out", "i2c2", NULL, NULL, NULL, NULL, NULL}},
+ {"PB22", 1, 22, {"gpio_in", "gpio_out", "uart0", "ir1", NULL, NULL, NULL, NULL}},
+ {"PB23", 1, 23, {"gpio_in", "gpio_out", "uart0", "ir1", NULL, NULL, NULL, NULL}},
+
+ {"PC0", 2, 0, {"gpio_in", "gpio_out", "nand0", "spi0", NULL, NULL, NULL, NULL}},
+ {"PC1", 2, 1, {"gpio_in", "gpio_out", "nand0", "spi0", NULL, NULL, NULL, NULL}},
+ {"PC2", 2, 2, {"gpio_in", "gpio_out", "nand0", "spi0", NULL, NULL, NULL, NULL}},
+ {"PC3", 2, 3, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}},
+ {"PC4", 2, 4, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}},
+ {"PC5", 2, 5, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}},
+ {"PC6", 2, 6, {"gpio_in", "gpio_out", "nand0", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC7", 2, 7, {"gpio_in", "gpio_out", "nand0", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC8", 2, 8, {"gpio_in", "gpio_out", "nand0", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC9", 2, 9, {"gpio_in", "gpio_out", "nand0", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC10", 2, 10, {"gpio_in", "gpio_out", "nand0", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC11", 2, 11, {"gpio_in", "gpio_out", "nand0", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC12", 2, 12, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}},
+ {"PC13", 2, 13, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}},
+ {"PC14", 2, 14, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}},
+ {"PC15", 2, 15, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}},
+ {"PC16", 2, 16, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}},
+ {"PC17", 2, 17, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}},
+ {"PC18", 2, 18, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}},
+ {"PC19", 2, 19, {"gpio_in", "gpio_out", "nand0", "spi2", NULL, NULL, NULL, NULL}},
+ {"PC20", 2, 20, {"gpio_in", "gpio_out", "nand0", "spi2", NULL, NULL, NULL, NULL}},
+ {"PC21", 2, 21, {"gpio_in", "gpio_out", "nand0", "spi2", NULL, NULL, NULL, NULL}},
+ {"PC22", 2, 22, {"gpio_in", "gpio_out", "nand0", "spi2", NULL, NULL, NULL, NULL}},
+ {"PC23", 2, 23, {"gpio_in", "gpio_out", NULL, "spi0", NULL, NULL, NULL, NULL}},
+ {"PC24", 2, 24, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}},
+
+ {"PD0", 3, 0, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD1", 3, 1, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD2", 3, 2, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD3", 3, 3, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD4", 3, 4, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD5", 3, 5, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD6", 3, 6, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD7", 3, 7, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD8", 3, 8, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD9", 3, 9, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD10", 3, 10, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD11", 3, 11, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD12", 3, 12, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD13", 3, 13, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD14", 3, 14, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD15", 3, 15, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD16", 3, 16, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD17", 3, 17, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD18", 3, 18, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD19", 3, 19, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD20", 3, 20, {"gpio_in", "gpio_out", "lcd0", "csi1", NULL, NULL, NULL, NULL}},
+ {"PD21", 3, 21, {"gpio_in", "gpio_out", "lcd0", "sim", NULL, NULL, NULL, NULL}},
+ {"PD22", 3, 22, {"gpio_in", "gpio_out", "lcd0", "sim", NULL, NULL, NULL, NULL}},
+ {"PD23", 3, 23, {"gpio_in", "gpio_out", "lcd0", "sim", NULL, NULL, NULL, NULL}},
+ {"PD24", 3, 24, {"gpio_in", "gpio_out", "lcd0", "sim", NULL, NULL, NULL, NULL}},
+ {"PD25", 3, 25, {"gpio_in", "gpio_out", "lcd0", "sim", NULL, NULL, NULL, NULL}},
+ {"PD26", 3, 26, {"gpio_in", "gpio_out", "lcd0", "sim", NULL, NULL, NULL, NULL}},
+ {"PD27", 3, 27, {"gpio_in", "gpio_out", "lcd0", "sim", NULL, NULL, NULL, NULL}},
+
+ {"PE0", 4, 0, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}},
+ {"PE1", 4, 1, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}},
+ {"PE2", 4, 2, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}},
+ {"PE3", 4, 3, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}},
+ {"PE4", 4, 4, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}},
+ {"PE5", 4, 5, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}},
+ {"PE6", 4, 6, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}},
+ {"PE7", 4, 7, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}},
+ {"PE8", 4, 8, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}},
+ {"PE9", 4, 9, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}},
+ {"PE10", 4, 10, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}},
+ {"PE11", 4, 11, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}},
+
+ {"PF0", 5, 0, {"gpio_in", "gpio_out", "mmc0", NULL, "jtag", NULL, NULL, NULL}},
+ {"PF1", 5, 1, {"gpio_in", "gpio_out", "mmc0", NULL, "jtag", NULL, NULL, NULL}},
+ {"PF2", 5, 2, {"gpio_in", "gpio_out", "mmc0", NULL, "uart0", NULL, NULL, NULL}},
+ {"PF3", 5, 3, {"gpio_in", "gpio_out", "mmc0", NULL, "jtag", NULL, NULL, NULL}},
+ {"PF4", 5, 4, {"gpio_in", "gpio_out", "mmc0", NULL, "uart0", NULL, NULL, NULL}},
+ {"PF5", 5, 5, {"gpio_in", "gpio_out", "mmc0", NULL, "jtag", NULL, NULL, NULL}},
+
+ {"PG0", 6, 0, {"gpio_in", "gpio_out", "ts1", "csi1", "mmc1", NULL, NULL, NULL}},
+ {"PG1", 6, 1, {"gpio_in", "gpio_out", "ts1", "csi1", "mmc1", NULL, NULL, NULL}},
+ {"PG2", 6, 2, {"gpio_in", "gpio_out", "ts1", "csi1", "mmc1", NULL, NULL, NULL}},
+ {"PG3", 6, 3, {"gpio_in", "gpio_out", "ts1", "csi1", "mmc1", NULL, NULL, NULL}},
+ {"PG4", 6, 4, {"gpio_in", "gpio_out", "ts1", "csi1", "mmc1", "csi0", NULL, NULL}},
+ {"PG5", 6, 5, {"gpio_in", "gpio_out", "ts1", "csi1", "mmc1", "csi0", NULL, NULL}},
+ {"PG6", 6, 6, {"gpio_in", "gpio_out", "ts1", "csi1", "uart3", "csi0", NULL, NULL}},
+ {"PG7", 6, 7, {"gpio_in", "gpio_out", "ts1", "csi1", "uart3", "csi0", NULL, NULL}},
+ {"PG8", 6, 8, {"gpio_in", "gpio_out", "ts1", "csi1", "uart3", "csi0", NULL, NULL}},
+ {"PG9", 6, 9, {"gpio_in", "gpio_out", "ts1", "csi1", "uart3", "csi0", NULL, NULL}},
+ {"PG10", 6, 10, {"gpio_in", "gpio_out", "ts1", "csi1", "uart4", "csi0", NULL, NULL}},
+ {"PG11", 6, 11, {"gpio_in", "gpio_out", "ts1", "csi1", "uart4", "csi0", NULL, NULL}},
+
+ {"PH0", 7, 0, {"gpio_in", "gpio_out", "lcd1", NULL, "uart3", NULL, "ph_eint0", "csi1"}, 6, 0, 0},
+ {"PH1", 7, 1, {"gpio_in", "gpio_out", "lcd1", NULL, "uart3", NULL, "ph_eint1", "csi1"}, 6, 1, 0},
+ {"PH2", 7, 2, {"gpio_in", "gpio_out", "lcd1", NULL, "uart3", NULL, "ph_eint2", "csi1"}, 6, 2, 0},
+ {"PH3", 7, 3, {"gpio_in", "gpio_out", "lcd1", NULL, "uart3", NULL, "ph_eint3", "csi1"}, 6, 3, 0},
+ {"PH4", 7, 4, {"gpio_in", "gpio_out", "lcd1", NULL, "uart4", NULL, "ph_eint4", "csi1"}, 6, 4, 0},
+ {"PH5", 7, 5, {"gpio_in", "gpio_out", "lcd1", NULL, "uart4", NULL, "ph_eint5", "csi1"}, 6, 5, 0},
+ {"PH6", 7, 6, {"gpio_in", "gpio_out", "lcd1", NULL, "uart5", "ms", "ph_eint6", "csi1"}, 6, 6, 0},
+ {"PH7", 7, 7, {"gpio_in", "gpio_out", "lcd1", NULL, "uart5", "ms", "ph_eint7", "csi1"}, 6, 7, 0},
+ {"PH8", 7, 8, {"gpio_in", "gpio_out", "lcd1", NULL, "keypad", "ms", "ph_eint8", "csi1"}, 6, 8, 0},
+ {"PH9", 7, 9, {"gpio_in", "gpio_out", "lcd1", NULL, "keypad", "ms", "ph_eint9", "csi1"}, 6, 9, 0},
+ {"PH10", 7, 10, {"gpio_in", "gpio_out", "lcd1", NULL, "keypad", "ms", "ph_eint10", "csi1"}, 6, 10, 0},
+ {"PH11", 7, 11, {"gpio_in", "gpio_out", "lcd1", NULL, "keypad", "ms", "ph_eint11", "csi1"}, 6, 11, 0},
+ {"PH12", 7, 12, {"gpio_in", "gpio_out", "lcd1", NULL, "ps2", NULL, "ph_eint12", "csi1"}, 6, 12, 0},
+ {"PH13", 7, 13, {"gpio_in", "gpio_out", "lcd1", NULL, "ps2", "sim", "ph_eint13", "csi1"}, 6, 13, 0},
+ {"PH14", 7, 14, {"gpio_in", "gpio_out", "lcd1", NULL, "keypad", "sim", "ph_eint14", "csi1"}, 6, 14, 0},
+ {"PH15", 7, 15, {"gpio_in", "gpio_out", "lcd1", NULL, "keypad", "sim", "ph_eint15", "csi1"}, 6, 15, 0},
+ {"PH16", 7, 16, {"gpio_in", "gpio_out", "lcd1", NULL, "keypad", "sim", "ph_eint16", "csi1"}, 6, 16, 0},
+ {"PH17", 7, 17, {"gpio_in", "gpio_out", "lcd1", NULL, "keypad", "sim", "ph_eint17", "csi1"}, 6, 17, 0},
+ {"PH18", 7, 18, {"gpio_in", "gpio_out", "lcd1", NULL, "keypad", "sim", "ph_eint18", "csi1"}, 6, 18, 0},
+ {"PH19", 7, 19, {"gpio_in", "gpio_out", "lcd1", NULL, "keypad", "sim", "ph_eint19", "csi1"}, 6, 19, 0},
+ {"PH20", 7, 20, {"gpio_in", "gpio_out", "lcd1", NULL, "can", NULL, "ph_eint20", "csi1"}, 6, 20, 0},
+ {"PH21", 7, 21, {"gpio_in", "gpio_out", "lcd1", NULL, "can", NULL, "ph_eint21", "csi1"}, 6, 21, 0},
+ {"PH22", 7, 22, {"gpio_in", "gpio_out", "lcd1", NULL, "keypad", "mmc1", NULL, "csi1"}},
+ {"PH23", 7, 23, {"gpio_in", "gpio_out", "lcd1", NULL, "keypad", "mmc1", NULL, "csi1"}},
+ {"PH24", 7, 24, {"gpio_in", "gpio_out", "lcd1", NULL, "keypad", "mmc1", NULL, "csi1"}},
+ {"PH25", 7, 25, {"gpio_in", "gpio_out", "lcd1", NULL, "keypad", "mmc1", NULL, "csi1"}},
+ {"PH26", 7, 26, {"gpio_in", "gpio_out", "lcd1", NULL, "keypad", "mmc1", NULL, "csi1"}},
+ {"PH27", 7, 27, {"gpio_in", "gpio_out", "lcd1", NULL, "keypad", "mmc1", NULL, "csi1"}},
+
+ {"PI0", 8, 0, {"gpio_in", "gpio_out", NULL, "i2c3", NULL, NULL, NULL, NULL}},
+ {"PI1", 8, 1, {"gpio_in", "gpio_out", NULL, "i2c3", NULL, NULL, NULL, NULL}},
+ {"PI2", 8, 2, {"gpio_in", "gpio_out", NULL, "i2c4", NULL, NULL, NULL, NULL}},
+ {"PI3", 8, 3, {"gpio_in", "gpio_out", "pwm", "i2c4", NULL, NULL, NULL, NULL}},
+ {"PI4", 8, 4, {"gpio_in", "gpio_out", "mmc3", NULL, NULL, NULL, NULL, NULL}},
+ {"PI5", 8, 5, {"gpio_in", "gpio_out", "mmc3", NULL, NULL, NULL, NULL, NULL}},
+ {"PI6", 8, 6, {"gpio_in", "gpio_out", "mmc3", NULL, NULL, NULL, NULL, NULL}},
+ {"PI7", 8, 7, {"gpio_in", "gpio_out", "mmc3", NULL, NULL, NULL, NULL, NULL}},
+ {"PI8", 8, 8, {"gpio_in", "gpio_out", "mmc3", NULL, NULL, NULL, NULL, NULL}},
+ {"PI9", 8, 9, {"gpio_in", "gpio_out", "mmc3", NULL, NULL, NULL, NULL, NULL}},
+ {"PI10", 8, 10, {"gpio_in", "gpio_out", "spi0", "uart5", NULL, NULL, "pi_eint22", NULL}, 6, 22, 0},
+ {"PI11", 8, 11, {"gpio_in", "gpio_out", "spi0", "uart5", NULL, NULL, "pi_eint23", NULL}, 6, 23, 0},
+ {"PI12", 8, 12, {"gpio_in", "gpio_out", "spi0", "uart6", "clk_out_a", NULL, "pi_eint24", NULL}, 6, 24, 0},
+ {"PI13", 8, 13, {"gpio_in", "gpio_out", "spi0", "uart6", "clk_out_b", NULL, "pi_eint25", NULL}, 6, 25, 0},
+ {"PI14", 8, 14, {"gpio_in", "gpio_out", "spi0", "ps2", "timer4", NULL, "pi_eint26", NULL}, 6, 26, 0},
+ {"PI15", 8, 15, {"gpio_in", "gpio_out", "spi1", "ps2", "timer5", NULL, "pi_eint27", NULL}, 6, 27, 0},
+ {"PI16", 8, 16, {"gpio_in", "gpio_out", "spi1", "uart2", NULL, NULL, "pi_eint28", NULL}, 6, 28, 0},
+ {"PI17", 8, 17, {"gpio_in", "gpio_out", "spi1", "uart2", NULL, NULL, "pi_eint29", NULL}, 6, 29, 0},
+ {"PI18", 8, 18, {"gpio_in", "gpio_out", "spi1", "uart2", NULL, NULL, "pi_eint30", NULL}, 6, 30, 0},
+ {"PI19", 8, 19, {"gpio_in", "gpio_out", "spi1", "uart2", NULL, NULL, "pi_eint31", NULL}, 6, 31, 0},
+ {"PI20", 8, 20, {"gpio_in", "gpio_out", "ps2", "uart7", "hdmi", NULL, NULL, NULL}},
+ {"PI21", 8, 21, {"gpio_in", "gpio_out", "ps2", "uart7", "hdmi", NULL, NULL, NULL}},
+};
+
+const struct allwinner_padconf a20_padconf = {
+ .npins = sizeof(a20_pins) / sizeof(struct allwinner_pins),
+ .pins = a20_pins,
+};
+
+#endif /* SOC_ALLWINNER_A20 */
diff --git a/sys/arm/allwinner/a20/files.a20 b/sys/arm/allwinner/a20/files.a20
new file mode 100644
index 000000000000..6cc952bd6ee9
--- /dev/null
+++ b/sys/arm/allwinner/a20/files.a20
@@ -0,0 +1,4 @@
+# $FreeBSD$
+
+arm/allwinner/a20/a20_padconf.c standard
+arm/allwinner/clkng/ccu_a10.c standard
diff --git a/sys/arm/allwinner/a31/a31_padconf.c b/sys/arm/allwinner/a31/a31_padconf.c
new file mode 100644
index 000000000000..01207fd767da
--- /dev/null
+++ b/sys/arm/allwinner/a31/a31_padconf.c
@@ -0,0 +1,219 @@
+/*-
+ * Copyright (c) 2016 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/types.h>
+
+#include <arm/allwinner/allwinner_pinctrl.h>
+
+#ifdef SOC_ALLWINNER_A31
+
+const static struct allwinner_pins a31_pins[] = {
+ {"PA0", 0, 0, {"gpio_in", "gpio_out", "gmac", "lcd1", "uart1", NULL, "pa_eint0", NULL}, 6, 0, 0},
+ {"PA1", 0, 1, {"gpio_in", "gpio_out", "gmac", "lcd1", "uart1", NULL, "pa_eint1", NULL}, 6, 1, 0},
+ {"PA2", 0, 2, {"gpio_in", "gpio_out", "gmac", "lcd1", "uart1", NULL, "pa_eint2", NULL}, 6, 2, 0},
+ {"PA3", 0, 3, {"gpio_in", "gpio_out", "gmac", "lcd1", "uart1", NULL, "pa_eint3", NULL}, 6, 3, 0},
+ {"PA4", 0, 4, {"gpio_in", "gpio_out", "gmac", "lcd1", "uart1", NULL, "pa_eint4", NULL}, 6, 4, 0},
+ {"PA5", 0, 5, {"gpio_in", "gpio_out", "gmac", "lcd1", "uart1", NULL, "pa_eint5", NULL}, 6, 5, 0},
+ {"PA6", 0, 6, {"gpio_in", "gpio_out", "gmac", "lcd1", "uart1", NULL, "pa_eint6", NULL}, 6, 6, 0},
+ {"PA7", 0, 7, {"gpio_in", "gpio_out", "gmac", "lcd1", "uart1", NULL, "pa_eint7", NULL}, 6, 7, 0},
+ {"PA8", 0, 8, {"gpio_in", "gpio_out", "gmac", "lcd1", NULL, NULL, "pa_eint8", NULL}, 6, 8, 0},
+ {"PA9", 0, 9, {"gpio_in", "gpio_out", "gmac", "lcd1", NULL, "mmc2", "pa_eint9", NULL}, 6, 9, 0},
+ {"PA10", 0, 10, {"gpio_in", "gpio_out", "gmac", "lcd1", "mmc3", "mmc2", "pa_eint10", NULL}, 6, 10, 0},
+ {"PA11", 0, 11, {"gpio_in", "gpio_out", "gmac", "lcd1", "mmc3", "mmc2", "pa_eint11", NULL}, 6, 11, 0},
+ {"PA12", 0, 12, {"gpio_in", "gpio_out", "gmac", "lcd1", "mmc3", "mmc2", "pa_eint12", NULL}, 6, 12, 0},
+ {"PA13", 0, 13, {"gpio_in", "gpio_out", "gmac", "lcd1", "mmc3", "mmc2", "pa_eint13", NULL}, 6, 13, 0},
+ {"PA14", 0, 14, {"gpio_in", "gpio_out", "gmac", "lcd1", "mmc3", "mmc2", "pa_eint14", NULL}, 6, 14, 0},
+ {"PA15", 0, 15, {"gpio_in", "gpio_out", "gmac", "lcd1", "clk_out_a", NULL, "pa_eint15", NULL}, 6, 15, 0},
+ {"PA16", 0, 16, {"gpio_in", "gpio_out", "gmac", "lcd1", "dmic", NULL, "pa_eint16", NULL}, 6, 16, 0},
+ {"PA17", 0, 17, {"gpio_in", "gpio_out", "gmac", "lcd1", "dmic", NULL, "pa_eint17", NULL}, 6, 17, 0},
+ {"PA18", 0, 18, {"gpio_in", "gpio_out", "gmac", "lcd1", "clk_out_b", NULL, "pa_eint18", NULL}, 6, 18, 0},
+ {"PA19", 0, 19, {"gpio_in", "gpio_out", "gmac", "lcd1", "pwm3", NULL, "pa_eint19", NULL}, 6, 19, 0},
+ {"PA20", 0, 20, {"gpio_in", "gpio_out", "gmac", "lcd1", "pwm3", NULL, "pa_eint20", NULL}, 6, 20, 0},
+ {"PA21", 0, 21, {"gpio_in", "gpio_out", "gmac", "lcd1", "spi3", NULL, "pa_eint21", NULL}, 6, 21, 0},
+ {"PA22", 0, 22, {"gpio_in", "gpio_out", "gmac", "lcd1", "spi3", NULL, "pa_eint22", NULL}, 6, 22, 0},
+ {"PA23", 0, 23, {"gpio_in", "gpio_out", "gmac", "lcd1", "spi3", NULL, "pa_eint23", NULL}, 6, 23, 0},
+ {"PA24", 0, 24, {"gpio_in", "gpio_out", "gmac", "lcd1", "spi3", NULL, "pa_eint24", NULL}, 6, 24, 0},
+ {"PA25", 0, 25, {"gpio_in", "gpio_out", "gmac", "lcd1", "spi3", NULL, "pa_eint25", NULL}, 6, 25, 0},
+ {"PA26", 0, 26, {"gpio_in", "gpio_out", "gmac", "lcd1", "clk_out_c", NULL, "pa_eint26", NULL}, 6, 26, 0},
+ {"PA27", 0, 27, {"gpio_in", "gpio_out", "gmac", "lcd1", NULL, NULL, "pa_eint27", NULL}, 6, 27, 0},
+
+ {"PB0", 1, 0, {"gpio_in", "gpio_out", "i2s0", "uart3", "csi", NULL, "pb_eint0", NULL}, 6, 0, 1},
+ {"PB1", 1, 1, {"gpio_in", "gpio_out", "i2s0", NULL, NULL, NULL, "pb_eint1", NULL}, 6, 1, 1},
+ {"PB2", 1, 2, {"gpio_in", "gpio_out", "i2s0", NULL, NULL, NULL, "pb_eint2", NULL}, 6, 2, 1},
+ {"PB3", 1, 3, {"gpio_in", "gpio_out", "i2s0", NULL, NULL, NULL, "pb_eint3", NULL}, 6, 3, 1},
+ {"PB4", 1, 4, {"gpio_in", "gpio_out", "i2s0", "uart3", NULL, NULL, "pb_eint4", NULL}, 6, 4, 1},
+ {"PB5", 1, 5, {"gpio_in", "gpio_out", "i2s0", "uart3", "i2c3", NULL, "pb_eint5", NULL}, 6, 5, 1},
+ {"PB6", 1, 6, {"gpio_in", "gpio_out", "i2s0", "uart3", "i2c3", NULL, "pb_eint6", NULL}, 6, 6, 1},
+ {"PB7", 1, 7, {"gpio_in", "gpio_out", "i2s0", NULL, NULL, NULL, "pb_eint7", NULL}, 6, 7, 1},
+
+ {"PC0", 2, 0, {"gpio_in", "gpio_out", "nand0", "spi0", NULL, NULL, NULL, NULL}},
+ {"PC1", 2, 1, {"gpio_in", "gpio_out", "nand0", "spi0", NULL, NULL, NULL, NULL}},
+ {"PC2", 2, 2, {"gpio_in", "gpio_out", "nand0", "spi0", NULL, NULL, NULL, NULL}},
+ {"PC3", 2, 3, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}},
+ {"PC4", 2, 4, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}},
+ {"PC5", 2, 5, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}},
+ {"PC6", 2, 6, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}},
+ {"PC7", 2, 7, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}},
+ {"PC8", 2, 8, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}},
+ {"PC9", 2, 9, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}},
+ {"PC10", 2, 10, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}},
+ {"PC11", 2, 11, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}},
+ {"PC12", 2, 12, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}},
+ {"PC13", 2, 13, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}},
+ {"PC14", 2, 14, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}},
+ {"PC15", 2, 15, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}},
+ {"PC16", 2, 16, {"gpio_in", "gpio_out", "nand0", "nand1", NULL, NULL, NULL, NULL}},
+ {"PC17", 2, 17, {"gpio_in", "gpio_out", "nand0", "nand1", NULL, NULL, NULL, NULL}},
+ {"PC18", 2, 18, {"gpio_in", "gpio_out", "nand0", "nand1", NULL, NULL, NULL, NULL}},
+ {"PC19", 2, 19, {"gpio_in", "gpio_out", "nand0", "nand1", NULL, NULL, NULL, NULL}},
+ {"PC20", 2, 20, {"gpio_in", "gpio_out", "nand0", "nand1", NULL, NULL, NULL, NULL}},
+ {"PC21", 2, 21, {"gpio_in", "gpio_out", "nand0", "nand1", NULL, NULL, NULL, NULL}},
+ {"PC22", 2, 22, {"gpio_in", "gpio_out", "nand0", "nand1", NULL, NULL, NULL, NULL}},
+ {"PC23", 2, 23, {"gpio_in", "gpio_out", "nand0", "nand1", NULL, NULL, NULL, NULL}},
+ {"PC24", 2, 24, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}},
+ {"PC25", 2, 25, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}},
+ {"PC26", 2, 26, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}},
+ {"PC27", 2, 27, {"gpio_in", "gpio_out", NULL, "spi0",NULL, NULL, NULL, NULL}},
+
+ {"PD0", 3, 0, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD1", 3, 1, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD2", 3, 2, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD3", 3, 3, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD4", 3, 4, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD5", 3, 5, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD6", 3, 6, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD7", 3, 7, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD8", 3, 8, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD9", 3, 9, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD10", 3, 10, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}},
+ {"PD11", 3, 11, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}},
+ {"PD12", 3, 12, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}},
+ {"PD13", 3, 13, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}},
+ {"PD14", 3, 14, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}},
+ {"PD15", 3, 15, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}},
+ {"PD16", 3, 16, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}},
+ {"PD17", 3, 17, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}},
+ {"PD18", 3, 18, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}},
+ {"PD19", 3, 19, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}},
+ {"PD20", 3, 20, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD21", 3, 21, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD22", 3, 22, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD23", 3, 23, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD24", 3, 24, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD25", 3, 25, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD26", 3, 26, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD27", 3, 27, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+
+ {"PE0", 4, 0, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, "pe_eint0", NULL}, 6, 0, 4},
+ {"PE1", 4, 1, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, "pe_eint1", NULL}, 6, 1, 4},
+ {"PE2", 4, 2, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, "pe_eint2", NULL}, 6, 2, 4},
+ {"PE3", 4, 3, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, "pe_eint3", NULL}, 6, 3, 4},
+ {"PE4", 4, 4, {"gpio_in", "gpio_out", "csi", "uart5", NULL, NULL, "pe_eint4", NULL}, 6, 4, 4},
+ {"PE5", 4, 5, {"gpio_in", "gpio_out", "csi", "uart5", NULL, NULL, "pe_eint5", NULL}, 6, 5, 4},
+ {"PE6", 4, 6, {"gpio_in", "gpio_out", "csi", "uart5", NULL, NULL, "pe_eint6", NULL}, 6, 6, 4},
+ {"PE7", 4, 7, {"gpio_in", "gpio_out", "csi", "uart5", NULL, NULL, "pe_eint7", NULL}, 6, 7, 4},
+ {"PE8", 4, 8, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, "pe_eint8", NULL}, 6, 8, 4},
+ {"PE9", 4, 9, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, "pe_eint9", NULL}, 6, 9, 4},
+ {"PE10", 4, 10, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, "pe_eint10", NULL}, 6, 10, 4},
+ {"PE11", 4, 11, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, "pe_eint11", NULL}, 6, 11, 4},
+ {"PE12", 4, 12, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, "pe_eint12", NULL}, 6, 12, 4},
+ {"PE13", 4, 13, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, "pe_eint13", NULL}, 6, 13, 4},
+ {"PE14", 4, 14, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, "pe_eint14", NULL}, 6, 14, 4},
+ {"PE15", 4, 15, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, "pe_eint15", NULL}, 6, 15, 4},
+ {"PE16", 4, 16, {"gpio_in", "gpio_out", "csi", NULL, NULL, NULL, "pe_eint16", NULL}, 6, 16, 4},
+
+ {"PF0", 5, 0, {"gpio_in", "gpio_out", "mmc0", NULL, "jtag", NULL, NULL, NULL}},
+ {"PF1", 5, 1, {"gpio_in", "gpio_out", "mmc0", NULL, "jtag", NULL, NULL, NULL}},
+ {"PF2", 5, 2, {"gpio_in", "gpio_out", "mmc0", NULL, "uart0", NULL, NULL, NULL}},
+ {"PF3", 5, 3, {"gpio_in", "gpio_out", "mmc0", NULL, "jtag", NULL, NULL, NULL}},
+ {"PF4", 5, 4, {"gpio_in", "gpio_out", "mmc0", NULL, "uart0", NULL, NULL, NULL}},
+ {"PF5", 5, 5, {"gpio_in", "gpio_out", "mmc0", NULL, "jtag", NULL, NULL, NULL}},
+
+ {"PG0", 6, 0, {"gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "pg_eint0", NULL}, 6, 0, 6},
+ {"PG1", 6, 1, {"gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "pg_eint1", NULL}, 6, 1, 6},
+ {"PG2", 6, 2, {"gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "pg_eint2", NULL}, 6, 2, 6},
+ {"PG3", 6, 3, {"gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "pg_eint3", NULL}, 6, 3, 6},
+ {"PG4", 6, 4, {"gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "pg_eint4", NULL}, 6, 4, 6},
+ {"PG5", 6, 5, {"gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "pg_eint5", NULL}, 6, 5, 6},
+ {"PG6", 6, 6, {"gpio_in", "gpio_out", "uart2", NULL, NULL, NULL, "pg_eint6", NULL}, 6, 6, 6},
+ {"PG7", 6, 7, {"gpio_in", "gpio_out", "uart2", NULL, NULL, NULL, "pg_eint7", NULL}, 6, 7, 6},
+ {"PG8", 6, 8, {"gpio_in", "gpio_out", "uart2", NULL, NULL, NULL, "pg_eint8", NULL}, 6, 8, 6},
+ {"PG9", 6, 9, {"gpio_in", "gpio_out", "uart2", NULL, NULL, NULL, "pg_eint9", NULL}, 6, 9, 6},
+ {"PG10", 6, 10, {"gpio_in", "gpio_out", "i2c3", "usb", NULL, NULL, "pg_eint10", NULL}, 6, 10, 6},
+ {"PG11", 6, 11, {"gpio_in", "gpio_out", "i2c3", "usb", NULL, NULL, "pg_eint11", NULL}, 6, 11, 6},
+ {"PG12", 6, 12, {"gpio_in", "gpio_out", "spi1", "i2s1", NULL, NULL, "pg_eint12", NULL}, 6, 12, 6},
+ {"PG13", 6, 13, {"gpio_in", "gpio_out", "spi1", "i2s1", NULL, NULL, "pg_eint13", NULL}, 6, 13, 6},
+ {"PG14", 6, 14, {"gpio_in", "gpio_out", "spi1", "i2s1", NULL, NULL, "pg_eint14", NULL}, 6, 14, 6},
+ {"PG15", 6, 15, {"gpio_in", "gpio_out", "spi1", "i2s1", NULL, NULL, "pg_eint15", NULL}, 6, 15, 6},
+ {"PG16", 6, 16, {"gpio_in", "gpio_out", "spi1", "i2s1", NULL, NULL, "pg_eint16", NULL}, 6, 16, 6},
+ {"PG17", 6, 17, {"gpio_in", "gpio_out", "uart4", NULL, NULL, NULL, "pg_eint17", NULL}, 6, 17, 6},
+ {"PG18", 6, 18, {"gpio_in", "gpio_out", "uart4", NULL, NULL, NULL, "pg_eint18", NULL}, 6, 18, 6},
+
+ {"PH0", 7, 0, {"gpio_in", "gpio_out", "nand1", NULL, NULL, NULL, NULL, NULL}},
+ {"PH1", 7, 1, {"gpio_in", "gpio_out", "nand1", NULL, NULL, NULL, NULL, NULL}},
+ {"PH2", 7, 2, {"gpio_in", "gpio_out", "nand1", NULL, NULL, NULL, NULL, NULL}},
+ {"PH3", 7, 3, {"gpio_in", "gpio_out", "nand1", NULL, NULL, NULL, NULL, NULL}},
+ {"PH4", 7, 4, {"gpio_in", "gpio_out", "nand1", NULL, NULL, NULL, NULL, NULL}},
+ {"PH5", 7, 5, {"gpio_in", "gpio_out", "nand1", NULL, NULL, NULL, NULL, NULL}},
+ {"PH6", 7, 6, {"gpio_in", "gpio_out", "nand1", NULL, NULL, NULL, NULL, NULL}},
+ {"PH7", 7, 7, {"gpio_in", "gpio_out", "nand1", NULL, NULL, NULL, NULL, NULL}},
+ {"PH8", 7, 8, {"gpio_in", "gpio_out", "nand1", NULL, NULL, NULL, NULL, NULL}},
+ {"PH9", 7, 9, {"gpio_in", "gpio_out", "spi2", "jtag", "pwm1", NULL, NULL, NULL}},
+ {"PH10", 7, 10, {"gpio_in", "gpio_out", "spi2", "jtag", "pwm1", NULL, NULL, NULL}},
+ {"PH11", 7, 11, {"gpio_in", "gpio_out", "spi2", "jtag", "pwm2", NULL, NULL, NULL}},
+ {"PH12", 7, 12, {"gpio_in", "gpio_out", "spi2", "jtag", "pwm2", NULL, NULL, NULL}},
+ {"PH13", 7, 13, {"gpio_in", "gpio_out", "pwm0", NULL, NULL, NULL, NULL, NULL}},
+ {"PH14", 7, 14, {"gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, NULL, NULL}},
+ {"PH15", 7, 15, {"gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, NULL, NULL}},
+ {"PH16", 7, 16, {"gpio_in", "gpio_out", "i2c1", NULL, NULL, NULL, NULL, NULL}},
+ {"PH17", 7, 17, {"gpio_in", "gpio_out", "i2c1", NULL, NULL, NULL, NULL, NULL}},
+ {"PH18", 7, 18, {"gpio_in", "gpio_out", "i2c2", NULL, NULL, NULL, NULL, NULL}},
+ {"PH19", 7, 19, {"gpio_in", "gpio_out", "i2c2", NULL, NULL, NULL, NULL, NULL}},
+ {"PH20", 7, 20, {"gpio_in", "gpio_out", "uart0", NULL, NULL, NULL, NULL, NULL}},
+ {"PH21", 7, 21, {"gpio_in", "gpio_out", "uart0", NULL, NULL, NULL, NULL, NULL}},
+ {"PH22", 7, 22, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}},
+ {"PH23", 7, 23, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}},
+ {"PH24", 7, 24, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}},
+ {"PH25", 7, 25, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}},
+ {"PH26", 7, 26, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}},
+ {"PH27", 7, 27, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}},
+ {"PH28", 7, 28, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}},
+ {"PH29", 7, 29, {"gpio_in", "gpio_out", "nand1", NULL, NULL, NULL, NULL, NULL}},
+ {"PH30", 7, 30, {"gpio_in", "gpio_out", "nand1", NULL, NULL, NULL, NULL, NULL}},
+};
+
+const struct allwinner_padconf a31_padconf = {
+ .npins = nitems(a31_pins),
+ .pins = a31_pins,
+};
+
+#endif /* SOC_ALLWINNER_A31 */
diff --git a/sys/arm/allwinner/a31/a31_r_padconf.c b/sys/arm/allwinner/a31/a31_r_padconf.c
new file mode 100644
index 000000000000..0bc3d60b8a15
--- /dev/null
+++ b/sys/arm/allwinner/a31/a31_r_padconf.c
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 2016 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/types.h>
+
+#include <arm/allwinner/allwinner_pinctrl.h>
+
+#if defined(SOC_ALLWINNER_A31) || defined(SOC_ALLWINNER_A31S)
+
+const static struct allwinner_pins a31_r_pins[] = {
+ {"PL0", 0, 0, {"gpio_in", "gpio_out", "s_twi", "s_p2wi", NULL, NULL, NULL, NULL}},
+ {"PL1", 0, 1, {"gpio_in", "gpio_out", "s_twi", "s_p2wi", NULL, NULL, NULL, NULL}},
+ {"PL2", 0, 2, {"gpio_in", "gpio_out", "s_uart", NULL, NULL, NULL, NULL, NULL}},
+ {"PL3", 0, 3, {"gpio_in", "gpio_out", "s_uart", NULL, NULL, NULL, NULL, NULL}},
+ {"PL4", 0, 4, {"gpio_in", "gpio_out", "s_ir", NULL, NULL, NULL, NULL, NULL}},
+ {"PL5", 0, 5, {"gpio_in", "gpio_out", "pl_eint0", "s_jtag", NULL, NULL, NULL, NULL}, 2, 0},
+ {"PL6", 0, 6, {"gpio_in", "gpio_out", "pl_eint1", "s_jtag", NULL, NULL, NULL, NULL}, 2, 1},
+ {"PL7", 0, 7, {"gpio_in", "gpio_out", "pl_eint2", "s_jtag", NULL, NULL, NULL, NULL}, 2, 2},
+ {"PL8", 0, 8, {"gpio_in", "gpio_out", "pl_eint3", "s_jtag", NULL, NULL, NULL, NULL}, 2, 3},
+
+ {"PM0", 1, 0, {"gpio_in", "gpio_out", "pm_eint0", NULL, NULL, NULL, NULL, NULL}, 2, 0},
+ {"PM1", 1, 1, {"gpio_in", "gpio_out", "pm_eint1", NULL, NULL, NULL, NULL, NULL}, 2, 1},
+ {"PM2", 1, 2, {"gpio_in", "gpio_out", "pm_eint2", "1wire", NULL, NULL, NULL, NULL}, 2, 2},
+ {"PM3", 1, 3, {"gpio_in", "gpio_out", "pm_eint3", NULL, NULL, NULL, NULL, NULL}, 2, 3},
+ {"PM4", 1, 4, {"gpio_in", "gpio_out", "pm_eint4", NULL, NULL, NULL, NULL, NULL}, 2, 4},
+ {"PM5", 1, 5, {"gpio_in", "gpio_out", "pm_eint5", NULL, NULL, NULL, NULL, NULL}, 2, 5},
+ {"PM6", 1, 6, {"gpio_in", "gpio_out", "pm_eint6", NULL, NULL, NULL, NULL, NULL}, 2, 6},
+ {"PM7", 1, 7, {"gpio_in", "gpio_out", "pm_eint7", "rtc", NULL, NULL, NULL, NULL}, 2, 7},
+};
+
+const struct allwinner_padconf a31_r_padconf = {
+ .npins = nitems(a31_r_pins),
+ .pins = a31_r_pins,
+};
+
+#endif
diff --git a/sys/arm/allwinner/a31/a31s_padconf.c b/sys/arm/allwinner/a31/a31s_padconf.c
new file mode 100644
index 000000000000..cdceef03fb3b
--- /dev/null
+++ b/sys/arm/allwinner/a31/a31s_padconf.c
@@ -0,0 +1,199 @@
+/*-
+ * Copyright (c) 2016 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/types.h>
+
+#include <arm/allwinner/allwinner_pinctrl.h>
+
+#ifdef SOC_ALLWINNER_A31S
+
+const static struct allwinner_pins a31s_pins[] = {
+ {"PA0", 0, 0, {"gpio_in", "gpio_out", "gmac", NULL, "uart1", NULL, "pa_eint0", NULL}, 6, 0, 0},
+ {"PA1", 0, 1, {"gpio_in", "gpio_out", "gmac", NULL, "uart1", NULL, "pa_eint1", NULL}, 6, 1, 0},
+ {"PA2", 0, 2, {"gpio_in", "gpio_out", "gmac", NULL, "uart1", NULL, "pa_eint2", NULL}, 6, 2, 0},
+ {"PA3", 0, 3, {"gpio_in", "gpio_out", "gmac", NULL, "uart1", NULL, "pa_eint3", NULL}, 6, 3, 0},
+ {"PA4", 0, 4, {"gpio_in", "gpio_out", "gmac", NULL, "uart1", NULL, "pa_eint4", NULL}, 6, 4, 0},
+ {"PA5", 0, 5, {"gpio_in", "gpio_out", "gmac", NULL, "uart1", NULL, "pa_eint5", NULL}, 6, 5, 0},
+ {"PA6", 0, 6, {"gpio_in", "gpio_out", "gmac", NULL, "uart1", NULL, "pa_eint6", NULL}, 6, 6, 0},
+ {"PA7", 0, 7, {"gpio_in", "gpio_out", "gmac", NULL, "uart1", NULL, "pa_eint7", NULL}, 6, 7, 0},
+ {"PA8", 0, 8, {"gpio_in", "gpio_out", "gmac", NULL, NULL, NULL, "pa_eint8", NULL}, 6, 8, 0},
+ {"PA9", 0, 9, {"gpio_in", "gpio_out", "gmac", NULL, NULL, NULL, "pa_eint9", NULL}, 6, 9, 0},
+ {"PA10", 0, 10, {"gpio_in", "gpio_out", "gmac", NULL, "mmc3", "mmc2", "pa_eint10", NULL}, 6, 10, 0},
+ {"PA11", 0, 11, {"gpio_in", "gpio_out", "gmac", NULL, "mmc3", "mmc2", "pa_eint11", NULL}, 6, 11, 0},
+ {"PA12", 0, 12, {"gpio_in", "gpio_out", "gmac", NULL, "mmc3", "mmc2", "pa_eint12", NULL}, 6, 12, 0},
+ {"PA13", 0, 13, {"gpio_in", "gpio_out", "gmac", NULL, "mmc3", "mmc2", "pa_eint13", NULL}, 6, 13, 0},
+ {"PA14", 0, 14, {"gpio_in", "gpio_out", "gmac", NULL, "mmc3", "mmc2", "pa_eint14", NULL}, 6, 14, 0},
+ {"PA15", 0, 15, {"gpio_in", "gpio_out", "gmac", NULL, "dmic", NULL, "pa_eint15", NULL}, 6, 15, 0},
+ {"PA16", 0, 16, {"gpio_in", "gpio_out", "gmac", NULL, "dmic", NULL, "pa_eint16", NULL}, 6, 16, 0},
+ {"PA17", 0, 17, {"gpio_in", "gpio_out", "gmac", NULL, "clk_out_b", NULL, "pa_eint17", NULL}, 6, 17, 0},
+ {"PA18", 0, 18, {"gpio_in", "gpio_out", "gmac", NULL, "pwm3", NULL, "pa_eint18", NULL}, 6, 18, 0},
+ {"PA19", 0, 19, {"gpio_in", "gpio_out", "gmac", NULL, "pwm3", NULL, "pa_eint19", NULL}, 6, 19, 0},
+ {"PA20", 0, 20, {"gpio_in", "gpio_out", "gmac", NULL, "spi3", NULL, "pa_eint20", NULL}, 6, 20, 0},
+ {"PA21", 0, 21, {"gpio_in", "gpio_out", "gmac", NULL, "spi3", NULL, "pa_eint21", NULL}, 6, 21, 0},
+ {"PA22", 0, 22, {"gpio_in", "gpio_out", "gmac", NULL, "spi3", NULL, "pa_eint22", NULL}, 6, 22, 0},
+ {"PA23", 0, 23, {"gpio_in", "gpio_out", "gmac", NULL, "spi3", NULL, "pa_eint23", NULL}, 6, 23, 0},
+ {"PA24", 0, 24, {"gpio_in", "gpio_out", "gmac", NULL, "spi3", NULL, "pa_eint24", NULL}, 6, 24, 0},
+ {"PA25", 0, 25, {"gpio_in", "gpio_out", "gmac", NULL, "spi3", NULL, "pa_eint25", NULL}, 6, 25, 0},
+ {"PA26", 0, 26, {"gpio_in", "gpio_out", "gmac", NULL, "clk_out_c", NULL, "pa_eint26", NULL}, 6, 26, 0},
+ {"PA27", 0, 27, {"gpio_in", "gpio_out", "gmac", NULL, NULL, NULL, "pa_eint27", NULL}, 6, 27, 0},
+
+ {"PB0", 1, 0, {"gpio_in", "gpio_out", "i2s0", "uart3", NULL , NULL, "pb_eint0", NULL}, 6, 0, 1},
+ {"PB1", 1, 1, {"gpio_in", "gpio_out", "i2s0", NULL, NULL, NULL, "pb_eint1", NULL}, 6, 1, 1},
+ {"PB2", 1, 2, {"gpio_in", "gpio_out", "i2s0", NULL, NULL, NULL, "pb_eint2", NULL}, 6, 2, 1},
+ {"PB3", 1, 3, {"gpio_in", "gpio_out", "i2s0", NULL, NULL, NULL, "pb_eint3", NULL}, 6, 3, 1},
+ {"PB4", 1, 4, {"gpio_in", "gpio_out", "i2s0", "uart3", NULL, NULL, "pb_eint4", NULL}, 6, 4, 1},
+ {"PB5", 1, 5, {"gpio_in", "gpio_out", "i2s0", "uart3", "i2c3", NULL, "pb_eint5", NULL}, 6, 5, 1},
+ {"PB6", 1, 6, {"gpio_in", "gpio_out", "i2s0", "uart3", "i2c3", NULL, "pb_eint6", NULL}, 6, 6, 1},
+ {"PB7", 1, 7, {"gpio_in", "gpio_out", "i2s0", NULL, NULL, NULL, "pb_eint7", NULL}, 6, 7, 1},
+
+ {"PC0", 2, 0, {"gpio_in", "gpio_out", "nand0", "spi0", NULL, NULL, NULL, NULL}},
+ {"PC1", 2, 1, {"gpio_in", "gpio_out", "nand0", "spi0", NULL, NULL, NULL, NULL}},
+ {"PC2", 2, 2, {"gpio_in", "gpio_out", "nand0", "spi0", NULL, NULL, NULL, NULL}},
+ {"PC3", 2, 3, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}},
+ {"PC4", 2, 4, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}},
+ {"PC5", 2, 5, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}},
+ {"PC6", 2, 6, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}},
+ {"PC7", 2, 7, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}},
+ {"PC8", 2, 8, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}},
+ {"PC9", 2, 9, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}},
+ {"PC10", 2, 10, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}},
+ {"PC11", 2, 11, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}},
+ {"PC12", 2, 12, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}},
+ {"PC13", 2, 13, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}},
+ {"PC14", 2, 14, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}},
+ {"PC15", 2, 15, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}},
+ {"PC24", 2, 24, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}},
+ {"PC25", 2, 25, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}},
+ {"PC26", 2, 26, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}},
+ {"PC27", 2, 27, {"gpio_in", "gpio_out", NULL, "spi0",NULL, NULL, NULL, NULL}},
+
+ {"PD0", 3, 0, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD1", 3, 1, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD2", 3, 2, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD3", 3, 3, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD4", 3, 4, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD5", 3, 5, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD6", 3, 6, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD7", 3, 7, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD8", 3, 8, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD9", 3, 9, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD10", 3, 10, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD11", 3, 11, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD12", 3, 12, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD13", 3, 13, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD14", 3, 14, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD15", 3, 15, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD16", 3, 16, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD17", 3, 17, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD18", 3, 18, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD19", 3, 19, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD20", 3, 20, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD21", 3, 21, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD22", 3, 22, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD23", 3, 23, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD24", 3, 24, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD25", 3, 25, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD26", 3, 26, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD27", 3, 27, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+
+ {"PE0", 4, 0, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, "pe_eint0", NULL}, 6, 0, 4},
+ {"PE1", 4, 1, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, "pe_eint1", NULL}, 6, 1, 4},
+ {"PE2", 4, 2, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, "pe_eint2", NULL}, 6, 2, 4},
+ {"PE3", 4, 3, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, "pe_eint3", NULL}, 6, 3, 4},
+ {"PE4", 4, 4, {"gpio_in", "gpio_out", "csi", "uart5", NULL, NULL, "pe_eint4", NULL}, 6, 4, 4},
+ {"PE5", 4, 5, {"gpio_in", "gpio_out", "csi", "uart5", NULL, NULL, "pe_eint5", NULL}, 6, 5, 4},
+ {"PE6", 4, 6, {"gpio_in", "gpio_out", "csi", "uart5", NULL, NULL, "pe_eint6", NULL}, 6, 6, 4},
+ {"PE7", 4, 7, {"gpio_in", "gpio_out", "csi", "uart5", NULL, NULL, "pe_eint7", NULL}, 6, 7, 4},
+ {"PE8", 4, 8, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, "pe_eint8", NULL}, 6, 8, 4},
+ {"PE9", 4, 9, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, "pe_eint9", NULL}, 6, 9, 4},
+ {"PE10", 4, 10, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, "pe_eint10", NULL}, 6, 10, 4},
+ {"PE11", 4, 11, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, "pe_eint11", NULL}, 6, 11, 4},
+ {"PE12", 4, 12, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, "pe_eint12", NULL}, 6, 12, 4},
+ {"PE13", 4, 13, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, "pe_eint13", NULL}, 6, 13, 4},
+ {"PE14", 4, 14, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, "pe_eint14", NULL}, 6, 14, 4},
+ {"PE15", 4, 15, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, "pe_eint15", NULL}, 6, 15, 4},
+
+ {"PF0", 5, 0, {"gpio_in", "gpio_out", "mmc0", NULL, "jtag", NULL, NULL, NULL}},
+ {"PF1", 5, 1, {"gpio_in", "gpio_out", "mmc0", NULL, "jtag", NULL, NULL, NULL}},
+ {"PF2", 5, 2, {"gpio_in", "gpio_out", "mmc0", NULL, "uart0", NULL, NULL, NULL}},
+ {"PF3", 5, 3, {"gpio_in", "gpio_out", "mmc0", NULL, "jtag", NULL, NULL, NULL}},
+ {"PF4", 5, 4, {"gpio_in", "gpio_out", "mmc0", NULL, "uart0", NULL, NULL, NULL}},
+ {"PF5", 5, 5, {"gpio_in", "gpio_out", "mmc0", NULL, "jtag", NULL, NULL, NULL}},
+
+ {"PG0", 6, 0, {"gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "pg_eint0", NULL}, 6, 0, 6},
+ {"PG1", 6, 1, {"gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "pg_eint1", NULL}, 6, 1, 6},
+ {"PG2", 6, 2, {"gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "pg_eint2", NULL}, 6, 2, 6},
+ {"PG3", 6, 3, {"gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "pg_eint3", NULL}, 6, 3, 6},
+ {"PG4", 6, 4, {"gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "pg_eint4", NULL}, 6, 4, 6},
+ {"PG5", 6, 5, {"gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "pg_eint5", NULL}, 6, 5, 6},
+ {"PG6", 6, 6, {"gpio_in", "gpio_out", "uart2", NULL, NULL, NULL, "pg_eint6", NULL}, 6, 6, 6},
+ {"PG7", 6, 7, {"gpio_in", "gpio_out", "uart2", NULL, NULL, NULL, "pg_eint7", NULL}, 6, 7, 6},
+ {"PG8", 6, 8, {"gpio_in", "gpio_out", "uart2", NULL, NULL, NULL, "pg_eint8", NULL}, 6, 8, 6},
+ {"PG9", 6, 9, {"gpio_in", "gpio_out", "uart2", NULL, NULL, NULL, "pg_eint9", NULL}, 6, 9, 6},
+ {"PG10", 6, 10, {"gpio_in", "gpio_out", "i2c3", NULL, NULL, NULL, "pg_eint10", NULL}, 6, 10, 6},
+ {"PG11", 6, 11, {"gpio_in", "gpio_out", "i2c3", NULL, NULL, NULL, "pg_eint11", NULL}, 6, 11, 6},
+ {"PG12", 6, 12, {"gpio_in", "gpio_out", "spi1", "i2s1", NULL, NULL, "pg_eint12", NULL}, 6, 12, 6},
+ {"PG13", 6, 13, {"gpio_in", "gpio_out", "spi1", "i2s1", NULL, NULL, "pg_eint13", NULL}, 6, 13, 6},
+ {"PG14", 6, 14, {"gpio_in", "gpio_out", "spi1", "i2s1", NULL, NULL, "pg_eint14", NULL}, 6, 14, 6},
+ {"PG15", 6, 15, {"gpio_in", "gpio_out", "spi1", "i2s1", NULL, NULL, "pg_eint15", NULL}, 6, 15, 6},
+ {"PG16", 6, 16, {"gpio_in", "gpio_out", "spi1", "i2s1", NULL, NULL, "pg_eint16", NULL}, 6, 16, 6},
+ {"PG17", 6, 17, {"gpio_in", "gpio_out", "uart4", NULL, NULL, NULL, "pg_eint17", NULL}, 6, 17, 6},
+ {"PG18", 6, 18, {"gpio_in", "gpio_out", "uart4", NULL, NULL, NULL, "pg_eint18", NULL}, 6, 18, 6},
+
+ {"PH9", 7, 9, {"gpio_in", "gpio_out", "spi2", "jtag", "pwm1", NULL, NULL, NULL}},
+ {"PH10", 7, 10, {"gpio_in", "gpio_out", "spi2", "jtag", "pwm1", NULL, NULL, NULL}},
+ {"PH11", 7, 11, {"gpio_in", "gpio_out", "spi2", "jtag", "pwm2", NULL, NULL, NULL}},
+ {"PH12", 7, 12, {"gpio_in", "gpio_out", "spi2", "jtag", "pwm2", NULL, NULL, NULL}},
+ {"PH13", 7, 13, {"gpio_in", "gpio_out", "pwm0", NULL, NULL, NULL, NULL, NULL}},
+ {"PH14", 7, 14, {"gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, NULL, NULL}},
+ {"PH15", 7, 15, {"gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, NULL, NULL}},
+ {"PH16", 7, 16, {"gpio_in", "gpio_out", "i2c1", NULL, NULL, NULL, NULL, NULL}},
+ {"PH17", 7, 17, {"gpio_in", "gpio_out", "i2c1", NULL, NULL, NULL, NULL, NULL}},
+ {"PH18", 7, 18, {"gpio_in", "gpio_out", "i2c2", NULL, NULL, NULL, NULL, NULL}},
+ {"PH19", 7, 19, {"gpio_in", "gpio_out", "i2c2", NULL, NULL, NULL, NULL, NULL}},
+ {"PH20", 7, 20, {"gpio_in", "gpio_out", "uart0", NULL, NULL, NULL, NULL, NULL}},
+ {"PH21", 7, 21, {"gpio_in", "gpio_out", "uart0", NULL, NULL, NULL, NULL, NULL}},
+ {"PH22", 7, 22, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}},
+ {"PH23", 7, 23, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}},
+ {"PH24", 7, 24, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}},
+ {"PH25", 7, 25, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}},
+ {"PH26", 7, 26, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}},
+ {"PH27", 7, 27, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}},
+ {"PH28", 7, 27, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}},
+};
+
+const struct allwinner_padconf a31s_padconf = {
+ .npins = nitems(a31s_pins),
+ .pins = a31s_pins,
+};
+
+#endif /* SOC_ALLWINNER_A31S */
diff --git a/sys/arm/allwinner/a31/files.a31 b/sys/arm/allwinner/a31/files.a31
new file mode 100644
index 000000000000..df3ad57da03e
--- /dev/null
+++ b/sys/arm/allwinner/a31/files.a31
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+arm/allwinner/clkng/ccu_a31.c standard
+arm/allwinner/a31/a31_padconf.c standard
+arm/allwinner/a31/a31_r_padconf.c standard
+arm/allwinner/a31/a31s_padconf.c standard
diff --git a/sys/arm/allwinner/a31_dmac.c b/sys/arm/allwinner/a31_dmac.c
new file mode 100644
index 000000000000..8af9bca73f98
--- /dev/null
+++ b/sys/arm/allwinner/a31_dmac.c
@@ -0,0 +1,559 @@
+/*-
+ * Copyright (c) 2016 Jared D. McNeill <jmcneill@invisible.ca>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/*
+ * Allwinner DMA controller
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/condvar.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/endian.h>
+
+#include <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/allwinner/a10_dmac.h>
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+
+#include "sunxi_dma_if.h"
+
+#define DMA_IRQ_EN_REG0 0x00
+#define DMA_IRQ_EN_REG1 0x04
+#define DMA_IRQ_EN_REG(ch) (DMA_IRQ_EN_REG0 + ((ch) / 8) * 4)
+#define DMA_PKG_IRQ_EN(ch) (1 << (((ch) % 8) * 4 + 1))
+#define DMA_PKG_IRQ_MASK 0x2222222222222222ULL
+#define DMA_IRQ_PEND_REG0 0x10
+#define DMA_IRQ_PEND_REG1 0x14
+#define DMA_IRQ_PEND_REG(ch) (DMA_IRQ_PEND_REG0 + ((ch) / 8) * 4)
+#define DMA_STA_REG 0x30
+#define DMA_EN_REG(n) (0x100 + (n) * 0x40 + 0x00)
+#define DMA_EN (1 << 0)
+#define DMA_PAU_REG(n) (0x100 + (n) * 0x40 + 0x04)
+#define DMA_STAR_ADDR_REG(n) (0x100 + (n) * 0x40 + 0x08)
+#define DMA_CFG_REG(n) (0x100 + (n) * 0x40 + 0x0c)
+#define DMA_DEST_DATA_WIDTH (0x3 << 25)
+#define DMA_DEST_DATA_WIDTH_SHIFT 25
+#define DMA_DEST_BST_LEN (0x3 << 22)
+#define DMA_DEST_BST_LEN_SHIFT 22
+#define DMA_DEST_ADDR_MODE (0x1 << 21)
+#define DMA_DEST_ADDR_MODE_SHIFT 21
+#define DMA_DEST_DRQ_TYPE (0x1f << 16)
+#define DMA_DEST_DRQ_TYPE_SHIFT 16
+#define DMA_SRC_DATA_WIDTH (0x3 << 9)
+#define DMA_SRC_DATA_WIDTH_SHIFT 9
+#define DMA_SRC_BST_LEN (0x3 << 6)
+#define DMA_SRC_BST_LEN_SHIFT 6
+#define DMA_SRC_ADDR_MODE (0x1 << 5)
+#define DMA_SRC_ADDR_MODE_SHIFT 5
+#define DMA_SRC_DRQ_TYPE (0x1f << 0)
+#define DMA_SRC_DRQ_TYPE_SHIFT 0
+#define DMA_DATA_WIDTH_8BIT 0
+#define DMA_DATA_WIDTH_16BIT 1
+#define DMA_DATA_WIDTH_32BIT 2
+#define DMA_DATA_WIDTH_64BIT 3
+#define DMA_ADDR_MODE_LINEAR 0
+#define DMA_ADDR_MODE_IO 1
+#define DMA_BST_LEN_1 0
+#define DMA_BST_LEN_4 1
+#define DMA_BST_LEN_8 2
+#define DMA_BST_LEN_16 3
+#define DMA_CUR_SRC_REG(n) (0x100 + (n) * 0x40 + 0x10)
+#define DMA_CUR_DEST_REG(n) (0x100 + (n) * 0x40 + 0x14)
+#define DMA_BCNT_LEFT_REG(n) (0x100 + (n) * 0x40 + 0x18)
+#define DMA_PARA_REG(n) (0x100 + (n) * 0x40 + 0x1c)
+#define WAIT_CYC (0xff << 0)
+#define WAIT_CYC_SHIFT 0
+
+struct a31dmac_desc {
+ uint32_t config;
+ uint32_t srcaddr;
+ uint32_t dstaddr;
+ uint32_t bcnt;
+ uint32_t para;
+ uint32_t next;
+#define DMA_NULL 0xfffff800
+};
+#define DESC_ALIGN 4
+#define DESC_SIZE sizeof(struct a31dmac_desc)
+
+struct a31dmac_config {
+ u_int nchans;
+};
+
+static const struct a31dmac_config a31_config = { .nchans = 16 };
+static const struct a31dmac_config h3_config = { .nchans = 12 };
+static const struct a31dmac_config a83t_config = { .nchans = 8 };
+static const struct a31dmac_config a64_config = { .nchans = 8 };
+
+static struct ofw_compat_data compat_data[] = {
+ { "allwinner,sun6i-a31-dma", (uintptr_t)&a31_config },
+ { "allwinner,sun8i-a83t-dma", (uintptr_t)&a83t_config },
+ { "allwinner,sun8i-h3-dma", (uintptr_t)&h3_config },
+ { "allwinner,sun50i-a64-dma", (uintptr_t)&a64_config },
+ { NULL, (uintptr_t)NULL }
+};
+
+struct a31dmac_softc;
+
+struct a31dmac_channel {
+ struct a31dmac_softc * sc;
+ uint8_t index;
+ void (*callback)(void *);
+ void * callbackarg;
+
+ bus_dmamap_t dmamap;
+ struct a31dmac_desc *desc;
+ bus_addr_t physaddr;
+};
+
+struct a31dmac_softc {
+ struct resource * res[2];
+ struct mtx mtx;
+ void * ih;
+
+ bus_dma_tag_t dmat;
+
+ u_int nchans;
+ struct a31dmac_channel * chans;
+};
+
+static struct resource_spec a31dmac_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+#define DMA_READ(sc, reg) bus_read_4((sc)->res[0], (reg))
+#define DMA_WRITE(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val))
+
+static void a31dmac_intr(void *);
+static void a31dmac_dmamap_cb(void *, bus_dma_segment_t *, int, int);
+
+static int
+a31dmac_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner DMA controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+a31dmac_attach(device_t dev)
+{
+ struct a31dmac_softc *sc;
+ struct a31dmac_config *conf;
+ u_int index;
+ hwreset_t rst;
+ clk_t clk;
+ int error;
+
+ sc = device_get_softc(dev);
+ conf = (void *)ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+ clk = NULL;
+ rst = NULL;
+
+ if (bus_alloc_resources(dev, a31dmac_spec, sc->res)) {
+ device_printf(dev, "cannot allocate resources for device\n");
+ return (ENXIO);
+ }
+
+ mtx_init(&sc->mtx, "a31 dmac", NULL, MTX_SPIN);
+
+ /* Clock and reset setup */
+ if (clk_get_by_ofw_index(dev, 0, 0, &clk) != 0) {
+ device_printf(dev, "cannot get clock\n");
+ goto fail;
+ }
+ if (clk_enable(clk) != 0) {
+ device_printf(dev, "cannot enable clock\n");
+ goto fail;
+ }
+ if (hwreset_get_by_ofw_idx(dev, 0, 0, &rst) != 0) {
+ device_printf(dev, "cannot get hwreset\n");
+ goto fail;
+ }
+ if (hwreset_deassert(rst) != 0) {
+ device_printf(dev, "cannot de-assert reset\n");
+ goto fail;
+ }
+
+ /* Descriptor DMA */
+ error = bus_dma_tag_create(
+ bus_get_dma_tag(dev), /* Parent tag */
+ DESC_ALIGN, 0, /* alignment, boundary */
+ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ DESC_SIZE, 1, /* maxsize, nsegs */
+ DESC_SIZE, /* maxsegsize */
+ 0, /* flags */
+ NULL, NULL, /* lockfunc, lockarg */
+ &sc->dmat);
+ if (error != 0) {
+ device_printf(dev, "cannot create dma tag\n");
+ goto fail;
+ }
+
+ /* Disable all interrupts and clear pending status */
+ DMA_WRITE(sc, DMA_IRQ_EN_REG0, 0);
+ DMA_WRITE(sc, DMA_IRQ_EN_REG1, 0);
+ DMA_WRITE(sc, DMA_IRQ_PEND_REG0, ~0);
+ DMA_WRITE(sc, DMA_IRQ_PEND_REG1, ~0);
+
+ /* Initialize channels */
+ sc->nchans = conf->nchans;
+ sc->chans = malloc(sizeof(*sc->chans) * sc->nchans, M_DEVBUF,
+ M_WAITOK | M_ZERO);
+
+ for (index = 0; index < sc->nchans; index++) {
+ sc->chans[index].sc = sc;
+ sc->chans[index].index = index;
+ sc->chans[index].callback = NULL;
+ sc->chans[index].callbackarg = NULL;
+
+ error = bus_dmamem_alloc(sc->dmat,
+ (void **)&sc->chans[index].desc,
+ BUS_DMA_WAITOK | BUS_DMA_COHERENT,
+ &sc->chans[index].dmamap);
+ if (error != 0) {
+ device_printf(dev, "cannot allocate dma mem\n");
+ goto fail;
+ }
+ error = bus_dmamap_load(sc->dmat, sc->chans[index].dmamap,
+ sc->chans[index].desc, sizeof(*sc->chans[index].desc),
+ a31dmac_dmamap_cb, &sc->chans[index], BUS_DMA_WAITOK);
+ if (error != 0) {
+ device_printf(dev, "cannot load dma map\n");
+ goto fail;
+ }
+
+ DMA_WRITE(sc, DMA_EN_REG(index), 0);
+ }
+
+ error = bus_setup_intr(dev, sc->res[1], INTR_MPSAFE | INTR_TYPE_MISC,
+ NULL, a31dmac_intr, sc, &sc->ih);
+ if (error != 0) {
+ device_printf(dev, "could not setup interrupt handler\n");
+ bus_release_resources(dev, a31dmac_spec, sc->res);
+ mtx_destroy(&sc->mtx);
+ return (ENXIO);
+ }
+
+ OF_device_register_xref(OF_xref_from_node(ofw_bus_get_node(dev)), dev);
+ return (0);
+
+fail:
+ for (index = 0; index < sc->nchans; index++)
+ if (sc->chans[index].desc != NULL) {
+ bus_dmamap_unload(sc->dmat, sc->chans[index].dmamap);
+ bus_dmamem_free(sc->dmat, sc->chans[index].desc,
+ sc->chans[index].dmamap);
+ }
+ if (sc->chans != NULL)
+ free(sc->chans, M_DEVBUF);
+ if (sc->ih != NULL)
+ bus_teardown_intr(dev, sc->res[1], sc->ih);
+ if (rst != NULL)
+ hwreset_release(rst);
+ if (clk != NULL)
+ clk_release(clk);
+ bus_release_resources(dev, a31dmac_spec, sc->res);
+
+ return (ENXIO);
+}
+
+static void
+a31dmac_dmamap_cb(void *priv, bus_dma_segment_t *segs, int nsegs, int error)
+{
+ struct a31dmac_channel *ch;
+
+ if (error != 0)
+ return;
+
+ ch = priv;
+ ch->physaddr = segs[0].ds_addr;
+}
+
+static void
+a31dmac_intr(void *priv)
+{
+ struct a31dmac_softc *sc;
+ uint32_t pend0, pend1, bit;
+ uint64_t pend, mask;
+ u_int index;
+
+ sc = priv;
+ pend0 = DMA_READ(sc, DMA_IRQ_PEND_REG0);
+ pend1 = sc->nchans > 8 ? DMA_READ(sc, DMA_IRQ_PEND_REG1) : 0;
+ if (pend0 == 0 && pend1 == 0)
+ return;
+
+ if (pend0 != 0)
+ DMA_WRITE(sc, DMA_IRQ_PEND_REG0, pend0);
+ if (pend1 != 0)
+ DMA_WRITE(sc, DMA_IRQ_PEND_REG1, pend1);
+
+ pend = pend0 | ((uint64_t)pend1 << 32);
+
+ while ((bit = ffsll(pend & DMA_PKG_IRQ_MASK)) != 0) {
+ mask = (1U << (bit - 1));
+ pend &= ~mask;
+ index = (bit - 1) / 4;
+
+ if (index >= sc->nchans)
+ continue;
+ if (sc->chans[index].callback == NULL)
+ continue;
+ sc->chans[index].callback(sc->chans[index].callbackarg);
+ }
+}
+
+static int
+a31dmac_set_config(device_t dev, void *priv, const struct sunxi_dma_config *cfg)
+{
+ struct a31dmac_channel *ch;
+ uint32_t config, para;
+ unsigned int dst_dw, dst_bl, dst_wc, dst_am;
+ unsigned int src_dw, src_bl, src_wc, src_am;
+
+ ch = priv;
+
+ switch (cfg->dst_width) {
+ case 8:
+ dst_dw = DMA_DATA_WIDTH_8BIT;
+ break;
+ case 16:
+ dst_dw = DMA_DATA_WIDTH_16BIT;
+ break;
+ case 32:
+ dst_dw = DMA_DATA_WIDTH_32BIT;
+ break;
+ case 64:
+ dst_dw = DMA_DATA_WIDTH_64BIT;
+ break;
+ default:
+ return (EINVAL);
+ }
+ switch (cfg->dst_burst_len) {
+ case 1:
+ dst_bl = DMA_BST_LEN_1;
+ break;
+ case 4:
+ dst_bl = DMA_BST_LEN_4;
+ break;
+ case 8:
+ dst_bl = DMA_BST_LEN_8;
+ break;
+ case 16:
+ dst_bl = DMA_BST_LEN_16;
+ break;
+ default:
+ return (EINVAL);
+ }
+ switch (cfg->src_width) {
+ case 8:
+ src_dw = DMA_DATA_WIDTH_8BIT;
+ break;
+ case 16:
+ src_dw = DMA_DATA_WIDTH_16BIT;
+ break;
+ case 32:
+ src_dw = DMA_DATA_WIDTH_32BIT;
+ break;
+ case 64:
+ src_dw = DMA_DATA_WIDTH_64BIT;
+ default:
+ return (EINVAL);
+ }
+ switch (cfg->src_burst_len) {
+ case 1:
+ src_bl = DMA_BST_LEN_1;
+ break;
+ case 4:
+ src_bl = DMA_BST_LEN_4;
+ break;
+ case 8:
+ src_bl = DMA_BST_LEN_8;
+ break;
+ case 16:
+ src_bl = DMA_BST_LEN_16;
+ break;
+ default:
+ return (EINVAL);
+ }
+ dst_am = cfg->dst_noincr ? DMA_ADDR_MODE_IO : DMA_ADDR_MODE_LINEAR;
+ src_am = cfg->src_noincr ? DMA_ADDR_MODE_IO : DMA_ADDR_MODE_LINEAR;
+ dst_wc = cfg->dst_wait_cyc;
+ src_wc = cfg->src_wait_cyc;
+ if (dst_wc != src_wc)
+ return (EINVAL);
+
+ config = (dst_dw << DMA_DEST_DATA_WIDTH_SHIFT) |
+ (dst_bl << DMA_DEST_BST_LEN_SHIFT) |
+ (dst_am << DMA_DEST_ADDR_MODE_SHIFT) |
+ (cfg->dst_drqtype << DMA_DEST_DRQ_TYPE_SHIFT) |
+ (src_dw << DMA_SRC_DATA_WIDTH_SHIFT) |
+ (src_bl << DMA_SRC_BST_LEN_SHIFT) |
+ (src_am << DMA_SRC_ADDR_MODE_SHIFT) |
+ (cfg->src_drqtype << DMA_SRC_DRQ_TYPE_SHIFT);
+ para = (dst_wc << WAIT_CYC_SHIFT);
+
+ ch->desc->config = htole32(config);
+ ch->desc->para = htole32(para);
+
+ return (0);
+}
+
+static void *
+a31dmac_alloc(device_t dev, bool dedicated, void (*cb)(void *), void *cbarg)
+{
+ struct a31dmac_softc *sc;
+ struct a31dmac_channel *ch;
+ uint32_t irqen;
+ u_int index;
+
+ sc = device_get_softc(dev);
+ ch = NULL;
+
+ mtx_lock_spin(&sc->mtx);
+ for (index = 0; index < sc->nchans; index++) {
+ if (sc->chans[index].callback == NULL) {
+ ch = &sc->chans[index];
+ ch->callback = cb;
+ ch->callbackarg = cbarg;
+
+ irqen = DMA_READ(sc, DMA_IRQ_EN_REG(index));
+ irqen |= DMA_PKG_IRQ_EN(index);
+ DMA_WRITE(sc, DMA_IRQ_EN_REG(index), irqen);
+ break;
+ }
+ }
+ mtx_unlock_spin(&sc->mtx);
+
+ return (ch);
+}
+
+static void
+a31dmac_free(device_t dev, void *priv)
+{
+ struct a31dmac_channel *ch;
+ struct a31dmac_softc *sc;
+ uint32_t irqen;
+ u_int index;
+
+ ch = priv;
+ sc = ch->sc;
+ index = ch->index;
+
+ mtx_lock_spin(&sc->mtx);
+
+ irqen = DMA_READ(sc, DMA_IRQ_EN_REG(index));
+ irqen &= ~DMA_PKG_IRQ_EN(index);
+ DMA_WRITE(sc, DMA_IRQ_EN_REG(index), irqen);
+ DMA_WRITE(sc, DMA_IRQ_PEND_REG(index), DMA_PKG_IRQ_EN(index));
+
+ ch->callback = NULL;
+ ch->callbackarg = NULL;
+
+ mtx_unlock_spin(&sc->mtx);
+}
+
+static int
+a31dmac_transfer(device_t dev, void *priv, bus_addr_t src, bus_addr_t dst,
+ size_t nbytes)
+{
+ struct a31dmac_channel *ch;
+ struct a31dmac_softc *sc;
+
+ ch = priv;
+ sc = ch->sc;
+
+ ch->desc->srcaddr = htole32((uint32_t)src);
+ ch->desc->dstaddr = htole32((uint32_t)dst);
+ ch->desc->bcnt = htole32(nbytes);
+ ch->desc->next = htole32(DMA_NULL);
+
+ DMA_WRITE(sc, DMA_STAR_ADDR_REG(ch->index), (uint32_t)ch->physaddr);
+ DMA_WRITE(sc, DMA_EN_REG(ch->index), DMA_EN);
+
+ return (0);
+}
+
+static void
+a31dmac_halt(device_t dev, void *priv)
+{
+ struct a31dmac_channel *ch;
+ struct a31dmac_softc *sc;
+
+ ch = priv;
+ sc = ch->sc;
+
+ DMA_WRITE(sc, DMA_EN_REG(ch->index), 0);
+}
+
+static device_method_t a31dmac_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, a31dmac_probe),
+ DEVMETHOD(device_attach, a31dmac_attach),
+
+ /* sunxi DMA interface */
+ DEVMETHOD(sunxi_dma_alloc, a31dmac_alloc),
+ DEVMETHOD(sunxi_dma_free, a31dmac_free),
+ DEVMETHOD(sunxi_dma_set_config, a31dmac_set_config),
+ DEVMETHOD(sunxi_dma_transfer, a31dmac_transfer),
+ DEVMETHOD(sunxi_dma_halt, a31dmac_halt),
+
+ DEVMETHOD_END
+};
+
+static driver_t a31dmac_driver = {
+ "a31dmac",
+ a31dmac_methods,
+ sizeof(struct a31dmac_softc)
+};
+
+static devclass_t a31dmac_devclass;
+
+DRIVER_MODULE(a31dmac, simplebus, a31dmac_driver, a31dmac_devclass, 0, 0);
diff --git a/sys/arm/allwinner/a33/a33_padconf.c b/sys/arm/allwinner/a33/a33_padconf.c
new file mode 100644
index 000000000000..9ad3dd4e6caf
--- /dev/null
+++ b/sys/arm/allwinner/a33/a33_padconf.c
@@ -0,0 +1,148 @@
+/*-
+ * Copyright (c) 2016 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/types.h>
+
+#include <arm/allwinner/allwinner_pinctrl.h>
+
+#ifdef SOC_ALLWINNER_A33
+
+const static struct allwinner_pins a33_pins[] = {
+ {"PB0", 1, 0, {"gpio_in", "gpio_out", "uart2", "uart0", "pb_eint0", NULL}, 4, 0, 1},
+ {"PB1", 1, 1, {"gpio_in", "gpio_out", "uart2", "uart0", "pb_eint1", NULL}, 4, 1, 1},
+ {"PB2", 1, 2, {"gpio_in", "gpio_out", "uart2", NULL, "pb_eint2", NULL}, 4, 2, 1},
+ {"PB3", 1, 3, {"gpio_in", "gpio_out", "uart2", NULL, "pb_eint3", NULL}, 4, 3, 1},
+ {"PB4", 1, 4, {"gpio_in", "gpio_out", "i2s0", "aif2", "pb_eint4", NULL}, 4, 4, 1},
+ {"PB5", 1, 5, {"gpio_in", "gpio_out", "i2s0", "aif2", "pb_eint5", NULL}, 4, 5, 1},
+ {"PB6", 1, 6, {"gpio_in", "gpio_out", "i2s0", "aif2", "pb_eint6", NULL}, 4, 6, 1},
+ {"PB7", 1, 7, {"gpio_in", "gpio_out", "i2s0", "aif2", "pb_eint7", NULL}, 4, 7, 1},
+
+ {"PC0", 2, 0, {"gpio_in", "gpio_out", "nand0", "spi0", NULL, NULL, NULL, NULL}},
+ {"PC1", 2, 1, {"gpio_in", "gpio_out", "nand0", "spi0", NULL, NULL, NULL, NULL}},
+ {"PC2", 2, 2, {"gpio_in", "gpio_out", "nand0", "spi0", NULL, NULL, NULL, NULL}},
+ {"PC3", 2, 3, {"gpio_in", "gpio_out", "nand0", "spi0", NULL, NULL, NULL, NULL}},
+ {"PC4", 2, 4, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}},
+ {"PC5", 2, 5, {"gpio_in", "gpio_out", "nand0", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC6", 2, 6, {"gpio_in", "gpio_out", "nand0", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC7", 2, 7, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}},
+ {"PC8", 2, 8, {"gpio_in", "gpio_out", "nand0", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC9", 2, 9, {"gpio_in", "gpio_out", "nand0", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC10", 2, 10, {"gpio_in", "gpio_out", "nand0", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC11", 2, 11, {"gpio_in", "gpio_out", "nand0", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC12", 2, 12, {"gpio_in", "gpio_out", "nand0", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC13", 2, 13, {"gpio_in", "gpio_out", "nand0", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC14", 2, 14, {"gpio_in", "gpio_out", "nand0", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC15", 2, 15, {"gpio_in", "gpio_out", "nand0", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC16", 2, 16, {"gpio_in", "gpio_out", "nand0", "mmc2", NULL, NULL, NULL, NULL}},
+
+ {"PD2", 3, 2, {"gpio_in", "gpio_out", "lcd0", "mmc1", NULL, NULL, NULL, NULL}},
+ {"PD3", 3, 3, {"gpio_in", "gpio_out", "lcd0", "mmc1", NULL, NULL, NULL, NULL}},
+ {"PD4", 3, 4, {"gpio_in", "gpio_out", "lcd0", "mmc1", NULL, NULL, NULL, NULL}},
+ {"PD5", 3, 5, {"gpio_in", "gpio_out", "lcd0", "mmc1", NULL, NULL, NULL, NULL}},
+ {"PD6", 3, 6, {"gpio_in", "gpio_out", "lcd0", "mmc1", NULL, NULL, NULL, NULL}},
+ {"PD7", 3, 7, {"gpio_in", "gpio_out", "lcd0", "mmc1", NULL, NULL, NULL, NULL}},
+ {"PD10", 3, 10, {"gpio_in", "gpio_out", "lcd0", "uart1", NULL, NULL, NULL, NULL}},
+ {"PD11", 3, 11, {"gpio_in", "gpio_out", "lcd0", "uart1", NULL, NULL, NULL, NULL}},
+ {"PD12", 3, 12, {"gpio_in", "gpio_out", "lcd0", "uart1", NULL, NULL, NULL, NULL}},
+ {"PD13", 3, 13, {"gpio_in", "gpio_out", "lcd0", "uart1", NULL, NULL, NULL, NULL}},
+ {"PD14", 3, 14, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD15", 3, 15, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}},
+ {"PD18", 3, 18, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD19", 3, 19, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD20", 3, 20, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD21", 3, 21, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD22", 3, 22, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD23", 3, 23, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD24", 3, 24, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD25", 3, 25, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD26", 3, 26, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+ {"PD27", 3, 27, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}},
+
+ {"PE0", 4, 0, {"gpio_in", "gpio_out", "csi", NULL, NULL, NULL, NULL, NULL}, 0, 0},
+ {"PE1", 4, 1, {"gpio_in", "gpio_out", "csi", NULL, NULL, NULL, NULL, NULL}, 0, 0},
+ {"PE2", 4, 2, {"gpio_in", "gpio_out", "csi", NULL, NULL, NULL, NULL, NULL}, 0, 0},
+ {"PE3", 4, 3, {"gpio_in", "gpio_out", "csi", NULL, NULL, NULL, NULL, NULL}, 0, 0},
+ {"PE4", 4, 4, {"gpio_in", "gpio_out", "csi", NULL, NULL, NULL, NULL, NULL}, 0, 0},
+ {"PE5", 4, 5, {"gpio_in", "gpio_out", "csi", NULL, NULL, NULL, NULL, NULL}, 0, 0},
+ {"PE6", 4, 6, {"gpio_in", "gpio_out", "csi", NULL, NULL, NULL, NULL, NULL}, 0, 0},
+ {"PE7", 4, 7, {"gpio_in", "gpio_out", "csi", NULL, NULL, NULL, NULL, NULL}, 0, 0},
+ {"PE8", 4, 8, {"gpio_in", "gpio_out", "csi", NULL, NULL, NULL, NULL, NULL}, 0, 0},
+ {"PE9", 4, 9, {"gpio_in", "gpio_out", "csi", NULL, NULL, NULL, NULL, NULL}, 0, 0},
+ {"PE10", 4, 10, {"gpio_in", "gpio_out", "csi", NULL, NULL, NULL, NULL, NULL}, 0, 0},
+ {"PE11", 4, 11, {"gpio_in", "gpio_out", "csi", NULL, NULL, NULL, NULL, NULL}, 0, 0},
+ {"PE12", 4, 12, {"gpio_in", "gpio_out", "csi", "i2c2", NULL, NULL, NULL, NULL}, 0, 0},
+ {"PE13", 4, 13, {"gpio_in", "gpio_out", "csi", "i2c2", NULL, NULL, NULL, NULL}, 0, 0},
+ {"PE14", 4, 14, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}, 0, 0},
+ {"PE15", 4, 15, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}, 0, 0},
+ {"PE16", 4, 16, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}, 0, 0},
+ {"PE17", 4, 16, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}, 0, 0},
+
+ {"PF0", 5, 0, {"gpio_in", "gpio_out", "mmc0", "jtag", NULL, NULL, NULL}},
+ {"PF1", 5, 1, {"gpio_in", "gpio_out", "mmc0", "jtag", NULL, NULL, NULL}},
+ {"PF2", 5, 2, {"gpio_in", "gpio_out", "mmc0", "uart0", NULL, NULL, NULL}},
+ {"PF3", 5, 3, {"gpio_in", "gpio_out", "mmc0", "jtag", NULL, NULL, NULL}},
+ {"PF4", 5, 4, {"gpio_in", "gpio_out", "mmc0", "uart0", NULL, NULL, NULL}},
+ {"PF5", 5, 5, {"gpio_in", "gpio_out", "mmc0", "jtag", NULL, NULL, NULL}},
+
+ {"PG0", 6, 0, {"gpio_in", "gpio_out", "mmc1", NULL, "pg_eint0", NULL}, 4, 0, 6},
+ {"PG1", 6, 1, {"gpio_in", "gpio_out", "mmc1", NULL, "pg_eint1", NULL}, 4, 1, 6},
+ {"PG2", 6, 2, {"gpio_in", "gpio_out", "mmc1", NULL, "pg_eint2", NULL}, 4, 2, 6},
+ {"PG3", 6, 3, {"gpio_in", "gpio_out", "mmc1", NULL, "pg_eint3", NULL}, 4, 3, 6},
+ {"PG4", 6, 4, {"gpio_in", "gpio_out", "mmc1", NULL, "pg_eint4", NULL}, 4, 4, 6},
+ {"PG5", 6, 5, {"gpio_in", "gpio_out", "mmc1", NULL, "pg_eint5", NULL}, 4, 5, 6},
+ {"PG6", 6, 6, {"gpio_in", "gpio_out", "uart1", NULL, "pg_eint6", NULL}, 4, 6, 6},
+ {"PG7", 6, 7, {"gpio_in", "gpio_out", "uart1", NULL, "pg_eint7", NULL}, 4, 7, 6},
+ {"PG8", 6, 8, {"gpio_in", "gpio_out", "uart1", NULL, "pg_eint8", NULL}, 4, 8, 6},
+ {"PG9", 6, 9, {"gpio_in", "gpio_out", "uart1", NULL, "pg_eint9", NULL}, 4, 9, 6},
+ {"PG10", 6, 10, {"gpio_in", "gpio_out", "i2s1", "aif3", "pg_eint10", NULL}, 4, 10, 6},
+ {"PG11", 6, 11, {"gpio_in", "gpio_out", "i2s1", "aif3", "pg_eint11", NULL}, 4, 11, 6},
+ {"PG12", 6, 12, {"gpio_in", "gpio_out", "i2s1", "aif3", "pg_eint12", NULL}, 4, 12, 6},
+ {"PG13", 6, 13, {"gpio_in", "gpio_out", "i2s1", "aif3", "pg_eint13", NULL}, 4, 13, 6},
+
+ {"PH0", 7, 0, {"gpio_in", "gpio_out", "pwm0", NULL, NULL, NULL, NULL, NULL}},
+ {"PH1", 7, 1, {"gpio_in", "gpio_out", "pwm1", NULL, NULL, NULL, NULL, NULL}},
+ {"PH2", 7, 2, {"gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, NULL, NULL}},
+ {"PH3", 7, 3, {"gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, NULL, NULL}},
+ {"PH4", 7, 4, {"gpio_in", "gpio_out", "i2c1", NULL, NULL, NULL, NULL, NULL}},
+ {"PH5", 7, 5, {"gpio_in", "gpio_out", "i2c1", NULL, NULL, NULL, NULL, NULL}},
+ {"PH6", 7, 6, {"gpio_in", "gpio_out", "spi0", "uart3", NULL, NULL, NULL, NULL}},
+ {"PH7", 7, 7, {"gpio_in", "gpio_out", "spi0", "uart3", NULL, NULL, NULL, NULL}},
+ {"PH8", 7, 8, {"gpio_in", "gpio_out", "spi0", "uart3", NULL, NULL, NULL, NULL}},
+ {"PH9", 7, 9, {"gpio_in", "gpio_out", "spi0", "uart3", NULL, NULL, NULL, NULL}},
+};
+
+const struct allwinner_padconf a33_padconf = {
+ .npins = nitems(a33_pins),
+ .pins = a33_pins,
+};
+
+#endif /* SOC_ALLWINNER_A33 */
diff --git a/sys/arm/allwinner/a33/files.a33 b/sys/arm/allwinner/a33/files.a33
new file mode 100644
index 000000000000..c80c476e7dd2
--- /dev/null
+++ b/sys/arm/allwinner/a33/files.a33
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+arm/allwinner/a33/a33_padconf.c standard
diff --git a/sys/arm/allwinner/a64/a64_padconf.c b/sys/arm/allwinner/a64/a64_padconf.c
new file mode 100644
index 000000000000..08d600bf7112
--- /dev/null
+++ b/sys/arm/allwinner/a64/a64_padconf.c
@@ -0,0 +1,159 @@
+/*-
+ * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/types.h>
+
+#include <arm/allwinner/allwinner_pinctrl.h>
+
+#include "opt_soc.h"
+
+#ifdef SOC_ALLWINNER_A64
+
+static const struct allwinner_pins a64_pins[] = {
+ { "PB0", 1, 0, { "gpio_in", "gpio_out", "uart2", NULL, "jtag", NULL, "pb_eint0" }, 6, 0, 0},
+ { "PB1", 1, 1, { "gpio_in", "gpio_out", "uart2", NULL, "jtag", "sim", "pb_eint1" }, 6, 1, 0},
+ { "PB2", 1, 2, { "gpio_in", "gpio_out", "uart2", NULL, "jtag", "sim", "pb_eint2" }, 6, 2, 0},
+ { "PB3", 1, 3, { "gpio_in", "gpio_out", "uart2", "i2s0", "jtag", "sim", "pb_eint3" }, 6, 3, 0},
+ { "PB4", 1, 4, { "gpio_in", "gpio_out", "aif2", "pcm0", NULL, "sim", "pb_eint4" }, 6, 4, 0},
+ { "PB5", 1, 5, { "gpio_in", "gpio_out", "aif2", "pcm0", NULL, "sim", "pb_eint5" }, 6, 5, 0},
+ { "PB6", 1, 6, { "gpio_in", "gpio_out", "aif2", "pcm0", NULL, "sim", "pb_eint6" }, 6, 6, 0},
+ { "PB7", 1, 7, { "gpio_in", "gpio_out", "aif2", "pcm0", NULL, "sim", "pb_eint7" }, 6, 7, 0},
+ { "PB8", 1, 8, { "gpio_in", "gpio_out", NULL, NULL, "uart0", NULL, "pb_eint8" }, 6, 8, 0},
+ { "PB9", 1, 9, { "gpio_in", "gpio_out", NULL, NULL, "uart0", NULL, "pb_eint9" }, 6, 9, 0},
+
+ { "PC0", 2, 0, { "gpio_in", "gpio_out", "nand", NULL, "spi0" } },
+ { "PC1", 2, 1, { "gpio_in", "gpio_out", "nand", "mmc2", "spi0" } },
+ { "PC2", 2, 2, { "gpio_in", "gpio_out", "nand", NULL, "spi0" } },
+ { "PC3", 2, 3, { "gpio_in", "gpio_out", "nand", NULL, "spi0" } },
+ { "PC4", 2, 4, { "gpio_in", "gpio_out", "nand" } },
+ { "PC5", 2, 5, { "gpio_in", "gpio_out", "nand", "mmc2" } },
+ { "PC6", 2, 6, { "gpio_in", "gpio_out", "nand", "mmc2" } },
+ { "PC7", 2, 7, { "gpio_in", "gpio_out", "nand" } },
+ { "PC8", 2, 8, { "gpio_in", "gpio_out", "nand", "mmc2" } },
+ { "PC9", 2, 9, { "gpio_in", "gpio_out", "nand", "mmc2" } },
+ { "PC10", 2, 10, { "gpio_in", "gpio_out", "nand", "mmc2" } },
+ { "PC11", 2, 11, { "gpio_in", "gpio_out", "nand", "mmc2" } },
+ { "PC12", 2, 12, { "gpio_in", "gpio_out", "nand", "mmc2" } },
+ { "PC13", 2, 13, { "gpio_in", "gpio_out", "nand", "mmc2" } },
+ { "PC14", 2, 14, { "gpio_in", "gpio_out", "nand", "mmc2" } },
+ { "PC15", 2, 15, { "gpio_in", "gpio_out", "nand", "mmc2" } },
+ { "PC16", 2, 16, { "gpio_in", "gpio_out", "nand", "mmc2" } },
+
+ { "PD0", 3, 0, { "gpio_in", "gpio_out", "lcd", "uart3", "spi1", "ccir" } },
+ { "PD1", 3, 1, { "gpio_in", "gpio_out", "lcd", "uart3", "spi1", "ccir" } },
+ { "PD2", 3, 2, { "gpio_in", "gpio_out", "lcd", "uart4", "spi1", "ccir" } },
+ { "PD3", 3, 3, { "gpio_in", "gpio_out", "lcd", "uart4", "spi1", "ccir" } },
+ { "PD4", 3, 4, { "gpio_in", "gpio_out", "lcd", "uart4", "spi1", "ccir" } },
+ { "PD5", 3, 5, { "gpio_in", "gpio_out", "lcd", "uart4", "spi1", "ccir" } },
+ { "PD6", 3, 6, { "gpio_in", "gpio_out", "lcd", NULL, NULL, "ccir" } },
+ { "PD7", 3, 7, { "gpio_in", "gpio_out", "lcd", NULL, NULL, "ccir" } },
+ { "PD8", 3, 8, { "gpio_in", "gpio_out", "lcd", NULL, "emac", "ccir" } },
+ { "PD9", 3, 9, { "gpio_in", "gpio_out", "lcd", NULL, "emac", "ccir" } },
+ { "PD10", 3, 10, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } },
+ { "PD11", 3, 11, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } },
+ { "PD12", 3, 12, { "gpio_in", "gpio_out", "lcd", "lvds", "emac" } },
+ { "PD13", 3, 13, { "gpio_in", "gpio_out", "lcd", "lvds", "emac" } },
+ { "PD14", 3, 14, { "gpio_in", "gpio_out", "lcd", "lvds", "emac" } },
+ { "PD15", 3, 15, { "gpio_in", "gpio_out", "lcd", "lvds", "emac", "ccir" } },
+ { "PD16", 3, 16, { "gpio_in", "gpio_out", "lcd", "lvds", "emac", "ccir" } },
+ { "PD17", 3, 17, { "gpio_in", "gpio_out", "lcd", "lvds", "emac" } },
+ { "PD18", 3, 18, { "gpio_in", "gpio_out", "lcd", "lvds", "emac" } },
+ { "PD19", 3, 19, { "gpio_in", "gpio_out", "lcd", "lvds", "emac" } },
+ { "PD20", 3, 20, { "gpio_in", "gpio_out", "lcd", "lvds", "emac" } },
+ { "PD21", 3, 21, { "gpio_in", "gpio_out", "lcd", "lvds", "emac" } },
+ { "PD22", 3, 22, { "gpio_in", "gpio_out", "pwm0", NULL, "emac" } },
+ { "PD23", 3, 23, { "gpio_in", "gpio_out", NULL, NULL, "emac" } },
+ { "PD24", 3, 24, { "gpio_in", "gpio_out" } },
+
+ { "PE0", 4, 0, { "gpio_in", "gpio_out", "csi", NULL, "ts" } },
+ { "PE1", 4, 1, { "gpio_in", "gpio_out", "csi", NULL, "ts" } },
+ { "PE2", 4, 2, { "gpio_in", "gpio_out", "csi", NULL, "ts" } },
+ { "PE3", 4, 3, { "gpio_in", "gpio_out", "csi", NULL, "ts" } },
+ { "PE4", 4, 4, { "gpio_in", "gpio_out", "csi", NULL, "ts" } },
+ { "PE5", 4, 5, { "gpio_in", "gpio_out", "csi", NULL, "ts" } },
+ { "PE6", 4, 6, { "gpio_in", "gpio_out", "csi", NULL, "ts" } },
+ { "PE7", 4, 7, { "gpio_in", "gpio_out", "csi", NULL, "ts" } },
+ { "PE8", 4, 8, { "gpio_in", "gpio_out", "csi", NULL, "ts" } },
+ { "PE9", 4, 9, { "gpio_in", "gpio_out", "csi", NULL, "ts" } },
+ { "PE10", 4, 10, { "gpio_in", "gpio_out", "csi", NULL, "ts" } },
+ { "PE11", 4, 11, { "gpio_in", "gpio_out", "csi", NULL, "ts" } },
+ { "PE12", 4, 12, { "gpio_in", "gpio_out", "csi" } },
+ { "PE13", 4, 13, { "gpio_in", "gpio_out", "csi" } },
+ { "PE14", 4, 14, { "gpio_in", "gpio_out", "pll_lock", "twi2" } },
+ { "PE15", 4, 15, { "gpio_in", "gpio_out", NULL, "twi2" } },
+ { "PE16", 4, 16, { "gpio_in", "gpio_out" } },
+ { "PE17", 4, 17, { "gpio_in", "gpio_out" } },
+
+ { "PF0", 5, 0, { "gpio_in", "gpio_out", "mmc0", "jtag" } },
+ { "PF1", 5, 1, { "gpio_in", "gpio_out", "mmc0", "jtag" } },
+ { "PF2", 5, 2, { "gpio_in", "gpio_out", "mmc0", "uart0" } },
+ { "PF3", 5, 3, { "gpio_in", "gpio_out", "mmc0", "jtag" } },
+ { "PF4", 5, 4, { "gpio_in", "gpio_out", "mmc0", "uart0" } },
+ { "PF5", 5, 5, { "gpio_in", "gpio_out", "mmc0", "jtag" } },
+ { "PF6", 5, 6, { "gpio_in", "gpio_out" } },
+
+ { "PG0", 6, 0, { "gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "pg_eint0" }, 6, 0, 1},
+ { "PG1", 6, 1, { "gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "pg_eint1" }, 6, 1, 1},
+ { "PG2", 6, 2, { "gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "pg_eint2" }, 6, 2, 1},
+ { "PG3", 6, 3, { "gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "pg_eint3" }, 6, 3, 1},
+ { "PG4", 6, 4, { "gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "pg_eint4" }, 6, 4, 1},
+ { "PG5", 6, 5, { "gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "pg_eint5" }, 6, 5, 1},
+ { "PG6", 6, 6, { "gpio_in", "gpio_out", "uart1", NULL, NULL, NULL, "pg_eint6" }, 6, 6, 1},
+ { "PG7", 6, 7, { "gpio_in", "gpio_out", "uart1", NULL, NULL, NULL, "pg_eint7" }, 6, 7, 1},
+ { "PG8", 6, 8, { "gpio_in", "gpio_out", "uart1", NULL, NULL, NULL, "pg_eint8" }, 6, 8, 1},
+ { "PG9", 6, 9, { "gpio_in", "gpio_out", "uart1", NULL, NULL, NULL, "pg_eint9" }, 6, 9, 1},
+ { "PG10", 6, 10, { "gpio_in", "gpio_out", "aif3", "pcm1", NULL, NULL, "pg_eint10" }, 6, 10, 1},
+ { "PG11", 6, 11, { "gpio_in", "gpio_out", "aif3", "pcm1", NULL, NULL, "pg_eint11" }, 6, 11, 1},
+ { "PG12", 6, 12, { "gpio_in", "gpio_out", "aif3", "pcm1", NULL, NULL, "pg_eint12" }, 6, 12, 1},
+ { "PG13", 6, 13, { "gpio_in", "gpio_out", "aif3", "pcm1", NULL, NULL, "pg_eint13" }, 6, 13, 1},
+
+ { "PH0", 7, 0, { "gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, "ph_eint0" }, 6, 0, 2},
+ { "PH1", 7, 1, { "gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, "ph_eint1" }, 6, 1, 2},
+ { "PH2", 7, 2, { "gpio_in", "gpio_out", "i2c1", NULL, NULL, NULL, "ph_eint2" }, 6, 2, 2},
+ { "PH3", 7, 3, { "gpio_in", "gpio_out", "i2c1", NULL, NULL, NULL, "ph_eint3" }, 6, 3, 2},
+ { "PH4", 7, 4, { "gpio_in", "gpio_out", "uart3", NULL, NULL, NULL, "ph_eint4" }, 6, 4, 2},
+ { "PH5", 7, 5, { "gpio_in", "gpio_out", "uart3", NULL, NULL, NULL, "ph_eint5" }, 6, 5, 2},
+ { "PH6", 7, 6, { "gpio_in", "gpio_out", "uart3", NULL, NULL, NULL, "ph_eint6" }, 6, 6, 2},
+ { "PH7", 7, 7, { "gpio_in", "gpio_out", "uart3", NULL, NULL, NULL, "ph_eint7" }, 6, 7, 2},
+ { "PH8", 7, 8, { "gpio_in", "gpio_out", "owa", NULL, NULL, NULL, "ph_eint8" }, 6, 8, 2},
+ { "PH9", 7, 9, { "gpio_in", "gpio_out", NULL, NULL, NULL, NULL, "ph_eint9" }, 6, 9, 2},
+ { "PH10", 7, 10, { "gpio_in", "gpio_out", "mic", NULL, NULL, NULL, "ph_eint10" }, 6, 10, 2},
+ { "PH11", 7, 11, { "gpio_in", "gpio_out", "mic", NULL, NULL, NULL, "ph_eint11" }, 6, 11, 2},
+};
+
+const struct allwinner_padconf a64_padconf = {
+ .npins = nitems(a64_pins),
+ .pins = a64_pins,
+};
+
+#endif /* !SOC_ALLWINNER_A64 */
diff --git a/sys/arm/allwinner/a64/a64_r_padconf.c b/sys/arm/allwinner/a64/a64_r_padconf.c
new file mode 100644
index 000000000000..afbdfc28f7fc
--- /dev/null
+++ b/sys/arm/allwinner/a64/a64_r_padconf.c
@@ -0,0 +1,63 @@
+/*-
+ * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/types.h>
+
+#include <arm/allwinner/allwinner_pinctrl.h>
+
+#include "opt_soc.h"
+
+#ifdef SOC_ALLWINNER_A64
+
+static const struct allwinner_pins a64_r_pins[] = {
+ { "PL0", 0, 0, { "gpio_in", "gpio_out", "s_rsb", "s_i2c", NULL, NULL, "pl_eint0" }, 6, 0, 0},
+ { "PL1", 0, 1, { "gpio_in", "gpio_out", "s_rsb", "s_i2c", NULL, NULL, "pl_eint1" }, 6, 1, 0},
+ { "PL2", 0, 2, { "gpio_in", "gpio_out", "s_uart", NULL, NULL, NULL, "pl_eint2" }, 6, 2, 0},
+ { "PL3", 0, 3, { "gpio_in", "gpio_out", "s_uart", NULL, NULL, NULL, "pl_eint3" }, 6, 3, 0},
+ { "PL4", 0, 4, { "gpio_in", "gpio_out", "s_jtag", NULL, NULL, NULL, "pl_eint4" }, 6, 4, 0},
+ { "PL5", 0, 5, { "gpio_in", "gpio_out", "s_jtag", NULL, NULL, NULL, "pl_eint5" }, 6, 5, 0},
+ { "PL6", 0, 6, { "gpio_in", "gpio_out", "s_jtag", NULL, NULL, NULL, "pl_eint6" }, 6, 6, 0},
+ { "PL7", 0, 7, { "gpio_in", "gpio_out", "s_jtag", NULL, NULL, NULL, "pl_eint7" }, 6, 7, 0},
+ { "PL8", 0, 8, { "gpio_in", "gpio_out", "s_i2c", NULL, NULL, NULL, "pl_eint8" }, 6, 8, 0},
+ { "PL9", 0, 9, { "gpio_in", "gpio_out", "s_i2c", NULL, NULL, NULL, "pl_eint9" }, 6, 9, 0},
+ { "PL10", 0, 10, { "gpio_in", "gpio_out", "s_pwm", NULL, NULL, NULL, "pl_eint10" }, 6, 10, 0},
+ { "PL11", 0, 11, { "gpio_in", "gpio_out", "s_cir", NULL, NULL, NULL, "pl_eint11" }, 6, 11, 0},
+ { "PL12", 0, 12, { "gpio_in", "gpio_out", NULL, NULL, NULL, NULL, "pl_eint12" }, 6, 12, 0},
+};
+
+const struct allwinner_padconf a64_r_padconf = {
+ .npins = nitems(a64_r_pins),
+ .pins = a64_r_pins,
+};
+
+#endif /* !SOC_ALLWINNER_A64 */
diff --git a/sys/arm/allwinner/a83t/a83t_padconf.c b/sys/arm/allwinner/a83t/a83t_padconf.c
new file mode 100644
index 000000000000..5dc6f2752c1d
--- /dev/null
+++ b/sys/arm/allwinner/a83t/a83t_padconf.c
@@ -0,0 +1,161 @@
+/*-
+ * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/types.h>
+
+#include <arm/allwinner/allwinner_pinctrl.h>
+
+#ifdef SOC_ALLWINNER_A83T
+
+static const struct allwinner_pins a83t_pins[] = {
+ { "PB0", 1, 0, { "gpio_in", "gpio_out", "uart2", "jtag", NULL, NULL, "pb_eint0" }, 6, 0, 0},
+ { "PB1", 1, 1, { "gpio_in", "gpio_out", "uart2", "jtag", NULL, NULL, "pb_eint1" }, 6, 1, 0},
+ { "PB2", 1, 2, { "gpio_in", "gpio_out", "uart2", "jtag", NULL, NULL, "pb_eint2" }, 6, 2, 0},
+ { "PB3", 1, 3, { "gpio_in", "gpio_out", "uart2", "jtag", NULL, NULL, "pb_eint3" }, 6, 3, 0},
+ { "PB4", 1, 4, { "gpio_in", "gpio_out", "i2s0", "tdm", NULL, NULL, "pb_eint4" }, 6, 4, 0},
+ { "PB5", 1, 5, { "gpio_in", "gpio_out", "i2s0", "tdm", NULL, NULL, "pb_eint5" }, 6, 5, 0},
+ { "PB6", 1, 6, { "gpio_in", "gpio_out", "i2s0", "tdm", NULL, NULL, "pb_eint6" }, 6, 6, 0},
+ { "PB7", 1, 7, { "gpio_in", "gpio_out", "i2s0", "tdm", NULL, NULL, "pb_eint7" }, 6, 7, 0},
+ { "PB8", 1, 8, { "gpio_in", "gpio_out", "i2s0", "tdm", NULL, NULL, "pb_eint8" }, 6, 8, 0},
+ { "PB9", 1, 9, { "gpio_in", "gpio_out", "uart0", NULL, NULL, NULL, "pb_eint9" }, 6, 9, 0},
+ { "PB10", 1, 10, { "gpio_in", "gpio_out", "uart0", NULL, NULL, NULL, "pb_eint10" }, 6, 10, 0},
+
+ { "PC0", 2, 0, { "gpio_in", "gpio_out", "nand", "spi0" } },
+ { "PC1", 2, 1, { "gpio_in", "gpio_out", "nand", "spi0" } },
+ { "PC2", 2, 2, { "gpio_in", "gpio_out", "nand", "spi0" } },
+ { "PC3", 2, 3, { "gpio_in", "gpio_out", "nand", "spi0" } },
+ { "PC4", 2, 4, { "gpio_in", "gpio_out", "nand" } },
+ { "PC5", 2, 5, { "gpio_in", "gpio_out", "nand", "mmc2" } },
+ { "PC6", 2, 6, { "gpio_in", "gpio_out", "nand", "mmc2" } },
+ { "PC7", 2, 7, { "gpio_in", "gpio_out", "nand" } },
+ { "PC8", 2, 8, { "gpio_in", "gpio_out", "nand", "mmc2" } },
+ { "PC9", 2, 9, { "gpio_in", "gpio_out", "nand", "mmc2" } },
+ { "PC10", 2, 10, { "gpio_in", "gpio_out", "nand", "mmc2" } },
+ { "PC11", 2, 11, { "gpio_in", "gpio_out", "nand", "mmc2" } },
+ { "PC12", 2, 12, { "gpio_in", "gpio_out", "nand", "mmc2" } },
+ { "PC13", 2, 13, { "gpio_in", "gpio_out", "nand", "mmc2" } },
+ { "PC14", 2, 14, { "gpio_in", "gpio_out", "nand", "mmc2" } },
+ { "PC15", 2, 15, { "gpio_in", "gpio_out", "nand", "mmc2" } },
+ { "PC16", 2, 16, { "gpio_in", "gpio_out", "nand", "mmc2" } },
+ { "PC17", 2, 17, { "gpio_in", "gpio_out", "nand" } },
+ { "PC18", 2, 18, { "gpio_in", "gpio_out", "nand" } },
+
+ { "PD2", 3, 2, { "gpio_in", "gpio_out", "lcd", NULL, "gmac" } },
+ { "PD3", 3, 3, { "gpio_in", "gpio_out", "lcd", NULL, "gmac" } },
+ { "PD4", 3, 4, { "gpio_in", "gpio_out", "lcd", NULL, "gmac" } },
+ { "PD5", 3, 5, { "gpio_in", "gpio_out", "lcd", NULL, "gmac" } },
+ { "PD6", 3, 6, { "gpio_in", "gpio_out", "lcd", NULL, "gmac" } },
+ { "PD7", 3, 7, { "gpio_in", "gpio_out", "lcd", NULL, "gmac" } },
+ { "PD10", 3, 10, { "gpio_in", "gpio_out", "lcd", NULL, "gmac" } },
+ { "PD11", 3, 11, { "gpio_in", "gpio_out", "lcd", NULL, "gmac" } },
+ { "PD12", 3, 12, { "gpio_in", "gpio_out", "lcd", NULL, "gmac" } },
+ { "PD13", 3, 13, { "gpio_in", "gpio_out", "lcd", NULL, "gmac" } },
+ { "PD14", 3, 14, { "gpio_in", "gpio_out", "lcd", NULL, "gmac" } },
+ { "PD15", 3, 15, { "gpio_in", "gpio_out", "lcd", NULL, "gmac" } },
+ { "PD18", 3, 18, { "gpio_in", "gpio_out", "lcd", "lvds", "gmac" } },
+ { "PD19", 3, 19, { "gpio_in", "gpio_out", "lcd", "lvds", "gmac" } },
+ { "PD20", 3, 20, { "gpio_in", "gpio_out", "lcd", "lvds", "gmac" } },
+ { "PD21", 3, 21, { "gpio_in", "gpio_out", "lcd", "lvds", "gmac" } },
+ { "PD22", 3, 22, { "gpio_in", "gpio_out", "lcd", "lvds", "gmac" } },
+ { "PD23", 3, 23, { "gpio_in", "gpio_out", "lcd", "lvds", "gmac" } },
+ { "PD24", 3, 24, { "gpio_in", "gpio_out", "lcd", "lvds" } },
+ { "PD25", 3, 25, { "gpio_in", "gpio_out", "lcd", "lvds" } },
+ { "PD26", 3, 26, { "gpio_in", "gpio_out", "lcd", "lvds" } },
+ { "PD27", 3, 27, { "gpio_in", "gpio_out", "lcd", "lvds" } },
+ { "PD28", 3, 28, { "gpio_in", "gpio_out", "pwm" } },
+ { "PD29", 3, 29, { "gpio_in", "gpio_out" } },
+
+ { "PE0", 4, 0, { "gpio_in", "gpio_out", "csi", NULL, "ccir" } },
+ { "PE1", 4, 1, { "gpio_in", "gpio_out", "csi", NULL, "ccir" } },
+ { "PE2", 4, 2, { "gpio_in", "gpio_out", "csi", NULL, "ccir" } },
+ { "PE3", 4, 3, { "gpio_in", "gpio_out", "csi", NULL, "ccir" } },
+ { "PE4", 4, 4, { "gpio_in", "gpio_out", "csi" } },
+ { "PE5", 4, 5, { "gpio_in", "gpio_out", "csi" } },
+ { "PE6", 4, 6, { "gpio_in", "gpio_out", "csi", NULL, "ccir" } },
+ { "PE7", 4, 7, { "gpio_in", "gpio_out", "csi", NULL, "ccir" } },
+ { "PE8", 4, 8, { "gpio_in", "gpio_out", "csi", NULL, "ccir" } },
+ { "PE9", 4, 9, { "gpio_in", "gpio_out", "csi", NULL, "ccir" } },
+ { "PE10", 4, 10, { "gpio_in", "gpio_out", "csi", "uart4", "ccir" } },
+ { "PE11", 4, 11, { "gpio_in", "gpio_out", "csi", "uart4", "ccir" } },
+ { "PE12", 4, 12, { "gpio_in", "gpio_out", "csi", "uart4", "ccir" } },
+ { "PE13", 4, 13, { "gpio_in", "gpio_out", "csi", "uart4", "ccir" } },
+ { "PE14", 4, 14, { "gpio_in", "gpio_out", "csi", "twi2" } },
+ { "PE15", 4, 15, { "gpio_in", "gpio_out", "csi", "twi2" } },
+ { "PE16", 4, 16, { "gpio_in", "gpio_out" } },
+ { "PE17", 4, 17, { "gpio_in", "gpio_out" } },
+ { "PE18", 4, 18, { "gpio_in", "gpio_out", NULL, "owa" } },
+ { "PE19", 4, 19, { "gpio_in", "gpio_out" } },
+
+ { "PF0", 5, 0, { "gpio_in", "gpio_out", "mmc0", "jtag" } },
+ { "PF1", 5, 1, { "gpio_in", "gpio_out", "mmc0", "jtag" } },
+ { "PF2", 5, 2, { "gpio_in", "gpio_out", "mmc0", "uart0" } },
+ { "PF3", 5, 3, { "gpio_in", "gpio_out", "mmc0", "jtag" } },
+ { "PF4", 5, 4, { "gpio_in", "gpio_out", "mmc0", "uart0" } },
+ { "PF5", 5, 5, { "gpio_in", "gpio_out", "mmc0", "jtag" } },
+ { "PF6", 5, 6, { "gpio_in", "gpio_out" } },
+
+ { "PG0", 6, 0, { "gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "pg_eint0" }, 6, 0, 1},
+ { "PG1", 6, 1, { "gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "pg_eint1" }, 6, 1, 1},
+ { "PG2", 6, 2, { "gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "pg_eint2" }, 6, 2, 1},
+ { "PG3", 6, 3, { "gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "pg_eint3" }, 6, 3, 1},
+ { "PG4", 6, 4, { "gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "pg_eint4" }, 6, 4, 1},
+ { "PG5", 6, 5, { "gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "pg_eint5" }, 6, 5, 1},
+ { "PG6", 6, 6, { "gpio_in", "gpio_out", "uart1", "spi1", NULL, NULL, "pg_eint6" }, 6, 6, 1},
+ { "PG7", 6, 7, { "gpio_in", "gpio_out", "uart1", "spi1", NULL, NULL, "pg_eint7" }, 6, 7, 1},
+ { "PG8", 6, 8, { "gpio_in", "gpio_out", "uart1", "spi1", NULL, NULL, "pg_eint8" }, 6, 8, 1},
+ { "PG9", 6, 9, { "gpio_in", "gpio_out", "uart1", "spi1", NULL, NULL, "pg_eint9" }, 6, 9, 1},
+ { "PG10", 6, 10, { "gpio_in", "gpio_out", "i2s1", "uart3", NULL, NULL, "pg_eint10" }, 6, 10, 1},
+ { "PG11", 6, 11, { "gpio_in", "gpio_out", "i2s1", "uart3", NULL, NULL, "pg_eint11" }, 6, 11, 1},
+ { "PG12", 6, 12, { "gpio_in", "gpio_out", "i2s1", "uart3", NULL, NULL, "pg_eint12" }, 6, 12, 1},
+ { "PG13", 6, 13, { "gpio_in", "gpio_out", "i2s1", "uart3", NULL, NULL, "pg_eint13" }, 6, 13, 1},
+
+ { "PH0", 7, 0, { "gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, "ph_eint0" }, 6, 0, 2},
+ { "PH1", 7, 1, { "gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, "ph_eint1" }, 6, 1, 2},
+ { "PH2", 7, 2, { "gpio_in", "gpio_out", "i2c1", NULL, NULL, NULL, "ph_eint2" }, 6, 2, 2},
+ { "PH3", 7, 3, { "gpio_in", "gpio_out", "i2c1", NULL, NULL, NULL, "ph_eint3" }, 6, 3, 2},
+ { "PH4", 7, 4, { "gpio_in", "gpio_out", "i2c2", NULL, NULL, NULL, "ph_eint4" }, 6, 4, 2},
+ { "PH5", 7, 5, { "gpio_in", "gpio_out", "i2c2", NULL, NULL, NULL, "ph_eint5" }, 6, 5, 2},
+ { "PH6", 7, 6, { "gpio_in", "gpio_out", "hdmiddc", NULL, NULL, NULL, "ph_eint6" }, 6, 6, 2},
+ { "PH7", 7, 7, { "gpio_in", "gpio_out", "hdmiddc", NULL, NULL, NULL, "ph_eint7" }, 6, 7, 2},
+ { "PH8", 7, 8, { "gpio_in", "gpio_out", "hdmiddc", NULL, NULL, NULL, "ph_eint8" }, 6, 8, 2},
+ { "PH9", 7, 9, { "gpio_in", "gpio_out", NULL, NULL, NULL, NULL, "ph_eint9" }, 6, 9, 2},
+ { "PH10", 7, 10, { "gpio_in", "gpio_out", NULL, NULL, NULL, NULL, "ph_eint10" }, 6, 10, 2},
+ { "PH11", 7, 11, { "gpio_in", "gpio_out", NULL, NULL, NULL, NULL, "ph_eint11" }, 6, 11, 2},
+};
+
+const struct allwinner_padconf a83t_padconf = {
+ .npins = nitems(a83t_pins),
+ .pins = a83t_pins,
+};
+
+#endif /* !SOC_ALLWINNER_A83T */
diff --git a/sys/arm/allwinner/a83t/a83t_r_padconf.c b/sys/arm/allwinner/a83t/a83t_r_padconf.c
new file mode 100644
index 000000000000..ed4f08d67415
--- /dev/null
+++ b/sys/arm/allwinner/a83t/a83t_r_padconf.c
@@ -0,0 +1,61 @@
+/*-
+ * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/types.h>
+
+#include <arm/allwinner/allwinner_pinctrl.h>
+
+#ifdef SOC_ALLWINNER_A83T
+
+static const struct allwinner_pins a83t_r_pins[] = {
+ { "PL0", 0, 0, { "gpio_in", "gpio_out", "s_rsb", "s_i2c", NULL, NULL, "eint" } },
+ { "PL1", 0, 1, { "gpio_in", "gpio_out", "s_rsb", "s_i2c", NULL, NULL, "eint" } },
+ { "PL2", 0, 2, { "gpio_in", "gpio_out", "s_uart", NULL, NULL, NULL, "eint" } },
+ { "PL3", 0, 3, { "gpio_in", "gpio_out", "s_uart", NULL, NULL, NULL, "eint" } },
+ { "PL4", 0, 4, { "gpio_in", "gpio_out", "s_jtag", NULL, NULL, NULL, "eint" } },
+ { "PL5", 0, 5, { "gpio_in", "gpio_out", "s_jtag", NULL, NULL, NULL, "eint" } },
+ { "PL6", 0, 6, { "gpio_in", "gpio_out", "s_jtag", NULL, NULL, NULL, "eint" } },
+ { "PL7", 0, 7, { "gpio_in", "gpio_out", "s_jtag", NULL, NULL, NULL, "eint" } },
+ { "PL8", 0, 8, { "gpio_in", "gpio_out", "s_i2c", NULL, NULL, NULL, "eint" } },
+ { "PL9", 0, 9, { "gpio_in", "gpio_out", "s_i2c", NULL, NULL, NULL, "eint" } },
+ { "PL10", 0, 10, { "gpio_in", "gpio_out", "s_pwm", NULL, NULL, NULL, "eint" } },
+ { "PL11", 0, 11, { "gpio_in", "gpio_out", NULL, NULL, NULL, "eint" } },
+ { "PL12", 0, 12, { "gpio_in", "gpio_out", "s_cir", NULL, NULL, NULL, "eint" } },
+};
+
+const struct allwinner_padconf a83t_r_padconf = {
+ .npins = nitems(a83t_r_pins),
+ .pins = a83t_r_pins,
+};
+
+#endif /* !SOC_ALLWINNER_A83T */
diff --git a/sys/arm/allwinner/a83t/files.a83t b/sys/arm/allwinner/a83t/files.a83t
new file mode 100644
index 000000000000..d8822ae019c4
--- /dev/null
+++ b/sys/arm/allwinner/a83t/files.a83t
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+arm/allwinner/clkng/ccu_a83t.c standard
+arm/allwinner/clkng/ccu_sun8i_r.c standard
+arm/allwinner/a83t/a83t_padconf.c standard
+arm/allwinner/a83t/a83t_r_padconf.c standard
diff --git a/sys/arm/allwinner/allwinner_pinctrl.h b/sys/arm/allwinner/allwinner_pinctrl.h
new file mode 100644
index 000000000000..d9fb056dff67
--- /dev/null
+++ b/sys/arm/allwinner/allwinner_pinctrl.h
@@ -0,0 +1,48 @@
+/*-
+ * Copyright (c) 2016 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _ALLWINNER_PINCTRL_H_
+#define _ALLWINNER_PINCTRL_H_
+
+#define AW_MAX_FUNC_BY_PIN 8
+
+struct allwinner_pins {
+ const char *name;
+ uint8_t port;
+ uint8_t pin;
+ const char *functions[8];
+ uint8_t eint_func;
+ uint8_t eint_num;
+ uint8_t eint_bank;
+};
+
+struct allwinner_padconf {
+ uint32_t npins;
+ const struct allwinner_pins * pins;
+};
+
+#endif /* _ALLWINNER_PINCTRL_H_ */
diff --git a/sys/arm/allwinner/aw_ccu.c b/sys/arm/allwinner/aw_ccu.c
new file mode 100644
index 000000000000..6cdf75ffecf9
--- /dev/null
+++ b/sys/arm/allwinner/aw_ccu.c
@@ -0,0 +1,254 @@
+/*-
+ * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Allwinner oscillator clock
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <machine/bus.h>
+
+#include <dev/fdt/simplebus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/extres/clk/clk.h>
+
+#include "clkdev_if.h"
+
+#define CCU_BASE 0x01c20000
+#define CCU_SIZE 0x400
+
+struct aw_ccu_softc {
+ struct simplebus_softc sc;
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+ struct mtx mtx;
+ int flags;
+};
+
+static struct ofw_compat_data compat_data[] = {
+ { "allwinner,sun7i-a20", 1 },
+ { "allwinner,sun6i-a31", 1 },
+ { "allwinner,sun6i-a31s", 1 },
+ { NULL, 0 }
+};
+
+static int
+aw_ccu_check_addr(struct aw_ccu_softc *sc, bus_addr_t addr,
+ bus_space_handle_t *pbsh, bus_size_t *poff)
+{
+ if (addr >= CCU_BASE && addr < (CCU_BASE + CCU_SIZE)) {
+ *poff = addr - CCU_BASE;
+ *pbsh = sc->bsh;
+ return (0);
+ }
+ return (EINVAL);
+}
+
+static int
+aw_ccu_write_4(device_t dev, bus_addr_t addr, uint32_t val)
+{
+ struct aw_ccu_softc *sc;
+ bus_space_handle_t bsh;
+ bus_size_t reg;
+
+ sc = device_get_softc(dev);
+
+ if (aw_ccu_check_addr(sc, addr, &bsh, &reg) != 0)
+ return (EINVAL);
+
+ mtx_assert(&sc->mtx, MA_OWNED);
+ bus_space_write_4(sc->bst, bsh, reg, val);
+
+ return (0);
+}
+
+static int
+aw_ccu_read_4(device_t dev, bus_addr_t addr, uint32_t *val)
+{
+ struct aw_ccu_softc *sc;
+ bus_space_handle_t bsh;
+ bus_size_t reg;
+
+ sc = device_get_softc(dev);
+
+ if (aw_ccu_check_addr(sc, addr, &bsh, &reg) != 0)
+ return (EINVAL);
+
+ mtx_assert(&sc->mtx, MA_OWNED);
+ *val = bus_space_read_4(sc->bst, bsh, reg);
+
+ return (0);
+}
+
+static int
+aw_ccu_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set)
+{
+ struct aw_ccu_softc *sc;
+ bus_space_handle_t bsh;
+ bus_size_t reg;
+ uint32_t val;
+
+ sc = device_get_softc(dev);
+
+ if (aw_ccu_check_addr(sc, addr, &bsh, &reg) != 0)
+ return (EINVAL);
+
+ mtx_assert(&sc->mtx, MA_OWNED);
+ val = bus_space_read_4(sc->bst, bsh, reg);
+ val &= ~clr;
+ val |= set;
+ bus_space_write_4(sc->bst, bsh, reg, val);
+
+ return (0);
+}
+
+static void
+aw_ccu_device_lock(device_t dev)
+{
+ struct aw_ccu_softc *sc;
+
+ sc = device_get_softc(dev);
+ mtx_lock(&sc->mtx);
+}
+
+static void
+aw_ccu_device_unlock(device_t dev)
+{
+ struct aw_ccu_softc *sc;
+
+ sc = device_get_softc(dev);
+ mtx_unlock(&sc->mtx);
+}
+
+static const struct ofw_compat_data *
+aw_ccu_search_compatible(void)
+{
+ const struct ofw_compat_data *compat;
+ phandle_t root;
+
+ root = OF_finddevice("/");
+ for (compat = compat_data; compat->ocd_str != NULL; compat++)
+ if (ofw_bus_node_is_compatible(root, compat->ocd_str))
+ break;
+
+ return (compat);
+}
+
+static int
+aw_ccu_probe(device_t dev)
+{
+ const char *name;
+
+ name = ofw_bus_get_name(dev);
+
+ if (name == NULL || strcmp(name, "clocks") != 0)
+ return (ENXIO);
+
+ if (aw_ccu_search_compatible()->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner Clock Control Unit");
+ return (BUS_PROBE_SPECIFIC);
+}
+
+static int
+aw_ccu_attach(device_t dev)
+{
+ struct aw_ccu_softc *sc;
+ phandle_t node, child;
+ device_t cdev;
+ int error;
+
+ sc = device_get_softc(dev);
+ node = ofw_bus_get_node(dev);
+
+ simplebus_init(dev, node);
+
+ sc->flags = aw_ccu_search_compatible()->ocd_data;
+
+ /*
+ * Map registers. The DT doesn't have a "reg" property
+ * for the /clocks node and child nodes have conflicting "reg"
+ * properties.
+ */
+ sc->bst = bus_get_bus_tag(dev);
+ error = bus_space_map(sc->bst, CCU_BASE, CCU_SIZE, 0,
+ &sc->bsh);
+ if (error != 0) {
+ device_printf(dev, "couldn't map CCU: %d\n", error);
+ return (error);
+ }
+
+ mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ /* Attach child devices */
+ for (child = OF_child(node); child > 0; child = OF_peer(child)) {
+ cdev = simplebus_add_device(dev, child, 0, NULL, -1, NULL);
+ if (cdev != NULL)
+ device_probe_and_attach(cdev);
+ }
+
+ return (bus_generic_attach(dev));
+}
+
+static device_method_t aw_ccu_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, aw_ccu_probe),
+ DEVMETHOD(device_attach, aw_ccu_attach),
+
+ /* clkdev interface */
+ DEVMETHOD(clkdev_write_4, aw_ccu_write_4),
+ DEVMETHOD(clkdev_read_4, aw_ccu_read_4),
+ DEVMETHOD(clkdev_modify_4, aw_ccu_modify_4),
+ DEVMETHOD(clkdev_device_lock, aw_ccu_device_lock),
+ DEVMETHOD(clkdev_device_unlock, aw_ccu_device_unlock),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(aw_ccu, aw_ccu_driver, aw_ccu_methods,
+ sizeof(struct aw_ccu_softc), simplebus_driver);
+
+static devclass_t aw_ccu_devclass;
+
+EARLY_DRIVER_MODULE(aw_ccu, simplebus, aw_ccu_driver, aw_ccu_devclass,
+ 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
+
+MODULE_VERSION(aw_ccu, 1);
diff --git a/sys/arm/allwinner/aw_cir.c b/sys/arm/allwinner/aw_cir.c
new file mode 100644
index 000000000000..eabab4cba8f2
--- /dev/null
+++ b/sys/arm/allwinner/aw_cir.c
@@ -0,0 +1,555 @@
+/*-
+ * Copyright (c) 2016 Ganbold Tsagaankhuu <ganbold@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Allwinner Consumer IR controller
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+#include <machine/bus.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+
+#include <dev/evdev/input.h>
+#include <dev/evdev/evdev.h>
+
+#define READ(_sc, _r) bus_read_4((_sc)->res[0], (_r))
+#define WRITE(_sc, _r, _v) bus_write_4((_sc)->res[0], (_r), (_v))
+
+/* IR Control */
+#define AW_IR_CTL 0x00
+/* Global Enable */
+#define AW_IR_CTL_GEN (1 << 0)
+/* RX enable */
+#define AW_IR_CTL_RXEN (1 << 1)
+/* CIR mode enable */
+#define AW_IR_CTL_MD (1 << 4) | (1 << 5)
+
+/* RX Config Reg */
+#define AW_IR_RXCTL 0x10
+/* Pulse Polarity Invert flag */
+#define AW_IR_RXCTL_RPPI (1 << 2)
+
+/* RX Data */
+#define AW_IR_RXFIFO 0x20
+
+/* RX Interrupt Control */
+#define AW_IR_RXINT 0x2C
+/* RX FIFO Overflow */
+#define AW_IR_RXINT_ROI_EN (1 << 0)
+/* RX Packet End */
+#define AW_IR_RXINT_RPEI_EN (1 << 1)
+/* RX FIFO Data Available */
+#define AW_IR_RXINT_RAI_EN (1 << 4)
+/* RX FIFO available byte level */
+#define AW_IR_RXINT_RAL(val) ((val) << 8)
+
+/* RX Interrupt Status Reg */
+#define AW_IR_RXSTA 0x30
+/* RX FIFO Get Available Counter */
+#define AW_IR_RXSTA_COUNTER(val) (((val) >> 8) & (sc->fifo_size * 2 - 1))
+/* Clear all interrupt status */
+#define AW_IR_RXSTA_CLEARALL 0xff
+
+/* IR Sample Configure Reg */
+#define AW_IR_CIR 0x34
+
+/*
+ * Frequency sample: 23437.5Hz (Cycle: 42.7us)
+ * Pulse of NEC Remote > 560us
+ */
+/* Filter Threshold = 8 * 42.7 = ~341us < 500us */
+#define AW_IR_RXFILT_VAL (((8) & 0x3f) << 2)
+/* Idle Threshold = (2 + 1) * 128 * 42.7 = ~16.4ms > 9ms */
+#define AW_IR_RXIDLE_VAL (((2) & 0xff) << 8)
+
+/* Bit 15 - value (pulse/space) */
+#define VAL_MASK 0x80
+/* Bits 0:14 - sample duration */
+#define PERIOD_MASK 0x7f
+
+/* Clock rate for IR0 or IR1 clock in CIR mode */
+#define AW_IR_BASE_CLK 3000000
+/* Frequency sample 3MHz/64 = 46875Hz (21.3us) */
+#define AW_IR_SAMPLE_64 (0 << 0)
+/* Frequency sample 3MHz/128 = 23437.5Hz (42.7us) */
+#define AW_IR_SAMPLE_128 (1 << 0)
+
+#define AW_IR_ERROR_CODE 0xffffffff
+#define AW_IR_REPEAT_CODE 0x0
+
+/* 80 * 42.7 = ~3.4ms, Lead1(4.5ms) > AW_IR_L1_MIN */
+#define AW_IR_L1_MIN 80
+/* 40 * 42.7 = ~1.7ms, Lead0(4.5ms) Lead0R(2.25ms) > AW_IR_L0_MIN */
+#define AW_IR_L0_MIN 40
+/* 26 * 42.7 = ~1109us ~= 561 * 2, Pulse < AW_IR_PMAX */
+#define AW_IR_PMAX 26
+/* 26 * 42.7 = ~1109us ~= 561 * 2, D1 > AW_IR_DMID, D0 <= AW_IR_DMID */
+#define AW_IR_DMID 26
+/* 53 * 42.7 = ~2263us ~= 561 * 4, D < AW_IR_DMAX */
+#define AW_IR_DMAX 53
+
+/* Active Thresholds */
+#define AW_IR_ACTIVE_T_VAL AW_IR_L1_MIN
+#define AW_IR_ACTIVE_T (((AW_IR_ACTIVE_T_VAL - 1) & 0xff) << 16)
+#define AW_IR_ACTIVE_T_C_VAL 0
+#define AW_IR_ACTIVE_T_C ((AW_IR_ACTIVE_T_C_VAL & 0xff) << 23)
+
+/* Code masks */
+#define CODE_MASK 0x00ff00ff
+#define INV_CODE_MASK 0xff00ff00
+#define VALID_CODE_MASK 0x00ff0000
+
+enum {
+ A10_IR = 1,
+ A13_IR,
+ A31_IR,
+};
+
+#define AW_IR_RAW_BUF_SIZE 128
+
+struct aw_ir_softc {
+ device_t dev;
+ struct resource *res[2];
+ void * intrhand;
+ int fifo_size;
+ int dcnt; /* Packet Count */
+ unsigned char buf[AW_IR_RAW_BUF_SIZE];
+ struct evdev_dev *sc_evdev;
+};
+
+static struct resource_spec aw_ir_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE },
+ { -1, 0 }
+};
+
+static struct ofw_compat_data compat_data[] = {
+ { "allwinner,sun4i-a10-ir", A10_IR },
+ { "allwinner,sun5i-a13-ir", A13_IR },
+ { "allwinner,sun6i-a31-ir", A31_IR },
+ { NULL, 0 }
+};
+
+static void
+aw_ir_buf_reset(struct aw_ir_softc *sc)
+{
+
+ sc->dcnt = 0;
+}
+
+static void
+aw_ir_buf_write(struct aw_ir_softc *sc, unsigned char data)
+{
+
+ if (sc->dcnt < AW_IR_RAW_BUF_SIZE)
+ sc->buf[sc->dcnt++] = data;
+ else
+ if (bootverbose)
+ device_printf(sc->dev, "IR RX Buffer Full!\n");
+}
+
+static int
+aw_ir_buf_full(struct aw_ir_softc *sc)
+{
+
+ return (sc->dcnt >= AW_IR_RAW_BUF_SIZE);
+}
+
+static unsigned char
+aw_ir_read_data(struct aw_ir_softc *sc)
+{
+
+ return (unsigned char)(READ(sc, AW_IR_RXFIFO) & 0xff);
+}
+
+static unsigned long
+aw_ir_decode_packets(struct aw_ir_softc *sc)
+{
+ unsigned int len, code;
+ unsigned int active_delay;
+ unsigned char val, last;
+ int i, bitcount;
+
+ if (bootverbose)
+ device_printf(sc->dev, "sc->dcnt = %d\n", sc->dcnt);
+
+ /* Find Lead 1 (bit separator) */
+ active_delay = AW_IR_ACTIVE_T_VAL *
+ (AW_IR_ACTIVE_T_C_VAL != 0 ? 128 : 1);
+ len = active_delay;
+ if (bootverbose)
+ device_printf(sc->dev, "Initial len: %d\n", len);
+ for (i = 0; i < sc->dcnt; i++) {
+ val = sc->buf[i];
+ if (val & VAL_MASK)
+ len += (val & PERIOD_MASK) + 1;
+ else {
+ if (len > AW_IR_L1_MIN)
+ break;
+ len = 0;
+ }
+ }
+ if (bootverbose)
+ device_printf(sc->dev, "len = %d\n", len);
+ if ((val & VAL_MASK) || (len <= AW_IR_L1_MIN)) {
+ if (bootverbose)
+ device_printf(sc->dev, "Bit separator error\n");
+ goto error_code;
+ }
+
+ /* Find Lead 0 (bit length) */
+ len = 0;
+ for (; i < sc->dcnt; i++) {
+ val = sc->buf[i];
+ if (val & VAL_MASK) {
+ if(len > AW_IR_L0_MIN)
+ break;
+ len = 0;
+ } else
+ len += (val & PERIOD_MASK) + 1;
+ }
+ if ((!(val & VAL_MASK)) || (len <= AW_IR_L0_MIN)) {
+ if (bootverbose)
+ device_printf(sc->dev, "Bit length error\n");
+ goto error_code;
+ }
+
+ /* Start decoding */
+ code = 0;
+ bitcount = 0;
+ last = 1;
+ len = 0;
+ for (; i < sc->dcnt; i++) {
+ val = sc->buf[i];
+ if (last) {
+ if (val & VAL_MASK)
+ len += (val & PERIOD_MASK) + 1;
+ else {
+ if (len > AW_IR_PMAX) {
+ if (bootverbose)
+ device_printf(sc->dev,
+ "Pulse error, len=%d\n",
+ len);
+ goto error_code;
+ }
+ last = 0;
+ len = (val & PERIOD_MASK) + 1;
+ }
+ } else {
+ if (val & VAL_MASK) {
+ if (len > AW_IR_DMAX) {
+ if (bootverbose)
+ device_printf(sc->dev,
+ "Distance error, len=%d\n",
+ len);
+ goto error_code;
+ } else {
+ if (len > AW_IR_DMID) {
+ /* Decode */
+ code |= 1 << bitcount;
+ }
+ bitcount++;
+ if (bitcount == 32)
+ break; /* Finish decoding */
+ }
+ last = 1;
+ len = (val & PERIOD_MASK) + 1;
+ } else
+ len += (val & PERIOD_MASK) + 1;
+ }
+ }
+ return (code);
+
+error_code:
+
+ return (AW_IR_ERROR_CODE);
+}
+
+static int
+aw_ir_validate_code(unsigned long code)
+{
+ unsigned long v1, v2;
+
+ /* Don't check address */
+ v1 = code & CODE_MASK;
+ v2 = (code & INV_CODE_MASK) >> 8;
+
+ if (((v1 ^ v2) & VALID_CODE_MASK) == VALID_CODE_MASK)
+ return (0); /* valid */
+ else
+ return (1); /* invalid */
+}
+
+static void
+aw_ir_intr(void *arg)
+{
+ struct aw_ir_softc *sc;
+ uint32_t val;
+ int i, dcnt;
+ unsigned long ir_code;
+ int stat;
+
+ sc = (struct aw_ir_softc *)arg;
+
+ /* Read RX interrupt status */
+ val = READ(sc, AW_IR_RXSTA);
+ if (bootverbose)
+ device_printf(sc->dev, "RX interrupt status: %x\n", val);
+
+ /* Clean all pending interrupt statuses */
+ WRITE(sc, AW_IR_RXSTA, val | AW_IR_RXSTA_CLEARALL);
+
+ /* When Rx FIFO Data available or Packet end */
+ if (val & (AW_IR_RXINT_RAI_EN | AW_IR_RXINT_RPEI_EN)) {
+ if (bootverbose)
+ device_printf(sc->dev,
+ "RX FIFO Data available or Packet end\n");
+ /* Get available message count in RX FIFO */
+ dcnt = AW_IR_RXSTA_COUNTER(val);
+ /* Read FIFO */
+ for (i = 0; i < dcnt; i++) {
+ if (aw_ir_buf_full(sc)) {
+ if (bootverbose)
+ device_printf(sc->dev,
+ "raw buffer full\n");
+ break;
+ } else
+ aw_ir_buf_write(sc, aw_ir_read_data(sc));
+ }
+ }
+
+ if (val & AW_IR_RXINT_RPEI_EN) {
+ /* RX Packet end */
+ if (bootverbose)
+ device_printf(sc->dev, "RX Packet end\n");
+ ir_code = aw_ir_decode_packets(sc);
+ stat = aw_ir_validate_code(ir_code);
+ if (stat == 0) {
+ evdev_push_event(sc->sc_evdev,
+ EV_MSC, MSC_SCAN, ir_code);
+ evdev_sync(sc->sc_evdev);
+ }
+ if (bootverbose) {
+ device_printf(sc->dev, "Final IR code: %lx\n",
+ ir_code);
+ device_printf(sc->dev, "IR code status: %d\n",
+ stat);
+ }
+ aw_ir_buf_reset(sc);
+ }
+ if (val & AW_IR_RXINT_ROI_EN) {
+ /* RX FIFO overflow */
+ if (bootverbose)
+ device_printf(sc->dev, "RX FIFO overflow\n");
+ /* Flush raw buffer */
+ aw_ir_buf_reset(sc);
+ }
+}
+
+static int
+aw_ir_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner CIR controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+aw_ir_attach(device_t dev)
+{
+ struct aw_ir_softc *sc;
+ hwreset_t rst_apb;
+ clk_t clk_ir, clk_gate;
+ int err;
+ uint32_t val = 0;
+
+ clk_ir = clk_gate = NULL;
+ rst_apb = NULL;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ if (bus_alloc_resources(dev, aw_ir_spec, sc->res) != 0) {
+ device_printf(dev, "could not allocate memory resource\n");
+ return (ENXIO);
+ }
+
+ switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data) {
+ case A10_IR:
+ sc->fifo_size = 16;
+ break;
+ case A13_IR:
+ case A31_IR:
+ sc->fifo_size = 64;
+ break;
+ }
+
+ /* De-assert reset */
+ if (hwreset_get_by_ofw_idx(dev, 0, 0, &rst_apb) == 0) {
+ err = hwreset_deassert(rst_apb);
+ if (err != 0) {
+ device_printf(dev, "cannot de-assert reset\n");
+ goto error;
+ }
+ }
+
+ /* Reset buffer */
+ aw_ir_buf_reset(sc);
+
+ /* Get clocks and enable them */
+ err = clk_get_by_ofw_name(dev, 0, "apb", &clk_gate);
+ if (err != 0) {
+ device_printf(dev, "Cannot get gate clock\n");
+ goto error;
+ }
+ err = clk_get_by_ofw_name(dev, 0, "ir", &clk_ir);
+ if (err != 0) {
+ device_printf(dev, "Cannot get IR clock\n");
+ goto error;
+ }
+ /* Set clock rate */
+ err = clk_set_freq(clk_ir, AW_IR_BASE_CLK, 0);
+ if (err != 0) {
+ device_printf(dev, "cannot set IR clock rate\n");
+ goto error;
+ }
+ /* Enable clocks */
+ err = clk_enable(clk_gate);
+ if (err != 0) {
+ device_printf(dev, "Cannot enable clk gate\n");
+ goto error;
+ }
+ err = clk_enable(clk_ir);
+ if (err != 0) {
+ device_printf(dev, "Cannot enable IR clock\n");
+ goto error;
+ }
+
+ if (bus_setup_intr(dev, sc->res[1],
+ INTR_TYPE_MISC | INTR_MPSAFE, NULL, aw_ir_intr, sc,
+ &sc->intrhand)) {
+ bus_release_resources(dev, aw_ir_spec, sc->res);
+ device_printf(dev, "cannot setup interrupt handler\n");
+ err = ENXIO;
+ goto error;
+ }
+
+ /* Enable CIR Mode */
+ WRITE(sc, AW_IR_CTL, AW_IR_CTL_MD);
+
+ /*
+ * Set clock sample, filter, idle thresholds.
+ * Frequency sample = 3MHz/128 = 23437.5Hz (42.7us)
+ */
+ val = AW_IR_SAMPLE_128;
+ val |= (AW_IR_RXFILT_VAL | AW_IR_RXIDLE_VAL);
+ val |= (AW_IR_ACTIVE_T | AW_IR_ACTIVE_T_C);
+ WRITE(sc, AW_IR_CIR, val);
+
+ /* Invert Input Signal */
+ WRITE(sc, AW_IR_RXCTL, AW_IR_RXCTL_RPPI);
+
+ /* Clear All RX Interrupt Status */
+ WRITE(sc, AW_IR_RXSTA, AW_IR_RXSTA_CLEARALL);
+
+ /*
+ * Enable RX interrupt in case of overflow, packet end
+ * and FIFO available.
+ * RX FIFO Threshold = FIFO size / 2
+ */
+ WRITE(sc, AW_IR_RXINT, AW_IR_RXINT_ROI_EN | AW_IR_RXINT_RPEI_EN |
+ AW_IR_RXINT_RAI_EN | AW_IR_RXINT_RAL((sc->fifo_size >> 1) - 1));
+
+ /* Enable IR Module */
+ val = READ(sc, AW_IR_CTL);
+ WRITE(sc, AW_IR_CTL, val | AW_IR_CTL_GEN | AW_IR_CTL_RXEN);
+
+ sc->sc_evdev = evdev_alloc();
+ evdev_set_name(sc->sc_evdev, device_get_desc(sc->dev));
+ evdev_set_phys(sc->sc_evdev, device_get_nameunit(sc->dev));
+ evdev_set_id(sc->sc_evdev, BUS_HOST, 0, 0, 0);
+ evdev_support_event(sc->sc_evdev, EV_SYN);
+ evdev_support_event(sc->sc_evdev, EV_MSC);
+ evdev_support_msc(sc->sc_evdev, MSC_SCAN);
+
+ err = evdev_register(sc->sc_evdev);
+ if (err) {
+ device_printf(dev,
+ "failed to register evdev: error=%d\n", err);
+ goto error;
+ }
+
+ return (0);
+error:
+ if (clk_gate != NULL)
+ clk_release(clk_gate);
+ if (clk_ir != NULL)
+ clk_release(clk_ir);
+ if (rst_apb != NULL)
+ hwreset_release(rst_apb);
+ evdev_free(sc->sc_evdev);
+ sc->sc_evdev = NULL; /* Avoid double free */
+
+ bus_release_resources(dev, aw_ir_spec, sc->res);
+ return (ENXIO);
+}
+
+static device_method_t aw_ir_methods[] = {
+ DEVMETHOD(device_probe, aw_ir_probe),
+ DEVMETHOD(device_attach, aw_ir_attach),
+
+ DEVMETHOD_END
+};
+
+static driver_t aw_ir_driver = {
+ "aw_ir",
+ aw_ir_methods,
+ sizeof(struct aw_ir_softc),
+};
+static devclass_t aw_ir_devclass;
+
+DRIVER_MODULE(aw_ir, simplebus, aw_ir_driver, aw_ir_devclass, 0, 0);
+MODULE_DEPEND(aw_ir, evdev, 1, 1, 1);
diff --git a/sys/arm/allwinner/aw_dwc3.c b/sys/arm/allwinner/aw_dwc3.c
new file mode 100644
index 000000000000..6f15fe18d10e
--- /dev/null
+++ b/sys/arm/allwinner/aw_dwc3.c
@@ -0,0 +1,146 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2019 Emmanuel Vadot <manu@FreeBSD.Org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Rockchip DWC3 glue
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/gpio.h>
+#include <machine/bus.h>
+
+#include <dev/fdt/simplebus.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/ofw_subr.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/extres/phy/phy_usb.h>
+
+static struct ofw_compat_data compat_data[] = {
+ { "allwinner,sun50i-h6-dwc3", 1 },
+ { NULL, 0 }
+};
+
+struct aw_dwc3_softc {
+ struct simplebus_softc sc;
+ device_t dev;
+ clk_t clk_bus;
+ hwreset_t rst_bus;
+};
+
+static int
+aw_dwc3_probe(device_t dev)
+{
+ phandle_t node;
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ /* Binding says that we need a child node for the actual dwc3 controller */
+ node = ofw_bus_get_node(dev);
+ if (OF_child(node) <= 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner H6 DWC3");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+aw_dwc3_attach(device_t dev)
+{
+ struct aw_dwc3_softc *sc;
+ device_t cdev;
+ phandle_t node, child;
+ int err;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ node = ofw_bus_get_node(dev);
+
+ /* Enable the clocks */
+ if (clk_get_by_ofw_name(dev, 0, "bus", &sc->clk_bus) != 0) {
+ device_printf(dev, "Cannot get bus clock\n");
+ return (ENXIO);
+ }
+ err = clk_enable(sc->clk_bus);
+ if (err != 0) {
+ device_printf(dev, "Could not enable clock %s\n",
+ clk_get_name(sc->clk_bus));
+ return (ENXIO);
+ }
+
+ /* Put module out of reset */
+ if (hwreset_get_by_ofw_name(dev, node, "bus", &sc->rst_bus) == 0) {
+ if (hwreset_deassert(sc->rst_bus) != 0) {
+ device_printf(dev, "Cannot deassert reset\n");
+ return (ENXIO);
+ }
+ }
+
+ simplebus_init(dev, node);
+ if (simplebus_fill_ranges(node, &sc->sc) < 0) {
+ device_printf(dev, "could not get ranges\n");
+ return (ENXIO);
+ }
+
+ for (child = OF_child(node); child > 0; child = OF_peer(child)) {
+ cdev = simplebus_add_device(dev, child, 0, NULL, -1, NULL);
+ if (cdev != NULL)
+ device_probe_and_attach(cdev);
+ }
+
+ return (bus_generic_attach(dev));
+}
+
+static device_method_t aw_dwc3_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, aw_dwc3_probe),
+ DEVMETHOD(device_attach, aw_dwc3_attach),
+
+ DEVMETHOD_END
+};
+
+static devclass_t aw_dwc3_devclass;
+
+DEFINE_CLASS_1(aw_dwc3, aw_dwc3_driver, aw_dwc3_methods,
+ sizeof(struct aw_dwc3_softc), simplebus_driver);
+DRIVER_MODULE(aw_dwc3, simplebus, aw_dwc3_driver, aw_dwc3_devclass, 0, 0);
diff --git a/sys/arm/allwinner/aw_gmacclk.c b/sys/arm/allwinner/aw_gmacclk.c
new file mode 100644
index 000000000000..ba93ea1d4c4a
--- /dev/null
+++ b/sys/arm/allwinner/aw_gmacclk.c
@@ -0,0 +1,279 @@
+/*-
+ * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Allwinner GMAC clock
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/ofw_subr.h>
+
+#include <dev/extres/clk/clk_mux.h>
+#include <dev/extres/clk/clk_gate.h>
+
+#include "clkdev_if.h"
+
+#define GMAC_CLK_PIT (0x1 << 2)
+#define GMAC_CLK_PIT_SHIFT 2
+#define GMAC_CLK_PIT_MII 0
+#define GMAC_CLK_PIT_RGMII 1
+#define GMAC_CLK_SRC (0x3 << 0)
+#define GMAC_CLK_SRC_SHIFT 0
+#define GMAC_CLK_SRC_MII 0
+#define GMAC_CLK_SRC_EXT_RGMII 1
+#define GMAC_CLK_SRC_RGMII 2
+
+#define EMAC_TXC_DIV_CFG (1 << 15)
+#define EMAC_TXC_DIV_CFG_SHIFT 15
+#define EMAC_TXC_DIV_CFG_125MHZ 0
+#define EMAC_TXC_DIV_CFG_25MHZ 1
+#define EMAC_PHY_SELECT (1 << 16)
+#define EMAC_PHY_SELECT_SHIFT 16
+#define EMAC_PHY_SELECT_INT 0
+#define EMAC_PHY_SELECT_EXT 1
+#define EMAC_ETXDC (0x7 << 10)
+#define EMAC_ETXDC_SHIFT 10
+#define EMAC_ERXDC (0x1f << 5)
+#define EMAC_ERXDC_SHIFT 5
+
+#define CLK_IDX_MII 0
+#define CLK_IDX_RGMII 1
+#define CLK_IDX_COUNT 2
+
+static struct ofw_compat_data compat_data[] = {
+ { "allwinner,sun7i-a20-gmac-clk", 1 },
+ { NULL, 0 }
+};
+
+struct aw_gmacclk_sc {
+ device_t clkdev;
+ bus_addr_t reg;
+
+ int rx_delay;
+ int tx_delay;
+};
+
+#define GMACCLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val))
+#define GMACCLK_WRITE(sc, val) CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val))
+#define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev)
+#define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev)
+
+static int
+aw_gmacclk_init(struct clknode *clk, device_t dev)
+{
+ struct aw_gmacclk_sc *sc;
+ uint32_t val, index;
+
+ sc = clknode_get_softc(clk);
+
+ DEVICE_LOCK(sc);
+ GMACCLK_READ(sc, &val);
+ DEVICE_UNLOCK(sc);
+
+ switch ((val & GMAC_CLK_SRC) >> GMAC_CLK_SRC_SHIFT) {
+ case GMAC_CLK_SRC_MII:
+ index = CLK_IDX_MII;
+ break;
+ case GMAC_CLK_SRC_RGMII:
+ index = CLK_IDX_RGMII;
+ break;
+ default:
+ return (ENXIO);
+ }
+
+ clknode_init_parent_idx(clk, index);
+ return (0);
+}
+
+static int
+aw_gmacclk_set_mux(struct clknode *clk, int index)
+{
+ struct aw_gmacclk_sc *sc;
+ uint32_t val, clk_src, pit, txc_div;
+ int error;
+
+ sc = clknode_get_softc(clk);
+ error = 0;
+
+ switch (index) {
+ case CLK_IDX_MII:
+ clk_src = GMAC_CLK_SRC_MII;
+ pit = GMAC_CLK_PIT_MII;
+ txc_div = EMAC_TXC_DIV_CFG_25MHZ;
+ break;
+ case CLK_IDX_RGMII:
+ clk_src = GMAC_CLK_SRC_RGMII;
+ pit = GMAC_CLK_PIT_RGMII;
+ txc_div = EMAC_TXC_DIV_CFG_125MHZ;
+ break;
+ default:
+ return (ENXIO);
+ }
+
+ DEVICE_LOCK(sc);
+ GMACCLK_READ(sc, &val);
+ val &= ~(GMAC_CLK_SRC | GMAC_CLK_PIT);
+ val |= (clk_src << GMAC_CLK_SRC_SHIFT);
+ val |= (pit << GMAC_CLK_PIT_SHIFT);
+ GMACCLK_WRITE(sc, val);
+ DEVICE_UNLOCK(sc);
+
+ return (0);
+}
+
+static clknode_method_t aw_gmacclk_clknode_methods[] = {
+ /* Device interface */
+ CLKNODEMETHOD(clknode_init, aw_gmacclk_init),
+ CLKNODEMETHOD(clknode_set_mux, aw_gmacclk_set_mux),
+ CLKNODEMETHOD_END
+};
+DEFINE_CLASS_1(aw_gmacclk_clknode, aw_gmacclk_clknode_class,
+ aw_gmacclk_clknode_methods, sizeof(struct aw_gmacclk_sc), clknode_class);
+
+static int
+aw_gmacclk_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner GMAC Clock");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+aw_gmacclk_attach(device_t dev)
+{
+ struct clknode_init_def def;
+ struct aw_gmacclk_sc *sc;
+ struct clkdom *clkdom;
+ struct clknode *clk;
+ clk_t clk_parent;
+ bus_addr_t paddr;
+ bus_size_t psize;
+ phandle_t node;
+ int error, ncells, i;
+
+ node = ofw_bus_get_node(dev);
+
+ if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) {
+ device_printf(dev, "cannot parse 'reg' property\n");
+ return (ENXIO);
+ }
+
+ error = ofw_bus_parse_xref_list_get_length(node, "clocks",
+ "#clock-cells", &ncells);
+ if (error != 0 || ncells != CLK_IDX_COUNT) {
+ device_printf(dev, "couldn't find parent clocks\n");
+ return (ENXIO);
+ }
+
+ clkdom = clkdom_create(dev);
+
+ memset(&def, 0, sizeof(def));
+ error = clk_parse_ofw_clk_name(dev, node, &def.name);
+ if (error != 0) {
+ device_printf(dev, "cannot parse clock name\n");
+ error = ENXIO;
+ goto fail;
+ }
+ def.id = 1;
+ def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP, M_WAITOK);
+ for (i = 0; i < ncells; i++) {
+ error = clk_get_by_ofw_index(dev, 0, i, &clk_parent);
+ if (error != 0) {
+ device_printf(dev, "cannot get clock %d\n", error);
+ goto fail;
+ }
+ def.parent_names[i] = clk_get_name(clk_parent);
+ clk_release(clk_parent);
+ }
+ def.parent_cnt = ncells;
+
+ clk = clknode_create(clkdom, &aw_gmacclk_clknode_class, &def);
+ if (clk == NULL) {
+ device_printf(dev, "cannot create clknode\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ sc = clknode_get_softc(clk);
+ sc->reg = paddr;
+ sc->clkdev = device_get_parent(dev);
+ sc->tx_delay = sc->rx_delay = -1;
+ OF_getencprop(node, "tx-delay", &sc->tx_delay, sizeof(sc->tx_delay));
+ OF_getencprop(node, "rx-delay", &sc->rx_delay, sizeof(sc->rx_delay));
+
+ clknode_register(clkdom, clk);
+
+ if (clkdom_finit(clkdom) != 0) {
+ device_printf(dev, "cannot finalize clkdom initialization\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ if (bootverbose)
+ clkdom_dump(clkdom);
+
+ return (0);
+
+fail:
+ return (error);
+}
+
+static device_method_t aw_gmacclk_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, aw_gmacclk_probe),
+ DEVMETHOD(device_attach, aw_gmacclk_attach),
+
+ DEVMETHOD_END
+};
+
+static driver_t aw_gmacclk_driver = {
+ "aw_gmacclk",
+ aw_gmacclk_methods,
+ 0
+};
+
+static devclass_t aw_gmacclk_devclass;
+
+EARLY_DRIVER_MODULE(aw_gmacclk, simplebus, aw_gmacclk_driver,
+ aw_gmacclk_devclass, 0, 0, BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE);
diff --git a/sys/arm/allwinner/aw_gpio.c b/sys/arm/allwinner/aw_gpio.c
new file mode 100644
index 000000000000..c6be70aec493
--- /dev/null
+++ b/sys/arm/allwinner/aw_gpio.c
@@ -0,0 +1,1487 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Ganbold Tsagaankhuu <ganbold@freebsd.org>
+ * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * Copyright (c) 2012 Luiz Otavio O Souza.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/gpio.h>
+#include <sys/proc.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/intr.h>
+
+#include <dev/gpio/gpiobusvar.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/fdt/fdt_pinctrl.h>
+
+#include <arm/allwinner/aw_machdep.h>
+#include <arm/allwinner/allwinner_pinctrl.h>
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/extres/regulator/regulator.h>
+
+#if defined(__aarch64__)
+#include "opt_soc.h"
+#endif
+
+#include "pic_if.h"
+#include "gpio_if.h"
+
+#define AW_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \
+ GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN);
+
+#define AW_GPIO_INTR_CAPS (GPIO_INTR_LEVEL_LOW | GPIO_INTR_LEVEL_HIGH | \
+ GPIO_INTR_EDGE_RISING | GPIO_INTR_EDGE_FALLING | GPIO_INTR_EDGE_BOTH)
+
+#define AW_GPIO_NONE 0
+#define AW_GPIO_PULLUP 1
+#define AW_GPIO_PULLDOWN 2
+
+#define AW_GPIO_INPUT 0
+#define AW_GPIO_OUTPUT 1
+
+#define AW_GPIO_DRV_MASK 0x3
+#define AW_GPIO_PUD_MASK 0x3
+
+#define AW_PINCTRL 1
+#define AW_R_PINCTRL 2
+
+struct aw_gpio_conf {
+ struct allwinner_padconf *padconf;
+ const char *banks;
+};
+
+/* Defined in aw_padconf.c */
+#ifdef SOC_ALLWINNER_A10
+extern struct allwinner_padconf a10_padconf;
+struct aw_gpio_conf a10_gpio_conf = {
+ .padconf = &a10_padconf,
+ .banks = "abcdefghi",
+};
+#endif
+
+/* Defined in a13_padconf.c */
+#ifdef SOC_ALLWINNER_A13
+extern struct allwinner_padconf a13_padconf;
+struct aw_gpio_conf a13_gpio_conf = {
+ .padconf = &a13_padconf,
+ .banks = "bcdefg",
+};
+#endif
+
+/* Defined in a20_padconf.c */
+#ifdef SOC_ALLWINNER_A20
+extern struct allwinner_padconf a20_padconf;
+struct aw_gpio_conf a20_gpio_conf = {
+ .padconf = &a20_padconf,
+ .banks = "abcdefghi",
+};
+#endif
+
+/* Defined in a31_padconf.c */
+#ifdef SOC_ALLWINNER_A31
+extern struct allwinner_padconf a31_padconf;
+struct aw_gpio_conf a31_gpio_conf = {
+ .padconf = &a31_padconf,
+ .banks = "abcdefgh",
+};
+#endif
+
+/* Defined in a31s_padconf.c */
+#ifdef SOC_ALLWINNER_A31S
+extern struct allwinner_padconf a31s_padconf;
+struct aw_gpio_conf a31s_gpio_conf = {
+ .padconf = &a31s_padconf,
+ .banks = "abcdefgh",
+};
+#endif
+
+#if defined(SOC_ALLWINNER_A31) || defined(SOC_ALLWINNER_A31S)
+extern struct allwinner_padconf a31_r_padconf;
+struct aw_gpio_conf a31_r_gpio_conf = {
+ .padconf = &a31_r_padconf,
+ .banks = "lm",
+};
+#endif
+
+/* Defined in a33_padconf.c */
+#ifdef SOC_ALLWINNER_A33
+extern struct allwinner_padconf a33_padconf;
+struct aw_gpio_conf a33_gpio_conf = {
+ .padconf = &a33_padconf,
+ .banks = "bcdefgh",
+};
+#endif
+
+/* Defined in h3_padconf.c */
+#if defined(SOC_ALLWINNER_H3) || defined(SOC_ALLWINNER_H5)
+extern struct allwinner_padconf h3_padconf;
+extern struct allwinner_padconf h3_r_padconf;
+struct aw_gpio_conf h3_gpio_conf = {
+ .padconf = &h3_padconf,
+ .banks = "acdefg",
+};
+struct aw_gpio_conf h3_r_gpio_conf = {
+ .padconf = &h3_r_padconf,
+ .banks = "l",
+};
+#endif
+
+/* Defined in a83t_padconf.c */
+#ifdef SOC_ALLWINNER_A83T
+extern struct allwinner_padconf a83t_padconf;
+extern struct allwinner_padconf a83t_r_padconf;
+struct aw_gpio_conf a83t_gpio_conf = {
+ .padconf = &a83t_padconf,
+ .banks = "bcdefgh"
+};
+struct aw_gpio_conf a83t_r_gpio_conf = {
+ .padconf = &a83t_r_padconf,
+ .banks = "l",
+};
+#endif
+
+/* Defined in a64_padconf.c */
+#ifdef SOC_ALLWINNER_A64
+extern struct allwinner_padconf a64_padconf;
+extern struct allwinner_padconf a64_r_padconf;
+struct aw_gpio_conf a64_gpio_conf = {
+ .padconf = &a64_padconf,
+ .banks = "bcdefgh",
+};
+struct aw_gpio_conf a64_r_gpio_conf = {
+ .padconf = &a64_r_padconf,
+ .banks = "l",
+};
+#endif
+
+/* Defined in h6_padconf.c */
+#ifdef SOC_ALLWINNER_H6
+extern struct allwinner_padconf h6_padconf;
+extern struct allwinner_padconf h6_r_padconf;
+struct aw_gpio_conf h6_gpio_conf = {
+ .padconf = &h6_padconf,
+ .banks = "cdfgh",
+};
+struct aw_gpio_conf h6_r_gpio_conf = {
+ .padconf = &h6_r_padconf,
+ .banks = "lm",
+};
+#endif
+
+static struct ofw_compat_data compat_data[] = {
+#ifdef SOC_ALLWINNER_A10
+ {"allwinner,sun4i-a10-pinctrl", (uintptr_t)&a10_gpio_conf},
+#endif
+#ifdef SOC_ALLWINNER_A13
+ {"allwinner,sun5i-a13-pinctrl", (uintptr_t)&a13_gpio_conf},
+#endif
+#ifdef SOC_ALLWINNER_A20
+ {"allwinner,sun7i-a20-pinctrl", (uintptr_t)&a20_gpio_conf},
+#endif
+#ifdef SOC_ALLWINNER_A31
+ {"allwinner,sun6i-a31-pinctrl", (uintptr_t)&a31_gpio_conf},
+#endif
+#ifdef SOC_ALLWINNER_A31S
+ {"allwinner,sun6i-a31s-pinctrl", (uintptr_t)&a31s_gpio_conf},
+#endif
+#if defined(SOC_ALLWINNER_A31) || defined(SOC_ALLWINNER_A31S)
+ {"allwinner,sun6i-a31-r-pinctrl", (uintptr_t)&a31_r_gpio_conf},
+#endif
+#ifdef SOC_ALLWINNER_A33
+ {"allwinner,sun6i-a33-pinctrl", (uintptr_t)&a33_gpio_conf},
+#endif
+#ifdef SOC_ALLWINNER_A83T
+ {"allwinner,sun8i-a83t-pinctrl", (uintptr_t)&a83t_gpio_conf},
+ {"allwinner,sun8i-a83t-r-pinctrl", (uintptr_t)&a83t_r_gpio_conf},
+#endif
+#if defined(SOC_ALLWINNER_H3) || defined(SOC_ALLWINNER_H5)
+ {"allwinner,sun8i-h3-pinctrl", (uintptr_t)&h3_gpio_conf},
+ {"allwinner,sun50i-h5-pinctrl", (uintptr_t)&h3_gpio_conf},
+ {"allwinner,sun8i-h3-r-pinctrl", (uintptr_t)&h3_r_gpio_conf},
+#endif
+#ifdef SOC_ALLWINNER_A64
+ {"allwinner,sun50i-a64-pinctrl", (uintptr_t)&a64_gpio_conf},
+ {"allwinner,sun50i-a64-r-pinctrl", (uintptr_t)&a64_r_gpio_conf},
+#endif
+#ifdef SOC_ALLWINNER_H6
+ {"allwinner,sun50i-h6-pinctrl", (uintptr_t)&h6_gpio_conf},
+ {"allwinner,sun50i-h6-r-pinctrl", (uintptr_t)&h6_r_gpio_conf},
+#endif
+ {NULL, 0}
+};
+
+struct clk_list {
+ TAILQ_ENTRY(clk_list) next;
+ clk_t clk;
+};
+
+struct gpio_irqsrc {
+ struct intr_irqsrc isrc;
+ u_int irq;
+ uint32_t mode;
+ uint32_t pin;
+ uint32_t bank;
+ uint32_t intnum;
+ uint32_t intfunc;
+ uint32_t oldfunc;
+ bool enabled;
+};
+
+#define AW_GPIO_MEMRES 0
+#define AW_GPIO_IRQRES 1
+#define AW_GPIO_RESSZ 2
+
+struct aw_gpio_softc {
+ device_t sc_dev;
+ device_t sc_busdev;
+ struct resource * sc_res[AW_GPIO_RESSZ];
+ struct mtx sc_mtx;
+ struct resource * sc_mem_res;
+ struct resource * sc_irq_res;
+ void * sc_intrhand;
+ struct aw_gpio_conf *conf;
+ TAILQ_HEAD(, clk_list) clk_list;
+
+ struct gpio_irqsrc *gpio_pic_irqsrc;
+ int nirqs;
+};
+
+static struct resource_spec aw_gpio_res_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE },
+ { -1, 0, 0 }
+};
+
+#define AW_GPIO_LOCK(_sc) mtx_lock_spin(&(_sc)->sc_mtx)
+#define AW_GPIO_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->sc_mtx)
+#define AW_GPIO_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED)
+
+#define AW_GPIO_GP_CFG(_bank, _idx) 0x00 + ((_bank) * 0x24) + ((_idx) << 2)
+#define AW_GPIO_GP_DAT(_bank) 0x10 + ((_bank) * 0x24)
+#define AW_GPIO_GP_DRV(_bank, _idx) 0x14 + ((_bank) * 0x24) + ((_idx) << 2)
+#define AW_GPIO_GP_PUL(_bank, _idx) 0x1c + ((_bank) * 0x24) + ((_idx) << 2)
+
+#define AW_GPIO_GP_INT_BASE(_bank) (0x200 + 0x20 * _bank)
+
+#define AW_GPIO_GP_INT_CFG(_bank, _pin) (AW_GPIO_GP_INT_BASE(_bank) + (0x4 * ((_pin) / 8)))
+#define AW_GPIO_GP_INT_CTL(_bank) (AW_GPIO_GP_INT_BASE(_bank) + 0x10)
+#define AW_GPIO_GP_INT_STA(_bank) (AW_GPIO_GP_INT_BASE(_bank) + 0x14)
+#define AW_GPIO_GP_INT_DEB(_bank) (AW_GPIO_GP_INT_BASE(_bank) + 0x18)
+
+#define AW_GPIO_INT_EDGE_POSITIVE 0x0
+#define AW_GPIO_INT_EDGE_NEGATIVE 0x1
+#define AW_GPIO_INT_LEVEL_HIGH 0x2
+#define AW_GPIO_INT_LEVEL_LOW 0x3
+#define AW_GPIO_INT_EDGE_BOTH 0x4
+
+static char *aw_gpio_parse_function(phandle_t node);
+static const char **aw_gpio_parse_pins(phandle_t node, int *pins_nb);
+static uint32_t aw_gpio_parse_bias(phandle_t node);
+static int aw_gpio_parse_drive_strength(phandle_t node, uint32_t *drive);
+
+static int aw_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *value);
+static int aw_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value);
+static int aw_gpio_pin_get_locked(struct aw_gpio_softc *sc, uint32_t pin, unsigned int *value);
+static int aw_gpio_pin_set_locked(struct aw_gpio_softc *sc, uint32_t pin, unsigned int value);
+
+static void aw_gpio_intr(void *arg);
+static void aw_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc);
+static void aw_gpio_pic_disable_intr_locked(struct aw_gpio_softc *sc, struct intr_irqsrc *isrc);
+static void aw_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc);
+static int aw_gpio_register_isrcs(struct aw_gpio_softc *sc);
+
+#define AW_GPIO_WRITE(_sc, _off, _val) \
+ bus_write_4((_sc)->sc_res[AW_GPIO_MEMRES], _off, _val)
+#define AW_GPIO_READ(_sc, _off) \
+ bus_read_4((_sc)->sc_res[AW_GPIO_MEMRES], _off)
+
+static uint32_t
+aw_gpio_get_function(struct aw_gpio_softc *sc, uint32_t pin)
+{
+ uint32_t bank, func, offset;
+
+ /* Must be called with lock held. */
+ AW_GPIO_LOCK_ASSERT(sc);
+
+ if (pin > sc->conf->padconf->npins)
+ return (0);
+ bank = sc->conf->padconf->pins[pin].port;
+ pin = sc->conf->padconf->pins[pin].pin;
+ offset = ((pin & 0x07) << 2);
+
+ func = AW_GPIO_READ(sc, AW_GPIO_GP_CFG(bank, pin >> 3));
+
+ return ((func >> offset) & 0x7);
+}
+
+static int
+aw_gpio_set_function(struct aw_gpio_softc *sc, uint32_t pin, uint32_t f)
+{
+ uint32_t bank, data, offset;
+
+ /* Check if the function exists in the padconf data */
+ if (sc->conf->padconf->pins[pin].functions[f] == NULL)
+ return (EINVAL);
+
+ /* Must be called with lock held. */
+ AW_GPIO_LOCK_ASSERT(sc);
+
+ bank = sc->conf->padconf->pins[pin].port;
+ pin = sc->conf->padconf->pins[pin].pin;
+ offset = ((pin & 0x07) << 2);
+
+ data = AW_GPIO_READ(sc, AW_GPIO_GP_CFG(bank, pin >> 3));
+ data &= ~(7 << offset);
+ data |= (f << offset);
+ AW_GPIO_WRITE(sc, AW_GPIO_GP_CFG(bank, pin >> 3), data);
+
+ return (0);
+}
+
+static uint32_t
+aw_gpio_get_pud(struct aw_gpio_softc *sc, uint32_t pin)
+{
+ uint32_t bank, offset, val;
+
+ /* Must be called with lock held. */
+ AW_GPIO_LOCK_ASSERT(sc);
+
+ bank = sc->conf->padconf->pins[pin].port;
+ pin = sc->conf->padconf->pins[pin].pin;
+ offset = ((pin & 0x0f) << 1);
+
+ val = AW_GPIO_READ(sc, AW_GPIO_GP_PUL(bank, pin >> 4));
+
+ return ((val >> offset) & AW_GPIO_PUD_MASK);
+}
+
+static void
+aw_gpio_set_pud(struct aw_gpio_softc *sc, uint32_t pin, uint32_t state)
+{
+ uint32_t bank, offset, val;
+
+ if (aw_gpio_get_pud(sc, pin) == state)
+ return;
+
+ /* Must be called with lock held. */
+ AW_GPIO_LOCK_ASSERT(sc);
+
+ bank = sc->conf->padconf->pins[pin].port;
+ pin = sc->conf->padconf->pins[pin].pin;
+ offset = ((pin & 0x0f) << 1);
+
+ val = AW_GPIO_READ(sc, AW_GPIO_GP_PUL(bank, pin >> 4));
+ val &= ~(AW_GPIO_PUD_MASK << offset);
+ val |= (state << offset);
+ AW_GPIO_WRITE(sc, AW_GPIO_GP_PUL(bank, pin >> 4), val);
+}
+
+static uint32_t
+aw_gpio_get_drv(struct aw_gpio_softc *sc, uint32_t pin)
+{
+ uint32_t bank, offset, val;
+
+ /* Must be called with lock held. */
+ AW_GPIO_LOCK_ASSERT(sc);
+
+ bank = sc->conf->padconf->pins[pin].port;
+ pin = sc->conf->padconf->pins[pin].pin;
+ offset = ((pin & 0x0f) << 1);
+
+ val = AW_GPIO_READ(sc, AW_GPIO_GP_DRV(bank, pin >> 4));
+
+ return ((val >> offset) & AW_GPIO_DRV_MASK);
+}
+
+static void
+aw_gpio_set_drv(struct aw_gpio_softc *sc, uint32_t pin, uint32_t drive)
+{
+ uint32_t bank, offset, val;
+
+ if (aw_gpio_get_drv(sc, pin) == drive)
+ return;
+
+ /* Must be called with lock held. */
+ AW_GPIO_LOCK_ASSERT(sc);
+
+ bank = sc->conf->padconf->pins[pin].port;
+ pin = sc->conf->padconf->pins[pin].pin;
+ offset = ((pin & 0x0f) << 1);
+
+ val = AW_GPIO_READ(sc, AW_GPIO_GP_DRV(bank, pin >> 4));
+ val &= ~(AW_GPIO_DRV_MASK << offset);
+ val |= (drive << offset);
+ AW_GPIO_WRITE(sc, AW_GPIO_GP_DRV(bank, pin >> 4), val);
+}
+
+static int
+aw_gpio_pin_configure(struct aw_gpio_softc *sc, uint32_t pin, uint32_t flags)
+{
+ u_int val;
+ int err = 0;
+
+ /* Must be called with lock held. */
+ AW_GPIO_LOCK_ASSERT(sc);
+
+ if (pin > sc->conf->padconf->npins)
+ return (EINVAL);
+
+ /* Manage input/output. */
+ if (flags & GPIO_PIN_INPUT) {
+ err = aw_gpio_set_function(sc, pin, AW_GPIO_INPUT);
+ } else if ((flags & GPIO_PIN_OUTPUT) &&
+ aw_gpio_get_function(sc, pin) != AW_GPIO_OUTPUT) {
+ if (flags & GPIO_PIN_PRESET_LOW) {
+ aw_gpio_pin_set_locked(sc, pin, 0);
+ } else if (flags & GPIO_PIN_PRESET_HIGH) {
+ aw_gpio_pin_set_locked(sc, pin, 1);
+ } else {
+ /* Read the pin and preset output to current state. */
+ err = aw_gpio_set_function(sc, pin, AW_GPIO_INPUT);
+ if (err == 0) {
+ aw_gpio_pin_get_locked(sc, pin, &val);
+ aw_gpio_pin_set_locked(sc, pin, val);
+ }
+ }
+ if (err == 0)
+ err = aw_gpio_set_function(sc, pin, AW_GPIO_OUTPUT);
+ }
+
+ if (err)
+ return (err);
+
+ /* Manage Pull-up/pull-down. */
+ if (flags & GPIO_PIN_PULLUP)
+ aw_gpio_set_pud(sc, pin, AW_GPIO_PULLUP);
+ else if (flags & GPIO_PIN_PULLDOWN)
+ aw_gpio_set_pud(sc, pin, AW_GPIO_PULLDOWN);
+ else
+ aw_gpio_set_pud(sc, pin, AW_GPIO_NONE);
+
+ return (0);
+}
+
+static device_t
+aw_gpio_get_bus(device_t dev)
+{
+ struct aw_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ return (sc->sc_busdev);
+}
+
+static int
+aw_gpio_pin_max(device_t dev, int *maxpin)
+{
+ struct aw_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ *maxpin = sc->conf->padconf->npins - 1;
+ return (0);
+}
+
+static int
+aw_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
+{
+ struct aw_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->conf->padconf->npins)
+ return (EINVAL);
+
+ *caps = AW_GPIO_DEFAULT_CAPS;
+ if (sc->conf->padconf->pins[pin].eint_func != 0)
+ *caps |= AW_GPIO_INTR_CAPS;
+
+ return (0);
+}
+
+static int
+aw_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
+{
+ struct aw_gpio_softc *sc;
+ uint32_t func;
+ uint32_t pud;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->conf->padconf->npins)
+ return (EINVAL);
+
+ AW_GPIO_LOCK(sc);
+ func = aw_gpio_get_function(sc, pin);
+ switch (func) {
+ case AW_GPIO_INPUT:
+ *flags = GPIO_PIN_INPUT;
+ break;
+ case AW_GPIO_OUTPUT:
+ *flags = GPIO_PIN_OUTPUT;
+ break;
+ default:
+ *flags = 0;
+ break;
+ }
+
+ pud = aw_gpio_get_pud(sc, pin);
+ switch (pud) {
+ case AW_GPIO_PULLDOWN:
+ *flags |= GPIO_PIN_PULLDOWN;
+ break;
+ case AW_GPIO_PULLUP:
+ *flags |= GPIO_PIN_PULLUP;
+ break;
+ default:
+ break;
+ }
+
+ AW_GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+aw_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
+{
+ struct aw_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->conf->padconf->npins)
+ return (EINVAL);
+
+ snprintf(name, GPIOMAXNAME - 1, "%s",
+ sc->conf->padconf->pins[pin].name);
+ name[GPIOMAXNAME - 1] = '\0';
+
+ return (0);
+}
+
+static int
+aw_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
+{
+ struct aw_gpio_softc *sc;
+ int err;
+
+ sc = device_get_softc(dev);
+ if (pin > sc->conf->padconf->npins)
+ return (EINVAL);
+
+ AW_GPIO_LOCK(sc);
+ err = aw_gpio_pin_configure(sc, pin, flags);
+ AW_GPIO_UNLOCK(sc);
+
+ return (err);
+}
+
+static int
+aw_gpio_pin_set_locked(struct aw_gpio_softc *sc, uint32_t pin,
+ unsigned int value)
+{
+ uint32_t bank, data;
+
+ AW_GPIO_LOCK_ASSERT(sc);
+
+ if (pin > sc->conf->padconf->npins)
+ return (EINVAL);
+
+ bank = sc->conf->padconf->pins[pin].port;
+ pin = sc->conf->padconf->pins[pin].pin;
+
+ data = AW_GPIO_READ(sc, AW_GPIO_GP_DAT(bank));
+ if (value)
+ data |= (1 << pin);
+ else
+ data &= ~(1 << pin);
+ AW_GPIO_WRITE(sc, AW_GPIO_GP_DAT(bank), data);
+
+ return (0);
+}
+
+static int
+aw_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
+{
+ struct aw_gpio_softc *sc;
+ int ret;
+
+ sc = device_get_softc(dev);
+
+ AW_GPIO_LOCK(sc);
+ ret = aw_gpio_pin_set_locked(sc, pin, value);
+ AW_GPIO_UNLOCK(sc);
+
+ return (ret);
+}
+
+static int
+aw_gpio_pin_get_locked(struct aw_gpio_softc *sc,uint32_t pin,
+ unsigned int *val)
+{
+ uint32_t bank, reg_data;
+
+ AW_GPIO_LOCK_ASSERT(sc);
+
+ if (pin > sc->conf->padconf->npins)
+ return (EINVAL);
+
+ bank = sc->conf->padconf->pins[pin].port;
+ pin = sc->conf->padconf->pins[pin].pin;
+
+ reg_data = AW_GPIO_READ(sc, AW_GPIO_GP_DAT(bank));
+ *val = (reg_data & (1 << pin)) ? 1 : 0;
+
+ return (0);
+}
+
+static char *
+aw_gpio_parse_function(phandle_t node)
+{
+ char *function;
+
+ if (OF_getprop_alloc(node, "function",
+ (void **)&function) != -1)
+ return (function);
+ if (OF_getprop_alloc(node, "allwinner,function",
+ (void **)&function) != -1)
+ return (function);
+
+ return (NULL);
+}
+
+static const char **
+aw_gpio_parse_pins(phandle_t node, int *pins_nb)
+{
+ const char **pinlist;
+
+ *pins_nb = ofw_bus_string_list_to_array(node, "pins", &pinlist);
+ if (*pins_nb > 0)
+ return (pinlist);
+
+ *pins_nb = ofw_bus_string_list_to_array(node, "allwinner,pins",
+ &pinlist);
+ if (*pins_nb > 0)
+ return (pinlist);
+
+ return (NULL);
+}
+
+static uint32_t
+aw_gpio_parse_bias(phandle_t node)
+{
+ uint32_t bias;
+
+ if (OF_getencprop(node, "pull", &bias, sizeof(bias)) != -1)
+ return (bias);
+ if (OF_getencprop(node, "allwinner,pull", &bias, sizeof(bias)) != -1)
+ return (bias);
+ if (OF_hasprop(node, "bias-disable"))
+ return (AW_GPIO_NONE);
+ if (OF_hasprop(node, "bias-pull-up"))
+ return (AW_GPIO_PULLUP);
+ if (OF_hasprop(node, "bias-pull-down"))
+ return (AW_GPIO_PULLDOWN);
+
+ return (AW_GPIO_NONE);
+}
+
+static int
+aw_gpio_parse_drive_strength(phandle_t node, uint32_t *drive)
+{
+ uint32_t drive_str;
+
+ if (OF_getencprop(node, "drive", drive, sizeof(*drive)) != -1)
+ return (0);
+ if (OF_getencprop(node, "allwinner,drive", drive, sizeof(*drive)) != -1)
+ return (0);
+ if (OF_getencprop(node, "drive-strength", &drive_str,
+ sizeof(drive_str)) != -1) {
+ *drive = (drive_str / 10) - 1;
+ return (0);
+ }
+
+ return (1);
+}
+
+static int
+aw_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
+{
+ struct aw_gpio_softc *sc;
+ int ret;
+
+ sc = device_get_softc(dev);
+
+ AW_GPIO_LOCK(sc);
+ ret = aw_gpio_pin_get_locked(sc, pin, val);
+ AW_GPIO_UNLOCK(sc);
+
+ return (ret);
+}
+
+static int
+aw_gpio_pin_toggle(device_t dev, uint32_t pin)
+{
+ struct aw_gpio_softc *sc;
+ uint32_t bank, data;
+
+ sc = device_get_softc(dev);
+ if (pin > sc->conf->padconf->npins)
+ return (EINVAL);
+
+ bank = sc->conf->padconf->pins[pin].port;
+ pin = sc->conf->padconf->pins[pin].pin;
+
+ AW_GPIO_LOCK(sc);
+ data = AW_GPIO_READ(sc, AW_GPIO_GP_DAT(bank));
+ if (data & (1 << pin))
+ data &= ~(1 << pin);
+ else
+ data |= (1 << pin);
+ AW_GPIO_WRITE(sc, AW_GPIO_GP_DAT(bank), data);
+ AW_GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+aw_gpio_pin_access_32(device_t dev, uint32_t first_pin, uint32_t clear_pins,
+ uint32_t change_pins, uint32_t *orig_pins)
+{
+ struct aw_gpio_softc *sc;
+ uint32_t bank, data, pin;
+
+ sc = device_get_softc(dev);
+ if (first_pin > sc->conf->padconf->npins)
+ return (EINVAL);
+
+ /*
+ * We require that first_pin refers to the first pin in a bank, because
+ * this API is not about convenience, it's for making a set of pins
+ * change simultaneously (required) with reasonably high performance
+ * (desired); we need to do a read-modify-write on a single register.
+ */
+ bank = sc->conf->padconf->pins[first_pin].port;
+ pin = sc->conf->padconf->pins[first_pin].pin;
+ if (pin != 0)
+ return (EINVAL);
+
+ AW_GPIO_LOCK(sc);
+ data = AW_GPIO_READ(sc, AW_GPIO_GP_DAT(bank));
+ if ((clear_pins | change_pins) != 0)
+ AW_GPIO_WRITE(sc, AW_GPIO_GP_DAT(bank),
+ (data & ~clear_pins) ^ change_pins);
+ AW_GPIO_UNLOCK(sc);
+
+ if (orig_pins != NULL)
+ *orig_pins = data;
+
+ return (0);
+}
+
+static int
+aw_gpio_pin_config_32(device_t dev, uint32_t first_pin, uint32_t num_pins,
+ uint32_t *pin_flags)
+{
+ struct aw_gpio_softc *sc;
+ uint32_t bank, pin;
+ int err;
+
+ sc = device_get_softc(dev);
+ if (first_pin > sc->conf->padconf->npins)
+ return (EINVAL);
+
+ bank = sc->conf->padconf->pins[first_pin].port;
+ if (sc->conf->padconf->pins[first_pin].pin != 0)
+ return (EINVAL);
+
+ /*
+ * The configuration for a bank of pins is scattered among several
+ * registers; we cannot g'tee to simultaneously change the state of all
+ * the pins in the flags array. So just loop through the array
+ * configuring each pin for now. If there was a strong need, it might
+ * be possible to support some limited simultaneous config, such as
+ * adjacent groups of 8 pins that line up the same as the config regs.
+ */
+ for (err = 0, pin = first_pin; err == 0 && pin < num_pins; ++pin) {
+ if (pin_flags[pin] & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT))
+ err = aw_gpio_pin_configure(sc, pin, pin_flags[pin]);
+ }
+
+ return (err);
+}
+
+static int
+aw_gpio_map_gpios(device_t bus, phandle_t dev, phandle_t gparent, int gcells,
+ pcell_t *gpios, uint32_t *pin, uint32_t *flags)
+{
+ struct aw_gpio_softc *sc;
+ int i;
+
+ sc = device_get_softc(bus);
+
+ /* The GPIO pins are mapped as: <gpio-phandle bank pin flags>. */
+ for (i = 0; i < sc->conf->padconf->npins; i++)
+ if (sc->conf->padconf->pins[i].port == gpios[0] &&
+ sc->conf->padconf->pins[i].pin == gpios[1]) {
+ *pin = i;
+ break;
+ }
+ *flags = gpios[gcells - 1];
+
+ return (0);
+}
+
+static int
+aw_find_pinnum_by_name(struct aw_gpio_softc *sc, const char *pinname)
+{
+ int i;
+
+ for (i = 0; i < sc->conf->padconf->npins; i++)
+ if (!strcmp(pinname, sc->conf->padconf->pins[i].name))
+ return i;
+
+ return (-1);
+}
+
+static int
+aw_find_pin_func(struct aw_gpio_softc *sc, int pin, const char *func)
+{
+ int i;
+
+ for (i = 0; i < AW_MAX_FUNC_BY_PIN; i++)
+ if (sc->conf->padconf->pins[pin].functions[i] &&
+ !strcmp(func, sc->conf->padconf->pins[pin].functions[i]))
+ return (i);
+
+ return (-1);
+}
+
+static int
+aw_fdt_configure_pins(device_t dev, phandle_t cfgxref)
+{
+ struct aw_gpio_softc *sc;
+ phandle_t node;
+ const char **pinlist = NULL;
+ char *pin_function = NULL;
+ uint32_t pin_drive, pin_pull;
+ int pins_nb, pin_num, pin_func, i, ret;
+ bool set_drive;
+
+ sc = device_get_softc(dev);
+ node = OF_node_from_xref(cfgxref);
+ ret = 0;
+ set_drive = false;
+
+ /* Getting all prop for configuring pins */
+ pinlist = aw_gpio_parse_pins(node, &pins_nb);
+ if (pinlist == NULL)
+ return (ENOENT);
+
+ pin_function = aw_gpio_parse_function(node);
+ if (pin_function == NULL) {
+ ret = ENOENT;
+ goto out;
+ }
+
+ if (aw_gpio_parse_drive_strength(node, &pin_drive) == 0)
+ set_drive = true;
+
+ pin_pull = aw_gpio_parse_bias(node);
+
+ /* Configure each pin to the correct function, drive and pull */
+ for (i = 0; i < pins_nb; i++) {
+ pin_num = aw_find_pinnum_by_name(sc, pinlist[i]);
+ if (pin_num == -1) {
+ ret = ENOENT;
+ goto out;
+ }
+ pin_func = aw_find_pin_func(sc, pin_num, pin_function);
+ if (pin_func == -1) {
+ ret = ENOENT;
+ goto out;
+ }
+
+ AW_GPIO_LOCK(sc);
+
+ if (aw_gpio_get_function(sc, pin_num) != pin_func)
+ aw_gpio_set_function(sc, pin_num, pin_func);
+ if (set_drive)
+ aw_gpio_set_drv(sc, pin_num, pin_drive);
+ if (pin_pull != AW_GPIO_NONE)
+ aw_gpio_set_pud(sc, pin_num, pin_pull);
+
+ AW_GPIO_UNLOCK(sc);
+ }
+
+ out:
+ OF_prop_free(pinlist);
+ OF_prop_free(pin_function);
+ return (ret);
+}
+
+static void
+aw_gpio_enable_bank_supply(void *arg)
+{
+ struct aw_gpio_softc *sc = arg;
+ regulator_t vcc_supply;
+ char bank_reg_name[16];
+ int i, nbanks;
+
+ nbanks = strlen(sc->conf->banks);
+ for (i = 0; i < nbanks; i++) {
+ snprintf(bank_reg_name, sizeof(bank_reg_name), "vcc-p%c-supply",
+ sc->conf->banks[i]);
+
+ if (regulator_get_by_ofw_property(sc->sc_dev, 0, bank_reg_name, &vcc_supply) == 0) {
+ if (bootverbose)
+ device_printf(sc->sc_dev,
+ "Enabling regulator for gpio bank %c\n",
+ sc->conf->banks[i]);
+ if (regulator_enable(vcc_supply) != 0) {
+ device_printf(sc->sc_dev,
+ "Cannot enable regulator for bank %c\n",
+ sc->conf->banks[i]);
+ }
+ }
+ }
+}
+
+static int
+aw_gpio_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner GPIO/Pinmux controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+aw_gpio_attach(device_t dev)
+{
+ int error;
+ phandle_t gpio;
+ struct aw_gpio_softc *sc;
+ struct clk_list *clkp, *clkp_tmp;
+ clk_t clk;
+ hwreset_t rst = NULL;
+ int off, err, clkret;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+
+ mtx_init(&sc->sc_mtx, "aw gpio", "gpio", MTX_SPIN);
+
+ if (bus_alloc_resources(dev, aw_gpio_res_spec, sc->sc_res) != 0) {
+ device_printf(dev, "cannot allocate device resources\n");
+ return (ENXIO);
+ }
+
+ if (bus_setup_intr(dev, sc->sc_res[AW_GPIO_IRQRES],
+ INTR_TYPE_CLK | INTR_MPSAFE, NULL, aw_gpio_intr, sc,
+ &sc->sc_intrhand)) {
+ device_printf(dev, "cannot setup interrupt handler\n");
+ goto fail;
+ }
+
+ /* Find our node. */
+ gpio = ofw_bus_get_node(sc->sc_dev);
+ if (!OF_hasprop(gpio, "gpio-controller"))
+ /* Node is not a GPIO controller. */
+ goto fail;
+
+ /* Use the right pin data for the current SoC */
+ sc->conf = (struct aw_gpio_conf *)ofw_bus_search_compatible(dev,
+ compat_data)->ocd_data;
+
+ if (hwreset_get_by_ofw_idx(dev, 0, 0, &rst) == 0) {
+ error = hwreset_deassert(rst);
+ if (error != 0) {
+ device_printf(dev, "cannot de-assert reset\n");
+ goto fail;
+ }
+ }
+
+ TAILQ_INIT(&sc->clk_list);
+ for (off = 0, clkret = 0; clkret == 0; off++) {
+ clkret = clk_get_by_ofw_index(dev, 0, off, &clk);
+ if (clkret != 0)
+ break;
+ err = clk_enable(clk);
+ if (err != 0) {
+ device_printf(dev, "Could not enable clock %s\n",
+ clk_get_name(clk));
+ goto fail;
+ }
+ clkp = malloc(sizeof(*clkp), M_DEVBUF, M_WAITOK | M_ZERO);
+ clkp->clk = clk;
+ TAILQ_INSERT_TAIL(&sc->clk_list, clkp, next);
+ }
+ if (clkret != 0 && clkret != ENOENT) {
+ device_printf(dev, "Could not find clock at offset %d (%d)\n",
+ off, clkret);
+ goto fail;
+ }
+
+ aw_gpio_register_isrcs(sc);
+ intr_pic_register(dev, OF_xref_from_node(ofw_bus_get_node(dev)));
+
+ sc->sc_busdev = gpiobus_attach_bus(dev);
+ if (sc->sc_busdev == NULL)
+ goto fail;
+
+ /*
+ * Register as a pinctrl device
+ */
+ fdt_pinctrl_register(dev, "pins");
+ fdt_pinctrl_configure_tree(dev);
+ fdt_pinctrl_register(dev, "allwinner,pins");
+ fdt_pinctrl_configure_tree(dev);
+
+ config_intrhook_oneshot(aw_gpio_enable_bank_supply, sc);
+
+ return (0);
+
+fail:
+ if (sc->sc_irq_res)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
+ if (sc->sc_mem_res)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+ mtx_destroy(&sc->sc_mtx);
+
+ /* Disable clock */
+ TAILQ_FOREACH_SAFE(clkp, &sc->clk_list, next, clkp_tmp) {
+ err = clk_disable(clkp->clk);
+ if (err != 0)
+ device_printf(dev, "Could not disable clock %s\n",
+ clk_get_name(clkp->clk));
+ err = clk_release(clkp->clk);
+ if (err != 0)
+ device_printf(dev, "Could not release clock %s\n",
+ clk_get_name(clkp->clk));
+ TAILQ_REMOVE(&sc->clk_list, clkp, next);
+ free(clkp, M_DEVBUF);
+ }
+
+ /* Assert resets */
+ if (rst) {
+ hwreset_assert(rst);
+ hwreset_release(rst);
+ }
+
+ return (ENXIO);
+}
+
+static int
+aw_gpio_detach(device_t dev)
+{
+
+ return (EBUSY);
+}
+
+static void
+aw_gpio_intr(void *arg)
+{
+ struct aw_gpio_softc *sc;
+ struct intr_irqsrc *isrc;
+ uint32_t reg;
+ int irq;
+
+ sc = (struct aw_gpio_softc *)arg;
+
+ AW_GPIO_LOCK(sc);
+ for (irq = 0; irq < sc->nirqs; irq++) {
+ if (!sc->gpio_pic_irqsrc[irq].enabled)
+ continue;
+
+ reg = AW_GPIO_READ(sc, AW_GPIO_GP_INT_STA(sc->gpio_pic_irqsrc[irq].bank));
+ if (!(reg & (1 << sc->gpio_pic_irqsrc[irq].intnum)))
+ continue;
+
+ isrc = &sc->gpio_pic_irqsrc[irq].isrc;
+ if (intr_isrc_dispatch(isrc, curthread->td_intr_frame) != 0) {
+ aw_gpio_pic_disable_intr_locked(sc, isrc);
+ aw_gpio_pic_post_filter(sc->sc_dev, isrc);
+ device_printf(sc->sc_dev, "Stray irq %u disabled\n", irq);
+ }
+ }
+ AW_GPIO_UNLOCK(sc);
+}
+
+/*
+ * Interrupts support
+ */
+
+static int
+aw_gpio_register_isrcs(struct aw_gpio_softc *sc)
+{
+ const char *name;
+ int nirqs;
+ int pin;
+ int err;
+
+ name = device_get_nameunit(sc->sc_dev);
+
+ for (nirqs = 0, pin = 0; pin < sc->conf->padconf->npins; pin++) {
+ if (sc->conf->padconf->pins[pin].eint_func == 0)
+ continue;
+
+ nirqs++;
+ }
+
+ sc->gpio_pic_irqsrc = malloc(sizeof(*sc->gpio_pic_irqsrc) * nirqs,
+ M_DEVBUF, M_WAITOK | M_ZERO);
+ for (nirqs = 0, pin = 0; pin < sc->conf->padconf->npins; pin++) {
+ if (sc->conf->padconf->pins[pin].eint_func == 0)
+ continue;
+
+ sc->gpio_pic_irqsrc[nirqs].pin = pin;
+ sc->gpio_pic_irqsrc[nirqs].bank = sc->conf->padconf->pins[pin].eint_bank;
+ sc->gpio_pic_irqsrc[nirqs].intnum = sc->conf->padconf->pins[pin].eint_num;
+ sc->gpio_pic_irqsrc[nirqs].intfunc = sc->conf->padconf->pins[pin].eint_func;
+ sc->gpio_pic_irqsrc[nirqs].irq = nirqs;
+ sc->gpio_pic_irqsrc[nirqs].mode = GPIO_INTR_CONFORM;
+
+ err = intr_isrc_register(&sc->gpio_pic_irqsrc[nirqs].isrc,
+ sc->sc_dev, 0, "%s,%s", name,
+ sc->conf->padconf->pins[pin].functions[sc->conf->padconf->pins[pin].eint_func]);
+ if (err) {
+ device_printf(sc->sc_dev, "intr_isrs_register failed for irq %d\n", nirqs);
+ }
+
+ nirqs++;
+ }
+
+ sc->nirqs = nirqs;
+
+ return (0);
+}
+
+static void
+aw_gpio_pic_disable_intr_locked(struct aw_gpio_softc *sc, struct intr_irqsrc *isrc)
+{
+ u_int irq;
+ uint32_t reg;
+
+ AW_GPIO_LOCK_ASSERT(sc);
+ irq = ((struct gpio_irqsrc *)isrc)->irq;
+ reg = AW_GPIO_READ(sc, AW_GPIO_GP_INT_CTL(sc->gpio_pic_irqsrc[irq].bank));
+ reg &= ~(1 << sc->gpio_pic_irqsrc[irq].intnum);
+ AW_GPIO_WRITE(sc, AW_GPIO_GP_INT_CTL(sc->gpio_pic_irqsrc[irq].bank), reg);
+
+ sc->gpio_pic_irqsrc[irq].enabled = false;
+}
+
+static void
+aw_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct aw_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ AW_GPIO_LOCK(sc);
+ aw_gpio_pic_disable_intr_locked(sc, isrc);
+ AW_GPIO_UNLOCK(sc);
+}
+
+static void
+aw_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct aw_gpio_softc *sc;
+ u_int irq;
+ uint32_t reg;
+
+ sc = device_get_softc(dev);
+ irq = ((struct gpio_irqsrc *)isrc)->irq;
+ AW_GPIO_LOCK(sc);
+ reg = AW_GPIO_READ(sc, AW_GPIO_GP_INT_CTL(sc->gpio_pic_irqsrc[irq].bank));
+ reg |= 1 << sc->gpio_pic_irqsrc[irq].intnum;
+ AW_GPIO_WRITE(sc, AW_GPIO_GP_INT_CTL(sc->gpio_pic_irqsrc[irq].bank), reg);
+ AW_GPIO_UNLOCK(sc);
+
+ sc->gpio_pic_irqsrc[irq].enabled = true;
+}
+
+static int
+aw_gpio_pic_map_gpio(struct aw_gpio_softc *sc, struct intr_map_data_gpio *dag,
+ u_int *irqp, u_int *mode)
+{
+ u_int irq;
+ int pin;
+
+ irq = dag->gpio_pin_num;
+
+ for (pin = 0; pin < sc->nirqs; pin++)
+ if (sc->gpio_pic_irqsrc[pin].pin == irq)
+ break;
+ if (pin == sc->nirqs) {
+ device_printf(sc->sc_dev, "Invalid interrupt number %u\n", irq);
+ return (EINVAL);
+ }
+
+ switch (dag->gpio_intr_mode) {
+ case GPIO_INTR_LEVEL_LOW:
+ case GPIO_INTR_LEVEL_HIGH:
+ case GPIO_INTR_EDGE_RISING:
+ case GPIO_INTR_EDGE_FALLING:
+ case GPIO_INTR_EDGE_BOTH:
+ break;
+ default:
+ device_printf(sc->sc_dev, "Unsupported interrupt mode 0x%8x\n",
+ dag->gpio_intr_mode);
+ return (EINVAL);
+ }
+
+ *irqp = pin;
+ if (mode != NULL)
+ *mode = dag->gpio_intr_mode;
+
+ return (0);
+}
+
+static int
+aw_gpio_pic_map_intr(device_t dev, struct intr_map_data *data,
+ struct intr_irqsrc **isrcp)
+{
+ struct aw_gpio_softc *sc;
+ u_int irq;
+ int err;
+
+ sc = device_get_softc(dev);
+ switch (data->type) {
+ case INTR_MAP_DATA_GPIO:
+ err = aw_gpio_pic_map_gpio(sc,
+ (struct intr_map_data_gpio *)data,
+ &irq, NULL);
+ break;
+ default:
+ return (ENOTSUP);
+ };
+
+ if (err == 0)
+ *isrcp = &sc->gpio_pic_irqsrc[irq].isrc;
+ return (0);
+}
+
+static int
+aw_gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ struct aw_gpio_softc *sc;
+ struct gpio_irqsrc *gi;
+ uint32_t irqcfg;
+ uint32_t pinidx, reg;
+ u_int irq, mode;
+ int err;
+
+ sc = device_get_softc(dev);
+ gi = (struct gpio_irqsrc *)isrc;
+
+ switch (data->type) {
+ case INTR_MAP_DATA_GPIO:
+ err = aw_gpio_pic_map_gpio(sc,
+ (struct intr_map_data_gpio *)data,
+ &irq, &mode);
+ break;
+ default:
+ return (ENOTSUP);
+ };
+
+ pinidx = (sc->gpio_pic_irqsrc[irq].intnum % 8) * 4;
+
+ AW_GPIO_LOCK(sc);
+ switch (mode) {
+ case GPIO_INTR_LEVEL_LOW:
+ irqcfg = AW_GPIO_INT_LEVEL_LOW << pinidx;
+ break;
+ case GPIO_INTR_LEVEL_HIGH:
+ irqcfg = AW_GPIO_INT_LEVEL_HIGH << pinidx;
+ break;
+ case GPIO_INTR_EDGE_RISING:
+ irqcfg = AW_GPIO_INT_EDGE_POSITIVE << pinidx;
+ break;
+ case GPIO_INTR_EDGE_FALLING:
+ irqcfg = AW_GPIO_INT_EDGE_NEGATIVE << pinidx;
+ break;
+ case GPIO_INTR_EDGE_BOTH:
+ irqcfg = AW_GPIO_INT_EDGE_BOTH << pinidx;
+ break;
+ }
+
+ /* Switch the pin to interrupt mode */
+ sc->gpio_pic_irqsrc[irq].oldfunc = aw_gpio_get_function(sc,
+ sc->gpio_pic_irqsrc[irq].pin);
+ aw_gpio_set_function(sc, sc->gpio_pic_irqsrc[irq].pin,
+ sc->gpio_pic_irqsrc[irq].intfunc);
+
+ /* Write interrupt mode */
+ reg = AW_GPIO_READ(sc,
+ AW_GPIO_GP_INT_CFG(sc->gpio_pic_irqsrc[irq].bank,
+ sc->gpio_pic_irqsrc[irq].intnum));
+ reg &= ~(0xF << pinidx);
+ reg |= irqcfg;
+ AW_GPIO_WRITE(sc,
+ AW_GPIO_GP_INT_CFG(sc->gpio_pic_irqsrc[irq].bank,
+ sc->gpio_pic_irqsrc[irq].intnum),
+ reg);
+
+ AW_GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+aw_gpio_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ struct aw_gpio_softc *sc;
+ struct gpio_irqsrc *gi;
+
+ sc = device_get_softc(dev);
+ gi = (struct gpio_irqsrc *)isrc;
+
+ /* Switch back the pin to it's original function */
+ AW_GPIO_LOCK(sc);
+ aw_gpio_set_function(sc, gi->pin, gi->oldfunc);
+ AW_GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static void
+aw_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct aw_gpio_softc *sc;
+ struct gpio_irqsrc *gi;
+
+ sc = device_get_softc(dev);
+ gi = (struct gpio_irqsrc *)isrc;
+
+ arm_irq_memory_barrier(0);
+ AW_GPIO_WRITE(sc, AW_GPIO_GP_INT_STA(gi->bank), 1 << gi->intnum);
+}
+
+static void
+aw_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct aw_gpio_softc *sc;
+ struct gpio_irqsrc *gi;
+
+ sc = device_get_softc(dev);
+ gi = (struct gpio_irqsrc *)isrc;
+
+ arm_irq_memory_barrier(0);
+ AW_GPIO_WRITE(sc, AW_GPIO_GP_INT_STA(gi->bank), 1 << gi->intnum);
+ aw_gpio_pic_enable_intr(dev, isrc);
+}
+
+static void
+aw_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct aw_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+ aw_gpio_pic_disable_intr_locked(sc, isrc);
+}
+
+/*
+ * OFWBUS Interface
+ */
+static phandle_t
+aw_gpio_get_node(device_t dev, device_t bus)
+{
+
+ /* We only have one child, the GPIO bus, which needs our own node. */
+ return (ofw_bus_get_node(dev));
+}
+
+static device_method_t aw_gpio_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, aw_gpio_probe),
+ DEVMETHOD(device_attach, aw_gpio_attach),
+ DEVMETHOD(device_detach, aw_gpio_detach),
+
+ /* Interrupt controller interface */
+ DEVMETHOD(pic_disable_intr, aw_gpio_pic_disable_intr),
+ DEVMETHOD(pic_enable_intr, aw_gpio_pic_enable_intr),
+ DEVMETHOD(pic_map_intr, aw_gpio_pic_map_intr),
+ DEVMETHOD(pic_setup_intr, aw_gpio_pic_setup_intr),
+ DEVMETHOD(pic_teardown_intr, aw_gpio_pic_teardown_intr),
+ DEVMETHOD(pic_post_filter, aw_gpio_pic_post_filter),
+ DEVMETHOD(pic_post_ithread, aw_gpio_pic_post_ithread),
+ DEVMETHOD(pic_pre_ithread, aw_gpio_pic_pre_ithread),
+
+ /* GPIO protocol */
+ DEVMETHOD(gpio_get_bus, aw_gpio_get_bus),
+ DEVMETHOD(gpio_pin_max, aw_gpio_pin_max),
+ DEVMETHOD(gpio_pin_getname, aw_gpio_pin_getname),
+ DEVMETHOD(gpio_pin_getflags, aw_gpio_pin_getflags),
+ DEVMETHOD(gpio_pin_getcaps, aw_gpio_pin_getcaps),
+ DEVMETHOD(gpio_pin_setflags, aw_gpio_pin_setflags),
+ DEVMETHOD(gpio_pin_get, aw_gpio_pin_get),
+ DEVMETHOD(gpio_pin_set, aw_gpio_pin_set),
+ DEVMETHOD(gpio_pin_toggle, aw_gpio_pin_toggle),
+ DEVMETHOD(gpio_pin_access_32, aw_gpio_pin_access_32),
+ DEVMETHOD(gpio_pin_config_32, aw_gpio_pin_config_32),
+ DEVMETHOD(gpio_map_gpios, aw_gpio_map_gpios),
+
+ /* ofw_bus interface */
+ DEVMETHOD(ofw_bus_get_node, aw_gpio_get_node),
+
+ /* fdt_pinctrl interface */
+ DEVMETHOD(fdt_pinctrl_configure,aw_fdt_configure_pins),
+
+ DEVMETHOD_END
+};
+
+static devclass_t aw_gpio_devclass;
+
+static driver_t aw_gpio_driver = {
+ "gpio",
+ aw_gpio_methods,
+ sizeof(struct aw_gpio_softc),
+};
+
+EARLY_DRIVER_MODULE(aw_gpio, simplebus, aw_gpio_driver, aw_gpio_devclass, 0, 0,
+ BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);
diff --git a/sys/arm/allwinner/aw_if_dwc.c b/sys/arm/allwinner/aw_if_dwc.c
new file mode 100644
index 000000000000..fa1742bfeaf3
--- /dev/null
+++ b/sys/arm/allwinner/aw_if_dwc.c
@@ -0,0 +1,145 @@
+/*-
+ * Copyright (c) 2015 Luiz Otavio O Souza <loos@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+
+#include <machine/bus.h>
+
+#include <dev/dwc/if_dwc.h>
+#include <dev/dwc/if_dwcvar.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/allwinner/aw_machdep.h>
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/regulator/regulator.h>
+
+#include "if_dwc_if.h"
+
+static int
+a20_if_dwc_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+ if (!ofw_bus_is_compatible(dev, "allwinner,sun7i-a20-gmac"))
+ return (ENXIO);
+ device_set_desc(dev, "A20 Gigabit Ethernet Controller");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+a20_if_dwc_init(device_t dev)
+{
+ const char *tx_parent_name;
+ char *phy_type;
+ clk_t clk_tx, clk_tx_parent;
+ regulator_t reg;
+ phandle_t node;
+ int error;
+
+ node = ofw_bus_get_node(dev);
+
+ /* Configure PHY for MII or RGMII mode */
+ if (OF_getprop_alloc(node, "phy-mode", (void **)&phy_type)) {
+ error = clk_get_by_ofw_name(dev, 0, "allwinner_gmac_tx", &clk_tx);
+ if (error != 0) {
+ device_printf(dev, "could not get tx clk\n");
+ return (error);
+ }
+
+ if (strcmp(phy_type, "rgmii") == 0)
+ tx_parent_name = "gmac_int_tx";
+ else
+ tx_parent_name = "mii_phy_tx";
+
+ error = clk_get_by_name(dev, tx_parent_name, &clk_tx_parent);
+ if (error != 0) {
+ device_printf(dev, "could not get clock '%s'\n",
+ tx_parent_name);
+ return (error);
+ }
+
+ error = clk_set_parent_by_clk(clk_tx, clk_tx_parent);
+ if (error != 0) {
+ device_printf(dev, "could not set tx clk parent\n");
+ return (error);
+ }
+ }
+
+ /* Enable PHY regulator if applicable */
+ if (regulator_get_by_ofw_property(dev, 0, "phy-supply", &reg) == 0) {
+ error = regulator_enable(reg);
+ if (error != 0) {
+ device_printf(dev, "could not enable PHY regulator\n");
+ return (error);
+ }
+ }
+
+ return (0);
+}
+
+static int
+a20_if_dwc_mac_type(device_t dev)
+{
+
+ return (DWC_GMAC_NORMAL_DESC);
+}
+
+static int
+a20_if_dwc_mii_clk(device_t dev)
+{
+
+ return (GMAC_MII_CLK_150_250M_DIV102);
+}
+
+static device_method_t a20_dwc_methods[] = {
+ DEVMETHOD(device_probe, a20_if_dwc_probe),
+
+ DEVMETHOD(if_dwc_init, a20_if_dwc_init),
+ DEVMETHOD(if_dwc_mac_type, a20_if_dwc_mac_type),
+ DEVMETHOD(if_dwc_mii_clk, a20_if_dwc_mii_clk),
+
+ DEVMETHOD_END
+};
+
+static devclass_t a20_dwc_devclass;
+
+extern driver_t dwc_driver;
+
+DEFINE_CLASS_1(dwc, a20_dwc_driver, a20_dwc_methods, sizeof(struct dwc_softc),
+ dwc_driver);
+DRIVER_MODULE(a20_dwc, simplebus, a20_dwc_driver, a20_dwc_devclass, 0, 0);
+
+MODULE_DEPEND(a20_dwc, dwc, 1, 1, 1);
diff --git a/sys/arm/allwinner/aw_machdep.c b/sys/arm/allwinner/aw_machdep.c
new file mode 100644
index 000000000000..688012d5f14c
--- /dev/null
+++ b/sys/arm/allwinner/aw_machdep.c
@@ -0,0 +1,304 @@
+/*-
+ * Copyright (c) 2012 Ganbold Tsagaankhuu <ganbold@freebsd.org>
+ * Copyright (c) 2015-2016 Emmanuel Vadot <manu@freebsd.org>
+ * All rights reserved.
+ *
+ * This code is derived from software written for Brini by Mark Brinicombe
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+
+ * from: FreeBSD: //depot/projects/arm/src/sys/arm/ti/ti_machdep.c
+ */
+
+#include "opt_ddb.h"
+#include "opt_platform.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/devmap.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/bus.h>
+#include <machine/machdep.h>
+#include <machine/platformvar.h>
+
+#include <arm/allwinner/aw_mp.h>
+#include <arm/allwinner/aw_wdog.h>
+#include <arm/allwinner/aw_machdep.h>
+
+#include "platform_if.h"
+
+static platform_attach_t a10_attach;
+static platform_attach_t a13_attach;
+static platform_attach_t a20_attach;
+static platform_attach_t a31_attach;
+static platform_attach_t a31s_attach;
+static platform_attach_t a83t_attach;
+static platform_attach_t h3_attach;
+static platform_devmap_init_t allwinner_devmap_init;
+static platform_cpu_reset_t allwinner_cpu_reset;
+
+static u_int soc_type;
+static u_int soc_family;
+
+static int
+a10_attach(platform_t plat)
+{
+ soc_type = ALLWINNERSOC_A10;
+ soc_family = ALLWINNERSOC_SUN4I;
+ return (0);
+}
+
+static int
+a13_attach(platform_t plat)
+{
+ soc_type = ALLWINNERSOC_A13;
+ soc_family = ALLWINNERSOC_SUN5I;
+ return (0);
+}
+
+static int
+a20_attach(platform_t plat)
+{
+ soc_type = ALLWINNERSOC_A20;
+ soc_family = ALLWINNERSOC_SUN7I;
+
+ return (0);
+}
+
+static int
+a31_attach(platform_t plat)
+{
+ soc_type = ALLWINNERSOC_A31;
+ soc_family = ALLWINNERSOC_SUN6I;
+
+ return (0);
+}
+
+static int
+a31s_attach(platform_t plat)
+{
+ soc_type = ALLWINNERSOC_A31S;
+ soc_family = ALLWINNERSOC_SUN6I;
+
+ return (0);
+}
+
+static int
+a33_attach(platform_t plat)
+{
+ soc_type = ALLWINNERSOC_A33;
+ soc_family = ALLWINNERSOC_SUN8I;
+
+ return (0);
+}
+
+static int
+a83t_attach(platform_t plat)
+{
+ soc_type = ALLWINNERSOC_A83T;
+ soc_family = ALLWINNERSOC_SUN8I;
+
+ return (0);
+}
+
+static int
+h3_attach(platform_t plat)
+{
+ soc_type = ALLWINNERSOC_H3;
+ soc_family = ALLWINNERSOC_SUN8I;
+
+ return (0);
+}
+
+/*
+ * Set up static device mappings.
+ *
+ * This covers all the on-chip device with 1MB section mappings, which is good
+ * for performance (uses fewer TLB entries for device access).
+ *
+ * XXX It also covers a block of SRAM and some GPU (mali400) stuff that maybe
+ * shouldn't be device-mapped. The original code mapped a 4MB block, but
+ * perhaps a 1MB block would be more appropriate.
+ */
+static int
+allwinner_devmap_init(platform_t plat)
+{
+
+ devmap_add_entry(0x01C00000, 0x00400000); /* 4MB */
+
+ return (0);
+}
+
+static void
+allwinner_cpu_reset(platform_t plat)
+{
+ aw_wdog_watchdog_reset();
+ printf("Reset failed!\n");
+ while (1);
+}
+
+#if defined(SOC_ALLWINNER_A10)
+static platform_method_t a10_methods[] = {
+ PLATFORMMETHOD(platform_attach, a10_attach),
+ PLATFORMMETHOD(platform_devmap_init, allwinner_devmap_init),
+ PLATFORMMETHOD(platform_cpu_reset, allwinner_cpu_reset),
+
+ PLATFORMMETHOD_END,
+};
+FDT_PLATFORM_DEF(a10, "a10", 0, "allwinner,sun4i-a10", 200);
+#endif
+
+#if defined(SOC_ALLWINNER_A13)
+static platform_method_t a13_methods[] = {
+ PLATFORMMETHOD(platform_attach, a13_attach),
+ PLATFORMMETHOD(platform_devmap_init, allwinner_devmap_init),
+ PLATFORMMETHOD(platform_cpu_reset, allwinner_cpu_reset),
+
+ PLATFORMMETHOD_END,
+};
+FDT_PLATFORM_DEF(a13, "a13", 0, "allwinner,sun5i-a13", 200);
+#endif
+
+#if defined(SOC_ALLWINNER_A20)
+static platform_method_t a20_methods[] = {
+ PLATFORMMETHOD(platform_attach, a20_attach),
+ PLATFORMMETHOD(platform_devmap_init, allwinner_devmap_init),
+ PLATFORMMETHOD(platform_cpu_reset, allwinner_cpu_reset),
+
+#ifdef SMP
+ PLATFORMMETHOD(platform_mp_start_ap, aw_mp_start_ap),
+ PLATFORMMETHOD(platform_mp_setmaxid, aw_mp_setmaxid),
+#endif
+ PLATFORMMETHOD_END,
+};
+FDT_PLATFORM_DEF(a20, "a20", 0, "allwinner,sun7i-a20", 200);
+#endif
+
+#if defined(SOC_ALLWINNER_A31)
+static platform_method_t a31_methods[] = {
+ PLATFORMMETHOD(platform_attach, a31_attach),
+ PLATFORMMETHOD(platform_devmap_init, allwinner_devmap_init),
+ PLATFORMMETHOD(platform_cpu_reset, allwinner_cpu_reset),
+
+#ifdef SMP
+ PLATFORMMETHOD(platform_mp_start_ap, aw_mp_start_ap),
+ PLATFORMMETHOD(platform_mp_setmaxid, aw_mp_setmaxid),
+#endif
+ PLATFORMMETHOD_END,
+};
+FDT_PLATFORM_DEF(a31, "a31", 0, "allwinner,sun6i-a31", 200);
+#endif
+
+#if defined(SOC_ALLWINNER_A31S)
+static platform_method_t a31s_methods[] = {
+ PLATFORMMETHOD(platform_attach, a31s_attach),
+ PLATFORMMETHOD(platform_devmap_init, allwinner_devmap_init),
+ PLATFORMMETHOD(platform_cpu_reset, allwinner_cpu_reset),
+
+#ifdef SMP
+ PLATFORMMETHOD(platform_mp_start_ap, aw_mp_start_ap),
+ PLATFORMMETHOD(platform_mp_setmaxid, aw_mp_setmaxid),
+#endif
+ PLATFORMMETHOD_END,
+};
+FDT_PLATFORM_DEF(a31s, "a31s", 0, "allwinner,sun6i-a31s", 200);
+#endif
+
+#if defined(SOC_ALLWINNER_A33)
+static platform_method_t a33_methods[] = {
+ PLATFORMMETHOD(platform_attach, a33_attach),
+ PLATFORMMETHOD(platform_devmap_init, allwinner_devmap_init),
+ PLATFORMMETHOD(platform_cpu_reset, allwinner_cpu_reset),
+
+#ifdef SMP
+ PLATFORMMETHOD(platform_mp_start_ap, aw_mp_start_ap),
+ PLATFORMMETHOD(platform_mp_setmaxid, aw_mp_setmaxid),
+#endif
+ PLATFORMMETHOD_END,
+};
+FDT_PLATFORM_DEF(a33, "a33", 0, "allwinner,sun8i-a33", 200);
+#endif
+
+#if defined(SOC_ALLWINNER_A83T)
+static platform_method_t a83t_methods[] = {
+ PLATFORMMETHOD(platform_attach, a83t_attach),
+ PLATFORMMETHOD(platform_devmap_init, allwinner_devmap_init),
+ PLATFORMMETHOD(platform_cpu_reset, allwinner_cpu_reset),
+
+#ifdef SMP
+ PLATFORMMETHOD(platform_mp_start_ap, a83t_mp_start_ap),
+ PLATFORMMETHOD(platform_mp_setmaxid, aw_mp_setmaxid),
+#endif
+ PLATFORMMETHOD_END,
+};
+FDT_PLATFORM_DEF(a83t, "a83t", 0, "allwinner,sun8i-a83t", 200);
+#endif
+
+#if defined(SOC_ALLWINNER_H2PLUS)
+static platform_method_t h2_plus_methods[] = {
+ PLATFORMMETHOD(platform_attach, h3_attach),
+ PLATFORMMETHOD(platform_devmap_init, allwinner_devmap_init),
+ PLATFORMMETHOD(platform_cpu_reset, allwinner_cpu_reset),
+
+#ifdef SMP
+ PLATFORMMETHOD(platform_mp_start_ap, aw_mp_start_ap),
+ PLATFORMMETHOD(platform_mp_setmaxid, aw_mp_setmaxid),
+#endif
+ PLATFORMMETHOD_END,
+};
+FDT_PLATFORM_DEF(h2_plus, "h2_plus", 0, "allwinner,sun8i-h2-plus", 200);
+#endif
+
+#if defined(SOC_ALLWINNER_H3)
+static platform_method_t h3_methods[] = {
+ PLATFORMMETHOD(platform_attach, h3_attach),
+ PLATFORMMETHOD(platform_devmap_init, allwinner_devmap_init),
+ PLATFORMMETHOD(platform_cpu_reset, allwinner_cpu_reset),
+
+#ifdef SMP
+ PLATFORMMETHOD(platform_mp_start_ap, aw_mp_start_ap),
+ PLATFORMMETHOD(platform_mp_setmaxid, aw_mp_setmaxid),
+#endif
+ PLATFORMMETHOD_END,
+};
+FDT_PLATFORM_DEF(h3, "h3", 0, "allwinner,sun8i-h3", 200);
+#endif
+
+u_int
+allwinner_soc_type(void)
+{
+ return (soc_type);
+}
+
+u_int
+allwinner_soc_family(void)
+{
+ return (soc_family);
+}
diff --git a/sys/arm/allwinner/aw_machdep.h b/sys/arm/allwinner/aw_machdep.h
new file mode 100644
index 000000000000..11e116ddc6a4
--- /dev/null
+++ b/sys/arm/allwinner/aw_machdep.h
@@ -0,0 +1,51 @@
+/*-
+ * Copyright (c) 2015 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#ifndef AW_MACHDEP_H
+#define AW_MACHDEP_H
+
+#define ALLWINNERSOC_A10 0x10000000
+#define ALLWINNERSOC_A13 0x13000000
+#define ALLWINNERSOC_A10S 0x10000001
+#define ALLWINNERSOC_A20 0x20000000
+#define ALLWINNERSOC_H3 0x30000000
+#define ALLWINNERSOC_A31 0x31000000
+#define ALLWINNERSOC_A31S 0x31000001
+#define ALLWINNERSOC_A33 0x33000000
+#define ALLWINNERSOC_A83T 0x83000000
+
+#define ALLWINNERSOC_SUN4I 0x40000000
+#define ALLWINNERSOC_SUN5I 0x50000000
+#define ALLWINNERSOC_SUN6I 0x60000000
+#define ALLWINNERSOC_SUN7I 0x70000000
+#define ALLWINNERSOC_SUN8I 0x80000000
+
+u_int allwinner_soc_type(void);
+u_int allwinner_soc_family(void);
+
+#endif /* AW_MACHDEP_H */
diff --git a/sys/arm/allwinner/aw_mmc.c b/sys/arm/allwinner/aw_mmc.c
new file mode 100644
index 000000000000..1c28a882bf46
--- /dev/null
+++ b/sys/arm/allwinner/aw_mmc.c
@@ -0,0 +1,1593 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2018 Emmanuel Vadot <manu@FreeBSD.org>
+ * Copyright (c) 2013 Alexander Fedorov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+#include <sys/queue.h>
+#include <sys/taskqueue.h>
+
+#include <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/mmc/bridge.h>
+#include <dev/mmc/mmcbrvar.h>
+#include <dev/mmc/mmc_fdt_helpers.h>
+
+#include <arm/allwinner/aw_mmc.h>
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/extres/regulator/regulator.h>
+
+#include "opt_mmccam.h"
+
+#ifdef MMCCAM
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_debug.h>
+#include <cam/cam_sim.h>
+#include <cam/cam_xpt_sim.h>
+#endif
+
+#define AW_MMC_MEMRES 0
+#define AW_MMC_IRQRES 1
+#define AW_MMC_RESSZ 2
+#define AW_MMC_DMA_SEGS (PAGE_SIZE / sizeof(struct aw_mmc_dma_desc))
+#define AW_MMC_DMA_DESC_SIZE (sizeof(struct aw_mmc_dma_desc) * AW_MMC_DMA_SEGS)
+#define AW_MMC_DMA_FTRGLEVEL 0x20070008
+
+#define AW_MMC_RESET_RETRY 1000
+
+#define CARD_ID_FREQUENCY 400000
+
+struct aw_mmc_conf {
+ uint32_t dma_xferlen;
+ bool mask_data0;
+ bool can_calibrate;
+ bool new_timing;
+};
+
+static const struct aw_mmc_conf a10_mmc_conf = {
+ .dma_xferlen = 0x2000,
+};
+
+static const struct aw_mmc_conf a13_mmc_conf = {
+ .dma_xferlen = 0x10000,
+};
+
+static const struct aw_mmc_conf a64_mmc_conf = {
+ .dma_xferlen = 0x10000,
+ .mask_data0 = true,
+ .can_calibrate = true,
+ .new_timing = true,
+};
+
+static const struct aw_mmc_conf a64_emmc_conf = {
+ .dma_xferlen = 0x2000,
+ .can_calibrate = true,
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"allwinner,sun4i-a10-mmc", (uintptr_t)&a10_mmc_conf},
+ {"allwinner,sun5i-a13-mmc", (uintptr_t)&a13_mmc_conf},
+ {"allwinner,sun7i-a20-mmc", (uintptr_t)&a13_mmc_conf},
+ {"allwinner,sun50i-a64-mmc", (uintptr_t)&a64_mmc_conf},
+ {"allwinner,sun50i-a64-emmc", (uintptr_t)&a64_emmc_conf},
+ {NULL, 0}
+};
+
+struct aw_mmc_softc {
+ device_t aw_dev;
+ clk_t aw_clk_ahb;
+ clk_t aw_clk_mmc;
+ hwreset_t aw_rst_ahb;
+ int aw_bus_busy;
+ int aw_resid;
+ int aw_timeout;
+ struct callout aw_timeoutc;
+ struct mmc_host aw_host;
+ struct mmc_fdt_helper mmc_helper;
+#ifdef MMCCAM
+ union ccb * ccb;
+ struct cam_devq * devq;
+ struct cam_sim * sim;
+ struct mtx sim_mtx;
+#else
+ struct mmc_request * aw_req;
+#endif
+ struct mtx aw_mtx;
+ struct resource * aw_res[AW_MMC_RESSZ];
+ struct aw_mmc_conf * aw_mmc_conf;
+ uint32_t aw_intr;
+ uint32_t aw_intr_wait;
+ void * aw_intrhand;
+ unsigned int aw_clock;
+ device_t child;
+
+ /* Fields required for DMA access. */
+ bus_addr_t aw_dma_desc_phys;
+ bus_dmamap_t aw_dma_map;
+ bus_dma_tag_t aw_dma_tag;
+ void * aw_dma_desc;
+ bus_dmamap_t aw_dma_buf_map;
+ bus_dma_tag_t aw_dma_buf_tag;
+ int aw_dma_map_err;
+};
+
+static struct resource_spec aw_mmc_res_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE },
+ { -1, 0, 0 }
+};
+
+static int aw_mmc_probe(device_t);
+static int aw_mmc_attach(device_t);
+static int aw_mmc_detach(device_t);
+static int aw_mmc_setup_dma(struct aw_mmc_softc *);
+static void aw_mmc_teardown_dma(struct aw_mmc_softc *sc);
+static int aw_mmc_reset(struct aw_mmc_softc *);
+static int aw_mmc_init(struct aw_mmc_softc *);
+static void aw_mmc_intr(void *);
+static int aw_mmc_update_clock(struct aw_mmc_softc *, uint32_t);
+static void aw_mmc_helper_cd_handler(device_t, bool);
+
+static void aw_mmc_print_error(uint32_t);
+static int aw_mmc_update_ios(device_t, device_t);
+static int aw_mmc_request(device_t, device_t, struct mmc_request *);
+static int aw_mmc_get_ro(device_t, device_t);
+static int aw_mmc_acquire_host(device_t, device_t);
+static int aw_mmc_release_host(device_t, device_t);
+#ifdef MMCCAM
+static void aw_mmc_cam_action(struct cam_sim *, union ccb *);
+static void aw_mmc_cam_poll(struct cam_sim *);
+static int aw_mmc_cam_settran_settings(struct aw_mmc_softc *, union ccb *);
+static int aw_mmc_cam_request(struct aw_mmc_softc *, union ccb *);
+static void aw_mmc_cam_handle_mmcio(struct cam_sim *, union ccb *);
+#endif
+
+#define AW_MMC_LOCK(_sc) mtx_lock(&(_sc)->aw_mtx)
+#define AW_MMC_UNLOCK(_sc) mtx_unlock(&(_sc)->aw_mtx)
+#define AW_MMC_READ_4(_sc, _reg) \
+ bus_read_4((_sc)->aw_res[AW_MMC_MEMRES], _reg)
+#define AW_MMC_WRITE_4(_sc, _reg, _value) \
+ bus_write_4((_sc)->aw_res[AW_MMC_MEMRES], _reg, _value)
+
+SYSCTL_NODE(_hw, OID_AUTO, aw_mmc, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
+ "aw_mmc driver");
+
+static int aw_mmc_debug = 0;
+SYSCTL_INT(_hw_aw_mmc, OID_AUTO, debug, CTLFLAG_RWTUN, &aw_mmc_debug, 0,
+ "Debug level bit0=card changes bit1=ios changes, bit2=interrupts, bit3=commands");
+#define AW_MMC_DEBUG_CARD 0x1
+#define AW_MMC_DEBUG_IOS 0x2
+#define AW_MMC_DEBUG_INT 0x4
+#define AW_MMC_DEBUG_CMD 0x8
+
+#ifdef MMCCAM
+static void
+aw_mmc_cam_handle_mmcio(struct cam_sim *sim, union ccb *ccb)
+{
+ struct aw_mmc_softc *sc;
+
+ sc = cam_sim_softc(sim);
+
+ aw_mmc_cam_request(sc, ccb);
+}
+
+static void
+aw_mmc_cam_action(struct cam_sim *sim, union ccb *ccb)
+{
+ struct aw_mmc_softc *sc;
+
+ sc = cam_sim_softc(sim);
+ if (sc == NULL) {
+ ccb->ccb_h.status = CAM_SEL_TIMEOUT;
+ xpt_done(ccb);
+ return;
+ }
+
+ mtx_assert(&sc->sim_mtx, MA_OWNED);
+
+ switch (ccb->ccb_h.func_code) {
+ case XPT_PATH_INQ:
+ mmc_path_inq(&ccb->cpi, "Deglitch Networks", sim,
+ (sc->aw_mmc_conf->dma_xferlen * AW_MMC_DMA_SEGS) /
+ MMC_SECTOR_SIZE);
+ break;
+
+ case XPT_GET_TRAN_SETTINGS:
+ {
+ struct ccb_trans_settings *cts = &ccb->cts;
+
+ if (__predict_false(aw_mmc_debug & AW_MMC_DEBUG_IOS))
+ device_printf(sc->aw_dev, "Got XPT_GET_TRAN_SETTINGS\n");
+
+ cts->protocol = PROTO_MMCSD;
+ cts->protocol_version = 1;
+ cts->transport = XPORT_MMCSD;
+ cts->transport_version = 1;
+ cts->xport_specific.valid = 0;
+ cts->proto_specific.mmc.host_ocr = sc->aw_host.host_ocr;
+ cts->proto_specific.mmc.host_f_min = sc->aw_host.f_min;
+ cts->proto_specific.mmc.host_f_max = sc->aw_host.f_max;
+ cts->proto_specific.mmc.host_caps = sc->aw_host.caps;
+ cts->proto_specific.mmc.host_max_data = (sc->aw_mmc_conf->dma_xferlen *
+ AW_MMC_DMA_SEGS) / MMC_SECTOR_SIZE;
+ memcpy(&cts->proto_specific.mmc.ios, &sc->aw_host.ios, sizeof(struct mmc_ios));
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ break;
+ }
+ case XPT_SET_TRAN_SETTINGS:
+ {
+ if (__predict_false(aw_mmc_debug & AW_MMC_DEBUG_IOS))
+ device_printf(sc->aw_dev, "Got XPT_SET_TRAN_SETTINGS\n");
+ aw_mmc_cam_settran_settings(sc, ccb);
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ break;
+ }
+ case XPT_RESET_BUS:
+ if (__predict_false(aw_mmc_debug & AW_MMC_DEBUG_IOS))
+ device_printf(sc->aw_dev, "Got XPT_RESET_BUS, ACK it...\n");
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ break;
+ case XPT_MMC_IO:
+ /*
+ * Here is the HW-dependent part of
+ * sending the command to the underlying h/w
+ * At some point in the future an interrupt comes.
+ * Then the request will be marked as completed.
+ */
+ ccb->ccb_h.status = CAM_REQ_INPROG;
+
+ aw_mmc_cam_handle_mmcio(sim, ccb);
+ return;
+ /* NOTREACHED */
+ break;
+ default:
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ break;
+ }
+ xpt_done(ccb);
+ return;
+}
+
+static void
+aw_mmc_cam_poll(struct cam_sim *sim)
+{
+ return;
+}
+
+static int
+aw_mmc_cam_settran_settings(struct aw_mmc_softc *sc, union ccb *ccb)
+{
+ struct mmc_ios *ios;
+ struct mmc_ios *new_ios;
+ struct ccb_trans_settings_mmc *cts;
+
+ ios = &sc->aw_host.ios;
+
+ cts = &ccb->cts.proto_specific.mmc;
+ new_ios = &cts->ios;
+
+ /* Update only requested fields */
+ if (cts->ios_valid & MMC_CLK) {
+ ios->clock = new_ios->clock;
+ if (__predict_false(aw_mmc_debug & AW_MMC_DEBUG_IOS))
+ device_printf(sc->aw_dev, "Clock => %d\n", ios->clock);
+ }
+ if (cts->ios_valid & MMC_VDD) {
+ ios->vdd = new_ios->vdd;
+ if (__predict_false(aw_mmc_debug & AW_MMC_DEBUG_IOS))
+ device_printf(sc->aw_dev, "VDD => %d\n", ios->vdd);
+ }
+ if (cts->ios_valid & MMC_CS) {
+ ios->chip_select = new_ios->chip_select;
+ if (__predict_false(aw_mmc_debug & AW_MMC_DEBUG_IOS))
+ device_printf(sc->aw_dev, "CS => %d\n", ios->chip_select);
+ }
+ if (cts->ios_valid & MMC_BW) {
+ ios->bus_width = new_ios->bus_width;
+ if (__predict_false(aw_mmc_debug & AW_MMC_DEBUG_IOS))
+ device_printf(sc->aw_dev, "Bus width => %d\n", ios->bus_width);
+ }
+ if (cts->ios_valid & MMC_PM) {
+ ios->power_mode = new_ios->power_mode;
+ if (__predict_false(aw_mmc_debug & AW_MMC_DEBUG_IOS))
+ device_printf(sc->aw_dev, "Power mode => %d\n", ios->power_mode);
+ }
+ if (cts->ios_valid & MMC_BT) {
+ ios->timing = new_ios->timing;
+ if (__predict_false(aw_mmc_debug & AW_MMC_DEBUG_IOS))
+ device_printf(sc->aw_dev, "Timing => %d\n", ios->timing);
+ }
+ if (cts->ios_valid & MMC_BM) {
+ ios->bus_mode = new_ios->bus_mode;
+ if (__predict_false(aw_mmc_debug & AW_MMC_DEBUG_IOS))
+ device_printf(sc->aw_dev, "Bus mode => %d\n", ios->bus_mode);
+ }
+
+ return (aw_mmc_update_ios(sc->aw_dev, NULL));
+}
+
+static int
+aw_mmc_cam_request(struct aw_mmc_softc *sc, union ccb *ccb)
+{
+ struct ccb_mmcio *mmcio;
+
+ mmcio = &ccb->mmcio;
+
+ AW_MMC_LOCK(sc);
+
+ if (__predict_false(aw_mmc_debug & AW_MMC_DEBUG_CMD)) {
+ device_printf(sc->aw_dev, "CMD%u arg %#x flags %#x dlen %u dflags %#x\n",
+ mmcio->cmd.opcode, mmcio->cmd.arg, mmcio->cmd.flags,
+ mmcio->cmd.data != NULL ? (unsigned int) mmcio->cmd.data->len : 0,
+ mmcio->cmd.data != NULL ? mmcio->cmd.data->flags: 0);
+ }
+ if (mmcio->cmd.data != NULL) {
+ if (mmcio->cmd.data->len == 0 || mmcio->cmd.data->flags == 0)
+ panic("data->len = %d, data->flags = %d -- something is b0rked",
+ (int)mmcio->cmd.data->len, mmcio->cmd.data->flags);
+ }
+ if (sc->ccb != NULL) {
+ device_printf(sc->aw_dev, "Controller still has an active command\n");
+ return (EBUSY);
+ }
+ sc->ccb = ccb;
+ /* aw_mmc_request locks again */
+ AW_MMC_UNLOCK(sc);
+ aw_mmc_request(sc->aw_dev, NULL, NULL);
+
+ return (0);
+}
+#endif /* MMCCAM */
+
+static void
+aw_mmc_helper_cd_handler(device_t dev, bool present)
+{
+ struct aw_mmc_softc *sc;
+
+ sc = device_get_softc(dev);
+#ifdef MMCCAM
+ mmccam_start_discovery(sc->sim);
+#else
+ AW_MMC_LOCK(sc);
+ if (present) {
+ if (sc->child == NULL) {
+ if (__predict_false(aw_mmc_debug & AW_MMC_DEBUG_CARD))
+ device_printf(sc->aw_dev, "Card inserted\n");
+
+ sc->child = device_add_child(sc->aw_dev, "mmc", -1);
+ AW_MMC_UNLOCK(sc);
+ if (sc->child) {
+ device_set_ivars(sc->child, sc);
+ (void)device_probe_and_attach(sc->child);
+ }
+ } else
+ AW_MMC_UNLOCK(sc);
+ } else {
+ /* Card isn't present, detach if necessary */
+ if (sc->child != NULL) {
+ if (__predict_false(aw_mmc_debug & AW_MMC_DEBUG_CARD))
+ device_printf(sc->aw_dev, "Card removed\n");
+
+ AW_MMC_UNLOCK(sc);
+ device_delete_child(sc->aw_dev, sc->child);
+ sc->child = NULL;
+ } else
+ AW_MMC_UNLOCK(sc);
+ }
+#endif /* MMCCAM */
+}
+
+static int
+aw_mmc_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner Integrated MMC/SD controller");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+aw_mmc_attach(device_t dev)
+{
+ struct aw_mmc_softc *sc;
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid_list *tree;
+ int error;
+
+ sc = device_get_softc(dev);
+ sc->aw_dev = dev;
+
+ sc->aw_mmc_conf = (struct aw_mmc_conf *)ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+
+#ifndef MMCCAM
+ sc->aw_req = NULL;
+#endif
+ if (bus_alloc_resources(dev, aw_mmc_res_spec, sc->aw_res) != 0) {
+ device_printf(dev, "cannot allocate device resources\n");
+ return (ENXIO);
+ }
+ if (bus_setup_intr(dev, sc->aw_res[AW_MMC_IRQRES],
+ INTR_TYPE_NET | INTR_MPSAFE, NULL, aw_mmc_intr, sc,
+ &sc->aw_intrhand)) {
+ bus_release_resources(dev, aw_mmc_res_spec, sc->aw_res);
+ device_printf(dev, "cannot setup interrupt handler\n");
+ return (ENXIO);
+ }
+ mtx_init(&sc->aw_mtx, device_get_nameunit(sc->aw_dev), "aw_mmc",
+ MTX_DEF);
+ callout_init_mtx(&sc->aw_timeoutc, &sc->aw_mtx, 0);
+
+ /* De-assert reset */
+ if (hwreset_get_by_ofw_name(dev, 0, "ahb", &sc->aw_rst_ahb) == 0) {
+ error = hwreset_deassert(sc->aw_rst_ahb);
+ if (error != 0) {
+ device_printf(dev, "cannot de-assert reset\n");
+ goto fail;
+ }
+ }
+
+ /* Activate the module clock. */
+ error = clk_get_by_ofw_name(dev, 0, "ahb", &sc->aw_clk_ahb);
+ if (error != 0) {
+ device_printf(dev, "cannot get ahb clock\n");
+ goto fail;
+ }
+ error = clk_enable(sc->aw_clk_ahb);
+ if (error != 0) {
+ device_printf(dev, "cannot enable ahb clock\n");
+ goto fail;
+ }
+ error = clk_get_by_ofw_name(dev, 0, "mmc", &sc->aw_clk_mmc);
+ if (error != 0) {
+ device_printf(dev, "cannot get mmc clock\n");
+ goto fail;
+ }
+ error = clk_set_freq(sc->aw_clk_mmc, CARD_ID_FREQUENCY,
+ CLK_SET_ROUND_DOWN);
+ if (error != 0) {
+ device_printf(dev, "cannot init mmc clock\n");
+ goto fail;
+ }
+ error = clk_enable(sc->aw_clk_mmc);
+ if (error != 0) {
+ device_printf(dev, "cannot enable mmc clock\n");
+ goto fail;
+ }
+
+ sc->aw_timeout = 10;
+ ctx = device_get_sysctl_ctx(dev);
+ tree = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
+ SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "req_timeout", CTLFLAG_RW,
+ &sc->aw_timeout, 0, "Request timeout in seconds");
+
+ /* Soft Reset controller. */
+ if (aw_mmc_reset(sc) != 0) {
+ device_printf(dev, "cannot reset the controller\n");
+ goto fail;
+ }
+
+ if (aw_mmc_setup_dma(sc) != 0) {
+ device_printf(sc->aw_dev, "Couldn't setup DMA!\n");
+ goto fail;
+ }
+
+ /* Set some defaults for freq and supported mode */
+ sc->aw_host.f_min = 400000;
+ sc->aw_host.f_max = 52000000;
+ sc->aw_host.host_ocr = MMC_OCR_320_330 | MMC_OCR_330_340;
+ sc->aw_host.caps |= MMC_CAP_HSPEED | MMC_CAP_SIGNALING_330;
+ mmc_fdt_parse(dev, 0, &sc->mmc_helper, &sc->aw_host);
+ mmc_fdt_gpio_setup(dev, 0, &sc->mmc_helper, aw_mmc_helper_cd_handler);
+
+#ifdef MMCCAM
+ sc->ccb = NULL;
+ if ((sc->devq = cam_simq_alloc(1)) == NULL) {
+ goto fail;
+ }
+
+ mtx_init(&sc->sim_mtx, "awmmcsim", NULL, MTX_DEF);
+ sc->sim = cam_sim_alloc_dev(aw_mmc_cam_action, aw_mmc_cam_poll,
+ "aw_mmc_sim", sc, dev,
+ &sc->sim_mtx, 1, 1, sc->devq);
+
+ if (sc->sim == NULL) {
+ cam_simq_free(sc->devq);
+ device_printf(dev, "cannot allocate CAM SIM\n");
+ goto fail;
+ }
+
+ mtx_lock(&sc->sim_mtx);
+ if (xpt_bus_register(sc->sim, sc->aw_dev, 0) != 0) {
+ device_printf(dev, "cannot register SCSI pass-through bus\n");
+ cam_sim_free(sc->sim, FALSE);
+ cam_simq_free(sc->devq);
+ mtx_unlock(&sc->sim_mtx);
+ goto fail;
+ }
+
+ mtx_unlock(&sc->sim_mtx);
+#endif /* MMCCAM */
+
+ return (0);
+
+fail:
+ callout_drain(&sc->aw_timeoutc);
+ mtx_destroy(&sc->aw_mtx);
+ bus_teardown_intr(dev, sc->aw_res[AW_MMC_IRQRES], sc->aw_intrhand);
+ bus_release_resources(dev, aw_mmc_res_spec, sc->aw_res);
+
+#ifdef MMCCAM
+ if (sc->sim != NULL) {
+ mtx_lock(&sc->sim_mtx);
+ xpt_bus_deregister(cam_sim_path(sc->sim));
+ cam_sim_free(sc->sim, FALSE);
+ mtx_unlock(&sc->sim_mtx);
+ }
+
+ if (sc->devq != NULL)
+ cam_simq_free(sc->devq);
+#endif
+ return (ENXIO);
+}
+
+static int
+aw_mmc_detach(device_t dev)
+{
+ struct aw_mmc_softc *sc;
+ device_t d;
+
+ sc = device_get_softc(dev);
+
+ clk_disable(sc->aw_clk_mmc);
+ clk_disable(sc->aw_clk_ahb);
+ hwreset_assert(sc->aw_rst_ahb);
+
+ mmc_fdt_gpio_teardown(&sc->mmc_helper);
+
+ callout_drain(&sc->aw_timeoutc);
+
+ AW_MMC_LOCK(sc);
+ d = sc->child;
+ sc->child = NULL;
+ AW_MMC_UNLOCK(sc);
+ if (d != NULL)
+ device_delete_child(sc->aw_dev, d);
+
+ aw_mmc_teardown_dma(sc);
+
+ mtx_destroy(&sc->aw_mtx);
+
+ bus_teardown_intr(dev, sc->aw_res[AW_MMC_IRQRES], sc->aw_intrhand);
+ bus_release_resources(dev, aw_mmc_res_spec, sc->aw_res);
+
+#ifdef MMCCAM
+ if (sc->sim != NULL) {
+ mtx_lock(&sc->sim_mtx);
+ xpt_bus_deregister(cam_sim_path(sc->sim));
+ cam_sim_free(sc->sim, FALSE);
+ mtx_unlock(&sc->sim_mtx);
+ }
+
+ if (sc->devq != NULL)
+ cam_simq_free(sc->devq);
+#endif
+
+ return (0);
+}
+
+static void
+aw_dma_desc_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int err)
+{
+ struct aw_mmc_softc *sc;
+
+ sc = (struct aw_mmc_softc *)arg;
+ if (err) {
+ sc->aw_dma_map_err = err;
+ return;
+ }
+ sc->aw_dma_desc_phys = segs[0].ds_addr;
+}
+
+static int
+aw_mmc_setup_dma(struct aw_mmc_softc *sc)
+{
+ int error;
+
+ /* Allocate the DMA descriptor memory. */
+ error = bus_dma_tag_create(
+ bus_get_dma_tag(sc->aw_dev), /* parent */
+ AW_MMC_DMA_ALIGN, 0, /* align, boundary */
+ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg*/
+ AW_MMC_DMA_DESC_SIZE, 1, /* maxsize, nsegment */
+ AW_MMC_DMA_DESC_SIZE, /* maxsegsize */
+ 0, /* flags */
+ NULL, NULL, /* lock, lockarg*/
+ &sc->aw_dma_tag);
+ if (error)
+ return (error);
+
+ error = bus_dmamem_alloc(sc->aw_dma_tag, &sc->aw_dma_desc,
+ BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO,
+ &sc->aw_dma_map);
+ if (error)
+ return (error);
+
+ error = bus_dmamap_load(sc->aw_dma_tag,
+ sc->aw_dma_map,
+ sc->aw_dma_desc, AW_MMC_DMA_DESC_SIZE,
+ aw_dma_desc_cb, sc, 0);
+ if (error)
+ return (error);
+ if (sc->aw_dma_map_err)
+ return (sc->aw_dma_map_err);
+
+ /* Create the DMA map for data transfers. */
+ error = bus_dma_tag_create(
+ bus_get_dma_tag(sc->aw_dev), /* parent */
+ AW_MMC_DMA_ALIGN, 0, /* align, boundary */
+ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg*/
+ sc->aw_mmc_conf->dma_xferlen *
+ AW_MMC_DMA_SEGS, AW_MMC_DMA_SEGS, /* maxsize, nsegments */
+ sc->aw_mmc_conf->dma_xferlen, /* maxsegsize */
+ BUS_DMA_ALLOCNOW, /* flags */
+ NULL, NULL, /* lock, lockarg*/
+ &sc->aw_dma_buf_tag);
+ if (error)
+ return (error);
+ error = bus_dmamap_create(sc->aw_dma_buf_tag, 0,
+ &sc->aw_dma_buf_map);
+ if (error)
+ return (error);
+
+ return (0);
+}
+
+static void
+aw_mmc_teardown_dma(struct aw_mmc_softc *sc)
+{
+
+ bus_dmamap_unload(sc->aw_dma_tag, sc->aw_dma_map);
+ bus_dmamem_free(sc->aw_dma_tag, sc->aw_dma_desc, sc->aw_dma_map);
+ if (bus_dma_tag_destroy(sc->aw_dma_tag) != 0)
+ device_printf(sc->aw_dev, "Cannot destroy the dma tag\n");
+
+ bus_dmamap_unload(sc->aw_dma_buf_tag, sc->aw_dma_buf_map);
+ bus_dmamap_destroy(sc->aw_dma_buf_tag, sc->aw_dma_buf_map);
+ if (bus_dma_tag_destroy(sc->aw_dma_buf_tag) != 0)
+ device_printf(sc->aw_dev, "Cannot destroy the dma buf tag\n");
+}
+
+static void
+aw_dma_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int err)
+{
+ int i;
+ struct aw_mmc_dma_desc *dma_desc;
+ struct aw_mmc_softc *sc;
+
+ sc = (struct aw_mmc_softc *)arg;
+ sc->aw_dma_map_err = err;
+
+ if (err)
+ return;
+
+ dma_desc = sc->aw_dma_desc;
+ for (i = 0; i < nsegs; i++) {
+ if (segs[i].ds_len == sc->aw_mmc_conf->dma_xferlen)
+ dma_desc[i].buf_size = 0; /* Size of 0 indicate max len */
+ else
+ dma_desc[i].buf_size = segs[i].ds_len;
+ dma_desc[i].buf_addr = segs[i].ds_addr;
+ dma_desc[i].config = AW_MMC_DMA_CONFIG_CH |
+ AW_MMC_DMA_CONFIG_OWN | AW_MMC_DMA_CONFIG_DIC;
+
+ dma_desc[i].next = sc->aw_dma_desc_phys +
+ ((i + 1) * sizeof(struct aw_mmc_dma_desc));
+ }
+
+ dma_desc[0].config |= AW_MMC_DMA_CONFIG_FD;
+ dma_desc[nsegs - 1].config |= AW_MMC_DMA_CONFIG_LD |
+ AW_MMC_DMA_CONFIG_ER;
+ dma_desc[nsegs - 1].config &= ~AW_MMC_DMA_CONFIG_DIC;
+ dma_desc[nsegs - 1].next = 0;
+}
+
+static int
+aw_mmc_prepare_dma(struct aw_mmc_softc *sc)
+{
+ bus_dmasync_op_t sync_op;
+ int error;
+ struct mmc_command *cmd;
+ uint32_t val;
+
+#ifdef MMCCAM
+ cmd = &sc->ccb->mmcio.cmd;
+#else
+ cmd = sc->aw_req->cmd;
+#endif
+ if (cmd->data->len > (sc->aw_mmc_conf->dma_xferlen * AW_MMC_DMA_SEGS))
+ return (EFBIG);
+ error = bus_dmamap_load(sc->aw_dma_buf_tag, sc->aw_dma_buf_map,
+ cmd->data->data, cmd->data->len, aw_dma_cb, sc, 0);
+ if (error)
+ return (error);
+ if (sc->aw_dma_map_err)
+ return (sc->aw_dma_map_err);
+
+ if (cmd->data->flags & MMC_DATA_WRITE)
+ sync_op = BUS_DMASYNC_PREWRITE;
+ else
+ sync_op = BUS_DMASYNC_PREREAD;
+ bus_dmamap_sync(sc->aw_dma_buf_tag, sc->aw_dma_buf_map, sync_op);
+ bus_dmamap_sync(sc->aw_dma_tag, sc->aw_dma_map, BUS_DMASYNC_PREWRITE);
+
+ /* Enable DMA */
+ val = AW_MMC_READ_4(sc, AW_MMC_GCTL);
+ val &= ~AW_MMC_GCTL_FIFO_AC_MOD;
+ val |= AW_MMC_GCTL_DMA_ENB;
+ AW_MMC_WRITE_4(sc, AW_MMC_GCTL, val);
+
+ /* Reset DMA */
+ val |= AW_MMC_GCTL_DMA_RST;
+ AW_MMC_WRITE_4(sc, AW_MMC_GCTL, val);
+
+ AW_MMC_WRITE_4(sc, AW_MMC_DMAC, AW_MMC_DMAC_IDMAC_SOFT_RST);
+ AW_MMC_WRITE_4(sc, AW_MMC_DMAC,
+ AW_MMC_DMAC_IDMAC_IDMA_ON | AW_MMC_DMAC_IDMAC_FIX_BURST);
+
+ /* Enable RX or TX DMA interrupt */
+ val = AW_MMC_READ_4(sc, AW_MMC_IDIE);
+ if (cmd->data->flags & MMC_DATA_WRITE)
+ val |= AW_MMC_IDST_TX_INT;
+ else
+ val |= AW_MMC_IDST_RX_INT;
+ AW_MMC_WRITE_4(sc, AW_MMC_IDIE, val);
+
+ /* Set DMA descritptor list address */
+ AW_MMC_WRITE_4(sc, AW_MMC_DLBA, sc->aw_dma_desc_phys);
+
+ /* FIFO trigger level */
+ AW_MMC_WRITE_4(sc, AW_MMC_FWLR, AW_MMC_DMA_FTRGLEVEL);
+
+ return (0);
+}
+
+static int
+aw_mmc_reset(struct aw_mmc_softc *sc)
+{
+ uint32_t reg;
+ int timeout;
+
+ reg = AW_MMC_READ_4(sc, AW_MMC_GCTL);
+ reg |= AW_MMC_GCTL_RESET;
+ AW_MMC_WRITE_4(sc, AW_MMC_GCTL, reg);
+ timeout = AW_MMC_RESET_RETRY;
+ while (--timeout > 0) {
+ if ((AW_MMC_READ_4(sc, AW_MMC_GCTL) & AW_MMC_GCTL_RESET) == 0)
+ break;
+ DELAY(100);
+ }
+ if (timeout == 0)
+ return (ETIMEDOUT);
+
+ return (0);
+}
+
+static int
+aw_mmc_init(struct aw_mmc_softc *sc)
+{
+ uint32_t reg;
+ int ret;
+
+ ret = aw_mmc_reset(sc);
+ if (ret != 0)
+ return (ret);
+
+ /* Set the timeout. */
+ AW_MMC_WRITE_4(sc, AW_MMC_TMOR,
+ AW_MMC_TMOR_DTO_LMT_SHIFT(AW_MMC_TMOR_DTO_LMT_MASK) |
+ AW_MMC_TMOR_RTO_LMT_SHIFT(AW_MMC_TMOR_RTO_LMT_MASK));
+
+ /* Unmask interrupts. */
+ AW_MMC_WRITE_4(sc, AW_MMC_IMKR, 0);
+
+ /* Clear pending interrupts. */
+ AW_MMC_WRITE_4(sc, AW_MMC_RISR, 0xffffffff);
+
+ /* Debug register, undocumented */
+ AW_MMC_WRITE_4(sc, AW_MMC_DBGC, 0xdeb);
+
+ /* Function select register */
+ AW_MMC_WRITE_4(sc, AW_MMC_FUNS, 0xceaa0000);
+
+ AW_MMC_WRITE_4(sc, AW_MMC_IDST, 0xffffffff);
+
+ /* Enable interrupts and disable AHB access. */
+ reg = AW_MMC_READ_4(sc, AW_MMC_GCTL);
+ reg |= AW_MMC_GCTL_INT_ENB;
+ reg &= ~AW_MMC_GCTL_FIFO_AC_MOD;
+ reg &= ~AW_MMC_GCTL_WAIT_MEM_ACCESS;
+ AW_MMC_WRITE_4(sc, AW_MMC_GCTL, reg);
+
+ return (0);
+}
+
+static void
+aw_mmc_req_done(struct aw_mmc_softc *sc)
+{
+ struct mmc_command *cmd;
+#ifdef MMCCAM
+ union ccb *ccb;
+#else
+ struct mmc_request *req;
+#endif
+ uint32_t val, mask;
+ int retry;
+
+#ifdef MMCCAM
+ ccb = sc->ccb;
+ cmd = &ccb->mmcio.cmd;
+#else
+ cmd = sc->aw_req->cmd;
+#endif
+ if (__predict_false(aw_mmc_debug & AW_MMC_DEBUG_CMD)) {
+ device_printf(sc->aw_dev, "%s: cmd %d err %d\n", __func__, cmd->opcode, cmd->error);
+ }
+ if (cmd->error != MMC_ERR_NONE) {
+ /* Reset the FIFO and DMA engines. */
+ mask = AW_MMC_GCTL_FIFO_RST | AW_MMC_GCTL_DMA_RST;
+ val = AW_MMC_READ_4(sc, AW_MMC_GCTL);
+ AW_MMC_WRITE_4(sc, AW_MMC_GCTL, val | mask);
+
+ retry = AW_MMC_RESET_RETRY;
+ while (--retry > 0) {
+ if ((AW_MMC_READ_4(sc, AW_MMC_GCTL) &
+ AW_MMC_GCTL_RESET) == 0)
+ break;
+ DELAY(100);
+ }
+ if (retry == 0)
+ device_printf(sc->aw_dev,
+ "timeout resetting DMA/FIFO\n");
+ aw_mmc_update_clock(sc, 1);
+ }
+
+ callout_stop(&sc->aw_timeoutc);
+ sc->aw_intr = 0;
+ sc->aw_resid = 0;
+ sc->aw_dma_map_err = 0;
+ sc->aw_intr_wait = 0;
+#ifdef MMCCAM
+ sc->ccb = NULL;
+ ccb->ccb_h.status =
+ (ccb->mmcio.cmd.error == 0 ? CAM_REQ_CMP : CAM_REQ_CMP_ERR);
+ xpt_done(ccb);
+#else
+ req = sc->aw_req;
+ sc->aw_req = NULL;
+ req->done(req);
+#endif
+}
+
+static void
+aw_mmc_req_ok(struct aw_mmc_softc *sc)
+{
+ int timeout;
+ struct mmc_command *cmd;
+ uint32_t status;
+
+ timeout = 1000;
+ while (--timeout > 0) {
+ status = AW_MMC_READ_4(sc, AW_MMC_STAR);
+ if ((status & AW_MMC_STAR_CARD_BUSY) == 0)
+ break;
+ DELAY(1000);
+ }
+#ifdef MMCCAM
+ cmd = &sc->ccb->mmcio.cmd;
+#else
+ cmd = sc->aw_req->cmd;
+#endif
+ if (timeout == 0) {
+ cmd->error = MMC_ERR_FAILED;
+ aw_mmc_req_done(sc);
+ return;
+ }
+ if (cmd->flags & MMC_RSP_PRESENT) {
+ if (cmd->flags & MMC_RSP_136) {
+ cmd->resp[0] = AW_MMC_READ_4(sc, AW_MMC_RESP3);
+ cmd->resp[1] = AW_MMC_READ_4(sc, AW_MMC_RESP2);
+ cmd->resp[2] = AW_MMC_READ_4(sc, AW_MMC_RESP1);
+ cmd->resp[3] = AW_MMC_READ_4(sc, AW_MMC_RESP0);
+ } else
+ cmd->resp[0] = AW_MMC_READ_4(sc, AW_MMC_RESP0);
+ }
+ /* All data has been transferred ? */
+ if (cmd->data != NULL && (sc->aw_resid << 2) < cmd->data->len)
+ cmd->error = MMC_ERR_FAILED;
+ aw_mmc_req_done(sc);
+}
+
+static inline void
+set_mmc_error(struct aw_mmc_softc *sc, int error_code)
+{
+#ifdef MMCCAM
+ sc->ccb->mmcio.cmd.error = error_code;
+#else
+ sc->aw_req->cmd->error = error_code;
+#endif
+}
+
+static void
+aw_mmc_timeout(void *arg)
+{
+ struct aw_mmc_softc *sc;
+
+ sc = (struct aw_mmc_softc *)arg;
+#ifdef MMCCAM
+ if (sc->ccb != NULL) {
+#else
+ if (sc->aw_req != NULL) {
+#endif
+ device_printf(sc->aw_dev, "controller timeout\n");
+ set_mmc_error(sc, MMC_ERR_TIMEOUT);
+ aw_mmc_req_done(sc);
+ } else
+ device_printf(sc->aw_dev,
+ "Spurious timeout - no active request\n");
+}
+
+static void
+aw_mmc_print_error(uint32_t err)
+{
+ if(err & AW_MMC_INT_RESP_ERR)
+ printf("AW_MMC_INT_RESP_ERR ");
+ if (err & AW_MMC_INT_RESP_CRC_ERR)
+ printf("AW_MMC_INT_RESP_CRC_ERR ");
+ if (err & AW_MMC_INT_DATA_CRC_ERR)
+ printf("AW_MMC_INT_DATA_CRC_ERR ");
+ if (err & AW_MMC_INT_RESP_TIMEOUT)
+ printf("AW_MMC_INT_RESP_TIMEOUT ");
+ if (err & AW_MMC_INT_FIFO_RUN_ERR)
+ printf("AW_MMC_INT_FIFO_RUN_ERR ");
+ if (err & AW_MMC_INT_CMD_BUSY)
+ printf("AW_MMC_INT_CMD_BUSY ");
+ if (err & AW_MMC_INT_DATA_START_ERR)
+ printf("AW_MMC_INT_DATA_START_ERR ");
+ if (err & AW_MMC_INT_DATA_END_BIT_ERR)
+ printf("AW_MMC_INT_DATA_END_BIT_ERR");
+ printf("\n");
+}
+
+static void
+aw_mmc_intr(void *arg)
+{
+ bus_dmasync_op_t sync_op;
+ struct aw_mmc_softc *sc;
+ struct mmc_data *data;
+ uint32_t idst, imask, rint;
+
+ sc = (struct aw_mmc_softc *)arg;
+ AW_MMC_LOCK(sc);
+ rint = AW_MMC_READ_4(sc, AW_MMC_RISR);
+ idst = AW_MMC_READ_4(sc, AW_MMC_IDST);
+ imask = AW_MMC_READ_4(sc, AW_MMC_IMKR);
+ if (idst == 0 && imask == 0 && rint == 0) {
+ AW_MMC_UNLOCK(sc);
+ return;
+ }
+ if (__predict_false(aw_mmc_debug & AW_MMC_DEBUG_INT)) {
+ device_printf(sc->aw_dev, "idst: %#x, imask: %#x, rint: %#x\n",
+ idst, imask, rint);
+ }
+#ifdef MMCCAM
+ if (sc->ccb == NULL) {
+#else
+ if (sc->aw_req == NULL) {
+#endif
+ device_printf(sc->aw_dev,
+ "Spurious interrupt - no active request, rint: 0x%08X\n",
+ rint);
+ aw_mmc_print_error(rint);
+ goto end;
+ }
+ if (rint & AW_MMC_INT_ERR_BIT) {
+ if (__predict_false(aw_mmc_debug & AW_MMC_DEBUG_INT)) {
+ device_printf(sc->aw_dev, "error rint: 0x%08X\n", rint);
+ aw_mmc_print_error(rint);
+ }
+ if (rint & AW_MMC_INT_RESP_TIMEOUT)
+ set_mmc_error(sc, MMC_ERR_TIMEOUT);
+ else
+ set_mmc_error(sc, MMC_ERR_FAILED);
+ aw_mmc_req_done(sc);
+ goto end;
+ }
+ if (idst & AW_MMC_IDST_ERROR) {
+ if (__predict_false(aw_mmc_debug & AW_MMC_DEBUG_INT))
+ device_printf(sc->aw_dev, "error idst: 0x%08x\n", idst);
+ set_mmc_error(sc, MMC_ERR_FAILED);
+ aw_mmc_req_done(sc);
+ goto end;
+ }
+
+ sc->aw_intr |= rint;
+#ifdef MMCCAM
+ data = sc->ccb->mmcio.cmd.data;
+#else
+ data = sc->aw_req->cmd->data;
+#endif
+ if (data != NULL && (idst & AW_MMC_IDST_COMPLETE) != 0) {
+ if (data->flags & MMC_DATA_WRITE)
+ sync_op = BUS_DMASYNC_POSTWRITE;
+ else
+ sync_op = BUS_DMASYNC_POSTREAD;
+ bus_dmamap_sync(sc->aw_dma_buf_tag, sc->aw_dma_buf_map,
+ sync_op);
+ bus_dmamap_sync(sc->aw_dma_tag, sc->aw_dma_map,
+ BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(sc->aw_dma_buf_tag, sc->aw_dma_buf_map);
+ sc->aw_resid = data->len >> 2;
+ }
+ if ((sc->aw_intr & sc->aw_intr_wait) == sc->aw_intr_wait)
+ aw_mmc_req_ok(sc);
+
+end:
+ AW_MMC_WRITE_4(sc, AW_MMC_IDST, idst);
+ AW_MMC_WRITE_4(sc, AW_MMC_RISR, rint);
+ AW_MMC_UNLOCK(sc);
+}
+
+static int
+aw_mmc_request(device_t bus, device_t child, struct mmc_request *req)
+{
+ int blksz;
+ struct aw_mmc_softc *sc;
+ struct mmc_command *cmd;
+ uint32_t cmdreg, imask;
+ int err;
+
+ sc = device_get_softc(bus);
+
+ AW_MMC_LOCK(sc);
+#ifdef MMCCAM
+ KASSERT(req == NULL, ("req should be NULL in MMCCAM case!"));
+ /*
+ * For MMCCAM, sc->ccb has been NULL-checked and populated
+ * by aw_mmc_cam_request() already.
+ */
+ cmd = &sc->ccb->mmcio.cmd;
+#else
+ if (sc->aw_req) {
+ AW_MMC_UNLOCK(sc);
+ return (EBUSY);
+ }
+ sc->aw_req = req;
+ cmd = req->cmd;
+
+ if (__predict_false(aw_mmc_debug & AW_MMC_DEBUG_CMD)) {
+ device_printf(sc->aw_dev, "CMD%u arg %#x flags %#x dlen %u dflags %#x\n",
+ cmd->opcode, cmd->arg, cmd->flags,
+ cmd->data != NULL ? (unsigned int)cmd->data->len : 0,
+ cmd->data != NULL ? cmd->data->flags: 0);
+ }
+#endif
+ cmdreg = AW_MMC_CMDR_LOAD;
+ imask = AW_MMC_INT_ERR_BIT;
+ sc->aw_intr_wait = 0;
+ sc->aw_intr = 0;
+ sc->aw_resid = 0;
+ cmd->error = MMC_ERR_NONE;
+
+ if (cmd->opcode == MMC_GO_IDLE_STATE)
+ cmdreg |= AW_MMC_CMDR_SEND_INIT_SEQ;
+
+ if (cmd->flags & MMC_RSP_PRESENT)
+ cmdreg |= AW_MMC_CMDR_RESP_RCV;
+ if (cmd->flags & MMC_RSP_136)
+ cmdreg |= AW_MMC_CMDR_LONG_RESP;
+ if (cmd->flags & MMC_RSP_CRC)
+ cmdreg |= AW_MMC_CMDR_CHK_RESP_CRC;
+
+ if (cmd->data) {
+ cmdreg |= AW_MMC_CMDR_DATA_TRANS | AW_MMC_CMDR_WAIT_PRE_OVER;
+
+ if (cmd->data->flags & MMC_DATA_MULTI) {
+ cmdreg |= AW_MMC_CMDR_STOP_CMD_FLAG;
+ imask |= AW_MMC_INT_AUTO_STOP_DONE;
+ sc->aw_intr_wait |= AW_MMC_INT_AUTO_STOP_DONE;
+ } else {
+ sc->aw_intr_wait |= AW_MMC_INT_DATA_OVER;
+ imask |= AW_MMC_INT_DATA_OVER;
+ }
+ if (cmd->data->flags & MMC_DATA_WRITE)
+ cmdreg |= AW_MMC_CMDR_DIR_WRITE;
+#ifdef MMCCAM
+ if (cmd->data->flags & MMC_DATA_BLOCK_SIZE) {
+ AW_MMC_WRITE_4(sc, AW_MMC_BKSR, cmd->data->block_size);
+ AW_MMC_WRITE_4(sc, AW_MMC_BYCR, cmd->data->len);
+ } else
+#endif
+ {
+ blksz = min(cmd->data->len, MMC_SECTOR_SIZE);
+ AW_MMC_WRITE_4(sc, AW_MMC_BKSR, blksz);
+ AW_MMC_WRITE_4(sc, AW_MMC_BYCR, cmd->data->len);
+ }
+ } else {
+ imask |= AW_MMC_INT_CMD_DONE;
+ }
+
+ /* Enable the interrupts we are interested in */
+ AW_MMC_WRITE_4(sc, AW_MMC_IMKR, imask);
+ AW_MMC_WRITE_4(sc, AW_MMC_RISR, 0xffffffff);
+
+ /* Enable auto stop if needed */
+ AW_MMC_WRITE_4(sc, AW_MMC_A12A,
+ cmdreg & AW_MMC_CMDR_STOP_CMD_FLAG ? 0 : 0xffff);
+
+ /* Write the command argument */
+ AW_MMC_WRITE_4(sc, AW_MMC_CAGR, cmd->arg);
+
+ /*
+ * If we don't have data start the request
+ * if we do prepare the dma request and start the request
+ */
+ if (cmd->data == NULL) {
+ AW_MMC_WRITE_4(sc, AW_MMC_CMDR, cmdreg | cmd->opcode);
+ } else {
+ err = aw_mmc_prepare_dma(sc);
+ if (err != 0)
+ device_printf(sc->aw_dev, "prepare_dma failed: %d\n", err);
+
+ AW_MMC_WRITE_4(sc, AW_MMC_CMDR, cmdreg | cmd->opcode);
+ }
+
+ callout_reset(&sc->aw_timeoutc, sc->aw_timeout * hz,
+ aw_mmc_timeout, sc);
+ AW_MMC_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+aw_mmc_read_ivar(device_t bus, device_t child, int which,
+ uintptr_t *result)
+{
+ struct aw_mmc_softc *sc;
+
+ sc = device_get_softc(bus);
+ switch (which) {
+ default:
+ return (EINVAL);
+ case MMCBR_IVAR_BUS_MODE:
+ *(int *)result = sc->aw_host.ios.bus_mode;
+ break;
+ case MMCBR_IVAR_BUS_WIDTH:
+ *(int *)result = sc->aw_host.ios.bus_width;
+ break;
+ case MMCBR_IVAR_CHIP_SELECT:
+ *(int *)result = sc->aw_host.ios.chip_select;
+ break;
+ case MMCBR_IVAR_CLOCK:
+ *(int *)result = sc->aw_host.ios.clock;
+ break;
+ case MMCBR_IVAR_F_MIN:
+ *(int *)result = sc->aw_host.f_min;
+ break;
+ case MMCBR_IVAR_F_MAX:
+ *(int *)result = sc->aw_host.f_max;
+ break;
+ case MMCBR_IVAR_HOST_OCR:
+ *(int *)result = sc->aw_host.host_ocr;
+ break;
+ case MMCBR_IVAR_MODE:
+ *(int *)result = sc->aw_host.mode;
+ break;
+ case MMCBR_IVAR_OCR:
+ *(int *)result = sc->aw_host.ocr;
+ break;
+ case MMCBR_IVAR_POWER_MODE:
+ *(int *)result = sc->aw_host.ios.power_mode;
+ break;
+ case MMCBR_IVAR_VDD:
+ *(int *)result = sc->aw_host.ios.vdd;
+ break;
+ case MMCBR_IVAR_VCCQ:
+ *(int *)result = sc->aw_host.ios.vccq;
+ break;
+ case MMCBR_IVAR_CAPS:
+ *(int *)result = sc->aw_host.caps;
+ break;
+ case MMCBR_IVAR_TIMING:
+ *(int *)result = sc->aw_host.ios.timing;
+ break;
+ case MMCBR_IVAR_MAX_DATA:
+ *(int *)result = (sc->aw_mmc_conf->dma_xferlen *
+ AW_MMC_DMA_SEGS) / MMC_SECTOR_SIZE;
+ break;
+ case MMCBR_IVAR_RETUNE_REQ:
+ *(int *)result = retune_req_none;
+ break;
+ }
+
+ return (0);
+}
+
+static int
+aw_mmc_write_ivar(device_t bus, device_t child, int which,
+ uintptr_t value)
+{
+ struct aw_mmc_softc *sc;
+
+ sc = device_get_softc(bus);
+ switch (which) {
+ default:
+ return (EINVAL);
+ case MMCBR_IVAR_BUS_MODE:
+ sc->aw_host.ios.bus_mode = value;
+ break;
+ case MMCBR_IVAR_BUS_WIDTH:
+ sc->aw_host.ios.bus_width = value;
+ break;
+ case MMCBR_IVAR_CHIP_SELECT:
+ sc->aw_host.ios.chip_select = value;
+ break;
+ case MMCBR_IVAR_CLOCK:
+ sc->aw_host.ios.clock = value;
+ break;
+ case MMCBR_IVAR_MODE:
+ sc->aw_host.mode = value;
+ break;
+ case MMCBR_IVAR_OCR:
+ sc->aw_host.ocr = value;
+ break;
+ case MMCBR_IVAR_POWER_MODE:
+ sc->aw_host.ios.power_mode = value;
+ break;
+ case MMCBR_IVAR_VDD:
+ sc->aw_host.ios.vdd = value;
+ break;
+ case MMCBR_IVAR_VCCQ:
+ sc->aw_host.ios.vccq = value;
+ break;
+ case MMCBR_IVAR_TIMING:
+ sc->aw_host.ios.timing = value;
+ break;
+ /* These are read-only */
+ case MMCBR_IVAR_CAPS:
+ case MMCBR_IVAR_HOST_OCR:
+ case MMCBR_IVAR_F_MIN:
+ case MMCBR_IVAR_F_MAX:
+ case MMCBR_IVAR_MAX_DATA:
+ return (EINVAL);
+ }
+
+ return (0);
+}
+
+static int
+aw_mmc_update_clock(struct aw_mmc_softc *sc, uint32_t clkon)
+{
+ uint32_t reg;
+ int retry;
+
+ reg = AW_MMC_READ_4(sc, AW_MMC_CKCR);
+ reg &= ~(AW_MMC_CKCR_ENB | AW_MMC_CKCR_LOW_POWER |
+ AW_MMC_CKCR_MASK_DATA0);
+
+ if (clkon)
+ reg |= AW_MMC_CKCR_ENB;
+ if (sc->aw_mmc_conf->mask_data0)
+ reg |= AW_MMC_CKCR_MASK_DATA0;
+
+ AW_MMC_WRITE_4(sc, AW_MMC_CKCR, reg);
+
+ reg = AW_MMC_CMDR_LOAD | AW_MMC_CMDR_PRG_CLK |
+ AW_MMC_CMDR_WAIT_PRE_OVER;
+ AW_MMC_WRITE_4(sc, AW_MMC_CMDR, reg);
+ retry = 0xfffff;
+
+ while (reg & AW_MMC_CMDR_LOAD && --retry > 0) {
+ reg = AW_MMC_READ_4(sc, AW_MMC_CMDR);
+ DELAY(10);
+ }
+ AW_MMC_WRITE_4(sc, AW_MMC_RISR, 0xffffffff);
+
+ if (reg & AW_MMC_CMDR_LOAD) {
+ device_printf(sc->aw_dev, "timeout updating clock\n");
+ return (ETIMEDOUT);
+ }
+
+ if (sc->aw_mmc_conf->mask_data0) {
+ reg = AW_MMC_READ_4(sc, AW_MMC_CKCR);
+ reg &= ~AW_MMC_CKCR_MASK_DATA0;
+ AW_MMC_WRITE_4(sc, AW_MMC_CKCR, reg);
+ }
+
+ return (0);
+}
+
+static int
+aw_mmc_switch_vccq(device_t bus, device_t child)
+{
+ struct aw_mmc_softc *sc;
+ int uvolt, err;
+
+ sc = device_get_softc(bus);
+
+ if (sc->mmc_helper.vqmmc_supply == NULL)
+ return EOPNOTSUPP;
+
+ switch (sc->aw_host.ios.vccq) {
+ case vccq_180:
+ uvolt = 1800000;
+ break;
+ case vccq_330:
+ uvolt = 3300000;
+ break;
+ default:
+ return EINVAL;
+ }
+
+ err = regulator_set_voltage(sc->mmc_helper.vqmmc_supply, uvolt, uvolt);
+ if (err != 0) {
+ device_printf(sc->aw_dev,
+ "Cannot set vqmmc to %d<->%d\n",
+ uvolt,
+ uvolt);
+ return (err);
+ }
+
+ return (0);
+}
+
+static int
+aw_mmc_update_ios(device_t bus, device_t child)
+{
+ int error;
+ struct aw_mmc_softc *sc;
+ struct mmc_ios *ios;
+ unsigned int clock;
+ uint32_t reg, div = 1;
+
+ sc = device_get_softc(bus);
+
+ ios = &sc->aw_host.ios;
+
+ /* Set the bus width. */
+ switch (ios->bus_width) {
+ case bus_width_1:
+ AW_MMC_WRITE_4(sc, AW_MMC_BWDR, AW_MMC_BWDR1);
+ break;
+ case bus_width_4:
+ AW_MMC_WRITE_4(sc, AW_MMC_BWDR, AW_MMC_BWDR4);
+ break;
+ case bus_width_8:
+ AW_MMC_WRITE_4(sc, AW_MMC_BWDR, AW_MMC_BWDR8);
+ break;
+ }
+
+ switch (ios->power_mode) {
+ case power_on:
+ break;
+ case power_off:
+ if (__predict_false(aw_mmc_debug & AW_MMC_DEBUG_CARD))
+ device_printf(sc->aw_dev, "Powering down sd/mmc\n");
+
+ if (sc->mmc_helper.vmmc_supply)
+ regulator_disable(sc->mmc_helper.vmmc_supply);
+ if (sc->mmc_helper.vqmmc_supply)
+ regulator_disable(sc->mmc_helper.vqmmc_supply);
+
+ aw_mmc_reset(sc);
+ break;
+ case power_up:
+ if (__predict_false(aw_mmc_debug & AW_MMC_DEBUG_CARD))
+ device_printf(sc->aw_dev, "Powering up sd/mmc\n");
+
+ if (sc->mmc_helper.vmmc_supply)
+ regulator_enable(sc->mmc_helper.vmmc_supply);
+ if (sc->mmc_helper.vqmmc_supply)
+ regulator_enable(sc->mmc_helper.vqmmc_supply);
+ aw_mmc_init(sc);
+ break;
+ };
+
+ /* Enable ddr mode if needed */
+ reg = AW_MMC_READ_4(sc, AW_MMC_GCTL);
+ if (ios->timing == bus_timing_uhs_ddr50 ||
+ ios->timing == bus_timing_mmc_ddr52)
+ reg |= AW_MMC_GCTL_DDR_MOD_SEL;
+ else
+ reg &= ~AW_MMC_GCTL_DDR_MOD_SEL;
+ AW_MMC_WRITE_4(sc, AW_MMC_GCTL, reg);
+
+ if (ios->clock && ios->clock != sc->aw_clock) {
+ sc->aw_clock = clock = ios->clock;
+
+ /* Disable clock */
+ error = aw_mmc_update_clock(sc, 0);
+ if (error != 0)
+ return (error);
+
+ if (ios->timing == bus_timing_mmc_ddr52 &&
+ (sc->aw_mmc_conf->new_timing ||
+ ios->bus_width == bus_width_8)) {
+ div = 2;
+ clock <<= 1;
+ }
+
+ /* Reset the divider. */
+ reg = AW_MMC_READ_4(sc, AW_MMC_CKCR);
+ reg &= ~AW_MMC_CKCR_DIV;
+ reg |= div - 1;
+ AW_MMC_WRITE_4(sc, AW_MMC_CKCR, reg);
+
+ /* New timing mode if needed */
+ if (sc->aw_mmc_conf->new_timing) {
+ reg = AW_MMC_READ_4(sc, AW_MMC_NTSR);
+ reg |= AW_MMC_NTSR_MODE_SELECT;
+ AW_MMC_WRITE_4(sc, AW_MMC_NTSR, reg);
+ }
+
+ /* Set the MMC clock. */
+ error = clk_disable(sc->aw_clk_mmc);
+ if (error != 0 && bootverbose)
+ device_printf(sc->aw_dev,
+ "failed to disable mmc clock: %d\n", error);
+ error = clk_set_freq(sc->aw_clk_mmc, clock,
+ CLK_SET_ROUND_DOWN);
+ if (error != 0) {
+ device_printf(sc->aw_dev,
+ "failed to set frequency to %u Hz: %d\n",
+ clock, error);
+ return (error);
+ }
+ error = clk_enable(sc->aw_clk_mmc);
+ if (error != 0 && bootverbose)
+ device_printf(sc->aw_dev,
+ "failed to re-enable mmc clock: %d\n", error);
+
+ if (sc->aw_mmc_conf->can_calibrate)
+ AW_MMC_WRITE_4(sc, AW_MMC_SAMP_DL, AW_MMC_SAMP_DL_SW_EN);
+
+ /* Enable clock. */
+ error = aw_mmc_update_clock(sc, 1);
+ if (error != 0)
+ return (error);
+ }
+
+ return (0);
+}
+
+static int
+aw_mmc_get_ro(device_t bus, device_t child)
+{
+ struct aw_mmc_softc *sc;
+
+ sc = device_get_softc(bus);
+
+ return (mmc_fdt_gpio_get_readonly(&sc->mmc_helper));
+}
+
+static int
+aw_mmc_acquire_host(device_t bus, device_t child)
+{
+ struct aw_mmc_softc *sc;
+ int error;
+
+ sc = device_get_softc(bus);
+ AW_MMC_LOCK(sc);
+ while (sc->aw_bus_busy) {
+ error = msleep(sc, &sc->aw_mtx, PCATCH, "mmchw", 0);
+ if (error != 0) {
+ AW_MMC_UNLOCK(sc);
+ return (error);
+ }
+ }
+ sc->aw_bus_busy++;
+ AW_MMC_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+aw_mmc_release_host(device_t bus, device_t child)
+{
+ struct aw_mmc_softc *sc;
+
+ sc = device_get_softc(bus);
+ AW_MMC_LOCK(sc);
+ sc->aw_bus_busy--;
+ wakeup(sc);
+ AW_MMC_UNLOCK(sc);
+
+ return (0);
+}
+
+static device_method_t aw_mmc_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, aw_mmc_probe),
+ DEVMETHOD(device_attach, aw_mmc_attach),
+ DEVMETHOD(device_detach, aw_mmc_detach),
+
+ /* Bus interface */
+ DEVMETHOD(bus_read_ivar, aw_mmc_read_ivar),
+ DEVMETHOD(bus_write_ivar, aw_mmc_write_ivar),
+ DEVMETHOD(bus_add_child, bus_generic_add_child),
+
+ /* MMC bridge interface */
+ DEVMETHOD(mmcbr_update_ios, aw_mmc_update_ios),
+ DEVMETHOD(mmcbr_request, aw_mmc_request),
+ DEVMETHOD(mmcbr_get_ro, aw_mmc_get_ro),
+ DEVMETHOD(mmcbr_switch_vccq, aw_mmc_switch_vccq),
+ DEVMETHOD(mmcbr_acquire_host, aw_mmc_acquire_host),
+ DEVMETHOD(mmcbr_release_host, aw_mmc_release_host),
+
+ DEVMETHOD_END
+};
+
+static devclass_t aw_mmc_devclass;
+
+static driver_t aw_mmc_driver = {
+ "aw_mmc",
+ aw_mmc_methods,
+ sizeof(struct aw_mmc_softc),
+};
+
+DRIVER_MODULE(aw_mmc, simplebus, aw_mmc_driver, aw_mmc_devclass, NULL,
+ NULL);
+#ifndef MMCCAM
+MMC_DECLARE_BRIDGE(aw_mmc);
+#endif
+SIMPLEBUS_PNP_INFO(compat_data);
diff --git a/sys/arm/allwinner/aw_mmc.h b/sys/arm/allwinner/aw_mmc.h
new file mode 100644
index 000000000000..18308b55cc43
--- /dev/null
+++ b/sys/arm/allwinner/aw_mmc.h
@@ -0,0 +1,228 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2018 Emmanuel Vadot <manu@FreeBSD.org>
+ * Copyright (c) 2013 Alexander Fedorov <alexander.fedorov@rtlservice.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _AW_MMC_H_
+#define _AW_MMC_H_
+
+#define AW_MMC_GCTL 0x00 /* Control Register */
+#define AW_MMC_CKCR 0x04 /* Clock Control Register */
+#define AW_MMC_TMOR 0x08 /* Timeout Register */
+#define AW_MMC_BWDR 0x0C /* Bus Width Register */
+#define AW_MMC_BKSR 0x10 /* Block Size Register */
+#define AW_MMC_BYCR 0x14 /* Byte Count Register */
+#define AW_MMC_CMDR 0x18 /* Command Register */
+#define AW_MMC_CAGR 0x1C /* Argument Register */
+#define AW_MMC_RESP0 0x20 /* Response Register 0 */
+#define AW_MMC_RESP1 0x24 /* Response Register 1 */
+#define AW_MMC_RESP2 0x28 /* Response Register 2 */
+#define AW_MMC_RESP3 0x2C /* Response Register 3 */
+#define AW_MMC_IMKR 0x30 /* Interrupt Mask Register */
+#define AW_MMC_MISR 0x34 /* Masked Interrupt Status Register */
+#define AW_MMC_RISR 0x38 /* Raw Interrupt Status Register */
+#define AW_MMC_STAR 0x3C /* Status Register */
+#define AW_MMC_FWLR 0x40 /* FIFO Threshold Watermark Register */
+#define AW_MMC_FUNS 0x44 /* Function Select Register */
+#define AW_MMC_DBGC 0x50 /* Debug register */
+#define AW_MMC_CSDC 0x54 /* CRC status detect controler register (A64 smhc2 only) */
+#define AW_MMC_A12A 0x58 /* Auto command 12 argument register */
+#define AW_MMC_NTSR 0x5C /* SD new timing register (H3, A64 smhc0/1 only) */
+#define AW_MMC_HWRST 0x78 /* Hardware reset */
+#define AW_MMC_DMAC 0x80 /* IDMAC Control Register */
+#define AW_MMC_DLBA 0x84 /* IDMAC Desc List Base Address Reg */
+#define AW_MMC_IDST 0x88 /* IDMAC Status Register */
+#define AW_MMC_IDIE 0x8C /* IDMAC Interrupt Enable Register */
+
+#define AW_MMC_DDR_SBIT_DET 0x10C /* eMMC4.5 DDR Start Bit Detection control register */
+#define AW_MMC_DRV_DL 0x140 /* Drive Delay control register */
+#define AW_MMC_SAMP_DL 0x144 /* Sample Delay controle register */
+#define AW_MMC_DS_DL 0x148 /* Data strobe delay control register */
+
+#define AW_MMC_FIFO 0x100 /* FIFO Access Address (A10/A20) */
+#define A31_MMC_FIFO 0x200 /* FIFO Access Address (A31) */
+
+/* AW_MMC_GCTL */
+#define AW_MMC_GCTL_SOFT_RST (1U << 0)
+#define AW_MMC_GCTL_FIFO_RST (1U << 1)
+#define AW_MMC_GCTL_DMA_RST (1U << 2)
+#define AW_MMC_GCTL_INT_ENB (1U << 4)
+#define AW_MMC_GCTL_DMA_ENB (1U << 5)
+#define AW_MMC_GCTL_CD_DBC_ENB (1U << 8)
+#define AW_MMC_GCTL_DDR_MOD_SEL (1U << 10)
+#define AW_MMC_GCTL_WAIT_MEM_ACCESS (1U << 30)
+#define AW_MMC_GCTL_FIFO_AC_MOD (1U << 31)
+#define AW_MMC_GCTL_RESET \
+ (AW_MMC_GCTL_SOFT_RST | AW_MMC_GCTL_FIFO_RST | AW_MMC_GCTL_DMA_RST)
+
+/* AW_MMC_CKCR */
+#define AW_MMC_CKCR_DIV 0xff
+#define AW_MMC_CKCR_ENB (1U << 16)
+#define AW_MMC_CKCR_LOW_POWER (1U << 17)
+#define AW_MMC_CKCR_MASK_DATA0 (1U << 31)
+
+/* AW_MMC_TMOR */
+#define AW_MMC_TMOR_RTO_LMT_SHIFT(x) x /* Response timeout limit */
+#define AW_MMC_TMOR_RTO_LMT_MASK 0xff
+#define AW_MMC_TMOR_DTO_LMT_SHIFT(x) (x << 8) /* Data timeout limit */
+#define AW_MMC_TMOR_DTO_LMT_MASK 0xffffff
+
+/* AW_MMC_BWDR */
+#define AW_MMC_BWDR1 0
+#define AW_MMC_BWDR4 1
+#define AW_MMC_BWDR8 2
+
+/* AW_MMC_CMDR */
+#define AW_MMC_CMDR_RESP_RCV (1U << 6)
+#define AW_MMC_CMDR_LONG_RESP (1U << 7)
+#define AW_MMC_CMDR_CHK_RESP_CRC (1U << 8)
+#define AW_MMC_CMDR_DATA_TRANS (1U << 9)
+#define AW_MMC_CMDR_DIR_WRITE (1U << 10)
+#define AW_MMC_CMDR_TRANS_MODE_STREAM (1U << 11)
+#define AW_MMC_CMDR_STOP_CMD_FLAG (1U << 12)
+#define AW_MMC_CMDR_WAIT_PRE_OVER (1U << 13)
+#define AW_MMC_CMDR_STOP_ABT_CMD (1U << 14)
+#define AW_MMC_CMDR_SEND_INIT_SEQ (1U << 15)
+#define AW_MMC_CMDR_PRG_CLK (1U << 21)
+#define AW_MMC_CMDR_RD_CEDATA_DEV (1U << 22)
+#define AW_MMC_CMDR_CCS_EXP (1U << 23)
+#define AW_MMC_CMDR_BOOT_MOD_SHIFT 24
+#define AW_MMC_CMDR_BOOT_MOD_NORMAL 0
+#define AW_MMC_CMDR_BOOT_MOD_MANDATORY 1
+#define AW_MMC_CMDR_BOOT_MOD_ALT 2
+#define AW_MMC_CMDR_EXP_BOOT_ACK (1U << 26)
+#define AW_MMC_CMDR_BOOT_ABT (1U << 27)
+#define AW_MMC_CMDR_VOL_SW (1U << 28)
+#define AW_MMC_CMDR_LOAD (1U << 31)
+
+/* AW_MMC_IMKR and AW_MMC_RISR */
+#define AW_MMC_INT_RESP_ERR (1U << 1)
+#define AW_MMC_INT_CMD_DONE (1U << 2)
+#define AW_MMC_INT_DATA_OVER (1U << 3)
+#define AW_MMC_INT_TX_DATA_REQ (1U << 4)
+#define AW_MMC_INT_RX_DATA_REQ (1U << 5)
+#define AW_MMC_INT_RESP_CRC_ERR (1U << 6)
+#define AW_MMC_INT_DATA_CRC_ERR (1U << 7)
+#define AW_MMC_INT_RESP_TIMEOUT (1U << 8)
+#define AW_MMC_INT_BOOT_ACK_RECV (1U << 8)
+#define AW_MMC_INT_DATA_TIMEOUT (1U << 9)
+#define AW_MMC_INT_BOOT_START (1U << 9)
+#define AW_MMC_INT_DATA_STARVE (1U << 10)
+#define AW_MMC_INT_VOL_CHG_DONE (1U << 10)
+#define AW_MMC_INT_FIFO_RUN_ERR (1U << 11)
+#define AW_MMC_INT_CMD_BUSY (1U << 12)
+#define AW_MMC_INT_DATA_START_ERR (1U << 13)
+#define AW_MMC_INT_AUTO_STOP_DONE (1U << 14)
+#define AW_MMC_INT_DATA_END_BIT_ERR (1U << 15)
+#define AW_MMC_INT_SDIO (1U << 16)
+#define AW_MMC_INT_CARD_INSERT (1U << 30)
+#define AW_MMC_INT_CARD_REMOVE (1U << 31)
+#define AW_MMC_INT_ERR_BIT \
+ (AW_MMC_INT_RESP_ERR | AW_MMC_INT_RESP_CRC_ERR | \
+ AW_MMC_INT_DATA_CRC_ERR | AW_MMC_INT_RESP_TIMEOUT | \
+ AW_MMC_INT_FIFO_RUN_ERR | AW_MMC_INT_CMD_BUSY | \
+ AW_MMC_INT_DATA_START_ERR | AW_MMC_INT_DATA_END_BIT_ERR)
+
+/* AW_MMC_STAR */
+#define AW_MMC_STAR_FIFO_RX_LEVEL (1U << 0)
+#define AW_MMC_STAR_FIFO_TX_LEVEL (1U << 1)
+#define AW_MMC_STAR_FIFO_EMPTY (1U << 2)
+#define AW_MMC_STAR_FIFO_FULL (1U << 3)
+#define AW_MMC_STAR_CARD_PRESENT (1U << 8)
+#define AW_MMC_STAR_CARD_BUSY (1U << 9)
+#define AW_MMC_STAR_FSM_BUSY (1U << 10)
+#define AW_MMC_STAR_DMA_REQ (1U << 31)
+
+/* AW_MMC_FUNS */
+#define AW_MMC_CE_ATA_ON (0xceaaU << 16)
+#define AW_MMC_SEND_IRQ_RESP (1U << 0)
+#define AW_MMC_SDIO_RD_WAIT (1U << 1)
+#define AW_MMC_ABT_RD_DATA (1U << 2)
+#define AW_MMC_SEND_CC_SD (1U << 8)
+#define AW_MMC_SEND_AUTOSTOP_CC_SD (1U << 9)
+#define AW_MMC_CE_ATA_DEV_INT_ENB (1U << 10)
+
+/* AW_MMC_NTSR */
+#define AW_MMC_NTSR_MODE_SELECT (1U << 31)
+
+/* IDMA CONTROLLER BUS MOD BIT FIELD */
+#define AW_MMC_DMAC_IDMAC_SOFT_RST (1U << 0)
+#define AW_MMC_DMAC_IDMAC_FIX_BURST (1U << 1)
+#define AW_MMC_DMAC_IDMAC_IDMA_ON (1U << 7)
+#define AW_MMC_DMAC_IDMAC_REFETCH_DES (1U << 31)
+
+/* AW_MMC_IDST */
+#define AW_MMC_IDST_TX_INT (1U << 0)
+#define AW_MMC_IDST_RX_INT (1U << 1)
+#define AW_MMC_IDST_FATAL_BERR_INT (1U << 2)
+#define AW_MMC_IDST_DES_UNAVL_INT (1U << 4)
+#define AW_MMC_IDST_ERR_FLAG_SUM (1U << 5)
+#define AW_MMC_IDST_NOR_INT_SUM (1U << 8)
+#define AW_MMC_IDST_ABN_INT_SUM (1U << 9)
+#define AW_MMC_IDST_HOST_ABT_INTX (1U << 10)
+#define AW_MMC_IDST_HOST_ABT_INRX (1U << 10)
+#define AW_MMC_IDST_IDLE (0U << 13)
+#define AW_MMC_IDST_SUSPEND (1U << 13)
+#define AW_MMC_IDST_DESC_RD (2U << 13)
+#define AW_MMC_IDST_DESC_CHECK (3U << 13)
+#define AW_MMC_IDST_RD_REQ_WAIT (4U << 13)
+#define AW_MMC_IDST_WR_REQ_WAIT (5U << 13)
+#define AW_MMC_IDST_RD (6U << 13)
+#define AW_MMC_IDST_WR (7U << 13)
+#define AW_MMC_IDST_DESC_CLOSE (8U << 13)
+#define AW_MMC_IDST_ERROR \
+ (AW_MMC_IDST_FATAL_BERR_INT | AW_MMC_IDST_ERR_FLAG_SUM | \
+ AW_MMC_IDST_DES_UNAVL_INT | AW_MMC_IDST_ABN_INT_SUM)
+#define AW_MMC_IDST_COMPLETE \
+ (AW_MMC_IDST_TX_INT | AW_MMC_IDST_RX_INT)
+
+/* AW_MMC_DDR_SBIT_DET */
+#define AW_MMC_DDR_SBIT_HS_MD_EN (1U << 31)
+
+/* AW_MMC_SAMP */
+#define AW_MMC_SAMP_DL_SW_EN (1U << 7)
+
+/* The DMA descriptor table. */
+struct aw_mmc_dma_desc {
+ uint32_t config;
+#define AW_MMC_DMA_CONFIG_DIC (1U << 1) /* Disable Interrupt Completion */
+#define AW_MMC_DMA_CONFIG_LD (1U << 2) /* Last DES */
+#define AW_MMC_DMA_CONFIG_FD (1U << 3) /* First DES */
+#define AW_MMC_DMA_CONFIG_CH (1U << 4) /* CHAIN MOD */
+#define AW_MMC_DMA_CONFIG_ER (1U << 5) /* End of Ring (undocumented register) */
+#define AW_MMC_DMA_CONFIG_CES (1U << 30) /* Card Error Summary */
+#define AW_MMC_DMA_CONFIG_OWN (1U << 31) /* DES Own Flag */
+ uint32_t buf_size;
+ uint32_t buf_addr;
+ uint32_t next;
+};
+
+#define AW_MMC_DMA_ALIGN 4
+
+#endif /* _AW_MMC_H_ */
diff --git a/sys/arm/allwinner/aw_mp.c b/sys/arm/allwinner/aw_mp.c
new file mode 100644
index 000000000000..df798443a44a
--- /dev/null
+++ b/sys/arm/allwinner/aw_mp.c
@@ -0,0 +1,288 @@
+/*-
+ * Copyright (c) 2014 Ganbold Tsagaankhuu <ganbold@freebsd.org>
+ * Copyright (c) 2016 Emmanuel Vadot <manu@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/smp.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/cpu.h>
+#include <machine/cpu-v6.h>
+#include <machine/smp.h>
+#include <machine/fdt.h>
+#include <machine/intr.h>
+#include <machine/platformvar.h>
+
+#include <arm/allwinner/aw_mp.h>
+#include <arm/allwinner/aw_machdep.h>
+
+/* Register for all dual-core SoC */
+#define A20_CPUCFG_BASE 0x01c25c00
+/* Register for all quad-core SoC */
+#define CPUCFG_BASE 0x01f01c00
+#define CPUCFG_SIZE 0x400
+#define PRCM_BASE 0x01f01400
+#define PRCM_SIZE 0x800
+/* Register for multi-cluster SoC */
+#define CPUXCFG_BASE 0x01700000
+#define CPUXCFG_SIZE 0x400
+
+#define CPU_OFFSET 0x40
+#define CPU_OFFSET_CTL 0x04
+#define CPU_OFFSET_STATUS 0x08
+#define CPU_RST_CTL(cpuid) ((cpuid + 1) * CPU_OFFSET)
+#define CPU_CTL(cpuid) (((cpuid + 1) * CPU_OFFSET) + CPU_OFFSET_CTL)
+#define CPU_STATUS(cpuid) (((cpuid + 1) * CPU_OFFSET) + CPU_OFFSET_STATUS)
+
+#define CPU_RESET (1 << 0)
+#define CPU_CORE_RESET (1 << 1)
+
+#define CPUCFG_GENCTL 0x184
+#define CPUCFG_P_REG0 0x1a4
+
+#define A20_CPU1_PWR_CLAMP 0x1b0
+#define CPU_PWR_CLAMP_REG 0x140
+#define CPU_PWR_CLAMP(cpu) ((cpu * 4) + CPU_PWR_CLAMP_REG)
+#define CPU_PWR_CLAMP_STEPS 8
+
+#define A20_CPU1_PWROFF_REG 0x1b4
+#define CPU_PWROFF 0x100
+
+#define CPUCFG_DBGCTL0 0x1e0
+#define CPUCFG_DBGCTL1 0x1e4
+
+#define CPUS_CL_RST(cl) (0x30 + (cl) * 0x4)
+#define CPUX_CL_CTRL0(cl) (0x0 + (cl) * 0x10)
+#define CPUX_CL_CTRL1(cl) (0x4 + (cl) * 0x10)
+#define CPUX_CL_CPU_STATUS(cl) (0x30 + (cl) * 0x4)
+#define CPUX_CL_RST(cl) (0x80 + (cl) * 0x4)
+#define PRCM_CL_PWROFF(cl) (0x100 + (cl) * 0x4)
+#define PRCM_CL_PWR_CLAMP(cl, cpu) (0x140 + (cl) * 0x4 + (cpu) * 0x4)
+
+void
+aw_mp_setmaxid(platform_t plat)
+{
+ int ncpu;
+ uint32_t reg;
+
+ if (mp_ncpus != 0)
+ return;
+
+ reg = cp15_l2ctlr_get();
+ ncpu = CPUV7_L2CTLR_NPROC(reg);
+
+ mp_ncpus = ncpu;
+ mp_maxid = ncpu - 1;
+}
+
+void
+aw_mp_start_ap(platform_t plat)
+{
+ bus_space_handle_t cpucfg;
+ bus_space_handle_t prcm;
+ int i, j, soc_family;
+ uint32_t val;
+
+ soc_family = allwinner_soc_family();
+ if (soc_family == ALLWINNERSOC_SUN7I) {
+ if (bus_space_map(fdtbus_bs_tag, A20_CPUCFG_BASE, CPUCFG_SIZE,
+ 0, &cpucfg) != 0)
+ panic("Couldn't map the CPUCFG\n");
+ } else {
+ if (bus_space_map(fdtbus_bs_tag, CPUCFG_BASE, CPUCFG_SIZE,
+ 0, &cpucfg) != 0)
+ panic("Couldn't map the CPUCFG\n");
+ if (bus_space_map(fdtbus_bs_tag, PRCM_BASE, PRCM_SIZE, 0,
+ &prcm) != 0)
+ panic("Couldn't map the PRCM\n");
+ }
+
+ dcache_wbinv_poc_all();
+
+ bus_space_write_4(fdtbus_bs_tag, cpucfg, CPUCFG_P_REG0,
+ pmap_kextract((vm_offset_t)mpentry));
+
+ /*
+ * Assert nCOREPORESET low and set L1RSTDISABLE low.
+ * Ensure DBGPWRDUP is set to LOW to prevent any external
+ * debug access to the processor.
+ */
+ for (i = 1; i < mp_ncpus; i++)
+ bus_space_write_4(fdtbus_bs_tag, cpucfg, CPU_RST_CTL(i), 0);
+
+ /* Set L1RSTDISABLE low */
+ val = bus_space_read_4(fdtbus_bs_tag, cpucfg, CPUCFG_GENCTL);
+ for (i = 1; i < mp_ncpus; i++)
+ val &= ~(1 << i);
+ bus_space_write_4(fdtbus_bs_tag, cpucfg, CPUCFG_GENCTL, val);
+
+ /* Set DBGPWRDUP low */
+ val = bus_space_read_4(fdtbus_bs_tag, cpucfg, CPUCFG_DBGCTL1);
+ for (i = 1; i < mp_ncpus; i++)
+ val &= ~(1 << i);
+ bus_space_write_4(fdtbus_bs_tag, cpucfg, CPUCFG_DBGCTL1, val);
+
+ /* Release power clamp */
+ for (i = 1; i < mp_ncpus; i++)
+ for (j = 0; j <= CPU_PWR_CLAMP_STEPS; j++) {
+ if (soc_family != ALLWINNERSOC_SUN7I) {
+ bus_space_write_4(fdtbus_bs_tag, prcm,
+ CPU_PWR_CLAMP(i), 0xff >> j);
+ } else {
+ bus_space_write_4(fdtbus_bs_tag,
+ cpucfg, A20_CPU1_PWR_CLAMP, 0xff >> j);
+ }
+ }
+ DELAY(10000);
+
+ /* Clear power-off gating */
+ if (soc_family != ALLWINNERSOC_SUN7I) {
+ val = bus_space_read_4(fdtbus_bs_tag, prcm, CPU_PWROFF);
+ for (i = 0; i < mp_ncpus; i++)
+ val &= ~(1 << i);
+ bus_space_write_4(fdtbus_bs_tag, prcm, CPU_PWROFF, val);
+ } else {
+ val = bus_space_read_4(fdtbus_bs_tag,
+ cpucfg, A20_CPU1_PWROFF_REG);
+ val &= ~(1 << 0);
+ bus_space_write_4(fdtbus_bs_tag, cpucfg,
+ A20_CPU1_PWROFF_REG, val);
+ }
+ DELAY(1000);
+
+ /* De-assert cpu core reset */
+ for (i = 1; i < mp_ncpus; i++)
+ bus_space_write_4(fdtbus_bs_tag, cpucfg, CPU_RST_CTL(i),
+ CPU_RESET | CPU_CORE_RESET);
+
+ /* Assert DBGPWRDUP signal */
+ val = bus_space_read_4(fdtbus_bs_tag, cpucfg, CPUCFG_DBGCTL1);
+ for (i = 1; i < mp_ncpus; i++)
+ val |= (1 << i);
+ bus_space_write_4(fdtbus_bs_tag, cpucfg, CPUCFG_DBGCTL1, val);
+
+ dsb();
+ sev();
+ bus_space_unmap(fdtbus_bs_tag, cpucfg, CPUCFG_SIZE);
+ if (soc_family != ALLWINNERSOC_SUN7I)
+ bus_space_unmap(fdtbus_bs_tag, prcm, PRCM_SIZE);
+}
+
+static void
+aw_mc_mp_start_cpu(bus_space_handle_t cpuscfg, bus_space_handle_t cpuxcfg,
+ bus_space_handle_t prcm, int cluster, int cpu)
+{
+ uint32_t val;
+ int i;
+
+ /* Assert core reset */
+ val = bus_space_read_4(fdtbus_bs_tag, cpuxcfg, CPUX_CL_RST(cluster));
+ val &= ~(1 << cpu);
+ bus_space_write_4(fdtbus_bs_tag, cpuxcfg, CPUX_CL_RST(cluster), val);
+
+ /* Assert power-on reset */
+ val = bus_space_read_4(fdtbus_bs_tag, cpuscfg, CPUS_CL_RST(cluster));
+ val &= ~(1 << cpu);
+ bus_space_write_4(fdtbus_bs_tag, cpuscfg, CPUS_CL_RST(cluster), val);
+
+ /* Disable automatic L1 cache invalidate at reset */
+ val = bus_space_read_4(fdtbus_bs_tag, cpuxcfg, CPUX_CL_CTRL0(cluster));
+ val &= ~(1 << cpu);
+ bus_space_write_4(fdtbus_bs_tag, cpuxcfg, CPUX_CL_CTRL0(cluster), val);
+
+ /* Release power clamp */
+ for (i = 0; i <= CPU_PWR_CLAMP_STEPS; i++)
+ bus_space_write_4(fdtbus_bs_tag, prcm,
+ PRCM_CL_PWR_CLAMP(cluster, cpu), 0xff >> i);
+ while (bus_space_read_4(fdtbus_bs_tag, prcm,
+ PRCM_CL_PWR_CLAMP(cluster, cpu)) != 0)
+ ;
+
+ /* Clear power-off gating */
+ val = bus_space_read_4(fdtbus_bs_tag, prcm, PRCM_CL_PWROFF(cluster));
+ val &= ~(1 << cpu);
+ bus_space_write_4(fdtbus_bs_tag, prcm, PRCM_CL_PWROFF(cluster), val);
+
+ /* De-assert power-on reset */
+ val = bus_space_read_4(fdtbus_bs_tag, cpuscfg, CPUS_CL_RST(cluster));
+ val |= (1 << cpu);
+ bus_space_write_4(fdtbus_bs_tag, cpuscfg, CPUS_CL_RST(cluster), val);
+
+ /* De-assert core reset */
+ val = bus_space_read_4(fdtbus_bs_tag, cpuxcfg, CPUX_CL_RST(cluster));
+ val |= (1 << cpu);
+ bus_space_write_4(fdtbus_bs_tag, cpuxcfg, CPUX_CL_RST(cluster), val);
+}
+
+static void
+aw_mc_mp_start_ap(bus_space_handle_t cpuscfg, bus_space_handle_t cpuxcfg,
+ bus_space_handle_t prcm)
+{
+ int cluster, cpu;
+
+ KASSERT(mp_ncpus <= 4, ("multiple clusters not yet supported"));
+
+ dcache_wbinv_poc_all();
+
+ bus_space_write_4(fdtbus_bs_tag, cpuscfg, CPUCFG_P_REG0,
+ pmap_kextract((vm_offset_t)mpentry));
+
+ cluster = 0;
+ for (cpu = 1; cpu < mp_ncpus; cpu++)
+ aw_mc_mp_start_cpu(cpuscfg, cpuxcfg, prcm, cluster, cpu);
+}
+
+void
+a83t_mp_start_ap(platform_t plat)
+{
+ bus_space_handle_t cpuscfg, cpuxcfg, prcm;
+
+ if (bus_space_map(fdtbus_bs_tag, CPUCFG_BASE, CPUCFG_SIZE,
+ 0, &cpuscfg) != 0)
+ panic("Couldn't map the CPUCFG\n");
+ if (bus_space_map(fdtbus_bs_tag, CPUXCFG_BASE, CPUXCFG_SIZE,
+ 0, &cpuxcfg) != 0)
+ panic("Couldn't map the CPUXCFG\n");
+ if (bus_space_map(fdtbus_bs_tag, PRCM_BASE, PRCM_SIZE, 0,
+ &prcm) != 0)
+ panic("Couldn't map the PRCM\n");
+
+ aw_mc_mp_start_ap(cpuscfg, cpuxcfg, prcm);
+ dsb();
+ sev();
+ bus_space_unmap(fdtbus_bs_tag, cpuxcfg, CPUXCFG_SIZE);
+ bus_space_unmap(fdtbus_bs_tag, cpuscfg, CPUCFG_SIZE);
+ bus_space_unmap(fdtbus_bs_tag, prcm, PRCM_SIZE);
+}
diff --git a/sys/arm/allwinner/aw_mp.h b/sys/arm/allwinner/aw_mp.h
new file mode 100644
index 000000000000..c86bd08087c3
--- /dev/null
+++ b/sys/arm/allwinner/aw_mp.h
@@ -0,0 +1,34 @@
+/*-
+ * Copyright (c) 2016 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _AW_MP_H_
+#define _AW_MP_H_
+
+void aw_mp_setmaxid(platform_t plat);
+void aw_mp_start_ap(platform_t plat);
+void a83t_mp_start_ap(platform_t plat);
+
+#endif /* _AW_MP_H_ */
diff --git a/sys/arm/allwinner/aw_nmi.c b/sys/arm/allwinner/aw_nmi.c
new file mode 100644
index 000000000000..d242726c29b9
--- /dev/null
+++ b/sys/arm/allwinner/aw_nmi.c
@@ -0,0 +1,420 @@
+/*-
+ * Copyright (c) 2016 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/proc.h>
+#include <sys/rman.h>
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <dev/fdt/fdt_intr.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "pic_if.h"
+
+#define NMI_IRQ_CTRL_REG 0x0
+#define NMI_IRQ_LOW_LEVEL 0x0
+#define NMI_IRQ_LOW_EDGE 0x1
+#define NMI_IRQ_HIGH_LEVEL 0x2
+#define NMI_IRQ_HIGH_EDGE 0x3
+#define NMI_IRQ_PENDING_REG 0x4
+#define NMI_IRQ_ACK (1U << 0)
+#define A20_NMI_IRQ_ENABLE_REG 0x8
+#define A31_NMI_IRQ_ENABLE_REG 0x34
+#define NMI_IRQ_ENABLE (1U << 0)
+
+#define R_NMI_IRQ_CTRL_REG 0x0c
+#define R_NMI_IRQ_PENDING_REG 0x10
+#define R_NMI_IRQ_ENABLE_REG 0x40
+
+#define SC_NMI_READ(_sc, _reg) bus_read_4(_sc->res[0], _reg)
+#define SC_NMI_WRITE(_sc, _reg, _val) bus_write_4(_sc->res[0], _reg, _val)
+
+static struct resource_spec aw_nmi_res_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE },
+ { -1, 0, 0 }
+};
+
+struct aw_nmi_intr {
+ struct intr_irqsrc isrc;
+ u_int irq;
+ enum intr_polarity pol;
+ enum intr_trigger tri;
+};
+
+struct aw_nmi_reg_cfg {
+ uint8_t ctrl_reg;
+ uint8_t pending_reg;
+ uint8_t enable_reg;
+};
+
+struct aw_nmi_softc {
+ device_t dev;
+ struct resource * res[2];
+ void * intrcookie;
+ struct aw_nmi_intr intr;
+ struct aw_nmi_reg_cfg * cfg;
+};
+
+static struct aw_nmi_reg_cfg a20_nmi_cfg = {
+ .ctrl_reg = NMI_IRQ_CTRL_REG,
+ .pending_reg = NMI_IRQ_PENDING_REG,
+ .enable_reg = A20_NMI_IRQ_ENABLE_REG,
+};
+
+static struct aw_nmi_reg_cfg a31_nmi_cfg = {
+ .ctrl_reg = NMI_IRQ_CTRL_REG,
+ .pending_reg = NMI_IRQ_PENDING_REG,
+ .enable_reg = A31_NMI_IRQ_ENABLE_REG,
+};
+
+static struct aw_nmi_reg_cfg a83t_r_nmi_cfg = {
+ .ctrl_reg = R_NMI_IRQ_CTRL_REG,
+ .pending_reg = R_NMI_IRQ_PENDING_REG,
+ .enable_reg = R_NMI_IRQ_ENABLE_REG,
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"allwinner,sun7i-a20-sc-nmi", (uintptr_t)&a20_nmi_cfg},
+ {"allwinner,sun6i-a31-sc-nmi", (uintptr_t)&a31_nmi_cfg},
+ {"allwinner,sun6i-a31-r-intc", (uintptr_t)&a83t_r_nmi_cfg},
+ {"allwinner,sun8i-a83t-r-intc", (uintptr_t)&a83t_r_nmi_cfg},
+ {NULL, 0},
+};
+
+static int
+aw_nmi_intr(void *arg)
+{
+ struct aw_nmi_softc *sc;
+
+ sc = arg;
+
+ if (SC_NMI_READ(sc, sc->cfg->pending_reg) == 0) {
+ device_printf(sc->dev, "Spurious interrupt\n");
+ return (FILTER_HANDLED);
+ }
+
+ if (intr_isrc_dispatch(&sc->intr.isrc, curthread->td_intr_frame) != 0) {
+ SC_NMI_WRITE(sc, sc->cfg->enable_reg, ~NMI_IRQ_ENABLE);
+ device_printf(sc->dev, "Stray interrupt, NMI disabled\n");
+ }
+
+ return (FILTER_HANDLED);
+}
+
+static void
+aw_nmi_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct aw_nmi_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ SC_NMI_WRITE(sc, sc->cfg->enable_reg, NMI_IRQ_ENABLE);
+}
+
+static void
+aw_nmi_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct aw_nmi_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ SC_NMI_WRITE(sc, sc->cfg->enable_reg, ~NMI_IRQ_ENABLE);
+}
+
+static int
+aw_nmi_map_fdt(device_t dev, u_int ncells, pcell_t *cells, u_int *irqp,
+ enum intr_polarity *polp, enum intr_trigger *trigp)
+{
+ u_int irq, tripol;
+ enum intr_polarity pol;
+ enum intr_trigger trig;
+
+ if (ncells != 2) {
+ device_printf(dev, "Invalid #interrupt-cells\n");
+ return (EINVAL);
+ }
+
+ irq = cells[0];
+ if (irq != 0) {
+ device_printf(dev, "Controller only support irq 0\n");
+ return (EINVAL);
+ }
+
+ tripol = cells[1];
+
+ switch (tripol) {
+ case FDT_INTR_EDGE_RISING:
+ trig = INTR_TRIGGER_EDGE;
+ pol = INTR_POLARITY_HIGH;
+ break;
+ case FDT_INTR_EDGE_FALLING:
+ trig = INTR_TRIGGER_EDGE;
+ pol = INTR_POLARITY_LOW;
+ break;
+ case FDT_INTR_LEVEL_HIGH:
+ trig = INTR_TRIGGER_LEVEL;
+ pol = INTR_POLARITY_HIGH;
+ break;
+ case FDT_INTR_LEVEL_LOW:
+ trig = INTR_TRIGGER_LEVEL;
+ pol = INTR_POLARITY_LOW;
+ break;
+ default:
+ device_printf(dev, "unsupported trigger/polarity 0x%2x\n",
+ tripol);
+ return (ENOTSUP);
+ }
+
+ *irqp = irq;
+ if (polp != NULL)
+ *polp = pol;
+ if (trigp != NULL)
+ *trigp = trig;
+ return (0);
+}
+
+static int
+aw_nmi_map_intr(device_t dev, struct intr_map_data *data,
+ struct intr_irqsrc **isrcp)
+{
+ struct intr_map_data_fdt *daf;
+ struct aw_nmi_softc *sc;
+ int error;
+ u_int irq;
+
+ if (data->type != INTR_MAP_DATA_FDT)
+ return (ENOTSUP);
+
+ sc = device_get_softc(dev);
+ daf = (struct intr_map_data_fdt *)data;
+
+ error = aw_nmi_map_fdt(dev, daf->ncells, daf->cells, &irq, NULL, NULL);
+ if (error == 0)
+ *isrcp = &sc->intr.isrc;
+
+ return (error);
+}
+
+static int
+aw_nmi_setup_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ struct intr_map_data_fdt *daf;
+ struct aw_nmi_softc *sc;
+ struct aw_nmi_intr *nmi_intr;
+ int error, icfg;
+ u_int irq;
+ enum intr_trigger trig;
+ enum intr_polarity pol;
+
+ /* Get config for interrupt. */
+ if (data == NULL || data->type != INTR_MAP_DATA_FDT)
+ return (ENOTSUP);
+
+ sc = device_get_softc(dev);
+ nmi_intr = (struct aw_nmi_intr *)isrc;
+ daf = (struct intr_map_data_fdt *)data;
+
+ error = aw_nmi_map_fdt(dev, daf->ncells, daf->cells, &irq, &pol, &trig);
+ if (error != 0)
+ return (error);
+ if (nmi_intr->irq != irq)
+ return (EINVAL);
+
+ /* Compare config if this is not first setup. */
+ if (isrc->isrc_handlers != 0) {
+ if (pol != nmi_intr->pol || trig != nmi_intr->tri)
+ return (EINVAL);
+ else
+ return (0);
+ }
+
+ nmi_intr->pol = pol;
+ nmi_intr->tri = trig;
+
+ if (trig == INTR_TRIGGER_LEVEL) {
+ if (pol == INTR_POLARITY_LOW)
+ icfg = NMI_IRQ_LOW_LEVEL;
+ else
+ icfg = NMI_IRQ_HIGH_LEVEL;
+ } else {
+ if (pol == INTR_POLARITY_HIGH)
+ icfg = NMI_IRQ_HIGH_EDGE;
+ else
+ icfg = NMI_IRQ_LOW_EDGE;
+ }
+
+ SC_NMI_WRITE(sc, sc->cfg->ctrl_reg, icfg);
+
+ return (0);
+}
+
+static int
+aw_nmi_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ struct aw_nmi_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (isrc->isrc_handlers == 0) {
+ sc->intr.pol = INTR_POLARITY_CONFORM;
+ sc->intr.tri = INTR_TRIGGER_CONFORM;
+
+ SC_NMI_WRITE(sc, sc->cfg->enable_reg, ~NMI_IRQ_ENABLE);
+ }
+
+ return (0);
+}
+
+static void
+aw_nmi_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct aw_nmi_softc *sc;
+
+ sc = device_get_softc(dev);
+ aw_nmi_disable_intr(dev, isrc);
+ SC_NMI_WRITE(sc, sc->cfg->pending_reg, NMI_IRQ_ACK);
+}
+
+static void
+aw_nmi_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+
+ arm_irq_memory_barrier(0);
+ aw_nmi_enable_intr(dev, isrc);
+}
+
+static void
+aw_nmi_post_filter(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct aw_nmi_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ arm_irq_memory_barrier(0);
+ SC_NMI_WRITE(sc, sc->cfg->pending_reg, NMI_IRQ_ACK);
+}
+
+static int
+aw_nmi_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+ device_set_desc(dev, "Allwinner NMI Controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+aw_nmi_attach(device_t dev)
+{
+ struct aw_nmi_softc *sc;
+ phandle_t xref;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ sc->cfg = (struct aw_nmi_reg_cfg *)
+ ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+
+ if (bus_alloc_resources(dev, aw_nmi_res_spec, sc->res) != 0) {
+ device_printf(dev, "can't allocate device resources\n");
+ return (ENXIO);
+ }
+ if ((bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC,
+ aw_nmi_intr, NULL, sc, &sc->intrcookie))) {
+ device_printf(dev, "unable to register interrupt handler\n");
+ bus_release_resources(dev, aw_nmi_res_spec, sc->res);
+ return (ENXIO);
+ }
+
+ /* Disable and clear interrupts */
+ SC_NMI_WRITE(sc, sc->cfg->enable_reg, ~NMI_IRQ_ENABLE);
+ SC_NMI_WRITE(sc, sc->cfg->pending_reg, NMI_IRQ_ACK);
+
+ xref = OF_xref_from_node(ofw_bus_get_node(dev));
+ /* Register our isrc */
+ sc->intr.irq = 0;
+ sc->intr.pol = INTR_POLARITY_CONFORM;
+ sc->intr.tri = INTR_TRIGGER_CONFORM;
+ if (intr_isrc_register(&sc->intr.isrc, sc->dev, 0, "%s,%u",
+ device_get_nameunit(sc->dev), sc->intr.irq) != 0)
+ goto error;
+
+ if (intr_pic_register(dev, (intptr_t)xref) == NULL) {
+ device_printf(dev, "could not register pic\n");
+ goto error;
+ }
+ return (0);
+
+error:
+ bus_teardown_intr(dev, sc->res[1], sc->intrcookie);
+ bus_release_resources(dev, aw_nmi_res_spec, sc->res);
+ return (ENXIO);
+}
+
+static device_method_t aw_nmi_methods[] = {
+ DEVMETHOD(device_probe, aw_nmi_probe),
+ DEVMETHOD(device_attach, aw_nmi_attach),
+
+ /* Interrupt controller interface */
+ DEVMETHOD(pic_disable_intr, aw_nmi_disable_intr),
+ DEVMETHOD(pic_enable_intr, aw_nmi_enable_intr),
+ DEVMETHOD(pic_map_intr, aw_nmi_map_intr),
+ DEVMETHOD(pic_setup_intr, aw_nmi_setup_intr),
+ DEVMETHOD(pic_teardown_intr, aw_nmi_teardown_intr),
+ DEVMETHOD(pic_post_filter, aw_nmi_post_filter),
+ DEVMETHOD(pic_post_ithread, aw_nmi_post_ithread),
+ DEVMETHOD(pic_pre_ithread, aw_nmi_pre_ithread),
+
+ {0, 0},
+};
+
+static driver_t aw_nmi_driver = {
+ "aw_nmi",
+ aw_nmi_methods,
+ sizeof(struct aw_nmi_softc),
+};
+
+static devclass_t aw_nmi_devclass;
+
+EARLY_DRIVER_MODULE(aw_nmi, simplebus, aw_nmi_driver,
+ aw_nmi_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);
diff --git a/sys/arm/allwinner/aw_pwm.c b/sys/arm/allwinner/aw_pwm.c
new file mode 100644
index 000000000000..a851005115f5
--- /dev/null
+++ b/sys/arm/allwinner/aw_pwm.c
@@ -0,0 +1,405 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2018 Emmanuel Vadot <manu@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/resource.h>
+#include <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/extres/clk/clk.h>
+
+#include "pwmbus_if.h"
+
+#define AW_PWM_CTRL 0x00
+#define AW_PWM_CTRL_PRESCALE_MASK 0xF
+#define AW_PWM_CTRL_EN (1 << 4)
+#define AW_PWM_CTRL_ACTIVE_LEVEL_HIGH (1 << 5)
+#define AW_PWM_CTRL_GATE (1 << 6)
+#define AW_PWM_CTRL_MODE_MASK 0x80
+#define AW_PWM_CTRL_PULSE_MODE (1 << 7)
+#define AW_PWM_CTRL_CYCLE_MODE (0 << 7)
+#define AW_PWM_CTRL_PULSE_START (1 << 8)
+#define AW_PWM_CTRL_CLK_BYPASS (1 << 9)
+#define AW_PWM_CTRL_PERIOD_BUSY (1 << 28)
+
+#define AW_PWM_PERIOD 0x04
+#define AW_PWM_PERIOD_TOTAL_MASK 0xFFFF
+#define AW_PWM_PERIOD_TOTAL_SHIFT 16
+#define AW_PWM_PERIOD_ACTIVE_MASK 0xFFFF
+#define AW_PWM_PERIOD_ACTIVE_SHIFT 0
+
+#define AW_PWM_MAX_FREQ 24000000
+
+#define NS_PER_SEC 1000000000
+
+static struct ofw_compat_data compat_data[] = {
+ { "allwinner,sun5i-a13-pwm", 1 },
+ { "allwinner,sun8i-h3-pwm", 1 },
+ { NULL, 0 }
+};
+
+static struct resource_spec aw_pwm_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+struct aw_pwm_softc {
+ device_t dev;
+ device_t busdev;
+ clk_t clk;
+ struct resource *res;
+
+ uint64_t clk_freq;
+ unsigned int period;
+ unsigned int duty;
+ uint32_t flags;
+ bool enabled;
+};
+
+static uint32_t aw_pwm_clk_prescaler[] = {
+ 120,
+ 180,
+ 240,
+ 360,
+ 480,
+ 0,
+ 0,
+ 0,
+ 12000,
+ 24000,
+ 36000,
+ 48000,
+ 72000,
+ 0,
+ 0,
+ 1,
+};
+
+#define AW_PWM_READ(sc, reg) bus_read_4((sc)->res, (reg))
+#define AW_PWM_WRITE(sc, reg, val) bus_write_4((sc)->res, (reg), (val))
+
+static int aw_pwm_probe(device_t dev);
+static int aw_pwm_attach(device_t dev);
+static int aw_pwm_detach(device_t dev);
+
+static int
+aw_pwm_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner PWM");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+aw_pwm_attach(device_t dev)
+{
+ struct aw_pwm_softc *sc;
+ uint64_t clk_freq;
+ uint32_t reg;
+ phandle_t node;
+ int error;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ error = clk_get_by_ofw_index(dev, 0, 0, &sc->clk);
+ if (error != 0) {
+ device_printf(dev, "cannot get clock\n");
+ goto fail;
+ }
+ error = clk_enable(sc->clk);
+ if (error != 0) {
+ device_printf(dev, "cannot enable clock\n");
+ goto fail;
+ }
+
+ error = clk_get_freq(sc->clk, &sc->clk_freq);
+ if (error != 0) {
+ device_printf(dev, "cannot get clock frequency\n");
+ goto fail;
+ }
+
+ if (bus_alloc_resources(dev, aw_pwm_spec, &sc->res) != 0) {
+ device_printf(dev, "cannot allocate resources for device\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ /* Read the configuration left by U-Boot */
+ reg = AW_PWM_READ(sc, AW_PWM_CTRL);
+ if (reg & (AW_PWM_CTRL_GATE | AW_PWM_CTRL_EN))
+ sc->enabled = true;
+
+ reg = AW_PWM_READ(sc, AW_PWM_CTRL);
+ reg &= AW_PWM_CTRL_PRESCALE_MASK;
+ if (reg > nitems(aw_pwm_clk_prescaler)) {
+ device_printf(dev, "Bad prescaler %x, cannot guess current settings\n", reg);
+ goto skipcfg;
+ }
+ clk_freq = sc->clk_freq / aw_pwm_clk_prescaler[reg];
+
+ reg = AW_PWM_READ(sc, AW_PWM_PERIOD);
+ sc->period = NS_PER_SEC /
+ (clk_freq / ((reg >> AW_PWM_PERIOD_TOTAL_SHIFT) & AW_PWM_PERIOD_TOTAL_MASK));
+ sc->duty = NS_PER_SEC /
+ (clk_freq / ((reg >> AW_PWM_PERIOD_ACTIVE_SHIFT) & AW_PWM_PERIOD_ACTIVE_MASK));
+
+skipcfg:
+ /*
+ * Note that we don't check for failure to attach pwmbus -- even without
+ * it we can still service clients who connect via fdt xref data.
+ */
+ node = ofw_bus_get_node(dev);
+ OF_device_register_xref(OF_xref_from_node(node), dev);
+
+ sc->busdev = device_add_child(dev, "pwmbus", -1);
+
+ return (bus_generic_attach(dev));
+
+fail:
+ aw_pwm_detach(dev);
+ return (error);
+}
+
+static int
+aw_pwm_detach(device_t dev)
+{
+ struct aw_pwm_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ if ((error = bus_generic_detach(sc->dev)) != 0) {
+ device_printf(sc->dev, "cannot detach child devices\n");
+ return (error);
+ }
+
+ if (sc->busdev != NULL)
+ device_delete_child(dev, sc->busdev);
+
+ if (sc->res != NULL)
+ bus_release_resources(dev, aw_pwm_spec, &sc->res);
+
+ return (0);
+}
+
+static phandle_t
+aw_pwm_get_node(device_t bus, device_t dev)
+{
+
+ /*
+ * Share our controller node with our pwmbus child; it instantiates
+ * devices by walking the children contained within our node.
+ */
+ return ofw_bus_get_node(bus);
+}
+
+static int
+aw_pwm_channel_count(device_t dev, u_int *nchannel)
+{
+
+ *nchannel = 1;
+
+ return (0);
+}
+
+static int
+aw_pwm_channel_config(device_t dev, u_int channel, u_int period, u_int duty)
+{
+ struct aw_pwm_softc *sc;
+ uint64_t period_freq, duty_freq;
+ uint64_t clk_rate, div;
+ uint32_t reg;
+ int prescaler;
+ int i;
+
+ sc = device_get_softc(dev);
+
+ period_freq = NS_PER_SEC / period;
+ if (period_freq > AW_PWM_MAX_FREQ)
+ return (EINVAL);
+
+ /*
+ * FIXME. The hardware is capable of sub-Hz frequencies, that is,
+ * periods longer than a second. But the current code cannot deal
+ * with those properly.
+ */
+ if (period_freq == 0)
+ return (EINVAL);
+
+ /*
+ * FIXME. There is a great loss of precision when the period and the
+ * duty are near 1 second. In some cases period_freq and duty_freq can
+ * be equal even if the period and the duty are significantly different.
+ */
+ duty_freq = NS_PER_SEC / duty;
+ if (duty_freq < period_freq) {
+ device_printf(sc->dev, "duty < period\n");
+ return (EINVAL);
+ }
+
+ /* First test without prescaler */
+ clk_rate = AW_PWM_MAX_FREQ;
+ prescaler = AW_PWM_CTRL_PRESCALE_MASK;
+ div = AW_PWM_MAX_FREQ / period_freq;
+ if ((div - 1) > AW_PWM_PERIOD_TOTAL_MASK) {
+ /* Test all prescaler */
+ for (i = 0; i < nitems(aw_pwm_clk_prescaler); i++) {
+ if (aw_pwm_clk_prescaler[i] == 0)
+ continue;
+ div = AW_PWM_MAX_FREQ / aw_pwm_clk_prescaler[i] / period_freq;
+ if ((div - 1) < AW_PWM_PERIOD_TOTAL_MASK ) {
+ prescaler = i;
+ clk_rate = AW_PWM_MAX_FREQ / aw_pwm_clk_prescaler[i];
+ break;
+ }
+ }
+ if (prescaler == AW_PWM_CTRL_PRESCALE_MASK)
+ return (EINVAL);
+ }
+
+ reg = AW_PWM_READ(sc, AW_PWM_CTRL);
+
+ /* Write the prescalar */
+ reg &= ~AW_PWM_CTRL_PRESCALE_MASK;
+ reg |= prescaler;
+
+ reg &= ~AW_PWM_CTRL_MODE_MASK;
+ reg |= AW_PWM_CTRL_CYCLE_MODE;
+
+ reg &= ~AW_PWM_CTRL_PULSE_START;
+ reg &= ~AW_PWM_CTRL_CLK_BYPASS;
+
+ AW_PWM_WRITE(sc, AW_PWM_CTRL, reg);
+
+ /* Write the total/active cycles */
+ reg = ((clk_rate / period_freq - 1) << AW_PWM_PERIOD_TOTAL_SHIFT) |
+ ((clk_rate / duty_freq) << AW_PWM_PERIOD_ACTIVE_SHIFT);
+ AW_PWM_WRITE(sc, AW_PWM_PERIOD, reg);
+
+ sc->period = period;
+ sc->duty = duty;
+
+ return (0);
+}
+
+static int
+aw_pwm_channel_get_config(device_t dev, u_int channel, u_int *period, u_int *duty)
+{
+ struct aw_pwm_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ *period = sc->period;
+ *duty = sc->duty;
+
+ return (0);
+}
+
+static int
+aw_pwm_channel_enable(device_t dev, u_int channel, bool enable)
+{
+ struct aw_pwm_softc *sc;
+ uint32_t reg;
+
+ sc = device_get_softc(dev);
+
+ if (enable && sc->enabled)
+ return (0);
+
+ reg = AW_PWM_READ(sc, AW_PWM_CTRL);
+ if (enable)
+ reg |= AW_PWM_CTRL_GATE | AW_PWM_CTRL_EN;
+ else
+ reg &= ~(AW_PWM_CTRL_GATE | AW_PWM_CTRL_EN);
+
+ AW_PWM_WRITE(sc, AW_PWM_CTRL, reg);
+
+ sc->enabled = enable;
+
+ return (0);
+}
+
+static int
+aw_pwm_channel_is_enabled(device_t dev, u_int channel, bool *enabled)
+{
+ struct aw_pwm_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ *enabled = sc->enabled;
+
+ return (0);
+}
+
+static device_method_t aw_pwm_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, aw_pwm_probe),
+ DEVMETHOD(device_attach, aw_pwm_attach),
+ DEVMETHOD(device_detach, aw_pwm_detach),
+
+ /* ofw_bus interface */
+ DEVMETHOD(ofw_bus_get_node, aw_pwm_get_node),
+
+ /* pwmbus interface */
+ DEVMETHOD(pwmbus_channel_count, aw_pwm_channel_count),
+ DEVMETHOD(pwmbus_channel_config, aw_pwm_channel_config),
+ DEVMETHOD(pwmbus_channel_get_config, aw_pwm_channel_get_config),
+ DEVMETHOD(pwmbus_channel_enable, aw_pwm_channel_enable),
+ DEVMETHOD(pwmbus_channel_is_enabled, aw_pwm_channel_is_enabled),
+
+ DEVMETHOD_END
+};
+
+static driver_t aw_pwm_driver = {
+ "pwm",
+ aw_pwm_methods,
+ sizeof(struct aw_pwm_softc),
+};
+
+static devclass_t aw_pwm_devclass;
+
+DRIVER_MODULE(aw_pwm, simplebus, aw_pwm_driver, aw_pwm_devclass, 0, 0);
+MODULE_VERSION(aw_pwm, 1);
+SIMPLEBUS_PNP_INFO(compat_data);
diff --git a/sys/arm/allwinner/aw_reset.c b/sys/arm/allwinner/aw_reset.c
new file mode 100644
index 000000000000..39fbe53f2404
--- /dev/null
+++ b/sys/arm/allwinner/aw_reset.c
@@ -0,0 +1,165 @@
+/*-
+ * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Allwinner module software reset registers
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/extres/hwreset/hwreset.h>
+
+#include "hwreset_if.h"
+
+#define RESET_OFFSET(index) ((index / 32) * 4)
+#define RESET_SHIFT(index) (index % 32)
+
+static struct ofw_compat_data compat_data[] = {
+ { "allwinner,sun6i-a31-ahb1-reset", 1 },
+ { "allwinner,sun6i-a31-clock-reset", 1 },
+ { NULL, 0 }
+};
+
+struct aw_reset_softc {
+ struct resource *res;
+ struct mtx mtx;
+};
+
+static struct resource_spec aw_reset_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+#define RESET_READ(sc, reg) bus_read_4((sc)->res, (reg))
+#define RESET_WRITE(sc, reg, val) bus_write_4((sc)->res, (reg), (val))
+
+static int
+aw_reset_assert(device_t dev, intptr_t id, bool reset)
+{
+ struct aw_reset_softc *sc;
+ uint32_t reg_value;
+
+ sc = device_get_softc(dev);
+
+ mtx_lock(&sc->mtx);
+ reg_value = RESET_READ(sc, RESET_OFFSET(id));
+ if (reset)
+ reg_value &= ~(1 << RESET_SHIFT(id));
+ else
+ reg_value |= (1 << RESET_SHIFT(id));
+ RESET_WRITE(sc, RESET_OFFSET(id), reg_value);
+ mtx_unlock(&sc->mtx);
+
+ return (0);
+}
+
+static int
+aw_reset_is_asserted(device_t dev, intptr_t id, bool *reset)
+{
+ struct aw_reset_softc *sc;
+ uint32_t reg_value;
+
+ sc = device_get_softc(dev);
+
+ mtx_lock(&sc->mtx);
+ reg_value = RESET_READ(sc, RESET_OFFSET(id));
+ mtx_unlock(&sc->mtx);
+
+ *reset = (reg_value & (1 << RESET_SHIFT(id))) != 0 ? false : true;
+
+ return (0);
+}
+
+static int
+aw_reset_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner Module Resets");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+aw_reset_attach(device_t dev)
+{
+ struct aw_reset_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (bus_alloc_resources(dev, aw_reset_spec, &sc->res) != 0) {
+ device_printf(dev, "cannot allocate resources for device\n");
+ return (ENXIO);
+ }
+
+ mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ hwreset_register_ofw_provider(dev);
+
+ return (0);
+}
+
+static device_method_t aw_reset_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, aw_reset_probe),
+ DEVMETHOD(device_attach, aw_reset_attach),
+
+ /* Reset interface */
+ DEVMETHOD(hwreset_assert, aw_reset_assert),
+ DEVMETHOD(hwreset_is_asserted, aw_reset_is_asserted),
+
+ DEVMETHOD_END
+};
+
+static driver_t aw_reset_driver = {
+ "aw_reset",
+ aw_reset_methods,
+ sizeof(struct aw_reset_softc),
+};
+
+static devclass_t aw_reset_devclass;
+
+EARLY_DRIVER_MODULE(aw_reset, simplebus, aw_reset_driver, aw_reset_devclass,
+ 0, 0, BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE);
+MODULE_VERSION(aw_reset, 1);
diff --git a/sys/arm/allwinner/aw_rsb.c b/sys/arm/allwinner/aw_rsb.c
new file mode 100644
index 000000000000..48103af6ac6e
--- /dev/null
+++ b/sys/arm/allwinner/aw_rsb.c
@@ -0,0 +1,501 @@
+/*-
+ * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Allwinner RSB (Reduced Serial Bus) and P2WI (Push-Pull Two Wire Interface)
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/iicbus/iiconf.h>
+#include <dev/iicbus/iicbus.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+
+#include "iicbus_if.h"
+
+#define RSB_CTRL 0x00
+#define START_TRANS (1 << 7)
+#define GLOBAL_INT_ENB (1 << 1)
+#define SOFT_RESET (1 << 0)
+#define RSB_CCR 0x04
+#define RSB_INTE 0x08
+#define RSB_INTS 0x0c
+#define INT_TRANS_ERR_ID(x) (((x) >> 8) & 0xf)
+#define INT_LOAD_BSY (1 << 2)
+#define INT_TRANS_ERR (1 << 1)
+#define INT_TRANS_OVER (1 << 0)
+#define INT_MASK (INT_LOAD_BSY|INT_TRANS_ERR|INT_TRANS_OVER)
+#define RSB_DADDR0 0x10
+#define RSB_DADDR1 0x14
+#define RSB_DLEN 0x18
+#define DLEN_READ (1 << 4)
+#define RSB_DATA0 0x1c
+#define RSB_DATA1 0x20
+#define RSB_CMD 0x2c
+#define CMD_SRTA 0xe8
+#define CMD_RD8 0x8b
+#define CMD_RD16 0x9c
+#define CMD_RD32 0xa6
+#define CMD_WR8 0x4e
+#define CMD_WR16 0x59
+#define CMD_WR32 0x63
+#define RSB_DAR 0x30
+#define DAR_RTA (0xff << 16)
+#define DAR_RTA_SHIFT 16
+#define DAR_DA (0xffff << 0)
+#define DAR_DA_SHIFT 0
+
+#define RSB_MAXLEN 8
+#define RSB_RESET_RETRY 100
+#define RSB_I2C_TIMEOUT hz
+
+#define RSB_ADDR_PMIC_PRIMARY 0x3a3
+#define RSB_ADDR_PMIC_SECONDARY 0x745
+#define RSB_ADDR_PERIPH_IC 0xe89
+
+#define A31_P2WI 1
+#define A23_RSB 2
+
+static struct ofw_compat_data compat_data[] = {
+ { "allwinner,sun6i-a31-p2wi", A31_P2WI },
+ { "allwinner,sun8i-a23-rsb", A23_RSB },
+ { NULL, 0 }
+};
+
+static struct resource_spec rsb_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+/*
+ * Device address to Run-time address mappings.
+ *
+ * Run-time address (RTA) is an 8-bit value used to address the device during
+ * a read or write transaction. The following are valid RTAs:
+ * 0x17 0x2d 0x3a 0x4e 0x59 0x63 0x74 0x8b 0x9c 0xa6 0xb1 0xc5 0xd2 0xe8 0xff
+ *
+ * Allwinner uses RTA 0x2d for the primary PMIC, 0x3a for the secondary PMIC,
+ * and 0x4e for the peripheral IC (where applicable).
+ */
+static const struct {
+ uint16_t addr;
+ uint8_t rta;
+} rsb_rtamap[] = {
+ { .addr = RSB_ADDR_PMIC_PRIMARY, .rta = 0x2d },
+ { .addr = RSB_ADDR_PMIC_SECONDARY, .rta = 0x3a },
+ { .addr = RSB_ADDR_PERIPH_IC, .rta = 0x4e },
+ { .addr = 0, .rta = 0 }
+};
+
+struct rsb_softc {
+ struct resource *res;
+ struct mtx mtx;
+ clk_t clk;
+ hwreset_t rst;
+ device_t iicbus;
+ int busy;
+ uint32_t status;
+ uint16_t cur_addr;
+ int type;
+
+ struct iic_msg *msg;
+};
+
+#define RSB_LOCK(sc) mtx_lock(&(sc)->mtx)
+#define RSB_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
+#define RSB_ASSERT_LOCKED(sc) mtx_assert(&(sc)->mtx, MA_OWNED)
+#define RSB_READ(sc, reg) bus_read_4((sc)->res, (reg))
+#define RSB_WRITE(sc, reg, val) bus_write_4((sc)->res, (reg), (val))
+
+static phandle_t
+rsb_get_node(device_t bus, device_t dev)
+{
+ return (ofw_bus_get_node(bus));
+}
+
+static int
+rsb_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
+{
+ struct rsb_softc *sc;
+ int retry;
+
+ sc = device_get_softc(dev);
+
+ RSB_LOCK(sc);
+
+ /* Write soft-reset bit and wait for it to self-clear. */
+ RSB_WRITE(sc, RSB_CTRL, SOFT_RESET);
+ for (retry = RSB_RESET_RETRY; retry > 0; retry--)
+ if ((RSB_READ(sc, RSB_CTRL) & SOFT_RESET) == 0)
+ break;
+
+ RSB_UNLOCK(sc);
+
+ if (retry == 0) {
+ device_printf(dev, "soft reset timeout\n");
+ return (ETIMEDOUT);
+ }
+
+ return (IIC_ENOADDR);
+}
+
+static uint32_t
+rsb_encode(const uint8_t *buf, u_int len, u_int off)
+{
+ uint32_t val;
+ u_int n;
+
+ val = 0;
+ for (n = off; n < MIN(len, 4 + off); n++)
+ val |= ((uint32_t)buf[n] << ((n - off) * NBBY));
+
+ return val;
+}
+
+static void
+rsb_decode(const uint32_t val, uint8_t *buf, u_int len, u_int off)
+{
+ u_int n;
+
+ for (n = off; n < MIN(len, 4 + off); n++)
+ buf[n] = (val >> ((n - off) * NBBY)) & 0xff;
+}
+
+static int
+rsb_start(device_t dev)
+{
+ struct rsb_softc *sc;
+ int error, retry;
+
+ sc = device_get_softc(dev);
+
+ RSB_ASSERT_LOCKED(sc);
+
+ /* Start the transfer */
+ RSB_WRITE(sc, RSB_CTRL, GLOBAL_INT_ENB | START_TRANS);
+
+ /* Wait for transfer to complete */
+ error = ETIMEDOUT;
+ for (retry = RSB_I2C_TIMEOUT; retry > 0; retry--) {
+ sc->status |= RSB_READ(sc, RSB_INTS);
+ if ((sc->status & INT_TRANS_OVER) != 0) {
+ error = 0;
+ break;
+ }
+ DELAY((1000 * hz) / RSB_I2C_TIMEOUT);
+ }
+ if (error == 0 && (sc->status & INT_TRANS_OVER) == 0) {
+ device_printf(dev, "transfer error, status 0x%08x\n",
+ sc->status);
+ error = EIO;
+ }
+
+ return (error);
+
+}
+
+static int
+rsb_set_rta(device_t dev, uint16_t addr)
+{
+ struct rsb_softc *sc;
+ uint8_t rta;
+ int i;
+
+ sc = device_get_softc(dev);
+
+ RSB_ASSERT_LOCKED(sc);
+
+ /* Lookup run-time address for given device address */
+ for (rta = 0, i = 0; rsb_rtamap[i].rta != 0; i++)
+ if (rsb_rtamap[i].addr == addr) {
+ rta = rsb_rtamap[i].rta;
+ break;
+ }
+ if (rta == 0) {
+ device_printf(dev, "RTA not known for address %#x\n", addr);
+ return (ENXIO);
+ }
+
+ /* Set run-time address */
+ RSB_WRITE(sc, RSB_INTS, RSB_READ(sc, RSB_INTS));
+ RSB_WRITE(sc, RSB_DAR, (addr << DAR_DA_SHIFT) | (rta << DAR_RTA_SHIFT));
+ RSB_WRITE(sc, RSB_CMD, CMD_SRTA);
+
+ return (rsb_start(dev));
+}
+
+static int
+rsb_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
+{
+ struct rsb_softc *sc;
+ uint32_t daddr[2], data[2], dlen;
+ uint16_t device_addr;
+ uint8_t cmd;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ /*
+ * P2WI and RSB are not really I2C or SMBus controllers, so there are
+ * some restrictions imposed by the driver.
+ *
+ * Transfers must contain exactly two messages. The first is always
+ * a write, containing a single data byte offset. Data will either
+ * be read from or written to the corresponding data byte in the
+ * second message. The slave address in both messages must be the
+ * same.
+ */
+ if (nmsgs != 2 || (msgs[0].flags & IIC_M_RD) == IIC_M_RD ||
+ (msgs[0].slave >> 1) != (msgs[1].slave >> 1) ||
+ msgs[0].len != 1 || msgs[1].len > RSB_MAXLEN)
+ return (EINVAL);
+
+ /* The RSB controller can read or write 1, 2, or 4 bytes at a time. */
+ if (sc->type == A23_RSB) {
+ if ((msgs[1].flags & IIC_M_RD) != 0) {
+ switch (msgs[1].len) {
+ case 1:
+ cmd = CMD_RD8;
+ break;
+ case 2:
+ cmd = CMD_RD16;
+ break;
+ case 4:
+ cmd = CMD_RD32;
+ break;
+ default:
+ return (EINVAL);
+ }
+ } else {
+ switch (msgs[1].len) {
+ case 1:
+ cmd = CMD_WR8;
+ break;
+ case 2:
+ cmd = CMD_WR16;
+ break;
+ case 4:
+ cmd = CMD_WR32;
+ break;
+ default:
+ return (EINVAL);
+ }
+ }
+ }
+
+ RSB_LOCK(sc);
+ while (sc->busy)
+ mtx_sleep(sc, &sc->mtx, 0, "i2cbuswait", 0);
+ sc->busy = 1;
+ sc->status = 0;
+
+ /* Select current run-time address if necessary */
+ if (sc->type == A23_RSB) {
+ device_addr = msgs[0].slave >> 1;
+ if (sc->cur_addr != device_addr) {
+ error = rsb_set_rta(dev, device_addr);
+ if (error != 0)
+ goto done;
+ sc->cur_addr = device_addr;
+ sc->status = 0;
+ }
+ }
+
+ /* Clear interrupt status */
+ RSB_WRITE(sc, RSB_INTS, RSB_READ(sc, RSB_INTS));
+
+ /* Program data access address registers */
+ daddr[0] = rsb_encode(msgs[0].buf, msgs[0].len, 0);
+ RSB_WRITE(sc, RSB_DADDR0, daddr[0]);
+
+ /* Write data */
+ if ((msgs[1].flags & IIC_M_RD) == 0) {
+ data[0] = rsb_encode(msgs[1].buf, msgs[1].len, 0);
+ RSB_WRITE(sc, RSB_DATA0, data[0]);
+ }
+
+ /* Set command type for RSB */
+ if (sc->type == A23_RSB)
+ RSB_WRITE(sc, RSB_CMD, cmd);
+
+ /* Program data length register and transfer direction */
+ dlen = msgs[0].len - 1;
+ if ((msgs[1].flags & IIC_M_RD) == IIC_M_RD)
+ dlen |= DLEN_READ;
+ RSB_WRITE(sc, RSB_DLEN, dlen);
+
+ /* Start transfer */
+ error = rsb_start(dev);
+ if (error != 0)
+ goto done;
+
+ /* Read data */
+ if ((msgs[1].flags & IIC_M_RD) == IIC_M_RD) {
+ data[0] = RSB_READ(sc, RSB_DATA0);
+ rsb_decode(data[0], msgs[1].buf, msgs[1].len, 0);
+ }
+
+done:
+ sc->msg = NULL;
+ sc->busy = 0;
+ wakeup(sc);
+ RSB_UNLOCK(sc);
+
+ return (error);
+}
+
+static int
+rsb_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data) {
+ case A23_RSB:
+ device_set_desc(dev, "Allwinner RSB");
+ break;
+ case A31_P2WI:
+ device_set_desc(dev, "Allwinner P2WI");
+ break;
+ default:
+ return (ENXIO);
+ }
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+rsb_attach(device_t dev)
+{
+ struct rsb_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+ mtx_init(&sc->mtx, device_get_nameunit(dev), "rsb", MTX_DEF);
+
+ sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+
+ if (clk_get_by_ofw_index(dev, 0, 0, &sc->clk) == 0) {
+ error = clk_enable(sc->clk);
+ if (error != 0) {
+ device_printf(dev, "cannot enable clock\n");
+ goto fail;
+ }
+ }
+ if (hwreset_get_by_ofw_idx(dev, 0, 0, &sc->rst) == 0) {
+ error = hwreset_deassert(sc->rst);
+ if (error != 0) {
+ device_printf(dev, "cannot de-assert reset\n");
+ goto fail;
+ }
+ }
+
+ if (bus_alloc_resources(dev, rsb_spec, &sc->res) != 0) {
+ device_printf(dev, "cannot allocate resources for device\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ sc->iicbus = device_add_child(dev, "iicbus", -1);
+ if (sc->iicbus == NULL) {
+ device_printf(dev, "cannot add iicbus child device\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ bus_generic_attach(dev);
+
+ return (0);
+
+fail:
+ bus_release_resources(dev, rsb_spec, &sc->res);
+ if (sc->rst != NULL)
+ hwreset_release(sc->rst);
+ if (sc->clk != NULL)
+ clk_release(sc->clk);
+ mtx_destroy(&sc->mtx);
+ return (error);
+}
+
+static device_method_t rsb_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, rsb_probe),
+ DEVMETHOD(device_attach, rsb_attach),
+
+ /* Bus interface */
+ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
+ DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
+ DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource),
+ DEVMETHOD(bus_release_resource, bus_generic_release_resource),
+ DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
+ DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource),
+ DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource),
+ DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource),
+
+ /* OFW methods */
+ DEVMETHOD(ofw_bus_get_node, rsb_get_node),
+
+ /* iicbus interface */
+ DEVMETHOD(iicbus_callback, iicbus_null_callback),
+ DEVMETHOD(iicbus_reset, rsb_reset),
+ DEVMETHOD(iicbus_transfer, rsb_transfer),
+
+ DEVMETHOD_END
+};
+
+static driver_t rsb_driver = {
+ "iichb",
+ rsb_methods,
+ sizeof(struct rsb_softc),
+};
+
+static devclass_t rsb_devclass;
+
+EARLY_DRIVER_MODULE(iicbus, rsb, iicbus_driver, iicbus_devclass, 0, 0,
+ BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE);
+EARLY_DRIVER_MODULE(rsb, simplebus, rsb_driver, rsb_devclass, 0, 0,
+ BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE);
+MODULE_VERSION(rsb, 1);
+MODULE_DEPEND(rsb, iicbus, 1, 1, 1);
+SIMPLEBUS_PNP_INFO(compat_data);
diff --git a/sys/arm/allwinner/aw_rtc.c b/sys/arm/allwinner/aw_rtc.c
new file mode 100644
index 000000000000..a7002234d41a
--- /dev/null
+++ b/sys/arm/allwinner/aw_rtc.c
@@ -0,0 +1,368 @@
+/*-
+ * Copyright (c) 2019 Emmanuel Vadot <manu@FreeBSD.Org>
+ * Copyright (c) 2016 Vladimir Belian <fate10@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/time.h>
+#include <sys/rman.h>
+#include <sys/clock.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/resource.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/extres/clk/clk_fixed.h>
+
+#include <arm/allwinner/aw_machdep.h>
+
+#include "clock_if.h"
+
+#define LOSC_CTRL_REG 0x00
+#define A10_RTC_DATE_REG 0x04
+#define A10_RTC_TIME_REG 0x08
+#define A31_LOSC_AUTO_SWT_STA 0x04
+#define A31_RTC_DATE_REG 0x10
+#define A31_RTC_TIME_REG 0x14
+
+#define TIME_MASK 0x001f3f3f
+
+#define LOSC_OSC_SRC (1 << 0)
+#define LOSC_GSM (1 << 3)
+#define LOSC_AUTO_SW_EN (1 << 14)
+#define LOSC_MAGIC 0x16aa0000
+#define LOSC_BUSY_MASK 0x00000380
+
+#define IS_SUN7I (sc->conf->is_a20 == true)
+
+#define YEAR_MIN (IS_SUN7I ? 1970 : 2010)
+#define YEAR_MAX (IS_SUN7I ? 2100 : 2073)
+#define YEAR_OFFSET (IS_SUN7I ? 1900 : 2010)
+#define YEAR_MASK (IS_SUN7I ? 0xff : 0x3f)
+#define LEAP_BIT (IS_SUN7I ? 24 : 22)
+
+#define GET_SEC_VALUE(x) ((x) & 0x0000003f)
+#define GET_MIN_VALUE(x) (((x) & 0x00003f00) >> 8)
+#define GET_HOUR_VALUE(x) (((x) & 0x001f0000) >> 16)
+#define GET_DAY_VALUE(x) ((x) & 0x0000001f)
+#define GET_MON_VALUE(x) (((x) & 0x00000f00) >> 8)
+#define GET_YEAR_VALUE(x) (((x) >> 16) & YEAR_MASK)
+
+#define SET_DAY_VALUE(x) GET_DAY_VALUE(x)
+#define SET_MON_VALUE(x) (((x) & 0x0000000f) << 8)
+#define SET_YEAR_VALUE(x) (((x) & YEAR_MASK) << 16)
+#define SET_LEAP_VALUE(x) (((x) & 0x00000001) << LEAP_BIT)
+#define SET_SEC_VALUE(x) GET_SEC_VALUE(x)
+#define SET_MIN_VALUE(x) (((x) & 0x0000003f) << 8)
+#define SET_HOUR_VALUE(x) (((x) & 0x0000001f) << 16)
+
+#define HALF_OF_SEC_NS 500000000
+#define RTC_RES_US 1000000
+#define RTC_TIMEOUT 70
+
+#define RTC_READ(sc, reg) bus_read_4((sc)->res, (reg))
+#define RTC_WRITE(sc, reg, val) bus_write_4((sc)->res, (reg), (val))
+
+#define IS_LEAP_YEAR(y) (((y) % 400) == 0 || (((y) % 100) != 0 && ((y) % 4) == 0))
+
+struct aw_rtc_conf {
+ uint64_t iosc_freq;
+ bus_size_t rtc_date;
+ bus_size_t rtc_time;
+ bus_size_t rtc_losc_sta;
+ bool is_a20;
+};
+
+struct aw_rtc_conf a10_conf = {
+ .rtc_date = A10_RTC_DATE_REG,
+ .rtc_time = A10_RTC_TIME_REG,
+ .rtc_losc_sta = LOSC_CTRL_REG,
+};
+
+struct aw_rtc_conf a20_conf = {
+ .rtc_date = A10_RTC_DATE_REG,
+ .rtc_time = A10_RTC_TIME_REG,
+ .rtc_losc_sta = LOSC_CTRL_REG,
+ .is_a20 = true,
+};
+
+struct aw_rtc_conf a31_conf = {
+ .iosc_freq = 650000, /* between 600 and 700 Khz */
+ .rtc_date = A31_RTC_DATE_REG,
+ .rtc_time = A31_RTC_TIME_REG,
+ .rtc_losc_sta = A31_LOSC_AUTO_SWT_STA,
+};
+
+struct aw_rtc_conf h3_conf = {
+ .iosc_freq = 16000000,
+ .rtc_date = A31_RTC_DATE_REG,
+ .rtc_time = A31_RTC_TIME_REG,
+ .rtc_losc_sta = A31_LOSC_AUTO_SWT_STA,
+};
+
+static struct ofw_compat_data compat_data[] = {
+ { "allwinner,sun4i-a10-rtc", (uintptr_t) &a10_conf },
+ { "allwinner,sun7i-a20-rtc", (uintptr_t) &a20_conf },
+ { "allwinner,sun6i-a31-rtc", (uintptr_t) &a31_conf },
+ { "allwinner,sun8i-h3-rtc", (uintptr_t) &h3_conf },
+ { "allwinner,sun50i-h5-rtc", (uintptr_t) &h3_conf },
+ { "allwinner,sun50i-h6-rtc", (uintptr_t) &h3_conf },
+ { NULL, 0 }
+};
+
+struct aw_rtc_softc {
+ struct resource *res;
+ struct aw_rtc_conf *conf;
+ int type;
+};
+
+static struct clk_fixed_def aw_rtc_osc32k = {
+ .clkdef.id = 0,
+ .freq = 32768,
+};
+
+static struct clk_fixed_def aw_rtc_iosc = {
+ .clkdef.id = 2,
+};
+
+static void aw_rtc_install_clocks(struct aw_rtc_softc *sc, device_t dev);
+
+static int aw_rtc_probe(device_t dev);
+static int aw_rtc_attach(device_t dev);
+static int aw_rtc_detach(device_t dev);
+
+static int aw_rtc_gettime(device_t dev, struct timespec *ts);
+static int aw_rtc_settime(device_t dev, struct timespec *ts);
+
+static device_method_t aw_rtc_methods[] = {
+ DEVMETHOD(device_probe, aw_rtc_probe),
+ DEVMETHOD(device_attach, aw_rtc_attach),
+ DEVMETHOD(device_detach, aw_rtc_detach),
+
+ DEVMETHOD(clock_gettime, aw_rtc_gettime),
+ DEVMETHOD(clock_settime, aw_rtc_settime),
+
+ DEVMETHOD_END
+};
+
+static driver_t aw_rtc_driver = {
+ "rtc",
+ aw_rtc_methods,
+ sizeof(struct aw_rtc_softc),
+};
+
+static devclass_t aw_rtc_devclass;
+
+EARLY_DRIVER_MODULE(aw_rtc, simplebus, aw_rtc_driver, aw_rtc_devclass, 0, 0,
+ BUS_PASS_RESOURCE + BUS_PASS_ORDER_FIRST);
+MODULE_VERSION(aw_rtc, 1);
+SIMPLEBUS_PNP_INFO(compat_data);
+
+static int
+aw_rtc_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner RTC");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+aw_rtc_attach(device_t dev)
+{
+ struct aw_rtc_softc *sc = device_get_softc(dev);
+ uint32_t val;
+ int rid = 0;
+
+ sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
+ if (!sc->res) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ sc->conf = (struct aw_rtc_conf *)ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+ val = RTC_READ(sc, LOSC_CTRL_REG);
+ val |= LOSC_AUTO_SW_EN;
+ val |= LOSC_MAGIC | LOSC_GSM | LOSC_OSC_SRC;
+ RTC_WRITE(sc, LOSC_CTRL_REG, val);
+
+ DELAY(100);
+
+ if (bootverbose) {
+ val = RTC_READ(sc, sc->conf->rtc_losc_sta);
+ if ((val & LOSC_OSC_SRC) == 0)
+ device_printf(dev, "Using internal oscillator\n");
+ else
+ device_printf(dev, "Using external oscillator\n");
+ }
+
+ aw_rtc_install_clocks(sc, dev);
+
+ clock_register(dev, RTC_RES_US);
+
+ return (0);
+}
+
+static int
+aw_rtc_detach(device_t dev)
+{
+ /* can't support detach, since there's no clock_unregister function */
+ return (EBUSY);
+}
+
+static void
+aw_rtc_install_clocks(struct aw_rtc_softc *sc, device_t dev) {
+ struct clkdom *clkdom;
+ const char **clknames;
+ phandle_t node;
+ int nclocks;
+
+ node = ofw_bus_get_node(dev);
+ nclocks = ofw_bus_string_list_to_array(node, "clock-output-names", &clknames);
+ /* No clocks to export */
+ if (nclocks <= 0)
+ return;
+
+ if (nclocks != 3) {
+ device_printf(dev, "Having only %d clocks instead of 3, aborting\n", nclocks);
+ return;
+ }
+
+ clkdom = clkdom_create(dev);
+
+ aw_rtc_osc32k.clkdef.name = clknames[0];
+ if (clknode_fixed_register(clkdom, &aw_rtc_osc32k) != 0)
+ device_printf(dev, "Cannot register osc32k clock\n");
+
+ aw_rtc_iosc.clkdef.name = clknames[2];
+ aw_rtc_iosc.freq = sc->conf->iosc_freq;
+ if (clknode_fixed_register(clkdom, &aw_rtc_iosc) != 0)
+ device_printf(dev, "Cannot register iosc clock\n");
+
+ clkdom_finit(clkdom);
+
+ if (bootverbose)
+ clkdom_dump(clkdom);
+}
+
+static int
+aw_rtc_gettime(device_t dev, struct timespec *ts)
+{
+ struct aw_rtc_softc *sc = device_get_softc(dev);
+ struct clocktime ct;
+ uint32_t rdate, rtime;
+
+ rdate = RTC_READ(sc, sc->conf->rtc_date);
+ rtime = RTC_READ(sc, sc->conf->rtc_time);
+
+ if ((rtime & TIME_MASK) == 0)
+ rdate = RTC_READ(sc, sc->conf->rtc_date);
+
+ ct.sec = GET_SEC_VALUE(rtime);
+ ct.min = GET_MIN_VALUE(rtime);
+ ct.hour = GET_HOUR_VALUE(rtime);
+ ct.day = GET_DAY_VALUE(rdate);
+ ct.mon = GET_MON_VALUE(rdate);
+ ct.year = GET_YEAR_VALUE(rdate) + YEAR_OFFSET;
+ ct.dow = -1;
+ /* RTC resolution is 1 sec */
+ ct.nsec = 0;
+
+ return (clock_ct_to_ts(&ct, ts));
+}
+
+static int
+aw_rtc_settime(device_t dev, struct timespec *ts)
+{
+ struct aw_rtc_softc *sc = device_get_softc(dev);
+ struct clocktime ct;
+ uint32_t clk, rdate, rtime;
+
+ /* RTC resolution is 1 sec */
+ if (ts->tv_nsec >= HALF_OF_SEC_NS)
+ ts->tv_sec++;
+ ts->tv_nsec = 0;
+
+ clock_ts_to_ct(ts, &ct);
+
+ if ((ct.year < YEAR_MIN) || (ct.year > YEAR_MAX)) {
+ device_printf(dev, "could not set time, year out of range\n");
+ return (EINVAL);
+ }
+
+ for (clk = 0; RTC_READ(sc, LOSC_CTRL_REG) & LOSC_BUSY_MASK; clk++) {
+ if (clk > RTC_TIMEOUT) {
+ device_printf(dev, "could not set time, RTC busy\n");
+ return (EINVAL);
+ }
+ DELAY(1);
+ }
+ /* reset time register to avoid unexpected date increment */
+ RTC_WRITE(sc, sc->conf->rtc_time, 0);
+
+ rdate = SET_DAY_VALUE(ct.day) | SET_MON_VALUE(ct.mon) |
+ SET_YEAR_VALUE(ct.year - YEAR_OFFSET) |
+ SET_LEAP_VALUE(IS_LEAP_YEAR(ct.year));
+
+ rtime = SET_SEC_VALUE(ct.sec) | SET_MIN_VALUE(ct.min) |
+ SET_HOUR_VALUE(ct.hour);
+
+ for (clk = 0; RTC_READ(sc, LOSC_CTRL_REG) & LOSC_BUSY_MASK; clk++) {
+ if (clk > RTC_TIMEOUT) {
+ device_printf(dev, "could not set date, RTC busy\n");
+ return (EINVAL);
+ }
+ DELAY(1);
+ }
+ RTC_WRITE(sc, sc->conf->rtc_date, rdate);
+
+ for (clk = 0; RTC_READ(sc, LOSC_CTRL_REG) & LOSC_BUSY_MASK; clk++) {
+ if (clk > RTC_TIMEOUT) {
+ device_printf(dev, "could not set time, RTC busy\n");
+ return (EINVAL);
+ }
+ DELAY(1);
+ }
+ RTC_WRITE(sc, sc->conf->rtc_time, rtime);
+
+ DELAY(RTC_TIMEOUT);
+
+ return (0);
+}
diff --git a/sys/arm/allwinner/aw_sid.c b/sys/arm/allwinner/aw_sid.c
new file mode 100644
index 000000000000..4c949770d58f
--- /dev/null
+++ b/sys/arm/allwinner/aw_sid.c
@@ -0,0 +1,415 @@
+/*-
+ * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Allwinner secure ID controller
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/endian.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+#include <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/allwinner/aw_sid.h>
+
+#include "nvmem_if.h"
+
+/*
+ * Starting at least from sun8iw6 (A83T) EFUSE starts at 0x200
+ * There is 3 registers in the low area to read/write protected EFUSE.
+ */
+#define SID_PRCTL 0x40
+#define SID_PRCTL_OFFSET_MASK 0xff
+#define SID_PRCTL_OFFSET(n) (((n) & SID_PRCTL_OFFSET_MASK) << 16)
+#define SID_PRCTL_LOCK (0xac << 8)
+#define SID_PRCTL_READ (0x01 << 1)
+#define SID_PRCTL_WRITE (0x01 << 0)
+#define SID_PRKEY 0x50
+#define SID_RDKEY 0x60
+
+#define EFUSE_OFFSET 0x200
+#define EFUSE_NAME_SIZE 32
+#define EFUSE_DESC_SIZE 64
+
+struct aw_sid_efuse {
+ char name[EFUSE_NAME_SIZE];
+ char desc[EFUSE_DESC_SIZE];
+ bus_size_t base;
+ bus_size_t offset;
+ uint32_t size;
+ enum aw_sid_fuse_id id;
+ bool public;
+};
+
+static struct aw_sid_efuse a10_efuses[] = {
+ {
+ .name = "rootkey",
+ .desc = "Root Key or ChipID",
+ .offset = 0x0,
+ .size = 16,
+ .id = AW_SID_FUSE_ROOTKEY,
+ .public = true,
+ },
+};
+
+static struct aw_sid_efuse a64_efuses[] = {
+ {
+ .name = "rootkey",
+ .desc = "Root Key or ChipID",
+ .base = EFUSE_OFFSET,
+ .offset = 0x00,
+ .size = 16,
+ .id = AW_SID_FUSE_ROOTKEY,
+ .public = true,
+ },
+ {
+ .name = "calibration",
+ .desc = "Thermal Sensor Calibration Data",
+ .base = EFUSE_OFFSET,
+ .offset = 0x34,
+ .size = 8,
+ .id = AW_SID_FUSE_THSSENSOR,
+ .public = true,
+ },
+};
+
+static struct aw_sid_efuse a83t_efuses[] = {
+ {
+ .name = "rootkey",
+ .desc = "Root Key or ChipID",
+ .base = EFUSE_OFFSET,
+ .offset = 0x00,
+ .size = 16,
+ .id = AW_SID_FUSE_ROOTKEY,
+ .public = true,
+ },
+ {
+ .name = "calibration",
+ .desc = "Thermal Sensor Calibration Data",
+ .base = EFUSE_OFFSET,
+ .offset = 0x34,
+ .size = 8,
+ .id = AW_SID_FUSE_THSSENSOR,
+ .public = true,
+ },
+};
+
+static struct aw_sid_efuse h3_efuses[] = {
+ {
+ .name = "rootkey",
+ .desc = "Root Key or ChipID",
+ .base = EFUSE_OFFSET,
+ .offset = 0x00,
+ .size = 16,
+ .id = AW_SID_FUSE_ROOTKEY,
+ .public = true,
+ },
+ {
+ .name = "calibration",
+ .desc = "Thermal Sensor Calibration Data",
+ .base = EFUSE_OFFSET,
+ .offset = 0x34,
+ .size = 4,
+ .id = AW_SID_FUSE_THSSENSOR,
+ .public = false,
+ },
+};
+
+static struct aw_sid_efuse h5_efuses[] = {
+ {
+ .name = "rootkey",
+ .desc = "Root Key or ChipID",
+ .base = EFUSE_OFFSET,
+ .offset = 0x00,
+ .size = 16,
+ .id = AW_SID_FUSE_ROOTKEY,
+ .public = true,
+ },
+ {
+ .name = "calibration",
+ .desc = "Thermal Sensor Calibration Data",
+ .base = EFUSE_OFFSET,
+ .offset = 0x34,
+ .size = 4,
+ .id = AW_SID_FUSE_THSSENSOR,
+ .public = true,
+ },
+};
+
+struct aw_sid_conf {
+ struct aw_sid_efuse *efuses;
+ size_t nfuses;
+};
+
+static const struct aw_sid_conf a10_conf = {
+ .efuses = a10_efuses,
+ .nfuses = nitems(a10_efuses),
+};
+
+static const struct aw_sid_conf a20_conf = {
+ .efuses = a10_efuses,
+ .nfuses = nitems(a10_efuses),
+};
+
+static const struct aw_sid_conf a64_conf = {
+ .efuses = a64_efuses,
+ .nfuses = nitems(a64_efuses),
+};
+
+static const struct aw_sid_conf a83t_conf = {
+ .efuses = a83t_efuses,
+ .nfuses = nitems(a83t_efuses),
+};
+
+static const struct aw_sid_conf h3_conf = {
+ .efuses = h3_efuses,
+ .nfuses = nitems(h3_efuses),
+};
+
+static const struct aw_sid_conf h5_conf = {
+ .efuses = h5_efuses,
+ .nfuses = nitems(h5_efuses),
+};
+
+static struct ofw_compat_data compat_data[] = {
+ { "allwinner,sun4i-a10-sid", (uintptr_t)&a10_conf},
+ { "allwinner,sun7i-a20-sid", (uintptr_t)&a20_conf},
+ { "allwinner,sun50i-a64-sid", (uintptr_t)&a64_conf},
+ { "allwinner,sun8i-a83t-sid", (uintptr_t)&a83t_conf},
+ { "allwinner,sun8i-h3-sid", (uintptr_t)&h3_conf},
+ { "allwinner,sun50i-h5-sid", (uintptr_t)&h5_conf},
+ { NULL, 0 }
+};
+
+struct aw_sid_softc {
+ device_t sid_dev;
+ struct resource *res;
+ struct aw_sid_conf *sid_conf;
+ struct mtx prctl_mtx;
+};
+
+static struct aw_sid_softc *aw_sid_sc;
+
+static struct resource_spec aw_sid_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+#define RD1(sc, reg) bus_read_1((sc)->res, (reg))
+#define RD4(sc, reg) bus_read_4((sc)->res, (reg))
+#define WR4(sc, reg, val) bus_write_4((sc)->res, (reg), (val))
+
+static int aw_sid_sysctl(SYSCTL_HANDLER_ARGS);
+
+static int
+aw_sid_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner Secure ID Controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+aw_sid_attach(device_t dev)
+{
+ struct aw_sid_softc *sc;
+ phandle_t node;
+ int i;
+
+ node = ofw_bus_get_node(dev);
+ sc = device_get_softc(dev);
+ sc->sid_dev = dev;
+
+ if (bus_alloc_resources(dev, aw_sid_spec, &sc->res) != 0) {
+ device_printf(dev, "cannot allocate resources for device\n");
+ return (ENXIO);
+ }
+
+ mtx_init(&sc->prctl_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+ sc->sid_conf = (struct aw_sid_conf *)ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+ aw_sid_sc = sc;
+
+ /* Register ourself so device can resolve who we are */
+ OF_device_register_xref(OF_xref_from_node(node), dev);
+
+ for (i = 0; i < sc->sid_conf->nfuses ;i++) {\
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
+ OID_AUTO, sc->sid_conf->efuses[i].name,
+ CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
+ dev, sc->sid_conf->efuses[i].id, aw_sid_sysctl,
+ "A", sc->sid_conf->efuses[i].desc);
+ }
+ return (0);
+}
+
+int
+aw_sid_get_fuse(enum aw_sid_fuse_id id, uint8_t *out, uint32_t *size)
+{
+ struct aw_sid_softc *sc;
+ uint32_t val;
+ int i, j;
+
+ sc = aw_sid_sc;
+ if (sc == NULL)
+ return (ENXIO);
+
+ for (i = 0; i < sc->sid_conf->nfuses; i++)
+ if (id == sc->sid_conf->efuses[i].id)
+ break;
+
+ if (i == sc->sid_conf->nfuses)
+ return (ENOENT);
+
+ if (*size != sc->sid_conf->efuses[i].size) {
+ *size = sc->sid_conf->efuses[i].size;
+ return (ENOMEM);
+ }
+
+ if (out == NULL)
+ return (ENOMEM);
+
+ if (sc->sid_conf->efuses[i].public == false)
+ mtx_lock(&sc->prctl_mtx);
+ for (j = 0; j < sc->sid_conf->efuses[i].size; j += 4) {
+ if (sc->sid_conf->efuses[i].public == false) {
+ val = SID_PRCTL_OFFSET(sc->sid_conf->efuses[i].offset + j) |
+ SID_PRCTL_LOCK |
+ SID_PRCTL_READ;
+ WR4(sc, SID_PRCTL, val);
+ /* Read bit will be cleared once read has concluded */
+ while (RD4(sc, SID_PRCTL) & SID_PRCTL_READ)
+ continue;
+ val = RD4(sc, SID_RDKEY);
+ } else
+ val = RD4(sc, sc->sid_conf->efuses[i].base +
+ sc->sid_conf->efuses[i].offset + j);
+ out[j] = val & 0xFF;
+ if (j + 1 < *size)
+ out[j + 1] = (val & 0xFF00) >> 8;
+ if (j + 2 < *size)
+ out[j + 2] = (val & 0xFF0000) >> 16;
+ if (j + 3 < *size)
+ out[j + 3] = (val & 0xFF000000) >> 24;
+ }
+ if (sc->sid_conf->efuses[i].public == false)
+ mtx_unlock(&sc->prctl_mtx);
+
+ return (0);
+}
+
+static int
+aw_sid_read(device_t dev, uint32_t offset, uint32_t size, uint8_t *buffer)
+{
+ struct aw_sid_softc *sc;
+ enum aw_sid_fuse_id fuse_id = 0;
+ int i;
+
+ sc = device_get_softc(dev);
+
+ for (i = 0; i < sc->sid_conf->nfuses; i++)
+ if (offset == sc->sid_conf->efuses[i].offset) {
+ fuse_id = sc->sid_conf->efuses[i].id;
+ break;
+ }
+
+ if (fuse_id == 0)
+ return (ENOENT);
+
+ return (aw_sid_get_fuse(fuse_id, buffer, &size));
+}
+
+static int
+aw_sid_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ struct aw_sid_softc *sc;
+ device_t dev = arg1;
+ enum aw_sid_fuse_id fuse = arg2;
+ uint8_t data[32];
+ char out[128];
+ uint32_t size;
+ int ret, i;
+
+ sc = device_get_softc(dev);
+
+ /* Get the size of the efuse data */
+ size = 0;
+ aw_sid_get_fuse(fuse, NULL, &size);
+ /* We now have the real size */
+ ret = aw_sid_get_fuse(fuse, data, &size);
+ if (ret != 0) {
+ device_printf(dev, "Cannot get fuse id %d: %d\n", fuse, ret);
+ return (ENOENT);
+ }
+
+ for (i = 0; i < size; i++)
+ snprintf(out + (i * 2), sizeof(out) - (i * 2),
+ "%.2x", data[i]);
+
+ return sysctl_handle_string(oidp, out, sizeof(out), req);
+}
+
+static device_method_t aw_sid_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, aw_sid_probe),
+ DEVMETHOD(device_attach, aw_sid_attach),
+
+ /* NVMEM interface */
+ DEVMETHOD(nvmem_read, aw_sid_read),
+ DEVMETHOD_END
+};
+
+static driver_t aw_sid_driver = {
+ "aw_sid",
+ aw_sid_methods,
+ sizeof(struct aw_sid_softc),
+};
+
+static devclass_t aw_sid_devclass;
+
+EARLY_DRIVER_MODULE(aw_sid, simplebus, aw_sid_driver, aw_sid_devclass, 0, 0,
+ BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_FIRST);
+MODULE_VERSION(aw_sid, 1);
+SIMPLEBUS_PNP_INFO(compat_data);
diff --git a/sys/arm/allwinner/aw_sid.h b/sys/arm/allwinner/aw_sid.h
new file mode 100644
index 000000000000..a94b3d6ab36f
--- /dev/null
+++ b/sys/arm/allwinner/aw_sid.h
@@ -0,0 +1,39 @@
+/*-
+ * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __AW_SID_H__
+#define __AW_SID_H__
+
+enum aw_sid_fuse_id {
+ AW_SID_FUSE_ROOTKEY = 1,
+ AW_SID_FUSE_THSSENSOR,
+};
+
+int aw_sid_read_tscalib(uint32_t *, uint32_t *);
+int aw_sid_get_fuse(enum aw_sid_fuse_id id, uint8_t *out, uint32_t *size);
+
+#endif /* !__AW_SID_H__ */
diff --git a/sys/arm/allwinner/aw_spi.c b/sys/arm/allwinner/aw_spi.c
new file mode 100644
index 000000000000..0952102575b3
--- /dev/null
+++ b/sys/arm/allwinner/aw_spi.c
@@ -0,0 +1,607 @@
+/*-
+ * Copyright (c) 2018 Emmanuel Vadot <manu@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/resource.h>
+#include <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/spibus/spi.h>
+#include <dev/spibus/spibusvar.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+
+#include "spibus_if.h"
+
+#define AW_SPI_GCR 0x04 /* Global Control Register */
+#define AW_SPI_GCR_EN (1 << 0) /* ENable */
+#define AW_SPI_GCR_MODE_MASTER (1 << 1) /* 1 = Master, 0 = Slave */
+#define AW_SPI_GCR_TP_EN (1 << 7) /* 1 = Stop transmit when FIFO is full */
+#define AW_SPI_GCR_SRST (1 << 31) /* Soft Reset */
+
+#define AW_SPI_TCR 0x08 /* Transfer Control register */
+#define AW_SPI_TCR_XCH (1 << 31) /* Initiate transfer */
+#define AW_SPI_TCR_SDDM (1 << 14) /* Sending Delay Data Mode */
+#define AW_SPI_TCR_SDM (1 << 13) /* Master Sample Data Mode */
+#define AW_SPI_TCR_FBS (1 << 12) /* First Transmit Bit Select (1 == LSB) */
+#define AW_SPI_TCR_SDC (1 << 11) /* Master Sample Data Control */
+#define AW_SPI_TCR_RPSM (1 << 10) /* Rapid Mode Select */
+#define AW_SPI_TCR_DDB (1 << 9) /* Dummy Burst Type */
+#define AW_SPI_TCR_SSSEL_MASK 0x30 /* Chip select */
+#define AW_SPI_TCR_SSSEL_SHIFT 4
+#define AW_SPI_TCR_SS_LEVEL (1 << 7) /* 1 == CS High */
+#define AW_SPI_TCR_SS_OWNER (1 << 6) /* 1 == Software controlled */
+#define AW_SPI_TCR_SPOL (1 << 2) /* 1 == Active low */
+#define AW_SPI_TCR_CPOL (1 << 1) /* 1 == Active low */
+#define AW_SPI_TCR_CPHA (1 << 0) /* 1 == Phase 1 */
+
+#define AW_SPI_IER 0x10 /* Interrupt Control Register */
+#define AW_SPI_IER_SS (1 << 13) /* Chip select went from valid to invalid */
+#define AW_SPI_IER_TC (1 << 12) /* Transfer complete */
+#define AW_SPI_IER_TF_UDR (1 << 11) /* TXFIFO underrun */
+#define AW_SPI_IER_TF_OVF (1 << 10) /* TXFIFO overrun */
+#define AW_SPI_IER_RF_UDR (1 << 9) /* RXFIFO underrun */
+#define AW_SPI_IER_RF_OVF (1 << 8) /* RXFIFO overrun */
+#define AW_SPI_IER_TF_FULL (1 << 6) /* TXFIFO Full */
+#define AW_SPI_IER_TF_EMP (1 << 5) /* TXFIFO Empty */
+#define AW_SPI_IER_TF_ERQ (1 << 4) /* TXFIFO Empty Request */
+#define AW_SPI_IER_RF_FULL (1 << 2) /* RXFIFO Full */
+#define AW_SPI_IER_RF_EMP (1 << 1) /* RXFIFO Empty */
+#define AW_SPI_IER_RF_ERQ (1 << 0) /* RXFIFO Empty Request */
+
+#define AW_SPI_ISR 0x14 /* Interrupt Status Register */
+
+#define AW_SPI_FCR 0x18 /* FIFO Control Register */
+#define AW_SPI_FCR_TX_RST (1 << 31) /* Reset TX FIFO */
+#define AW_SPI_FCR_TX_TRIG_MASK 0xFF0000 /* TX FIFO Trigger level */
+#define AW_SPI_FCR_TX_TRIG_SHIFT 16
+#define AW_SPI_FCR_RX_RST (1 << 15) /* Reset RX FIFO */
+#define AW_SPI_FCR_RX_TRIG_MASK 0xFF /* RX FIFO Trigger level */
+#define AW_SPI_FCR_RX_TRIG_SHIFT 0
+
+#define AW_SPI_FSR 0x1C /* FIFO Status Register */
+#define AW_SPI_FSR_TB_WR (1 << 31)
+#define AW_SPI_FSR_TB_CNT_MASK 0x70000000
+#define AW_SPI_FSR_TB_CNT_SHIFT 28
+#define AW_SPI_FSR_TF_CNT_MASK 0xFF0000
+#define AW_SPI_FSR_TF_CNT_SHIFT 16
+#define AW_SPI_FSR_RB_WR (1 << 15)
+#define AW_SPI_FSR_RB_CNT_MASK 0x7000
+#define AW_SPI_FSR_RB_CNT_SHIFT 12
+#define AW_SPI_FSR_RF_CNT_MASK 0xFF
+#define AW_SPI_FSR_RF_CNT_SHIFT 0
+
+#define AW_SPI_WCR 0x20 /* Wait Clock Counter Register */
+
+#define AW_SPI_CCR 0x24 /* Clock Rate Control Register */
+#define AW_SPI_CCR_DRS (1 << 12) /* Clock divider select */
+#define AW_SPI_CCR_CDR1_MASK 0xF00
+#define AW_SPI_CCR_CDR1_SHIFT 8
+#define AW_SPI_CCR_CDR2_MASK 0xFF
+#define AW_SPI_CCR_CDR2_SHIFT 0
+
+#define AW_SPI_MBC 0x30 /* Burst Counter Register */
+#define AW_SPI_MTC 0x34 /* Transmit Counter Register */
+#define AW_SPI_BCC 0x38 /* Burst Control Register */
+#define AW_SPI_MDMA_CTL 0x88 /* Normal DMA Control Register */
+#define AW_SPI_TXD 0x200 /* TX Data Register */
+#define AW_SPI_RDX 0x300 /* RX Data Register */
+
+#define AW_SPI_MAX_CS 4
+#define AW_SPI_FIFO_SIZE 64
+
+static struct ofw_compat_data compat_data[] = {
+ { "allwinner,sun8i-h3-spi", 1 },
+ { NULL, 0 }
+};
+
+static struct resource_spec aw_spi_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE },
+ { -1, 0 }
+};
+
+struct aw_spi_softc {
+ device_t dev;
+ device_t spibus;
+ struct resource *res[2];
+ struct mtx mtx;
+ clk_t clk_ahb;
+ clk_t clk_mod;
+ uint64_t mod_freq;
+ hwreset_t rst_ahb;
+ void * intrhand;
+ int transfer;
+
+ uint8_t *rxbuf;
+ uint32_t rxcnt;
+ uint8_t *txbuf;
+ uint32_t txcnt;
+ uint32_t txlen;
+ uint32_t rxlen;
+};
+
+#define AW_SPI_LOCK(sc) mtx_lock(&(sc)->mtx)
+#define AW_SPI_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
+#define AW_SPI_ASSERT_LOCKED(sc) mtx_assert(&(sc)->mtx, MA_OWNED)
+#define AW_SPI_READ_1(sc, reg) bus_read_1((sc)->res[0], (reg))
+#define AW_SPI_WRITE_1(sc, reg, val) bus_write_1((sc)->res[0], (reg), (val))
+#define AW_SPI_READ_4(sc, reg) bus_read_4((sc)->res[0], (reg))
+#define AW_SPI_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val))
+
+static int aw_spi_probe(device_t dev);
+static int aw_spi_attach(device_t dev);
+static int aw_spi_detach(device_t dev);
+static void aw_spi_intr(void *arg);
+
+static int
+aw_spi_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner SPI");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+aw_spi_attach(device_t dev)
+{
+ struct aw_spi_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ if (bus_alloc_resources(dev, aw_spi_spec, sc->res) != 0) {
+ device_printf(dev, "cannot allocate resources for device\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ if (bus_setup_intr(dev, sc->res[1],
+ INTR_TYPE_MISC | INTR_MPSAFE, NULL, aw_spi_intr, sc,
+ &sc->intrhand)) {
+ bus_release_resources(dev, aw_spi_spec, sc->res);
+ device_printf(dev, "cannot setup interrupt handler\n");
+ return (ENXIO);
+ }
+
+ /* De-assert reset */
+ if (hwreset_get_by_ofw_idx(dev, 0, 0, &sc->rst_ahb) == 0) {
+ error = hwreset_deassert(sc->rst_ahb);
+ if (error != 0) {
+ device_printf(dev, "cannot de-assert reset\n");
+ goto fail;
+ }
+ }
+
+ /* Activate the module clock. */
+ error = clk_get_by_ofw_name(dev, 0, "ahb", &sc->clk_ahb);
+ if (error != 0) {
+ device_printf(dev, "cannot get ahb clock\n");
+ goto fail;
+ }
+ error = clk_get_by_ofw_name(dev, 0, "mod", &sc->clk_mod);
+ if (error != 0) {
+ device_printf(dev, "cannot get mod clock\n");
+ goto fail;
+ }
+ error = clk_enable(sc->clk_ahb);
+ if (error != 0) {
+ device_printf(dev, "cannot enable ahb clock\n");
+ goto fail;
+ }
+ error = clk_enable(sc->clk_mod);
+ if (error != 0) {
+ device_printf(dev, "cannot enable mod clock\n");
+ goto fail;
+ }
+
+ sc->spibus = device_add_child(dev, "spibus", -1);
+
+ return (bus_generic_attach(dev));
+
+fail:
+ aw_spi_detach(dev);
+ return (error);
+}
+
+static int
+aw_spi_detach(device_t dev)
+{
+ struct aw_spi_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ bus_generic_detach(sc->dev);
+ if (sc->spibus != NULL)
+ device_delete_child(dev, sc->spibus);
+
+ if (sc->clk_mod != NULL)
+ clk_release(sc->clk_mod);
+ if (sc->clk_ahb)
+ clk_release(sc->clk_ahb);
+ if (sc->rst_ahb)
+ hwreset_assert(sc->rst_ahb);
+
+ if (sc->intrhand != NULL)
+ bus_teardown_intr(sc->dev, sc->res[1], sc->intrhand);
+
+ bus_release_resources(dev, aw_spi_spec, sc->res);
+ mtx_destroy(&sc->mtx);
+
+ return (0);
+}
+
+static phandle_t
+aw_spi_get_node(device_t bus, device_t dev)
+{
+
+ return ofw_bus_get_node(bus);
+}
+
+static void
+aw_spi_setup_mode(struct aw_spi_softc *sc, uint32_t mode)
+{
+ uint32_t reg;
+
+ /* We only support master mode */
+ reg = AW_SPI_READ_4(sc, AW_SPI_GCR);
+ reg |= AW_SPI_GCR_MODE_MASTER;
+ AW_SPI_WRITE_4(sc, AW_SPI_GCR, reg);
+
+ /* Setup the modes */
+ reg = AW_SPI_READ_4(sc, AW_SPI_TCR);
+ if (mode & SPIBUS_MODE_CPHA)
+ reg |= AW_SPI_TCR_CPHA;
+ if (mode & SPIBUS_MODE_CPOL)
+ reg |= AW_SPI_TCR_CPOL;
+
+ AW_SPI_WRITE_4(sc, AW_SPI_TCR, reg);
+}
+
+static void
+aw_spi_setup_cs(struct aw_spi_softc *sc, uint32_t cs, bool low)
+{
+ uint32_t reg;
+
+ /* Setup CS */
+ reg = AW_SPI_READ_4(sc, AW_SPI_TCR);
+ reg &= ~(AW_SPI_TCR_SSSEL_MASK);
+ reg |= cs << AW_SPI_TCR_SSSEL_SHIFT;
+ reg |= AW_SPI_TCR_SS_OWNER;
+ if (low)
+ reg &= ~(AW_SPI_TCR_SS_LEVEL);
+ else
+ reg |= AW_SPI_TCR_SS_LEVEL;
+
+ AW_SPI_WRITE_4(sc, AW_SPI_TCR, reg);
+}
+
+static uint64_t
+aw_spi_clock_test_cdr1(struct aw_spi_softc *sc, uint64_t clock, uint32_t *ccr)
+{
+ uint64_t cur, best = 0;
+ int i, max, best_div;
+
+ max = AW_SPI_CCR_CDR1_MASK >> AW_SPI_CCR_CDR1_SHIFT;
+ for (i = 0; i < max; i++) {
+ cur = sc->mod_freq / (1 << i);
+ if ((clock - cur) < (clock - best)) {
+ best = cur;
+ best_div = i;
+ }
+ }
+
+ *ccr = (best_div << AW_SPI_CCR_CDR1_SHIFT);
+ return (best);
+}
+
+static uint64_t
+aw_spi_clock_test_cdr2(struct aw_spi_softc *sc, uint64_t clock, uint32_t *ccr)
+{
+ uint64_t cur, best = 0;
+ int i, max, best_div;
+
+ max = ((AW_SPI_CCR_CDR2_MASK) >> AW_SPI_CCR_CDR2_SHIFT);
+ for (i = 0; i < max; i++) {
+ cur = sc->mod_freq / (2 * i + 1);
+ if ((clock - cur) < (clock - best)) {
+ best = cur;
+ best_div = i;
+ }
+ }
+
+ *ccr = AW_SPI_CCR_DRS | (best_div << AW_SPI_CCR_CDR2_SHIFT);
+ return (best);
+}
+
+static void
+aw_spi_setup_clock(struct aw_spi_softc *sc, uint64_t clock)
+{
+ uint64_t best_ccr1, best_ccr2;
+ uint32_t ccr, ccr1, ccr2;
+
+ best_ccr1 = aw_spi_clock_test_cdr1(sc, clock, &ccr1);
+ best_ccr2 = aw_spi_clock_test_cdr2(sc, clock, &ccr2);
+
+ if (best_ccr1 == clock) {
+ ccr = ccr1;
+ } else if (best_ccr2 == clock) {
+ ccr = ccr2;
+ } else {
+ if ((clock - best_ccr1) < (clock - best_ccr2))
+ ccr = ccr1;
+ else
+ ccr = ccr2;
+ }
+
+ AW_SPI_WRITE_4(sc, AW_SPI_CCR, ccr);
+}
+
+static inline void
+aw_spi_fill_txfifo(struct aw_spi_softc *sc)
+{
+ uint32_t reg, txcnt;
+ int i;
+
+ if (sc->txcnt == sc->txlen)
+ return;
+
+ reg = AW_SPI_READ_4(sc, AW_SPI_FSR);
+ reg &= AW_SPI_FSR_TF_CNT_MASK;
+ txcnt = reg >> AW_SPI_FSR_TF_CNT_SHIFT;
+
+ for (i = 0; i < (AW_SPI_FIFO_SIZE - txcnt); i++) {
+ AW_SPI_WRITE_1(sc, AW_SPI_TXD, sc->txbuf[sc->txcnt++]);
+ if (sc->txcnt == sc->txlen)
+ break;
+ }
+
+ return;
+}
+
+static inline void
+aw_spi_read_rxfifo(struct aw_spi_softc *sc)
+{
+ uint32_t reg;
+ uint8_t val;
+ int i;
+
+ if (sc->rxcnt == sc->rxlen)
+ return;
+
+ reg = AW_SPI_READ_4(sc, AW_SPI_FSR);
+ reg = (reg & AW_SPI_FSR_RF_CNT_MASK) >> AW_SPI_FSR_RF_CNT_SHIFT;
+
+ for (i = 0; i < reg; i++) {
+ val = AW_SPI_READ_1(sc, AW_SPI_RDX);
+ if (sc->rxcnt < sc->rxlen)
+ sc->rxbuf[sc->rxcnt++] = val;
+ }
+}
+
+static void
+aw_spi_intr(void *arg)
+{
+ struct aw_spi_softc *sc;
+ uint32_t intr;
+
+ sc = (struct aw_spi_softc *)arg;
+
+ intr = AW_SPI_READ_4(sc, AW_SPI_ISR);
+
+ if (intr & AW_SPI_IER_RF_FULL)
+ aw_spi_read_rxfifo(sc);
+
+ if (intr & AW_SPI_IER_TF_EMP) {
+ aw_spi_fill_txfifo(sc);
+ /*
+ * If we don't have anything else to write
+ * disable TXFifo interrupts
+ */
+ if (sc->txcnt == sc->txlen)
+ AW_SPI_WRITE_4(sc, AW_SPI_IER, AW_SPI_IER_TC |
+ AW_SPI_IER_RF_FULL);
+ }
+
+ if (intr & AW_SPI_IER_TC) {
+ /* read the rest of the data from the fifo */
+ aw_spi_read_rxfifo(sc);
+
+ /* Disable the interrupts */
+ AW_SPI_WRITE_4(sc, AW_SPI_IER, 0);
+ sc->transfer = 0;
+ wakeup(sc);
+ }
+
+ /* Clear Interrupts */
+ AW_SPI_WRITE_4(sc, AW_SPI_ISR, intr);
+}
+
+static int
+aw_spi_xfer(struct aw_spi_softc *sc, void *rxbuf, void *txbuf, uint32_t txlen, uint32_t rxlen)
+{
+ uint32_t reg;
+ int error = 0, timeout;
+
+ sc->rxbuf = rxbuf;
+ sc->rxcnt = 0;
+ sc->txbuf = txbuf;
+ sc->txcnt = 0;
+ sc->txlen = txlen;
+ sc->rxlen = rxlen;
+
+ /* Reset the FIFOs */
+ AW_SPI_WRITE_4(sc, AW_SPI_FCR, AW_SPI_FCR_TX_RST | AW_SPI_FCR_RX_RST);
+
+ for (timeout = 1000; timeout > 0; timeout--) {
+ reg = AW_SPI_READ_4(sc, AW_SPI_FCR);
+ if (reg == 0)
+ break;
+ }
+ if (timeout == 0) {
+ device_printf(sc->dev, "Cannot reset the FIFOs\n");
+ return (EIO);
+ }
+
+ /* Write the counters */
+ AW_SPI_WRITE_4(sc, AW_SPI_MBC, txlen);
+ AW_SPI_WRITE_4(sc, AW_SPI_MTC, txlen);
+ AW_SPI_WRITE_4(sc, AW_SPI_BCC, txlen);
+
+ /* First fill */
+ aw_spi_fill_txfifo(sc);
+
+ /* Start transmit */
+ reg = AW_SPI_READ_4(sc, AW_SPI_TCR);
+ reg |= AW_SPI_TCR_XCH;
+ AW_SPI_WRITE_4(sc, AW_SPI_TCR, reg);
+
+ /*
+ * Enable interrupts for :
+ * Transmit complete
+ * TX Fifo empty
+ * RX Fifo full
+ */
+ AW_SPI_WRITE_4(sc, AW_SPI_IER, AW_SPI_IER_TC |
+ AW_SPI_IER_TF_EMP | AW_SPI_IER_RF_FULL);
+
+ sc->transfer = 1;
+
+ while (error == 0 && sc->transfer != 0)
+ error = msleep(sc, &sc->mtx, 0, "aw_spi", 10 * hz);
+
+ return (0);
+}
+
+static int
+aw_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
+{
+ struct aw_spi_softc *sc;
+ uint32_t cs, mode, clock, reg;
+ int err = 0;
+
+ sc = device_get_softc(dev);
+
+ spibus_get_cs(child, &cs);
+ spibus_get_clock(child, &clock);
+ spibus_get_mode(child, &mode);
+
+ /* The minimum divider is 2 so set the clock at twice the needed speed */
+ clk_set_freq(sc->clk_mod, 2 * clock, CLK_SET_ROUND_DOWN);
+ clk_get_freq(sc->clk_mod, &sc->mod_freq);
+ if (cs >= AW_SPI_MAX_CS) {
+ device_printf(dev, "Invalid cs %d\n", cs);
+ return (EINVAL);
+ }
+
+ mtx_lock(&sc->mtx);
+
+ /* Enable and reset the module */
+ reg = AW_SPI_READ_4(sc, AW_SPI_GCR);
+ reg |= AW_SPI_GCR_EN | AW_SPI_GCR_SRST;
+ AW_SPI_WRITE_4(sc, AW_SPI_GCR, reg);
+
+ /* Setup clock, CS and mode */
+ aw_spi_setup_clock(sc, clock);
+ aw_spi_setup_mode(sc, mode);
+ if (cs & SPIBUS_CS_HIGH)
+ aw_spi_setup_cs(sc, cs, false);
+ else
+ aw_spi_setup_cs(sc, cs, true);
+
+ /* xfer */
+ err = 0;
+ if (cmd->tx_cmd_sz > 0)
+ err = aw_spi_xfer(sc, cmd->rx_cmd, cmd->tx_cmd,
+ cmd->tx_cmd_sz, cmd->rx_cmd_sz);
+ if (cmd->tx_data_sz > 0 && err == 0)
+ err = aw_spi_xfer(sc, cmd->rx_data, cmd->tx_data,
+ cmd->tx_data_sz, cmd->rx_data_sz);
+
+ if (cs & SPIBUS_CS_HIGH)
+ aw_spi_setup_cs(sc, cs, true);
+ else
+ aw_spi_setup_cs(sc, cs, false);
+
+ /* Disable the module */
+ reg = AW_SPI_READ_4(sc, AW_SPI_GCR);
+ reg &= ~AW_SPI_GCR_EN;
+ AW_SPI_WRITE_4(sc, AW_SPI_GCR, reg);
+
+ mtx_unlock(&sc->mtx);
+
+ return (err);
+}
+
+static device_method_t aw_spi_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, aw_spi_probe),
+ DEVMETHOD(device_attach, aw_spi_attach),
+ DEVMETHOD(device_detach, aw_spi_detach),
+
+ /* spibus_if */
+ DEVMETHOD(spibus_transfer, aw_spi_transfer),
+
+ /* ofw_bus_if */
+ DEVMETHOD(ofw_bus_get_node, aw_spi_get_node),
+
+ DEVMETHOD_END
+};
+
+static driver_t aw_spi_driver = {
+ "aw_spi",
+ aw_spi_methods,
+ sizeof(struct aw_spi_softc),
+};
+
+static devclass_t aw_spi_devclass;
+
+DRIVER_MODULE(aw_spi, simplebus, aw_spi_driver, aw_spi_devclass, 0, 0);
+DRIVER_MODULE(ofw_spibus, aw_spi, ofw_spibus_driver, ofw_spibus_devclass, 0, 0);
+MODULE_DEPEND(aw_spi, ofw_spibus, 1, 1, 1);
+SIMPLEBUS_PNP_INFO(compat_data);
diff --git a/sys/arm/allwinner/aw_syscon.c b/sys/arm/allwinner/aw_syscon.c
new file mode 100644
index 000000000000..f1a01dac8e1b
--- /dev/null
+++ b/sys/arm/allwinner/aw_syscon.c
@@ -0,0 +1,86 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Allwinner syscon driver
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <machine/bus.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/extres/syscon/syscon.h>
+#include <dev/extres/syscon/syscon_generic.h>
+
+static struct ofw_compat_data compat_data[] = {
+ {"allwinner,sun50i-a64-system-controller", 1},
+ {"allwinner,sun50i-a64-system-control", 1},
+ {"allwinner,sun8i-a83t-system-controller", 1},
+ {"allwinner,sun8i-h3-system-controller", 1},
+ {"allwinner,sun8i-h3-system-control", 1},
+ {"allwinner,sun50i-h5-system-control", 1},
+ {NULL, 0}
+};
+
+static int
+aw_syscon_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner syscon");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static device_method_t aw_syscon_methods[] = {
+ DEVMETHOD(device_probe, aw_syscon_probe),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(aw_syscon, aw_syscon_driver, aw_syscon_methods,
+ sizeof(struct syscon_generic_softc), syscon_generic_driver);
+
+static devclass_t aw_syscon_devclass;
+/* aw_syscon needs to attach prior to if_awg */
+EARLY_DRIVER_MODULE(aw_syscon, simplebus, aw_syscon_driver, aw_syscon_devclass,
+ 0, 0, BUS_PASS_SCHEDULER + BUS_PASS_ORDER_LAST);
+MODULE_VERSION(aw_syscon, 1);
diff --git a/sys/arm/allwinner/aw_thermal.c b/sys/arm/allwinner/aw_thermal.c
new file mode 100644
index 000000000000..90836124ad66
--- /dev/null
+++ b/sys/arm/allwinner/aw_thermal.c
@@ -0,0 +1,732 @@
+/*-
+ * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Allwinner thermal sensor controller
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/eventhandler.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
+#include <sys/reboot.h>
+#include <sys/module.h>
+#include <sys/cpu.h>
+#include <sys/taskqueue.h>
+#include <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/extres/nvmem/nvmem.h>
+
+#include <arm/allwinner/aw_sid.h>
+
+#include "cpufreq_if.h"
+#include "nvmem_if.h"
+
+#define THS_CTRL0 0x00
+#define THS_CTRL1 0x04
+#define ADC_CALI_EN (1 << 17)
+#define THS_CTRL2 0x40
+#define SENSOR_ACQ1_SHIFT 16
+#define SENSOR2_EN (1 << 2)
+#define SENSOR1_EN (1 << 1)
+#define SENSOR0_EN (1 << 0)
+#define THS_INTC 0x44
+#define THS_THERMAL_PER_SHIFT 12
+#define THS_INTS 0x48
+#define THS2_DATA_IRQ_STS (1 << 10)
+#define THS1_DATA_IRQ_STS (1 << 9)
+#define THS0_DATA_IRQ_STS (1 << 8)
+#define SHUT_INT2_STS (1 << 6)
+#define SHUT_INT1_STS (1 << 5)
+#define SHUT_INT0_STS (1 << 4)
+#define ALARM_INT2_STS (1 << 2)
+#define ALARM_INT1_STS (1 << 1)
+#define ALARM_INT0_STS (1 << 0)
+#define THS_ALARM0_CTRL 0x50
+#define ALARM_T_HOT_MASK 0xfff
+#define ALARM_T_HOT_SHIFT 16
+#define ALARM_T_HYST_MASK 0xfff
+#define ALARM_T_HYST_SHIFT 0
+#define THS_SHUTDOWN0_CTRL 0x60
+#define SHUT_T_HOT_MASK 0xfff
+#define SHUT_T_HOT_SHIFT 16
+#define THS_FILTER 0x70
+#define THS_CALIB0 0x74
+#define THS_CALIB1 0x78
+#define THS_DATA0 0x80
+#define THS_DATA1 0x84
+#define THS_DATA2 0x88
+#define DATA_MASK 0xfff
+
+#define A83T_CLK_RATE 24000000
+#define A83T_ADC_ACQUIRE_TIME 23 /* 24Mhz/(23 + 1) = 1us */
+#define A83T_THERMAL_PER 1 /* 4096 * (1 + 1) / 24Mhz = 341 us */
+#define A83T_FILTER 0x5 /* Filter enabled, avg of 4 */
+#define A83T_TEMP_BASE 2719000
+#define A83T_TEMP_MUL 1000
+#define A83T_TEMP_DIV 14186
+
+#define A64_CLK_RATE 4000000
+#define A64_ADC_ACQUIRE_TIME 400 /* 4Mhz/(400 + 1) = 100 us */
+#define A64_THERMAL_PER 24 /* 4096 * (24 + 1) / 4Mhz = 25.6 ms */
+#define A64_FILTER 0x6 /* Filter enabled, avg of 8 */
+#define A64_TEMP_BASE 2170000
+#define A64_TEMP_MUL 1000
+#define A64_TEMP_DIV 8560
+
+#define H3_CLK_RATE 4000000
+#define H3_ADC_ACQUIRE_TIME 0x3f
+#define H3_THERMAL_PER 401
+#define H3_FILTER 0x6 /* Filter enabled, avg of 8 */
+#define H3_TEMP_BASE 217
+#define H3_TEMP_MUL 1000
+#define H3_TEMP_DIV 8253
+#define H3_TEMP_MINUS 1794000
+#define H3_INIT_ALARM 90 /* degC */
+#define H3_INIT_SHUT 105 /* degC */
+
+#define H5_CLK_RATE 24000000
+#define H5_ADC_ACQUIRE_TIME 479 /* 24Mhz/479 = 20us */
+#define H5_THERMAL_PER 58 /* 4096 * (58 + 1) / 24Mhz = 10ms */
+#define H5_FILTER 0x6 /* Filter enabled, avg of 8 */
+#define H5_TEMP_BASE 233832448
+#define H5_TEMP_MUL 124885
+#define H5_TEMP_DIV 20
+#define H5_TEMP_BASE_CPU 271581184
+#define H5_TEMP_MUL_CPU 152253
+#define H5_TEMP_BASE_GPU 289406976
+#define H5_TEMP_MUL_GPU 166724
+#define H5_INIT_CPU_ALARM 80 /* degC */
+#define H5_INIT_CPU_SHUT 96 /* degC */
+#define H5_INIT_GPU_ALARM 84 /* degC */
+#define H5_INIT_GPU_SHUT 100 /* degC */
+
+#define TEMP_C_TO_K 273
+#define SENSOR_ENABLE_ALL (SENSOR0_EN|SENSOR1_EN|SENSOR2_EN)
+#define SHUT_INT_ALL (SHUT_INT0_STS|SHUT_INT1_STS|SHUT_INT2_STS)
+#define ALARM_INT_ALL (ALARM_INT0_STS)
+
+#define MAX_SENSORS 3
+#define MAX_CF_LEVELS 64
+
+#define THROTTLE_ENABLE_DEFAULT 1
+
+/* Enable thermal throttling */
+static int aw_thermal_throttle_enable = THROTTLE_ENABLE_DEFAULT;
+TUNABLE_INT("hw.aw_thermal.throttle_enable", &aw_thermal_throttle_enable);
+
+struct aw_thermal_sensor {
+ const char *name;
+ const char *desc;
+ int init_alarm;
+ int init_shut;
+};
+
+struct aw_thermal_config {
+ struct aw_thermal_sensor sensors[MAX_SENSORS];
+ int nsensors;
+ uint64_t clk_rate;
+ uint32_t adc_acquire_time;
+ int adc_cali_en;
+ uint32_t filter;
+ uint32_t thermal_per;
+ int (*to_temp)(uint32_t, int);
+ uint32_t (*to_reg)(int, int);
+ int temp_base;
+ int temp_mul;
+ int temp_div;
+ int calib0, calib1;
+ uint32_t calib0_mask, calib1_mask;
+};
+
+static int
+a83t_to_temp(uint32_t val, int sensor)
+{
+ return ((A83T_TEMP_BASE - (val * A83T_TEMP_MUL)) / A83T_TEMP_DIV);
+}
+
+static const struct aw_thermal_config a83t_config = {
+ .nsensors = 3,
+ .sensors = {
+ [0] = {
+ .name = "cluster0",
+ .desc = "CPU cluster 0 temperature",
+ },
+ [1] = {
+ .name = "cluster1",
+ .desc = "CPU cluster 1 temperature",
+ },
+ [2] = {
+ .name = "gpu",
+ .desc = "GPU temperature",
+ },
+ },
+ .clk_rate = A83T_CLK_RATE,
+ .adc_acquire_time = A83T_ADC_ACQUIRE_TIME,
+ .adc_cali_en = 1,
+ .filter = A83T_FILTER,
+ .thermal_per = A83T_THERMAL_PER,
+ .to_temp = a83t_to_temp,
+ .calib0_mask = 0xffffffff,
+ .calib1_mask = 0xffff,
+};
+
+static int
+a64_to_temp(uint32_t val, int sensor)
+{
+ return ((A64_TEMP_BASE - (val * A64_TEMP_MUL)) / A64_TEMP_DIV);
+}
+
+static const struct aw_thermal_config a64_config = {
+ .nsensors = 3,
+ .sensors = {
+ [0] = {
+ .name = "cpu",
+ .desc = "CPU temperature",
+ },
+ [1] = {
+ .name = "gpu1",
+ .desc = "GPU temperature 1",
+ },
+ [2] = {
+ .name = "gpu2",
+ .desc = "GPU temperature 2",
+ },
+ },
+ .clk_rate = A64_CLK_RATE,
+ .adc_acquire_time = A64_ADC_ACQUIRE_TIME,
+ .adc_cali_en = 1,
+ .filter = A64_FILTER,
+ .thermal_per = A64_THERMAL_PER,
+ .to_temp = a64_to_temp,
+ .calib0_mask = 0xffffffff,
+ .calib1_mask = 0xffff,
+};
+
+static int
+h3_to_temp(uint32_t val, int sensor)
+{
+ return (H3_TEMP_BASE - ((val * H3_TEMP_MUL) / H3_TEMP_DIV));
+}
+
+static uint32_t
+h3_to_reg(int val, int sensor)
+{
+ return ((H3_TEMP_MINUS - (val * H3_TEMP_DIV)) / H3_TEMP_MUL);
+}
+
+static const struct aw_thermal_config h3_config = {
+ .nsensors = 1,
+ .sensors = {
+ [0] = {
+ .name = "cpu",
+ .desc = "CPU temperature",
+ .init_alarm = H3_INIT_ALARM,
+ .init_shut = H3_INIT_SHUT,
+ },
+ },
+ .clk_rate = H3_CLK_RATE,
+ .adc_acquire_time = H3_ADC_ACQUIRE_TIME,
+ .adc_cali_en = 1,
+ .filter = H3_FILTER,
+ .thermal_per = H3_THERMAL_PER,
+ .to_temp = h3_to_temp,
+ .to_reg = h3_to_reg,
+ .calib0_mask = 0xffffffff,
+};
+
+static int
+h5_to_temp(uint32_t val, int sensor)
+{
+ int tmp;
+
+ /* Temp is lower than 70 degrees */
+ if (val > 0x500) {
+ tmp = H5_TEMP_BASE - (val * H5_TEMP_MUL);
+ tmp >>= H5_TEMP_DIV;
+ return (tmp);
+ }
+
+ if (sensor == 0)
+ tmp = H5_TEMP_BASE_CPU - (val * H5_TEMP_MUL_CPU);
+ else if (sensor == 1)
+ tmp = H5_TEMP_BASE_GPU - (val * H5_TEMP_MUL_GPU);
+ else {
+ printf("Unknown sensor %d\n", sensor);
+ return (val);
+ }
+
+ tmp >>= H5_TEMP_DIV;
+ return (tmp);
+}
+
+static uint32_t
+h5_to_reg(int val, int sensor)
+{
+ int tmp;
+
+ if (val < 70) {
+ tmp = H5_TEMP_BASE - (val << H5_TEMP_DIV);
+ tmp /= H5_TEMP_MUL;
+ } else {
+ if (sensor == 0) {
+ tmp = H5_TEMP_BASE_CPU - (val << H5_TEMP_DIV);
+ tmp /= H5_TEMP_MUL_CPU;
+ } else if (sensor == 1) {
+ tmp = H5_TEMP_BASE_GPU - (val << H5_TEMP_DIV);
+ tmp /= H5_TEMP_MUL_GPU;
+ } else {
+ printf("Unknown sensor %d\n", sensor);
+ return (val);
+ }
+ }
+
+ return ((uint32_t)tmp);
+}
+
+static const struct aw_thermal_config h5_config = {
+ .nsensors = 2,
+ .sensors = {
+ [0] = {
+ .name = "cpu",
+ .desc = "CPU temperature",
+ .init_alarm = H5_INIT_CPU_ALARM,
+ .init_shut = H5_INIT_CPU_SHUT,
+ },
+ [1] = {
+ .name = "gpu",
+ .desc = "GPU temperature",
+ .init_alarm = H5_INIT_GPU_ALARM,
+ .init_shut = H5_INIT_GPU_SHUT,
+ },
+ },
+ .clk_rate = H5_CLK_RATE,
+ .adc_acquire_time = H5_ADC_ACQUIRE_TIME,
+ .filter = H5_FILTER,
+ .thermal_per = H5_THERMAL_PER,
+ .to_temp = h5_to_temp,
+ .to_reg = h5_to_reg,
+ .calib0_mask = 0xffffffff,
+};
+
+static struct ofw_compat_data compat_data[] = {
+ { "allwinner,sun8i-a83t-ths", (uintptr_t)&a83t_config },
+ { "allwinner,sun8i-h3-ths", (uintptr_t)&h3_config },
+ { "allwinner,sun50i-a64-ths", (uintptr_t)&a64_config },
+ { "allwinner,sun50i-h5-ths", (uintptr_t)&h5_config },
+ { NULL, (uintptr_t)NULL }
+};
+
+#define THS_CONF(d) \
+ (void *)ofw_bus_search_compatible((d), compat_data)->ocd_data
+
+struct aw_thermal_softc {
+ device_t dev;
+ struct resource *res[2];
+ struct aw_thermal_config *conf;
+
+ struct task cf_task;
+ int throttle;
+ int min_freq;
+ struct cf_level levels[MAX_CF_LEVELS];
+ eventhandler_tag cf_pre_tag;
+
+ clk_t clk_apb;
+ clk_t clk_ths;
+};
+
+static struct resource_spec aw_thermal_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+#define RD4(sc, reg) bus_read_4((sc)->res[0], (reg))
+#define WR4(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val))
+
+static int
+aw_thermal_init(struct aw_thermal_softc *sc)
+{
+ phandle_t node;
+ uint32_t calib[2];
+ int error;
+
+ node = ofw_bus_get_node(sc->dev);
+ if (nvmem_get_cell_len(node, "calibration") > sizeof(calib)) {
+ device_printf(sc->dev, "calibration nvmem cell is too large\n");
+ return (ENXIO);
+ }
+ error = nvmem_read_cell_by_name(node, "calibration",
+ (void *)&calib, nvmem_get_cell_len(node, "calibration"));
+ /* Read calibration settings from EFUSE */
+ if (error != 0) {
+ device_printf(sc->dev, "Cannot read THS efuse\n");
+ return (error);
+ }
+
+ calib[0] &= sc->conf->calib0_mask;
+ calib[1] &= sc->conf->calib1_mask;
+
+ /* Write calibration settings to thermal controller */
+ if (calib[0] != 0)
+ WR4(sc, THS_CALIB0, calib[0]);
+ if (calib[1] != 0)
+ WR4(sc, THS_CALIB1, calib[1]);
+
+ /* Configure ADC acquire time (CLK_IN/(N+1)) and enable sensors */
+ WR4(sc, THS_CTRL1, ADC_CALI_EN);
+ WR4(sc, THS_CTRL0, sc->conf->adc_acquire_time);
+ WR4(sc, THS_CTRL2, sc->conf->adc_acquire_time << SENSOR_ACQ1_SHIFT);
+
+ /* Set thermal period */
+ WR4(sc, THS_INTC, sc->conf->thermal_per << THS_THERMAL_PER_SHIFT);
+
+ /* Enable average filter */
+ WR4(sc, THS_FILTER, sc->conf->filter);
+
+ /* Enable interrupts */
+ WR4(sc, THS_INTS, RD4(sc, THS_INTS));
+ WR4(sc, THS_INTC, RD4(sc, THS_INTC) | SHUT_INT_ALL | ALARM_INT_ALL);
+
+ /* Enable sensors */
+ WR4(sc, THS_CTRL2, RD4(sc, THS_CTRL2) | SENSOR_ENABLE_ALL);
+
+ return (0);
+}
+
+static int
+aw_thermal_gettemp(struct aw_thermal_softc *sc, int sensor)
+{
+ uint32_t val;
+
+ val = RD4(sc, THS_DATA0 + (sensor * 4));
+
+ return (sc->conf->to_temp(val, sensor));
+}
+
+static int
+aw_thermal_getshut(struct aw_thermal_softc *sc, int sensor)
+{
+ uint32_t val;
+
+ val = RD4(sc, THS_SHUTDOWN0_CTRL + (sensor * 4));
+ val = (val >> SHUT_T_HOT_SHIFT) & SHUT_T_HOT_MASK;
+
+ return (sc->conf->to_temp(val, sensor));
+}
+
+static void
+aw_thermal_setshut(struct aw_thermal_softc *sc, int sensor, int temp)
+{
+ uint32_t val;
+
+ val = RD4(sc, THS_SHUTDOWN0_CTRL + (sensor * 4));
+ val &= ~(SHUT_T_HOT_MASK << SHUT_T_HOT_SHIFT);
+ val |= (sc->conf->to_reg(temp, sensor) << SHUT_T_HOT_SHIFT);
+ WR4(sc, THS_SHUTDOWN0_CTRL + (sensor * 4), val);
+}
+
+static int
+aw_thermal_gethyst(struct aw_thermal_softc *sc, int sensor)
+{
+ uint32_t val;
+
+ val = RD4(sc, THS_ALARM0_CTRL + (sensor * 4));
+ val = (val >> ALARM_T_HYST_SHIFT) & ALARM_T_HYST_MASK;
+
+ return (sc->conf->to_temp(val, sensor));
+}
+
+static int
+aw_thermal_getalarm(struct aw_thermal_softc *sc, int sensor)
+{
+ uint32_t val;
+
+ val = RD4(sc, THS_ALARM0_CTRL + (sensor * 4));
+ val = (val >> ALARM_T_HOT_SHIFT) & ALARM_T_HOT_MASK;
+
+ return (sc->conf->to_temp(val, sensor));
+}
+
+static void
+aw_thermal_setalarm(struct aw_thermal_softc *sc, int sensor, int temp)
+{
+ uint32_t val;
+
+ val = RD4(sc, THS_ALARM0_CTRL + (sensor * 4));
+ val &= ~(ALARM_T_HOT_MASK << ALARM_T_HOT_SHIFT);
+ val |= (sc->conf->to_reg(temp, sensor) << ALARM_T_HOT_SHIFT);
+ WR4(sc, THS_ALARM0_CTRL + (sensor * 4), val);
+}
+
+static int
+aw_thermal_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ struct aw_thermal_softc *sc;
+ int sensor, val;
+
+ sc = arg1;
+ sensor = arg2;
+
+ val = aw_thermal_gettemp(sc, sensor) + TEMP_C_TO_K;
+
+ return sysctl_handle_opaque(oidp, &val, sizeof(val), req);
+}
+
+static void
+aw_thermal_throttle(struct aw_thermal_softc *sc, int enable)
+{
+ device_t cf_dev;
+ int count, error;
+
+ if (enable == sc->throttle)
+ return;
+
+ if (enable != 0) {
+ /* Set the lowest available frequency */
+ cf_dev = devclass_get_device(devclass_find("cpufreq"), 0);
+ if (cf_dev == NULL)
+ return;
+ count = MAX_CF_LEVELS;
+ error = CPUFREQ_LEVELS(cf_dev, sc->levels, &count);
+ if (error != 0 || count == 0)
+ return;
+ sc->min_freq = sc->levels[count - 1].total_set.freq;
+ error = CPUFREQ_SET(cf_dev, &sc->levels[count - 1],
+ CPUFREQ_PRIO_USER);
+ if (error != 0)
+ return;
+ }
+
+ sc->throttle = enable;
+}
+
+static void
+aw_thermal_cf_task(void *arg, int pending)
+{
+ struct aw_thermal_softc *sc;
+
+ sc = arg;
+
+ aw_thermal_throttle(sc, 1);
+}
+
+static void
+aw_thermal_cf_pre_change(void *arg, const struct cf_level *level, int *status)
+{
+ struct aw_thermal_softc *sc;
+ int temp_cur, temp_alarm;
+
+ sc = arg;
+
+ if (aw_thermal_throttle_enable == 0 || sc->throttle == 0 ||
+ level->total_set.freq == sc->min_freq)
+ return;
+
+ temp_cur = aw_thermal_gettemp(sc, 0);
+ temp_alarm = aw_thermal_getalarm(sc, 0);
+
+ if (temp_cur < temp_alarm)
+ aw_thermal_throttle(sc, 0);
+ else
+ *status = ENXIO;
+}
+
+static void
+aw_thermal_intr(void *arg)
+{
+ struct aw_thermal_softc *sc;
+ device_t dev;
+ uint32_t ints;
+
+ dev = arg;
+ sc = device_get_softc(dev);
+
+ ints = RD4(sc, THS_INTS);
+ WR4(sc, THS_INTS, ints);
+
+ if ((ints & SHUT_INT_ALL) != 0) {
+ device_printf(dev,
+ "WARNING - current temperature exceeds safe limits\n");
+ shutdown_nice(RB_POWEROFF);
+ }
+
+ if ((ints & ALARM_INT_ALL) != 0)
+ taskqueue_enqueue(taskqueue_thread, &sc->cf_task);
+}
+
+static int
+aw_thermal_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (THS_CONF(dev) == NULL)
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner Thermal Sensor Controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+aw_thermal_attach(device_t dev)
+{
+ struct aw_thermal_softc *sc;
+ hwreset_t rst;
+ int i, error;
+ void *ih;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ rst = NULL;
+ ih = NULL;
+
+ sc->conf = THS_CONF(dev);
+ TASK_INIT(&sc->cf_task, 0, aw_thermal_cf_task, sc);
+
+ if (bus_alloc_resources(dev, aw_thermal_spec, sc->res) != 0) {
+ device_printf(dev, "cannot allocate resources for device\n");
+ return (ENXIO);
+ }
+
+ if (clk_get_by_ofw_name(dev, 0, "bus", &sc->clk_apb) == 0) {
+ error = clk_enable(sc->clk_apb);
+ if (error != 0) {
+ device_printf(dev, "cannot enable apb clock\n");
+ goto fail;
+ }
+ }
+
+ if (clk_get_by_ofw_name(dev, 0, "mod", &sc->clk_ths) == 0) {
+ error = clk_set_freq(sc->clk_ths, sc->conf->clk_rate, 0);
+ if (error != 0) {
+ device_printf(dev, "cannot set ths clock rate\n");
+ goto fail;
+ }
+ error = clk_enable(sc->clk_ths);
+ if (error != 0) {
+ device_printf(dev, "cannot enable ths clock\n");
+ goto fail;
+ }
+ }
+
+ if (hwreset_get_by_ofw_idx(dev, 0, 0, &rst) == 0) {
+ error = hwreset_deassert(rst);
+ if (error != 0) {
+ device_printf(dev, "cannot de-assert reset\n");
+ goto fail;
+ }
+ }
+
+ error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE,
+ NULL, aw_thermal_intr, dev, &ih);
+ if (error != 0) {
+ device_printf(dev, "cannot setup interrupt handler\n");
+ goto fail;
+ }
+
+ for (i = 0; i < sc->conf->nsensors; i++) {
+ if (sc->conf->sensors[i].init_alarm > 0)
+ aw_thermal_setalarm(sc, i,
+ sc->conf->sensors[i].init_alarm);
+ if (sc->conf->sensors[i].init_shut > 0)
+ aw_thermal_setshut(sc, i,
+ sc->conf->sensors[i].init_shut);
+ }
+
+ if (aw_thermal_init(sc) != 0)
+ goto fail;
+
+ for (i = 0; i < sc->conf->nsensors; i++)
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
+ OID_AUTO, sc->conf->sensors[i].name,
+ CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
+ sc, i, aw_thermal_sysctl, "IK0",
+ sc->conf->sensors[i].desc);
+
+ if (bootverbose)
+ for (i = 0; i < sc->conf->nsensors; i++) {
+ device_printf(dev,
+ "%s: alarm %dC hyst %dC shut %dC\n",
+ sc->conf->sensors[i].name,
+ aw_thermal_getalarm(sc, i),
+ aw_thermal_gethyst(sc, i),
+ aw_thermal_getshut(sc, i));
+ }
+
+ sc->cf_pre_tag = EVENTHANDLER_REGISTER(cpufreq_pre_change,
+ aw_thermal_cf_pre_change, sc, EVENTHANDLER_PRI_FIRST);
+
+ return (0);
+
+fail:
+ if (ih != NULL)
+ bus_teardown_intr(dev, sc->res[1], ih);
+ if (rst != NULL)
+ hwreset_release(rst);
+ if (sc->clk_apb != NULL)
+ clk_release(sc->clk_apb);
+ if (sc->clk_ths != NULL)
+ clk_release(sc->clk_ths);
+ bus_release_resources(dev, aw_thermal_spec, sc->res);
+
+ return (ENXIO);
+}
+
+static device_method_t aw_thermal_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, aw_thermal_probe),
+ DEVMETHOD(device_attach, aw_thermal_attach),
+
+ DEVMETHOD_END
+};
+
+static driver_t aw_thermal_driver = {
+ "aw_thermal",
+ aw_thermal_methods,
+ sizeof(struct aw_thermal_softc),
+};
+
+static devclass_t aw_thermal_devclass;
+
+DRIVER_MODULE(aw_thermal, simplebus, aw_thermal_driver, aw_thermal_devclass,
+ 0, 0);
+MODULE_VERSION(aw_thermal, 1);
+MODULE_DEPEND(aw_thermal, aw_sid, 1, 1, 1);
+SIMPLEBUS_PNP_INFO(compat_data);
diff --git a/sys/arm/allwinner/aw_ts.c b/sys/arm/allwinner/aw_ts.c
new file mode 100644
index 000000000000..91e51894c181
--- /dev/null
+++ b/sys/arm/allwinner/aw_ts.c
@@ -0,0 +1,229 @@
+/*-
+ * Copyright (c) 2016 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Allwinner Touch Sreen driver
+ * Touch screen part is not done, only the thermal sensor part is.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+#include <machine/bus.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#define READ(_sc, _r) bus_read_4((_sc)->res[0], (_r))
+#define WRITE(_sc, _r, _v) bus_write_4((_sc)->res[0], (_r), (_v))
+
+/* Control register 0 */
+#define TP_CTRL0 0x00
+#define TP_CTRL0_TACQ(x) ((x & 0xFF) << 0)
+#define TP_CTRL0_FS_DIV(x) ((x & 0xF) << 16)
+#define TP_CTRL0_CLK_DIV(x) ((x & 0x3) << 20)
+#define TP_CTRL0_CLK_SELECT(x) ((x & 0x1) << 22)
+
+/* Control register 1 */
+#define TP_CTRL1 0x04
+#define TP_CTRL1_MODE_EN (1 << 4)
+
+/* Control register 2 */
+#define TP_CTRL2 0x08
+
+/* Control register 3 */
+#define TP_CTRL3 0x0C
+
+/* Int/FIFO control register */
+#define TP_FIFOC 0x10
+#define TP_FIFOC_TEMP_IRQ_ENABLE (1 << 18)
+
+/* Int/FIFO status register */
+#define TP_FIFOS 0x14
+#define TP_FIFOS_TEMP_IRQ_PENDING (1 << 18)
+
+/* Temperature Period Register */
+#define TP_TPR 0x18
+#define TP_TPR_TEMP_EN (1 << 16)
+#define TP_TPR_TEMP_PERIOD(x) (x << 0)
+
+/* Common data register */
+#define TP_CDAT 0x1C
+
+/* Temperature data register */
+#define TEMP_DATA 0x20
+
+/* TP Data register*/
+#define TP_DATA 0x24
+
+/* TP IO config register */
+#define TP_IO_CONFIG 0x28
+
+/* TP IO port data register */
+#define TP_IO_DATA 0x2C
+
+struct aw_ts_softc {
+ device_t dev;
+ struct resource * res[2];
+ void * intrhand;
+ int temp_data;
+ int temp_offset;
+ int temp_step;
+};
+
+static struct resource_spec aw_ts_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE },
+ { -1, 0 }
+};
+
+#define A10_TS 1
+#define A13_TS 2
+
+#define AW_TS_TEMP_SYSCTL 1
+
+static struct ofw_compat_data compat_data[] = {
+ {"allwinner,sun4i-a10-ts", A10_TS},
+ {"allwinner,sun5i-a13-ts", A13_TS},
+ {NULL, 0}
+};
+
+static void
+aw_ts_intr(void *arg)
+{
+ struct aw_ts_softc *sc;
+ int val;
+
+ sc= (struct aw_ts_softc *)arg;
+
+ val = READ(sc, TP_FIFOS);
+ if (val & TP_FIFOS_TEMP_IRQ_PENDING) {
+ /* Convert the value to millicelsius then millikelvin */
+ sc->temp_data = (READ(sc, TEMP_DATA) * sc->temp_step - sc->temp_offset)
+ + 273150;
+ }
+
+ WRITE(sc, TP_FIFOS, val);
+}
+
+static int
+aw_ts_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner Touch Screen controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+aw_ts_attach(device_t dev)
+{
+ struct aw_ts_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ if (bus_alloc_resources(dev, aw_ts_spec, sc->res) != 0) {
+ device_printf(dev, "could not allocate memory resource\n");
+ return (ENXIO);
+ }
+
+ if (bus_setup_intr(dev, sc->res[1],
+ INTR_TYPE_MISC | INTR_MPSAFE, NULL, aw_ts_intr, sc,
+ &sc->intrhand)) {
+ bus_release_resources(dev, aw_ts_spec, sc->res);
+ device_printf(dev, "cannot setup interrupt handler\n");
+ return (ENXIO);
+ }
+
+ /*
+ * Thoses magic values were taken from linux which take them from
+ * the allwinner SDK or found them by deduction
+ */
+ switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data) {
+ case A10_TS:
+ sc->temp_offset = 257000;
+ sc->temp_step = 133;
+ break;
+ case A13_TS:
+ sc->temp_offset = 144700;
+ sc->temp_step = 100;
+ break;
+ }
+
+ /* Enable clock and set divisers */
+ WRITE(sc, TP_CTRL0, TP_CTRL0_CLK_SELECT(0) |
+ TP_CTRL0_CLK_DIV(2) |
+ TP_CTRL0_FS_DIV(7) |
+ TP_CTRL0_TACQ(63));
+
+ /* Enable TS module */
+ WRITE(sc, TP_CTRL1, TP_CTRL1_MODE_EN);
+
+ /* Enable Temperature, period is ~2s */
+ WRITE(sc, TP_TPR, TP_TPR_TEMP_EN | TP_TPR_TEMP_PERIOD(1953));
+
+ /* Enable temp irq */
+ WRITE(sc, TP_FIFOC, TP_FIFOC_TEMP_IRQ_ENABLE);
+
+ /* Add sysctl */
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
+ OID_AUTO, "temperature",
+ CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
+ &sc->temp_data, 0, sysctl_handle_int,
+ "IK3", "CPU Temperature");
+
+ return (0);
+}
+
+static device_method_t aw_ts_methods[] = {
+ DEVMETHOD(device_probe, aw_ts_probe),
+ DEVMETHOD(device_attach, aw_ts_attach),
+
+ DEVMETHOD_END
+};
+
+static driver_t aw_ts_driver = {
+ "aw_ts",
+ aw_ts_methods,
+ sizeof(struct aw_ts_softc),
+};
+static devclass_t aw_ts_devclass;
+
+DRIVER_MODULE(aw_ts, simplebus, aw_ts_driver, aw_ts_devclass, 0, 0);
diff --git a/sys/arm/allwinner/aw_usb3phy.c b/sys/arm/allwinner/aw_usb3phy.c
new file mode 100644
index 000000000000..f75622f1755d
--- /dev/null
+++ b/sys/arm/allwinner/aw_usb3phy.c
@@ -0,0 +1,299 @@
+/* $NetBSD: sunxi_usb3phy.c,v 1.1 2018/05/01 23:59:42 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2018 Jared McNeill <jmcneill@invisible.ca>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Allwinner USB3PHY
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/gpio.h>
+#include <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/extres/regulator/regulator.h>
+#include <dev/extres/phy/phy_usb.h>
+
+#include "phynode_if.h"
+
+#define USB3PHY_APP 0x00
+#define APP_FORCE_VBUS (0x3 << 12)
+
+#define USB3PHY_PIPE_CLOCK_CONTROL 0x14
+#define PCC_PIPE_CLK_OPEN (1 << 6)
+
+#define USB3PHY_PHY_TUNE_LOW 0x18
+#define PTL_MAGIC 0x0047fc87
+
+#define USB3PHY_PHY_TUNE_HIGH 0x1c
+#define PTH_TX_DEEMPH_3P5DB (0x1F << 19)
+#define PTH_TX_DEEMPH_6DB (0x3F << 13)
+#define PTH_TX_SWING_FULL (0x7F << 6)
+#define PTH_LOS_BIAS (0x7 << 3)
+#define PTH_TX_BOOST_LVL (0x7 << 0)
+
+#define USB3PHY_PHY_EXTERNAL_CONTROL 0x20
+#define PEC_REF_SSP_EN (1 << 26)
+#define PEC_SSC_EN (1 << 24)
+#define PEC_EXTERN_VBUS (0x3 << 1)
+
+#define __LOWEST_SET_BIT(__mask) ((((__mask) - 1) & (__mask)) ^ (__mask))
+#define __SHIFTIN(__x, __mask) ((__x) * __LOWEST_SET_BIT(__mask))
+
+static struct ofw_compat_data compat_data[] = {
+ { "allwinner,sun50i-h6-usb3-phy", 1 },
+ { NULL, 0 }
+};
+
+static struct resource_spec aw_usb3phy_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+struct awusb3phy_softc {
+ struct resource * res;
+ regulator_t reg;
+ int mode;
+};
+
+ /* Phy class and methods. */
+static int awusb3phy_phy_enable(struct phynode *phy, bool enable);
+static int awusb3phy_get_mode(struct phynode *phy, int *mode);
+static int awusb3phy_set_mode(struct phynode *phy, int mode);
+static phynode_usb_method_t awusb3phy_phynode_methods[] = {
+ PHYNODEMETHOD(phynode_enable, awusb3phy_phy_enable),
+ PHYNODEMETHOD(phynode_usb_get_mode, awusb3phy_get_mode),
+ PHYNODEMETHOD(phynode_usb_set_mode, awusb3phy_set_mode),
+
+ PHYNODEMETHOD_END
+};
+DEFINE_CLASS_1(awusb3phy_phynode, awusb3phy_phynode_class, awusb3phy_phynode_methods,
+ sizeof(struct phynode_usb_sc), phynode_usb_class);
+
+#define RD4(res, o) bus_read_4(res, (o))
+#define WR4(res, o, v) bus_write_4(res, (o), (v))
+
+static int
+awusb3phy_phy_enable(struct phynode *phynode, bool enable)
+{
+ struct awusb3phy_softc *sc;
+ device_t dev;
+ uint32_t val;
+ int error = 0;
+
+ dev = phynode_get_device(phynode);
+ sc = device_get_softc(dev);
+
+ device_printf(dev, "%s: called\n", __func__);
+
+ if (enable) {
+ val = RD4(sc->res, USB3PHY_PHY_EXTERNAL_CONTROL);
+ device_printf(dev, "EXTERNAL_CONTROL: %x\n", val);
+ val |= PEC_EXTERN_VBUS;
+ val |= PEC_SSC_EN;
+ val |= PEC_REF_SSP_EN;
+ device_printf(dev, "EXTERNAL_CONTROL: %x\n", val);
+ WR4(sc->res, USB3PHY_PHY_EXTERNAL_CONTROL, val);
+
+ val = RD4(sc->res, USB3PHY_PIPE_CLOCK_CONTROL);
+ device_printf(dev, "PIPE_CONTROL: %x\n", val);
+ val |= PCC_PIPE_CLK_OPEN;
+ device_printf(dev, "PIPE_CONTROL: %x\n", val);
+ WR4(sc->res, USB3PHY_PIPE_CLOCK_CONTROL, val);
+
+ val = RD4(sc->res, USB3PHY_APP);
+ device_printf(dev, "APP: %x\n", val);
+ val |= APP_FORCE_VBUS;
+ device_printf(dev, "APP: %x\n", val);
+ WR4(sc->res, USB3PHY_APP, val);
+
+ WR4(sc->res, USB3PHY_PHY_TUNE_LOW, PTL_MAGIC);
+
+ val = RD4(sc->res, USB3PHY_PHY_TUNE_HIGH);
+ device_printf(dev, "PHY_TUNE_HIGH: %x\n", val);
+ val |= PTH_TX_BOOST_LVL;
+ val |= PTH_LOS_BIAS;
+ val &= ~PTH_TX_SWING_FULL;
+ val |= __SHIFTIN(0x55, PTH_TX_SWING_FULL);
+ val &= ~PTH_TX_DEEMPH_6DB;
+ val |= __SHIFTIN(0x20, PTH_TX_DEEMPH_6DB);
+ val &= ~PTH_TX_DEEMPH_3P5DB;
+ val |= __SHIFTIN(0x15, PTH_TX_DEEMPH_3P5DB);
+ device_printf(dev, "PHY_TUNE_HIGH: %x\n", val);
+ WR4(sc->res, USB3PHY_PHY_TUNE_HIGH, val);
+
+ if (sc->reg)
+ error = regulator_enable(sc->reg);
+ } else {
+ if (sc->reg)
+ error = regulator_disable(sc->reg);
+ }
+
+ if (error != 0) {
+ device_printf(dev,
+ "couldn't %s regulator for phy\n",
+ enable ? "enable" : "disable");
+ return (error);
+ }
+
+ return (0);
+}
+
+static int
+awusb3phy_get_mode(struct phynode *phynode, int *mode)
+{
+ struct awusb3phy_softc *sc;
+ device_t dev;
+
+ dev = phynode_get_device(phynode);
+ sc = device_get_softc(dev);
+
+ *mode = sc->mode;
+
+ return (0);
+}
+
+static int
+awusb3phy_set_mode(struct phynode *phynode, int mode)
+{
+ device_t dev;
+ intptr_t phy;
+ struct awusb3phy_softc *sc;
+
+ dev = phynode_get_device(phynode);
+ phy = phynode_get_id(phynode);
+ sc = device_get_softc(dev);
+
+ if (mode != PHY_USB_MODE_HOST)
+ return (EINVAL);
+
+ sc->mode = mode;
+
+ return (0);
+}
+
+static int
+awusb3phy_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner USB3PHY");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+awusb3phy_attach(device_t dev)
+{
+ struct phynode *phynode;
+ struct phynode_init_def phy_init;
+ struct awusb3phy_softc *sc;
+ clk_t clk;
+ hwreset_t rst;
+ phandle_t node;
+ int error, i;
+
+ sc = device_get_softc(dev);
+ node = ofw_bus_get_node(dev);
+
+ if (bus_alloc_resources(dev, aw_usb3phy_spec, &sc->res) != 0) {
+ device_printf(dev, "cannot allocate resources for device\n");
+ return (ENXIO);
+ }
+
+ /* Enable clocks */
+ for (i = 0; clk_get_by_ofw_index(dev, 0, i, &clk) == 0; i++) {
+ error = clk_enable(clk);
+ if (error != 0) {
+ device_printf(dev, "couldn't enable clock %s\n",
+ clk_get_name(clk));
+ return (error);
+ }
+ }
+
+ /* De-assert resets */
+ for (i = 0; hwreset_get_by_ofw_idx(dev, 0, i, &rst) == 0; i++) {
+ error = hwreset_deassert(rst);
+ if (error != 0) {
+ device_printf(dev, "couldn't de-assert reset %d\n",
+ i);
+ return (error);
+ }
+ }
+
+ /* Get regulators */
+ regulator_get_by_ofw_property(dev, node, "phy-supply", &sc->reg);
+
+ /* Create the phy */
+ phy_init.ofw_node = ofw_bus_get_node(dev);
+ phynode = phynode_create(dev, &awusb3phy_phynode_class,
+ &phy_init);
+ if (phynode == NULL) {
+ device_printf(dev, "failed to create USB PHY\n");
+ return (ENXIO);
+ }
+ if (phynode_register(phynode) == NULL) {
+ device_printf(dev, "failed to create USB PHY\n");
+ return (ENXIO);
+ }
+
+ return (error);
+}
+
+static device_method_t awusb3phy_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, awusb3phy_probe),
+ DEVMETHOD(device_attach, awusb3phy_attach),
+
+ DEVMETHOD_END
+};
+
+static driver_t awusb3phy_driver = {
+ "awusb3phy",
+ awusb3phy_methods,
+ sizeof(struct awusb3phy_softc)
+};
+
+static devclass_t awusb3phy_devclass;
+/* aw_usb3phy needs to come up after regulators/gpio/etc, but before ehci/ohci */
+EARLY_DRIVER_MODULE(awusb3phy, simplebus, awusb3phy_driver, awusb3phy_devclass,
+ 0, 0, BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE);
+MODULE_VERSION(awusb3phy, 1);
diff --git a/sys/arm/allwinner/aw_usbphy.c b/sys/arm/allwinner/aw_usbphy.c
new file mode 100644
index 000000000000..b0fd24a81eee
--- /dev/null
+++ b/sys/arm/allwinner/aw_usbphy.c
@@ -0,0 +1,530 @@
+/*-
+ * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Allwinner USB PHY
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/gpio.h>
+#include <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/gpio/gpiobusvar.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/extres/regulator/regulator.h>
+#include <dev/extres/phy/phy_usb.h>
+
+#include "phynode_if.h"
+
+enum awusbphy_type {
+ AWUSBPHY_TYPE_A10 = 1,
+ AWUSBPHY_TYPE_A13,
+ AWUSBPHY_TYPE_A20,
+ AWUSBPHY_TYPE_A31,
+ AWUSBPHY_TYPE_H3,
+ AWUSBPHY_TYPE_A64,
+ AWUSBPHY_TYPE_A83T,
+ AWUSBPHY_TYPE_H6,
+};
+
+struct aw_usbphy_conf {
+ int num_phys;
+ enum awusbphy_type phy_type;
+ bool pmu_unk1;
+ bool phy0_route;
+};
+
+static const struct aw_usbphy_conf a10_usbphy_conf = {
+ .num_phys = 3,
+ .phy_type = AWUSBPHY_TYPE_A10,
+ .pmu_unk1 = false,
+ .phy0_route = false,
+};
+
+static const struct aw_usbphy_conf a13_usbphy_conf = {
+ .num_phys = 2,
+ .phy_type = AWUSBPHY_TYPE_A13,
+ .pmu_unk1 = false,
+ .phy0_route = false,
+};
+
+static const struct aw_usbphy_conf a20_usbphy_conf = {
+ .num_phys = 3,
+ .phy_type = AWUSBPHY_TYPE_A20,
+ .pmu_unk1 = false,
+ .phy0_route = false,
+};
+
+static const struct aw_usbphy_conf a31_usbphy_conf = {
+ .num_phys = 3,
+ .phy_type = AWUSBPHY_TYPE_A31,
+ .pmu_unk1 = false,
+ .phy0_route = false,
+};
+
+static const struct aw_usbphy_conf h3_usbphy_conf = {
+ .num_phys = 4,
+ .phy_type = AWUSBPHY_TYPE_H3,
+ .pmu_unk1 = true,
+ .phy0_route = true,
+};
+
+static const struct aw_usbphy_conf a64_usbphy_conf = {
+ .num_phys = 2,
+ .phy_type = AWUSBPHY_TYPE_A64,
+ .pmu_unk1 = true,
+ .phy0_route = true,
+};
+
+static const struct aw_usbphy_conf a83t_usbphy_conf = {
+ .num_phys = 3,
+ .phy_type = AWUSBPHY_TYPE_A83T,
+ .pmu_unk1 = false,
+ .phy0_route = false,
+};
+
+static const struct aw_usbphy_conf h6_usbphy_conf = {
+ .num_phys = 4,
+ .phy_type = AWUSBPHY_TYPE_H6,
+ .pmu_unk1 = false,
+ .phy0_route = true,
+};
+
+static struct ofw_compat_data compat_data[] = {
+ { "allwinner,sun4i-a10-usb-phy", (uintptr_t)&a10_usbphy_conf },
+ { "allwinner,sun5i-a13-usb-phy", (uintptr_t)&a13_usbphy_conf },
+ { "allwinner,sun6i-a31-usb-phy", (uintptr_t)&a31_usbphy_conf },
+ { "allwinner,sun7i-a20-usb-phy", (uintptr_t)&a20_usbphy_conf },
+ { "allwinner,sun8i-h3-usb-phy", (uintptr_t)&h3_usbphy_conf },
+ { "allwinner,sun50i-a64-usb-phy", (uintptr_t)&a64_usbphy_conf },
+ { "allwinner,sun8i-a83t-usb-phy", (uintptr_t)&a83t_usbphy_conf },
+ { "allwinner,sun50i-h6-usb-phy", (uintptr_t)&h6_usbphy_conf },
+ { NULL, 0 }
+};
+
+struct awusbphy_softc {
+ struct resource * phy_ctrl;
+ struct resource ** pmu;
+ regulator_t * reg;
+ gpio_pin_t id_det_pin;
+ int id_det_valid;
+ gpio_pin_t vbus_det_pin;
+ int vbus_det_valid;
+ struct aw_usbphy_conf *phy_conf;
+ int mode;
+};
+
+ /* Phy class and methods. */
+static int awusbphy_phy_enable(struct phynode *phy, bool enable);
+static int awusbphy_get_mode(struct phynode *phy, int *mode);
+static int awusbphy_set_mode(struct phynode *phy, int mode);
+static phynode_usb_method_t awusbphy_phynode_methods[] = {
+ PHYNODEMETHOD(phynode_enable, awusbphy_phy_enable),
+ PHYNODEMETHOD(phynode_usb_get_mode, awusbphy_get_mode),
+ PHYNODEMETHOD(phynode_usb_set_mode, awusbphy_set_mode),
+
+ PHYNODEMETHOD_END
+};
+DEFINE_CLASS_1(awusbphy_phynode, awusbphy_phynode_class, awusbphy_phynode_methods,
+ sizeof(struct phynode_usb_sc), phynode_usb_class);
+
+#define RD4(res, o) bus_read_4(res, (o))
+#define WR4(res, o, v) bus_write_4(res, (o), (v))
+#define CLR4(res, o, m) WR4(res, o, RD4(res, o) & ~(m))
+#define SET4(res, o, m) WR4(res, o, RD4(res, o) | (m))
+
+#define PHY_CSR 0x00
+#define ID_PULLUP_EN (1 << 17)
+#define DPDM_PULLUP_EN (1 << 16)
+#define FORCE_ID (0x3 << 14)
+#define FORCE_ID_SHIFT 14
+#define FORCE_ID_LOW 2
+#define FORCE_ID_HIGH 3
+#define FORCE_VBUS_VALID (0x3 << 12)
+#define FORCE_VBUS_VALID_SHIFT 12
+#define FORCE_VBUS_VALID_LOW 2
+#define FORCE_VBUS_VALID_HIGH 3
+#define VBUS_CHANGE_DET (1 << 6)
+#define ID_CHANGE_DET (1 << 5)
+#define DPDM_CHANGE_DET (1 << 4)
+#define OTG_PHY_CFG 0x20
+#define OTG_PHY_ROUTE_OTG (1 << 0)
+#define PMU_IRQ_ENABLE 0x00
+#define PMU_AHB_INCR8 (1 << 10)
+#define PMU_AHB_INCR4 (1 << 9)
+#define PMU_AHB_INCRX_ALIGN (1 << 8)
+#define PMU_ULPI_BYPASS (1 << 0)
+#define PMU_UNK_H3 0x10
+#define PMU_UNK_H3_CLR 0x2
+
+static void
+awusbphy_configure(device_t dev, int phyno)
+{
+ struct awusbphy_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (sc->pmu[phyno] == NULL)
+ return;
+
+ if (sc->phy_conf->pmu_unk1 == true)
+ CLR4(sc->pmu[phyno], PMU_UNK_H3, PMU_UNK_H3_CLR);
+
+ SET4(sc->pmu[phyno], PMU_IRQ_ENABLE, PMU_ULPI_BYPASS |
+ PMU_AHB_INCR8 | PMU_AHB_INCR4 | PMU_AHB_INCRX_ALIGN);
+}
+
+static int
+awusbphy_init(device_t dev)
+{
+ struct awusbphy_softc *sc;
+ phandle_t node;
+ char pname[20];
+ uint32_t val;
+ int error, off, rid;
+ regulator_t reg;
+ hwreset_t rst;
+ clk_t clk;
+
+ sc = device_get_softc(dev);
+ node = ofw_bus_get_node(dev);
+
+ sc->phy_conf = (struct aw_usbphy_conf *)ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+
+ /* Get phy_ctrl region */
+ if (ofw_bus_find_string_index(node, "reg-names", "phy_ctrl", &rid) != 0) {
+ device_printf(dev, "Cannot locate phy control resource\n");
+ return (ENXIO);
+ }
+ sc->phy_ctrl = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->phy_ctrl == NULL) {
+ device_printf(dev, "Cannot allocate resource\n");
+ return (ENXIO);
+ }
+
+ /* Enable clocks */
+ for (off = 0; clk_get_by_ofw_index(dev, 0, off, &clk) == 0; off++) {
+ error = clk_enable(clk);
+ if (error != 0) {
+ device_printf(dev, "couldn't enable clock %s\n",
+ clk_get_name(clk));
+ return (error);
+ }
+ }
+
+ /* De-assert resets */
+ for (off = 0; hwreset_get_by_ofw_idx(dev, 0, off, &rst) == 0; off++) {
+ error = hwreset_deassert(rst);
+ if (error != 0) {
+ device_printf(dev, "couldn't de-assert reset %d\n",
+ off);
+ return (error);
+ }
+ }
+
+ /* Get GPIOs */
+ error = gpio_pin_get_by_ofw_property(dev, node, "usb0_id_det-gpios",
+ &sc->id_det_pin);
+ if (error == 0)
+ sc->id_det_valid = 1;
+ error = gpio_pin_get_by_ofw_property(dev, node, "usb0_vbus_det-gpios",
+ &sc->vbus_det_pin);
+ if (error == 0)
+ sc->vbus_det_valid = 1;
+
+ sc->reg = malloc(sizeof(*(sc->reg)) * sc->phy_conf->num_phys, M_DEVBUF,
+ M_WAITOK | M_ZERO);
+ sc->pmu = malloc(sizeof(*(sc->pmu)) * sc->phy_conf->num_phys, M_DEVBUF,
+ M_WAITOK | M_ZERO);
+ /* Get regulators */
+ for (off = 0; off < sc->phy_conf->num_phys; off++) {
+ snprintf(pname, sizeof(pname), "usb%d_vbus-supply", off);
+ if (regulator_get_by_ofw_property(dev, 0, pname, &reg) == 0)
+ sc->reg[off] = reg;
+
+ snprintf(pname, sizeof(pname), "pmu%d", off);
+ if (ofw_bus_find_string_index(node, "reg-names",
+ pname, &rid) != 0)
+ continue;
+
+ sc->pmu[off] = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->pmu[off] == NULL) {
+ device_printf(dev, "Cannot allocate resource\n");
+ return (ENXIO);
+ }
+ }
+
+ /* Enable OTG PHY for host mode */
+ val = bus_read_4(sc->phy_ctrl, PHY_CSR);
+ val &= ~(VBUS_CHANGE_DET | ID_CHANGE_DET | DPDM_CHANGE_DET);
+ val |= (ID_PULLUP_EN | DPDM_PULLUP_EN);
+ val &= ~FORCE_ID;
+ val |= (FORCE_ID_LOW << FORCE_ID_SHIFT);
+ val &= ~FORCE_VBUS_VALID;
+ val |= (FORCE_VBUS_VALID_HIGH << FORCE_VBUS_VALID_SHIFT);
+ bus_write_4(sc->phy_ctrl, PHY_CSR, val);
+
+ return (0);
+}
+
+static int
+awusbphy_vbus_detect(device_t dev, int *val)
+{
+ struct awusbphy_softc *sc;
+ bool active;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ if (sc->vbus_det_valid) {
+ error = gpio_pin_is_active(sc->vbus_det_pin, &active);
+ if (error != 0) {
+ device_printf(dev, "Cannot get status of id pin %d\n",
+ error);
+ return (error);
+ }
+ *val = active;
+ return (0);
+ }
+
+ /* TODO check vbus_power-supply. */
+
+ /*
+ * If there is no way to detect, assume present.
+ */
+ *val = 1;
+ return (0);
+}
+
+static int
+awusbphy_phy_enable(struct phynode *phynode, bool enable)
+{
+ device_t dev;
+ intptr_t phy;
+ struct awusbphy_softc *sc;
+ regulator_t reg;
+ int error, vbus_det;
+
+ dev = phynode_get_device(phynode);
+ phy = phynode_get_id(phynode);
+ sc = device_get_softc(dev);
+
+ if (phy < 0 || phy >= sc->phy_conf->num_phys)
+ return (ERANGE);
+
+ /* Configure PHY */
+ awusbphy_configure(dev, phy);
+
+ /* Regulators are optional. If not found, return success. */
+ reg = sc->reg[phy];
+ if (reg == NULL)
+ return (0);
+
+ if (phy == 0) {
+ /* If an external vbus is detected, do not enable phy 0 */
+ error = awusbphy_vbus_detect(dev, &vbus_det);
+ if (error)
+ goto out;
+
+ /* TODO check vbus_power-supply as well. */
+ if (sc->vbus_det_valid && vbus_det == 1) {
+ if (bootverbose)
+ device_printf(dev, "External VBUS detected, "
+ "not enabling the regulator\n");
+ return (0);
+ }
+ }
+ if (enable) {
+ /* Depending on the PHY we need to route OTG to OHCI/EHCI */
+ error = regulator_enable(reg);
+ } else
+ error = regulator_disable(reg);
+
+out:
+ if (error != 0) {
+ device_printf(dev,
+ "couldn't %s regulator for phy %jd\n",
+ enable ? "enable" : "disable", (intmax_t)phy);
+ return (error);
+ }
+
+ return (0);
+}
+
+static int
+awusbphy_get_mode(struct phynode *phynode, int *mode)
+{
+ struct awusbphy_softc *sc;
+ device_t dev;
+
+ dev = phynode_get_device(phynode);
+ sc = device_get_softc(dev);
+
+ *mode = sc->mode;
+
+ return (0);
+}
+
+static int
+awusbphy_set_mode(struct phynode *phynode, int mode)
+{
+ device_t dev;
+ intptr_t phy;
+ struct awusbphy_softc *sc;
+ uint32_t val;
+ int error, vbus_det;
+
+ dev = phynode_get_device(phynode);
+ phy = phynode_get_id(phynode);
+ sc = device_get_softc(dev);
+
+ if (phy != 0) {
+ if (mode != PHY_USB_MODE_HOST)
+ return (EINVAL);
+ return (0);
+ }
+
+ if (sc->mode == mode)
+ return (0);
+ if (mode == PHY_USB_MODE_OTG) /* TODO */
+ return (EOPNOTSUPP);
+
+ error = awusbphy_vbus_detect(dev, &vbus_det);
+ if (error != 0)
+ return (error);
+
+ val = bus_read_4(sc->phy_ctrl, PHY_CSR);
+ val &= ~(VBUS_CHANGE_DET | ID_CHANGE_DET | DPDM_CHANGE_DET);
+ val |= (ID_PULLUP_EN | DPDM_PULLUP_EN);
+ val &= ~FORCE_VBUS_VALID;
+ val |= (vbus_det ? FORCE_VBUS_VALID_HIGH : FORCE_VBUS_VALID_LOW) <<
+ FORCE_VBUS_VALID_SHIFT;
+ val &= ~FORCE_ID;
+
+ switch (mode) {
+ case PHY_USB_MODE_HOST:
+ val |= (FORCE_ID_LOW << FORCE_ID_SHIFT);
+ if (sc->phy_conf->phy0_route)
+ CLR4(sc->phy_ctrl, OTG_PHY_CFG, OTG_PHY_ROUTE_OTG);
+ break;
+ case PHY_USB_MODE_DEVICE:
+ val |= (FORCE_ID_HIGH << FORCE_ID_SHIFT);
+ if (sc->phy_conf->phy0_route)
+ SET4(sc->phy_ctrl, OTG_PHY_CFG, OTG_PHY_ROUTE_OTG);
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ bus_write_4(sc->phy_ctrl, PHY_CSR, val);
+ sc->mode = mode;
+ return (0);
+}
+
+static int
+awusbphy_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner USB PHY");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+awusbphy_attach(device_t dev)
+{
+ int error;
+ struct phynode *phynode;
+ struct phynode_init_def phy_init;
+ struct awusbphy_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+ error = awusbphy_init(dev);
+ if (error) {
+ device_printf(dev, "failed to initialize USB PHY, error %d\n",
+ error);
+ return (error);
+ }
+
+ /* Create and register phys. */
+ for (i = 0; i < sc->phy_conf->num_phys; i++) {
+ bzero(&phy_init, sizeof(phy_init));
+ phy_init.id = i;
+ phy_init.ofw_node = ofw_bus_get_node(dev);
+ phynode = phynode_create(dev, &awusbphy_phynode_class,
+ &phy_init);
+ if (phynode == NULL) {
+ device_printf(dev, "failed to create USB PHY\n");
+ return (ENXIO);
+ }
+ if (phynode_register(phynode) == NULL) {
+ device_printf(dev, "failed to create USB PHY\n");
+ return (ENXIO);
+ }
+ }
+
+ return (error);
+}
+
+static device_method_t awusbphy_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, awusbphy_probe),
+ DEVMETHOD(device_attach, awusbphy_attach),
+
+ DEVMETHOD_END
+};
+
+static driver_t awusbphy_driver = {
+ "awusbphy",
+ awusbphy_methods,
+ sizeof(struct awusbphy_softc)
+};
+
+static devclass_t awusbphy_devclass;
+/* aw_usbphy needs to come up after regulators/gpio/etc, but before ehci/ohci */
+EARLY_DRIVER_MODULE(awusbphy, simplebus, awusbphy_driver, awusbphy_devclass,
+ 0, 0, BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE);
+MODULE_VERSION(awusbphy, 1);
diff --git a/sys/arm/allwinner/aw_wdog.c b/sys/arm/allwinner/aw_wdog.c
new file mode 100644
index 000000000000..0fc5895eff9f
--- /dev/null
+++ b/sys/arm/allwinner/aw_wdog.c
@@ -0,0 +1,273 @@
+/*-
+ * Copyright (c) 2013 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * Copyright (c) 2016 Emmanuel Vadot <manu@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/eventhandler.h>
+#include <sys/systm.h>
+#include <sys/watchdog.h>
+#include <sys/reboot.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+#include <machine/machdep.h>
+
+#include <arm/allwinner/aw_wdog.h>
+
+#define READ(_sc, _r) bus_read_4((_sc)->res, (_r))
+#define WRITE(_sc, _r, _v) bus_write_4((_sc)->res, (_r), (_v))
+
+#define A10_WDOG_CTRL 0x00
+#define A31_WDOG_CTRL 0x10
+#define WDOG_CTRL_RESTART (1 << 0)
+#define A31_WDOG_CTRL_KEY (0xa57 << 1)
+#define A10_WDOG_MODE 0x04
+#define A31_WDOG_MODE 0x18
+#define A10_WDOG_MODE_INTVL_SHIFT 3
+#define A31_WDOG_MODE_INTVL_SHIFT 4
+#define A10_WDOG_MODE_RST_EN (1 << 1)
+#define WDOG_MODE_EN (1 << 0)
+#define A31_WDOG_CONFIG 0x14
+#define A31_WDOG_CONFIG_RST_EN_SYSTEM (1 << 0)
+#define A31_WDOG_CONFIG_RST_EN_INT (2 << 0)
+
+struct aw_wdog_interval {
+ uint64_t milliseconds;
+ unsigned int value;
+};
+
+struct aw_wdog_interval wd_intervals[] = {
+ { 500, 0 },
+ { 1000, 1 },
+ { 2000, 2 },
+ { 3000, 3 },
+ { 4000, 4 },
+ { 5000, 5 },
+ { 6000, 6 },
+ { 8000, 7 },
+ { 10000, 8 },
+ { 12000, 9 },
+ { 14000, 10 },
+ { 16000, 11 },
+ { 0, 0 } /* sentinel */
+};
+
+static struct aw_wdog_softc *aw_wdog_sc = NULL;
+
+struct aw_wdog_softc {
+ device_t dev;
+ struct resource * res;
+ struct mtx mtx;
+ uint8_t wdog_ctrl;
+ uint32_t wdog_ctrl_key;
+ uint8_t wdog_mode;
+ uint8_t wdog_mode_intvl_shift;
+ uint8_t wdog_mode_en;
+ uint8_t wdog_config;
+ uint8_t wdog_config_value;
+};
+
+#define A10_WATCHDOG 1
+#define A31_WATCHDOG 2
+
+static struct ofw_compat_data compat_data[] = {
+ {"allwinner,sun4i-a10-wdt", A10_WATCHDOG},
+ {"allwinner,sun6i-a31-wdt", A31_WATCHDOG},
+ {NULL, 0}
+};
+
+static void aw_wdog_watchdog_fn(void *, u_int, int *);
+static void aw_wdog_shutdown_fn(void *, int);
+
+static int
+aw_wdog_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+ switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data) {
+ case A10_WATCHDOG:
+ device_set_desc(dev, "Allwinner A10 Watchdog");
+ return (BUS_PROBE_DEFAULT);
+ case A31_WATCHDOG:
+ device_set_desc(dev, "Allwinner A31 Watchdog");
+ return (BUS_PROBE_DEFAULT);
+ }
+ return (ENXIO);
+}
+
+static int
+aw_wdog_attach(device_t dev)
+{
+ struct aw_wdog_softc *sc;
+ int rid;
+
+ if (aw_wdog_sc != NULL)
+ return (ENXIO);
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ rid = 0;
+ sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
+ if (sc->res == NULL) {
+ device_printf(dev, "could not allocate memory resource\n");
+ return (ENXIO);
+ }
+
+ aw_wdog_sc = sc;
+
+ switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data) {
+ case A10_WATCHDOG:
+ sc->wdog_ctrl = A10_WDOG_CTRL;
+ sc->wdog_mode = A10_WDOG_MODE;
+ sc->wdog_mode_intvl_shift = A10_WDOG_MODE_INTVL_SHIFT;
+ sc->wdog_mode_en = A10_WDOG_MODE_RST_EN | WDOG_MODE_EN;
+ break;
+ case A31_WATCHDOG:
+ sc->wdog_ctrl = A31_WDOG_CTRL;
+ sc->wdog_ctrl_key = A31_WDOG_CTRL_KEY;
+ sc->wdog_mode = A31_WDOG_MODE;
+ sc->wdog_mode_intvl_shift = A31_WDOG_MODE_INTVL_SHIFT;
+ sc->wdog_mode_en = WDOG_MODE_EN;
+ sc->wdog_config = A31_WDOG_CONFIG;
+ sc->wdog_config_value = A31_WDOG_CONFIG_RST_EN_SYSTEM;
+ break;
+ default:
+ bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->res);
+ return (ENXIO);
+ }
+
+ mtx_init(&sc->mtx, "AW Watchdog", "aw_wdog", MTX_DEF);
+ EVENTHANDLER_REGISTER(watchdog_list, aw_wdog_watchdog_fn, sc, 0);
+ EVENTHANDLER_REGISTER(shutdown_final, aw_wdog_shutdown_fn, sc,
+ SHUTDOWN_PRI_LAST - 1);
+
+ return (0);
+}
+
+static void
+aw_wdog_watchdog_fn(void *private, u_int cmd, int *error)
+{
+ struct aw_wdog_softc *sc;
+ uint64_t ms;
+ int i;
+
+ sc = private;
+ mtx_lock(&sc->mtx);
+
+ cmd &= WD_INTERVAL;
+
+ if (cmd > 0) {
+ ms = ((uint64_t)1 << (cmd & WD_INTERVAL)) / 1000000;
+ i = 0;
+ while (wd_intervals[i].milliseconds &&
+ (ms > wd_intervals[i].milliseconds))
+ i++;
+ if (wd_intervals[i].milliseconds) {
+ WRITE(sc, sc->wdog_mode,
+ (wd_intervals[i].value << sc->wdog_mode_intvl_shift) |
+ sc->wdog_mode_en);
+ WRITE(sc, sc->wdog_ctrl,
+ WDOG_CTRL_RESTART | sc->wdog_ctrl_key);
+ if (sc->wdog_config)
+ WRITE(sc, sc->wdog_config,
+ sc->wdog_config_value);
+ *error = 0;
+ }
+ else {
+ /*
+ * Can't arm
+ * disable watchdog as watchdog(9) requires
+ */
+ device_printf(sc->dev,
+ "Can't arm, timeout is more than 16 sec\n");
+ mtx_unlock(&sc->mtx);
+ WRITE(sc, sc->wdog_mode, 0);
+ return;
+ }
+ }
+ else
+ WRITE(sc, sc->wdog_mode, 0);
+
+ mtx_unlock(&sc->mtx);
+}
+
+static void
+aw_wdog_shutdown_fn(void *private, int howto)
+{
+ if ((howto & (RB_POWEROFF|RB_HALT)) == 0)
+ aw_wdog_watchdog_reset();
+}
+
+void
+aw_wdog_watchdog_reset(void)
+{
+
+ if (aw_wdog_sc == NULL) {
+ printf("Reset: watchdog device has not been initialized\n");
+ return;
+ }
+
+ WRITE(aw_wdog_sc, aw_wdog_sc->wdog_mode,
+ (wd_intervals[0].value << aw_wdog_sc->wdog_mode_intvl_shift) |
+ aw_wdog_sc->wdog_mode_en);
+ if (aw_wdog_sc->wdog_config)
+ WRITE(aw_wdog_sc, aw_wdog_sc->wdog_config,
+ aw_wdog_sc->wdog_config_value);
+ WRITE(aw_wdog_sc, aw_wdog_sc->wdog_ctrl,
+ WDOG_CTRL_RESTART | aw_wdog_sc->wdog_ctrl_key);
+ while(1)
+ ;
+
+}
+
+static device_method_t aw_wdog_methods[] = {
+ DEVMETHOD(device_probe, aw_wdog_probe),
+ DEVMETHOD(device_attach, aw_wdog_attach),
+
+ DEVMETHOD_END
+};
+
+static driver_t aw_wdog_driver = {
+ "aw_wdog",
+ aw_wdog_methods,
+ sizeof(struct aw_wdog_softc),
+};
+static devclass_t aw_wdog_devclass;
+
+DRIVER_MODULE(aw_wdog, simplebus, aw_wdog_driver, aw_wdog_devclass, 0, 0);
diff --git a/sys/arm/allwinner/aw_wdog.h b/sys/arm/allwinner/aw_wdog.h
new file mode 100644
index 000000000000..1aac03ae8c30
--- /dev/null
+++ b/sys/arm/allwinner/aw_wdog.h
@@ -0,0 +1,34 @@
+/*-
+ * Copyright (c) 2013 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+#ifndef __AW_WDOG_H__
+#define __AW_WDOG_H__
+
+void aw_wdog_watchdog_reset(void);
+
+#endif /*__AW_WDOG_H__*/
diff --git a/sys/arm/allwinner/axp209.c b/sys/arm/allwinner/axp209.c
new file mode 100644
index 000000000000..c4d2181e2ec7
--- /dev/null
+++ b/sys/arm/allwinner/axp209.c
@@ -0,0 +1,1424 @@
+/*-
+ * Copyright (c) 2015-2016 Emmanuel Vadot <manu@freebsd.org>
+ * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+* X-Power AXP209/AXP211 PMU for Allwinner SoCs
+*/
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/eventhandler.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/clock.h>
+#include <sys/time.h>
+#include <sys/bus.h>
+#include <sys/proc.h>
+#include <sys/gpio.h>
+#include <sys/reboot.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+
+#include <dev/iicbus/iiconf.h>
+
+#include <dev/gpio/gpiobusvar.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/extres/regulator/regulator.h>
+
+#include <arm/allwinner/axp209reg.h>
+
+#include "gpio_if.h"
+#include "regdev_if.h"
+
+MALLOC_DEFINE(M_AXP2XX_REG, "Axp2XX regulator", "Axp2XX power regulator");
+
+struct axp2xx_regdef {
+ intptr_t id;
+ char *name;
+ uint8_t enable_reg;
+ uint8_t enable_mask;
+ uint8_t voltage_reg;
+ uint8_t voltage_mask;
+ uint8_t voltage_shift;
+ int voltage_min;
+ int voltage_max;
+ int voltage_step;
+ int voltage_nstep;
+};
+
+static struct axp2xx_regdef axp209_regdefs[] = {
+ {
+ .id = AXP209_REG_ID_DCDC2,
+ .name = "dcdc2",
+ .enable_reg = AXP209_POWERCTL,
+ .enable_mask = AXP209_POWERCTL_DCDC2,
+ .voltage_reg = AXP209_REG_DCDC2_VOLTAGE,
+ .voltage_mask = 0x3f,
+ .voltage_min = 700,
+ .voltage_max = 2275,
+ .voltage_step = 25,
+ .voltage_nstep = 64,
+ },
+ {
+ .id = AXP209_REG_ID_DCDC3,
+ .name = "dcdc3",
+ .enable_reg = AXP209_POWERCTL,
+ .enable_mask = AXP209_POWERCTL_DCDC3,
+ .voltage_reg = AXP209_REG_DCDC3_VOLTAGE,
+ .voltage_mask = 0x7f,
+ .voltage_min = 700,
+ .voltage_max = 3500,
+ .voltage_step = 25,
+ .voltage_nstep = 128,
+ },
+ {
+ .id = AXP209_REG_ID_LDO2,
+ .name = "ldo2",
+ .enable_reg = AXP209_POWERCTL,
+ .enable_mask = AXP209_POWERCTL_LDO2,
+ .voltage_reg = AXP209_REG_LDO24_VOLTAGE,
+ .voltage_mask = 0xf0,
+ .voltage_shift = 4,
+ .voltage_min = 1800,
+ .voltage_max = 3300,
+ .voltage_step = 100,
+ .voltage_nstep = 16,
+ },
+ {
+ .id = AXP209_REG_ID_LDO3,
+ .name = "ldo3",
+ .enable_reg = AXP209_POWERCTL,
+ .enable_mask = AXP209_POWERCTL_LDO3,
+ .voltage_reg = AXP209_REG_LDO3_VOLTAGE,
+ .voltage_mask = 0x7f,
+ .voltage_min = 700,
+ .voltage_max = 2275,
+ .voltage_step = 25,
+ .voltage_nstep = 128,
+ },
+};
+
+static struct axp2xx_regdef axp221_regdefs[] = {
+ {
+ .id = AXP221_REG_ID_DLDO1,
+ .name = "dldo1",
+ .enable_reg = AXP221_POWERCTL_2,
+ .enable_mask = AXP221_POWERCTL2_DLDO1,
+ .voltage_reg = AXP221_REG_DLDO1_VOLTAGE,
+ .voltage_mask = 0x1f,
+ .voltage_min = 700,
+ .voltage_max = 3300,
+ .voltage_step = 100,
+ .voltage_nstep = 26,
+ },
+ {
+ .id = AXP221_REG_ID_DLDO2,
+ .name = "dldo2",
+ .enable_reg = AXP221_POWERCTL_2,
+ .enable_mask = AXP221_POWERCTL2_DLDO2,
+ .voltage_reg = AXP221_REG_DLDO2_VOLTAGE,
+ .voltage_mask = 0x1f,
+ .voltage_min = 700,
+ .voltage_max = 3300,
+ .voltage_step = 100,
+ .voltage_nstep = 26,
+ },
+ {
+ .id = AXP221_REG_ID_DLDO3,
+ .name = "dldo3",
+ .enable_reg = AXP221_POWERCTL_2,
+ .enable_mask = AXP221_POWERCTL2_DLDO3,
+ .voltage_reg = AXP221_REG_DLDO3_VOLTAGE,
+ .voltage_mask = 0x1f,
+ .voltage_min = 700,
+ .voltage_max = 3300,
+ .voltage_step = 100,
+ .voltage_nstep = 26,
+ },
+ {
+ .id = AXP221_REG_ID_DLDO4,
+ .name = "dldo4",
+ .enable_reg = AXP221_POWERCTL_2,
+ .enable_mask = AXP221_POWERCTL2_DLDO4,
+ .voltage_reg = AXP221_REG_DLDO4_VOLTAGE,
+ .voltage_mask = 0x1f,
+ .voltage_min = 700,
+ .voltage_max = 3300,
+ .voltage_step = 100,
+ .voltage_nstep = 26,
+ },
+ {
+ .id = AXP221_REG_ID_ELDO1,
+ .name = "eldo1",
+ .enable_reg = AXP221_POWERCTL_2,
+ .enable_mask = AXP221_POWERCTL2_ELDO1,
+ .voltage_reg = AXP221_REG_ELDO1_VOLTAGE,
+ .voltage_mask = 0x1f,
+ .voltage_min = 700,
+ .voltage_max = 3300,
+ .voltage_step = 100,
+ .voltage_nstep = 26,
+ },
+ {
+ .id = AXP221_REG_ID_ELDO2,
+ .name = "eldo2",
+ .enable_reg = AXP221_POWERCTL_2,
+ .enable_mask = AXP221_POWERCTL2_ELDO2,
+ .voltage_reg = AXP221_REG_ELDO2_VOLTAGE,
+ .voltage_mask = 0x1f,
+ .voltage_min = 700,
+ .voltage_max = 3300,
+ .voltage_step = 100,
+ .voltage_nstep = 26,
+ },
+ {
+ .id = AXP221_REG_ID_ELDO3,
+ .name = "eldo3",
+ .enable_reg = AXP221_POWERCTL_2,
+ .enable_mask = AXP221_POWERCTL2_ELDO3,
+ .voltage_reg = AXP221_REG_ELDO3_VOLTAGE,
+ .voltage_mask = 0x1f,
+ .voltage_min = 700,
+ .voltage_max = 3300,
+ .voltage_step = 100,
+ .voltage_nstep = 26,
+ },
+ {
+ .id = AXP221_REG_ID_DC5LDO,
+ .name = "dc5ldo",
+ .enable_reg = AXP221_POWERCTL_1,
+ .enable_mask = AXP221_POWERCTL1_DC5LDO,
+ .voltage_reg = AXP221_REG_DC5LDO_VOLTAGE,
+ .voltage_mask = 0x3,
+ .voltage_min = 700,
+ .voltage_max = 1400,
+ .voltage_step = 100,
+ .voltage_nstep = 7,
+ },
+ {
+ .id = AXP221_REG_ID_DCDC1,
+ .name = "dcdc1",
+ .enable_reg = AXP221_POWERCTL_1,
+ .enable_mask = AXP221_POWERCTL1_DCDC1,
+ .voltage_reg = AXP221_REG_DCDC1_VOLTAGE,
+ .voltage_mask = 0x1f,
+ .voltage_min = 1600,
+ .voltage_max = 3400,
+ .voltage_step = 100,
+ .voltage_nstep = 18,
+ },
+ {
+ .id = AXP221_REG_ID_DCDC2,
+ .name = "dcdc2",
+ .enable_reg = AXP221_POWERCTL_1,
+ .enable_mask = AXP221_POWERCTL1_DCDC2,
+ .voltage_reg = AXP221_REG_DCDC2_VOLTAGE,
+ .voltage_mask = 0x3f,
+ .voltage_min = 600,
+ .voltage_max = 1540,
+ .voltage_step = 20,
+ .voltage_nstep = 47,
+ },
+ {
+ .id = AXP221_REG_ID_DCDC3,
+ .name = "dcdc3",
+ .enable_reg = AXP221_POWERCTL_1,
+ .enable_mask = AXP221_POWERCTL1_DCDC3,
+ .voltage_reg = AXP221_REG_DCDC3_VOLTAGE,
+ .voltage_mask = 0x3f,
+ .voltage_min = 600,
+ .voltage_max = 1860,
+ .voltage_step = 20,
+ .voltage_nstep = 63,
+ },
+ {
+ .id = AXP221_REG_ID_DCDC4,
+ .name = "dcdc4",
+ .enable_reg = AXP221_POWERCTL_1,
+ .enable_mask = AXP221_POWERCTL1_DCDC4,
+ .voltage_reg = AXP221_REG_DCDC4_VOLTAGE,
+ .voltage_mask = 0x3f,
+ .voltage_min = 600,
+ .voltage_max = 1540,
+ .voltage_step = 20,
+ .voltage_nstep = 47,
+ },
+ {
+ .id = AXP221_REG_ID_DCDC5,
+ .name = "dcdc5",
+ .enable_reg = AXP221_POWERCTL_1,
+ .enable_mask = AXP221_POWERCTL1_DCDC5,
+ .voltage_reg = AXP221_REG_DCDC5_VOLTAGE,
+ .voltage_mask = 0x1f,
+ .voltage_min = 1000,
+ .voltage_max = 2550,
+ .voltage_step = 50,
+ .voltage_nstep = 31,
+ },
+ {
+ .id = AXP221_REG_ID_ALDO1,
+ .name = "aldo1",
+ .enable_reg = AXP221_POWERCTL_1,
+ .enable_mask = AXP221_POWERCTL1_ALDO1,
+ .voltage_reg = AXP221_REG_ALDO1_VOLTAGE,
+ .voltage_mask = 0x1f,
+ .voltage_min = 700,
+ .voltage_max = 3300,
+ .voltage_step = 100,
+ .voltage_nstep = 26,
+ },
+ {
+ .id = AXP221_REG_ID_ALDO2,
+ .name = "aldo2",
+ .enable_reg = AXP221_POWERCTL_1,
+ .enable_mask = AXP221_POWERCTL1_ALDO2,
+ .voltage_reg = AXP221_REG_ALDO2_VOLTAGE,
+ .voltage_mask = 0x1f,
+ .voltage_min = 700,
+ .voltage_max = 3300,
+ .voltage_step = 100,
+ .voltage_nstep = 26,
+ },
+ {
+ .id = AXP221_REG_ID_ALDO3,
+ .name = "aldo3",
+ .enable_reg = AXP221_POWERCTL_3,
+ .enable_mask = AXP221_POWERCTL3_ALDO3,
+ .voltage_reg = AXP221_REG_ALDO3_VOLTAGE,
+ .voltage_mask = 0x1f,
+ .voltage_min = 700,
+ .voltage_max = 3300,
+ .voltage_step = 100,
+ .voltage_nstep = 26,
+ },
+ {
+ .id = AXP221_REG_ID_DC1SW,
+ .name = "dc1sw",
+ .enable_reg = AXP221_POWERCTL_2,
+ .enable_mask = AXP221_POWERCTL2_DC1SW,
+ },
+};
+
+struct axp2xx_reg_sc {
+ struct regnode *regnode;
+ device_t base_dev;
+ struct axp2xx_regdef *def;
+ phandle_t xref;
+ struct regnode_std_param *param;
+};
+
+struct axp2xx_pins {
+ const char *name;
+ uint8_t ctrl_reg;
+ uint8_t status_reg;
+ uint8_t status_mask;
+ uint8_t status_shift;
+};
+
+/* GPIO3 is different, don't expose it for now */
+static const struct axp2xx_pins axp209_pins[] = {
+ {
+ .name = "GPIO0",
+ .ctrl_reg = AXP2XX_GPIO0_CTRL,
+ .status_reg = AXP2XX_GPIO_STATUS,
+ .status_mask = 0x10,
+ .status_shift = 4,
+ },
+ {
+ .name = "GPIO1",
+ .ctrl_reg = AXP2XX_GPIO1_CTRL,
+ .status_reg = AXP2XX_GPIO_STATUS,
+ .status_mask = 0x20,
+ .status_shift = 5,
+ },
+ {
+ .name = "GPIO2",
+ .ctrl_reg = AXP209_GPIO2_CTRL,
+ .status_reg = AXP2XX_GPIO_STATUS,
+ .status_mask = 0x40,
+ .status_shift = 6,
+ },
+};
+
+static const struct axp2xx_pins axp221_pins[] = {
+ {
+ .name = "GPIO0",
+ .ctrl_reg = AXP2XX_GPIO0_CTRL,
+ .status_reg = AXP2XX_GPIO_STATUS,
+ .status_mask = 0x1,
+ .status_shift = 0x0,
+ },
+ {
+ .name = "GPIO1",
+ .ctrl_reg = AXP2XX_GPIO0_CTRL,
+ .status_reg = AXP2XX_GPIO_STATUS,
+ .status_mask = 0x2,
+ .status_shift = 0x1,
+ },
+};
+
+struct axp2xx_sensors {
+ int id;
+ const char *name;
+ const char *desc;
+ const char *format;
+ uint8_t enable_reg;
+ uint8_t enable_mask;
+ uint8_t value_reg;
+ uint8_t value_size;
+ uint8_t h_value_mask;
+ uint8_t h_value_shift;
+ uint8_t l_value_mask;
+ uint8_t l_value_shift;
+ int value_step;
+ int value_convert;
+};
+
+static const struct axp2xx_sensors axp209_sensors[] = {
+ {
+ .id = AXP209_ACVOLT,
+ .name = "acvolt",
+ .desc = "AC Voltage (microvolt)",
+ .format = "I",
+ .enable_reg = AXP2XX_ADC_ENABLE1,
+ .enable_mask = AXP209_ADC1_ACVOLT,
+ .value_reg = AXP209_ACIN_VOLTAGE,
+ .value_size = 2,
+ .h_value_mask = 0xff,
+ .h_value_shift = 4,
+ .l_value_mask = 0xf,
+ .l_value_shift = 0,
+ .value_step = AXP209_VOLT_STEP,
+ },
+ {
+ .id = AXP209_ACCURRENT,
+ .name = "accurrent",
+ .desc = "AC Current (microAmpere)",
+ .format = "I",
+ .enable_reg = AXP2XX_ADC_ENABLE1,
+ .enable_mask = AXP209_ADC1_ACCURRENT,
+ .value_reg = AXP209_ACIN_CURRENT,
+ .value_size = 2,
+ .h_value_mask = 0xff,
+ .h_value_shift = 4,
+ .l_value_mask = 0xf,
+ .l_value_shift = 0,
+ .value_step = AXP209_ACCURRENT_STEP,
+ },
+ {
+ .id = AXP209_VBUSVOLT,
+ .name = "vbusvolt",
+ .desc = "VBUS Voltage (microVolt)",
+ .format = "I",
+ .enable_reg = AXP2XX_ADC_ENABLE1,
+ .enable_mask = AXP209_ADC1_VBUSVOLT,
+ .value_reg = AXP209_VBUS_VOLTAGE,
+ .value_size = 2,
+ .h_value_mask = 0xff,
+ .h_value_shift = 4,
+ .l_value_mask = 0xf,
+ .l_value_shift = 0,
+ .value_step = AXP209_VOLT_STEP,
+ },
+ {
+ .id = AXP209_VBUSCURRENT,
+ .name = "vbuscurrent",
+ .desc = "VBUS Current (microAmpere)",
+ .format = "I",
+ .enable_reg = AXP2XX_ADC_ENABLE1,
+ .enable_mask = AXP209_ADC1_VBUSCURRENT,
+ .value_reg = AXP209_VBUS_CURRENT,
+ .value_size = 2,
+ .h_value_mask = 0xff,
+ .h_value_shift = 4,
+ .l_value_mask = 0xf,
+ .l_value_shift = 0,
+ .value_step = AXP209_VBUSCURRENT_STEP,
+ },
+ {
+ .id = AXP2XX_BATVOLT,
+ .name = "batvolt",
+ .desc = "Battery Voltage (microVolt)",
+ .format = "I",
+ .enable_reg = AXP2XX_ADC_ENABLE1,
+ .enable_mask = AXP2XX_ADC1_BATVOLT,
+ .value_reg = AXP2XX_BAT_VOLTAGE,
+ .value_size = 2,
+ .h_value_mask = 0xff,
+ .h_value_shift = 4,
+ .l_value_mask = 0xf,
+ .l_value_shift = 0,
+ .value_step = AXP2XX_BATVOLT_STEP,
+ },
+ {
+ .id = AXP2XX_BATCHARGECURRENT,
+ .name = "batchargecurrent",
+ .desc = "Battery Charging Current (microAmpere)",
+ .format = "I",
+ .enable_reg = AXP2XX_ADC_ENABLE1,
+ .enable_mask = AXP2XX_ADC1_BATCURRENT,
+ .value_reg = AXP2XX_BAT_CHARGE_CURRENT,
+ .value_size = 2,
+ .h_value_mask = 0xff,
+ .h_value_shift = 5,
+ .l_value_mask = 0x1f,
+ .l_value_shift = 0,
+ .value_step = AXP2XX_BATCURRENT_STEP,
+ },
+ {
+ .id = AXP2XX_BATDISCHARGECURRENT,
+ .name = "batdischargecurrent",
+ .desc = "Battery Discharging Current (microAmpere)",
+ .format = "I",
+ .enable_reg = AXP2XX_ADC_ENABLE1,
+ .enable_mask = AXP2XX_ADC1_BATCURRENT,
+ .value_reg = AXP2XX_BAT_DISCHARGE_CURRENT,
+ .value_size = 2,
+ .h_value_mask = 0xff,
+ .h_value_shift = 5,
+ .l_value_mask = 0x1f,
+ .l_value_shift = 0,
+ .value_step = AXP2XX_BATCURRENT_STEP,
+ },
+ {
+ .id = AXP2XX_TEMP,
+ .name = "temp",
+ .desc = "Internal Temperature",
+ .format = "IK",
+ .enable_reg = AXP209_ADC_ENABLE2,
+ .enable_mask = AXP209_ADC2_TEMP,
+ .value_reg = AXP209_TEMPMON,
+ .value_size = 2,
+ .h_value_mask = 0xff,
+ .h_value_shift = 4,
+ .l_value_mask = 0xf,
+ .l_value_shift = 0,
+ .value_step = 1,
+ .value_convert = -(AXP209_TEMPMON_MIN - AXP209_0C_TO_K),
+ },
+};
+
+static const struct axp2xx_sensors axp221_sensors[] = {
+ {
+ .id = AXP2XX_BATVOLT,
+ .name = "batvolt",
+ .desc = "Battery Voltage (microVolt)",
+ .format = "I",
+ .enable_reg = AXP2XX_ADC_ENABLE1,
+ .enable_mask = AXP2XX_ADC1_BATVOLT,
+ .value_reg = AXP2XX_BAT_VOLTAGE,
+ .value_size = 2,
+ .h_value_mask = 0xff,
+ .h_value_shift = 4,
+ .l_value_mask = 0xf,
+ .l_value_shift = 0,
+ .value_step = AXP2XX_BATVOLT_STEP,
+ },
+ {
+ .id = AXP2XX_BATCHARGECURRENT,
+ .name = "batchargecurrent",
+ .desc = "Battery Charging Current (microAmpere)",
+ .format = "I",
+ .enable_reg = AXP2XX_ADC_ENABLE1,
+ .enable_mask = AXP2XX_ADC1_BATCURRENT,
+ .value_reg = AXP2XX_BAT_CHARGE_CURRENT,
+ .value_size = 2,
+ .h_value_mask = 0xff,
+ .h_value_shift = 5,
+ .l_value_mask = 0x1f,
+ .l_value_shift = 0,
+ .value_step = AXP2XX_BATCURRENT_STEP,
+ },
+ {
+ .id = AXP2XX_BATDISCHARGECURRENT,
+ .name = "batdischargecurrent",
+ .desc = "Battery Discharging Current (microAmpere)",
+ .format = "I",
+ .enable_reg = AXP2XX_ADC_ENABLE1,
+ .enable_mask = AXP2XX_ADC1_BATCURRENT,
+ .value_reg = AXP2XX_BAT_DISCHARGE_CURRENT,
+ .value_size = 2,
+ .h_value_mask = 0xff,
+ .h_value_shift = 5,
+ .l_value_mask = 0x1f,
+ .l_value_shift = 0,
+ .value_step = AXP2XX_BATCURRENT_STEP,
+ },
+ {
+ .id = AXP2XX_TEMP,
+ .name = "temp",
+ .desc = "Internal Temperature",
+ .format = "IK",
+ .enable_reg = AXP2XX_ADC_ENABLE1,
+ .enable_mask = AXP221_ADC1_TEMP,
+ .value_reg = AXP221_TEMPMON,
+ .value_size = 2,
+ .h_value_mask = 0xff,
+ .h_value_shift = 4,
+ .l_value_mask = 0xf,
+ .l_value_shift = 0,
+ .value_step = 1,
+ .value_convert = -(AXP221_TEMPMON_MIN - AXP209_0C_TO_K),
+ },
+};
+
+enum AXP2XX_TYPE {
+ AXP209 = 1,
+ AXP221,
+};
+
+struct axp2xx_softc {
+ device_t dev;
+ struct resource * res[1];
+ void * intrcookie;
+ struct intr_config_hook intr_hook;
+ struct mtx mtx;
+ uint8_t type;
+
+ /* GPIO */
+ device_t gpiodev;
+ int npins;
+ const struct axp2xx_pins *pins;
+
+ /* Sensors */
+ const struct axp2xx_sensors *sensors;
+ int nsensors;
+
+ /* Regulators */
+ struct axp2xx_reg_sc **regs;
+ int nregs;
+ struct axp2xx_regdef *regdefs;
+};
+
+static struct ofw_compat_data compat_data[] = {
+ { "x-powers,axp209", AXP209 },
+ { "x-powers,axp221", AXP221 },
+ { NULL, 0 }
+};
+
+static struct resource_spec axp_res_spec[] = {
+ { SYS_RES_IRQ, 0, RF_ACTIVE },
+ { -1, 0, 0 }
+};
+
+#define AXP_LOCK(sc) mtx_lock(&(sc)->mtx)
+#define AXP_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
+
+static int
+axp2xx_read(device_t dev, uint8_t reg, uint8_t *data, uint8_t size)
+{
+
+ return (iicdev_readfrom(dev, reg, data, size, IIC_INTRWAIT));
+}
+
+static int
+axp2xx_write(device_t dev, uint8_t reg, uint8_t data)
+{
+
+ return (iicdev_writeto(dev, reg, &data, sizeof(data), IIC_INTRWAIT));
+}
+
+static int
+axp2xx_regnode_init(struct regnode *regnode)
+{
+ return (0);
+}
+
+static int
+axp2xx_regnode_enable(struct regnode *regnode, bool enable, int *udelay)
+{
+ struct axp2xx_reg_sc *sc;
+ uint8_t val;
+
+ sc = regnode_get_softc(regnode);
+
+ axp2xx_read(sc->base_dev, sc->def->enable_reg, &val, 1);
+ if (enable)
+ val |= sc->def->enable_mask;
+ else
+ val &= ~sc->def->enable_mask;
+ axp2xx_write(sc->base_dev, sc->def->enable_reg, val);
+
+ *udelay = 0;
+
+ return (0);
+}
+
+static void
+axp2xx_regnode_reg_to_voltage(struct axp2xx_reg_sc *sc, uint8_t val, int *uv)
+{
+ if (val < sc->def->voltage_nstep)
+ *uv = sc->def->voltage_min + val * sc->def->voltage_step;
+ else
+ *uv = sc->def->voltage_min +
+ (sc->def->voltage_nstep * sc->def->voltage_step);
+ *uv *= 1000;
+}
+
+static int
+axp2xx_regnode_voltage_to_reg(struct axp2xx_reg_sc *sc, int min_uvolt,
+ int max_uvolt, uint8_t *val)
+{
+ uint8_t nval;
+ int nstep, uvolt;
+
+ nval = 0;
+ uvolt = sc->def->voltage_min * 1000;
+
+ for (nstep = 0; nstep < sc->def->voltage_nstep && uvolt < min_uvolt;
+ nstep++) {
+ ++nval;
+ uvolt += (sc->def->voltage_step * 1000);
+ }
+ if (uvolt > max_uvolt)
+ return (EINVAL);
+
+ *val = nval;
+ return (0);
+}
+
+static int
+axp2xx_regnode_status(struct regnode *regnode, int *status)
+{
+ struct axp2xx_reg_sc *sc;
+ uint8_t val;
+
+ sc = regnode_get_softc(regnode);
+
+ *status = 0;
+ axp2xx_read(sc->base_dev, sc->def->enable_reg, &val, 1);
+ if (val & sc->def->enable_mask)
+ *status = REGULATOR_STATUS_ENABLED;
+
+ return (0);
+}
+
+static int
+axp2xx_regnode_set_voltage(struct regnode *regnode, int min_uvolt,
+ int max_uvolt, int *udelay)
+{
+ struct axp2xx_reg_sc *sc;
+ uint8_t val;
+
+ sc = regnode_get_softc(regnode);
+
+ if (!sc->def->voltage_step)
+ return (ENXIO);
+
+ if (axp2xx_regnode_voltage_to_reg(sc, min_uvolt, max_uvolt, &val) != 0)
+ return (ERANGE);
+
+ axp2xx_write(sc->base_dev, sc->def->voltage_reg, val);
+
+ *udelay = 0;
+
+ return (0);
+}
+
+static int
+axp2xx_regnode_get_voltage(struct regnode *regnode, int *uvolt)
+{
+ struct axp2xx_reg_sc *sc;
+ uint8_t val;
+
+ sc = regnode_get_softc(regnode);
+
+ if (!sc->def->voltage_step)
+ return (ENXIO);
+
+ axp2xx_read(sc->base_dev, sc->def->voltage_reg, &val, 1);
+ axp2xx_regnode_reg_to_voltage(sc, val & sc->def->voltage_mask, uvolt);
+
+ return (0);
+}
+
+static regnode_method_t axp2xx_regnode_methods[] = {
+ /* Regulator interface */
+ REGNODEMETHOD(regnode_init, axp2xx_regnode_init),
+ REGNODEMETHOD(regnode_enable, axp2xx_regnode_enable),
+ REGNODEMETHOD(regnode_status, axp2xx_regnode_status),
+ REGNODEMETHOD(regnode_set_voltage, axp2xx_regnode_set_voltage),
+ REGNODEMETHOD(regnode_get_voltage, axp2xx_regnode_get_voltage),
+ REGNODEMETHOD(regnode_check_voltage, regnode_method_check_voltage),
+ REGNODEMETHOD_END
+};
+DEFINE_CLASS_1(axp2xx_regnode, axp2xx_regnode_class, axp2xx_regnode_methods,
+ sizeof(struct axp2xx_reg_sc), regnode_class);
+
+static int
+axp2xx_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ struct axp2xx_softc *sc;
+ device_t dev = arg1;
+ enum axp2xx_sensor sensor = arg2;
+ uint8_t data[2];
+ int val, error, i, found;
+
+ sc = device_get_softc(dev);
+
+ for (found = 0, i = 0; i < sc->nsensors; i++) {
+ if (sc->sensors[i].id == sensor) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (found == 0)
+ return (ENOENT);
+
+ error = axp2xx_read(dev, sc->sensors[i].value_reg, data, 2);
+ if (error != 0)
+ return (error);
+
+ val = ((data[0] & sc->sensors[i].h_value_mask) <<
+ sc->sensors[i].h_value_shift);
+ val |= ((data[1] & sc->sensors[i].l_value_mask) <<
+ sc->sensors[i].l_value_shift);
+ val *= sc->sensors[i].value_step;
+ val += sc->sensors[i].value_convert;
+
+ return sysctl_handle_opaque(oidp, &val, sizeof(val), req);
+}
+
+static void
+axp2xx_shutdown(void *devp, int howto)
+{
+ device_t dev;
+
+ if (!(howto & RB_POWEROFF))
+ return;
+ dev = (device_t)devp;
+
+ if (bootverbose)
+ device_printf(dev, "Shutdown AXP2xx\n");
+
+ axp2xx_write(dev, AXP2XX_SHUTBAT, AXP2XX_SHUTBAT_SHUTDOWN);
+}
+
+static void
+axp2xx_intr(void *arg)
+{
+ struct axp2xx_softc *sc;
+ uint8_t reg;
+
+ sc = arg;
+
+ axp2xx_read(sc->dev, AXP2XX_IRQ1_STATUS, &reg, 1);
+ if (reg) {
+ if (reg & AXP2XX_IRQ1_AC_OVERVOLT)
+ devctl_notify("PMU", "AC", "overvoltage", NULL);
+ if (reg & AXP2XX_IRQ1_VBUS_OVERVOLT)
+ devctl_notify("PMU", "USB", "overvoltage", NULL);
+ if (reg & AXP2XX_IRQ1_VBUS_LOW)
+ devctl_notify("PMU", "USB", "undervoltage", NULL);
+ if (reg & AXP2XX_IRQ1_AC_CONN)
+ devctl_notify("PMU", "AC", "plugged", NULL);
+ if (reg & AXP2XX_IRQ1_AC_DISCONN)
+ devctl_notify("PMU", "AC", "unplugged", NULL);
+ if (reg & AXP2XX_IRQ1_VBUS_CONN)
+ devctl_notify("PMU", "USB", "plugged", NULL);
+ if (reg & AXP2XX_IRQ1_VBUS_DISCONN)
+ devctl_notify("PMU", "USB", "unplugged", NULL);
+ axp2xx_write(sc->dev, AXP2XX_IRQ1_STATUS, AXP2XX_IRQ_ACK);
+ }
+
+ axp2xx_read(sc->dev, AXP2XX_IRQ2_STATUS, &reg, 1);
+ if (reg) {
+ if (reg & AXP2XX_IRQ2_BATT_CHARGED)
+ devctl_notify("PMU", "Battery", "charged", NULL);
+ if (reg & AXP2XX_IRQ2_BATT_CHARGING)
+ devctl_notify("PMU", "Battery", "charging", NULL);
+ if (reg & AXP2XX_IRQ2_BATT_CONN)
+ devctl_notify("PMU", "Battery", "connected", NULL);
+ if (reg & AXP2XX_IRQ2_BATT_DISCONN)
+ devctl_notify("PMU", "Battery", "disconnected", NULL);
+ if (reg & AXP2XX_IRQ2_BATT_TEMP_LOW)
+ devctl_notify("PMU", "Battery", "low-temp", NULL);
+ if (reg & AXP2XX_IRQ2_BATT_TEMP_OVER)
+ devctl_notify("PMU", "Battery", "high-temp", NULL);
+ axp2xx_write(sc->dev, AXP2XX_IRQ2_STATUS, AXP2XX_IRQ_ACK);
+ }
+
+ axp2xx_read(sc->dev, AXP2XX_IRQ3_STATUS, &reg, 1);
+ if (reg) {
+ if (reg & AXP2XX_IRQ3_PEK_SHORT)
+ shutdown_nice(RB_POWEROFF);
+ axp2xx_write(sc->dev, AXP2XX_IRQ3_STATUS, AXP2XX_IRQ_ACK);
+ }
+
+ axp2xx_read(sc->dev, AXP2XX_IRQ4_STATUS, &reg, 1);
+ if (reg) {
+ axp2xx_write(sc->dev, AXP2XX_IRQ4_STATUS, AXP2XX_IRQ_ACK);
+ }
+
+ axp2xx_read(sc->dev, AXP2XX_IRQ5_STATUS, &reg, 1);
+ if (reg) {
+ axp2xx_write(sc->dev, AXP2XX_IRQ5_STATUS, AXP2XX_IRQ_ACK);
+ }
+}
+
+static device_t
+axp2xx_gpio_get_bus(device_t dev)
+{
+ struct axp2xx_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ return (sc->gpiodev);
+}
+
+static int
+axp2xx_gpio_pin_max(device_t dev, int *maxpin)
+{
+ struct axp2xx_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ *maxpin = sc->npins - 1;
+
+ return (0);
+}
+
+static int
+axp2xx_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
+{
+ struct axp2xx_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (pin >= sc->npins)
+ return (EINVAL);
+
+ snprintf(name, GPIOMAXNAME, "%s", axp209_pins[pin].name);
+
+ return (0);
+}
+
+static int
+axp2xx_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
+{
+ struct axp2xx_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (pin >= sc->npins)
+ return (EINVAL);
+
+ *caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
+
+ return (0);
+}
+
+static int
+axp2xx_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
+{
+ struct axp2xx_softc *sc;
+ uint8_t data, func;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ if (pin >= sc->npins)
+ return (EINVAL);
+
+ AXP_LOCK(sc);
+ error = axp2xx_read(dev, sc->pins[pin].ctrl_reg, &data, 1);
+ if (error == 0) {
+ func = data & AXP2XX_GPIO_FUNC_MASK;
+ if (func == AXP2XX_GPIO_FUNC_INPUT)
+ *flags = GPIO_PIN_INPUT;
+ else if (func == AXP2XX_GPIO_FUNC_DRVLO ||
+ func == AXP2XX_GPIO_FUNC_DRVHI)
+ *flags = GPIO_PIN_OUTPUT;
+ else
+ *flags = 0;
+ }
+ AXP_UNLOCK(sc);
+
+ return (error);
+}
+
+static int
+axp2xx_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
+{
+ struct axp2xx_softc *sc;
+ uint8_t data;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ if (pin >= sc->npins)
+ return (EINVAL);
+
+ AXP_LOCK(sc);
+ error = axp2xx_read(dev, sc->pins[pin].ctrl_reg, &data, 1);
+ if (error == 0) {
+ data &= ~AXP2XX_GPIO_FUNC_MASK;
+ if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) != 0) {
+ if ((flags & GPIO_PIN_OUTPUT) == 0)
+ data |= AXP2XX_GPIO_FUNC_INPUT;
+ }
+ error = axp2xx_write(dev, sc->pins[pin].ctrl_reg, data);
+ }
+ AXP_UNLOCK(sc);
+
+ return (error);
+}
+
+static int
+axp2xx_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
+{
+ struct axp2xx_softc *sc;
+ uint8_t data, func;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ if (pin >= sc->npins)
+ return (EINVAL);
+
+ AXP_LOCK(sc);
+ error = axp2xx_read(dev, sc->pins[pin].ctrl_reg, &data, 1);
+ if (error == 0) {
+ func = data & AXP2XX_GPIO_FUNC_MASK;
+ switch (func) {
+ case AXP2XX_GPIO_FUNC_DRVLO:
+ *val = 0;
+ break;
+ case AXP2XX_GPIO_FUNC_DRVHI:
+ *val = 1;
+ break;
+ case AXP2XX_GPIO_FUNC_INPUT:
+ error = axp2xx_read(dev, sc->pins[pin].status_reg,
+ &data, 1);
+ if (error == 0) {
+ *val = (data & sc->pins[pin].status_mask);
+ *val >>= sc->pins[pin].status_shift;
+ }
+ break;
+ default:
+ error = EIO;
+ break;
+ }
+ }
+ AXP_UNLOCK(sc);
+
+ return (error);
+}
+
+static int
+axp2xx_gpio_pin_set(device_t dev, uint32_t pin, unsigned int val)
+{
+ struct axp2xx_softc *sc;
+ uint8_t data, func;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ if (pin >= sc->npins)
+ return (EINVAL);
+
+ AXP_LOCK(sc);
+ error = axp2xx_read(dev, sc->pins[pin].ctrl_reg, &data, 1);
+ if (error == 0) {
+ func = data & AXP2XX_GPIO_FUNC_MASK;
+ switch (func) {
+ case AXP2XX_GPIO_FUNC_DRVLO:
+ case AXP2XX_GPIO_FUNC_DRVHI:
+ /* GPIO2 can't be set to 1 */
+ if (pin == 2 && val == 1) {
+ error = EINVAL;
+ break;
+ }
+ data &= ~AXP2XX_GPIO_FUNC_MASK;
+ data |= val;
+ break;
+ default:
+ error = EIO;
+ break;
+ }
+ }
+ if (error == 0)
+ error = axp2xx_write(dev, sc->pins[pin].ctrl_reg, data);
+ AXP_UNLOCK(sc);
+
+ return (error);
+}
+
+static int
+axp2xx_gpio_pin_toggle(device_t dev, uint32_t pin)
+{
+ struct axp2xx_softc *sc;
+ uint8_t data, func;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ if (pin >= sc->npins)
+ return (EINVAL);
+
+ AXP_LOCK(sc);
+ error = axp2xx_read(dev, sc->pins[pin].ctrl_reg, &data, 1);
+ if (error == 0) {
+ func = data & AXP2XX_GPIO_FUNC_MASK;
+ switch (func) {
+ case AXP2XX_GPIO_FUNC_DRVLO:
+ /* Pin 2 can't be set to 1*/
+ if (pin == 2) {
+ error = EINVAL;
+ break;
+ }
+ data &= ~AXP2XX_GPIO_FUNC_MASK;
+ data |= AXP2XX_GPIO_FUNC_DRVHI;
+ break;
+ case AXP2XX_GPIO_FUNC_DRVHI:
+ data &= ~AXP2XX_GPIO_FUNC_MASK;
+ data |= AXP2XX_GPIO_FUNC_DRVLO;
+ break;
+ default:
+ error = EIO;
+ break;
+ }
+ }
+ if (error == 0)
+ error = axp2xx_write(dev, sc->pins[pin].ctrl_reg, data);
+ AXP_UNLOCK(sc);
+
+ return (error);
+}
+
+static int
+axp2xx_gpio_map_gpios(device_t bus, phandle_t dev, phandle_t gparent,
+ int gcells, pcell_t *gpios, uint32_t *pin, uint32_t *flags)
+{
+ struct axp2xx_softc *sc;
+
+ sc = device_get_softc(bus);
+
+ if (gpios[0] >= sc->npins)
+ return (EINVAL);
+
+ *pin = gpios[0];
+ *flags = gpios[1];
+
+ return (0);
+}
+
+static phandle_t
+axp2xx_get_node(device_t dev, device_t bus)
+{
+ return (ofw_bus_get_node(dev));
+}
+
+static struct axp2xx_reg_sc *
+axp2xx_reg_attach(device_t dev, phandle_t node,
+ struct axp2xx_regdef *def)
+{
+ struct axp2xx_reg_sc *reg_sc;
+ struct regnode_init_def initdef;
+ struct regnode *regnode;
+
+ memset(&initdef, 0, sizeof(initdef));
+ if (regulator_parse_ofw_stdparam(dev, node, &initdef) != 0) {
+ device_printf(dev, "cannot create regulator\n");
+ return (NULL);
+ }
+ if (initdef.std_param.min_uvolt == 0)
+ initdef.std_param.min_uvolt = def->voltage_min * 1000;
+ if (initdef.std_param.max_uvolt == 0)
+ initdef.std_param.max_uvolt = def->voltage_max * 1000;
+ initdef.id = def->id;
+ initdef.ofw_node = node;
+ regnode = regnode_create(dev, &axp2xx_regnode_class, &initdef);
+ if (regnode == NULL) {
+ device_printf(dev, "cannot create regulator\n");
+ return (NULL);
+ }
+
+ reg_sc = regnode_get_softc(regnode);
+ reg_sc->regnode = regnode;
+ reg_sc->base_dev = dev;
+ reg_sc->def = def;
+ reg_sc->xref = OF_xref_from_node(node);
+ reg_sc->param = regnode_get_stdparam(regnode);
+
+ regnode_register(regnode);
+
+ return (reg_sc);
+}
+
+static int
+axp2xx_regdev_map(device_t dev, phandle_t xref, int ncells, pcell_t *cells,
+ intptr_t *num)
+{
+ struct axp2xx_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+ for (i = 0; i < sc->nregs; i++) {
+ if (sc->regs[i] == NULL)
+ continue;
+ if (sc->regs[i]->xref == xref) {
+ *num = sc->regs[i]->def->id;
+ return (0);
+ }
+ }
+
+ return (ENXIO);
+}
+
+static void
+axp2xx_start(void *pdev)
+{
+ device_t dev;
+ struct axp2xx_softc *sc;
+ const char *pwr_name[] = {"Battery", "AC", "USB", "AC and USB"};
+ int i;
+ uint8_t reg, data;
+ uint8_t pwr_src;
+
+ dev = pdev;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ if (bootverbose) {
+ /*
+ * Read the Power State register.
+ * Shift the AC presence into bit 0.
+ * Shift the Battery presence into bit 1.
+ */
+ axp2xx_read(dev, AXP2XX_PSR, &data, 1);
+ pwr_src = ((data & AXP2XX_PSR_ACIN) >> AXP2XX_PSR_ACIN_SHIFT) |
+ ((data & AXP2XX_PSR_VBUS) >> (AXP2XX_PSR_VBUS_SHIFT - 1));
+
+ device_printf(dev, "Powered by %s\n",
+ pwr_name[pwr_src]);
+ }
+
+ /* Only enable interrupts that we are interested in */
+ axp2xx_write(dev, AXP2XX_IRQ1_ENABLE,
+ AXP2XX_IRQ1_AC_OVERVOLT |
+ AXP2XX_IRQ1_AC_DISCONN |
+ AXP2XX_IRQ1_AC_CONN |
+ AXP2XX_IRQ1_VBUS_OVERVOLT |
+ AXP2XX_IRQ1_VBUS_DISCONN |
+ AXP2XX_IRQ1_VBUS_CONN);
+ axp2xx_write(dev, AXP2XX_IRQ2_ENABLE,
+ AXP2XX_IRQ2_BATT_CONN |
+ AXP2XX_IRQ2_BATT_DISCONN |
+ AXP2XX_IRQ2_BATT_CHARGE_ACCT_ON |
+ AXP2XX_IRQ2_BATT_CHARGE_ACCT_OFF |
+ AXP2XX_IRQ2_BATT_CHARGING |
+ AXP2XX_IRQ2_BATT_CHARGED |
+ AXP2XX_IRQ2_BATT_TEMP_OVER |
+ AXP2XX_IRQ2_BATT_TEMP_LOW);
+ axp2xx_write(dev, AXP2XX_IRQ3_ENABLE,
+ AXP2XX_IRQ3_PEK_SHORT | AXP2XX_IRQ3_PEK_LONG);
+ axp2xx_write(dev, AXP2XX_IRQ4_ENABLE, AXP2XX_IRQ4_APS_LOW_2);
+ axp2xx_write(dev, AXP2XX_IRQ5_ENABLE, 0x0);
+
+ EVENTHANDLER_REGISTER(shutdown_final, axp2xx_shutdown, dev,
+ SHUTDOWN_PRI_LAST);
+
+ /* Enable ADC sensors */
+ for (i = 0; i < sc->nsensors; i++) {
+ if (axp2xx_read(dev, sc->sensors[i].enable_reg, &reg, 1) == -1) {
+ device_printf(dev, "Cannot enable sensor '%s'\n",
+ sc->sensors[i].name);
+ continue;
+ }
+ reg |= sc->sensors[i].enable_mask;
+ if (axp2xx_write(dev, sc->sensors[i].enable_reg, reg) == -1) {
+ device_printf(dev, "Cannot enable sensor '%s'\n",
+ sc->sensors[i].name);
+ continue;
+ }
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
+ OID_AUTO, sc->sensors[i].name,
+ CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
+ dev, sc->sensors[i].id, axp2xx_sysctl,
+ sc->sensors[i].format,
+ sc->sensors[i].desc);
+ }
+
+ if ((bus_setup_intr(dev, sc->res[0], INTR_TYPE_MISC | INTR_MPSAFE,
+ NULL, axp2xx_intr, sc, &sc->intrcookie)))
+ device_printf(dev, "unable to register interrupt handler\n");
+
+ config_intrhook_disestablish(&sc->intr_hook);
+}
+
+static int
+axp2xx_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+ {
+ case AXP209:
+ device_set_desc(dev, "X-Powers AXP209 Power Management Unit");
+ break;
+ case AXP221:
+ device_set_desc(dev, "X-Powers AXP221 Power Management Unit");
+ break;
+ default:
+ return (ENXIO);
+ }
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+axp2xx_attach(device_t dev)
+{
+ struct axp2xx_softc *sc;
+ struct axp2xx_reg_sc *reg;
+ struct axp2xx_regdef *regdefs;
+ phandle_t rnode, child;
+ int i;
+
+ sc = device_get_softc(dev);
+ mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ if (bus_alloc_resources(dev, axp_res_spec, sc->res) != 0) {
+ device_printf(dev, "can't allocate device resources\n");
+ return (ENXIO);
+ }
+
+ sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+ switch (sc->type) {
+ case AXP209:
+ sc->pins = axp209_pins;
+ sc->npins = nitems(axp209_pins);
+ sc->gpiodev = gpiobus_attach_bus(dev);
+
+ sc->sensors = axp209_sensors;
+ sc->nsensors = nitems(axp209_sensors);
+
+ regdefs = axp209_regdefs;
+ sc->nregs = nitems(axp209_regdefs);
+ break;
+ case AXP221:
+ sc->pins = axp221_pins;
+ sc->npins = nitems(axp221_pins);
+ sc->gpiodev = gpiobus_attach_bus(dev);
+
+ sc->sensors = axp221_sensors;
+ sc->nsensors = nitems(axp221_sensors);
+
+ regdefs = axp221_regdefs;
+ sc->nregs = nitems(axp221_regdefs);
+ break;
+ }
+
+ sc->regs = malloc(sizeof(struct axp2xx_reg_sc *) * sc->nregs,
+ M_AXP2XX_REG, M_WAITOK | M_ZERO);
+
+ sc->intr_hook.ich_func = axp2xx_start;
+ sc->intr_hook.ich_arg = dev;
+
+ if (config_intrhook_establish(&sc->intr_hook) != 0)
+ return (ENOMEM);
+
+ /* Attach known regulators that exist in the DT */
+ rnode = ofw_bus_find_child(ofw_bus_get_node(dev), "regulators");
+ if (rnode > 0) {
+ for (i = 0; i < sc->nregs; i++) {
+ child = ofw_bus_find_child(rnode,
+ regdefs[i].name);
+ if (child == 0)
+ continue;
+ reg = axp2xx_reg_attach(dev, child, &regdefs[i]);
+ if (reg == NULL) {
+ device_printf(dev,
+ "cannot attach regulator %s\n",
+ regdefs[i].name);
+ continue;
+ }
+ sc->regs[i] = reg;
+ if (bootverbose)
+ device_printf(dev, "Regulator %s attached\n",
+ regdefs[i].name);
+ }
+ }
+
+ return (0);
+}
+
+static device_method_t axp2xx_methods[] = {
+ DEVMETHOD(device_probe, axp2xx_probe),
+ DEVMETHOD(device_attach, axp2xx_attach),
+
+ /* GPIO interface */
+ DEVMETHOD(gpio_get_bus, axp2xx_gpio_get_bus),
+ DEVMETHOD(gpio_pin_max, axp2xx_gpio_pin_max),
+ DEVMETHOD(gpio_pin_getname, axp2xx_gpio_pin_getname),
+ DEVMETHOD(gpio_pin_getcaps, axp2xx_gpio_pin_getcaps),
+ DEVMETHOD(gpio_pin_getflags, axp2xx_gpio_pin_getflags),
+ DEVMETHOD(gpio_pin_setflags, axp2xx_gpio_pin_setflags),
+ DEVMETHOD(gpio_pin_get, axp2xx_gpio_pin_get),
+ DEVMETHOD(gpio_pin_set, axp2xx_gpio_pin_set),
+ DEVMETHOD(gpio_pin_toggle, axp2xx_gpio_pin_toggle),
+ DEVMETHOD(gpio_map_gpios, axp2xx_gpio_map_gpios),
+
+ /* Regdev interface */
+ DEVMETHOD(regdev_map, axp2xx_regdev_map),
+
+ /* OFW bus interface */
+ DEVMETHOD(ofw_bus_get_node, axp2xx_get_node),
+
+ DEVMETHOD_END
+};
+
+static driver_t axp2xx_driver = {
+ "axp2xx_pmu",
+ axp2xx_methods,
+ sizeof(struct axp2xx_softc),
+};
+
+static devclass_t axp2xx_devclass;
+extern devclass_t ofwgpiobus_devclass, gpioc_devclass;
+extern driver_t ofw_gpiobus_driver, gpioc_driver;
+
+EARLY_DRIVER_MODULE(axp2xx, iicbus, axp2xx_driver, axp2xx_devclass,
+ 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);
+EARLY_DRIVER_MODULE(ofw_gpiobus, axp2xx_pmu, ofw_gpiobus_driver,
+ ofwgpiobus_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);
+DRIVER_MODULE(gpioc, axp2xx_pmu, gpioc_driver, gpioc_devclass,
+ 0, 0);
+MODULE_VERSION(axp2xx, 1);
+MODULE_DEPEND(axp2xx, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
diff --git a/sys/arm/allwinner/axp209reg.h b/sys/arm/allwinner/axp209reg.h
new file mode 100644
index 000000000000..d6be123c5281
--- /dev/null
+++ b/sys/arm/allwinner/axp209reg.h
@@ -0,0 +1,246 @@
+/*-
+ * Copyright (c) 2016 Emmanuel Vadot <manu@freeebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _AXP209REG_H_
+#define _AXP209REG_H_
+
+/* Power State Register */
+#define AXP2XX_PSR 0x00
+#define AXP2XX_PSR_ACIN 0x80
+#define AXP2XX_PSR_ACIN_SHIFT 7
+#define AXP2XX_PSR_VBUS 0x20
+#define AXP2XX_PSR_VBUS_SHIFT 5
+
+/* Shutdown and battery control */
+#define AXP2XX_SHUTBAT 0x32
+#define AXP2XX_SHUTBAT_SHUTDOWN 0x80
+
+/* Voltage/Current Monitor */
+#define AXP209_ACIN_VOLTAGE 0x56
+#define AXP209_ACIN_CURRENT 0x58
+#define AXP209_VBUS_VOLTAGE 0x5A
+#define AXP209_VBUS_CURRENT 0x5C
+#define AXP2XX_BAT_VOLTAGE 0x78
+#define AXP2XX_BAT_CHARGE_CURRENT 0x7A
+#define AXP2XX_BAT_DISCHARGE_CURRENT 0x7C
+
+#define AXP209_VOLT_STEP 1700
+#define AXP2XX_BATVOLT_STEP 1100
+#define AXP209_ACCURRENT_STEP 625
+#define AXP209_VBUSCURRENT_STEP 375
+#define AXP2XX_BATCURRENT_STEP 500
+
+/* Temperature monitor */
+#define AXP209_TEMPMON 0x5e
+#define AXP209_TEMPMON_MIN 1447 /* -144.7C */
+#define AXP221_TEMPMON_MIN 2437 /* -243.7C */
+
+#define AXP221_TEMPMON 0x56
+
+/* Sensors conversion macros */
+#define AXP209_SENSOR_H(a) ((a) << 4)
+#define AXP209_SENSOR_L(a) ((a) & 0xf)
+#define AXP209_SENSOR_BAT_H(a) ((a) << 5)
+#define AXP209_SENSOR_BAT_L(a) ((a) & 0x1f)
+
+#define AXP209_0C_TO_K 2732
+
+/* ADC Sensors */
+#define AXP2XX_ADC_ENABLE1 0x82
+#define AXP209_ADC_ENABLE2 0x83
+
+#define AXP2XX_ADC1_BATVOLT (1 << 7)
+#define AXP2XX_ADC1_BATCURRENT (1 << 6)
+#define AXP209_ADC1_ACVOLT (1 << 5)
+#define AXP221_ADC1_TEMP (1 << 5)
+#define AXP209_ADC1_ACCURRENT (1 << 4)
+#define AXP209_ADC1_VBUSVOLT (1 << 3)
+#define AXP209_ADC1_VBUSCURRENT (1 << 2)
+#define AXP221_ADC1_TS_PIN (1 << 0)
+
+#define AXP209_ADC2_TEMP (1 << 7)
+
+/* Interrupt related registers */
+#define AXP2XX_IRQ1_ENABLE 0x40
+#define AXP2XX_IRQ1_STATUS 0x48
+#define AXP2XX_IRQ1_AC_OVERVOLT (1 << 7)
+#define AXP2XX_IRQ1_AC_CONN (1 << 6)
+#define AXP2XX_IRQ1_AC_DISCONN (1 << 5)
+#define AXP2XX_IRQ1_VBUS_OVERVOLT (1 << 4)
+#define AXP2XX_IRQ1_VBUS_CONN (1 << 3)
+#define AXP2XX_IRQ1_VBUS_DISCONN (1 << 2)
+#define AXP2XX_IRQ1_VBUS_LOW (1 << 1)
+
+#define AXP2XX_IRQ2_ENABLE 0x41
+#define AXP2XX_IRQ2_STATUS 0x49
+#define AXP2XX_IRQ2_BATT_CONN (1 << 7)
+#define AXP2XX_IRQ2_BATT_DISCONN (1 << 6)
+#define AXP2XX_IRQ2_BATT_CHARGE_ACCT_ON (1 << 5)
+#define AXP2XX_IRQ2_BATT_CHARGE_ACCT_OFF (1 << 4)
+#define AXP2XX_IRQ2_BATT_CHARGING (1 << 3)
+#define AXP2XX_IRQ2_BATT_CHARGED (1 << 2)
+#define AXP2XX_IRQ2_BATT_TEMP_OVER (1 << 1)
+#define AXP2XX_IRQ2_BATT_TEMP_LOW (1 << 0)
+
+#define AXP2XX_IRQ3_ENABLE 0x42
+#define AXP2XX_IRQ3_STATUS 0x4A
+#define AXP2XX_IRQ3_TEMP_OVER (1 << 7)
+#define AXP2XX_IRQ3_CHARGE_CURRENT_LOW (1 << 6)
+#define AXP2XX_IRQ3_DCDC2_LOW (1 << 4)
+#define AXP2XX_IRQ3_DCDC3_LOW (1 << 3)
+#define AXP2XX_IRQ3_LDO3_LOW (1 << 2)
+#define AXP2XX_IRQ3_PEK_SHORT (1 << 1)
+#define AXP2XX_IRQ3_PEK_LONG (1 << 0)
+
+#define AXP2XX_IRQ4_ENABLE 0x43
+#define AXP2XX_IRQ4_STATUS 0x4B
+#define AXP2XX_IRQ4_NOE_START (1 << 7)
+#define AXP2XX_IRQ4_NOE_SHUT (1 << 6)
+#define AXP2XX_IRQ4_VBUS_VALID (1 << 5)
+#define AXP2XX_IRQ4_VBUS_INVALID (1 << 4)
+#define AXP2XX_IRQ4_VBUS_SESSION (1 << 3)
+#define AXP2XX_IRQ4_VBUS_SESSION_END (1 << 2)
+#define AXP2XX_IRQ4_APS_LOW_1 (1 << 1)
+#define AXP2XX_IRQ4_APS_LOW_2 (1 << 0)
+
+#define AXP2XX_IRQ5_ENABLE 0x44
+#define AXP2XX_IRQ5_STATUS 0x4C
+#define AXP2XX_IRQ5_TIMER_EXPIRE (1 << 7)
+#define AXP2XX_IRQ5_PEK_RISE_EDGE (1 << 6)
+#define AXP2XX_IRQ5_PEK_FALL_EDGE (1 << 5)
+#define AXP2XX_IRQ5_GPIO3 (1 << 3)
+#define AXP2XX_IRQ5_GPIO2 (1 << 2)
+#define AXP2XX_IRQ5_GPIO1 (1 << 1)
+#define AXP2XX_IRQ5_GPIO0 (1 << 0)
+
+#define AXP2XX_IRQ_ACK 0xff
+
+/* GPIOs registers */
+#define AXP2XX_GPIO_FUNC_MASK 0x7
+
+#define AXP2XX_GPIO_FUNC_DRVLO 0x0
+#define AXP2XX_GPIO_FUNC_DRVHI 0x1
+#define AXP2XX_GPIO_FUNC_INPUT 0x2
+
+#define AXP2XX_GPIO0_CTRL 0x90
+#define AXP2XX_GPIO1_CTRL 0x92
+#define AXP209_GPIO2_CTRL 0x93
+#define AXP2XX_GPIO_STATUS 0x94
+
+/* Regulators registers */
+#define AXP209_POWERCTL 0x12
+#define AXP209_POWERCTL_LDO3 (1 << 6)
+#define AXP209_POWERCTL_DCDC2 (1 << 4)
+#define AXP209_POWERCTL_LDO4 (1 << 3)
+#define AXP209_POWERCTL_LDO2 (1 << 2)
+#define AXP209_POWERCTL_DCDC3 (1 << 1)
+
+#define AXP221_POWERCTL_1 0x10
+#define AXP221_POWERCTL1_ALDO2 (1 << 7)
+#define AXP221_POWERCTL1_ALDO1 (1 << 6)
+#define AXP221_POWERCTL1_DCDC5 (1 << 5)
+#define AXP221_POWERCTL1_DCDC4 (1 << 4)
+#define AXP221_POWERCTL1_DCDC3 (1 << 3)
+#define AXP221_POWERCTL1_DCDC2 (1 << 2)
+#define AXP221_POWERCTL1_DCDC1 (1 << 1)
+#define AXP221_POWERCTL1_DC5LDO (1 << 0)
+
+#define AXP221_POWERCTL_2 0x12
+#define AXP221_POWERCTL2_DC1SW (1 << 7)
+#define AXP221_POWERCTL2_DLDO4 (1 << 6)
+#define AXP221_POWERCTL2_DLDO3 (1 << 5)
+#define AXP221_POWERCTL2_DLDO2 (1 << 4)
+#define AXP221_POWERCTL2_DLDO1 (1 << 3)
+#define AXP221_POWERCTL2_ELDO3 (1 << 2)
+#define AXP221_POWERCTL2_ELDO2 (1 << 1)
+#define AXP221_POWERCTL2_ELDO1 (1 << 0)
+
+#define AXP221_POWERCTL_3 0x14
+#define AXP221_POWERCTL3_ALDO3 (1 << 7)
+
+#define AXP209_REG_DCDC2_VOLTAGE 0x23
+#define AXP209_REG_DCDC3_VOLTAGE 0x27
+#define AXP209_REG_LDO24_VOLTAGE 0x28
+#define AXP209_REG_LDO3_VOLTAGE 0x29
+
+#define AXP221_REG_DLDO1_VOLTAGE 0x15
+#define AXP221_REG_DLDO2_VOLTAGE 0x16
+#define AXP221_REG_DLDO3_VOLTAGE 0x17
+#define AXP221_REG_DLDO4_VOLTAGE 0x18
+#define AXP221_REG_ELDO1_VOLTAGE 0x19
+#define AXP221_REG_ELDO2_VOLTAGE 0x1A
+#define AXP221_REG_ELDO3_VOLTAGE 0x1B
+#define AXP221_REG_DC5LDO_VOLTAGE 0x1C
+#define AXP221_REG_DCDC1_VOLTAGE 0x21
+#define AXP221_REG_DCDC2_VOLTAGE 0x22
+#define AXP221_REG_DCDC3_VOLTAGE 0x23
+#define AXP221_REG_DCDC4_VOLTAGE 0x24
+#define AXP221_REG_DCDC5_VOLTAGE 0x25
+#define AXP221_REG_DCDC23_VRC 0x27
+#define AXP221_REG_ALDO1_VOLTAGE 0x28
+#define AXP221_REG_ALDO2_VOLTAGE 0x29
+#define AXP221_REG_ALDO3_VOLTAGE 0x2A
+
+enum axp2xx_sensor {
+ AXP209_ACVOLT,
+ AXP209_ACCURRENT,
+ AXP209_VBUSVOLT,
+ AXP209_VBUSCURRENT,
+ AXP2XX_TEMP,
+ AXP2XX_BATVOLT,
+ AXP2XX_BATCHARGECURRENT,
+ AXP2XX_BATDISCHARGECURRENT,
+};
+
+enum axp2xx_regulators {
+ AXP209_REG_ID_DCDC2,
+ AXP209_REG_ID_DCDC3,
+ AXP209_REG_ID_LDO1,
+ AXP209_REG_ID_LDO2,
+ AXP209_REG_ID_LDO3,
+ /* LDO4 is weird, need to find a correct way to handle it */
+ /* AXP209_REG_ID_LDO4, */
+ AXP209_REG_ID_LDO5,
+ AXP221_REG_ID_DLDO1,
+ AXP221_REG_ID_DLDO2,
+ AXP221_REG_ID_DLDO3,
+ AXP221_REG_ID_DLDO4,
+ AXP221_REG_ID_ELDO1,
+ AXP221_REG_ID_ELDO2,
+ AXP221_REG_ID_ELDO3,
+ AXP221_REG_ID_DC5LDO,
+ AXP221_REG_ID_DCDC1,
+ AXP221_REG_ID_DCDC2,
+ AXP221_REG_ID_DCDC3,
+ AXP221_REG_ID_DCDC4,
+ AXP221_REG_ID_DCDC5,
+ AXP221_REG_ID_ALDO1,
+ AXP221_REG_ID_ALDO2,
+ AXP221_REG_ID_ALDO3,
+ AXP221_REG_ID_DC1SW,
+};
+
+#endif /* _AXP209REG_H_ */
diff --git a/sys/arm/allwinner/axp81x.c b/sys/arm/allwinner/axp81x.c
new file mode 100644
index 000000000000..85971a7773c4
--- /dev/null
+++ b/sys/arm/allwinner/axp81x.c
@@ -0,0 +1,1650 @@
+/*-
+ * Copyright (c) 2018 Emmanuel Vadot <manu@freebsd.org>
+ * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * X-Powers AXP803/813/818 PMU for Allwinner SoCs
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/eventhandler.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/kernel.h>
+#include <sys/reboot.h>
+#include <sys/gpio.h>
+#include <sys/module.h>
+#include <machine/bus.h>
+
+#include <dev/iicbus/iicbus.h>
+#include <dev/iicbus/iiconf.h>
+
+#include <dev/gpio/gpiobusvar.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/extres/regulator/regulator.h>
+
+#include "gpio_if.h"
+#include "iicbus_if.h"
+#include "regdev_if.h"
+
+MALLOC_DEFINE(M_AXP8XX_REG, "AXP8xx regulator", "AXP8xx power regulator");
+
+#define AXP_POWERSRC 0x00
+#define AXP_POWERSRC_ACIN (1 << 7)
+#define AXP_POWERSRC_VBUS (1 << 5)
+#define AXP_POWERSRC_VBAT (1 << 3)
+#define AXP_POWERSRC_CHARING (1 << 2) /* Charging Direction */
+#define AXP_POWERSRC_SHORTED (1 << 1)
+#define AXP_POWERSRC_STARTUP (1 << 0)
+#define AXP_POWERMODE 0x01
+#define AXP_POWERMODE_BAT_CHARGING (1 << 6)
+#define AXP_POWERMODE_BAT_PRESENT (1 << 5)
+#define AXP_POWERMODE_BAT_VALID (1 << 4)
+#define AXP_ICTYPE 0x03
+#define AXP_POWERCTL1 0x10
+#define AXP_POWERCTL1_DCDC7 (1 << 6) /* AXP813/818 only */
+#define AXP_POWERCTL1_DCDC6 (1 << 5)
+#define AXP_POWERCTL1_DCDC5 (1 << 4)
+#define AXP_POWERCTL1_DCDC4 (1 << 3)
+#define AXP_POWERCTL1_DCDC3 (1 << 2)
+#define AXP_POWERCTL1_DCDC2 (1 << 1)
+#define AXP_POWERCTL1_DCDC1 (1 << 0)
+#define AXP_POWERCTL2 0x12
+#define AXP_POWERCTL2_DC1SW (1 << 7) /* AXP803 only */
+#define AXP_POWERCTL2_DLDO4 (1 << 6)
+#define AXP_POWERCTL2_DLDO3 (1 << 5)
+#define AXP_POWERCTL2_DLDO2 (1 << 4)
+#define AXP_POWERCTL2_DLDO1 (1 << 3)
+#define AXP_POWERCTL2_ELDO3 (1 << 2)
+#define AXP_POWERCTL2_ELDO2 (1 << 1)
+#define AXP_POWERCTL2_ELDO1 (1 << 0)
+#define AXP_POWERCTL3 0x13
+#define AXP_POWERCTL3_ALDO3 (1 << 7)
+#define AXP_POWERCTL3_ALDO2 (1 << 6)
+#define AXP_POWERCTL3_ALDO1 (1 << 5)
+#define AXP_POWERCTL3_FLDO3 (1 << 4) /* AXP813/818 only */
+#define AXP_POWERCTL3_FLDO2 (1 << 3)
+#define AXP_POWERCTL3_FLDO1 (1 << 2)
+#define AXP_VOLTCTL_DLDO1 0x15
+#define AXP_VOLTCTL_DLDO2 0x16
+#define AXP_VOLTCTL_DLDO3 0x17
+#define AXP_VOLTCTL_DLDO4 0x18
+#define AXP_VOLTCTL_ELDO1 0x19
+#define AXP_VOLTCTL_ELDO2 0x1A
+#define AXP_VOLTCTL_ELDO3 0x1B
+#define AXP_VOLTCTL_FLDO1 0x1C
+#define AXP_VOLTCTL_FLDO2 0x1D
+#define AXP_VOLTCTL_DCDC1 0x20
+#define AXP_VOLTCTL_DCDC2 0x21
+#define AXP_VOLTCTL_DCDC3 0x22
+#define AXP_VOLTCTL_DCDC4 0x23
+#define AXP_VOLTCTL_DCDC5 0x24
+#define AXP_VOLTCTL_DCDC6 0x25
+#define AXP_VOLTCTL_DCDC7 0x26
+#define AXP_VOLTCTL_ALDO1 0x28
+#define AXP_VOLTCTL_ALDO2 0x29
+#define AXP_VOLTCTL_ALDO3 0x2A
+#define AXP_VOLTCTL_STATUS (1 << 7)
+#define AXP_VOLTCTL_MASK 0x7f
+#define AXP_POWERBAT 0x32
+#define AXP_POWERBAT_SHUTDOWN (1 << 7)
+#define AXP_CHARGERCTL1 0x33
+#define AXP_CHARGERCTL1_MIN 0
+#define AXP_CHARGERCTL1_MAX 13
+#define AXP_CHARGERCTL1_CMASK 0xf
+#define AXP_IRQEN1 0x40
+#define AXP_IRQEN1_ACIN_HI (1 << 6)
+#define AXP_IRQEN1_ACIN_LO (1 << 5)
+#define AXP_IRQEN1_VBUS_HI (1 << 3)
+#define AXP_IRQEN1_VBUS_LO (1 << 2)
+#define AXP_IRQEN2 0x41
+#define AXP_IRQEN2_BAT_IN (1 << 7)
+#define AXP_IRQEN2_BAT_NO (1 << 6)
+#define AXP_IRQEN2_BATCHGC (1 << 3)
+#define AXP_IRQEN2_BATCHGD (1 << 2)
+#define AXP_IRQEN3 0x42
+#define AXP_IRQEN4 0x43
+#define AXP_IRQEN4_BATLVL_LO1 (1 << 1)
+#define AXP_IRQEN4_BATLVL_LO0 (1 << 0)
+#define AXP_IRQEN5 0x44
+#define AXP_IRQEN5_POKSIRQ (1 << 4)
+#define AXP_IRQEN5_POKLIRQ (1 << 3)
+#define AXP_IRQEN6 0x45
+#define AXP_IRQSTAT1 0x48
+#define AXP_IRQSTAT1_ACIN_HI (1 << 6)
+#define AXP_IRQSTAT1_ACIN_LO (1 << 5)
+#define AXP_IRQSTAT1_VBUS_HI (1 << 3)
+#define AXP_IRQSTAT1_VBUS_LO (1 << 2)
+#define AXP_IRQSTAT2 0x49
+#define AXP_IRQSTAT2_BAT_IN (1 << 7)
+#define AXP_IRQSTAT2_BAT_NO (1 << 6)
+#define AXP_IRQSTAT2_BATCHGC (1 << 3)
+#define AXP_IRQSTAT2_BATCHGD (1 << 2)
+#define AXP_IRQSTAT3 0x4a
+#define AXP_IRQSTAT4 0x4b
+#define AXP_IRQSTAT4_BATLVL_LO1 (1 << 1)
+#define AXP_IRQSTAT4_BATLVL_LO0 (1 << 0)
+#define AXP_IRQSTAT5 0x4c
+#define AXP_IRQSTAT5_POKSIRQ (1 << 4)
+#define AXP_IRQEN5_POKLIRQ (1 << 3)
+#define AXP_IRQSTAT6 0x4d
+#define AXP_BATSENSE_HI 0x78
+#define AXP_BATSENSE_LO 0x79
+#define AXP_BATCHG_HI 0x7a
+#define AXP_BATCHG_LO 0x7b
+#define AXP_BATDISCHG_HI 0x7c
+#define AXP_BATDISCHG_LO 0x7d
+#define AXP_GPIO0_CTRL 0x90
+#define AXP_GPIO0LDO_CTRL 0x91
+#define AXP_GPIO1_CTRL 0x92
+#define AXP_GPIO1LDO_CTRL 0x93
+#define AXP_GPIO_FUNC (0x7 << 0)
+#define AXP_GPIO_FUNC_SHIFT 0
+#define AXP_GPIO_FUNC_DRVLO 0
+#define AXP_GPIO_FUNC_DRVHI 1
+#define AXP_GPIO_FUNC_INPUT 2
+#define AXP_GPIO_FUNC_LDO_ON 3
+#define AXP_GPIO_FUNC_LDO_OFF 4
+#define AXP_GPIO_SIGBIT 0x94
+#define AXP_GPIO_PD 0x97
+#define AXP_FUEL_GAUGECTL 0xb8
+#define AXP_FUEL_GAUGECTL_EN (1 << 7)
+
+#define AXP_BAT_CAP 0xb9
+#define AXP_BAT_CAP_VALID (1 << 7)
+#define AXP_BAT_CAP_PERCENT 0x7f
+
+#define AXP_BAT_MAX_CAP_HI 0xe0
+#define AXP_BAT_MAX_CAP_VALID (1 << 7)
+#define AXP_BAT_MAX_CAP_LO 0xe1
+
+#define AXP_BAT_COULOMB_HI 0xe2
+#define AXP_BAT_COULOMB_VALID (1 << 7)
+#define AXP_BAT_COULOMB_LO 0xe3
+
+#define AXP_BAT_CAP_WARN 0xe6
+#define AXP_BAT_CAP_WARN_LV1 0xf0 /* Bits 4, 5, 6, 7 */
+#define AXP_BAP_CAP_WARN_LV1BASE 5 /* 5-20%, 1% per step */
+#define AXP_BAT_CAP_WARN_LV2 0xf /* Bits 0, 1, 2, 3 */
+
+/* Sensor conversion macros */
+#define AXP_SENSOR_BAT_H(hi) ((hi) << 4)
+#define AXP_SENSOR_BAT_L(lo) ((lo) & 0xf)
+#define AXP_SENSOR_COULOMB(hi, lo) (((hi & ~(1 << 7)) << 8) | (lo))
+
+static const struct {
+ const char *name;
+ uint8_t ctrl_reg;
+} axp8xx_pins[] = {
+ { "GPIO0", AXP_GPIO0_CTRL },
+ { "GPIO1", AXP_GPIO1_CTRL },
+};
+
+enum AXP8XX_TYPE {
+ AXP803 = 1,
+ AXP813,
+};
+
+static struct ofw_compat_data compat_data[] = {
+ { "x-powers,axp803", AXP803 },
+ { "x-powers,axp813", AXP813 },
+ { "x-powers,axp818", AXP813 },
+ { NULL, 0 }
+};
+
+static struct resource_spec axp8xx_spec[] = {
+ { SYS_RES_IRQ, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+struct axp8xx_regdef {
+ intptr_t id;
+ char *name;
+ char *supply_name;
+ uint8_t enable_reg;
+ uint8_t enable_mask;
+ uint8_t enable_value;
+ uint8_t disable_value;
+ uint8_t voltage_reg;
+ int voltage_min;
+ int voltage_max;
+ int voltage_step1;
+ int voltage_nstep1;
+ int voltage_step2;
+ int voltage_nstep2;
+};
+
+enum axp8xx_reg_id {
+ AXP8XX_REG_ID_DCDC1 = 100,
+ AXP8XX_REG_ID_DCDC2,
+ AXP8XX_REG_ID_DCDC3,
+ AXP8XX_REG_ID_DCDC4,
+ AXP8XX_REG_ID_DCDC5,
+ AXP8XX_REG_ID_DCDC6,
+ AXP813_REG_ID_DCDC7,
+ AXP803_REG_ID_DC1SW,
+ AXP8XX_REG_ID_DLDO1,
+ AXP8XX_REG_ID_DLDO2,
+ AXP8XX_REG_ID_DLDO3,
+ AXP8XX_REG_ID_DLDO4,
+ AXP8XX_REG_ID_ELDO1,
+ AXP8XX_REG_ID_ELDO2,
+ AXP8XX_REG_ID_ELDO3,
+ AXP8XX_REG_ID_ALDO1,
+ AXP8XX_REG_ID_ALDO2,
+ AXP8XX_REG_ID_ALDO3,
+ AXP8XX_REG_ID_FLDO1,
+ AXP8XX_REG_ID_FLDO2,
+ AXP813_REG_ID_FLDO3,
+ AXP8XX_REG_ID_GPIO0_LDO,
+ AXP8XX_REG_ID_GPIO1_LDO,
+};
+
+static struct axp8xx_regdef axp803_regdefs[] = {
+ {
+ .id = AXP803_REG_ID_DC1SW,
+ .name = "dc1sw",
+ .enable_reg = AXP_POWERCTL2,
+ .enable_mask = (uint8_t) AXP_POWERCTL2_DC1SW,
+ .enable_value = AXP_POWERCTL2_DC1SW,
+ },
+};
+
+static struct axp8xx_regdef axp813_regdefs[] = {
+ {
+ .id = AXP813_REG_ID_DCDC7,
+ .name = "dcdc7",
+ .enable_reg = AXP_POWERCTL1,
+ .enable_mask = (uint8_t) AXP_POWERCTL1_DCDC7,
+ .enable_value = AXP_POWERCTL1_DCDC7,
+ .voltage_reg = AXP_VOLTCTL_DCDC7,
+ .voltage_min = 600,
+ .voltage_max = 1520,
+ .voltage_step1 = 10,
+ .voltage_nstep1 = 50,
+ .voltage_step2 = 20,
+ .voltage_nstep2 = 21,
+ },
+};
+
+static struct axp8xx_regdef axp8xx_common_regdefs[] = {
+ {
+ .id = AXP8XX_REG_ID_DCDC1,
+ .name = "dcdc1",
+ .enable_reg = AXP_POWERCTL1,
+ .enable_mask = (uint8_t) AXP_POWERCTL1_DCDC1,
+ .enable_value = AXP_POWERCTL1_DCDC1,
+ .voltage_reg = AXP_VOLTCTL_DCDC1,
+ .voltage_min = 1600,
+ .voltage_max = 3400,
+ .voltage_step1 = 100,
+ .voltage_nstep1 = 18,
+ },
+ {
+ .id = AXP8XX_REG_ID_DCDC2,
+ .name = "dcdc2",
+ .enable_reg = AXP_POWERCTL1,
+ .enable_mask = (uint8_t) AXP_POWERCTL1_DCDC2,
+ .enable_value = AXP_POWERCTL1_DCDC2,
+ .voltage_reg = AXP_VOLTCTL_DCDC2,
+ .voltage_min = 500,
+ .voltage_max = 1300,
+ .voltage_step1 = 10,
+ .voltage_nstep1 = 70,
+ .voltage_step2 = 20,
+ .voltage_nstep2 = 5,
+ },
+ {
+ .id = AXP8XX_REG_ID_DCDC3,
+ .name = "dcdc3",
+ .enable_reg = AXP_POWERCTL1,
+ .enable_mask = (uint8_t) AXP_POWERCTL1_DCDC3,
+ .enable_value = AXP_POWERCTL1_DCDC3,
+ .voltage_reg = AXP_VOLTCTL_DCDC3,
+ .voltage_min = 500,
+ .voltage_max = 1300,
+ .voltage_step1 = 10,
+ .voltage_nstep1 = 70,
+ .voltage_step2 = 20,
+ .voltage_nstep2 = 5,
+ },
+ {
+ .id = AXP8XX_REG_ID_DCDC4,
+ .name = "dcdc4",
+ .enable_reg = AXP_POWERCTL1,
+ .enable_mask = (uint8_t) AXP_POWERCTL1_DCDC4,
+ .enable_value = AXP_POWERCTL1_DCDC4,
+ .voltage_reg = AXP_VOLTCTL_DCDC4,
+ .voltage_min = 500,
+ .voltage_max = 1300,
+ .voltage_step1 = 10,
+ .voltage_nstep1 = 70,
+ .voltage_step2 = 20,
+ .voltage_nstep2 = 5,
+ },
+ {
+ .id = AXP8XX_REG_ID_DCDC5,
+ .name = "dcdc5",
+ .enable_reg = AXP_POWERCTL1,
+ .enable_mask = (uint8_t) AXP_POWERCTL1_DCDC5,
+ .enable_value = AXP_POWERCTL1_DCDC5,
+ .voltage_reg = AXP_VOLTCTL_DCDC5,
+ .voltage_min = 800,
+ .voltage_max = 1840,
+ .voltage_step1 = 10,
+ .voltage_nstep1 = 42,
+ .voltage_step2 = 20,
+ .voltage_nstep2 = 36,
+ },
+ {
+ .id = AXP8XX_REG_ID_DCDC6,
+ .name = "dcdc6",
+ .enable_reg = AXP_POWERCTL1,
+ .enable_mask = (uint8_t) AXP_POWERCTL1_DCDC6,
+ .enable_value = AXP_POWERCTL1_DCDC6,
+ .voltage_reg = AXP_VOLTCTL_DCDC6,
+ .voltage_min = 600,
+ .voltage_max = 1520,
+ .voltage_step1 = 10,
+ .voltage_nstep1 = 50,
+ .voltage_step2 = 20,
+ .voltage_nstep2 = 21,
+ },
+ {
+ .id = AXP8XX_REG_ID_DLDO1,
+ .name = "dldo1",
+ .enable_reg = AXP_POWERCTL2,
+ .enable_mask = (uint8_t) AXP_POWERCTL2_DLDO1,
+ .enable_value = AXP_POWERCTL2_DLDO1,
+ .voltage_reg = AXP_VOLTCTL_DLDO1,
+ .voltage_min = 700,
+ .voltage_max = 3300,
+ .voltage_step1 = 100,
+ .voltage_nstep1 = 26,
+ },
+ {
+ .id = AXP8XX_REG_ID_DLDO2,
+ .name = "dldo2",
+ .enable_reg = AXP_POWERCTL2,
+ .enable_mask = (uint8_t) AXP_POWERCTL2_DLDO2,
+ .enable_value = AXP_POWERCTL2_DLDO2,
+ .voltage_reg = AXP_VOLTCTL_DLDO2,
+ .voltage_min = 700,
+ .voltage_max = 4200,
+ .voltage_step1 = 100,
+ .voltage_nstep1 = 27,
+ .voltage_step2 = 200,
+ .voltage_nstep2 = 4,
+ },
+ {
+ .id = AXP8XX_REG_ID_DLDO3,
+ .name = "dldo3",
+ .enable_reg = AXP_POWERCTL2,
+ .enable_mask = (uint8_t) AXP_POWERCTL2_DLDO3,
+ .enable_value = AXP_POWERCTL2_DLDO3,
+ .voltage_reg = AXP_VOLTCTL_DLDO3,
+ .voltage_min = 700,
+ .voltage_max = 3300,
+ .voltage_step1 = 100,
+ .voltage_nstep1 = 26,
+ },
+ {
+ .id = AXP8XX_REG_ID_DLDO4,
+ .name = "dldo4",
+ .enable_reg = AXP_POWERCTL2,
+ .enable_mask = (uint8_t) AXP_POWERCTL2_DLDO4,
+ .enable_value = AXP_POWERCTL2_DLDO4,
+ .voltage_reg = AXP_VOLTCTL_DLDO4,
+ .voltage_min = 700,
+ .voltage_max = 3300,
+ .voltage_step1 = 100,
+ .voltage_nstep1 = 26,
+ },
+ {
+ .id = AXP8XX_REG_ID_ALDO1,
+ .name = "aldo1",
+ .enable_reg = AXP_POWERCTL3,
+ .enable_mask = (uint8_t) AXP_POWERCTL3_ALDO1,
+ .enable_value = AXP_POWERCTL3_ALDO1,
+ .voltage_reg = AXP_VOLTCTL_ALDO1,
+ .voltage_min = 700,
+ .voltage_max = 3300,
+ .voltage_step1 = 100,
+ .voltage_nstep1 = 26,
+ },
+ {
+ .id = AXP8XX_REG_ID_ALDO2,
+ .name = "aldo2",
+ .enable_reg = AXP_POWERCTL3,
+ .enable_mask = (uint8_t) AXP_POWERCTL3_ALDO2,
+ .enable_value = AXP_POWERCTL3_ALDO2,
+ .voltage_reg = AXP_VOLTCTL_ALDO2,
+ .voltage_min = 700,
+ .voltage_max = 3300,
+ .voltage_step1 = 100,
+ .voltage_nstep1 = 26,
+ },
+ {
+ .id = AXP8XX_REG_ID_ALDO3,
+ .name = "aldo3",
+ .enable_reg = AXP_POWERCTL3,
+ .enable_mask = (uint8_t) AXP_POWERCTL3_ALDO3,
+ .enable_value = AXP_POWERCTL3_ALDO3,
+ .voltage_reg = AXP_VOLTCTL_ALDO3,
+ .voltage_min = 700,
+ .voltage_max = 3300,
+ .voltage_step1 = 100,
+ .voltage_nstep1 = 26,
+ },
+ {
+ .id = AXP8XX_REG_ID_ELDO1,
+ .name = "eldo1",
+ .enable_reg = AXP_POWERCTL2,
+ .enable_mask = (uint8_t) AXP_POWERCTL2_ELDO1,
+ .enable_value = AXP_POWERCTL2_ELDO1,
+ .voltage_reg = AXP_VOLTCTL_ELDO1,
+ .voltage_min = 700,
+ .voltage_max = 1900,
+ .voltage_step1 = 50,
+ .voltage_nstep1 = 24,
+ },
+ {
+ .id = AXP8XX_REG_ID_ELDO2,
+ .name = "eldo2",
+ .enable_reg = AXP_POWERCTL2,
+ .enable_mask = (uint8_t) AXP_POWERCTL2_ELDO2,
+ .enable_value = AXP_POWERCTL2_ELDO2,
+ .voltage_reg = AXP_VOLTCTL_ELDO2,
+ .voltage_min = 700,
+ .voltage_max = 1900,
+ .voltage_step1 = 50,
+ .voltage_nstep1 = 24,
+ },
+ {
+ .id = AXP8XX_REG_ID_ELDO3,
+ .name = "eldo3",
+ .enable_reg = AXP_POWERCTL2,
+ .enable_mask = (uint8_t) AXP_POWERCTL2_ELDO3,
+ .enable_value = AXP_POWERCTL2_ELDO3,
+ .voltage_reg = AXP_VOLTCTL_ELDO3,
+ .voltage_min = 700,
+ .voltage_max = 1900,
+ .voltage_step1 = 50,
+ .voltage_nstep1 = 24,
+ },
+ {
+ .id = AXP8XX_REG_ID_FLDO1,
+ .name = "fldo1",
+ .enable_reg = AXP_POWERCTL3,
+ .enable_mask = (uint8_t) AXP_POWERCTL3_FLDO1,
+ .enable_value = AXP_POWERCTL3_FLDO1,
+ .voltage_reg = AXP_VOLTCTL_FLDO1,
+ .voltage_min = 700,
+ .voltage_max = 1450,
+ .voltage_step1 = 50,
+ .voltage_nstep1 = 15,
+ },
+ {
+ .id = AXP8XX_REG_ID_FLDO2,
+ .name = "fldo2",
+ .enable_reg = AXP_POWERCTL3,
+ .enable_mask = (uint8_t) AXP_POWERCTL3_FLDO2,
+ .enable_value = AXP_POWERCTL3_FLDO2,
+ .voltage_reg = AXP_VOLTCTL_FLDO2,
+ .voltage_min = 700,
+ .voltage_max = 1450,
+ .voltage_step1 = 50,
+ .voltage_nstep1 = 15,
+ },
+ {
+ .id = AXP8XX_REG_ID_GPIO0_LDO,
+ .name = "ldo-io0",
+ .enable_reg = AXP_GPIO0_CTRL,
+ .enable_mask = (uint8_t) AXP_GPIO_FUNC,
+ .enable_value = AXP_GPIO_FUNC_LDO_ON,
+ .disable_value = AXP_GPIO_FUNC_LDO_OFF,
+ .voltage_reg = AXP_GPIO0LDO_CTRL,
+ .voltage_min = 700,
+ .voltage_max = 3300,
+ .voltage_step1 = 100,
+ .voltage_nstep1 = 26,
+ },
+ {
+ .id = AXP8XX_REG_ID_GPIO1_LDO,
+ .name = "ldo-io1",
+ .enable_reg = AXP_GPIO1_CTRL,
+ .enable_mask = (uint8_t) AXP_GPIO_FUNC,
+ .enable_value = AXP_GPIO_FUNC_LDO_ON,
+ .disable_value = AXP_GPIO_FUNC_LDO_OFF,
+ .voltage_reg = AXP_GPIO1LDO_CTRL,
+ .voltage_min = 700,
+ .voltage_max = 3300,
+ .voltage_step1 = 100,
+ .voltage_nstep1 = 26,
+ },
+};
+
+enum axp8xx_sensor {
+ AXP_SENSOR_ACIN_PRESENT,
+ AXP_SENSOR_VBUS_PRESENT,
+ AXP_SENSOR_BATT_PRESENT,
+ AXP_SENSOR_BATT_CHARGING,
+ AXP_SENSOR_BATT_CHARGE_STATE,
+ AXP_SENSOR_BATT_VOLTAGE,
+ AXP_SENSOR_BATT_CHARGE_CURRENT,
+ AXP_SENSOR_BATT_DISCHARGE_CURRENT,
+ AXP_SENSOR_BATT_CAPACITY_PERCENT,
+ AXP_SENSOR_BATT_MAXIMUM_CAPACITY,
+ AXP_SENSOR_BATT_CURRENT_CAPACITY,
+};
+
+enum battery_capacity_state {
+ BATT_CAPACITY_NORMAL = 1, /* normal cap in battery */
+ BATT_CAPACITY_WARNING, /* warning cap in battery */
+ BATT_CAPACITY_CRITICAL, /* critical cap in battery */
+ BATT_CAPACITY_HIGH, /* high cap in battery */
+ BATT_CAPACITY_MAX, /* maximum cap in battery */
+ BATT_CAPACITY_LOW /* low cap in battery */
+};
+
+struct axp8xx_sensors {
+ int id;
+ const char *name;
+ const char *desc;
+ const char *format;
+};
+
+static const struct axp8xx_sensors axp8xx_common_sensors[] = {
+ {
+ .id = AXP_SENSOR_ACIN_PRESENT,
+ .name = "acin",
+ .format = "I",
+ .desc = "ACIN Present",
+ },
+ {
+ .id = AXP_SENSOR_VBUS_PRESENT,
+ .name = "vbus",
+ .format = "I",
+ .desc = "VBUS Present",
+ },
+ {
+ .id = AXP_SENSOR_BATT_PRESENT,
+ .name = "bat",
+ .format = "I",
+ .desc = "Battery Present",
+ },
+ {
+ .id = AXP_SENSOR_BATT_CHARGING,
+ .name = "batcharging",
+ .format = "I",
+ .desc = "Battery Charging",
+ },
+ {
+ .id = AXP_SENSOR_BATT_CHARGE_STATE,
+ .name = "batchargestate",
+ .format = "I",
+ .desc = "Battery Charge State",
+ },
+ {
+ .id = AXP_SENSOR_BATT_VOLTAGE,
+ .name = "batvolt",
+ .format = "I",
+ .desc = "Battery Voltage",
+ },
+ {
+ .id = AXP_SENSOR_BATT_CHARGE_CURRENT,
+ .name = "batchargecurrent",
+ .format = "I",
+ .desc = "Average Battery Charging Current",
+ },
+ {
+ .id = AXP_SENSOR_BATT_DISCHARGE_CURRENT,
+ .name = "batdischargecurrent",
+ .format = "I",
+ .desc = "Average Battery Discharging Current",
+ },
+ {
+ .id = AXP_SENSOR_BATT_CAPACITY_PERCENT,
+ .name = "batcapacitypercent",
+ .format = "I",
+ .desc = "Battery Capacity Percentage",
+ },
+ {
+ .id = AXP_SENSOR_BATT_MAXIMUM_CAPACITY,
+ .name = "batmaxcapacity",
+ .format = "I",
+ .desc = "Battery Maximum Capacity",
+ },
+ {
+ .id = AXP_SENSOR_BATT_CURRENT_CAPACITY,
+ .name = "batcurrentcapacity",
+ .format = "I",
+ .desc = "Battery Current Capacity",
+ },
+};
+
+struct axp8xx_config {
+ const char *name;
+ int batsense_step; /* uV */
+ int charge_step; /* uA */
+ int discharge_step; /* uA */
+ int maxcap_step; /* uAh */
+ int coulomb_step; /* uAh */
+};
+
+static struct axp8xx_config axp803_config = {
+ .name = "AXP803",
+ .batsense_step = 1100,
+ .charge_step = 1000,
+ .discharge_step = 1000,
+ .maxcap_step = 1456,
+ .coulomb_step = 1456,
+};
+
+struct axp8xx_softc;
+
+struct axp8xx_reg_sc {
+ struct regnode *regnode;
+ device_t base_dev;
+ struct axp8xx_regdef *def;
+ phandle_t xref;
+ struct regnode_std_param *param;
+};
+
+struct axp8xx_softc {
+ struct resource *res;
+ uint16_t addr;
+ void *ih;
+ device_t gpiodev;
+ struct mtx mtx;
+ int busy;
+
+ int type;
+
+ /* Configs */
+ const struct axp8xx_config *config;
+
+ /* Sensors */
+ const struct axp8xx_sensors *sensors;
+ int nsensors;
+
+ /* Regulators */
+ struct axp8xx_reg_sc **regs;
+ int nregs;
+
+ /* Warning, shutdown thresholds */
+ int warn_thres;
+ int shut_thres;
+};
+
+#define AXP_LOCK(sc) mtx_lock(&(sc)->mtx)
+#define AXP_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
+static int axp8xx_regnode_set_voltage(struct regnode *regnode, int min_uvolt,
+ int max_uvolt, int *udelay);
+
+static int
+axp8xx_read(device_t dev, uint8_t reg, uint8_t *data, uint8_t size)
+{
+ struct axp8xx_softc *sc;
+ struct iic_msg msg[2];
+
+ sc = device_get_softc(dev);
+
+ msg[0].slave = sc->addr;
+ msg[0].flags = IIC_M_WR;
+ msg[0].len = 1;
+ msg[0].buf = &reg;
+
+ msg[1].slave = sc->addr;
+ msg[1].flags = IIC_M_RD;
+ msg[1].len = size;
+ msg[1].buf = data;
+
+ return (iicbus_transfer(dev, msg, 2));
+}
+
+static int
+axp8xx_write(device_t dev, uint8_t reg, uint8_t val)
+{
+ struct axp8xx_softc *sc;
+ struct iic_msg msg[2];
+
+ sc = device_get_softc(dev);
+
+ msg[0].slave = sc->addr;
+ msg[0].flags = IIC_M_WR;
+ msg[0].len = 1;
+ msg[0].buf = &reg;
+
+ msg[1].slave = sc->addr;
+ msg[1].flags = IIC_M_WR;
+ msg[1].len = 1;
+ msg[1].buf = &val;
+
+ return (iicbus_transfer(dev, msg, 2));
+}
+
+static int
+axp8xx_regnode_init(struct regnode *regnode)
+{
+ struct axp8xx_reg_sc *sc;
+ struct regnode_std_param *param;
+ int rv, udelay;
+
+ sc = regnode_get_softc(regnode);
+ param = regnode_get_stdparam(regnode);
+ if (param->min_uvolt == 0)
+ return (0);
+
+ /*
+ * Set the regulator at the correct voltage
+ * Do not enable it, this is will be done either by a
+ * consumer or by regnode_set_constraint if boot_on is true
+ */
+ rv = axp8xx_regnode_set_voltage(regnode, param->min_uvolt,
+ param->max_uvolt, &udelay);
+ if (rv != 0)
+ DELAY(udelay);
+
+ return (rv);
+}
+
+static int
+axp8xx_regnode_enable(struct regnode *regnode, bool enable, int *udelay)
+{
+ struct axp8xx_reg_sc *sc;
+ uint8_t val;
+
+ sc = regnode_get_softc(regnode);
+
+ if (bootverbose)
+ device_printf(sc->base_dev, "%sable %s (%s)\n",
+ enable ? "En" : "Dis",
+ regnode_get_name(regnode),
+ sc->def->name);
+
+ axp8xx_read(sc->base_dev, sc->def->enable_reg, &val, 1);
+ val &= ~sc->def->enable_mask;
+ if (enable)
+ val |= sc->def->enable_value;
+ else {
+ if (sc->def->disable_value)
+ val |= sc->def->disable_value;
+ else
+ val &= ~sc->def->enable_value;
+ }
+ axp8xx_write(sc->base_dev, sc->def->enable_reg, val);
+
+ *udelay = 0;
+
+ return (0);
+}
+
+static void
+axp8xx_regnode_reg_to_voltage(struct axp8xx_reg_sc *sc, uint8_t val, int *uv)
+{
+ if (val < sc->def->voltage_nstep1)
+ *uv = sc->def->voltage_min + val * sc->def->voltage_step1;
+ else
+ *uv = sc->def->voltage_min +
+ (sc->def->voltage_nstep1 * sc->def->voltage_step1) +
+ ((val - sc->def->voltage_nstep1) * sc->def->voltage_step2);
+ *uv *= 1000;
+}
+
+static int
+axp8xx_regnode_voltage_to_reg(struct axp8xx_reg_sc *sc, int min_uvolt,
+ int max_uvolt, uint8_t *val)
+{
+ uint8_t nval;
+ int nstep, uvolt;
+
+ nval = 0;
+ uvolt = sc->def->voltage_min * 1000;
+
+ for (nstep = 0; nstep < sc->def->voltage_nstep1 && uvolt < min_uvolt;
+ nstep++) {
+ ++nval;
+ uvolt += (sc->def->voltage_step1 * 1000);
+ }
+ for (nstep = 0; nstep < sc->def->voltage_nstep2 && uvolt < min_uvolt;
+ nstep++) {
+ ++nval;
+ uvolt += (sc->def->voltage_step2 * 1000);
+ }
+ if (uvolt > max_uvolt)
+ return (EINVAL);
+
+ *val = nval;
+ return (0);
+}
+
+static int
+axp8xx_regnode_set_voltage(struct regnode *regnode, int min_uvolt,
+ int max_uvolt, int *udelay)
+{
+ struct axp8xx_reg_sc *sc;
+ uint8_t val;
+
+ sc = regnode_get_softc(regnode);
+
+ if (bootverbose)
+ device_printf(sc->base_dev, "Setting %s (%s) to %d<->%d\n",
+ regnode_get_name(regnode),
+ sc->def->name,
+ min_uvolt, max_uvolt);
+
+ if (sc->def->voltage_step1 == 0)
+ return (ENXIO);
+
+ if (axp8xx_regnode_voltage_to_reg(sc, min_uvolt, max_uvolt, &val) != 0)
+ return (ERANGE);
+
+ axp8xx_write(sc->base_dev, sc->def->voltage_reg, val);
+
+ *udelay = 0;
+
+ return (0);
+}
+
+static int
+axp8xx_regnode_get_voltage(struct regnode *regnode, int *uvolt)
+{
+ struct axp8xx_reg_sc *sc;
+ uint8_t val;
+
+ sc = regnode_get_softc(regnode);
+
+ if (!sc->def->voltage_step1 || !sc->def->voltage_step2)
+ return (ENXIO);
+
+ axp8xx_read(sc->base_dev, sc->def->voltage_reg, &val, 1);
+ axp8xx_regnode_reg_to_voltage(sc, val & AXP_VOLTCTL_MASK, uvolt);
+
+ return (0);
+}
+
+static regnode_method_t axp8xx_regnode_methods[] = {
+ /* Regulator interface */
+ REGNODEMETHOD(regnode_init, axp8xx_regnode_init),
+ REGNODEMETHOD(regnode_enable, axp8xx_regnode_enable),
+ REGNODEMETHOD(regnode_set_voltage, axp8xx_regnode_set_voltage),
+ REGNODEMETHOD(regnode_get_voltage, axp8xx_regnode_get_voltage),
+ REGNODEMETHOD(regnode_check_voltage, regnode_method_check_voltage),
+ REGNODEMETHOD_END
+};
+DEFINE_CLASS_1(axp8xx_regnode, axp8xx_regnode_class, axp8xx_regnode_methods,
+ sizeof(struct axp8xx_reg_sc), regnode_class);
+
+static void
+axp8xx_shutdown(void *devp, int howto)
+{
+ device_t dev;
+
+ if ((howto & RB_POWEROFF) == 0)
+ return;
+
+ dev = devp;
+
+ if (bootverbose)
+ device_printf(dev, "Shutdown Axp8xx\n");
+
+ axp8xx_write(dev, AXP_POWERBAT, AXP_POWERBAT_SHUTDOWN);
+}
+
+static int
+axp8xx_sysctl_chargecurrent(SYSCTL_HANDLER_ARGS)
+{
+ device_t dev = arg1;
+ uint8_t data;
+ int val, error;
+
+ error = axp8xx_read(dev, AXP_CHARGERCTL1, &data, 1);
+ if (error != 0)
+ return (error);
+
+ if (bootverbose)
+ device_printf(dev, "Raw CHARGECTL1 val: 0x%0x\n", data);
+ val = (data & AXP_CHARGERCTL1_CMASK);
+ error = sysctl_handle_int(oidp, &val, 0, req);
+ if (error || !req->newptr) /* error || read request */
+ return (error);
+
+ if ((val < AXP_CHARGERCTL1_MIN) || (val > AXP_CHARGERCTL1_MAX))
+ return (EINVAL);
+
+ val |= (data & (AXP_CHARGERCTL1_CMASK << 4));
+ axp8xx_write(dev, AXP_CHARGERCTL1, val);
+
+ return (0);
+}
+
+static int
+axp8xx_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ struct axp8xx_softc *sc;
+ device_t dev = arg1;
+ enum axp8xx_sensor sensor = arg2;
+ const struct axp8xx_config *c;
+ uint8_t data;
+ int val, i, found, batt_val;
+ uint8_t lo, hi;
+
+ sc = device_get_softc(dev);
+ c = sc->config;
+
+ for (found = 0, i = 0; i < sc->nsensors; i++) {
+ if (sc->sensors[i].id == sensor) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (found == 0)
+ return (ENOENT);
+
+ switch (sensor) {
+ case AXP_SENSOR_ACIN_PRESENT:
+ if (axp8xx_read(dev, AXP_POWERSRC, &data, 1) == 0)
+ val = !!(data & AXP_POWERSRC_ACIN);
+ break;
+ case AXP_SENSOR_VBUS_PRESENT:
+ if (axp8xx_read(dev, AXP_POWERSRC, &data, 1) == 0)
+ val = !!(data & AXP_POWERSRC_VBUS);
+ break;
+ case AXP_SENSOR_BATT_PRESENT:
+ if (axp8xx_read(dev, AXP_POWERMODE, &data, 1) == 0) {
+ if (data & AXP_POWERMODE_BAT_VALID)
+ val = !!(data & AXP_POWERMODE_BAT_PRESENT);
+ }
+ break;
+ case AXP_SENSOR_BATT_CHARGING:
+ if (axp8xx_read(dev, AXP_POWERMODE, &data, 1) == 0)
+ val = !!(data & AXP_POWERMODE_BAT_CHARGING);
+ break;
+ case AXP_SENSOR_BATT_CHARGE_STATE:
+ if (axp8xx_read(dev, AXP_BAT_CAP, &data, 1) == 0 &&
+ (data & AXP_BAT_CAP_VALID) != 0) {
+ batt_val = (data & AXP_BAT_CAP_PERCENT);
+ if (batt_val <= sc->shut_thres)
+ val = BATT_CAPACITY_CRITICAL;
+ else if (batt_val <= sc->warn_thres)
+ val = BATT_CAPACITY_WARNING;
+ else
+ val = BATT_CAPACITY_NORMAL;
+ }
+ break;
+ case AXP_SENSOR_BATT_CAPACITY_PERCENT:
+ if (axp8xx_read(dev, AXP_BAT_CAP, &data, 1) == 0 &&
+ (data & AXP_BAT_CAP_VALID) != 0)
+ val = (data & AXP_BAT_CAP_PERCENT);
+ break;
+ case AXP_SENSOR_BATT_VOLTAGE:
+ if (axp8xx_read(dev, AXP_BATSENSE_HI, &hi, 1) == 0 &&
+ axp8xx_read(dev, AXP_BATSENSE_LO, &lo, 1) == 0) {
+ val = (AXP_SENSOR_BAT_H(hi) | AXP_SENSOR_BAT_L(lo));
+ val *= c->batsense_step;
+ }
+ break;
+ case AXP_SENSOR_BATT_CHARGE_CURRENT:
+ if (axp8xx_read(dev, AXP_POWERSRC, &data, 1) == 0 &&
+ (data & AXP_POWERSRC_CHARING) != 0 &&
+ axp8xx_read(dev, AXP_BATCHG_HI, &hi, 1) == 0 &&
+ axp8xx_read(dev, AXP_BATCHG_LO, &lo, 1) == 0) {
+ val = (AXP_SENSOR_BAT_H(hi) | AXP_SENSOR_BAT_L(lo));
+ val *= c->charge_step;
+ }
+ break;
+ case AXP_SENSOR_BATT_DISCHARGE_CURRENT:
+ if (axp8xx_read(dev, AXP_POWERSRC, &data, 1) == 0 &&
+ (data & AXP_POWERSRC_CHARING) == 0 &&
+ axp8xx_read(dev, AXP_BATDISCHG_HI, &hi, 1) == 0 &&
+ axp8xx_read(dev, AXP_BATDISCHG_LO, &lo, 1) == 0) {
+ val = (AXP_SENSOR_BAT_H(hi) | AXP_SENSOR_BAT_L(lo));
+ val *= c->discharge_step;
+ }
+ break;
+ case AXP_SENSOR_BATT_MAXIMUM_CAPACITY:
+ if (axp8xx_read(dev, AXP_BAT_MAX_CAP_HI, &hi, 1) == 0 &&
+ axp8xx_read(dev, AXP_BAT_MAX_CAP_LO, &lo, 1) == 0) {
+ val = AXP_SENSOR_COULOMB(hi, lo);
+ val *= c->maxcap_step;
+ }
+ break;
+ case AXP_SENSOR_BATT_CURRENT_CAPACITY:
+ if (axp8xx_read(dev, AXP_BAT_COULOMB_HI, &hi, 1) == 0 &&
+ axp8xx_read(dev, AXP_BAT_COULOMB_LO, &lo, 1) == 0) {
+ val = AXP_SENSOR_COULOMB(hi, lo);
+ val *= c->coulomb_step;
+ }
+ break;
+ }
+
+ return sysctl_handle_opaque(oidp, &val, sizeof(val), req);
+}
+
+static void
+axp8xx_intr(void *arg)
+{
+ device_t dev;
+ uint8_t val;
+ int error;
+
+ dev = arg;
+
+ error = axp8xx_read(dev, AXP_IRQSTAT1, &val, 1);
+ if (error != 0)
+ return;
+
+ if (val) {
+ if (bootverbose)
+ device_printf(dev, "AXP_IRQSTAT1 val: %x\n", val);
+ if (val & AXP_IRQSTAT1_ACIN_HI)
+ devctl_notify("PMU", "AC", "plugged", NULL);
+ if (val & AXP_IRQSTAT1_ACIN_LO)
+ devctl_notify("PMU", "AC", "unplugged", NULL);
+ if (val & AXP_IRQSTAT1_VBUS_HI)
+ devctl_notify("PMU", "USB", "plugged", NULL);
+ if (val & AXP_IRQSTAT1_VBUS_LO)
+ devctl_notify("PMU", "USB", "unplugged", NULL);
+ /* Acknowledge */
+ axp8xx_write(dev, AXP_IRQSTAT1, val);
+ }
+
+ error = axp8xx_read(dev, AXP_IRQSTAT2, &val, 1);
+ if (error != 0)
+ return;
+
+ if (val) {
+ if (bootverbose)
+ device_printf(dev, "AXP_IRQSTAT2 val: %x\n", val);
+ if (val & AXP_IRQSTAT2_BATCHGD)
+ devctl_notify("PMU", "Battery", "charged", NULL);
+ if (val & AXP_IRQSTAT2_BATCHGC)
+ devctl_notify("PMU", "Battery", "charging", NULL);
+ if (val & AXP_IRQSTAT2_BAT_NO)
+ devctl_notify("PMU", "Battery", "absent", NULL);
+ if (val & AXP_IRQSTAT2_BAT_IN)
+ devctl_notify("PMU", "Battery", "plugged", NULL);
+ /* Acknowledge */
+ axp8xx_write(dev, AXP_IRQSTAT2, val);
+ }
+
+ error = axp8xx_read(dev, AXP_IRQSTAT3, &val, 1);
+ if (error != 0)
+ return;
+
+ if (val) {
+ /* Acknowledge */
+ axp8xx_write(dev, AXP_IRQSTAT3, val);
+ }
+
+ error = axp8xx_read(dev, AXP_IRQSTAT4, &val, 1);
+ if (error != 0)
+ return;
+
+ if (val) {
+ if (bootverbose)
+ device_printf(dev, "AXP_IRQSTAT4 val: %x\n", val);
+ if (val & AXP_IRQSTAT4_BATLVL_LO0)
+ devctl_notify("PMU", "Battery", "shutdown-threshold", NULL);
+ if (val & AXP_IRQSTAT4_BATLVL_LO1)
+ devctl_notify("PMU", "Battery", "warning-threshold", NULL);
+ /* Acknowledge */
+ axp8xx_write(dev, AXP_IRQSTAT4, val);
+ }
+
+ error = axp8xx_read(dev, AXP_IRQSTAT5, &val, 1);
+ if (error != 0)
+ return;
+
+ if (val != 0) {
+ if ((val & AXP_IRQSTAT5_POKSIRQ) != 0) {
+ if (bootverbose)
+ device_printf(dev, "Power button pressed\n");
+ shutdown_nice(RB_POWEROFF);
+ }
+ /* Acknowledge */
+ axp8xx_write(dev, AXP_IRQSTAT5, val);
+ }
+
+ error = axp8xx_read(dev, AXP_IRQSTAT6, &val, 1);
+ if (error != 0)
+ return;
+
+ if (val) {
+ /* Acknowledge */
+ axp8xx_write(dev, AXP_IRQSTAT6, val);
+ }
+}
+
+static device_t
+axp8xx_gpio_get_bus(device_t dev)
+{
+ struct axp8xx_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ return (sc->gpiodev);
+}
+
+static int
+axp8xx_gpio_pin_max(device_t dev, int *maxpin)
+{
+ *maxpin = nitems(axp8xx_pins) - 1;
+
+ return (0);
+}
+
+static int
+axp8xx_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
+{
+ if (pin >= nitems(axp8xx_pins))
+ return (EINVAL);
+
+ snprintf(name, GPIOMAXNAME, "%s", axp8xx_pins[pin].name);
+
+ return (0);
+}
+
+static int
+axp8xx_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
+{
+ if (pin >= nitems(axp8xx_pins))
+ return (EINVAL);
+
+ *caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
+
+ return (0);
+}
+
+static int
+axp8xx_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
+{
+ struct axp8xx_softc *sc;
+ uint8_t data, func;
+ int error;
+
+ if (pin >= nitems(axp8xx_pins))
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+
+ AXP_LOCK(sc);
+ error = axp8xx_read(dev, axp8xx_pins[pin].ctrl_reg, &data, 1);
+ if (error == 0) {
+ func = (data & AXP_GPIO_FUNC) >> AXP_GPIO_FUNC_SHIFT;
+ if (func == AXP_GPIO_FUNC_INPUT)
+ *flags = GPIO_PIN_INPUT;
+ else if (func == AXP_GPIO_FUNC_DRVLO ||
+ func == AXP_GPIO_FUNC_DRVHI)
+ *flags = GPIO_PIN_OUTPUT;
+ else
+ *flags = 0;
+ }
+ AXP_UNLOCK(sc);
+
+ return (error);
+}
+
+static int
+axp8xx_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
+{
+ struct axp8xx_softc *sc;
+ uint8_t data;
+ int error;
+
+ if (pin >= nitems(axp8xx_pins))
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+
+ AXP_LOCK(sc);
+ error = axp8xx_read(dev, axp8xx_pins[pin].ctrl_reg, &data, 1);
+ if (error == 0) {
+ data &= ~AXP_GPIO_FUNC;
+ if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) != 0) {
+ if ((flags & GPIO_PIN_OUTPUT) == 0)
+ data |= AXP_GPIO_FUNC_INPUT;
+ }
+ error = axp8xx_write(dev, axp8xx_pins[pin].ctrl_reg, data);
+ }
+ AXP_UNLOCK(sc);
+
+ return (error);
+}
+
+static int
+axp8xx_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
+{
+ struct axp8xx_softc *sc;
+ uint8_t data, func;
+ int error;
+
+ if (pin >= nitems(axp8xx_pins))
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+
+ AXP_LOCK(sc);
+ error = axp8xx_read(dev, axp8xx_pins[pin].ctrl_reg, &data, 1);
+ if (error == 0) {
+ func = (data & AXP_GPIO_FUNC) >> AXP_GPIO_FUNC_SHIFT;
+ switch (func) {
+ case AXP_GPIO_FUNC_DRVLO:
+ *val = 0;
+ break;
+ case AXP_GPIO_FUNC_DRVHI:
+ *val = 1;
+ break;
+ case AXP_GPIO_FUNC_INPUT:
+ error = axp8xx_read(dev, AXP_GPIO_SIGBIT, &data, 1);
+ if (error == 0)
+ *val = (data & (1 << pin)) ? 1 : 0;
+ break;
+ default:
+ error = EIO;
+ break;
+ }
+ }
+ AXP_UNLOCK(sc);
+
+ return (error);
+}
+
+static int
+axp8xx_gpio_pin_set(device_t dev, uint32_t pin, unsigned int val)
+{
+ struct axp8xx_softc *sc;
+ uint8_t data, func;
+ int error;
+
+ if (pin >= nitems(axp8xx_pins))
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+
+ AXP_LOCK(sc);
+ error = axp8xx_read(dev, axp8xx_pins[pin].ctrl_reg, &data, 1);
+ if (error == 0) {
+ func = (data & AXP_GPIO_FUNC) >> AXP_GPIO_FUNC_SHIFT;
+ switch (func) {
+ case AXP_GPIO_FUNC_DRVLO:
+ case AXP_GPIO_FUNC_DRVHI:
+ data &= ~AXP_GPIO_FUNC;
+ data |= (val << AXP_GPIO_FUNC_SHIFT);
+ break;
+ default:
+ error = EIO;
+ break;
+ }
+ }
+ if (error == 0)
+ error = axp8xx_write(dev, axp8xx_pins[pin].ctrl_reg, data);
+ AXP_UNLOCK(sc);
+
+ return (error);
+}
+
+static int
+axp8xx_gpio_pin_toggle(device_t dev, uint32_t pin)
+{
+ struct axp8xx_softc *sc;
+ uint8_t data, func;
+ int error;
+
+ if (pin >= nitems(axp8xx_pins))
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+
+ AXP_LOCK(sc);
+ error = axp8xx_read(dev, axp8xx_pins[pin].ctrl_reg, &data, 1);
+ if (error == 0) {
+ func = (data & AXP_GPIO_FUNC) >> AXP_GPIO_FUNC_SHIFT;
+ switch (func) {
+ case AXP_GPIO_FUNC_DRVLO:
+ data &= ~AXP_GPIO_FUNC;
+ data |= (AXP_GPIO_FUNC_DRVHI << AXP_GPIO_FUNC_SHIFT);
+ break;
+ case AXP_GPIO_FUNC_DRVHI:
+ data &= ~AXP_GPIO_FUNC;
+ data |= (AXP_GPIO_FUNC_DRVLO << AXP_GPIO_FUNC_SHIFT);
+ break;
+ default:
+ error = EIO;
+ break;
+ }
+ }
+ if (error == 0)
+ error = axp8xx_write(dev, axp8xx_pins[pin].ctrl_reg, data);
+ AXP_UNLOCK(sc);
+
+ return (error);
+}
+
+static int
+axp8xx_gpio_map_gpios(device_t bus, phandle_t dev, phandle_t gparent,
+ int gcells, pcell_t *gpios, uint32_t *pin, uint32_t *flags)
+{
+ if (gpios[0] >= nitems(axp8xx_pins))
+ return (EINVAL);
+
+ *pin = gpios[0];
+ *flags = gpios[1];
+
+ return (0);
+}
+
+static phandle_t
+axp8xx_get_node(device_t dev, device_t bus)
+{
+ return (ofw_bus_get_node(dev));
+}
+
+static struct axp8xx_reg_sc *
+axp8xx_reg_attach(device_t dev, phandle_t node,
+ struct axp8xx_regdef *def)
+{
+ struct axp8xx_reg_sc *reg_sc;
+ struct regnode_init_def initdef;
+ struct regnode *regnode;
+
+ memset(&initdef, 0, sizeof(initdef));
+ if (regulator_parse_ofw_stdparam(dev, node, &initdef) != 0)
+ return (NULL);
+ if (initdef.std_param.min_uvolt == 0)
+ initdef.std_param.min_uvolt = def->voltage_min * 1000;
+ if (initdef.std_param.max_uvolt == 0)
+ initdef.std_param.max_uvolt = def->voltage_max * 1000;
+ initdef.id = def->id;
+ initdef.ofw_node = node;
+ regnode = regnode_create(dev, &axp8xx_regnode_class, &initdef);
+ if (regnode == NULL) {
+ device_printf(dev, "cannot create regulator\n");
+ return (NULL);
+ }
+
+ reg_sc = regnode_get_softc(regnode);
+ reg_sc->regnode = regnode;
+ reg_sc->base_dev = dev;
+ reg_sc->def = def;
+ reg_sc->xref = OF_xref_from_node(node);
+ reg_sc->param = regnode_get_stdparam(regnode);
+
+ regnode_register(regnode);
+
+ return (reg_sc);
+}
+
+static int
+axp8xx_regdev_map(device_t dev, phandle_t xref, int ncells, pcell_t *cells,
+ intptr_t *num)
+{
+ struct axp8xx_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+ for (i = 0; i < sc->nregs; i++) {
+ if (sc->regs[i] == NULL)
+ continue;
+ if (sc->regs[i]->xref == xref) {
+ *num = sc->regs[i]->def->id;
+ return (0);
+ }
+ }
+
+ return (ENXIO);
+}
+
+static int
+axp8xx_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+ {
+ case AXP803:
+ device_set_desc(dev, "X-Powers AXP803 Power Management Unit");
+ break;
+ case AXP813:
+ device_set_desc(dev, "X-Powers AXP813 Power Management Unit");
+ break;
+ default:
+ return (ENXIO);
+ }
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+axp8xx_attach(device_t dev)
+{
+ struct axp8xx_softc *sc;
+ struct axp8xx_reg_sc *reg;
+ uint8_t chip_id, val;
+ phandle_t rnode, child;
+ int error, i;
+
+ sc = device_get_softc(dev);
+
+ sc->addr = iicbus_get_addr(dev);
+ mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ error = bus_alloc_resources(dev, axp8xx_spec, &sc->res);
+ if (error != 0) {
+ device_printf(dev, "cannot allocate resources for device\n");
+ return (error);
+ }
+
+ if (bootverbose) {
+ axp8xx_read(dev, AXP_ICTYPE, &chip_id, 1);
+ device_printf(dev, "chip ID 0x%02x\n", chip_id);
+ }
+
+ sc->nregs = nitems(axp8xx_common_regdefs);
+ sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+ switch (sc->type) {
+ case AXP803:
+ sc->nregs += nitems(axp803_regdefs);
+ break;
+ case AXP813:
+ sc->nregs += nitems(axp813_regdefs);
+ break;
+ }
+ sc->config = &axp803_config;
+ sc->sensors = axp8xx_common_sensors;
+ sc->nsensors = nitems(axp8xx_common_sensors);
+
+ sc->regs = malloc(sizeof(struct axp8xx_reg_sc *) * sc->nregs,
+ M_AXP8XX_REG, M_WAITOK | M_ZERO);
+
+ /* Attach known regulators that exist in the DT */
+ rnode = ofw_bus_find_child(ofw_bus_get_node(dev), "regulators");
+ if (rnode > 0) {
+ for (i = 0; i < sc->nregs; i++) {
+ char *regname;
+ struct axp8xx_regdef *regdef;
+
+ if (i <= nitems(axp8xx_common_regdefs)) {
+ regname = axp8xx_common_regdefs[i].name;
+ regdef = &axp8xx_common_regdefs[i];
+ } else {
+ int off;
+
+ off = i - nitems(axp8xx_common_regdefs);
+ switch (sc->type) {
+ case AXP803:
+ regname = axp803_regdefs[off].name;
+ regdef = &axp803_regdefs[off];
+ break;
+ case AXP813:
+ regname = axp813_regdefs[off].name;
+ regdef = &axp813_regdefs[off];
+ break;
+ }
+ }
+ child = ofw_bus_find_child(rnode,
+ regname);
+ if (child == 0)
+ continue;
+ reg = axp8xx_reg_attach(dev, child,
+ regdef);
+ if (reg == NULL) {
+ device_printf(dev,
+ "cannot attach regulator %s\n",
+ regname);
+ continue;
+ }
+ sc->regs[i] = reg;
+ }
+ }
+
+ /* Add sensors */
+ for (i = 0; i < sc->nsensors; i++) {
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
+ OID_AUTO, sc->sensors[i].name,
+ CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
+ dev, sc->sensors[i].id, axp8xx_sysctl,
+ sc->sensors[i].format,
+ sc->sensors[i].desc);
+ }
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
+ OID_AUTO, "batchargecurrentstep",
+ CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
+ dev, 0, axp8xx_sysctl_chargecurrent,
+ "I", "Battery Charging Current Step, "
+ "0: 200mA, 1: 400mA, 2: 600mA, 3: 800mA, "
+ "4: 1000mA, 5: 1200mA, 6: 1400mA, 7: 1600mA, "
+ "8: 1800mA, 9: 2000mA, 10: 2200mA, 11: 2400mA, "
+ "12: 2600mA, 13: 2800mA");
+
+ /* Get thresholds */
+ if (axp8xx_read(dev, AXP_BAT_CAP_WARN, &val, 1) == 0) {
+ sc->warn_thres = (val & AXP_BAT_CAP_WARN_LV1) >> 4;
+ sc->warn_thres += AXP_BAP_CAP_WARN_LV1BASE;
+ sc->shut_thres = (val & AXP_BAT_CAP_WARN_LV2);
+ if (bootverbose) {
+ device_printf(dev,
+ "Raw reg val: 0x%02x\n", val);
+ device_printf(dev,
+ "Warning threshold: 0x%02x\n", sc->warn_thres);
+ device_printf(dev,
+ "Shutdown threshold: 0x%02x\n", sc->shut_thres);
+ }
+ }
+
+ /* Enable interrupts */
+ axp8xx_write(dev, AXP_IRQEN1,
+ AXP_IRQEN1_VBUS_LO |
+ AXP_IRQEN1_VBUS_HI |
+ AXP_IRQEN1_ACIN_LO |
+ AXP_IRQEN1_ACIN_HI);
+ axp8xx_write(dev, AXP_IRQEN2,
+ AXP_IRQEN2_BATCHGD |
+ AXP_IRQEN2_BATCHGC |
+ AXP_IRQEN2_BAT_NO |
+ AXP_IRQEN2_BAT_IN);
+ axp8xx_write(dev, AXP_IRQEN3, 0);
+ axp8xx_write(dev, AXP_IRQEN4,
+ AXP_IRQEN4_BATLVL_LO0 |
+ AXP_IRQEN4_BATLVL_LO1);
+ axp8xx_write(dev, AXP_IRQEN5,
+ AXP_IRQEN5_POKSIRQ |
+ AXP_IRQEN5_POKLIRQ);
+ axp8xx_write(dev, AXP_IRQEN6, 0);
+
+ /* Install interrupt handler */
+ error = bus_setup_intr(dev, sc->res, INTR_TYPE_MISC | INTR_MPSAFE,
+ NULL, axp8xx_intr, dev, &sc->ih);
+ if (error != 0) {
+ device_printf(dev, "cannot setup interrupt handler\n");
+ return (error);
+ }
+
+ EVENTHANDLER_REGISTER(shutdown_final, axp8xx_shutdown, dev,
+ SHUTDOWN_PRI_LAST);
+
+ sc->gpiodev = gpiobus_attach_bus(dev);
+
+ return (0);
+}
+
+static device_method_t axp8xx_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, axp8xx_probe),
+ DEVMETHOD(device_attach, axp8xx_attach),
+
+ /* GPIO interface */
+ DEVMETHOD(gpio_get_bus, axp8xx_gpio_get_bus),
+ DEVMETHOD(gpio_pin_max, axp8xx_gpio_pin_max),
+ DEVMETHOD(gpio_pin_getname, axp8xx_gpio_pin_getname),
+ DEVMETHOD(gpio_pin_getcaps, axp8xx_gpio_pin_getcaps),
+ DEVMETHOD(gpio_pin_getflags, axp8xx_gpio_pin_getflags),
+ DEVMETHOD(gpio_pin_setflags, axp8xx_gpio_pin_setflags),
+ DEVMETHOD(gpio_pin_get, axp8xx_gpio_pin_get),
+ DEVMETHOD(gpio_pin_set, axp8xx_gpio_pin_set),
+ DEVMETHOD(gpio_pin_toggle, axp8xx_gpio_pin_toggle),
+ DEVMETHOD(gpio_map_gpios, axp8xx_gpio_map_gpios),
+
+ /* Regdev interface */
+ DEVMETHOD(regdev_map, axp8xx_regdev_map),
+
+ /* OFW bus interface */
+ DEVMETHOD(ofw_bus_get_node, axp8xx_get_node),
+
+ DEVMETHOD_END
+};
+
+static driver_t axp8xx_driver = {
+ "axp8xx_pmu",
+ axp8xx_methods,
+ sizeof(struct axp8xx_softc),
+};
+
+static devclass_t axp8xx_devclass;
+extern devclass_t ofwgpiobus_devclass, gpioc_devclass;
+extern driver_t ofw_gpiobus_driver, gpioc_driver;
+
+EARLY_DRIVER_MODULE(axp8xx, iicbus, axp8xx_driver, axp8xx_devclass, 0, 0,
+ BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LAST);
+EARLY_DRIVER_MODULE(ofw_gpiobus, axp8xx_pmu, ofw_gpiobus_driver,
+ ofwgpiobus_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LAST);
+DRIVER_MODULE(gpioc, axp8xx_pmu, gpioc_driver, gpioc_devclass, 0, 0);
+MODULE_VERSION(axp8xx, 1);
+MODULE_DEPEND(axp8xx, iicbus, 1, 1, 1);
+SIMPLEBUS_PNP_INFO(compat_data);
diff --git a/sys/arm/allwinner/clkng/aw_ccung.c b/sys/arm/allwinner/clkng/aw_ccung.c
new file mode 100644
index 000000000000..ba930b993f20
--- /dev/null
+++ b/sys/arm/allwinner/clkng/aw_ccung.c
@@ -0,0 +1,364 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2017,2018 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Allwinner Clock Control Unit
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <machine/bus.h>
+
+#include <dev/fdt/simplebus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/clk/clk_gate.h>
+
+#include <dev/extres/hwreset/hwreset.h>
+
+#include <arm/allwinner/clkng/aw_ccung.h>
+#include <arm/allwinner/clkng/aw_clk.h>
+
+#ifdef __aarch64__
+#include "opt_soc.h"
+#endif
+
+#include "clkdev_if.h"
+#include "hwreset_if.h"
+
+#if 0
+#define dprintf(format, arg...) device_printf(dev, "%s: " format, __func__, arg)
+#else
+#define dprintf(format, arg...)
+#endif
+
+static struct resource_spec aw_ccung_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+#define CCU_READ4(sc, reg) bus_read_4((sc)->res, (reg))
+#define CCU_WRITE4(sc, reg, val) bus_write_4((sc)->res, (reg), (val))
+
+static int
+aw_ccung_write_4(device_t dev, bus_addr_t addr, uint32_t val)
+{
+ struct aw_ccung_softc *sc;
+
+ sc = device_get_softc(dev);
+ dprintf("offset=%lx write %x\n", addr, val);
+ CCU_WRITE4(sc, addr, val);
+ return (0);
+}
+
+static int
+aw_ccung_read_4(device_t dev, bus_addr_t addr, uint32_t *val)
+{
+ struct aw_ccung_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ *val = CCU_READ4(sc, addr);
+ dprintf("offset=%lx Read %x\n", addr, *val);
+ return (0);
+}
+
+static int
+aw_ccung_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set)
+{
+ struct aw_ccung_softc *sc;
+ uint32_t reg;
+
+ sc = device_get_softc(dev);
+
+ dprintf("offset=%lx clr: %x set: %x\n", addr, clr, set);
+ reg = CCU_READ4(sc, addr);
+ reg &= ~clr;
+ reg |= set;
+ CCU_WRITE4(sc, addr, reg);
+
+ return (0);
+}
+
+static int
+aw_ccung_reset_assert(device_t dev, intptr_t id, bool reset)
+{
+ struct aw_ccung_softc *sc;
+ uint32_t val;
+
+ sc = device_get_softc(dev);
+
+ dprintf("%sassert reset id %ld\n", reset ? "" : "De", id);
+ if (id >= sc->nresets || sc->resets[id].offset == 0)
+ return (0);
+
+ mtx_lock(&sc->mtx);
+ val = CCU_READ4(sc, sc->resets[id].offset);
+ dprintf("offset=%x Read %x\n", sc->resets[id].offset, val);
+ if (reset)
+ val &= ~(1 << sc->resets[id].shift);
+ else
+ val |= 1 << sc->resets[id].shift;
+ dprintf("offset=%x Write %x\n", sc->resets[id].offset, val);
+ CCU_WRITE4(sc, sc->resets[id].offset, val);
+ mtx_unlock(&sc->mtx);
+
+ return (0);
+}
+
+static int
+aw_ccung_reset_is_asserted(device_t dev, intptr_t id, bool *reset)
+{
+ struct aw_ccung_softc *sc;
+ uint32_t val;
+
+ sc = device_get_softc(dev);
+
+ if (id >= sc->nresets || sc->resets[id].offset == 0)
+ return (0);
+
+ mtx_lock(&sc->mtx);
+ val = CCU_READ4(sc, sc->resets[id].offset);
+ dprintf("offset=%x Read %x\n", sc->resets[id].offset, val);
+ *reset = (val & (1 << sc->resets[id].shift)) != 0 ? false : true;
+ mtx_unlock(&sc->mtx);
+
+ return (0);
+}
+
+static void
+aw_ccung_device_lock(device_t dev)
+{
+ struct aw_ccung_softc *sc;
+
+ sc = device_get_softc(dev);
+ mtx_lock(&sc->mtx);
+}
+
+static void
+aw_ccung_device_unlock(device_t dev)
+{
+ struct aw_ccung_softc *sc;
+
+ sc = device_get_softc(dev);
+ mtx_unlock(&sc->mtx);
+}
+
+static int
+aw_ccung_register_gates(struct aw_ccung_softc *sc)
+{
+ struct clk_gate_def def;
+ int i;
+
+ for (i = 0; i < sc->ngates; i++) {
+ if (sc->gates[i].name == NULL)
+ continue;
+ memset(&def, 0, sizeof(def));
+ def.clkdef.id = i;
+ def.clkdef.name = sc->gates[i].name;
+ def.clkdef.parent_names = &sc->gates[i].parent_name;
+ def.clkdef.parent_cnt = 1;
+ def.offset = sc->gates[i].offset;
+ def.shift = sc->gates[i].shift;
+ def.mask = 1;
+ def.on_value = 1;
+ def.off_value = 0;
+ clknode_gate_register(sc->clkdom, &def);
+ }
+
+ return (0);
+}
+
+static void
+aw_ccung_init_clocks(struct aw_ccung_softc *sc)
+{
+ struct clknode *clknode;
+ int i, error;
+
+ for (i = 0; i < sc->n_clk_init; i++) {
+ clknode = clknode_find_by_name(sc->clk_init[i].name);
+ if (clknode == NULL) {
+ device_printf(sc->dev, "Cannot find clock %s\n",
+ sc->clk_init[i].name);
+ continue;
+ }
+
+ if (sc->clk_init[i].parent_name != NULL) {
+ if (bootverbose)
+ device_printf(sc->dev, "Setting %s as parent for %s\n",
+ sc->clk_init[i].parent_name,
+ sc->clk_init[i].name);
+ error = clknode_set_parent_by_name(clknode,
+ sc->clk_init[i].parent_name);
+ if (error != 0) {
+ device_printf(sc->dev,
+ "Cannot set parent to %s for %s\n",
+ sc->clk_init[i].parent_name,
+ sc->clk_init[i].name);
+ continue;
+ }
+ }
+ if (sc->clk_init[i].default_freq != 0) {
+ if (bootverbose)
+ device_printf(sc->dev,
+ "Setting freq %ju for %s\n",
+ sc->clk_init[i].default_freq,
+ sc->clk_init[i].name);
+ error = clknode_set_freq(clknode,
+ sc->clk_init[i].default_freq, 0 , 0);
+ if (error != 0) {
+ device_printf(sc->dev,
+ "Cannot set frequency for %s to %ju\n",
+ sc->clk_init[i].name,
+ sc->clk_init[i].default_freq);
+ continue;
+ }
+ }
+ if (sc->clk_init[i].enable) {
+ error = clknode_enable(clknode);
+ if (error != 0) {
+ device_printf(sc->dev,
+ "Cannot enable %s\n",
+ sc->clk_init[i].name);
+ continue;
+ }
+ }
+ }
+}
+
+int
+aw_ccung_attach(device_t dev)
+{
+ struct aw_ccung_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ if (bus_alloc_resources(dev, aw_ccung_spec, &sc->res) != 0) {
+ device_printf(dev, "cannot allocate resources for device\n");
+ return (ENXIO);
+ }
+
+ mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ sc->clkdom = clkdom_create(dev);
+ if (sc->clkdom == NULL)
+ panic("Cannot create clkdom\n");
+
+ for (i = 0; i < sc->nclks; i++) {
+ switch (sc->clks[i].type) {
+ case AW_CLK_UNDEFINED:
+ break;
+ case AW_CLK_MUX:
+ clknode_mux_register(sc->clkdom, sc->clks[i].clk.mux);
+ break;
+ case AW_CLK_DIV:
+ clknode_div_register(sc->clkdom, sc->clks[i].clk.div);
+ break;
+ case AW_CLK_FIXED:
+ clknode_fixed_register(sc->clkdom,
+ sc->clks[i].clk.fixed);
+ break;
+ case AW_CLK_NKMP:
+ aw_clk_nkmp_register(sc->clkdom, sc->clks[i].clk.nkmp);
+ break;
+ case AW_CLK_NM:
+ aw_clk_nm_register(sc->clkdom, sc->clks[i].clk.nm);
+ break;
+ case AW_CLK_M:
+ aw_clk_m_register(sc->clkdom, sc->clks[i].clk.m);
+ break;
+ case AW_CLK_PREDIV_MUX:
+ aw_clk_prediv_mux_register(sc->clkdom,
+ sc->clks[i].clk.prediv_mux);
+ break;
+ case AW_CLK_FRAC:
+ aw_clk_frac_register(sc->clkdom, sc->clks[i].clk.frac);
+ break;
+ case AW_CLK_MIPI:
+ aw_clk_mipi_register(sc->clkdom, sc->clks[i].clk.mipi);
+ break;
+ case AW_CLK_NP:
+ aw_clk_np_register(sc->clkdom, sc->clks[i].clk.np);
+ break;
+ case AW_CLK_NMM:
+ aw_clk_nmm_register(sc->clkdom, sc->clks[i].clk.nmm);
+ break;
+ }
+ }
+
+ if (sc->gates)
+ aw_ccung_register_gates(sc);
+ if (clkdom_finit(sc->clkdom) != 0)
+ panic("cannot finalize clkdom initialization\n");
+
+ clkdom_xlock(sc->clkdom);
+ aw_ccung_init_clocks(sc);
+ clkdom_unlock(sc->clkdom);
+
+ if (bootverbose)
+ clkdom_dump(sc->clkdom);
+
+ /* If we have resets, register our self as a reset provider */
+ if (sc->resets)
+ hwreset_register_ofw_provider(dev);
+
+ return (0);
+}
+
+static device_method_t aw_ccung_methods[] = {
+ /* clkdev interface */
+ DEVMETHOD(clkdev_write_4, aw_ccung_write_4),
+ DEVMETHOD(clkdev_read_4, aw_ccung_read_4),
+ DEVMETHOD(clkdev_modify_4, aw_ccung_modify_4),
+ DEVMETHOD(clkdev_device_lock, aw_ccung_device_lock),
+ DEVMETHOD(clkdev_device_unlock, aw_ccung_device_unlock),
+
+ /* Reset interface */
+ DEVMETHOD(hwreset_assert, aw_ccung_reset_assert),
+ DEVMETHOD(hwreset_is_asserted, aw_ccung_reset_is_asserted),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_0(aw_ccung, aw_ccung_driver, aw_ccung_methods,
+ sizeof(struct aw_ccung_softc));
diff --git a/sys/arm/allwinner/clkng/aw_ccung.h b/sys/arm/allwinner/clkng/aw_ccung.h
new file mode 100644
index 000000000000..6a5c2fff2dde
--- /dev/null
+++ b/sys/arm/allwinner/clkng/aw_ccung.h
@@ -0,0 +1,110 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2017,2018 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __CCU_NG_H__
+#define __CCU_NG_H__
+
+#include <arm/allwinner/clkng/aw_clk.h>
+#include <arm/allwinner/clkng/aw_clk_m.h>
+#include <arm/allwinner/clkng/aw_clk_mipi.h>
+#include <arm/allwinner/clkng/aw_clk_nkmp.h>
+#include <arm/allwinner/clkng/aw_clk_nm.h>
+#include <arm/allwinner/clkng/aw_clk_nmm.h>
+#include <arm/allwinner/clkng/aw_clk_np.h>
+#include <arm/allwinner/clkng/aw_clk_prediv_mux.h>
+#include <arm/allwinner/clkng/aw_clk_frac.h>
+#include <dev/extres/clk/clk_mux.h>
+#include <dev/extres/clk/clk_div.h>
+#include <dev/extres/clk/clk_fixed.h>
+
+enum aw_ccung_clk_type {
+ AW_CLK_UNDEFINED = 0,
+ AW_CLK_MUX,
+ AW_CLK_DIV,
+ AW_CLK_FIXED,
+ AW_CLK_NKMP,
+ AW_CLK_NM,
+ AW_CLK_PREDIV_MUX,
+ AW_CLK_FRAC,
+ AW_CLK_M,
+ AW_CLK_MIPI,
+ AW_CLK_NP,
+ AW_CLK_NMM,
+};
+
+struct aw_ccung_clk {
+ enum aw_ccung_clk_type type;
+ union {
+ struct clk_mux_def *mux;
+ struct clk_div_def *div;
+ struct clk_fixed_def *fixed;
+ struct aw_clk_nkmp_def *nkmp;
+ struct aw_clk_nm_def *nm;
+ struct aw_clk_prediv_mux_def *prediv_mux;
+ struct aw_clk_frac_def *frac;
+ struct aw_clk_m_def *m;
+ struct aw_clk_mipi_def *mipi;
+ struct aw_clk_np_def *np;
+ struct aw_clk_nmm_def *nmm;
+ } clk;
+};
+
+struct aw_ccung_softc {
+ device_t dev;
+ struct resource *res;
+ struct clkdom *clkdom;
+ struct mtx mtx;
+ struct aw_ccung_reset *resets;
+ int nresets;
+ struct aw_ccung_gate *gates;
+ int ngates;
+ struct aw_ccung_clk *clks;
+ int nclks;
+ struct aw_clk_init *clk_init;
+ int n_clk_init;
+};
+
+struct aw_ccung_reset {
+ uint32_t offset;
+ uint32_t shift;
+};
+
+struct aw_ccung_gate {
+ const char *name;
+ const char *parent_name;
+ uint32_t id;
+ uint32_t offset;
+ uint32_t shift;
+};
+
+DECLARE_CLASS(aw_ccung_driver);
+
+int aw_ccung_attach(device_t dev);
+
+#endif /* __CCU_NG_H__ */
diff --git a/sys/arm/allwinner/clkng/aw_clk.h b/sys/arm/allwinner/clkng/aw_clk.h
new file mode 100644
index 000000000000..ec8d13599bb9
--- /dev/null
+++ b/sys/arm/allwinner/clkng/aw_clk.h
@@ -0,0 +1,604 @@
+/*-
+ * Copyright (c) 2017 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __AW_CLK_H__
+#define __AW_CLK_H__
+
+/*
+ Allwinner clocks formula :
+
+PLLs:
+
+(24MHz*N*K)/(M*P)
+(24MHz*N)/(M*P)
+(24MHz*N*2)/M
+(24MHz*N)/M
+(24MHz*N*K)/M
+(24MHz*N*K/2)
+(24MHz*N)/M
+(24MHz*N*K/2)
+(24MHz*N)/M
+
+Periph clocks:
+
+Clock Source/Divider N/Divider M
+Clock Source/Divider N/Divider M/2
+Clock Source*N/(Divider M+1)/(Divider P+1)
+
+ */
+
+struct aw_clk_init {
+ const char *name;
+ const char *parent_name;
+ uint64_t default_freq;
+ bool enable;
+};
+
+#define AW_CLK_HAS_GATE 0x0001
+#define AW_CLK_HAS_LOCK 0x0002
+#define AW_CLK_HAS_MUX 0x0004
+#define AW_CLK_REPARENT 0x0008
+#define AW_CLK_SCALE_CHANGE 0x0010
+#define AW_CLK_HAS_UPDATE 0x0040
+#define AW_CLK_HAS_PREDIV 0x0080
+#define AW_CLK_SET_PARENT 0x0100
+
+#define AW_CLK_FACTOR_POWER_OF_TWO 0x0001
+#define AW_CLK_FACTOR_ZERO_BASED 0x0002
+#define AW_CLK_FACTOR_HAS_COND 0x0004
+#define AW_CLK_FACTOR_FIXED 0x0008
+#define AW_CLK_FACTOR_ZERO_IS_ONE 0x0010
+#define AW_CLK_FACTOR_MIN_VALUE 0x0020
+#define AW_CLK_FACTOR_MAX_VALUE 0x0040
+
+struct aw_clk_factor {
+ uint32_t shift; /* Shift bits for the factor */
+ uint32_t mask; /* Mask to get the factor, will be override by the clk methods */
+ uint32_t width; /* Number of bits for the factor */
+ uint32_t value; /* Fixed value, depends on AW_CLK_FACTOR_FIXED */
+
+ uint32_t cond_shift;
+ uint32_t cond_mask;
+ uint32_t cond_width;
+ uint32_t cond_value;
+
+ uint32_t min_value;
+ uint32_t max_value;
+
+ uint32_t flags; /* Flags */
+};
+
+struct aw_clk_frac {
+ uint64_t freq0;
+ uint64_t freq1;
+ uint32_t mode_sel;
+ uint32_t freq_sel;
+};
+
+static inline uint32_t
+aw_clk_get_factor(uint32_t val, struct aw_clk_factor *factor)
+{
+ uint32_t factor_val;
+ uint32_t cond;
+
+ if (factor->flags & AW_CLK_FACTOR_HAS_COND) {
+ cond = (val & factor->cond_mask) >> factor->cond_shift;
+ if (cond != factor->cond_value)
+ return (1);
+ }
+
+ if (factor->flags & AW_CLK_FACTOR_FIXED)
+ return (factor->value);
+
+ factor_val = (val & factor->mask) >> factor->shift;
+ if (factor_val == 0 && (factor->flags & AW_CLK_FACTOR_ZERO_IS_ONE))
+ factor_val = 1;
+
+ if (factor->flags & AW_CLK_FACTOR_POWER_OF_TWO)
+ factor_val = 1 << factor_val;
+ else if (!(factor->flags & AW_CLK_FACTOR_ZERO_BASED))
+ factor_val += 1;
+
+ return (factor_val);
+}
+
+static inline uint32_t
+aw_clk_factor_get_max(struct aw_clk_factor *factor)
+{
+ uint32_t max;
+
+ if (factor->flags & AW_CLK_FACTOR_FIXED)
+ max = factor->value;
+ else if (factor->flags & AW_CLK_FACTOR_POWER_OF_TWO)
+ max = 1 << ((1 << factor->width) - 1);
+ else {
+ max = (1 << factor->width);
+ }
+
+ return (max);
+}
+
+static inline uint32_t
+aw_clk_factor_get_min(struct aw_clk_factor *factor)
+{
+ uint32_t min;
+
+ if (factor->flags & AW_CLK_FACTOR_FIXED)
+ min = factor->value;
+ else if (factor->flags & AW_CLK_FACTOR_ZERO_BASED)
+ min = 0;
+ else if (factor->flags & AW_CLK_FACTOR_MIN_VALUE)
+ min = factor->min_value;
+ else
+ min = 1;
+
+ return (min);
+}
+
+static inline uint32_t
+aw_clk_factor_get_value(struct aw_clk_factor *factor, uint32_t raw)
+{
+ uint32_t val;
+
+ if (factor->flags & AW_CLK_FACTOR_FIXED)
+ return (factor->value);
+
+ if (factor->flags & AW_CLK_FACTOR_ZERO_BASED)
+ val = raw;
+ else if (factor->flags & AW_CLK_FACTOR_POWER_OF_TWO) {
+ for (val = 0; raw != 1; val++)
+ raw >>= 1;
+ } else if (factor->flags & AW_CLK_FACTOR_MAX_VALUE)
+ val = factor->max_value;
+ else
+ val = raw - 1;
+
+ return (val);
+}
+
+#define CCU_RESET(idx, o, s) \
+ [idx] = { \
+ .offset = o, \
+ .shift = s, \
+ },
+
+#define CCU_GATE(idx, clkname, pname, o, s) \
+ [idx] = { \
+ .name = clkname, \
+ .parent_name = pname, \
+ .offset = o, \
+ .shift = s, \
+ },
+
+#define NKMP_CLK(_clkname, _id, _name, _pnames, \
+ _offset, \
+ _n_shift, _n_width, _n_value, _n_flags, \
+ _k_shift, _k_width, _k_value, _k_flags, \
+ _m_shift, _m_width, _m_value, _m_flags, \
+ _p_shift, _p_width, _p_value, _p_flags, \
+ _gate, \
+ _lock, _lock_retries, \
+ _flags) \
+ static struct aw_clk_nkmp_def _clkname = { \
+ .clkdef = { \
+ .id = _id, \
+ .name = _name, \
+ .parent_names = _pnames, \
+ .parent_cnt = nitems(_pnames), \
+ }, \
+ .offset = _offset, \
+ .n.shift = _n_shift, \
+ .n.width = _n_width, \
+ .n.value = _n_value, \
+ .n.flags = _n_flags, \
+ .k.shift = _k_shift, \
+ .k.width = _k_width, \
+ .k.value = _k_value, \
+ .k.flags = _k_flags, \
+ .m.shift = _m_shift, \
+ .m.width = _m_width, \
+ .m.value = _m_value, \
+ .m.flags = _m_flags, \
+ .p.shift = _p_shift, \
+ .p.width = _p_width, \
+ .p.value = _p_value, \
+ .p.flags = _p_flags, \
+ .gate_shift = _gate, \
+ .lock_shift = _lock, \
+ .lock_retries = _lock_retries, \
+ .flags = _flags, \
+ }
+
+#define NKMP_CLK_WITH_MUX(_clkname, \
+ _id, _name, _pnames, \
+ _offset, \
+ _n_shift, _n_width, _n_value, _n_flags, \
+ _k_shift, _k_width, _k_value, _k_flags, \
+ _m_shift, _m_width, _m_value, _m_flags, \
+ _p_shift, _p_width, _p_value, _p_flags, \
+ _mux_shift, _mux_width, _gate, \
+ _lock, _lock_retries, \
+ _flags) \
+ static struct aw_clk_nkmp_def _clkname = { \
+ .clkdef = { \
+ .id = _id, \
+ .name = _name, \
+ .parent_names = _pnames, \
+ .parent_cnt = nitems(_pnames), \
+ }, \
+ .offset = _offset, \
+ .n.shift = _n_shift, \
+ .n.width = _n_width, \
+ .n.value = _n_value, \
+ .n.flags = _n_flags, \
+ .k.shift = _k_shift, \
+ .k.width = _k_width, \
+ .k.value = _k_value, \
+ .k.flags = _k_flags, \
+ .m.shift = _m_shift, \
+ .m.width = _m_width, \
+ .m.value = _m_value, \
+ .m.flags = _m_flags, \
+ .p.shift = _p_shift, \
+ .p.width = _p_width, \
+ .p.value = _p_value, \
+ .p.flags = _p_flags, \
+ .mux_shift = _mux_shift, \
+ .mux_width = _mux_width, \
+ .gate_shift = _gate, \
+ .lock_shift = _lock, \
+ .lock_retries = _lock_retries, \
+ .flags = _flags, \
+ }
+
+#define NKMP_CLK_WITH_UPDATE(_clkname, \
+ _id, _name, _pnames, \
+ _offset, \
+ _n_shift, _n_width, _n_value, _n_flags, \
+ _k_shift, _k_width, _k_value, _k_flags, \
+ _m_shift, _m_width, _m_value, _m_flags, \
+ _p_shift, _p_width, _p_value, _p_flags, \
+ _gate, \
+ _lock, _lock_retries, \
+ _update, \
+ _flags) \
+ static struct aw_clk_nkmp_def _clkname = { \
+ .clkdef = { \
+ .id = _id, \
+ .name = _name, \
+ .parent_names = _pnames, \
+ .parent_cnt = nitems(_pnames), \
+ }, \
+ .offset = _offset, \
+ .n.shift = _n_shift, \
+ .n.width = _n_width, \
+ .n.value = _n_value, \
+ .n.flags = _n_flags, \
+ .k.shift = _k_shift, \
+ .k.width = _k_width, \
+ .k.value = _k_value, \
+ .k.flags = _k_flags, \
+ .m.shift = _m_shift, \
+ .m.width = _m_width, \
+ .m.value = _m_value, \
+ .m.flags = _m_flags, \
+ .p.shift = _p_shift, \
+ .p.width = _p_width, \
+ .p.value = _p_value, \
+ .p.flags = _p_flags, \
+ .gate_shift = _gate, \
+ .lock_shift = _lock, \
+ .lock_retries = _lock_retries, \
+ .update_shift = _update, \
+ .flags = _flags | AW_CLK_HAS_UPDATE, \
+ }
+
+#define FRAC_CLK(_clkname, _id, _name, _pnames, \
+ _offset, \
+ _nshift, _nwidth, _nvalue, _nflags, \
+ _mshift, _mwidth, _mvalue, _mflags, \
+ _gate_shift, _lock_shift,_lock_retries, \
+ _flags, _freq0, _freq1, _mode_sel, _freq_sel, \
+ _min_freq, _max_freq) \
+ static struct aw_clk_frac_def _clkname = { \
+ .clkdef = { \
+ .id = _id, \
+ .name = _name, \
+ .parent_names = _pnames, \
+ .parent_cnt = nitems(_pnames), \
+ .flags = CLK_NODE_GLITCH_FREE, \
+ }, \
+ .offset = _offset, \
+ .n.shift = _nshift, \
+ .n.width = _nwidth, \
+ .n.value = _nvalue, \
+ .n.flags = _nflags, \
+ .m.shift = _mshift, \
+ .m.width = _mwidth, \
+ .m.value = _mvalue, \
+ .m.flags = _mflags, \
+ .gate_shift = _gate_shift, \
+ .lock_shift = _lock_shift, \
+ .lock_retries = _lock_retries, \
+ .flags = _flags, \
+ .frac.freq0 = _freq0, \
+ .frac.freq1 = _freq1, \
+ .frac.mode_sel = _mode_sel, \
+ .frac.freq_sel = _freq_sel, \
+ .min_freq = _min_freq, \
+ .max_freq = _max_freq, \
+ }
+
+#define M_CLK(_clkname, _id, _name, _pnames, \
+ _offset, \
+ _mshift, _mwidth, _mvalue, _mflags, \
+ _mux_shift, _mux_width, \
+ _gate_shift, \
+ _flags) \
+ static struct aw_clk_m_def _clkname = { \
+ .clkdef = { \
+ .id = _id, \
+ .name = _name, \
+ .parent_names = _pnames, \
+ .parent_cnt = nitems(_pnames), \
+ }, \
+ .offset = _offset, \
+ .mux_shift = _mux_shift, \
+ .m.shift = _mshift, \
+ .m.width = _mwidth, \
+ .m.value = _mvalue, \
+ .m.flags = _mflags, \
+ .mux_width = _mux_width, \
+ .gate_shift = _gate_shift, \
+ .flags = _flags, \
+ }
+
+#define NM_CLK(_clkname, _id, _name, _pnames, \
+ _offset, \
+ _nshift, _nwidth, _nvalue, _nflags, \
+ _mshift, _mwidth, _mvalue, _mflags, \
+ _mux_shift, _mux_width, \
+ _gate_shift, \
+ _flags) \
+ static struct aw_clk_nm_def _clkname = { \
+ .clkdef = { \
+ .id = _id, \
+ .name = _name, \
+ .parent_names = _pnames, \
+ .parent_cnt = nitems(_pnames), \
+ }, \
+ .offset = _offset, \
+ .n.shift = _nshift, \
+ .n.width = _nwidth, \
+ .n.value = _nvalue, \
+ .n.flags = _nflags, \
+ .mux_shift = _mux_shift, \
+ .m.shift = _mshift, \
+ .m.width = _mwidth, \
+ .m.value = _mvalue, \
+ .m.flags = _mflags, \
+ .mux_width = _mux_width, \
+ .gate_shift = _gate_shift, \
+ .flags = _flags, \
+ }
+
+#define NMM_CLK(_clkname, _id, _name, _pnames, \
+ _offset, \
+ _nshift, _nwidth, _nvalue, _nflags, \
+ _m0shift, _m0width, _m0value, _m0flags, \
+ _m1shift, _m1width, _m1value, _m1flags, \
+ _gate_shift, \
+ _lock, _lock_retries, \
+ _flags) \
+ static struct aw_clk_nmm_def _clkname = { \
+ .clkdef = { \
+ .id = _id, \
+ .name = _name, \
+ .parent_names = _pnames, \
+ .parent_cnt = nitems(_pnames), \
+ }, \
+ .offset = _offset, \
+ .n.shift = _nshift, \
+ .n.width = _nwidth, \
+ .n.value = _nvalue, \
+ .n.flags = _nflags, \
+ .m0.shift = _m0shift, \
+ .m0.width = _m0width, \
+ .m0.value = _m0value, \
+ .m0.flags = _m0flags, \
+ .m1.shift = _m1shift, \
+ .m1.width = _m1width, \
+ .m1.value = _m1value, \
+ .m1.flags = _m1flags, \
+ .gate_shift = _gate_shift, \
+ .lock_shift = _lock, \
+ .lock_retries = _lock_retries, \
+ .flags = _flags, \
+ }
+
+#define NP_CLK(_clkname, _id, _name, _pnames, \
+ _offset, \
+ _nshift, _nwidth, _nvalue, _nflags, \
+ _pshift, _pwidth, _pvalue, _pflags, \
+ _gate_shift, \
+ _lock, _lock_retries, \
+ _flags) \
+ static struct aw_clk_np_def _clkname = { \
+ .clkdef = { \
+ .id = _id, \
+ .name = _name, \
+ .parent_names = _pnames, \
+ .parent_cnt = nitems(_pnames), \
+ }, \
+ .offset = _offset, \
+ .n.shift = _nshift, \
+ .n.width = _nwidth, \
+ .n.value = _nvalue, \
+ .n.flags = _nflags, \
+ .p.shift = _pshift, \
+ .p.width = _pwidth, \
+ .p.value = _pvalue, \
+ .p.flags = _pflags, \
+ .gate_shift = _gate_shift, \
+ .lock_shift = _lock, \
+ .lock_retries = _lock_retries, \
+ .flags = _flags, \
+ }
+
+#define PREDIV_CLK(_clkname, _id, _name, _pnames, \
+ _offset, \
+ _mux_shift, _mux_width, \
+ _div_shift, _div_width, _div_value, _div_flags, \
+ _prediv_shift, _prediv_width, _prediv_value, _prediv_flags, \
+ _prediv_cond_shift, _prediv_cond_width, _prediv_cond_value) \
+ static struct aw_clk_prediv_mux_def _clkname = { \
+ .clkdef = { \
+ .id = _id, \
+ .name = _name, \
+ .parent_names = _pnames, \
+ .parent_cnt = nitems(_pnames), \
+ }, \
+ .offset = _offset, \
+ .mux_shift = _mux_shift, \
+ .mux_width = _mux_width, \
+ .div.shift = _div_shift, \
+ .div.width = _div_width, \
+ .div.value = _div_value, \
+ .div.flags = _div_flags, \
+ .prediv.shift = _prediv_shift, \
+ .prediv.width = _prediv_width, \
+ .prediv.value = _prediv_value, \
+ .prediv.flags = _prediv_flags, \
+ .prediv.cond_shift = _prediv_cond_shift, \
+ .prediv.cond_width = _prediv_cond_width, \
+ .prediv.cond_value = _prediv_cond_value, \
+ }
+
+#define PREDIV_CLK_WITH_MASK(_clkname, _id, _name, _pnames, \
+ _offset, \
+ _mux_shift, _mux_width, \
+ _div_shift, _div_width, _div_value, _div_flags, \
+ _prediv_shift, _prediv_width, _prediv_value, _prediv_flags, \
+ _prediv_cond_mask, _prediv_cond_value) \
+ static struct aw_clk_prediv_mux_def _clkname = { \
+ .clkdef = { \
+ .id = _id, \
+ .name = _name, \
+ .parent_names = _pnames, \
+ .parent_cnt = nitems(_pnames), \
+ }, \
+ .offset = _offset, \
+ .mux_shift = _mux_shift, \
+ .mux_width = _mux_width, \
+ .div.shift = _div_shift, \
+ .div.width = _div_width, \
+ .div.value = _div_value, \
+ .div.flags = _div_flags, \
+ .prediv.shift = _prediv_shift, \
+ .prediv.width = _prediv_width, \
+ .prediv.value = _prediv_value, \
+ .prediv.flags = _prediv_flags, \
+ .prediv.cond_shift = 0, \
+ .prediv.cond_width = 0, \
+ .prediv.cond_mask = _prediv_cond_mask, \
+ .prediv.cond_value = _prediv_cond_value, \
+ }
+
+#define MIPI_CLK(_clkname, _id, _name, _pnames, \
+ _offset, \
+ _kshift, _kwidth, _kflags, _kmin, \
+ _mshift, _mwidth, \
+ _nshift, _nwidth, \
+ _gate_shift, _lock_shift) \
+ static struct aw_clk_mipi_def _clkname = { \
+ .clkdef = { \
+ .id = _id, \
+ .name = _name, \
+ .parent_names = _pnames, \
+ .parent_cnt = nitems(_pnames) \
+ }, \
+ .offset = _offset, \
+ .k.shift = _kshift, \
+ .k.width = _kwidth, \
+ .k.flags = _kflags, \
+ .k.min_value = _kmin, \
+ .m.shift = _mshift, \
+ .m.width = _mwidth, \
+ .n.shift = _nshift, \
+ .n.width = _nwidth, \
+ .gate_shift = _gate_shift, \
+ .lock_shift = _lock_shift, \
+ }
+
+#define MUX_CLK(_clkname, _id, _name, _pnames, \
+ _offset, _shift, _width) \
+ static struct clk_mux_def _clkname = { \
+ .clkdef = { \
+ .id = _id, \
+ .name = _name, \
+ .parent_names = _pnames, \
+ .parent_cnt = nitems(_pnames) \
+ }, \
+ .offset = _offset, \
+ .shift = _shift, \
+ .width = _width, \
+ }
+
+#define DIV_CLK(_clkname, _id, _name, _pnames, \
+ _offset, \
+ _i_shift, _i_width, \
+ _div_flags, _div_table) \
+ static struct clk_div_def _clkname = { \
+ .clkdef = { \
+ .id = _id, \
+ .name = _name, \
+ .parent_names = _pnames, \
+ .parent_cnt = nitems(_pnames) \
+ }, \
+ .offset = _offset, \
+ .i_shift = _i_shift, \
+ .i_width = _i_width, \
+ .div_flags = _div_flags, \
+ .div_table = _div_table, \
+ }
+
+#define FIXED_CLK(_clkname, _id, _name, _pnames, \
+ _freq, _mult, _div, _flags) \
+ static struct clk_fixed_def _clkname = { \
+ .clkdef = { \
+ .id = _id, \
+ .name = _name, \
+ .parent_names = _pnames, \
+ .parent_cnt = 1, \
+ }, \
+ .freq = _freq, \
+ .mult = _mult, \
+ .div = _div, \
+ .fixed_flags = _flags, \
+ }
+
+#endif /* __AW_CLK_H__ */
diff --git a/sys/arm/allwinner/clkng/aw_clk_frac.c b/sys/arm/allwinner/clkng/aw_clk_frac.c
new file mode 100644
index 000000000000..93235c0d3b15
--- /dev/null
+++ b/sys/arm/allwinner/clkng/aw_clk_frac.c
@@ -0,0 +1,399 @@
+/*-
+ * Copyright (c) 2019 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <dev/extres/clk/clk.h>
+
+#include <arm/allwinner/clkng/aw_clk.h>
+#include <arm/allwinner/clkng/aw_clk_frac.h>
+
+#include "clkdev_if.h"
+
+/* #define dprintf(format, arg...) printf("%s:(%s)" format, __func__, clknode_get_name(clk), arg) */
+#define dprintf(format, arg...)
+
+/*
+ * clknode for clocks matching the formula :
+ *
+ * clk = (24Mhz * n) / m in integer mode
+ * clk = frac_out1 or frac_out2 in fractional mode
+ *
+ */
+
+struct aw_clk_frac_sc {
+ uint32_t offset;
+
+ struct aw_clk_factor m;
+ struct aw_clk_factor n;
+ struct aw_clk_frac frac;
+
+ uint64_t min_freq;
+ uint64_t max_freq;
+
+ uint32_t mux_shift;
+ uint32_t mux_mask;
+ uint32_t gate_shift;
+ uint32_t lock_shift;
+ uint32_t lock_retries;
+
+ uint32_t flags;
+};
+
+#define WRITE4(_clk, off, val) \
+ CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)
+#define READ4(_clk, off, val) \
+ CLKDEV_READ_4(clknode_get_device(_clk), off, val)
+#define DEVICE_LOCK(_clk) \
+ CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
+#define DEVICE_UNLOCK(_clk) \
+ CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
+
+static int
+aw_clk_frac_init(struct clknode *clk, device_t dev)
+{
+ struct aw_clk_frac_sc *sc;
+ uint32_t val, idx;
+
+ sc = clknode_get_softc(clk);
+
+ idx = 0;
+ if ((sc->flags & AW_CLK_HAS_MUX) != 0) {
+ DEVICE_LOCK(clk);
+ READ4(clk, sc->offset, &val);
+ DEVICE_UNLOCK(clk);
+
+ idx = (val & sc->mux_mask) >> sc->mux_shift;
+ }
+
+ dprintf("init parent idx %d\n", idx);
+ clknode_init_parent_idx(clk, idx);
+ return (0);
+}
+
+static int
+aw_clk_frac_set_gate(struct clknode *clk, bool enable)
+{
+ struct aw_clk_frac_sc *sc;
+ uint32_t val;
+
+ sc = clknode_get_softc(clk);
+
+ if ((sc->flags & AW_CLK_HAS_GATE) == 0)
+ return (0);
+
+ dprintf("%sabling gate\n", enable ? "En" : "Dis");
+ DEVICE_LOCK(clk);
+ READ4(clk, sc->offset, &val);
+ if (enable)
+ val |= (1 << sc->gate_shift);
+ else
+ val &= ~(1 << sc->gate_shift);
+ WRITE4(clk, sc->offset, val);
+ DEVICE_UNLOCK(clk);
+
+ return (0);
+}
+
+static int
+aw_clk_frac_set_mux(struct clknode *clk, int index)
+{
+ struct aw_clk_frac_sc *sc;
+ uint32_t val;
+
+ sc = clknode_get_softc(clk);
+
+ if ((sc->flags & AW_CLK_HAS_MUX) == 0)
+ return (0);
+
+ dprintf("Set mux to %d\n", index);
+ DEVICE_LOCK(clk);
+ READ4(clk, sc->offset, &val);
+ val &= ~sc->mux_mask;
+ val |= index << sc->mux_shift;
+ WRITE4(clk, sc->offset, val);
+ DEVICE_UNLOCK(clk);
+
+ return (0);
+}
+
+static uint64_t
+aw_clk_frac_find_best(struct aw_clk_frac_sc *sc, uint64_t fparent, uint64_t fout,
+ uint32_t *factor_n, uint32_t *factor_m)
+{
+ uint64_t cur, best;
+ uint32_t m, n, max_m, max_n, min_m, min_n;
+
+ *factor_n = *factor_m = 0;
+ best = cur = 0;
+
+ max_m = aw_clk_factor_get_max(&sc->m);
+ max_n = aw_clk_factor_get_max(&sc->n);
+ min_m = aw_clk_factor_get_min(&sc->m);
+ min_n = sc->min_freq / fparent;
+
+ for (n = min_n; n <= max_n; n++) {
+ for (m = min_m; m <= max_m; m++) {
+ cur = fparent * n / m;
+ if (cur < sc->min_freq) {
+ continue;
+ }
+ if (cur > sc->max_freq) {
+ continue;
+ }
+ if (cur == fout) {
+ *factor_n = n;
+ *factor_m = m;
+ return (cur);
+ }
+ if (abs((fout - cur)) < abs((fout - best))) {
+ best = cur;
+ *factor_n = n;
+ *factor_m = m;
+ }
+ }
+ }
+
+ return (best);
+}
+
+static int
+aw_clk_frac_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,
+ int flags, int *stop)
+{
+ struct aw_clk_frac_sc *sc;
+ uint64_t cur, best, best_frac;
+ uint32_t val, m, n, best_m, best_n;
+ int retry, multiple, max_mult, best_mult;
+
+ sc = clknode_get_softc(clk);
+
+ best = best_frac = cur = 0;
+ best_mult = 0;
+ max_mult = 1;
+
+ dprintf("Trying to find freq %ju with parent %ju\n", *fout, fparent);
+ if ((flags & CLK_SET_ROUND_MULTIPLE) != 0)
+ max_mult = 10;
+ for (multiple = 1; multiple <= max_mult; multiple++) {
+ /* First test the fractional frequencies */
+ dprintf("Testing with multiple %d\n", multiple);
+ if (*fout * multiple == sc->frac.freq0) {
+ best = best_frac = sc->frac.freq0;
+ best_mult = multiple;
+ dprintf("Found with using frac.freq0 and multiple %d\n", multiple);
+ break;
+ }
+ else if (*fout * multiple == sc->frac.freq1) {
+ best = best_frac = sc->frac.freq1;
+ best_mult = multiple;
+ dprintf("Found with using frac.freq1 and multiple %d\n", multiple);
+ break;
+ }
+ else {
+ cur = aw_clk_frac_find_best(sc, fparent, *fout * multiple,
+ &n, &m);
+ dprintf("Got %ju with n=%d, m=%d\n", cur, n, m);
+ if (cur == (*fout * multiple)) {
+ best = cur;
+ best_mult = multiple;
+ best_n = n;
+ best_m = m;
+ dprintf("This is the one: n=%d m=%d mult=%d\n", best_n, best_m, best_mult);
+ break;
+ }
+ if (abs(((*fout * multiple) - cur)) < abs(((*fout * multiple) - best))) {
+ best = cur;
+ best_mult = multiple;
+ best_n = n;
+ best_m = m;
+ dprintf("This is the best for now: n=%d m=%d mult=%d\n", best_n, best_m, best_mult);
+ }
+ }
+ }
+
+ if (best < sc->min_freq ||
+ best > sc->max_freq) {
+ printf("%s: Cannot set %ju for %s (min=%ju max=%ju)\n",
+ __func__, best, clknode_get_name(clk),
+ sc->min_freq, sc->max_freq);
+ return (ERANGE);
+ }
+ if ((flags & CLK_SET_DRYRUN) != 0) {
+ *fout = best;
+ *stop = 1;
+ return (0);
+ }
+
+ if ((best < (*fout * best_mult)) &&
+ ((flags & CLK_SET_ROUND_DOWN) == 0)) {
+ *stop = 1;
+ return (ERANGE);
+ }
+ if ((best > *fout * best_mult) &&
+ ((flags & CLK_SET_ROUND_UP) == 0)) {
+ *stop = 1;
+ return (ERANGE);
+ }
+
+ DEVICE_LOCK(clk);
+ READ4(clk, sc->offset, &val);
+ /* Disable clock during freq changes */
+ val &= ~(1 << sc->gate_shift);
+ WRITE4(clk, sc->offset, val);
+
+ if (best_frac != 0) {
+ val &= ~sc->frac.mode_sel;
+ /* M should be 0 per the manual */
+ val &= ~sc->m.mask;
+ if (best_frac == sc->frac.freq0)
+ val &= ~sc->frac.freq_sel;
+ else
+ val |= sc->frac.freq_sel;
+ } else {
+ val |= sc->frac.mode_sel; /* Select integer mode */
+ n = aw_clk_factor_get_value(&sc->n, best_n);
+ m = aw_clk_factor_get_value(&sc->m, best_m);
+ val &= ~sc->n.mask;
+ val &= ~sc->m.mask;
+ val |= n << sc->n.shift;
+ val |= m << sc->m.shift;
+ }
+
+ /* Write the clock changes */
+ WRITE4(clk, sc->offset, val);
+
+ /* Enable clock now that we've change it */
+ val |= 1 << sc->gate_shift;
+ WRITE4(clk, sc->offset, val);
+ DEVICE_UNLOCK(clk);
+
+ for (retry = 0; retry < sc->lock_retries; retry++) {
+ READ4(clk, sc->offset, &val);
+ if ((val & (1 << sc->lock_shift)) != 0)
+ break;
+ DELAY(1000);
+ }
+
+ *fout = best;
+ *stop = 1;
+
+ return (0);
+}
+
+static int
+aw_clk_frac_recalc(struct clknode *clk, uint64_t *freq)
+{
+ struct aw_clk_frac_sc *sc;
+ uint32_t val, m, n;
+
+ sc = clknode_get_softc(clk);
+
+ DEVICE_LOCK(clk);
+ READ4(clk, sc->offset, &val);
+ DEVICE_UNLOCK(clk);
+
+ if ((val & sc->frac.mode_sel) == 0) {
+ if (val & sc->frac.freq_sel)
+ *freq = sc->frac.freq1;
+ else
+ *freq = sc->frac.freq0;
+ } else {
+ m = aw_clk_get_factor(val, &sc->m);
+ n = aw_clk_get_factor(val, &sc->n);
+ *freq = *freq * n / m;
+ }
+
+ return (0);
+}
+
+static clknode_method_t aw_frac_clknode_methods[] = {
+ /* Device interface */
+ CLKNODEMETHOD(clknode_init, aw_clk_frac_init),
+ CLKNODEMETHOD(clknode_set_gate, aw_clk_frac_set_gate),
+ CLKNODEMETHOD(clknode_set_mux, aw_clk_frac_set_mux),
+ CLKNODEMETHOD(clknode_recalc_freq, aw_clk_frac_recalc),
+ CLKNODEMETHOD(clknode_set_freq, aw_clk_frac_set_freq),
+ CLKNODEMETHOD_END
+};
+
+DEFINE_CLASS_1(aw_frac_clknode, aw_frac_clknode_class, aw_frac_clknode_methods,
+ sizeof(struct aw_clk_frac_sc), clknode_class);
+
+int
+aw_clk_frac_register(struct clkdom *clkdom, struct aw_clk_frac_def *clkdef)
+{
+ struct clknode *clk;
+ struct aw_clk_frac_sc *sc;
+
+ clk = clknode_create(clkdom, &aw_frac_clknode_class, &clkdef->clkdef);
+ if (clk == NULL)
+ return (1);
+
+ sc = clknode_get_softc(clk);
+
+ sc->offset = clkdef->offset;
+
+ sc->m.shift = clkdef->m.shift;
+ sc->m.width = clkdef->m.width;
+ sc->m.mask = ((1 << sc->m.width) - 1) << sc->m.shift;
+ sc->m.value = clkdef->m.value;
+ sc->m.flags = clkdef->m.flags;
+
+ sc->n.shift = clkdef->n.shift;
+ sc->n.width = clkdef->n.width;
+ sc->n.mask = ((1 << sc->n.width) - 1) << sc->n.shift;
+ sc->n.value = clkdef->n.value;
+ sc->n.flags = clkdef->n.flags;
+
+ sc->frac.freq0 = clkdef->frac.freq0;
+ sc->frac.freq1 = clkdef->frac.freq1;
+ sc->frac.mode_sel = 1 << clkdef->frac.mode_sel;
+ sc->frac.freq_sel = 1 << clkdef->frac.freq_sel;
+
+ sc->min_freq = clkdef->min_freq;
+ sc->max_freq = clkdef->max_freq;
+
+ sc->mux_shift = clkdef->mux_shift;
+ sc->mux_mask = ((1 << clkdef->mux_width) - 1) << sc->mux_shift;
+
+ sc->gate_shift = clkdef->gate_shift;
+
+ sc->lock_shift = clkdef->lock_shift;
+ sc->lock_retries = clkdef->lock_retries;
+
+ sc->flags = clkdef->flags;
+
+ clknode_register(clkdom, clk);
+
+ return (0);
+}
diff --git a/sys/arm/allwinner/clkng/aw_clk_frac.h b/sys/arm/allwinner/clkng/aw_clk_frac.h
new file mode 100644
index 000000000000..5206e1daf9e7
--- /dev/null
+++ b/sys/arm/allwinner/clkng/aw_clk_frac.h
@@ -0,0 +1,55 @@
+/*-
+ * Copyright (c) 2019 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __AW_CLK_FRAC_H__
+#define __AW_CLK_FRAC_H__
+
+#include <dev/extres/clk/clk.h>
+
+struct aw_clk_frac_def {
+ struct clknode_init_def clkdef;
+ uint32_t offset;
+
+ struct aw_clk_factor m;
+ struct aw_clk_factor n;
+ struct aw_clk_frac frac;
+
+ uint64_t min_freq;
+ uint64_t max_freq;
+
+ uint32_t mux_shift;
+ uint32_t mux_width;
+ uint32_t gate_shift;
+ uint32_t lock_shift;
+ uint32_t lock_retries;
+
+ uint32_t flags;
+};
+
+int aw_clk_frac_register(struct clkdom *clkdom, struct aw_clk_frac_def *clkdef);
+
+#endif /* __AW_CLK_FRAC_H__ */
diff --git a/sys/arm/allwinner/clkng/aw_clk_m.c b/sys/arm/allwinner/clkng/aw_clk_m.c
new file mode 100644
index 000000000000..4f8189d48299
--- /dev/null
+++ b/sys/arm/allwinner/clkng/aw_clk_m.c
@@ -0,0 +1,290 @@
+/*-
+ * Copyright (c) 2019 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <dev/extres/clk/clk.h>
+
+#include <arm/allwinner/clkng/aw_clk.h>
+#include <arm/allwinner/clkng/aw_clk_m.h>
+
+#include "clkdev_if.h"
+
+/*
+ * clknode for clocks matching the formula :
+ *
+ * clk = clkin / m
+ * And that needs to potentially :
+ * 1) Set the parent freq
+ * 2) Support Setting the parent to a multiple
+ *
+ */
+
+struct aw_clk_m_sc {
+ uint32_t offset;
+
+ struct aw_clk_factor m;
+
+ uint32_t mux_shift;
+ uint32_t mux_mask;
+ uint32_t gate_shift;
+
+ uint32_t flags;
+};
+
+#define WRITE4(_clk, off, val) \
+ CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)
+#define READ4(_clk, off, val) \
+ CLKDEV_READ_4(clknode_get_device(_clk), off, val)
+#define DEVICE_LOCK(_clk) \
+ CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
+#define DEVICE_UNLOCK(_clk) \
+ CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
+
+static int
+aw_clk_m_init(struct clknode *clk, device_t dev)
+{
+ struct aw_clk_m_sc *sc;
+ uint32_t val, idx;
+
+ sc = clknode_get_softc(clk);
+
+ idx = 0;
+ if ((sc->flags & AW_CLK_HAS_MUX) != 0) {
+ DEVICE_LOCK(clk);
+ READ4(clk, sc->offset, &val);
+ DEVICE_UNLOCK(clk);
+
+ idx = (val & sc->mux_mask) >> sc->mux_shift;
+ }
+
+ clknode_init_parent_idx(clk, idx);
+ return (0);
+}
+
+static int
+aw_clk_m_set_gate(struct clknode *clk, bool enable)
+{
+ struct aw_clk_m_sc *sc;
+ uint32_t val;
+
+ sc = clknode_get_softc(clk);
+
+ if ((sc->flags & AW_CLK_HAS_GATE) == 0)
+ return (0);
+
+ DEVICE_LOCK(clk);
+ READ4(clk, sc->offset, &val);
+ if (enable)
+ val |= (1 << sc->gate_shift);
+ else
+ val &= ~(1 << sc->gate_shift);
+ WRITE4(clk, sc->offset, val);
+ DEVICE_UNLOCK(clk);
+
+ return (0);
+}
+
+static int
+aw_clk_m_set_mux(struct clknode *clk, int index)
+{
+ struct aw_clk_m_sc *sc;
+ uint32_t val;
+
+ sc = clknode_get_softc(clk);
+
+ if ((sc->flags & AW_CLK_HAS_MUX) == 0)
+ return (0);
+
+ DEVICE_LOCK(clk);
+ READ4(clk, sc->offset, &val);
+ val &= ~sc->mux_mask;
+ val |= index << sc->mux_shift;
+ WRITE4(clk, sc->offset, val);
+ DEVICE_UNLOCK(clk);
+
+ return (0);
+}
+
+static uint64_t
+aw_clk_m_find_best(struct aw_clk_m_sc *sc, uint64_t fparent, uint64_t *fout,
+ uint32_t *factor_m)
+{
+ uint64_t cur, best;
+ uint32_t m, max_m, min_m;
+
+ *factor_m = 0;
+
+ max_m = aw_clk_factor_get_max(&sc->m);
+ min_m = aw_clk_factor_get_min(&sc->m);
+
+ for (m = min_m; m <= max_m; ) {
+ cur = fparent / m;
+ if (abs(*fout - cur) < abs(*fout - best)) {
+ best = cur;
+ *factor_m = m;
+ }
+ if ((sc->m.flags & AW_CLK_FACTOR_POWER_OF_TWO) != 0)
+ m <<= 1;
+ else
+ m++;
+ }
+
+ return (best);
+}
+
+static int
+aw_clk_m_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,
+ int flags, int *stop)
+{
+ struct aw_clk_m_sc *sc;
+ struct clknode *p_clk;
+ uint64_t cur, best;
+ uint32_t val, m, best_m;
+
+ sc = clknode_get_softc(clk);
+
+ best = cur = 0;
+
+ if ((sc->flags & AW_CLK_SET_PARENT) != 0) {
+ p_clk = clknode_get_parent(clk);
+ if (p_clk == NULL) {
+ printf("%s: Cannot get parent for clock %s\n",
+ __func__,
+ clknode_get_name(clk));
+ return (ENXIO);
+ }
+ clknode_set_freq(p_clk, *fout, CLK_SET_ROUND_MULTIPLE, 0);
+ clknode_get_freq(p_clk, &fparent);
+ best = aw_clk_m_find_best(sc, fparent, fout,
+ &best_m);
+ } else {
+ best = aw_clk_m_find_best(sc, fparent, fout,
+ &best_m);
+ }
+
+ if ((flags & CLK_SET_DRYRUN) != 0) {
+ *fout = best;
+ *stop = 1;
+ return (0);
+ }
+
+ if ((best < *fout) &&
+ ((flags & CLK_SET_ROUND_DOWN) == 0)) {
+ *stop = 1;
+ return (ERANGE);
+ }
+ if ((best > *fout) &&
+ ((flags & CLK_SET_ROUND_UP) == 0)) {
+ *stop = 1;
+ return (ERANGE);
+ }
+
+ DEVICE_LOCK(clk);
+ READ4(clk, sc->offset, &val);
+
+ m = aw_clk_factor_get_value(&sc->m, best_m);
+ val &= ~sc->m.mask;
+ val |= m << sc->m.shift;
+
+ WRITE4(clk, sc->offset, val);
+ DEVICE_UNLOCK(clk);
+
+ *fout = best;
+ *stop = 1;
+
+ return (0);
+}
+
+static int
+aw_clk_m_recalc(struct clknode *clk, uint64_t *freq)
+{
+ struct aw_clk_m_sc *sc;
+ uint32_t val, m;
+
+ sc = clknode_get_softc(clk);
+
+ DEVICE_LOCK(clk);
+ READ4(clk, sc->offset, &val);
+ DEVICE_UNLOCK(clk);
+
+ m = aw_clk_get_factor(val, &sc->m);
+
+ *freq = *freq / m;
+
+ return (0);
+}
+
+static clknode_method_t aw_m_clknode_methods[] = {
+ /* Device interface */
+ CLKNODEMETHOD(clknode_init, aw_clk_m_init),
+ CLKNODEMETHOD(clknode_set_gate, aw_clk_m_set_gate),
+ CLKNODEMETHOD(clknode_set_mux, aw_clk_m_set_mux),
+ CLKNODEMETHOD(clknode_recalc_freq, aw_clk_m_recalc),
+ CLKNODEMETHOD(clknode_set_freq, aw_clk_m_set_freq),
+ CLKNODEMETHOD_END
+};
+
+DEFINE_CLASS_1(aw_m_clknode, aw_m_clknode_class, aw_m_clknode_methods,
+ sizeof(struct aw_clk_m_sc), clknode_class);
+
+int
+aw_clk_m_register(struct clkdom *clkdom, struct aw_clk_m_def *clkdef)
+{
+ struct clknode *clk;
+ struct aw_clk_m_sc *sc;
+
+ clk = clknode_create(clkdom, &aw_m_clknode_class, &clkdef->clkdef);
+ if (clk == NULL)
+ return (1);
+
+ sc = clknode_get_softc(clk);
+
+ sc->offset = clkdef->offset;
+
+ sc->m.shift = clkdef->m.shift;
+ sc->m.width = clkdef->m.width;
+ sc->m.mask = ((1 << sc->m.width) - 1) << sc->m.shift;
+ sc->m.value = clkdef->m.value;
+ sc->m.flags = clkdef->m.flags;
+
+ sc->mux_shift = clkdef->mux_shift;
+ sc->mux_mask = ((1 << clkdef->mux_width) - 1) << sc->mux_shift;
+
+ sc->gate_shift = clkdef->gate_shift;
+
+ sc->flags = clkdef->flags;
+
+ clknode_register(clkdom, clk);
+
+ return (0);
+}
diff --git a/sys/arm/allwinner/clkng/aw_clk_m.h b/sys/arm/allwinner/clkng/aw_clk_m.h
new file mode 100644
index 000000000000..028a1fe541cf
--- /dev/null
+++ b/sys/arm/allwinner/clkng/aw_clk_m.h
@@ -0,0 +1,48 @@
+/*-
+ * Copyright (c) 2019 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __AW_CLK_M_H__
+#define __AW_CLK_M_H__
+
+#include <dev/extres/clk/clk.h>
+
+struct aw_clk_m_def {
+ struct clknode_init_def clkdef;
+ uint32_t offset;
+
+ struct aw_clk_factor m;
+
+ uint32_t mux_shift;
+ uint32_t mux_width;
+ uint32_t gate_shift;
+
+ uint32_t flags;
+};
+
+int aw_clk_m_register(struct clkdom *clkdom, struct aw_clk_m_def *clkdef);
+
+#endif /* __AW_CLK_M_H__ */
diff --git a/sys/arm/allwinner/clkng/aw_clk_mipi.c b/sys/arm/allwinner/clkng/aw_clk_mipi.c
new file mode 100644
index 000000000000..a0a64cb4330b
--- /dev/null
+++ b/sys/arm/allwinner/clkng/aw_clk_mipi.c
@@ -0,0 +1,297 @@
+/*-
+ * Copyright (c) 2019 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <dev/extres/clk/clk.h>
+
+#include <arm/allwinner/clkng/aw_clk.h>
+#include <arm/allwinner/clkng/aw_clk_mipi.h>
+
+#include "clkdev_if.h"
+
+/* #define dprintf(format, arg...) printf("%s:(%s)" format, __func__, clknode_get_name(clk), arg) */
+#define dprintf(format, arg...)
+
+/*
+ * clknode for PLL_MIPI :
+ *
+ * clk = (pll_video0 * n * k) / m when vfb_sel=0
+ * clk depend on sint_frac, sdiv2, s6p25_7p5, pll_feedback_div when vfb_sel=1
+ *
+ */
+
+struct aw_clk_mipi_sc {
+ uint32_t offset;
+
+ struct aw_clk_factor k;
+ struct aw_clk_factor m;
+ struct aw_clk_factor n;
+
+ uint64_t min_freq;
+ uint64_t max_freq;
+
+ uint32_t gate_shift;
+ uint32_t lock_shift;
+ uint32_t lock_retries;
+
+ uint32_t flags;
+};
+
+#define WRITE4(_clk, off, val) \
+ CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)
+#define READ4(_clk, off, val) \
+ CLKDEV_READ_4(clknode_get_device(_clk), off, val)
+#define DEVICE_LOCK(_clk) \
+ CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
+#define DEVICE_UNLOCK(_clk) \
+ CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
+
+#define LDO1_EN_SHIFT 23
+#define LDO2_EN_SHIFT 22
+#define VFB_SEL_SHIFT 16
+
+static int
+aw_clk_mipi_init(struct clknode *clk, device_t dev)
+{
+ struct aw_clk_mipi_sc *sc;
+
+ sc = clknode_get_softc(clk);
+
+ clknode_init_parent_idx(clk, 0);
+ return (0);
+}
+
+static int
+aw_clk_mipi_set_gate(struct clknode *clk, bool enable)
+{
+ struct aw_clk_mipi_sc *sc;
+ uint32_t val;
+
+ sc = clknode_get_softc(clk);
+
+ dprintf("%sabling gate\n", enable ? "En" : "Dis");
+ DEVICE_LOCK(clk);
+ READ4(clk, sc->offset, &val);
+ if (enable) {
+ val |= (1 << sc->gate_shift);
+ val |= (1 << LDO1_EN_SHIFT);
+ val |= (1 << LDO2_EN_SHIFT);
+ } else {
+ val &= ~(1 << sc->gate_shift);
+ val &= ~(1 << LDO1_EN_SHIFT);
+ val &= ~(1 << LDO2_EN_SHIFT);
+ }
+ WRITE4(clk, sc->offset, val);
+ DEVICE_UNLOCK(clk);
+
+ return (0);
+}
+
+static uint64_t
+aw_clk_mipi_find_best(struct aw_clk_mipi_sc *sc, uint64_t fparent, uint64_t *fout,
+ uint32_t *factor_k, uint32_t *factor_m, uint32_t *factor_n)
+{
+ uint64_t cur, best;
+ uint32_t n, k, m;
+
+ best = 0;
+ *factor_n = 0;
+ *factor_k = 0;
+ *factor_m = 0;
+
+ for (n = aw_clk_factor_get_min(&sc->n); n <= aw_clk_factor_get_max(&sc->n); n++) {
+ for (k = aw_clk_factor_get_min(&sc->k); k <= aw_clk_factor_get_max(&sc->k); k++) {
+ for (m = aw_clk_factor_get_min(&sc->m); m <= aw_clk_factor_get_max(&sc->m); m++) {
+ cur = (fparent * n * k) / m;
+ if ((*fout - cur) < (*fout - best)) {
+ best = cur;
+ *factor_n = n;
+ *factor_k = k;
+ *factor_m = m;
+ }
+ if (best == *fout)
+ return (best);
+ }
+ }
+ }
+
+ return best;
+}
+
+static int
+aw_clk_mipi_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,
+ int flags, int *stop)
+{
+ struct aw_clk_mipi_sc *sc;
+ uint64_t best = 0;
+ uint32_t best_k, best_m, best_n;
+ uint32_t k, m, n;
+ uint32_t val;
+ uint32_t retry;
+
+ sc = clknode_get_softc(clk);
+
+ best = aw_clk_mipi_find_best(sc, fparent, fout, &best_k, &best_m, &best_n);
+
+ if (best < sc->min_freq ||
+ best > sc->max_freq) {
+ printf("%s: Cannot set %ju for %s (min=%ju max=%ju)\n",
+ __func__, best, clknode_get_name(clk),
+ sc->min_freq, sc->max_freq);
+ return (ERANGE);
+ }
+ if ((flags & CLK_SET_DRYRUN) != 0) {
+ *fout = best;
+ *stop = 1;
+ return (0);
+ }
+
+ DEVICE_LOCK(clk);
+ READ4(clk, sc->offset, &val);
+ /* Disable clock during freq changes */
+ val &= ~(1 << sc->gate_shift);
+ WRITE4(clk, sc->offset, val);
+
+ k = aw_clk_factor_get_value(&sc->k, best_k);
+ n = aw_clk_factor_get_value(&sc->n, best_n);
+ m = aw_clk_factor_get_value(&sc->m, best_m);
+ val &= ~sc->k.mask;
+ val &= ~sc->m.mask;
+ val &= ~sc->n.mask;
+ val |= k << sc->k.shift;
+ val |= m << sc->m.shift;
+ val |= n << sc->n.shift;
+
+ /* Write the clock changes */
+ WRITE4(clk, sc->offset, val);
+
+ /* Enable clock now that we've change it */
+ val |= 1 << sc->gate_shift;
+ WRITE4(clk, sc->offset, val);
+ DEVICE_UNLOCK(clk);
+
+ for (retry = 0; retry < sc->lock_retries; retry++) {
+ READ4(clk, sc->offset, &val);
+ if ((val & (1 << sc->lock_shift)) != 0)
+ break;
+ DELAY(1000);
+ }
+
+ *fout = best;
+ *stop = 1;
+
+ return (0);
+}
+
+static int
+aw_clk_mipi_recalc(struct clknode *clk, uint64_t *freq)
+{
+ struct aw_clk_mipi_sc *sc;
+ uint32_t val, m, n, k;
+
+ sc = clknode_get_softc(clk);
+
+ DEVICE_LOCK(clk);
+ READ4(clk, sc->offset, &val);
+ DEVICE_UNLOCK(clk);
+
+ k = aw_clk_get_factor(val, &sc->k);
+ m = aw_clk_get_factor(val, &sc->m);
+ n = aw_clk_get_factor(val, &sc->n);
+
+ *freq = (*freq * n * k) / m;
+
+ return (0);
+}
+
+static clknode_method_t aw_mipi_clknode_methods[] = {
+ /* Device interface */
+ CLKNODEMETHOD(clknode_init, aw_clk_mipi_init),
+ CLKNODEMETHOD(clknode_set_gate, aw_clk_mipi_set_gate),
+ CLKNODEMETHOD(clknode_recalc_freq, aw_clk_mipi_recalc),
+ CLKNODEMETHOD(clknode_set_freq, aw_clk_mipi_set_freq),
+ CLKNODEMETHOD_END
+};
+
+DEFINE_CLASS_1(aw_mipi_clknode, aw_mipi_clknode_class, aw_mipi_clknode_methods,
+ sizeof(struct aw_clk_mipi_sc), clknode_class);
+
+int
+aw_clk_mipi_register(struct clkdom *clkdom, struct aw_clk_mipi_def *clkdef)
+{
+ struct clknode *clk;
+ struct aw_clk_mipi_sc *sc;
+
+ clk = clknode_create(clkdom, &aw_mipi_clknode_class, &clkdef->clkdef);
+ if (clk == NULL)
+ return (1);
+
+ sc = clknode_get_softc(clk);
+
+ sc->offset = clkdef->offset;
+
+ sc->k.shift = clkdef->k.shift;
+ sc->k.width = clkdef->k.width;
+ sc->k.mask = ((1 << sc->k.width) - 1) << sc->k.shift;
+ sc->k.value = clkdef->k.value;
+ sc->k.flags = clkdef->k.flags;
+ sc->k.min_value = clkdef->k.min_value;
+
+ sc->m.shift = clkdef->m.shift;
+ sc->m.width = clkdef->m.width;
+ sc->m.mask = ((1 << sc->m.width) - 1) << sc->m.shift;
+ sc->m.value = clkdef->m.value;
+ sc->m.flags = clkdef->m.flags;
+ sc->m.min_value = clkdef->m.min_value;
+
+ sc->n.shift = clkdef->n.shift;
+ sc->n.width = clkdef->n.width;
+ sc->n.mask = ((1 << sc->n.width) - 1) << sc->n.shift;
+ sc->n.value = clkdef->n.value;
+ sc->n.flags = clkdef->n.flags;
+ sc->n.min_value = clkdef->n.min_value;
+
+ sc->min_freq = clkdef->min_freq;
+ sc->max_freq = clkdef->max_freq;
+
+ sc->gate_shift = clkdef->gate_shift;
+
+ sc->lock_shift = clkdef->lock_shift;
+ sc->lock_retries = clkdef->lock_retries;
+
+ sc->flags = clkdef->flags;
+
+ clknode_register(clkdom, clk);
+
+ return (0);
+}
diff --git a/sys/arm/allwinner/clkng/aw_clk_mipi.h b/sys/arm/allwinner/clkng/aw_clk_mipi.h
new file mode 100644
index 000000000000..e2fe50003e97
--- /dev/null
+++ b/sys/arm/allwinner/clkng/aw_clk_mipi.h
@@ -0,0 +1,53 @@
+/*-
+ * Copyright (c) 2019 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __AW_CLK_MIPI_H__
+#define __AW_CLK_MIPI_H__
+
+#include <dev/extres/clk/clk.h>
+
+struct aw_clk_mipi_def {
+ struct clknode_init_def clkdef;
+ uint32_t offset;
+
+ struct aw_clk_factor k;
+ struct aw_clk_factor m;
+ struct aw_clk_factor n;
+
+ uint64_t min_freq;
+ uint64_t max_freq;
+
+ uint32_t gate_shift;
+ uint32_t lock_shift;
+ uint32_t lock_retries;
+
+ uint32_t flags;
+};
+
+int aw_clk_mipi_register(struct clkdom *clkdom, struct aw_clk_mipi_def *clkdef);
+
+#endif /* __AW_CLK_MIPI_H__ */
diff --git a/sys/arm/allwinner/clkng/aw_clk_nkmp.c b/sys/arm/allwinner/clkng/aw_clk_nkmp.c
new file mode 100644
index 000000000000..d76b4b242fce
--- /dev/null
+++ b/sys/arm/allwinner/clkng/aw_clk_nkmp.c
@@ -0,0 +1,414 @@
+/*-
+ * Copyright (c) 2017 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <dev/extres/clk/clk.h>
+
+#include <arm/allwinner/clkng/aw_clk.h>
+#include <arm/allwinner/clkng/aw_clk_nkmp.h>
+
+#include "clkdev_if.h"
+
+/*
+ * clknode for clocks matching the formula :
+ *
+ * clk = (clkin * n * k) / (m * p)
+ *
+ */
+
+struct aw_clk_nkmp_sc {
+ uint32_t offset;
+
+ struct aw_clk_factor n;
+ struct aw_clk_factor k;
+ struct aw_clk_factor m;
+ struct aw_clk_factor p;
+
+ uint32_t mux_shift;
+ uint32_t mux_mask;
+ uint32_t gate_shift;
+ uint32_t lock_shift;
+ uint32_t lock_retries;
+ uint32_t update_shift;
+
+ uint32_t flags;
+};
+
+#define WRITE4(_clk, off, val) \
+ CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)
+#define READ4(_clk, off, val) \
+ CLKDEV_READ_4(clknode_get_device(_clk), off, val)
+#define MODIFY4(_clk, off, clr, set ) \
+ CLKDEV_MODIFY_4(clknode_get_device(_clk), off, clr, set)
+#define DEVICE_LOCK(_clk) \
+ CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
+#define DEVICE_UNLOCK(_clk) \
+ CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
+
+static int
+aw_clk_nkmp_init(struct clknode *clk, device_t dev)
+{
+ struct aw_clk_nkmp_sc *sc;
+ uint32_t val, idx;
+
+ sc = clknode_get_softc(clk);
+
+ idx = 0;
+ if ((sc->flags & AW_CLK_HAS_MUX) != 0) {
+ DEVICE_LOCK(clk);
+ READ4(clk, sc->offset, &val);
+ DEVICE_UNLOCK(clk);
+
+ idx = (val & sc->mux_mask) >> sc->mux_shift;
+ }
+
+ clknode_init_parent_idx(clk, idx);
+ return (0);
+}
+
+static int
+aw_clk_nkmp_set_gate(struct clknode *clk, bool enable)
+{
+ struct aw_clk_nkmp_sc *sc;
+ uint32_t val;
+
+ sc = clknode_get_softc(clk);
+
+ if ((sc->flags & AW_CLK_HAS_GATE) == 0)
+ return (0);
+
+ DEVICE_LOCK(clk);
+ READ4(clk, sc->offset, &val);
+ if (enable)
+ val |= (1 << sc->gate_shift);
+ else
+ val &= ~(1 << sc->gate_shift);
+ WRITE4(clk, sc->offset, val);
+ DEVICE_UNLOCK(clk);
+
+ return (0);
+}
+
+static int
+aw_clk_nkmp_set_mux(struct clknode *clk, int index)
+{
+ struct aw_clk_nkmp_sc *sc;
+ uint32_t val;
+
+ sc = clknode_get_softc(clk);
+
+ if ((sc->flags & AW_CLK_HAS_MUX) == 0)
+ return (0);
+
+ DEVICE_LOCK(clk);
+ READ4(clk, sc->offset, &val);
+ val &= ~sc->mux_mask;
+ val |= index << sc->mux_shift;
+ WRITE4(clk, sc->offset, val);
+ DEVICE_UNLOCK(clk);
+
+ return (0);
+}
+
+static uint64_t
+aw_clk_nkmp_find_best(struct aw_clk_nkmp_sc *sc, uint64_t fparent, uint64_t *fout,
+ uint32_t *factor_n, uint32_t *factor_k, uint32_t *factor_m, uint32_t *factor_p)
+{
+ uint64_t cur, best;
+ uint32_t n, k, m, p;
+
+ best = 0;
+ *factor_n = 0;
+ *factor_k = 0;
+ *factor_m = 0;
+ *factor_p = 0;
+
+ for (n = aw_clk_factor_get_min(&sc->n); n <= aw_clk_factor_get_max(&sc->n); ) {
+ for (k = aw_clk_factor_get_min(&sc->k); k <= aw_clk_factor_get_max(&sc->k); ) {
+ for (m = aw_clk_factor_get_min(&sc->m); m <= aw_clk_factor_get_max(&sc->m); ) {
+ for (p = aw_clk_factor_get_min(&sc->p); p <= aw_clk_factor_get_max(&sc->p); ) {
+ cur = (fparent * n * k) / (m * p);
+ if ((*fout - cur) < (*fout - best)) {
+ best = cur;
+ *factor_n = n;
+ *factor_k = k;
+ *factor_m = m;
+ *factor_p = p;
+ }
+ if (best == *fout)
+ return (best);
+ if ((sc->p.flags & AW_CLK_FACTOR_POWER_OF_TWO) != 0)
+ p <<= 1;
+ else
+ p++;
+ }
+ if ((sc->m.flags & AW_CLK_FACTOR_POWER_OF_TWO) != 0)
+ m <<= 1;
+ else
+ m++;
+ }
+ if ((sc->k.flags & AW_CLK_FACTOR_POWER_OF_TWO) != 0)
+ k <<= 1;
+ else
+ k++;
+ }
+ if ((sc->n.flags & AW_CLK_FACTOR_POWER_OF_TWO) != 0)
+ n <<= 1;
+ else
+ n++;
+ }
+
+ return best;
+}
+
+static void
+aw_clk_nkmp_set_freq_scale(struct clknode *clk, struct aw_clk_nkmp_sc *sc,
+ uint32_t factor_n, uint32_t factor_k, uint32_t factor_m, uint32_t factor_p)
+{
+ uint32_t val, n, k, m, p;
+ int retry;
+
+ DEVICE_LOCK(clk);
+ READ4(clk, sc->offset, &val);
+
+ n = aw_clk_get_factor(val, &sc->n);
+ k = aw_clk_get_factor(val, &sc->k);
+ m = aw_clk_get_factor(val, &sc->m);
+ p = aw_clk_get_factor(val, &sc->p);
+
+ if (p < factor_p) {
+ val &= ~sc->p.mask;
+ val |= aw_clk_factor_get_value(&sc->p, factor_p) << sc->p.shift;
+ WRITE4(clk, sc->offset, val);
+ DELAY(2000);
+ }
+
+ if (m < factor_m) {
+ val &= ~sc->m.mask;
+ val |= aw_clk_factor_get_value(&sc->m, factor_m) << sc->m.shift;
+ WRITE4(clk, sc->offset, val);
+ DELAY(2000);
+ }
+
+ val &= ~sc->n.mask;
+ val &= ~sc->k.mask;
+ val |= aw_clk_factor_get_value(&sc->n, factor_n) << sc->n.shift;
+ val |= aw_clk_factor_get_value(&sc->k, factor_k) << sc->k.shift;
+ WRITE4(clk, sc->offset, val);
+ DELAY(2000);
+
+ if (m > factor_m) {
+ val &= ~sc->m.mask;
+ val |= aw_clk_factor_get_value(&sc->m, factor_m) << sc->m.shift;
+ WRITE4(clk, sc->offset, val);
+ DELAY(2000);
+ }
+
+ if (p > factor_p) {
+ val &= ~sc->p.mask;
+ val |= aw_clk_factor_get_value(&sc->p, factor_p) << sc->p.shift;
+ WRITE4(clk, sc->offset, val);
+ DELAY(2000);
+ }
+
+ if ((sc->flags & AW_CLK_HAS_LOCK) != 0) {
+ for (retry = 0; retry < sc->lock_retries; retry++) {
+ READ4(clk, sc->offset, &val);
+ if ((val & (1 << sc->lock_shift)) != 0)
+ break;
+ DELAY(1000);
+ }
+ }
+
+ DEVICE_UNLOCK(clk);
+}
+
+static int
+aw_clk_nkmp_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,
+ int flags, int *stop)
+{
+ struct aw_clk_nkmp_sc *sc;
+ uint64_t best;
+ uint32_t val, best_n, best_k, best_m, best_p;
+ int retry;
+
+ sc = clknode_get_softc(clk);
+
+ best = aw_clk_nkmp_find_best(sc, fparent, fout,
+ &best_n, &best_k, &best_m, &best_p);
+ if ((flags & CLK_SET_DRYRUN) != 0) {
+ *fout = best;
+ *stop = 1;
+ return (0);
+ }
+
+ if ((best < *fout) &&
+ ((flags & CLK_SET_ROUND_DOWN) != 0)) {
+ *stop = 1;
+ return (ERANGE);
+ }
+ if ((best > *fout) &&
+ ((flags & CLK_SET_ROUND_UP) != 0)) {
+ *stop = 1;
+ return (ERANGE);
+ }
+
+ if ((sc->flags & AW_CLK_SCALE_CHANGE) != 0)
+ aw_clk_nkmp_set_freq_scale(clk, sc,
+ best_n, best_k, best_m, best_p);
+ else {
+ DEVICE_LOCK(clk);
+ READ4(clk, sc->offset, &val);
+ val &= ~sc->n.mask;
+ val &= ~sc->k.mask;
+ val &= ~sc->m.mask;
+ val &= ~sc->p.mask;
+ val |= aw_clk_factor_get_value(&sc->n, best_n) << sc->n.shift;
+ val |= aw_clk_factor_get_value(&sc->k, best_k) << sc->k.shift;
+ val |= aw_clk_factor_get_value(&sc->m, best_m) << sc->m.shift;
+ val |= aw_clk_factor_get_value(&sc->p, best_p) << sc->p.shift;
+ WRITE4(clk, sc->offset, val);
+ DELAY(2000);
+ DEVICE_UNLOCK(clk);
+
+ if ((sc->flags & AW_CLK_HAS_UPDATE) != 0) {
+ DEVICE_LOCK(clk);
+ READ4(clk, sc->offset, &val);
+ val |= 1 << sc->update_shift;
+ WRITE4(clk, sc->offset, val);
+ DELAY(2000);
+ DEVICE_UNLOCK(clk);
+ }
+
+ if ((sc->flags & AW_CLK_HAS_LOCK) != 0) {
+ for (retry = 0; retry < sc->lock_retries; retry++) {
+ READ4(clk, sc->offset, &val);
+ if ((val & (1 << sc->lock_shift)) != 0)
+ break;
+ DELAY(1000);
+ }
+ }
+ }
+
+ *fout = best;
+ *stop = 1;
+
+ return (0);
+}
+
+static int
+aw_clk_nkmp_recalc(struct clknode *clk, uint64_t *freq)
+{
+ struct aw_clk_nkmp_sc *sc;
+ uint32_t val, m, n, k, p;
+
+ sc = clknode_get_softc(clk);
+
+ DEVICE_LOCK(clk);
+ READ4(clk, sc->offset, &val);
+ DEVICE_UNLOCK(clk);
+
+ n = aw_clk_get_factor(val, &sc->n);
+ k = aw_clk_get_factor(val, &sc->k);
+ m = aw_clk_get_factor(val, &sc->m);
+ p = aw_clk_get_factor(val, &sc->p);
+
+ *freq = (*freq * n * k) / (m * p);
+
+ return (0);
+}
+
+static clknode_method_t aw_nkmp_clknode_methods[] = {
+ /* Device interface */
+ CLKNODEMETHOD(clknode_init, aw_clk_nkmp_init),
+ CLKNODEMETHOD(clknode_set_gate, aw_clk_nkmp_set_gate),
+ CLKNODEMETHOD(clknode_set_mux, aw_clk_nkmp_set_mux),
+ CLKNODEMETHOD(clknode_recalc_freq, aw_clk_nkmp_recalc),
+ CLKNODEMETHOD(clknode_set_freq, aw_clk_nkmp_set_freq),
+ CLKNODEMETHOD_END
+};
+
+DEFINE_CLASS_1(aw_nkmp_clknode, aw_nkmp_clknode_class, aw_nkmp_clknode_methods,
+ sizeof(struct aw_clk_nkmp_sc), clknode_class);
+
+int
+aw_clk_nkmp_register(struct clkdom *clkdom, struct aw_clk_nkmp_def *clkdef)
+{
+ struct clknode *clk;
+ struct aw_clk_nkmp_sc *sc;
+
+ clk = clknode_create(clkdom, &aw_nkmp_clknode_class, &clkdef->clkdef);
+ if (clk == NULL)
+ return (1);
+
+ sc = clknode_get_softc(clk);
+
+ sc->offset = clkdef->offset;
+
+ sc->n.shift = clkdef->n.shift;
+ sc->n.width = clkdef->n.width;
+ sc->n.mask = ((1 << clkdef->n.width) - 1) << sc->n.shift;
+ sc->n.value = clkdef->n.value;
+ sc->n.flags = clkdef->n.flags;
+
+ sc->k.shift = clkdef->k.shift;
+ sc->k.width = clkdef->k.width;
+ sc->k.mask = ((1 << clkdef->k.width) - 1) << sc->k.shift;
+ sc->k.value = clkdef->k.value;
+ sc->k.flags = clkdef->k.flags;
+
+ sc->m.shift = clkdef->m.shift;
+ sc->m.width = clkdef->m.width;
+ sc->m.mask = ((1 << clkdef->m.width) - 1) << sc->m.shift;
+ sc->m.value = clkdef->m.value;
+ sc->m.flags = clkdef->m.flags;
+
+ sc->p.shift = clkdef->p.shift;
+ sc->p.width = clkdef->p.width;
+ sc->p.mask = ((1 << clkdef->p.width) - 1) << sc->p.shift;
+ sc->p.value = clkdef->p.value;
+ sc->p.flags = clkdef->p.flags;
+
+ sc->mux_shift = clkdef->mux_shift;
+ sc->mux_mask = ((1 << clkdef->mux_width) - 1) << sc->mux_shift;
+
+ sc->gate_shift = clkdef->gate_shift;
+ sc->lock_shift = clkdef->lock_shift;
+ sc->lock_retries = clkdef->lock_retries;
+ sc->update_shift = clkdef->update_shift;
+ sc->flags = clkdef->flags;
+
+ clknode_register(clkdom, clk);
+
+ return (0);
+}
diff --git a/sys/arm/allwinner/clkng/aw_clk_nkmp.h b/sys/arm/allwinner/clkng/aw_clk_nkmp.h
new file mode 100644
index 000000000000..a0c381830eb4
--- /dev/null
+++ b/sys/arm/allwinner/clkng/aw_clk_nkmp.h
@@ -0,0 +1,56 @@
+/*-
+ * Copyright (c) 2017 Emmanuel Vadot <manu@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __AW_CLK_NKMP_H__
+#define __AW_CLK_NKMP_H__
+
+#include <arm/allwinner/clkng/aw_clk.h>
+
+struct aw_clk_nkmp_def {
+ struct clknode_init_def clkdef;
+
+ uint32_t offset;
+
+ struct aw_clk_factor m;
+ struct aw_clk_factor k;
+ struct aw_clk_factor n;
+ struct aw_clk_factor p;
+
+ uint32_t mux_shift;
+ uint32_t mux_width;
+ uint32_t gate_shift;
+ uint32_t lock_shift;
+ uint32_t lock_retries;
+ uint32_t update_shift;
+
+ uint32_t flags;
+};
+
+int aw_clk_nkmp_register(struct clkdom *clkdom, struct aw_clk_nkmp_def *clkdef);
+
+#endif /* __AW_CLK_NKMP_H__ */
diff --git a/sys/arm/allwinner/clkng/aw_clk_nm.c b/sys/arm/allwinner/clkng/aw_clk_nm.c
new file mode 100644
index 000000000000..c7a302207b65
--- /dev/null
+++ b/sys/arm/allwinner/clkng/aw_clk_nm.c
@@ -0,0 +1,357 @@
+/*-
+ * Copyright (c) 2017 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <dev/extres/clk/clk.h>
+
+#include <arm/allwinner/clkng/aw_clk.h>
+#include <arm/allwinner/clkng/aw_clk_nm.h>
+
+#include "clkdev_if.h"
+
+/*
+ * clknode for clocks matching the formula :
+ *
+ * clk = clkin / n / m
+ *
+ */
+
+struct aw_clk_nm_sc {
+ uint32_t offset;
+
+ struct aw_clk_factor m;
+ struct aw_clk_factor n;
+ struct aw_clk_factor prediv;
+
+ uint32_t mux_shift;
+ uint32_t mux_mask;
+ uint32_t gate_shift;
+ uint32_t lock_shift;
+ uint32_t lock_retries;
+
+ uint32_t flags;
+};
+
+#define WRITE4(_clk, off, val) \
+ CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)
+#define READ4(_clk, off, val) \
+ CLKDEV_READ_4(clknode_get_device(_clk), off, val)
+#define DEVICE_LOCK(_clk) \
+ CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
+#define DEVICE_UNLOCK(_clk) \
+ CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
+
+static int
+aw_clk_nm_init(struct clknode *clk, device_t dev)
+{
+ struct aw_clk_nm_sc *sc;
+ uint32_t val, idx;
+
+ sc = clknode_get_softc(clk);
+
+ idx = 0;
+ if ((sc->flags & AW_CLK_HAS_MUX) != 0) {
+ DEVICE_LOCK(clk);
+ READ4(clk, sc->offset, &val);
+ DEVICE_UNLOCK(clk);
+
+ idx = (val & sc->mux_mask) >> sc->mux_shift;
+ }
+
+ clknode_init_parent_idx(clk, idx);
+ return (0);
+}
+
+static int
+aw_clk_nm_set_gate(struct clknode *clk, bool enable)
+{
+ struct aw_clk_nm_sc *sc;
+ uint32_t val;
+
+ sc = clknode_get_softc(clk);
+
+ if ((sc->flags & AW_CLK_HAS_GATE) == 0)
+ return (0);
+
+ DEVICE_LOCK(clk);
+ READ4(clk, sc->offset, &val);
+ if (enable)
+ val |= (1 << sc->gate_shift);
+ else
+ val &= ~(1 << sc->gate_shift);
+ WRITE4(clk, sc->offset, val);
+ DEVICE_UNLOCK(clk);
+
+ return (0);
+}
+
+static int
+aw_clk_nm_set_mux(struct clknode *clk, int index)
+{
+ struct aw_clk_nm_sc *sc;
+ uint32_t val;
+
+ sc = clknode_get_softc(clk);
+
+ if ((sc->flags & AW_CLK_HAS_MUX) == 0)
+ return (0);
+
+ DEVICE_LOCK(clk);
+ READ4(clk, sc->offset, &val);
+ val &= ~sc->mux_mask;
+ val |= index << sc->mux_shift;
+ WRITE4(clk, sc->offset, val);
+ DEVICE_UNLOCK(clk);
+
+ return (0);
+}
+
+static uint64_t
+aw_clk_nm_find_best(struct aw_clk_nm_sc *sc, uint64_t fparent, uint64_t *fout,
+ uint32_t *factor_n, uint32_t *factor_m)
+{
+ uint64_t cur, best;
+ uint32_t m, n, max_m, max_n, min_m, min_n;
+
+ *factor_n = *factor_m = 0;
+
+ max_m = aw_clk_factor_get_max(&sc->m);
+ max_n = aw_clk_factor_get_max(&sc->n);
+ min_m = aw_clk_factor_get_min(&sc->m);
+ min_n = aw_clk_factor_get_min(&sc->n);
+
+ for (m = min_m; m <= max_m; ) {
+ for (n = min_n; n <= max_n; ) {
+ cur = fparent / n / m;
+ if (clk_freq_diff(*fout, cur) <
+ clk_freq_diff(*fout, best)) {
+ best = cur;
+ *factor_n = n;
+ *factor_m = m;
+ }
+
+ if ((sc->n.flags & AW_CLK_FACTOR_POWER_OF_TWO) != 0)
+ n <<= 1;
+ else
+ n++;
+ }
+ if ((sc->m.flags & AW_CLK_FACTOR_POWER_OF_TWO) != 0)
+ m <<= 1;
+ else
+ m++;
+ }
+
+ return (best);
+}
+
+static int
+aw_clk_nm_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,
+ int flags, int *stop)
+{
+ struct aw_clk_nm_sc *sc;
+ struct clknode *p_clk;
+ const char **p_names;
+ uint64_t cur, best;
+ uint32_t val, m, n, best_m, best_n;
+ int p_idx, best_parent, retry;
+
+ sc = clknode_get_softc(clk);
+
+ best = cur = 0;
+ best_parent = 0;
+
+ if ((sc->flags & AW_CLK_REPARENT) != 0) {
+ p_names = clknode_get_parent_names(clk);
+ for (p_idx = 0; p_idx != clknode_get_parents_num(clk); p_idx++) {
+ p_clk = clknode_find_by_name(p_names[p_idx]);
+ clknode_get_freq(p_clk, &fparent);
+
+ cur = aw_clk_nm_find_best(sc, fparent, fout, &n, &m);
+ if (clk_freq_diff(*fout, cur) <
+ clk_freq_diff(*fout, best)) {
+ best = cur;
+ best_parent = p_idx;
+ best_n = n;
+ best_m = m;
+ }
+ }
+
+ p_idx = clknode_get_parent_idx(clk);
+ p_clk = clknode_get_parent(clk);
+ clknode_get_freq(p_clk, &fparent);
+ } else {
+ best = aw_clk_nm_find_best(sc, fparent, fout,
+ &best_n, &best_m);
+ }
+
+ if ((flags & CLK_SET_DRYRUN) != 0) {
+ *fout = best;
+ *stop = 1;
+ return (0);
+ }
+
+ if ((best < *fout) &&
+ ((flags & CLK_SET_ROUND_DOWN) == 0)) {
+ *stop = 1;
+ printf("best freq (%ju) < requested freq(%ju)\n",
+ best, *fout);
+ return (ERANGE);
+ }
+ if ((best > *fout) &&
+ ((flags & CLK_SET_ROUND_UP) == 0)) {
+ *stop = 1;
+ printf("best freq (%ju) > requested freq(%ju)\n",
+ best, *fout);
+ return (ERANGE);
+ }
+
+ if ((sc->flags & AW_CLK_REPARENT) != 0 && p_idx != best_parent)
+ clknode_set_parent_by_idx(clk, best_parent);
+
+ DEVICE_LOCK(clk);
+ READ4(clk, sc->offset, &val);
+
+ n = aw_clk_factor_get_value(&sc->n, best_n);
+ m = aw_clk_factor_get_value(&sc->m, best_m);
+ val &= ~sc->n.mask;
+ val &= ~sc->m.mask;
+ val |= n << sc->n.shift;
+ val |= m << sc->m.shift;
+
+ WRITE4(clk, sc->offset, val);
+ DEVICE_UNLOCK(clk);
+
+ if ((sc->flags & AW_CLK_HAS_LOCK) != 0) {
+ for (retry = 0; retry < sc->lock_retries; retry++) {
+ READ4(clk, sc->offset, &val);
+ if ((val & (1 << sc->lock_shift)) != 0)
+ break;
+ DELAY(1000);
+ }
+ }
+
+ *fout = best;
+ *stop = 1;
+
+ return (0);
+}
+
+static int
+aw_clk_nm_recalc(struct clknode *clk, uint64_t *freq)
+{
+ struct aw_clk_nm_sc *sc;
+ uint32_t val, m, n, prediv;
+
+ sc = clknode_get_softc(clk);
+
+ DEVICE_LOCK(clk);
+ READ4(clk, sc->offset, &val);
+ DEVICE_UNLOCK(clk);
+
+ m = aw_clk_get_factor(val, &sc->m);
+ n = aw_clk_get_factor(val, &sc->n);
+ if (sc->flags & AW_CLK_HAS_PREDIV)
+ prediv = aw_clk_get_factor(val, &sc->prediv);
+ else
+ prediv = 1;
+
+ *freq = *freq / prediv / n / m;
+
+ return (0);
+}
+
+static clknode_method_t aw_nm_clknode_methods[] = {
+ /* Device interface */
+ CLKNODEMETHOD(clknode_init, aw_clk_nm_init),
+ CLKNODEMETHOD(clknode_set_gate, aw_clk_nm_set_gate),
+ CLKNODEMETHOD(clknode_set_mux, aw_clk_nm_set_mux),
+ CLKNODEMETHOD(clknode_recalc_freq, aw_clk_nm_recalc),
+ CLKNODEMETHOD(clknode_set_freq, aw_clk_nm_set_freq),
+ CLKNODEMETHOD_END
+};
+
+DEFINE_CLASS_1(aw_nm_clknode, aw_nm_clknode_class, aw_nm_clknode_methods,
+ sizeof(struct aw_clk_nm_sc), clknode_class);
+
+int
+aw_clk_nm_register(struct clkdom *clkdom, struct aw_clk_nm_def *clkdef)
+{
+ struct clknode *clk;
+ struct aw_clk_nm_sc *sc;
+
+ clk = clknode_create(clkdom, &aw_nm_clknode_class, &clkdef->clkdef);
+ if (clk == NULL)
+ return (1);
+
+ sc = clknode_get_softc(clk);
+
+ sc->offset = clkdef->offset;
+
+ sc->m.shift = clkdef->m.shift;
+ sc->m.width = clkdef->m.width;
+ sc->m.mask = ((1 << sc->m.width) - 1) << sc->m.shift;
+ sc->m.value = clkdef->m.value;
+ sc->m.flags = clkdef->m.flags;
+
+ sc->n.shift = clkdef->n.shift;
+ sc->n.width = clkdef->n.width;
+ sc->n.mask = ((1 << sc->n.width) - 1) << sc->n.shift;
+ sc->n.value = clkdef->n.value;
+ sc->n.flags = clkdef->n.flags;
+
+ sc->prediv.shift = clkdef->prediv.shift;
+ sc->prediv.width = clkdef->prediv.width;
+ sc->prediv.mask = ((1 << sc->prediv.width) - 1) << sc->prediv.shift;
+ sc->prediv.value = clkdef->prediv.value;
+ sc->prediv.flags = clkdef->prediv.flags;
+ sc->prediv.cond_shift = clkdef->prediv.cond_shift;
+ if (clkdef->prediv.cond_width != 0)
+ sc->prediv.cond_mask = ((1 << clkdef->prediv.cond_width) - 1) << sc->prediv.shift;
+ else
+ sc->prediv.cond_mask = clkdef->prediv.cond_mask;
+ sc->prediv.cond_value = clkdef->prediv.cond_value;
+
+ sc->mux_shift = clkdef->mux_shift;
+ sc->mux_mask = ((1 << clkdef->mux_width) - 1) << sc->mux_shift;
+
+ sc->gate_shift = clkdef->gate_shift;
+
+ sc->lock_shift = clkdef->lock_shift;
+ sc->lock_retries = clkdef->lock_retries;
+
+ sc->flags = clkdef->flags;
+
+ clknode_register(clkdom, clk);
+
+ return (0);
+}
diff --git a/sys/arm/allwinner/clkng/aw_clk_nm.h b/sys/arm/allwinner/clkng/aw_clk_nm.h
new file mode 100644
index 000000000000..f71027b789d0
--- /dev/null
+++ b/sys/arm/allwinner/clkng/aw_clk_nm.h
@@ -0,0 +1,52 @@
+/*-
+ * Copyright (c) 2017 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __AW_CLK_NM_H__
+#define __AW_CLK_NM_H__
+
+#include <dev/extres/clk/clk.h>
+
+struct aw_clk_nm_def {
+ struct clknode_init_def clkdef;
+ uint32_t offset;
+
+ struct aw_clk_factor m;
+ struct aw_clk_factor n;
+ struct aw_clk_factor prediv;
+
+ uint32_t mux_shift;
+ uint32_t mux_width;
+ uint32_t gate_shift;
+ uint32_t lock_shift;
+ uint32_t lock_retries;
+
+ uint32_t flags;
+};
+
+int aw_clk_nm_register(struct clkdom *clkdom, struct aw_clk_nm_def *clkdef);
+
+#endif /* __AW_CLK_NM_H__ */
diff --git a/sys/arm/allwinner/clkng/aw_clk_nmm.c b/sys/arm/allwinner/clkng/aw_clk_nmm.c
new file mode 100644
index 000000000000..ef9eb02f7316
--- /dev/null
+++ b/sys/arm/allwinner/clkng/aw_clk_nmm.c
@@ -0,0 +1,285 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2019 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <dev/extres/clk/clk.h>
+
+#include <arm/allwinner/clkng/aw_clk.h>
+#include <arm/allwinner/clkng/aw_clk_nmm.h>
+
+#include "clkdev_if.h"
+
+/*
+ * clknode for clocks matching the formula :
+ *
+ * clk = clkin * n / m0 / m1
+ *
+ */
+
+struct aw_clk_nmm_sc {
+ uint32_t offset;
+
+ struct aw_clk_factor n;
+ struct aw_clk_factor m0;
+ struct aw_clk_factor m1;
+
+ uint32_t gate_shift;
+ uint32_t lock_shift;
+ uint32_t lock_retries;
+
+ uint32_t flags;
+};
+
+#define WRITE4(_clk, off, val) \
+ CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)
+#define READ4(_clk, off, val) \
+ CLKDEV_READ_4(clknode_get_device(_clk), off, val)
+#define DEVICE_LOCK(_clk) \
+ CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
+#define DEVICE_UNLOCK(_clk) \
+ CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
+
+static int
+aw_clk_nmm_init(struct clknode *clk, device_t dev)
+{
+ struct aw_clk_nmm_sc *sc;
+
+ sc = clknode_get_softc(clk);
+
+ clknode_init_parent_idx(clk, 0);
+ return (0);
+}
+
+static int
+aw_clk_nmm_set_gate(struct clknode *clk, bool enable)
+{
+ struct aw_clk_nmm_sc *sc;
+ uint32_t val;
+
+ sc = clknode_get_softc(clk);
+
+ if ((sc->flags & AW_CLK_HAS_GATE) == 0)
+ return (0);
+
+ DEVICE_LOCK(clk);
+ READ4(clk, sc->offset, &val);
+ if (enable)
+ val |= (1 << sc->gate_shift);
+ else
+ val &= ~(1 << sc->gate_shift);
+ WRITE4(clk, sc->offset, val);
+ DEVICE_UNLOCK(clk);
+
+ return (0);
+}
+
+static uint64_t
+aw_clk_nmm_find_best(struct aw_clk_nmm_sc *sc, uint64_t fparent, uint64_t *fout,
+ uint32_t *factor_n, uint32_t *factor_m0, uint32_t *factor_m1)
+{
+ uint64_t cur, best;
+ uint32_t n, m0, m1;
+ uint32_t max_n, max_m0, max_m1;
+ uint32_t min_n, min_m0, min_m1;
+
+ *factor_n = *factor_m0 = *factor_m1 = 0;
+
+ max_n = aw_clk_factor_get_max(&sc->n);
+ min_n = aw_clk_factor_get_min(&sc->n);
+ max_m0 = aw_clk_factor_get_max(&sc->m0);
+ min_m0 = aw_clk_factor_get_min(&sc->m0);
+ max_m1 = aw_clk_factor_get_max(&sc->m1);
+ min_m1 = aw_clk_factor_get_min(&sc->m1);
+
+ for (m0 = min_m0; m0 <= max_m0; ) {
+ for (m1 = min_m1; m1 <= max_m1; ) {
+ for (n = min_n; n <= max_n; ) {
+ cur = fparent * n / m0 / m1;
+ if (abs(*fout - cur) < abs(*fout - best)) {
+ best = cur;
+ *factor_n = n;
+ *factor_m0 = m0;
+ *factor_m1 = m1;
+ }
+ n++;
+ }
+ m1++;
+ }
+ m0++;
+ }
+
+ return (best);
+}
+
+static int
+aw_clk_nmm_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,
+ int flags, int *stop)
+{
+ struct aw_clk_nmm_sc *sc;
+ uint64_t cur, best;
+ uint32_t val, n, m0, m1, best_n, best_m0, best_m1;
+ int retry;
+
+ sc = clknode_get_softc(clk);
+
+ best = cur = 0;
+
+ best = aw_clk_nmm_find_best(sc, fparent, fout,
+ &best_n, &best_m0, &best_m1);
+
+ if ((flags & CLK_SET_DRYRUN) != 0) {
+ *fout = best;
+ *stop = 1;
+ return (0);
+ }
+
+ if ((best < *fout) &&
+ ((flags & CLK_SET_ROUND_DOWN) == 0)) {
+ *stop = 1;
+ return (ERANGE);
+ }
+ if ((best > *fout) &&
+ ((flags & CLK_SET_ROUND_UP) == 0)) {
+ *stop = 1;
+ return (ERANGE);
+ }
+
+ DEVICE_LOCK(clk);
+ READ4(clk, sc->offset, &val);
+
+ n = aw_clk_factor_get_value(&sc->n, best_n);
+ m0 = aw_clk_factor_get_value(&sc->m0, best_m0);
+ m1 = aw_clk_factor_get_value(&sc->m1, best_m1);
+ val &= ~sc->n.mask;
+ val &= ~sc->m0.mask;
+ val &= ~sc->m1.mask;
+ val |= n << sc->n.shift;
+ val |= m0 << sc->m0.shift;
+ val |= m1 << sc->m1.shift;
+
+ WRITE4(clk, sc->offset, val);
+ DEVICE_UNLOCK(clk);
+
+ if ((sc->flags & AW_CLK_HAS_LOCK) != 0) {
+ for (retry = 0; retry < sc->lock_retries; retry++) {
+ READ4(clk, sc->offset, &val);
+ if ((val & (1 << sc->lock_shift)) != 0)
+ break;
+ DELAY(1000);
+ }
+ }
+
+ *fout = best;
+ *stop = 1;
+
+ return (0);
+}
+
+static int
+aw_clk_nmm_recalc(struct clknode *clk, uint64_t *freq)
+{
+ struct aw_clk_nmm_sc *sc;
+ uint32_t val, n, m0, m1;
+
+ sc = clknode_get_softc(clk);
+
+ DEVICE_LOCK(clk);
+ READ4(clk, sc->offset, &val);
+ DEVICE_UNLOCK(clk);
+
+ n = aw_clk_get_factor(val, &sc->n);
+ m0 = aw_clk_get_factor(val, &sc->m0);
+ m1 = aw_clk_get_factor(val, &sc->m1);
+
+ *freq = *freq * n / m0 / m1;
+
+ return (0);
+}
+
+static clknode_method_t aw_nmm_clknode_methods[] = {
+ /* Device interface */
+ CLKNODEMETHOD(clknode_init, aw_clk_nmm_init),
+ CLKNODEMETHOD(clknode_set_gate, aw_clk_nmm_set_gate),
+ CLKNODEMETHOD(clknode_recalc_freq, aw_clk_nmm_recalc),
+ CLKNODEMETHOD(clknode_set_freq, aw_clk_nmm_set_freq),
+ CLKNODEMETHOD_END
+};
+
+DEFINE_CLASS_1(aw_nmm_clknode, aw_nmm_clknode_class, aw_nmm_clknode_methods,
+ sizeof(struct aw_clk_nmm_sc), clknode_class);
+
+int
+aw_clk_nmm_register(struct clkdom *clkdom, struct aw_clk_nmm_def *clkdef)
+{
+ struct clknode *clk;
+ struct aw_clk_nmm_sc *sc;
+
+ clk = clknode_create(clkdom, &aw_nmm_clknode_class, &clkdef->clkdef);
+ if (clk == NULL)
+ return (1);
+
+ sc = clknode_get_softc(clk);
+
+ sc->offset = clkdef->offset;
+
+ sc->n.shift = clkdef->n.shift;
+ sc->n.width = clkdef->n.width;
+ sc->n.mask = ((1 << sc->n.width) - 1) << sc->n.shift;
+ sc->n.value = clkdef->n.value;
+ sc->n.flags = clkdef->n.flags;
+
+ sc->m0.shift = clkdef->m0.shift;
+ sc->m0.width = clkdef->m0.width;
+ sc->m0.mask = ((1 << sc->m0.width) - 1) << sc->m0.shift;
+ sc->m0.value = clkdef->m0.value;
+ sc->m0.flags = clkdef->m0.flags;
+
+ sc->m1.shift = clkdef->m1.shift;
+ sc->m1.width = clkdef->m1.width;
+ sc->m1.mask = ((1 << sc->m1.width) - 1) << sc->m1.shift;
+ sc->m1.value = clkdef->m1.value;
+ sc->m1.flags = clkdef->m1.flags;
+
+ sc->gate_shift = clkdef->gate_shift;
+
+ sc->lock_shift = clkdef->lock_shift;
+ sc->lock_retries = clkdef->lock_retries;
+
+ sc->flags = clkdef->flags;
+
+ clknode_register(clkdom, clk);
+
+ return (0);
+}
diff --git a/sys/arm/allwinner/clkng/aw_clk_nmm.h b/sys/arm/allwinner/clkng/aw_clk_nmm.h
new file mode 100644
index 000000000000..e53d44d3b612
--- /dev/null
+++ b/sys/arm/allwinner/clkng/aw_clk_nmm.h
@@ -0,0 +1,52 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2019 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __AW_CLK_NMM_H__
+#define __AW_CLK_NMM_H__
+
+#include <dev/extres/clk/clk.h>
+
+struct aw_clk_nmm_def {
+ struct clknode_init_def clkdef;
+ uint32_t offset;
+
+ struct aw_clk_factor n;
+ struct aw_clk_factor m0;
+ struct aw_clk_factor m1;
+
+ uint32_t gate_shift;
+ uint32_t lock_shift;
+ uint32_t lock_retries;
+
+ uint32_t flags;
+};
+
+int aw_clk_nmm_register(struct clkdom *clkdom, struct aw_clk_nmm_def *clkdef);
+
+#endif /* __AW_CLK_NMM_H__ */
diff --git a/sys/arm/allwinner/clkng/aw_clk_np.c b/sys/arm/allwinner/clkng/aw_clk_np.c
new file mode 100644
index 000000000000..6403b52a9abe
--- /dev/null
+++ b/sys/arm/allwinner/clkng/aw_clk_np.c
@@ -0,0 +1,267 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2019 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <dev/extres/clk/clk.h>
+
+#include <arm/allwinner/clkng/aw_clk.h>
+#include <arm/allwinner/clkng/aw_clk_np.h>
+
+#include "clkdev_if.h"
+
+/*
+ * clknode for clocks matching the formula :
+ *
+ * clk = clkin * n / p
+ *
+ */
+
+struct aw_clk_np_sc {
+ uint32_t offset;
+
+ struct aw_clk_factor n;
+ struct aw_clk_factor p;
+
+ uint32_t gate_shift;
+ uint32_t lock_shift;
+ uint32_t lock_retries;
+
+ uint32_t flags;
+};
+
+#define WRITE4(_clk, off, val) \
+ CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)
+#define READ4(_clk, off, val) \
+ CLKDEV_READ_4(clknode_get_device(_clk), off, val)
+#define DEVICE_LOCK(_clk) \
+ CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
+#define DEVICE_UNLOCK(_clk) \
+ CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
+
+static int
+aw_clk_np_init(struct clknode *clk, device_t dev)
+{
+ struct aw_clk_np_sc *sc;
+
+ sc = clknode_get_softc(clk);
+
+ clknode_init_parent_idx(clk, 0);
+ return (0);
+}
+
+static int
+aw_clk_np_set_gate(struct clknode *clk, bool enable)
+{
+ struct aw_clk_np_sc *sc;
+ uint32_t val;
+
+ sc = clknode_get_softc(clk);
+
+ if ((sc->flags & AW_CLK_HAS_GATE) == 0)
+ return (0);
+
+ DEVICE_LOCK(clk);
+ READ4(clk, sc->offset, &val);
+ if (enable)
+ val |= (1 << sc->gate_shift);
+ else
+ val &= ~(1 << sc->gate_shift);
+ WRITE4(clk, sc->offset, val);
+ DEVICE_UNLOCK(clk);
+
+ return (0);
+}
+
+static uint64_t
+aw_clk_np_find_best(struct aw_clk_np_sc *sc, uint64_t fparent, uint64_t *fout,
+ uint32_t *factor_n, uint32_t *factor_p)
+{
+ uint64_t cur, best;
+ uint32_t n, p, max_n, max_p, min_n, min_p;
+
+ *factor_n = *factor_p = 0;
+
+ max_n = aw_clk_factor_get_max(&sc->n);
+ max_p = aw_clk_factor_get_max(&sc->p);
+ min_n = aw_clk_factor_get_min(&sc->n);
+ min_p = aw_clk_factor_get_min(&sc->p);
+
+ for (p = min_p; p <= max_p; ) {
+ for (n = min_n; n <= max_n; ) {
+ cur = fparent * n / p;
+ if (abs(*fout - cur) < abs(*fout - best)) {
+ best = cur;
+ *factor_n = n;
+ *factor_p = p;
+ }
+
+ n++;
+ }
+ p++;
+ }
+
+ return (best);
+}
+
+static int
+aw_clk_np_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,
+ int flags, int *stop)
+{
+ struct aw_clk_np_sc *sc;
+ uint64_t cur, best;
+ uint32_t val, n, p, best_n, best_p;
+ int retry;
+
+ sc = clknode_get_softc(clk);
+
+ best = cur = 0;
+
+ best = aw_clk_np_find_best(sc, fparent, fout,
+ &best_n, &best_p);
+
+ if ((flags & CLK_SET_DRYRUN) != 0) {
+ *fout = best;
+ *stop = 1;
+ return (0);
+ }
+
+ if ((best < *fout) &&
+ ((flags & CLK_SET_ROUND_DOWN) == 0)) {
+ *stop = 1;
+ return (ERANGE);
+ }
+ if ((best > *fout) &&
+ ((flags & CLK_SET_ROUND_UP) == 0)) {
+ *stop = 1;
+ return (ERANGE);
+ }
+
+ DEVICE_LOCK(clk);
+ READ4(clk, sc->offset, &val);
+
+ n = aw_clk_factor_get_value(&sc->n, best_n);
+ p = aw_clk_factor_get_value(&sc->p, best_p);
+ val &= ~sc->n.mask;
+ val &= ~sc->p.mask;
+ val |= n << sc->n.shift;
+ val |= p << sc->p.shift;
+
+ WRITE4(clk, sc->offset, val);
+ DEVICE_UNLOCK(clk);
+
+ if ((sc->flags & AW_CLK_HAS_LOCK) != 0) {
+ for (retry = 0; retry < sc->lock_retries; retry++) {
+ READ4(clk, sc->offset, &val);
+ if ((val & (1 << sc->lock_shift)) != 0)
+ break;
+ DELAY(1000);
+ }
+ }
+
+ *fout = best;
+ *stop = 1;
+
+ return (0);
+}
+
+static int
+aw_clk_np_recalc(struct clknode *clk, uint64_t *freq)
+{
+ struct aw_clk_np_sc *sc;
+ uint32_t val, n, p;
+
+ sc = clknode_get_softc(clk);
+
+ DEVICE_LOCK(clk);
+ READ4(clk, sc->offset, &val);
+ DEVICE_UNLOCK(clk);
+
+ n = aw_clk_get_factor(val, &sc->n);
+ p = aw_clk_get_factor(val, &sc->p);
+
+ *freq = *freq * n / p;
+
+ return (0);
+}
+
+static clknode_method_t aw_np_clknode_methods[] = {
+ /* Device interface */
+ CLKNODEMETHOD(clknode_init, aw_clk_np_init),
+ CLKNODEMETHOD(clknode_set_gate, aw_clk_np_set_gate),
+ CLKNODEMETHOD(clknode_recalc_freq, aw_clk_np_recalc),
+ CLKNODEMETHOD(clknode_set_freq, aw_clk_np_set_freq),
+ CLKNODEMETHOD_END
+};
+
+DEFINE_CLASS_1(aw_np_clknode, aw_np_clknode_class, aw_np_clknode_methods,
+ sizeof(struct aw_clk_np_sc), clknode_class);
+
+int
+aw_clk_np_register(struct clkdom *clkdom, struct aw_clk_np_def *clkdef)
+{
+ struct clknode *clk;
+ struct aw_clk_np_sc *sc;
+
+ clk = clknode_create(clkdom, &aw_np_clknode_class, &clkdef->clkdef);
+ if (clk == NULL)
+ return (1);
+
+ sc = clknode_get_softc(clk);
+
+ sc->offset = clkdef->offset;
+
+ sc->n.shift = clkdef->n.shift;
+ sc->n.width = clkdef->n.width;
+ sc->n.mask = ((1 << sc->n.width) - 1) << sc->n.shift;
+ sc->n.value = clkdef->n.value;
+ sc->n.flags = clkdef->n.flags;
+
+ sc->p.shift = clkdef->p.shift;
+ sc->p.width = clkdef->p.width;
+ sc->p.mask = ((1 << sc->p.width) - 1) << sc->p.shift;
+ sc->p.value = clkdef->p.value;
+ sc->p.flags = clkdef->p.flags;
+
+ sc->gate_shift = clkdef->gate_shift;
+
+ sc->lock_shift = clkdef->lock_shift;
+ sc->lock_retries = clkdef->lock_retries;
+
+ sc->flags = clkdef->flags;
+
+ clknode_register(clkdom, clk);
+
+ return (0);
+}
diff --git a/sys/arm/allwinner/clkng/aw_clk_np.h b/sys/arm/allwinner/clkng/aw_clk_np.h
new file mode 100644
index 000000000000..4997adb19d9c
--- /dev/null
+++ b/sys/arm/allwinner/clkng/aw_clk_np.h
@@ -0,0 +1,51 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2019 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __AW_CLK_NP_H__
+#define __AW_CLK_NP_H__
+
+#include <dev/extres/clk/clk.h>
+
+struct aw_clk_np_def {
+ struct clknode_init_def clkdef;
+ uint32_t offset;
+
+ struct aw_clk_factor n;
+ struct aw_clk_factor p;
+
+ uint32_t gate_shift;
+ uint32_t lock_shift;
+ uint32_t lock_retries;
+
+ uint32_t flags;
+};
+
+int aw_clk_np_register(struct clkdom *clkdom, struct aw_clk_np_def *clkdef);
+
+#endif /* __AW_CLK_NP_H__ */
diff --git a/sys/arm/allwinner/clkng/aw_clk_prediv_mux.c b/sys/arm/allwinner/clkng/aw_clk_prediv_mux.c
new file mode 100644
index 000000000000..b27473b03e84
--- /dev/null
+++ b/sys/arm/allwinner/clkng/aw_clk_prediv_mux.c
@@ -0,0 +1,183 @@
+/*-
+ * Copyright (c) 2017 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <dev/extres/clk/clk.h>
+
+#include <arm/allwinner/clkng/aw_clk.h>
+#include <arm/allwinner/clkng/aw_clk_prediv_mux.h>
+
+#include "clkdev_if.h"
+
+/*
+ * clknode for clocks matching the formula :
+ *
+ * clk = clkin / prediv / div
+ *
+ * and where prediv is conditional
+ *
+ */
+
+struct aw_clk_prediv_mux_sc {
+ uint32_t offset;
+
+ uint32_t mux_shift;
+ uint32_t mux_mask;
+
+ struct aw_clk_factor div;
+ struct aw_clk_factor prediv;
+
+ uint32_t flags;
+};
+
+#define WRITE4(_clk, off, val) \
+ CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)
+#define READ4(_clk, off, val) \
+ CLKDEV_READ_4(clknode_get_device(_clk), off, val)
+#define MODIFY4(_clk, off, clr, set ) \
+ CLKDEV_MODIFY_4(clknode_get_device(_clk), off, clr, set)
+#define DEVICE_LOCK(_clk) \
+ CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
+#define DEVICE_UNLOCK(_clk) \
+ CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
+
+static int
+aw_clk_prediv_mux_init(struct clknode *clk, device_t dev)
+{
+ struct aw_clk_prediv_mux_sc *sc;
+ uint32_t val;
+
+ sc = clknode_get_softc(clk);
+
+ DEVICE_LOCK(clk);
+ READ4(clk, sc->offset, &val);
+ DEVICE_UNLOCK(clk);
+
+ /* Init the current parent */
+ val = (val & sc->mux_mask) >> sc->mux_shift;
+ clknode_init_parent_idx(clk, val);
+
+ return (0);
+}
+
+static int
+aw_clk_prediv_mux_set_mux(struct clknode *clk, int index)
+{
+ struct aw_clk_prediv_mux_sc *sc;
+ uint32_t val;
+
+ sc = clknode_get_softc(clk);
+
+ DEVICE_LOCK(clk);
+ READ4(clk, sc->offset, &val);
+ val &= ~sc->mux_mask;
+ val |= index << sc->mux_shift;
+ WRITE4(clk, sc->offset, val);
+ DEVICE_UNLOCK(clk);
+
+ return (0);
+}
+
+static int
+aw_clk_prediv_mux_recalc(struct clknode *clk, uint64_t *freq)
+{
+ struct aw_clk_prediv_mux_sc *sc;
+ uint32_t val, div, prediv;
+
+ sc = clknode_get_softc(clk);
+
+ DEVICE_LOCK(clk);
+ READ4(clk, sc->offset, &val);
+ DEVICE_UNLOCK(clk);
+
+ div = aw_clk_get_factor(val, &sc->div);
+ prediv = aw_clk_get_factor(val, &sc->prediv);
+
+ *freq = *freq / prediv / div;
+ return (0);
+}
+
+static clknode_method_t aw_prediv_mux_clknode_methods[] = {
+ /* Device interface */
+ CLKNODEMETHOD(clknode_init, aw_clk_prediv_mux_init),
+ CLKNODEMETHOD(clknode_set_mux, aw_clk_prediv_mux_set_mux),
+ CLKNODEMETHOD(clknode_recalc_freq, aw_clk_prediv_mux_recalc),
+ CLKNODEMETHOD_END
+};
+
+DEFINE_CLASS_1(aw_prediv_mux_clknode, aw_prediv_mux_clknode_class,
+ aw_prediv_mux_clknode_methods, sizeof(struct aw_clk_prediv_mux_sc),
+ clknode_class);
+
+int
+aw_clk_prediv_mux_register(struct clkdom *clkdom, struct aw_clk_prediv_mux_def *clkdef)
+{
+ struct clknode *clk;
+ struct aw_clk_prediv_mux_sc *sc;
+
+ clk = clknode_create(clkdom, &aw_prediv_mux_clknode_class, &clkdef->clkdef);
+ if (clk == NULL)
+ return (1);
+
+ sc = clknode_get_softc(clk);
+
+ sc->offset = clkdef->offset;
+
+ sc->mux_shift = clkdef->mux_shift;
+ sc->mux_mask = ((1 << clkdef->mux_width) - 1) << sc->mux_shift;
+
+ sc->div.shift = clkdef->div.shift;
+ sc->div.mask = ((1 << clkdef->div.width) - 1) << sc->div.shift;
+ sc->div.value = clkdef->div.value;
+ sc->div.cond_shift = clkdef->div.cond_shift;
+ sc->div.cond_mask = ((1 << clkdef->div.cond_width) - 1) << sc->div.shift;
+ sc->div.cond_value = clkdef->div.cond_value;
+ sc->div.flags = clkdef->div.flags;
+
+ sc->prediv.shift = clkdef->prediv.shift;
+ sc->prediv.mask = ((1 << clkdef->prediv.width) - 1) << sc->prediv.shift;
+ sc->prediv.value = clkdef->prediv.value;
+ sc->prediv.cond_shift = clkdef->prediv.cond_shift;
+ if (clkdef->prediv.cond_width != 0)
+ sc->prediv.cond_mask = ((1 << clkdef->prediv.cond_width) - 1) << sc->prediv.shift;
+ else
+ sc->prediv.cond_mask = clkdef->prediv.cond_mask;
+ sc->prediv.cond_value = clkdef->prediv.cond_value;
+ sc->prediv.flags = clkdef->prediv.flags;
+
+ sc->flags = clkdef->flags;
+
+ clknode_register(clkdom, clk);
+
+ return (0);
+}
diff --git a/sys/arm/allwinner/clkng/aw_clk_prediv_mux.h b/sys/arm/allwinner/clkng/aw_clk_prediv_mux.h
new file mode 100644
index 000000000000..1a6109b22d60
--- /dev/null
+++ b/sys/arm/allwinner/clkng/aw_clk_prediv_mux.h
@@ -0,0 +1,48 @@
+/*-
+ * Copyright (c) 2017 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __AW_CLK_PREDIV_MUX_H__
+#define __AW_CLK_PREDIV_MUX_H__
+
+#include <arm/allwinner/clkng/aw_clk.h>
+
+struct aw_clk_prediv_mux_def {
+ struct clknode_init_def clkdef;
+ uint32_t offset;
+
+ uint32_t mux_shift;
+ uint32_t mux_width;
+
+ struct aw_clk_factor div;
+ struct aw_clk_factor prediv;
+
+ uint32_t flags;
+};
+
+int aw_clk_prediv_mux_register(struct clkdom *clkdom, struct aw_clk_prediv_mux_def *clkdef);
+
+#endif /* __AW_CLK_PREDIV_MUX_H__ */
diff --git a/sys/arm/allwinner/clkng/ccu_a10.c b/sys/arm/allwinner/clkng/ccu_a10.c
new file mode 100644
index 000000000000..182a19f35e98
--- /dev/null
+++ b/sys/arm/allwinner/clkng/ccu_a10.c
@@ -0,0 +1,625 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <machine/bus.h>
+
+#include <dev/fdt/simplebus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/extres/clk/clk_div.h>
+#include <dev/extres/clk/clk_fixed.h>
+#include <dev/extres/clk/clk_mux.h>
+
+#include <arm/allwinner/clkng/aw_ccung.h>
+
+#include <dt-bindings/clock/sun4i-a10-ccu.h>
+#include <dt-bindings/clock/sun7i-a20-ccu.h>
+#include <dt-bindings/reset/sun4i-a10-ccu.h>
+
+/* Non-exported resets */
+/* Non-exported clocks */
+#define CLK_PLL_CORE 2
+#define CLK_AXI 3
+#define CLK_AHB 4
+#define CLK_APB0 5
+#define CLK_APB1 6
+#define CLK_PLL_VIDEO0 8
+#define CLK_PLL_DDR 12
+#define CLK_PLL_DDR_OTHER 13
+#define CLK_PLL6 14
+#define CLK_PLL_PERIPH 15
+#define CLK_PLL_SATA 16
+#define CLK_PLL_VIDEO1 17
+
+/* Non-exported fixed clocks */
+
+static struct aw_ccung_reset a10_ccu_resets[] = {
+ CCU_RESET(RST_USB_PHY0, 0xcc, 0)
+ CCU_RESET(RST_USB_PHY1, 0xcc, 1)
+ CCU_RESET(RST_USB_PHY2, 0xcc, 2)
+
+ CCU_RESET(RST_GPS, 0xd0, 0)
+
+ CCU_RESET(RST_DE_BE0, 0x104, 30)
+ CCU_RESET(RST_DE_BE1, 0x108, 30)
+ CCU_RESET(RST_DE_FE0, 0x10c, 30)
+ CCU_RESET(RST_DE_FE1, 0x110, 30)
+ CCU_RESET(RST_DE_MP, 0x114, 30)
+
+ CCU_RESET(RST_TVE0, 0x118, 29)
+ CCU_RESET(RST_TCON0, 0x118, 30)
+
+ CCU_RESET(RST_TVE1, 0x11c, 29)
+ CCU_RESET(RST_TCON1, 0x11c, 30)
+
+ CCU_RESET(RST_CSI0, 0x134, 30)
+ CCU_RESET(RST_CSI1, 0x138, 30)
+
+ CCU_RESET(RST_VE, 0x13c, 0)
+
+ CCU_RESET(RST_ACE, 0x148, 16)
+
+ CCU_RESET(RST_LVDS, 0x14c, 0)
+
+ CCU_RESET(RST_GPU, 0x154, 30)
+
+ CCU_RESET(RST_HDMI_H, 0x170, 0)
+ CCU_RESET(RST_HDMI_SYS, 0x170, 1)
+ CCU_RESET(RST_HDMI_AUDIO_DMA, 0x170, 2)
+};
+
+static struct aw_ccung_gate a10_ccu_gates[] = {
+ CCU_GATE(CLK_HOSC, "hosc", "osc24M", 0x50, 0)
+
+ CCU_GATE(CLK_AHB_OTG, "ahb-otg", "ahb", 0x60, 0)
+ CCU_GATE(CLK_AHB_EHCI0, "ahb-ehci0", "ahb", 0x60, 1)
+ CCU_GATE(CLK_AHB_OHCI0, "ahb-ohci0", "ahb", 0x60, 2)
+ CCU_GATE(CLK_AHB_EHCI1, "ahb-ehci1", "ahb", 0x60, 3)
+ CCU_GATE(CLK_AHB_OHCI1, "ahb-ohci1", "ahb", 0x60, 4)
+ CCU_GATE(CLK_AHB_SS, "ahb-ss", "ahb", 0x60, 5)
+ CCU_GATE(CLK_AHB_DMA, "ahb-dma", "ahb", 0x60, 6)
+ CCU_GATE(CLK_AHB_BIST, "ahb-bist", "ahb", 0x60, 7)
+ CCU_GATE(CLK_AHB_MMC0, "ahb-mmc0", "ahb", 0x60, 8)
+ CCU_GATE(CLK_AHB_MMC1, "ahb-mmc1", "ahb", 0x60, 9)
+ CCU_GATE(CLK_AHB_MMC2, "ahb-mmc2", "ahb", 0x60, 10)
+ CCU_GATE(CLK_AHB_MMC3, "ahb-mmc3", "ahb", 0x60, 11)
+ CCU_GATE(CLK_AHB_MS, "ahb-ms", "ahb", 0x60, 12)
+ CCU_GATE(CLK_AHB_NAND, "ahb-nand", "ahb", 0x60, 13)
+ CCU_GATE(CLK_AHB_SDRAM, "ahb-sdram", "ahb", 0x60, 14)
+ CCU_GATE(CLK_AHB_ACE, "ahb-ace", "ahb", 0x60, 16)
+ CCU_GATE(CLK_AHB_EMAC, "ahb-emac", "ahb", 0x60, 17)
+ CCU_GATE(CLK_AHB_TS, "ahb-ts", "ahb", 0x60, 18)
+ CCU_GATE(CLK_AHB_SPI0, "ahb-spi0", "ahb", 0x60, 20)
+ CCU_GATE(CLK_AHB_SPI1, "ahb-spi1", "ahb", 0x60, 21)
+ CCU_GATE(CLK_AHB_SPI2, "ahb-spi2", "ahb", 0x60, 22)
+ CCU_GATE(CLK_AHB_SPI3, "ahb-spi3", "ahb", 0x60, 23)
+ CCU_GATE(CLK_AHB_SATA, "ahb-sata", "ahb", 0x60, 25)
+
+ CCU_GATE(CLK_AHB_VE, "ahb-ve", "ahb", 0x64, 0)
+ CCU_GATE(CLK_AHB_TVD, "ahb-tvd", "ahb", 0x64, 1)
+ CCU_GATE(CLK_AHB_TVE0, "ahb-tve0", "ahb", 0x64, 2)
+ CCU_GATE(CLK_AHB_TVE1, "ahb-tve1", "ahb", 0x64, 3)
+ CCU_GATE(CLK_AHB_LCD0, "ahb-lcd0", "ahb", 0x64, 4)
+ CCU_GATE(CLK_AHB_LCD1, "ahb-lcd1", "ahb", 0x64, 5)
+ CCU_GATE(CLK_AHB_CSI0, "ahb-csi0", "ahb", 0x64, 8)
+ CCU_GATE(CLK_AHB_CSI1, "ahb-csi1", "ahb", 0x64, 9)
+ CCU_GATE(CLK_AHB_HDMI1, "ahb-hdmi1", "ahb", 0x64, 10)
+ CCU_GATE(CLK_AHB_HDMI0, "ahb-hdmi0", "ahb", 0x64, 11)
+ CCU_GATE(CLK_AHB_DE_BE0, "ahb-de_be0", "ahb", 0x64, 12)
+ CCU_GATE(CLK_AHB_DE_BE1, "ahb-de_be1", "ahb", 0x64, 13)
+ CCU_GATE(CLK_AHB_DE_FE0, "ahb-de_fe0", "ahb", 0x64, 14)
+ CCU_GATE(CLK_AHB_DE_FE1, "ahb-de_fe1", "ahb", 0x64, 15)
+ CCU_GATE(CLK_AHB_GMAC, "ahb-gmac", "ahb", 0x64, 17)
+ CCU_GATE(CLK_AHB_MP, "ahb-mp", "ahb", 0x64, 18)
+ CCU_GATE(CLK_AHB_GPU, "ahb-gpu", "ahb", 0x64, 20)
+
+ CCU_GATE(CLK_APB0_CODEC, "apb0-codec", "apb0", 0x68, 0)
+ CCU_GATE(CLK_APB0_SPDIF, "apb0-spdif", "apb0", 0x68, 1)
+ CCU_GATE(CLK_APB0_AC97, "apb0-ac97", "apb0", 0x68, 2)
+ CCU_GATE(CLK_APB0_I2S0, "apb0-i2s0", "apb0", 0x68, 3)
+ CCU_GATE(CLK_APB0_I2S1, "apb0-i2s1", "apb0", 0x68, 4)
+ CCU_GATE(CLK_APB0_PIO, "apb0-pi0", "apb0", 0x68, 5)
+ CCU_GATE(CLK_APB0_IR0, "apb0-ir0", "apb0", 0x68, 6)
+ CCU_GATE(CLK_APB0_IR1, "apb0-ir1", "apb0", 0x68, 7)
+ CCU_GATE(CLK_APB0_I2S2, "apb0-i2s2", "apb0",0x68, 8)
+ CCU_GATE(CLK_APB0_KEYPAD, "apb0-keypad", "apb0", 0x68, 10)
+
+ CCU_GATE(CLK_APB1_I2C0, "apb1-i2c0", "apb1", 0x6c, 0)
+ CCU_GATE(CLK_APB1_I2C1, "apb1-i2c1", "apb1",0x6c, 1)
+ CCU_GATE(CLK_APB1_I2C2, "apb1-i2c2", "apb1",0x6c, 2)
+ CCU_GATE(CLK_APB1_I2C3, "apb1-i2c3", "apb1",0x6c, 3)
+ CCU_GATE(CLK_APB1_CAN, "apb1-can", "apb1",0x6c, 4)
+ CCU_GATE(CLK_APB1_SCR, "apb1-scr", "apb1",0x6c, 5)
+ CCU_GATE(CLK_APB1_PS20, "apb1-ps20", "apb1",0x6c, 6)
+ CCU_GATE(CLK_APB1_PS21, "apb1-ps21", "apb1",0x6c, 7)
+ CCU_GATE(CLK_APB1_I2C4, "apb1-i2c4", "apb1", 0x6c, 15)
+ CCU_GATE(CLK_APB1_UART0, "apb1-uart0", "apb1",0x6c, 16)
+ CCU_GATE(CLK_APB1_UART1, "apb1-uart1", "apb1",0x6c, 17)
+ CCU_GATE(CLK_APB1_UART2, "apb1-uart2", "apb1",0x6c, 18)
+ CCU_GATE(CLK_APB1_UART3, "apb1-uart3", "apb1",0x6c, 19)
+ CCU_GATE(CLK_APB1_UART4, "apb1-uart4", "apb1",0x6c, 20)
+ CCU_GATE(CLK_APB1_UART5, "apb1-uart5", "apb1",0x6c, 21)
+ CCU_GATE(CLK_APB1_UART6, "apb1-uart6", "apb1",0x6c, 22)
+ CCU_GATE(CLK_APB1_UART7, "apb1-uart7", "apb1",0x6c, 23)
+
+ CCU_GATE(CLK_USB_OHCI0, "usb-ohci0", "ahb", 0xcc, 6)
+ CCU_GATE(CLK_USB_OHCI1, "usb-ohci1", "ahb", 0xcc, 7)
+ CCU_GATE(CLK_USB_PHY, "usb-phy", "ahb", 0xcc, 8)
+
+ CCU_GATE(CLK_DRAM_VE, "dram-ve", "pll_ddr", 0x100, 0)
+ CCU_GATE(CLK_DRAM_CSI0, "dram-csi0", "pll_ddr", 0x100, 1)
+ CCU_GATE(CLK_DRAM_CSI1, "dram-csi1", "pll_ddr", 0x100, 2)
+ CCU_GATE(CLK_DRAM_TS, "dram-ts", "pll_ddr", 0x100, 3)
+ CCU_GATE(CLK_DRAM_TVD, "dram-tvd", "pll_ddr", 0x100, 4)
+ CCU_GATE(CLK_DRAM_TVE0, "dram-tve0", "pll_ddr", 0x100, 5)
+ CCU_GATE(CLK_DRAM_TVE1, "dram-tve1", "pll_ddr", 0x100, 6)
+ CCU_GATE(CLK_DRAM_OUT, "dram-out", "pll_ddr", 0x100, 15)
+ CCU_GATE(CLK_DRAM_DE_FE1, "dram-de_fe1", "pll_ddr", 0x100, 24)
+ CCU_GATE(CLK_DRAM_DE_FE0, "dram-de_fe0", "pll_ddr", 0x100, 25)
+ CCU_GATE(CLK_DRAM_DE_BE0, "dram-de_be0", "pll_ddr", 0x100, 26)
+ CCU_GATE(CLK_DRAM_DE_BE1, "dram-de_be1", "pll_ddr", 0x100, 27)
+ CCU_GATE(CLK_DRAM_MP, "dram-de_mp", "pll_ddr", 0x100, 28)
+ CCU_GATE(CLK_DRAM_ACE, "dram-ace", "pll_ddr", 0x100, 29)
+};
+
+static const char *pll_parents[] = {"osc24M"};
+NKMP_CLK(pll_core_clk,
+ CLK_PLL_CORE, /* id */
+ "pll_core", pll_parents, /* name, parents */
+ 0x00, /* offset */
+ 8, 5, 0, AW_CLK_FACTOR_ZERO_IS_ONE, /* n factor */
+ 4, 2, 0, 0, /* k factor */
+ 0, 2, 0, 0, /* m factor */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* p factor */
+ 31, /* gate */
+ 0, 0, /* lock */
+ AW_CLK_HAS_GATE); /* flags */
+
+FRAC_CLK(pll_video0_clk,
+ CLK_PLL_VIDEO0, /* id */
+ "pll_video0", pll_parents, /* name, parents */
+ 0x10, /* offset */
+ 0, 7, 0, 0, /* n factor */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* m factor (fake) */
+ 31, 0, 0, /* gate, lock, lock retries */
+ AW_CLK_HAS_GATE, /* flags */
+ 270000000, 297000000, /* freq0, freq1 */
+ 15, 14, /* mode sel, freq sel */
+ 27000000, 381000000); /* min freq, max freq */
+static const char *pll_video0_2x_parents[] = {"pll_video0"};
+FIXED_CLK(pll_video0_2x_clk,
+ CLK_PLL_VIDEO0_2X, /* id */
+ "pll_video0-2x", pll_video0_2x_parents, /* name, parents */
+ 0, /* freq */
+ 2, /* mult */
+ 1, /* div */
+ 0); /* flags */
+
+FRAC_CLK(pll_video1_clk,
+ CLK_PLL_VIDEO1, /* id */
+ "pll_video1", pll_parents, /* name, parents */
+ 0x30, /* offset */
+ 0, 7, 0, 0, /* n factor */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* m factor (fake) */
+ 31, 0, 0, /* gate, lock, lock retries */
+ AW_CLK_HAS_GATE, /* flags */
+ 270000000, 297000000, /* freq0, freq1 */
+ 15, 14, /* mode sel, freq sel */
+ 27000000, 381000000); /* min freq, max freq */
+static const char *pll_video1_2x_parents[] = {"pll_video1"};
+FIXED_CLK(pll_video1_2x_clk,
+ CLK_PLL_VIDEO1_2X, /* id */
+ "pll_video1-2x", pll_video1_2x_parents, /* name, parents */
+ 0, /* freq */
+ 2, /* mult */
+ 1, /* div */
+ 0); /* flags */
+
+static const char *cpu_parents[] = {"osc32k", "osc24M", "pll_core", "pll_periph"};
+static const char *axi_parents[] = {"cpu"};
+static const char *ahb_parents[] = {"axi", "pll_periph", "pll6"};
+static const char *apb0_parents[] = {"ahb"};
+static const char *apb1_parents[] = {"osc24M", "pll_periph", "osc32k"};
+MUX_CLK(cpu_clk,
+ CLK_CPU, /* id */
+ "cpu", cpu_parents, /* name, parents */
+ 0x54, 16, 2); /* offset, shift, width */
+NM_CLK(axi_clk,
+ CLK_AXI, /* id */
+ "axi", axi_parents, /* name, parents */
+ 0x54, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 2, 0, 0, /* m factor */
+ 0, 0, /* mux */
+ 0, /* gate */
+ 0); /* flags */
+NM_CLK(ahb_clk,
+ CLK_AHB, /* id */
+ "ahb", ahb_parents, /* name, parents */
+ 0x54, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 4, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* m factor */
+ 6, 2, /* mux */
+ 0, /* gate */
+ AW_CLK_HAS_MUX); /* flags */
+NM_CLK(apb0_clk,
+ CLK_APB0, /* id */
+ "apb0", apb0_parents, /* name, parents */
+ 0x54, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 8, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO |
+ AW_CLK_FACTOR_ZERO_IS_ONE, /* m factor */
+ 0, 0, /* mux */
+ 0, /* gate */
+ 0); /* flags */
+
+NM_CLK(apb1_clk,
+ CLK_APB1, /* id */
+ "apb1", apb1_parents, /* name, parents */
+ 0x58, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 5, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 0, /* gate */
+ AW_CLK_HAS_MUX); /* flags */
+
+NKMP_CLK(pll_ddr_other_clk,
+ CLK_PLL_DDR_OTHER, /* id */
+ "pll_ddr_other", pll_parents, /* name, parents */
+ 0x20, /* offset */
+ 8, 5, 0, AW_CLK_FACTOR_ZERO_BASED, /* n factor */
+ 4, 2, 0, 0, /* k factor */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* m factor (fake) */
+ 2, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* p factor */
+ 31, /* gate */
+ 0, 0, /* lock */
+ AW_CLK_HAS_GATE); /* flags */
+NKMP_CLK(pll_ddr_clk,
+ CLK_PLL_DDR, /* id */
+ "pll_ddr", pll_parents, /* name, parents */
+ 0x20, /* offset */
+ 8, 5, 0, AW_CLK_FACTOR_ZERO_BASED, /* n factor */
+ 4, 2, 0, 0, /* k factor */
+ 0, 2, 0, 0, /* m factor */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* p factor (fake) */
+ 31, /* gate */
+ 0, 0, /* lock */
+ AW_CLK_HAS_GATE); /* flags */
+
+NKMP_CLK(pll6_clk,
+ CLK_PLL6, /* id */
+ "pll6", pll_parents, /* name, parents */
+ 0x28, /* offset */
+ 8, 5, 0, AW_CLK_FACTOR_ZERO_BASED, /* n factor */
+ 4, 2, 0, 0, /* k factor */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* m factor (fake) */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* p factor (fake) */
+ 31, /* gate */
+ 0, 0, /* lock */
+ AW_CLK_HAS_GATE); /* flags */
+
+static const char *pll6_parents[] = {"pll6"};
+FIXED_CLK(pll_periph_clk,
+ CLK_PLL_PERIPH, /* id */
+ "pll_periph", pll6_parents, /* name, parents */
+ 0, /* freq */
+ 1, /* mult */
+ 2, /* div */
+ 0); /* flags */
+NKMP_CLK(pll_periph_sata_clk,
+ CLK_PLL_SATA, /* id */
+ "pll_periph_sata", pll6_parents, /* name, parents */
+ 0x28, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* k factor (fake) */
+ 0, 2, 0, 0, /* m factor */
+ 0, 0, 6, AW_CLK_FACTOR_FIXED, /* p factor (fake, 6) */
+ 14, /* gate */
+ 0, 0, /* lock */
+ AW_CLK_HAS_GATE); /* flags */
+
+static const char *mod_parents[] = {"osc24M", "pll_periph", "pll_ddr_other"};
+NM_CLK(nand_clk,
+ CLK_NAND, /* id */
+ "nand", mod_parents, /* name, parents */
+ 0x80, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+NM_CLK(ms_clk,
+ CLK_MS, /* id */
+ "ms", mod_parents, /* name, parents */
+ 0x84, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+NM_CLK(mmc0_clk,
+ CLK_MMC0, /* id */
+ "mmc0", mod_parents, /* name, parents */
+ 0x88, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE |
+ AW_CLK_REPARENT); /* flags */
+
+NM_CLK(mmc1_clk,
+ CLK_MMC1, /* id */
+ "mmc1", mod_parents, /* name, parents */
+ 0x8c, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE |
+ AW_CLK_REPARENT); /* flags */
+
+NM_CLK(mmc2_clk,
+ CLK_MMC2, /* id */
+ "mmc2", mod_parents, /* name, parents */
+ 0x90, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE |
+ AW_CLK_REPARENT); /* flags */
+
+NM_CLK(mmc3_clk,
+ CLK_MMC3, /* id */
+ "mmc3", mod_parents, /* name, parents */
+ 0x94, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE |
+ AW_CLK_REPARENT); /* flags */
+
+NM_CLK(ts_clk,
+ CLK_TS, /* id */
+ "ts", mod_parents, /* name, parents */
+ 0x94, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+NM_CLK(ss_clk,
+ CLK_SS, /* id */
+ "ss", mod_parents, /* name, parents */
+ 0x9c, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+NM_CLK(spi0_clk,
+ CLK_SPI0, /* id */
+ "spi0", mod_parents, /* name, parents */
+ 0xa0, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+NM_CLK(spi1_clk,
+ CLK_SPI1, /* id */
+ "spi1", mod_parents, /* name, parents */
+ 0xa4, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+NM_CLK(spi2_clk,
+ CLK_SPI2, /* id */
+ "spi2", mod_parents, /* name, parents */
+ 0xa8, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+/* MISSING CLK_PATA */
+
+NM_CLK(ir0_clk,
+ CLK_IR0, /* id */
+ "ir0", mod_parents, /* name, parents */
+ 0xb0, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+NM_CLK(ir1_clk,
+ CLK_IR1, /* id */
+ "ir1", mod_parents, /* name, parents */
+ 0xb4, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+/* MISSING CLK_I2S0, CLK_AC97, CLK_SPDIF */
+
+static const char *keypad_parents[] = {"osc24M", "osc24M", "osc32k"};
+NM_CLK(keypad_clk,
+ CLK_KEYPAD, /* id */
+ "keypad", keypad_parents, /* name, parents */
+ 0xc4, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 5, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+static const char *sata_parents[] = {"pll_periph_sata", "osc32k"};
+NM_CLK(sata_clk,
+ CLK_SATA, /* id */
+ "sata", sata_parents, /* name, parents */
+ 0xc8, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* m factor (fake) */
+ 24, 1, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+NM_CLK(spi3_clk,
+ CLK_SPI3, /* id */
+ "spi3", mod_parents, /* name, parents */
+ 0xd4, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+/* MISSING CLK_I2S1, CLK_I2S2, DE Clocks */
+
+static struct aw_ccung_clk a10_ccu_clks[] = {
+ { .type = AW_CLK_NKMP, .clk.nkmp = &pll_core_clk},
+ { .type = AW_CLK_NKMP, .clk.nkmp = &pll_ddr_other_clk},
+ { .type = AW_CLK_NKMP, .clk.nkmp = &pll_ddr_clk},
+ { .type = AW_CLK_NKMP, .clk.nkmp = &pll6_clk},
+ { .type = AW_CLK_NKMP, .clk.nkmp = &pll_periph_sata_clk},
+ { .type = AW_CLK_NM, .clk.nm = &axi_clk},
+ { .type = AW_CLK_NM, .clk.nm = &ahb_clk},
+ { .type = AW_CLK_NM, .clk.nm = &apb0_clk},
+ { .type = AW_CLK_NM, .clk.nm = &apb1_clk},
+ { .type = AW_CLK_FRAC, .clk.frac = &pll_video0_clk},
+ { .type = AW_CLK_FRAC, .clk.frac = &pll_video1_clk},
+ { .type = AW_CLK_NM, .clk.nm = &nand_clk},
+ { .type = AW_CLK_NM, .clk.nm = &ms_clk},
+ { .type = AW_CLK_NM, .clk.nm = &mmc0_clk},
+ { .type = AW_CLK_NM, .clk.nm = &mmc1_clk},
+ { .type = AW_CLK_NM, .clk.nm = &mmc2_clk},
+ { .type = AW_CLK_NM, .clk.nm = &mmc3_clk},
+ { .type = AW_CLK_NM, .clk.nm = &ts_clk},
+ { .type = AW_CLK_NM, .clk.nm = &ss_clk},
+ { .type = AW_CLK_NM, .clk.nm = &spi0_clk},
+ { .type = AW_CLK_NM, .clk.nm = &spi1_clk},
+ { .type = AW_CLK_NM, .clk.nm = &spi2_clk},
+ { .type = AW_CLK_NM, .clk.nm = &ir0_clk},
+ { .type = AW_CLK_NM, .clk.nm = &ir1_clk},
+ { .type = AW_CLK_NM, .clk.nm = &keypad_clk},
+ { .type = AW_CLK_NM, .clk.nm = &sata_clk},
+ { .type = AW_CLK_NM, .clk.nm = &spi3_clk},
+ { .type = AW_CLK_MUX, .clk.mux = &cpu_clk},
+ { .type = AW_CLK_FIXED, .clk.fixed = &pll_periph_clk},
+ { .type = AW_CLK_FIXED, .clk.fixed = &pll_video0_2x_clk},
+ { .type = AW_CLK_FIXED, .clk.fixed = &pll_video1_2x_clk},
+};
+
+static struct aw_clk_init a10_init_clks[] = {
+};
+
+static struct ofw_compat_data compat_data[] = {
+#if defined(SOC_ALLWINNER_A10)
+ { "allwinner,sun4i-a10-ccu", 1 },
+#endif
+#if defined(SOC_ALLWINNER_A20)
+ { "allwinner,sun7i-a20-ccu", 1 },
+#endif
+ { NULL, 0},
+};
+
+static int
+ccu_a10_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner A10/A20 Clock Control Unit NG");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ccu_a10_attach(device_t dev)
+{
+ struct aw_ccung_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ sc->resets = a10_ccu_resets;
+ sc->nresets = nitems(a10_ccu_resets);
+ sc->gates = a10_ccu_gates;
+ sc->ngates = nitems(a10_ccu_gates);
+ sc->clks = a10_ccu_clks;
+ sc->nclks = nitems(a10_ccu_clks);
+ sc->clk_init = a10_init_clks;
+ sc->n_clk_init = nitems(a10_init_clks);
+
+ return (aw_ccung_attach(dev));
+}
+
+static device_method_t ccu_a10ng_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ccu_a10_probe),
+ DEVMETHOD(device_attach, ccu_a10_attach),
+
+ DEVMETHOD_END
+};
+
+static devclass_t ccu_a10ng_devclass;
+
+DEFINE_CLASS_1(ccu_a10ng, ccu_a10ng_driver, ccu_a10ng_methods,
+ sizeof(struct aw_ccung_softc), aw_ccung_driver);
+
+EARLY_DRIVER_MODULE(ccu_a10ng, simplebus, ccu_a10ng_driver,
+ ccu_a10ng_devclass, 0, 0, BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE);
diff --git a/sys/arm/allwinner/clkng/ccu_a13.c b/sys/arm/allwinner/clkng/ccu_a13.c
new file mode 100644
index 000000000000..dbe4e1364f14
--- /dev/null
+++ b/sys/arm/allwinner/clkng/ccu_a13.c
@@ -0,0 +1,570 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2017,2018 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <machine/bus.h>
+
+#include <dev/fdt/simplebus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/extres/clk/clk_div.h>
+#include <dev/extres/clk/clk_fixed.h>
+#include <dev/extres/clk/clk_mux.h>
+
+#include <arm/allwinner/clkng/aw_ccung.h>
+
+#include <dt-bindings/clock/sun5i-ccu.h>
+#include <dt-bindings/reset/sun5i-ccu.h>
+
+/* Non-exported clocks */
+
+#define CLK_PLL_CORE 2
+#define CLK_PLL_AUDIO_BASE 3
+#define CLK_PLL_AUDIO 4
+#define CLK_PLL_AUDIO_2X 5
+#define CLK_PLL_AUDIO_4X 6
+#define CLK_PLL_AUDIO_8X 7
+#define CLK_PLL_VIDEO0 8
+
+#define CLK_PLL_VE 10
+#define CLK_PLL_DDR_BASE 11
+#define CLK_PLL_DDR 12
+#define CLK_PLL_DDR_OTHER 13
+#define CLK_PLL_PERIPH 14
+#define CLK_PLL_VIDEO1 15
+
+#define CLK_AXI 18
+#define CLK_AHB 19
+#define CLK_APB0 20
+#define CLK_APB1 21
+#define CLK_DRAM_AXI 22
+
+#define CLK_TCON_CH1_SCLK 91
+
+#define CLK_MBUS 99
+
+static struct aw_ccung_reset a13_ccu_resets[] = {
+ CCU_RESET(RST_USB_PHY0, 0xcc, 0)
+ CCU_RESET(RST_USB_PHY1, 0xcc, 1)
+
+ CCU_RESET(RST_GPS, 0xd0, 30)
+
+ CCU_RESET(RST_DE_BE, 0x104, 30)
+
+ CCU_RESET(RST_DE_FE, 0x10c, 30)
+
+ CCU_RESET(RST_TVE, 0x118, 29)
+ CCU_RESET(RST_LCD, 0x118, 30)
+
+ CCU_RESET(RST_CSI, 0x134, 30)
+
+ CCU_RESET(RST_VE, 0x13c, 0)
+ CCU_RESET(RST_GPU, 0x154, 30)
+ CCU_RESET(RST_IEP, 0x160, 30)
+
+};
+
+static struct aw_ccung_gate a13_ccu_gates[] = {
+ CCU_GATE(CLK_HOSC, "hosc", "osc24M", 0x50, 0)
+
+ CCU_GATE(CLK_DRAM_AXI, "axi-dram", "axi", 0x5c, 0)
+
+ CCU_GATE(CLK_AHB_OTG, "ahb-otg", "ahb", 0x60, 0)
+ CCU_GATE(CLK_AHB_EHCI, "ahb-ehci", "ahb", 0x60, 1)
+ CCU_GATE(CLK_AHB_OHCI, "ahb-ohci", "ahb", 0x60, 2)
+ CCU_GATE(CLK_AHB_SS, "ahb-ss", "ahb", 0x60, 5)
+ CCU_GATE(CLK_AHB_DMA, "ahb-dma", "ahb", 0x60, 6)
+ CCU_GATE(CLK_AHB_BIST, "ahb-bist", "ahb", 0x60, 7)
+ CCU_GATE(CLK_AHB_MMC0, "ahb-mmc0", "ahb", 0x60, 8)
+ CCU_GATE(CLK_AHB_MMC1, "ahb-mmc1", "ahb", 0x60, 9)
+ CCU_GATE(CLK_AHB_MMC2, "ahb-mmc2", "ahb", 0x60, 10)
+ CCU_GATE(CLK_AHB_NAND, "ahb-nand", "ahb", 0x60, 13)
+ CCU_GATE(CLK_AHB_SDRAM, "ahb-sdram", "ahb", 0x60, 14)
+ CCU_GATE(CLK_AHB_SPI0, "ahb-spi0", "ahb", 0x60, 20)
+ CCU_GATE(CLK_AHB_SPI1, "ahb-spi1", "ahb", 0x60, 21)
+ CCU_GATE(CLK_AHB_SPI2, "ahb-spi2", "ahb", 0x60, 22)
+ CCU_GATE(CLK_AHB_GPS, "ahb-gps", "ahb", 0x60, 26)
+ CCU_GATE(CLK_AHB_HSTIMER, "ahb-hstimer", "ahb", 0x60, 28)
+
+ CCU_GATE(CLK_AHB_VE, "ahb-ve", "ahb", 0x64, 0)
+ CCU_GATE(CLK_AHB_LCD, "ahb-lcd", "ahb", 0x64, 4)
+ CCU_GATE(CLK_AHB_CSI, "ahb-csi", "ahb", 0x64, 8)
+ CCU_GATE(CLK_AHB_DE_BE, "ahb-de-be", "ahb", 0x64, 12)
+ CCU_GATE(CLK_AHB_DE_FE, "ahb-de-fe", "ahb", 0x64, 14)
+ CCU_GATE(CLK_AHB_IEP, "ahb-iep", "ahb", 0x64, 19)
+ CCU_GATE(CLK_AHB_GPU, "ahb-gpu", "ahb", 0x64, 20)
+
+ CCU_GATE(CLK_APB0_CODEC, "apb0-codec", "apb0", 0x68, 0)
+ CCU_GATE(CLK_APB0_PIO, "apb0-pio", "apb0", 0x68, 5)
+ CCU_GATE(CLK_APB0_IR, "apb0-ir", "apb0", 0x68, 6)
+
+ CCU_GATE(CLK_APB1_I2C0, "apb1-i2c0", "apb1", 0x6c, 0)
+ CCU_GATE(CLK_APB1_I2C1, "apb1-i2c1", "apb1", 0x6c, 1)
+ CCU_GATE(CLK_APB1_I2C2, "apb1-i2c2", "apb1", 0x6c, 2)
+ CCU_GATE(CLK_APB1_UART1, "apb1-uart1", "apb1", 0x6c, 17)
+ CCU_GATE(CLK_APB1_UART3, "apb1-uart3", "apb1", 0x6c, 19)
+
+ CCU_GATE(CLK_DRAM_VE, "dram-ve", "pll-ddr", 0x100, 0)
+ CCU_GATE(CLK_DRAM_CSI, "dram-csi", "pll-ddr", 0x100, 1)
+ CCU_GATE(CLK_DRAM_DE_FE, "dram-de-fe", "pll-ddr", 0x100, 25)
+ CCU_GATE(CLK_DRAM_DE_BE, "dram-de-be", "pll-ddr", 0x100, 26)
+ CCU_GATE(CLK_DRAM_ACE, "dram-ace", "pll-ddr", 0x100, 29)
+ CCU_GATE(CLK_DRAM_IEP, "dram-iep", "pll-ddr", 0x100, 31)
+
+ CCU_GATE(CLK_CODEC, "codec", "pll-audio", 0x140, 31)
+
+ CCU_GATE(CLK_AVS, "avs", "hosc", 0x144, 31)
+};
+
+static const char *pll_parents[] = {"hosc"};
+static struct aw_clk_nkmp_def pll_core = {
+ .clkdef = {
+ .id = CLK_PLL_CORE,
+ .name = "pll-core",
+ .parent_names = pll_parents,
+ .parent_cnt = nitems(pll_parents),
+ },
+ .offset = 0x00,
+ .n = {.shift = 8, .width = 5},
+ .k = {.shift = 4, .width = 2},
+ .m = {.shift = 0, .width = 2},
+ .p = {.shift = 16, .width = 2},
+ .gate_shift = 31,
+ .flags = AW_CLK_HAS_GATE,
+};
+
+/*
+ * We only implement pll-audio for now
+ * For pll-audio-2/4/8 x we need a way to change the frequency
+ * of the parent clocks
+ */
+static struct aw_clk_nkmp_def pll_audio = {
+ .clkdef = {
+ .id = CLK_PLL_AUDIO,
+ .name = "pll-audio",
+ .parent_names = pll_parents,
+ .parent_cnt = nitems(pll_parents),
+ },
+ .offset = 0x08,
+ .n = {.shift = 8, .width = 7},
+ .k = {.value = 1, .flags = AW_CLK_FACTOR_FIXED},
+ .m = {.shift = 0, .width = 5},
+ .p = {.shift = 26, .width = 4},
+ .gate_shift = 31,
+ .flags = AW_CLK_HAS_GATE,
+};
+
+/* Missing PLL3-Video */
+/* Missing PLL4-VE */
+
+static struct aw_clk_nkmp_def pll_ddr_base = {
+ .clkdef = {
+ .id = CLK_PLL_DDR_BASE,
+ .name = "pll-ddr-base",
+ .parent_names = pll_parents,
+ .parent_cnt = nitems(pll_parents),
+ },
+ .offset = 0x20,
+ .n = {.shift = 8, .width = 5},
+ .k = {.shift = 4, .width = 2},
+ .m = {.value = 1, .flags = AW_CLK_FACTOR_FIXED},
+ .p = {.value = 1, .flags = AW_CLK_FACTOR_FIXED},
+ .gate_shift = 31,
+ .flags = AW_CLK_HAS_GATE,
+};
+
+static const char *pll_ddr_parents[] = {"pll-ddr-base"};
+static struct clk_div_def pll_ddr = {
+ .clkdef = {
+ .id = CLK_PLL_DDR,
+ .name = "pll-ddr",
+ .parent_names = pll_ddr_parents,
+ .parent_cnt = nitems(pll_ddr_parents),
+ },
+ .offset = 0x20,
+ .i_shift = 0,
+ .i_width = 2,
+};
+
+static const char *pll_ddr_other_parents[] = {"pll-ddr-base"};
+static struct clk_div_def pll_ddr_other = {
+ .clkdef = {
+ .id = CLK_PLL_DDR_OTHER,
+ .name = "pll-ddr-other",
+ .parent_names = pll_ddr_other_parents,
+ .parent_cnt = nitems(pll_ddr_other_parents),
+ },
+ .offset = 0x20,
+ .i_shift = 16,
+ .i_width = 2,
+};
+
+static struct aw_clk_nkmp_def pll_periph = {
+ .clkdef = {
+ .id = CLK_PLL_PERIPH,
+ .name = "pll-periph",
+ .parent_names = pll_parents,
+ .parent_cnt = nitems(pll_parents),
+ },
+ .offset = 0x28,
+ .n = {.shift = 8, .width = 5},
+ .k = {.shift = 4, .width = 2},
+ .m = {.shift = 0, .width = 2},
+ .p = {.value = 2, .flags = AW_CLK_FACTOR_FIXED},
+ .gate_shift = 31,
+ .flags = AW_CLK_HAS_GATE,
+};
+
+/* Missing PLL7-VIDEO1 */
+
+static const char *cpu_parents[] = {"osc32k", "hosc", "pll-core", "pll-periph"};
+static struct aw_clk_prediv_mux_def cpu_clk = {
+ .clkdef = {
+ .id = CLK_CPU,
+ .name = "cpu",
+ .parent_names = cpu_parents,
+ .parent_cnt = nitems(cpu_parents),
+ },
+ .offset = 0x54,
+ .mux_shift = 16, .mux_width = 2,
+ .prediv = {
+ .value = 6,
+ .flags = AW_CLK_FACTOR_FIXED,
+ .cond_shift = 16,
+ .cond_width = 2,
+ .cond_value = 3,
+ },
+};
+
+static const char *axi_parents[] = {"cpu"};
+static struct clk_div_def axi_clk = {
+ .clkdef = {
+ .id = CLK_AXI,
+ .name = "axi",
+ .parent_names = axi_parents,
+ .parent_cnt = nitems(axi_parents),
+ },
+ .offset = 0x50,
+ .i_shift = 0, .i_width = 2,
+};
+
+static const char *ahb_parents[] = {"axi", "cpu", "pll-periph"};
+static struct aw_clk_prediv_mux_def ahb_clk = {
+ .clkdef = {
+ .id = CLK_AHB,
+ .name = "ahb",
+ .parent_names = ahb_parents,
+ .parent_cnt = nitems(ahb_parents),
+ },
+ .offset = 0x54,
+ .mux_shift = 6,
+ .mux_width = 2,
+ .div = {
+ .shift = 4,
+ .width = 2,
+ .flags = AW_CLK_FACTOR_POWER_OF_TWO
+ },
+ .prediv = {
+ .value = 2,
+ .flags = AW_CLK_FACTOR_FIXED,
+ .cond_shift = 6,
+ .cond_width = 2,
+ .cond_value = 2,
+ },
+};
+
+static const char *apb0_parents[] = {"ahb"};
+static struct clk_div_table apb0_div_table[] = {
+ { .value = 0, .divider = 2, },
+ { .value = 1, .divider = 2, },
+ { .value = 2, .divider = 4, },
+ { .value = 3, .divider = 8, },
+ { },
+};
+static struct clk_div_def apb0_clk = {
+ .clkdef = {
+ .id = CLK_APB0,
+ .name = "apb0",
+ .parent_names = apb0_parents,
+ .parent_cnt = nitems(apb0_parents),
+ },
+ .offset = 0x54,
+ .i_shift = 8, .i_width = 2,
+ .div_flags = CLK_DIV_WITH_TABLE,
+ .div_table = apb0_div_table,
+};
+
+static const char *apb1_parents[] = {"hosc", "pll-periph", "osc32k"};
+static struct aw_clk_nm_def apb1_clk = {
+ .clkdef = {
+ .id = CLK_APB1,
+ .name = "apb1",
+ .parent_names = apb1_parents,
+ .parent_cnt = nitems(apb1_parents),
+ },
+ .offset = 0x58,
+ .n = {.shift = 16, .width = 2, .flags = AW_CLK_FACTOR_POWER_OF_TWO, },
+ .m = {.shift = 0, .width = 5},
+ .mux_shift = 24,
+ .mux_width = 2,
+ .flags = AW_CLK_HAS_MUX,
+};
+
+static const char *mod_parents[] = {"hosc", "pll-periph", "pll-ddr-other"};
+
+static struct aw_clk_nm_def nand_clk = {
+ .clkdef = {
+ .id = CLK_NAND,
+ .name = "nand",
+ .parent_names = mod_parents,
+ .parent_cnt = nitems(mod_parents),
+ },
+ .offset = 0x80,
+ .n = {.shift = 16, .width = 2, .flags = AW_CLK_FACTOR_POWER_OF_TWO, },
+ .m = {.shift = 0, .width = 4},
+ .mux_shift = 24,
+ .mux_width = 2,
+ .gate_shift = 31,
+ .flags = AW_CLK_HAS_MUX | AW_CLK_HAS_GATE | AW_CLK_REPARENT
+};
+
+static struct aw_clk_nm_def mmc0_clk = {
+ .clkdef = {
+ .id = CLK_MMC0,
+ .name = "mmc0",
+ .parent_names = mod_parents,
+ .parent_cnt = nitems(mod_parents),
+ },
+ .offset = 0x88,
+ .n = {.shift = 16, .width = 2, .flags = AW_CLK_FACTOR_POWER_OF_TWO, },
+ .m = {.shift = 0, .width = 4},
+ .mux_shift = 24,
+ .mux_width = 2,
+ .gate_shift = 31,
+ .flags = AW_CLK_HAS_MUX | AW_CLK_HAS_GATE | AW_CLK_REPARENT
+};
+
+static struct aw_clk_nm_def mmc1_clk = {
+ .clkdef = {
+ .id = CLK_MMC1,
+ .name = "mmc1",
+ .parent_names = mod_parents,
+ .parent_cnt = nitems(mod_parents),
+ },
+ .offset = 0x8C,
+ .n = {.shift = 16, .width = 2, .flags = AW_CLK_FACTOR_POWER_OF_TWO, },
+ .m = {.shift = 0, .width = 4},
+ .mux_shift = 24,
+ .mux_width = 2,
+ .gate_shift = 31,
+ .flags = AW_CLK_HAS_MUX | AW_CLK_HAS_GATE | AW_CLK_REPARENT
+};
+
+static struct aw_clk_nm_def mmc2_clk = {
+ .clkdef = {
+ .id = CLK_MMC2,
+ .name = "mmc2",
+ .parent_names = mod_parents,
+ .parent_cnt = nitems(mod_parents),
+ },
+ .offset = 0x90,
+ .n = {.shift = 16, .width = 2, .flags = AW_CLK_FACTOR_POWER_OF_TWO, },
+ .m = {.shift = 0, .width = 4},
+ .mux_shift = 24,
+ .mux_width = 2,
+ .gate_shift = 31,
+ .flags = AW_CLK_HAS_MUX | AW_CLK_HAS_GATE | AW_CLK_REPARENT
+};
+
+static struct aw_clk_nm_def ss_clk = {
+ .clkdef = {
+ .id = CLK_SS,
+ .name = "ss",
+ .parent_names = mod_parents,
+ .parent_cnt = nitems(mod_parents),
+ },
+ .offset = 0x9C,
+ .n = {.shift = 16, .width = 2, .flags = AW_CLK_FACTOR_POWER_OF_TWO, },
+ .m = {.shift = 0, .width = 4},
+ .mux_shift = 24,
+ .mux_width = 2,
+ .gate_shift = 31,
+ .flags = AW_CLK_HAS_MUX | AW_CLK_HAS_GATE | AW_CLK_REPARENT
+};
+
+static struct aw_clk_nm_def spi0_clk = {
+ .clkdef = {
+ .id = CLK_SPI0,
+ .name = "spi0",
+ .parent_names = mod_parents,
+ .parent_cnt = nitems(mod_parents),
+ },
+ .offset = 0xA0,
+ .n = {.shift = 16, .width = 2, .flags = AW_CLK_FACTOR_POWER_OF_TWO, },
+ .m = {.shift = 0, .width = 4},
+ .mux_shift = 24,
+ .mux_width = 2,
+ .gate_shift = 31,
+ .flags = AW_CLK_HAS_MUX | AW_CLK_HAS_GATE | AW_CLK_REPARENT
+};
+
+static struct aw_clk_nm_def spi1_clk = {
+ .clkdef = {
+ .id = CLK_SPI1,
+ .name = "spi1",
+ .parent_names = mod_parents,
+ .parent_cnt = nitems(mod_parents),
+ },
+ .offset = 0xA4,
+ .n = {.shift = 16, .width = 2, .flags = AW_CLK_FACTOR_POWER_OF_TWO, },
+ .m = {.shift = 0, .width = 4},
+ .mux_shift = 24,
+ .mux_width = 2,
+ .gate_shift = 31,
+ .flags = AW_CLK_HAS_MUX | AW_CLK_HAS_GATE | AW_CLK_REPARENT
+};
+
+static struct aw_clk_nm_def spi2_clk = {
+ .clkdef = {
+ .id = CLK_SPI2,
+ .name = "spi2",
+ .parent_names = mod_parents,
+ .parent_cnt = nitems(mod_parents),
+ },
+ .offset = 0xA8,
+ .n = {.shift = 16, .width = 2, .flags = AW_CLK_FACTOR_POWER_OF_TWO, },
+ .m = {.shift = 0, .width = 4},
+ .mux_shift = 24,
+ .mux_width = 2,
+ .gate_shift = 31,
+ .flags = AW_CLK_HAS_MUX | AW_CLK_HAS_GATE | AW_CLK_REPARENT
+};
+
+static struct aw_clk_nm_def ir_clk = {
+ .clkdef = {
+ .id = CLK_IR,
+ .name = "ir",
+ .parent_names = mod_parents,
+ .parent_cnt = nitems(mod_parents),
+ },
+ .offset = 0xB0,
+ .n = {.shift = 16, .width = 2, .flags = AW_CLK_FACTOR_POWER_OF_TWO, },
+ .m = {.shift = 0, .width = 4},
+ .mux_shift = 24,
+ .mux_width = 2,
+ .gate_shift = 31,
+ .flags = AW_CLK_HAS_MUX | AW_CLK_HAS_GATE | AW_CLK_REPARENT
+};
+
+/* Missing DE-BE clock */
+/* Missing DE-FE clock */
+/* Missing LCD CH1 clock */
+/* Missing CSI clock */
+/* Missing VE clock */
+
+/* Clocks list */
+static struct aw_ccung_clk a13_ccu_clks[] = {
+ { .type = AW_CLK_NKMP, .clk.nkmp = &pll_core},
+ { .type = AW_CLK_NKMP, .clk.nkmp = &pll_audio},
+ { .type = AW_CLK_NKMP, .clk.nkmp = &pll_ddr_base},
+ { .type = AW_CLK_NKMP, .clk.nkmp = &pll_periph},
+ { .type = AW_CLK_NM, .clk.nm = &apb1_clk},
+ { .type = AW_CLK_NM, .clk.nm = &nand_clk},
+ { .type = AW_CLK_NM, .clk.nm = &mmc0_clk},
+ { .type = AW_CLK_NM, .clk.nm = &mmc1_clk},
+ { .type = AW_CLK_NM, .clk.nm = &mmc2_clk},
+ { .type = AW_CLK_NM, .clk.nm = &ss_clk},
+ { .type = AW_CLK_NM, .clk.nm = &spi0_clk},
+ { .type = AW_CLK_NM, .clk.nm = &spi1_clk},
+ { .type = AW_CLK_NM, .clk.nm = &spi2_clk},
+ { .type = AW_CLK_NM, .clk.nm = &ir_clk},
+ { .type = AW_CLK_PREDIV_MUX, .clk.prediv_mux = &cpu_clk},
+ { .type = AW_CLK_PREDIV_MUX, .clk.prediv_mux = &ahb_clk},
+ { .type = AW_CLK_DIV, .clk.div = &pll_ddr},
+ { .type = AW_CLK_DIV, .clk.div = &pll_ddr_other},
+ { .type = AW_CLK_DIV, .clk.div = &axi_clk},
+ { .type = AW_CLK_DIV, .clk.div = &apb0_clk},
+};
+
+static int
+ccu_a13_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "allwinner,sun5i-a13-ccu"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner A13 Clock Control Unit NG");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ccu_a13_attach(device_t dev)
+{
+ struct aw_ccung_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ sc->resets = a13_ccu_resets;
+ sc->nresets = nitems(a13_ccu_resets);
+ sc->gates = a13_ccu_gates;
+ sc->ngates = nitems(a13_ccu_gates);
+ sc->clks = a13_ccu_clks;
+ sc->nclks = nitems(a13_ccu_clks);
+
+ return (aw_ccung_attach(dev));
+}
+
+static device_method_t ccu_a13ng_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ccu_a13_probe),
+ DEVMETHOD(device_attach, ccu_a13_attach),
+
+ DEVMETHOD_END
+};
+
+static devclass_t ccu_a13ng_devclass;
+
+DEFINE_CLASS_1(ccu_a13ng, ccu_a13ng_driver, ccu_a13ng_methods,
+ sizeof(struct aw_ccung_softc), aw_ccung_driver);
+
+EARLY_DRIVER_MODULE(ccu_a13ng, simplebus, ccu_a13ng_driver,
+ ccu_a13ng_devclass, 0, 0, BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE);
diff --git a/sys/arm/allwinner/clkng/ccu_a31.c b/sys/arm/allwinner/clkng/ccu_a31.c
new file mode 100644
index 000000000000..b19bebfe1786
--- /dev/null
+++ b/sys/arm/allwinner/clkng/ccu_a31.c
@@ -0,0 +1,980 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2017,2018 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <machine/bus.h>
+
+#include <dev/fdt/simplebus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/extres/clk/clk_div.h>
+#include <dev/extres/clk/clk_fixed.h>
+#include <dev/extres/clk/clk_mux.h>
+
+#include <arm/allwinner/clkng/aw_ccung.h>
+
+#include <dt-bindings/clock/sun6i-a31-ccu.h>
+#include <dt-bindings/reset/sun6i-a31-ccu.h>
+
+/* Non-exported clocks */
+#define CLK_PLL_CPU 0
+#define CLK_PLL_AUDIO_BASE 1
+#define CLK_PLL_AUDIO 2
+#define CLK_PLL_AUDIO_2X 3
+#define CLK_PLL_AUDIO_4X 4
+#define CLK_PLL_AUDIO_8X 5
+#define CLK_PLL_VIDEO0 6
+#define CLK_PLL_VIDEO0_2X 7
+#define CLK_PLL_VE 8
+#define CLK_PLL_DDR 9
+
+#define CLK_PLL_PERIPH_2X 11
+#define CLK_PLL_VIDEO1 12
+#define CLK_PLL_VIDEO1_2X 13
+#define CLK_PLL_GPU 14
+#define CLK_PLL_MIPI 15
+#define CLK_PLL9 16
+#define CLK_PLL10 17
+
+#define CLK_AXI 19
+#define CLK_AHB1 20
+#define CLK_APB1 21
+#define CLK_APB2 22
+
+#define CLK_MDFS 107
+#define CLK_SDRAM0 108
+#define CLK_SDRAM1 109
+
+#define CLK_MBUS0 141
+#define CLK_MBUS1 142
+
+static struct aw_ccung_reset a31_ccu_resets[] = {
+ CCU_RESET(RST_USB_PHY0, 0xcc, 0)
+ CCU_RESET(RST_USB_PHY1, 0xcc, 1)
+ CCU_RESET(RST_USB_PHY2, 0xcc, 2)
+
+ CCU_RESET(RST_AHB1_MIPI_DSI, 0x2c0, 1)
+ CCU_RESET(RST_AHB1_SS, 0x2c0, 5)
+ CCU_RESET(RST_AHB1_DMA, 0x2c0, 6)
+ CCU_RESET(RST_AHB1_MMC0, 0x2c0, 8)
+ CCU_RESET(RST_AHB1_MMC1, 0x2c0, 9)
+ CCU_RESET(RST_AHB1_MMC2, 0x2c0, 10)
+ CCU_RESET(RST_AHB1_MMC3, 0x2c0, 11)
+ CCU_RESET(RST_AHB1_NAND1, 0x2c0, 12)
+ CCU_RESET(RST_AHB1_NAND0, 0x2c0, 13)
+ CCU_RESET(RST_AHB1_SDRAM, 0x2c0, 14)
+ CCU_RESET(RST_AHB1_EMAC, 0x2c0, 17)
+ CCU_RESET(RST_AHB1_TS, 0x2c0, 18)
+ CCU_RESET(RST_AHB1_HSTIMER, 0x2c0, 19)
+ CCU_RESET(RST_AHB1_SPI0, 0x2c0, 20)
+ CCU_RESET(RST_AHB1_SPI1, 0x2c0, 21)
+ CCU_RESET(RST_AHB1_SPI2, 0x2c0, 22)
+ CCU_RESET(RST_AHB1_SPI3, 0x2c0, 23)
+ CCU_RESET(RST_AHB1_OTG, 0x2c0, 24)
+ CCU_RESET(RST_AHB1_EHCI0, 0x2c0, 26)
+ CCU_RESET(RST_AHB1_EHCI1, 0x2c0, 27)
+ CCU_RESET(RST_AHB1_OHCI0, 0x2c0, 29)
+ CCU_RESET(RST_AHB1_OHCI1, 0x2c0, 30)
+ CCU_RESET(RST_AHB1_OHCI2, 0x2c0, 31)
+
+ CCU_RESET(RST_AHB1_VE, 0x2c4, 0)
+ CCU_RESET(RST_AHB1_LCD0, 0x2c4, 4)
+ CCU_RESET(RST_AHB1_LCD1, 0x2c4, 5)
+ CCU_RESET(RST_AHB1_CSI, 0x2c4, 8)
+ CCU_RESET(RST_AHB1_HDMI, 0x2c4, 11)
+ CCU_RESET(RST_AHB1_BE0, 0x2c4, 12)
+ CCU_RESET(RST_AHB1_BE1, 0x2c4, 13)
+ CCU_RESET(RST_AHB1_FE0, 0x2c4, 14)
+ CCU_RESET(RST_AHB1_FE1, 0x2c4, 15)
+ CCU_RESET(RST_AHB1_MP, 0x2c4, 18)
+ CCU_RESET(RST_AHB1_GPU, 0x2c4, 20)
+ CCU_RESET(RST_AHB1_DEU0, 0x2c4, 23)
+ CCU_RESET(RST_AHB1_DEU1, 0x2c4, 24)
+ CCU_RESET(RST_AHB1_DRC0, 0x2c4, 25)
+ CCU_RESET(RST_AHB1_DRC1, 0x2c4, 26)
+
+ CCU_RESET(RST_AHB1_LVDS, 0x2c8, 0)
+
+ CCU_RESET(RST_APB1_CODEC, 0x2d0, 0)
+ CCU_RESET(RST_APB1_SPDIF, 0x2d0, 1)
+ CCU_RESET(RST_APB1_DIGITAL_MIC, 0x2d0, 4)
+ CCU_RESET(RST_APB1_DAUDIO0, 0x2d0, 12)
+ CCU_RESET(RST_APB1_DAUDIO1, 0x2d0, 13)
+
+ CCU_RESET(RST_APB2_I2C0, 0x2d8, 0)
+ CCU_RESET(RST_APB2_I2C1, 0x2d8, 1)
+ CCU_RESET(RST_APB2_I2C2, 0x2d8, 2)
+ CCU_RESET(RST_APB2_I2C3, 0x2d8, 3)
+ CCU_RESET(RST_APB2_UART0, 0x2d8, 16)
+ CCU_RESET(RST_APB2_UART1, 0x2d8, 17)
+ CCU_RESET(RST_APB2_UART2, 0x2d8, 18)
+ CCU_RESET(RST_APB2_UART3, 0x2d8, 19)
+ CCU_RESET(RST_APB2_UART4, 0x2d8, 20)
+ CCU_RESET(RST_APB2_UART5, 0x2d8, 21)
+};
+
+static struct aw_ccung_gate a31_ccu_gates[] = {
+ CCU_GATE(CLK_AHB1_MIPIDSI, "ahb1-mipidsi", "ahb1", 0x60, 1)
+ CCU_GATE(CLK_AHB1_SS, "ahb1-ss", "ahb1", 0x60, 5)
+ CCU_GATE(CLK_AHB1_DMA, "ahb1-dma", "ahb1", 0x60, 6)
+ CCU_GATE(CLK_AHB1_MMC0, "ahb1-mmc0", "ahb1", 0x60, 8)
+ CCU_GATE(CLK_AHB1_MMC1, "ahb1-mmc1", "ahb1", 0x60, 9)
+ CCU_GATE(CLK_AHB1_MMC2, "ahb1-mmc2", "ahb1", 0x60, 10)
+ CCU_GATE(CLK_AHB1_MMC3, "ahb1-mmc3", "ahb1", 0x60, 11)
+ CCU_GATE(CLK_AHB1_NAND1, "ahb1-nand1", "ahb1", 0x60, 12)
+ CCU_GATE(CLK_AHB1_NAND0, "ahb1-nand0", "ahb1", 0x60, 13)
+ CCU_GATE(CLK_AHB1_SDRAM, "ahb1-sdram", "ahb1", 0x60, 14)
+ CCU_GATE(CLK_AHB1_EMAC, "ahb1-emac", "ahb1", 0x60, 17)
+ CCU_GATE(CLK_AHB1_TS, "ahb1-ts", "ahb1", 0x60, 18)
+ CCU_GATE(CLK_AHB1_HSTIMER, "ahb1-hstimer", "ahb1", 0x60, 19)
+ CCU_GATE(CLK_AHB1_SPI0, "ahb1-spi0", "ahb1", 0x60, 20)
+ CCU_GATE(CLK_AHB1_SPI1, "ahb1-spi1", "ahb1", 0x60, 21)
+ CCU_GATE(CLK_AHB1_SPI2, "ahb1-spi2", "ahb1", 0x60, 22)
+ CCU_GATE(CLK_AHB1_SPI3, "ahb1-spi3", "ahb1", 0x60, 23)
+ CCU_GATE(CLK_AHB1_OTG, "ahb1-otg", "ahb1", 0x60, 24)
+ CCU_GATE(CLK_AHB1_EHCI0, "ahb1-ehci0", "ahb1", 0x60, 26)
+ CCU_GATE(CLK_AHB1_EHCI1, "ahb1-ehci1", "ahb1", 0x60, 27)
+ CCU_GATE(CLK_AHB1_OHCI0, "ahb1-ohci0", "ahb1", 0x60, 29)
+ CCU_GATE(CLK_AHB1_OHCI1, "ahb1-ohci1", "ahb1", 0x60, 30)
+ CCU_GATE(CLK_AHB1_OHCI2, "ahb1-ohci2", "ahb1", 0x60, 31)
+ CCU_GATE(CLK_AHB1_VE, "ahb1-ve", "ahb1", 0x64, 0)
+ CCU_GATE(CLK_AHB1_LCD0, "ahb1-lcd0", "ahb1", 0x64, 4)
+ CCU_GATE(CLK_AHB1_LCD1, "ahb1-lcd1", "ahb1", 0x64, 5)
+ CCU_GATE(CLK_AHB1_CSI, "ahb1-csi", "ahb1", 0x64, 8)
+ CCU_GATE(CLK_AHB1_HDMI, "ahb1-hdmi", "ahb1", 0x64, 11)
+ CCU_GATE(CLK_AHB1_BE0, "ahb1-be0", "ahb1", 0x64, 12)
+ CCU_GATE(CLK_AHB1_BE1, "ahb1-be1", "ahb1", 0x64, 13)
+ CCU_GATE(CLK_AHB1_FE0, "ahb1-fe0", "ahb1", 0x64, 14)
+ CCU_GATE(CLK_AHB1_FE1, "ahb1-fe1", "ahb1", 0x64, 15)
+ CCU_GATE(CLK_AHB1_MP, "ahb1-mp", "ahb1", 0x64, 18)
+ CCU_GATE(CLK_AHB1_GPU, "ahb1-gpu", "ahb1", 0x64, 20)
+ CCU_GATE(CLK_AHB1_DEU0, "ahb1-deu0", "ahb1", 0x64, 23)
+ CCU_GATE(CLK_AHB1_DEU1, "ahb1-deu1", "ahb1", 0x64, 24)
+ CCU_GATE(CLK_AHB1_DRC0, "ahb1-drc0", "ahb1", 0x64, 25)
+ CCU_GATE(CLK_AHB1_DRC1, "ahb1-drc1", "ahb1", 0x64, 26)
+
+ CCU_GATE(CLK_APB1_CODEC, "apb1-codec", "apb1", 0x68, 0)
+ CCU_GATE(CLK_APB1_SPDIF, "apb1-spdif", "apb1", 0x68, 1)
+ CCU_GATE(CLK_APB1_DIGITAL_MIC, "apb1-digital-mic", "apb1", 0x68, 4)
+ CCU_GATE(CLK_APB1_PIO, "apb1-pio", "apb1", 0x68, 5)
+ CCU_GATE(CLK_APB1_DAUDIO0, "apb1-daudio0", "apb1", 0x68, 12)
+ CCU_GATE(CLK_APB1_DAUDIO1, "apb1-daudio1", "apb1", 0x68, 13)
+
+ CCU_GATE(CLK_APB2_I2C0, "apb2-i2c0", "apb2", 0x6c, 0)
+ CCU_GATE(CLK_APB2_I2C1, "apb2-i2c1", "apb2", 0x6c, 1)
+ CCU_GATE(CLK_APB2_I2C2, "apb2-i2c2", "apb2", 0x6c, 2)
+ CCU_GATE(CLK_APB2_I2C3, "apb2-i2c3", "apb2", 0x6c, 3)
+ CCU_GATE(CLK_APB2_UART0, "apb2-uart0", "apb2", 0x6c, 16)
+ CCU_GATE(CLK_APB2_UART1, "apb2-uart1", "apb2", 0x6c, 17)
+ CCU_GATE(CLK_APB2_UART2, "apb2-uart2", "apb2", 0x6c, 18)
+ CCU_GATE(CLK_APB2_UART3, "apb2-uart3", "apb2", 0x6c, 19)
+ CCU_GATE(CLK_APB2_UART4, "apb2-uart4", "apb2", 0x6c, 20)
+ CCU_GATE(CLK_APB2_UART5, "apb2-uart5", "apb2", 0x6c, 21)
+
+ CCU_GATE(CLK_DAUDIO0, "daudio0", "daudio0mux", 0xb0, 31)
+ CCU_GATE(CLK_DAUDIO1, "daudio1", "daudio1mux", 0xb4, 31)
+
+ CCU_GATE(CLK_USB_PHY0, "usb-phy0", "osc24M", 0xcc, 8)
+ CCU_GATE(CLK_USB_PHY1, "usb-phy1", "osc24M", 0xcc, 9)
+ CCU_GATE(CLK_USB_PHY2, "usb-phy2", "osc24M", 0xcc, 10)
+ CCU_GATE(CLK_USB_OHCI0, "usb-ohci0", "osc24M", 0xcc, 16)
+ CCU_GATE(CLK_USB_OHCI1, "usb-ohci1", "osc24M", 0xcc, 17)
+ CCU_GATE(CLK_USB_OHCI2, "usb-ohci2", "osc24M", 0xcc, 18)
+
+ CCU_GATE(CLK_DRAM_VE, "dram-ve", "mdfs", 0x100, 0)
+ CCU_GATE(CLK_DRAM_CSI_ISP, "dram-csi_isp", "mdfs", 0x100, 1)
+ CCU_GATE(CLK_DRAM_TS, "dram-ts", "mdfs", 0x100, 3)
+ CCU_GATE(CLK_DRAM_DRC0, "dram-drc0", "mdfs", 0x100, 16)
+ CCU_GATE(CLK_DRAM_DRC1, "dram-drc1", "mdfs", 0x100, 17)
+ CCU_GATE(CLK_DRAM_DEU0, "dram-deu0", "mdfs", 0x100, 18)
+ CCU_GATE(CLK_DRAM_DEU1, "dram-deu1", "mdfs", 0x100, 19)
+ CCU_GATE(CLK_DRAM_FE0, "dram-fe0", "mdfs", 0x100, 24)
+ CCU_GATE(CLK_DRAM_FE1, "dram-fe1", "mdfs", 0x100, 25)
+ CCU_GATE(CLK_DRAM_BE0, "dram-be0", "mdfs", 0x100, 26)
+ CCU_GATE(CLK_DRAM_BE1, "dram-be1", "mdfs", 0x100, 27)
+ CCU_GATE(CLK_DRAM_MP, "dram-mp", "mdfs", 0x100, 28)
+
+ CCU_GATE(CLK_CODEC, "codec", "pll_audio", 0x140, 31)
+
+ CCU_GATE(CLK_AVS, "avs", "pll_audio", 0x144, 31)
+
+ CCU_GATE(CLK_DIGITAL_MIC, "digital-mic", "pll_audio", 0x148, 31)
+
+ CCU_GATE(CLK_HDMI_DDC, "hdmi-ddc", "osc24M", 0x150, 30)
+
+ CCU_GATE(CLK_PS, "ps", "lcd1_ch1", 0x154, 31)
+};
+
+static const char *pll_parents[] = {"osc24M"};
+
+NKMP_CLK(pll_cpu_clk,
+ CLK_PLL_CPU, /* id */
+ "pll_cpu", pll_parents, /* name, parents */
+ 0x00, /* offset */
+ 8, 5, 0, 0, /* n factor */
+ 4, 2, 0, 0, /* k factor */
+ 0, 2, 0, 0, /* m factor */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* p factor (fake) */
+ 31, /* gate */
+ 28, 1000, /* lock */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_LOCK | AW_CLK_SCALE_CHANGE); /* flags */
+
+NKMP_CLK(pll_audio_clk,
+ CLK_PLL_AUDIO, /* id */
+ "pll_audio", pll_parents, /* name, parents */
+ 0x08, /* offset */
+ 8, 7, 0, 0, /* n factor */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* k factor (fake) */
+ 0, 4, 1, 0, /* m factor */
+ 16, 3, 1, 0, /* p factor */
+ 31, /* gate */
+ 28, 1000, /* lock */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_LOCK); /* flags */
+
+static const char *pll_audio_mult_parents[] = {"pll_audio"};
+FIXED_CLK(pll_audio_2x_clk,
+ CLK_PLL_AUDIO_2X, /* id */
+ "pll_audio-2x", /* name */
+ pll_audio_mult_parents, /* parent */
+ 0, /* freq */
+ 2, /* mult */
+ 1, /* div */
+ 0); /* flags */
+FIXED_CLK(pll_audio_4x_clk,
+ CLK_PLL_AUDIO_4X, /* id */
+ "pll_audio-4x", /* name */
+ pll_audio_mult_parents, /* parent */
+ 0, /* freq */
+ 4, /* mult */
+ 1, /* div */
+ 0); /* flags */
+FIXED_CLK(pll_audio_8x_clk,
+ CLK_PLL_AUDIO_8X, /* id */
+ "pll_audio-8x", /* name */
+ pll_audio_mult_parents, /* parent */
+ 0, /* freq */
+ 8, /* mult */
+ 1, /* div */
+ 0); /* flags */
+
+FRAC_CLK(pll_video0_clk,
+ CLK_PLL_VIDEO0, /* id */
+ "pll_video0", pll_parents, /* name, parents */
+ 0x10, /* offset */
+ 8, 7, 0, 0, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 31, 28, 1000, /* gate, lock, lock retries */
+ AW_CLK_HAS_LOCK, /* flags */
+ 270000000, 297000000, /* freq0, freq1 */
+ 24, 25, /* mode sel, freq sel */
+ 30000000, 600000000); /* min freq, max freq */
+static const char *pll_video0_2x_parents[] = {"pll_video0"};
+FIXED_CLK(pll_video0_2x_clk,
+ CLK_PLL_VIDEO0_2X, /* id */
+ "pll_video0-2x", /* name */
+ pll_video0_2x_parents, /* parent */
+ 0, /* freq */
+ 2, /* mult */
+ 1, /* div */
+ 0); /* flags */
+
+FRAC_CLK(pll_ve_clk,
+ CLK_PLL_VE, /* id */
+ "pll_ve", pll_parents, /* name, parents */
+ 0x18, /* offset */
+ 8, 7, 0, 0, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 31, 28, 1000, /* gate, lock, lock retries */
+ AW_CLK_HAS_LOCK, /* flags */
+ 270000000, 297000000, /* freq0, freq1 */
+ 24, 25, /* mode sel, freq sel */
+ 30000000, 600000000); /* min freq, max freq */
+
+NKMP_CLK_WITH_UPDATE(pll_ddr_clk,
+ CLK_PLL_DDR, /* id */
+ "pll_ddr", pll_parents, /* name, parents */
+ 0x20, /* offset */
+ 8, 5, 0, 0, /* n factor */
+ 4, 2, 0, 0, /* k factor */
+ 0, 2, 0, 0, /* m factor */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* p factor (fake) */
+ 31, /* gate */
+ 28, 1000, /* lock */
+ 20, /* update */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_LOCK); /* flags */
+
+NKMP_CLK(pll_periph_clk,
+ CLK_PLL_PERIPH, /* id */
+ "pll_periph", pll_parents, /* name, parents */
+ 0x28, /* offset */
+ 8, 4, 0, 0, /* n factor */
+ 5, 2, 1, 0, /* k factor */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* m factor (fake) */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* p factor (fake) */
+ 31, /* gate */
+ 28, 1000, /* lock */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_LOCK); /* flags */
+
+static const char *pll_periph_2x_parents[] = {"pll_periph"};
+FIXED_CLK(pll_periph_2x_clk,
+ CLK_PLL_PERIPH_2X, /* id */
+ "pll_periph-2x", /* name */
+ pll_periph_2x_parents, /* parent */
+ 0, /* freq */
+ 2, /* mult */
+ 1, /* div */
+ 0); /* flags */
+
+FRAC_CLK(pll_video1_clk,
+ CLK_PLL_VIDEO1, /* id */
+ "pll_video1", pll_parents, /* name, parents */
+ 0x30, /* offset */
+ 8, 7, 0, 0, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 31, 28, 1000, /* gate, lock, lock retries */
+ AW_CLK_HAS_LOCK, /* flags */
+ 270000000, 297000000, /* freq0, freq1 */
+ 24, 25, /* mode sel, freq sel */
+ 30000000, 600000000); /* min freq, max freq */
+
+static const char *pll_video1_2x_parents[] = {"pll_video1"};
+FIXED_CLK(pll_video1_2x_clk,
+ CLK_PLL_VIDEO1_2X, /* id */
+ "pll_video1-2x", /* name */
+ pll_video1_2x_parents, /* parent */
+ 0, /* freq */
+ 2, /* mult */
+ 1, /* div */
+ 0); /* flags */
+
+FRAC_CLK(pll_gpu_clk,
+ CLK_PLL_GPU, /* id */
+ "pll_gpu", pll_parents, /* name, parents */
+ 0x38, /* offset */
+ 8, 7, 0, 0, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 31, 28, 1000, /* gate, lock, lock retries */
+ AW_CLK_HAS_LOCK, /* flags */
+ 270000000, 297000000, /* freq0, freq1 */
+ 24, 25, /* mode sel, freq sel */
+ 30000000, 600000000); /* min freq, max freq */
+
+static const char *pll_mipi_parents[] = {"pll_video0", "pll_video1"};
+NKMP_CLK(pll_mipi_clk,
+ CLK_PLL_MIPI, /* id */
+ "pll_mipi", pll_mipi_parents, /* name, parents */
+ 0x40, /* offset */
+ 8, 4, 0, 0, /* n factor */
+ 4, 2, 1, 0, /* k factor */
+ 0, 2, 0, 0, /* m factor (fake) */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* p factor (fake) */
+ 31, /* gate */
+ 28, 1000, /* lock */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_LOCK); /* flags */
+
+FRAC_CLK(pll9_clk,
+ CLK_PLL9, /* id */
+ "pll9", pll_parents, /* name, parents */
+ 0x44, /* offset */
+ 8, 7, 0, 0, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 31, 28, 1000, /* gate, lock, lock retries */
+ AW_CLK_HAS_LOCK, /* flags */
+ 270000000, 297000000, /* freq0, freq1 */
+ 24, 25, /* mode sel, freq sel */
+ 30000000, 600000000); /* min freq, max freq */
+
+FRAC_CLK(pll10_clk,
+ CLK_PLL10, /* id */
+ "pll10", pll_parents, /* name, parents */
+ 0x48, /* offset */
+ 8, 7, 0, 0, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 31, 28, 1000, /* gate, lock, lock retries */
+ AW_CLK_HAS_LOCK, /* flags */
+ 270000000, 297000000, /* freq0, freq1 */
+ 24, 25, /* mode sel, freq sel */
+ 30000000, 600000000); /* min freq, max freq */
+
+static struct clk_div_table axi_div_table[] = {
+ { .value = 0, .divider = 1, },
+ { .value = 1, .divider = 2, },
+ { .value = 2, .divider = 3, },
+ { .value = 3, .divider = 4, },
+ { .value = 4, .divider = 4, },
+ { .value = 5, .divider = 4, },
+ { .value = 6, .divider = 4, },
+ { .value = 7, .divider = 4, },
+ { },
+};
+static const char *axi_parents[] = {"cpu"};
+DIV_CLK(axi_clk,
+ CLK_AXI, /* id */
+ "axi", axi_parents, /* name, parents */
+ 0x50, /* offset */
+ 0, 2, /* shift, mask */
+ 0, axi_div_table); /* flags, div table */
+
+static const char *cpu_parents[] = {"osc32k", "osc24M", "pll_cpu", "pll_cpu"};
+MUX_CLK(cpu_clk,
+ CLK_CPU, /* id */
+ "cpu", cpu_parents, /* name, parents */
+ 0x50, 16, 2); /* offset, shift, width */
+
+static const char *ahb1_parents[] = {"osc32k", "osc24M", "axi", "pll_periph"};
+PREDIV_CLK(ahb1_clk,
+ CLK_AHB1, /* id */
+ "ahb1", ahb1_parents, /* name, parents */
+ 0x54, /* offset */
+ 12, 2, /* mux */
+ 4, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* div */
+ 6, 2, 0, AW_CLK_FACTOR_HAS_COND, /* prediv */
+ 12, 2, 3); /* prediv condition */
+
+static const char *apb1_parents[] = {"ahb1"};
+static struct clk_div_table apb1_div_table[] = {
+ { .value = 0, .divider = 2, },
+ { .value = 1, .divider = 2, },
+ { .value = 2, .divider = 4, },
+ { .value = 3, .divider = 8, },
+ { },
+};
+DIV_CLK(apb1_clk,
+ CLK_APB1, /* id */
+ "apb1", apb1_parents, /* name, parents */
+ 0x54, /* offset */
+ 8, 2, /* shift, mask */
+ CLK_DIV_WITH_TABLE, /* flags */
+ apb1_div_table); /* div table */
+
+static const char *apb2_parents[] = {"osc32k", "osc24M", "pll_periph", "pll_periph"};
+NM_CLK(apb2_clk,
+ CLK_APB2, /* id */
+ "apb2", apb2_parents, /* name, parents */
+ 0x58, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 5, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 0, /* gate */
+ AW_CLK_HAS_MUX);
+
+static const char *mod_parents[] = {"osc24M", "pll_periph"};
+NM_CLK(nand0_clk,
+ CLK_NAND0, "nand0", mod_parents, /* id, name, parents */
+ 0x80, /* offset */
+ 16, 3, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX); /* flags */
+
+NM_CLK(nand1_clk,
+ CLK_NAND1, "nand1", mod_parents, /* id, name, parents */
+ 0x80, /* offset */
+ 16, 3, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX); /* flags */
+
+NM_CLK(mmc0_clk,
+ CLK_MMC0, "mmc0", mod_parents, /* id, name, parents */
+ 0x88, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX |
+ AW_CLK_REPARENT); /* flags */
+
+NM_CLK(mmc1_clk,
+ CLK_MMC1, "mmc1", mod_parents, /* id, name, parents */
+ 0x8c, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX |
+ AW_CLK_REPARENT); /* flags */
+
+NM_CLK(mmc2_clk,
+ CLK_MMC2, "mmc2", mod_parents, /* id, name, parents */
+ 0x90, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX |
+ AW_CLK_REPARENT); /* flags */
+
+NM_CLK(mmc3_clk,
+ CLK_MMC2, "mmc3", mod_parents, /* id, name, parents */
+ 0x94, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX |
+ AW_CLK_REPARENT); /* flags */
+
+static const char *ts_parents[] = {"osc24M", "pll_periph"};
+NM_CLK(ts_clk,
+ CLK_TS, "ts", ts_parents, /* id, name, parents */
+ 0x98, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 4, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX); /* flags */
+
+NM_CLK(ss_clk,
+ CLK_SS, "ss", mod_parents, /* id, name, parents */
+ 0x9C, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 4, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX); /* flags */
+
+NM_CLK(spi0_clk,
+ CLK_SPI0, "spi0", mod_parents, /* id, name, parents */
+ 0xA0, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 4, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX); /* flags */
+
+NM_CLK(spi1_clk,
+ CLK_SPI1, "spi1", mod_parents, /* id, name, parents */
+ 0xA4, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 4, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX); /* flags */
+
+NM_CLK(spi2_clk,
+ CLK_SPI2, "spi2", mod_parents, /* id, name, parents */
+ 0xA8, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 4, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX); /* flags */
+
+NM_CLK(spi3_clk,
+ CLK_SPI3, "spi3", mod_parents, /* id, name, parents */
+ 0xAC, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 4, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX); /* flags */
+
+static const char *daudio_parents[] = {"pll_audio-8x", "pll_audio-4x", "pll_audio-2x", "pll_audio"};
+MUX_CLK(daudio0mux_clk,
+ 0,
+ "daudio0mux", daudio_parents,
+ 0xb0, 16, 2);
+MUX_CLK(daudio1mux_clk,
+ 0,
+ "daudio1mux", daudio_parents,
+ 0xb4, 16, 2);
+
+static const char *mdfs_parents[] = {"pll_ddr", "pll_periph"};
+NM_CLK(mdfs_clk,
+ CLK_MDFS, "mdfs", mdfs_parents, /* id, name, parents */
+ 0xF0, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 4, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX); /* flags */
+
+static const char *dram_parents[] = {"pll_ddr", "pll_periph"};
+NM_CLK(sdram0_clk,
+ CLK_SDRAM0, "sdram0", dram_parents, /* id, name, parents */
+ 0xF4, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 4, 0, 0, /* m factor */
+ 4, 1, /* mux */
+ 0, /* gate */
+ AW_CLK_HAS_MUX); /* flags */
+NM_CLK(sdram1_clk,
+ CLK_SDRAM1, "sdram1", dram_parents, /* id, name, parents */
+ 0xF4, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 8, 4, 0, 0, /* m factor */
+ 12, 1, /* mux */
+ 0, /* gate */
+ AW_CLK_HAS_MUX); /* flags */
+
+static const char *befe_parents[] = {"pll_video0", "pll_video1", "pll_periph-2x", "pll_gpu", "pll9", "pll10"};
+NM_CLK(be0_clk,
+ CLK_BE0, "be0", befe_parents, /* id, name, parents */
+ 0x104, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 4, 0, 0, /* m factor */
+ 24, 3, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+NM_CLK(be1_clk,
+ CLK_BE1, "be1", befe_parents, /* id, name, parents */
+ 0x108, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 4, 0, 0, /* m factor */
+ 24, 3, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+NM_CLK(fe0_clk,
+ CLK_FE0, "fe0", befe_parents, /* id, name, parents */
+ 0x104, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 4, 0, 0, /* m factor */
+ 24, 3, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+NM_CLK(fe1_clk,
+ CLK_FE1, "fe1", befe_parents, /* id, name, parents */
+ 0x108, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 4, 0, 0, /* m factor */
+ 24, 3, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+static const char *mp_parents[] = {"pll_video0", "pll_video1", "pll9", "pll10"};
+NM_CLK(mp_clk,
+ CLK_MP, "mp", mp_parents, /* id, name, parents */
+ 0x108, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 4, 0, 0, /* m factor */
+ 24, 3, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+static const char *lcd_ch0_parents[] = {"pll_video0", "pll_video1", "pll_video0-2x", "pll_video1-2x", "pll_mipi"};
+NM_CLK(lcd0_ch0_clk,
+ CLK_LCD0_CH0, "lcd0_ch0", lcd_ch0_parents, /* id, name, parents */
+ 0x118, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* m factor (fake )*/
+ 24, 3, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+NM_CLK(lcd1_ch0_clk,
+ CLK_LCD1_CH0, "lcd1_ch0", lcd_ch0_parents, /* id, name, parents */
+ 0x11C, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* m factor (fake )*/
+ 24, 3, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+static const char *lcd_ch1_parents[] = {"pll_video0", "pll_video1", "pll_video0-2x", "pll_video1-2x"};
+NM_CLK(lcd0_ch1_clk,
+ CLK_LCD0_CH1, "lcd0_ch1", lcd_ch1_parents, /* id, name, parents */
+ 0x12C, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 4, 0, 0, /* m factor */
+ 24, 3, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+NM_CLK(lcd1_ch1_clk,
+ CLK_LCD1_CH1, "lcd1_ch1", lcd_ch1_parents, /* id, name, parents */
+ 0x130, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 4, 0, 0, /* m factor */
+ 24, 3, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+/* CSI0 0x134 Need Mux table */
+/* CSI1 0x138 Need Mux table */
+
+static const char *ve_parents[] = {"pll_ve"};
+NM_CLK(ve_clk,
+ CLK_VE, "ve", ve_parents, /* id, name, parents */
+ 0x13C, /* offset */
+ 16, 3, 0, 0, /* n factor */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* m factor (fake) */
+ 0, 0, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE); /* flags */
+
+NM_CLK(hdmi_clk,
+ CLK_HDMI, "hdmi", lcd_ch1_parents, /* id, name, parents */
+ 0x150, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 4, 0, 0, /* m factor */
+ 0, 0, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE); /* flags */
+
+static const char *mbus_parents[] = {"osc24M", "pll_periph", "pll_ddr"};
+NM_CLK(mbus0_clk,
+ CLK_MBUS0, "mbus0", mbus_parents, /* id, name, parents */
+ 0x15C, /* offset */
+ 16, 2, 0, 0, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+NM_CLK(mbus1_clk,
+ CLK_MBUS1, "mbus1", mbus_parents, /* id, name, parents */
+ 0x160, /* offset */
+ 16, 2, 0, 0, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+static const char *mipi_parents[] = {"pll_video0", "pll_video1", "pll_video0-2x", "pll_video1-2x"};
+NM_CLK(mipi_dsi_clk,
+ CLK_MIPI_DSI, "mipi_dsi", mipi_parents, /* id, name, parents */
+ 0x168, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 16, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+NM_CLK(mipi_dsi_dphy_clk,
+ CLK_MIPI_DSI_DPHY, "mipi_dsi_dphy", mipi_parents, /* id, name, parents */
+ 0x168, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 4, 0, 0, /* m factor */
+ 8, 2, /* mux */
+ 15, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+NM_CLK(mipi_csi_dphy_clk,
+ CLK_MIPI_CSI_DPHY, "mipi_csi_dphy", mipi_parents, /* id, name, parents */
+ 0x16C, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 4, 0, 0, /* m factor */
+ 8, 2, /* mux */
+ 15, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+static const char *iep_parents[] = {"pll_video0", "pll_video1", "pll_periph-2x", "pll_gpu", "pll9", "pll10"};
+
+NM_CLK(iep_drc0_clk,
+ CLK_IEP_DRC0, "iep_drc0", iep_parents, /* id, name, parents */
+ 0x180, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+NM_CLK(iep_drc1_clk,
+ CLK_IEP_DRC1, "iep_drc1", iep_parents, /* id, name, parents */
+ 0x184, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+NM_CLK(iep_deu0_clk,
+ CLK_IEP_DEU0, "iep_deu0", iep_parents, /* id, name, parents */
+ 0x188, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+NM_CLK(iep_deu1_clk,
+ CLK_IEP_DEU1, "iep_deu1", iep_parents, /* id, name, parents */
+ 0x18C, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+static const char *gpu_parents[] = {"pll_gpu", "pll_periph-2x", "pll_video0", "pll_video1", "pll9", "pll10"};
+PREDIV_CLK(gpu_core_clk,
+ CLK_GPU_CORE, /* id */
+ "gpu_core", gpu_parents, /* name, parents */
+ 0x1A0, /* offset */
+ 24, 3, /* mux */
+ 0, 3, 0, 0, /* div */
+ 0, 0, 3, AW_CLK_FACTOR_HAS_COND | AW_CLK_FACTOR_FIXED, /* prediv */
+ 24, 2, 1); /* prediv condition */
+
+PREDIV_CLK(gpu_memory_clk,
+ CLK_GPU_MEMORY, /* id */
+ "gpu_memory", gpu_parents, /* name, parents */
+ 0x1A4, /* offset */
+ 24, 3, /* mux */
+ 0, 3, 0, 0, /* div */
+ 0, 0, 3, AW_CLK_FACTOR_HAS_COND | AW_CLK_FACTOR_FIXED, /* prediv */
+ 24, 2, 1); /* prediv condition */
+
+PREDIV_CLK(gpu_hyd_clk,
+ CLK_GPU_HYD, /* id */
+ "gpu_hyd", gpu_parents, /* name, parents */
+ 0x1A8, /* offset */
+ 24, 3, /* mux */
+ 0, 3, 0, 0, /* div */
+ 0, 0, 3, AW_CLK_FACTOR_HAS_COND | AW_CLK_FACTOR_FIXED, /* prediv */
+ 24, 2, 1); /* prediv condition */
+
+/* ATS 0x1B0 */
+/* Trace 0x1B4 */
+static struct aw_ccung_clk a31_ccu_clks[] = {
+ { .type = AW_CLK_NKMP, .clk.nkmp = &pll_cpu_clk},
+ { .type = AW_CLK_NKMP, .clk.nkmp = &pll_audio_clk},
+ { .type = AW_CLK_NKMP, .clk.nkmp = &pll_periph_clk},
+ { .type = AW_CLK_NKMP, .clk.nkmp = &pll_ddr_clk},
+ { .type = AW_CLK_NKMP, .clk.nkmp = &pll_mipi_clk},
+ { .type = AW_CLK_FRAC, .clk.frac = &pll_video0_clk},
+ { .type = AW_CLK_FRAC, .clk.frac = &pll_ve_clk},
+ { .type = AW_CLK_FRAC, .clk.frac = &pll_video1_clk},
+ { .type = AW_CLK_FRAC, .clk.frac = &pll_gpu_clk},
+ { .type = AW_CLK_FRAC, .clk.frac = &pll9_clk},
+ { .type = AW_CLK_FRAC, .clk.frac = &pll10_clk},
+ { .type = AW_CLK_NM, .clk.nm = &apb2_clk},
+ { .type = AW_CLK_NM, .clk.nm = &nand0_clk},
+ { .type = AW_CLK_NM, .clk.nm = &nand1_clk},
+ { .type = AW_CLK_NM, .clk.nm = &mmc0_clk},
+ { .type = AW_CLK_NM, .clk.nm = &mmc1_clk},
+ { .type = AW_CLK_NM, .clk.nm = &mmc2_clk},
+ { .type = AW_CLK_NM, .clk.nm = &mmc3_clk},
+ { .type = AW_CLK_NM, .clk.nm = &ts_clk},
+ { .type = AW_CLK_NM, .clk.nm = &ss_clk},
+ { .type = AW_CLK_NM, .clk.nm = &spi0_clk},
+ { .type = AW_CLK_NM, .clk.nm = &spi1_clk},
+ { .type = AW_CLK_NM, .clk.nm = &spi2_clk},
+ { .type = AW_CLK_NM, .clk.nm = &spi3_clk},
+ { .type = AW_CLK_NM, .clk.nm = &mdfs_clk},
+ { .type = AW_CLK_NM, .clk.nm = &sdram0_clk},
+ { .type = AW_CLK_NM, .clk.nm = &sdram1_clk},
+ { .type = AW_CLK_NM, .clk.nm = &be0_clk},
+ { .type = AW_CLK_NM, .clk.nm = &be1_clk},
+ { .type = AW_CLK_NM, .clk.nm = &fe0_clk},
+ { .type = AW_CLK_NM, .clk.nm = &fe1_clk},
+ { .type = AW_CLK_NM, .clk.nm = &mp_clk},
+ { .type = AW_CLK_NM, .clk.nm = &lcd0_ch0_clk},
+ { .type = AW_CLK_NM, .clk.nm = &lcd1_ch0_clk},
+ { .type = AW_CLK_NM, .clk.nm = &lcd0_ch1_clk},
+ { .type = AW_CLK_NM, .clk.nm = &lcd1_ch1_clk},
+ { .type = AW_CLK_NM, .clk.nm = &ve_clk},
+ { .type = AW_CLK_NM, .clk.nm = &hdmi_clk},
+ { .type = AW_CLK_NM, .clk.nm = &mbus0_clk},
+ { .type = AW_CLK_NM, .clk.nm = &mbus1_clk},
+ { .type = AW_CLK_NM, .clk.nm = &mipi_dsi_clk},
+ { .type = AW_CLK_NM, .clk.nm = &mipi_dsi_dphy_clk},
+ { .type = AW_CLK_NM, .clk.nm = &mipi_csi_dphy_clk},
+ { .type = AW_CLK_NM, .clk.nm = &iep_drc0_clk},
+ { .type = AW_CLK_NM, .clk.nm = &iep_drc1_clk},
+ { .type = AW_CLK_NM, .clk.nm = &iep_deu0_clk},
+ { .type = AW_CLK_NM, .clk.nm = &iep_deu1_clk},
+ { .type = AW_CLK_PREDIV_MUX, .clk.prediv_mux = &ahb1_clk},
+ { .type = AW_CLK_PREDIV_MUX, .clk.prediv_mux = &gpu_core_clk},
+ { .type = AW_CLK_PREDIV_MUX, .clk.prediv_mux = &gpu_memory_clk},
+ { .type = AW_CLK_PREDIV_MUX, .clk.prediv_mux = &gpu_hyd_clk},
+ { .type = AW_CLK_DIV, .clk.div = &axi_clk},
+ { .type = AW_CLK_DIV, .clk.div = &apb1_clk},
+ { .type = AW_CLK_MUX, .clk.mux = &cpu_clk},
+ { .type = AW_CLK_MUX, .clk.mux = &daudio0mux_clk},
+ { .type = AW_CLK_MUX, .clk.mux = &daudio1mux_clk},
+ { .type = AW_CLK_FIXED, .clk.fixed = &pll_audio_2x_clk},
+ { .type = AW_CLK_FIXED, .clk.fixed = &pll_audio_4x_clk},
+ { .type = AW_CLK_FIXED, .clk.fixed = &pll_audio_8x_clk},
+ { .type = AW_CLK_FIXED, .clk.fixed = &pll_video0_2x_clk},
+ { .type = AW_CLK_FIXED, .clk.fixed = &pll_periph_2x_clk},
+ { .type = AW_CLK_FIXED, .clk.fixed = &pll_video1_2x_clk},
+};
+
+static int
+ccu_a31_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "allwinner,sun6i-a31-ccu"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner A31 Clock Control Unit NG");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ccu_a31_attach(device_t dev)
+{
+ struct aw_ccung_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ sc->resets = a31_ccu_resets;
+ sc->nresets = nitems(a31_ccu_resets);
+ sc->gates = a31_ccu_gates;
+ sc->ngates = nitems(a31_ccu_gates);
+ sc->clks = a31_ccu_clks;
+ sc->nclks = nitems(a31_ccu_clks);
+
+ return (aw_ccung_attach(dev));
+}
+
+static device_method_t ccu_a31ng_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ccu_a31_probe),
+ DEVMETHOD(device_attach, ccu_a31_attach),
+
+ DEVMETHOD_END
+};
+
+static devclass_t ccu_a31ng_devclass;
+
+DEFINE_CLASS_1(ccu_a31ng, ccu_a31ng_driver, ccu_a31ng_methods,
+ sizeof(struct aw_ccung_softc), aw_ccung_driver);
+
+EARLY_DRIVER_MODULE(ccu_a31ng, simplebus, ccu_a31ng_driver,
+ ccu_a31ng_devclass, 0, 0, BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE);
diff --git a/sys/arm/allwinner/clkng/ccu_a64.c b/sys/arm/allwinner/clkng/ccu_a64.c
new file mode 100644
index 000000000000..41f531596475
--- /dev/null
+++ b/sys/arm/allwinner/clkng/ccu_a64.c
@@ -0,0 +1,843 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2017,2018 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <machine/bus.h>
+
+#include <dev/fdt/simplebus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/extres/clk/clk_div.h>
+#include <dev/extres/clk/clk_fixed.h>
+#include <dev/extres/clk/clk_mux.h>
+
+#include <arm/allwinner/clkng/aw_ccung.h>
+
+#include <dt-bindings/clock/sun50i-a64-ccu.h>
+#include <dt-bindings/reset/sun50i-a64-ccu.h>
+
+/* Non-exported clocks */
+
+#define CLK_OSC_12M 0
+#define CLK_PLL_CPUX 1
+#define CLK_PLL_AUDIO_BASE 2
+#define CLK_PLL_AUDIO 3
+#define CLK_PLL_AUDIO_2X 4
+#define CLK_PLL_AUDIO_4X 5
+#define CLK_PLL_AUDIO_8X 6
+#define CLK_PLL_VIDEO0 7
+#define CLK_PLL_VIDEO0_2X 8
+#define CLK_PLL_VE 9
+#define CLK_PLL_DDR0 10
+#define CLK_PLL_PERIPH0_2X 12
+#define CLK_PLL_PERIPH1 13
+#define CLK_PLL_PERIPH1_2X 14
+#define CLK_PLL_VIDEO1 15
+#define CLK_PLL_GPU 16
+#define CLK_PLL_MIPI 17
+#define CLK_PLL_HSIC 18
+#define CLK_PLL_DE 19
+#define CLK_PLL_DDR1 20
+#define CLK_CPUX 21
+#define CLK_AXI 22
+#define CLK_APB 23
+#define CLK_AHB1 24
+#define CLK_APB1 25
+#define CLK_APB2 26
+#define CLK_AHB2 27
+#define CLK_DRAM 94
+
+#define CLK_MBUS 112
+
+static struct aw_ccung_reset a64_ccu_resets[] = {
+ CCU_RESET(RST_USB_PHY0, 0x0cc, 0)
+ CCU_RESET(RST_USB_PHY1, 0x0cc, 1)
+ CCU_RESET(RST_USB_HSIC, 0x0cc, 2)
+
+ CCU_RESET(RST_BUS_MIPI_DSI, 0x2c0, 1)
+ CCU_RESET(RST_BUS_CE, 0x2c0, 5)
+ CCU_RESET(RST_BUS_DMA, 0x2c0, 6)
+ CCU_RESET(RST_BUS_MMC0, 0x2c0, 8)
+ CCU_RESET(RST_BUS_MMC1, 0x2c0, 9)
+ CCU_RESET(RST_BUS_MMC2, 0x2c0, 10)
+ CCU_RESET(RST_BUS_NAND, 0x2c0, 13)
+ CCU_RESET(RST_BUS_DRAM, 0x2c0, 14)
+ CCU_RESET(RST_BUS_EMAC, 0x2c0, 17)
+ CCU_RESET(RST_BUS_TS, 0x2c0, 18)
+ CCU_RESET(RST_BUS_HSTIMER, 0x2c0, 19)
+ CCU_RESET(RST_BUS_SPI0, 0x2c0, 20)
+ CCU_RESET(RST_BUS_SPI1, 0x2c0, 21)
+ CCU_RESET(RST_BUS_OTG, 0x2c0, 23)
+ CCU_RESET(RST_BUS_EHCI0, 0x2c0, 24)
+ CCU_RESET(RST_BUS_EHCI1, 0x2c0, 25)
+ CCU_RESET(RST_BUS_OHCI0, 0x2c0, 28)
+ CCU_RESET(RST_BUS_OHCI1, 0x2c0, 29)
+
+ CCU_RESET(RST_BUS_VE, 0x2c4, 0)
+ CCU_RESET(RST_BUS_TCON0, 0x2c4, 3)
+ CCU_RESET(RST_BUS_TCON1, 0x2c4, 4)
+ CCU_RESET(RST_BUS_DEINTERLACE, 0x2c4, 5)
+ CCU_RESET(RST_BUS_CSI, 0x2c4, 8)
+ CCU_RESET(RST_BUS_HDMI0, 0x2c4, 10)
+ CCU_RESET(RST_BUS_HDMI1, 0x2c4, 11)
+ CCU_RESET(RST_BUS_DE, 0x2c4, 12)
+ CCU_RESET(RST_BUS_GPU, 0x2c4, 20)
+ CCU_RESET(RST_BUS_MSGBOX, 0x2c4, 21)
+ CCU_RESET(RST_BUS_SPINLOCK, 0x2c4, 22)
+ CCU_RESET(RST_BUS_DBG, 0x2c4, 31)
+
+ CCU_RESET(RST_BUS_LVDS, 0x2C8, 31)
+
+ CCU_RESET(RST_BUS_CODEC, 0x2D0, 0)
+ CCU_RESET(RST_BUS_SPDIF, 0x2D0, 1)
+ CCU_RESET(RST_BUS_THS, 0x2D0, 8)
+ CCU_RESET(RST_BUS_I2S0, 0x2D0, 12)
+ CCU_RESET(RST_BUS_I2S1, 0x2D0, 13)
+ CCU_RESET(RST_BUS_I2S2, 0x2D0, 14)
+
+ CCU_RESET(RST_BUS_I2C0, 0x2D8, 0)
+ CCU_RESET(RST_BUS_I2C1, 0x2D8, 1)
+ CCU_RESET(RST_BUS_I2C2, 0x2D8, 2)
+ CCU_RESET(RST_BUS_SCR, 0x2D8, 5)
+ CCU_RESET(RST_BUS_UART0, 0x2D8, 16)
+ CCU_RESET(RST_BUS_UART1, 0x2D8, 17)
+ CCU_RESET(RST_BUS_UART2, 0x2D8, 18)
+ CCU_RESET(RST_BUS_UART3, 0x2D8, 19)
+ CCU_RESET(RST_BUS_UART4, 0x2D8, 20)
+};
+
+static struct aw_ccung_gate a64_ccu_gates[] = {
+ CCU_GATE(CLK_BUS_MIPI_DSI, "bus-mipi-dsi", "ahb1", 0x60, 1)
+ CCU_GATE(CLK_BUS_CE, "bus-ce", "ahb1", 0x60, 5)
+ CCU_GATE(CLK_BUS_DMA, "bus-dma", "ahb1", 0x60, 6)
+ CCU_GATE(CLK_BUS_MMC0, "bus-mmc0", "ahb1", 0x60, 8)
+ CCU_GATE(CLK_BUS_MMC1, "bus-mmc1", "ahb1", 0x60, 9)
+ CCU_GATE(CLK_BUS_MMC2, "bus-mmc2", "ahb1", 0x60, 10)
+ CCU_GATE(CLK_BUS_NAND, "bus-nand", "ahb1", 0x60, 13)
+ CCU_GATE(CLK_BUS_DRAM, "bus-dram", "ahb1", 0x60, 14)
+ CCU_GATE(CLK_BUS_EMAC, "bus-emac", "ahb2", 0x60, 16)
+ CCU_GATE(CLK_BUS_TS, "bus-ts", "ahb1", 0x60, 18)
+ CCU_GATE(CLK_BUS_HSTIMER, "bus-hstimer", "ahb1", 0x60, 19)
+ CCU_GATE(CLK_BUS_SPI0, "bus-spi0", "ahb1", 0x60, 20)
+ CCU_GATE(CLK_BUS_SPI1, "bus-spi1", "ahb1", 0x60, 21)
+ CCU_GATE(CLK_BUS_OTG, "bus-otg", "ahb1", 0x60, 23)
+ CCU_GATE(CLK_BUS_EHCI0, "bus-ehci0", "ahb1", 0x60, 24)
+ CCU_GATE(CLK_BUS_EHCI1, "bus-ehci1", "ahb2", 0x60, 25)
+ CCU_GATE(CLK_BUS_OHCI0, "bus-ohci0", "ahb1", 0x60, 28)
+ CCU_GATE(CLK_BUS_OHCI1, "bus-ohci1", "ahb2", 0x60, 29)
+
+ CCU_GATE(CLK_BUS_VE, "bus-ve", "ahb1", 0x64, 0)
+ CCU_GATE(CLK_BUS_TCON0, "bus-tcon0", "ahb1", 0x64, 3)
+ CCU_GATE(CLK_BUS_TCON1, "bus-tcon1", "ahb1", 0x64, 4)
+ CCU_GATE(CLK_BUS_DEINTERLACE, "bus-deinterlace", "ahb1", 0x64, 5)
+ CCU_GATE(CLK_BUS_CSI, "bus-csi", "ahb1", 0x64, 8)
+ CCU_GATE(CLK_BUS_HDMI, "bus-hdmi", "ahb1", 0x64, 11)
+ CCU_GATE(CLK_BUS_DE, "bus-de", "ahb1", 0x64, 12)
+ CCU_GATE(CLK_BUS_GPU, "bus-gpu", "ahb1", 0x64, 20)
+ CCU_GATE(CLK_BUS_MSGBOX, "bus-msgbox", "ahb1", 0x64, 21)
+ CCU_GATE(CLK_BUS_SPINLOCK, "bus-spinlock", "ahb1", 0x64, 22)
+
+ CCU_GATE(CLK_BUS_CODEC, "bus-codec", "apb1", 0x68, 0)
+ CCU_GATE(CLK_BUS_SPDIF, "bus-spdif", "apb1", 0x68, 1)
+ CCU_GATE(CLK_BUS_PIO, "bus-pio", "apb1", 0x68, 5)
+ CCU_GATE(CLK_BUS_THS, "bus-ths", "apb1", 0x68, 8)
+ CCU_GATE(CLK_BUS_I2S0, "bus-i2s0", "apb1", 0x68, 12)
+ CCU_GATE(CLK_BUS_I2S1, "bus-i2s1", "apb1", 0x68, 13)
+ CCU_GATE(CLK_BUS_I2S2, "bus-i2s2", "apb1", 0x68, 14)
+
+ CCU_GATE(CLK_BUS_I2C0, "bus-i2c0", "apb2", 0x6C, 0)
+ CCU_GATE(CLK_BUS_I2C1, "bus-i2c1", "apb2", 0x6C, 1)
+ CCU_GATE(CLK_BUS_I2C2, "bus-i2c2", "apb2", 0x6C, 2)
+ CCU_GATE(CLK_BUS_SCR, "bus-src", "apb2", 0x6C, 5)
+ CCU_GATE(CLK_BUS_UART0, "bus-uart0", "apb2", 0x6C, 16)
+ CCU_GATE(CLK_BUS_UART1, "bus-uart1", "apb2", 0x6C, 17)
+ CCU_GATE(CLK_BUS_UART2, "bus-uart2", "apb2", 0x6C, 18)
+ CCU_GATE(CLK_BUS_UART3, "bus-uart3", "apb2", 0x6C, 19)
+ CCU_GATE(CLK_BUS_UART4, "bus-uart4", "apb2", 0x6C, 20)
+
+ CCU_GATE(CLK_BUS_DBG, "bus-dbg", "ahb1", 0x70, 7)
+
+ CCU_GATE(CLK_THS, "ths", "thsdiv", 0x74, 31)
+
+ CCU_GATE(CLK_USB_PHY0, "usb-phy0", "osc24M", 0xcc, 8)
+ CCU_GATE(CLK_USB_PHY1, "usb-phy1", "osc24M", 0xcc, 9)
+ CCU_GATE(CLK_USB_HSIC, "usb-hsic", "pll_hsic", 0xcc, 10)
+ CCU_GATE(CLK_USB_HSIC_12M, "usb-hsic-12M", "osc12M", 0xcc, 11)
+ CCU_GATE(CLK_USB_OHCI0, "usb-ohci0", "osc12M", 0xcc, 16)
+ CCU_GATE(CLK_USB_OHCI1, "usb-ohci1", "usb-ohci0", 0xcc, 17)
+
+ CCU_GATE(CLK_DRAM_VE, "dram-ve", "dram", 0x100, 0)
+ CCU_GATE(CLK_DRAM_CSI, "dram-csi", "dram", 0x100, 1)
+ CCU_GATE(CLK_DRAM_DEINTERLACE, "dram-deinterlace", "dram", 0x100, 2)
+ CCU_GATE(CLK_DRAM_TS, "dram-ts", "dram", 0x100, 3)
+
+ CCU_GATE(CLK_CSI_MISC, "csi-misc", "osc24M", 0x130, 31)
+
+ CCU_GATE(CLK_AC_DIG_4X, "ac-dig-4x", "pll_audio-4x", 0x140, 30)
+ CCU_GATE(CLK_AC_DIG, "ac-dig", "pll_audio", 0x140, 31)
+
+ CCU_GATE(CLK_AVS, "avs", "osc24M", 0x144, 31)
+
+ CCU_GATE(CLK_HDMI_DDC, "hdmi-ddc", "osc24M", 0x154, 31)
+};
+
+static const char *osc12m_parents[] = {"osc24M"};
+FIXED_CLK(osc12m_clk,
+ CLK_OSC_12M, /* id */
+ "osc12M", /* name */
+ osc12m_parents, /* parent */
+ 0, /* freq */
+ 1, /* mult */
+ 2, /* div */
+ 0); /* flags */
+
+static const char *pll_cpux_parents[] = {"osc24M"};
+NKMP_CLK(pll_cpux_clk,
+ CLK_PLL_CPUX, /* id */
+ "pll_cpux", pll_cpux_parents, /* name, parents */
+ 0x00, /* offset */
+ 8, 5, 0, 0, /* n factor */
+ 4, 2, 0, 0, /* k factor */
+ 0, 2, 0, 0, /* m factor */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* p factor */
+ 31, /* gate */
+ 28, 1000, /* lock */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_LOCK | AW_CLK_SCALE_CHANGE); /* flags */
+
+static const char *pll_audio_parents[] = {"osc24M"};
+NKMP_CLK(pll_audio_clk,
+ CLK_PLL_AUDIO, /* id */
+ "pll_audio", pll_audio_parents, /* name, parents */
+ 0x08, /* offset */
+ 8, 7, 0, 0, /* n factor */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* k factor (fake) */
+ 0, 5, 0, 0, /* m factor */
+ 16, 4, 0, 0, /* p factor */
+ 31, /* gate */
+ 28, 1000, /* lock */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_LOCK); /* flags */
+
+static const char *pll_audio_mult_parents[] = {"pll_audio"};
+FIXED_CLK(pll_audio_2x_clk,
+ CLK_PLL_AUDIO_2X, /* id */
+ "pll_audio-2x", /* name */
+ pll_audio_mult_parents, /* parent */
+ 0, /* freq */
+ 2, /* mult */
+ 1, /* div */
+ 0); /* flags */
+FIXED_CLK(pll_audio_4x_clk,
+ CLK_PLL_AUDIO_4X, /* id */
+ "pll_audio-4x", /* name */
+ pll_audio_mult_parents, /* parent */
+ 0, /* freq */
+ 4, /* mult */
+ 1, /* div */
+ 0); /* flags */
+FIXED_CLK(pll_audio_8x_clk,
+ CLK_PLL_AUDIO_8X, /* id */
+ "pll_audio-8x", /* name */
+ pll_audio_mult_parents, /* parent */
+ 0, /* freq */
+ 8, /* mult */
+ 1, /* div */
+ 0); /* flags */
+
+static const char *pll_video0_parents[] = {"osc24M"};
+FRAC_CLK(pll_video0_clk,
+ CLK_PLL_VIDEO0, /* id */
+ "pll_video0", pll_video0_parents, /* name, parents */
+ 0x10, /* offset */
+ 8, 7, 0, 0, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 31, 28, 1000, /* gate, lock, lock retries */
+ AW_CLK_HAS_LOCK, /* flags */
+ 270000000, 297000000, /* freq0, freq1 */
+ 24, 25, /* mode sel, freq sel */
+ 192000000, 600000000); /* min freq, max freq */
+static const char *pll_video0_2x_parents[] = {"pll_video0"};
+FIXED_CLK(pll_video0_2x_clk,
+ CLK_PLL_VIDEO0_2X, /* id */
+ "pll_video0-2x", /* name */
+ pll_video0_2x_parents, /* parent */
+ 0, /* freq */
+ 2, /* mult */
+ 1, /* div */
+ 0); /* flags */
+
+static const char *pll_ve_parents[] = {"osc24M"};
+FRAC_CLK(pll_ve_clk,
+ CLK_PLL_VE, /* id */
+ "pll_ve", pll_ve_parents, /* name, parents */
+ 0x18, /* offset */
+ 8, 7, 0, 0, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 31, 28, 1000, /* gate, lock, lock retries */
+ AW_CLK_HAS_LOCK, /* flags */
+ 270000000, 297000000, /* freq0, freq1 */
+ 24, 25, /* mode sel, freq sel */
+ 192000000, 600000000); /* min freq, max freq */
+
+static const char *pll_ddr0_parents[] = {"osc24M"};
+NKMP_CLK_WITH_UPDATE(pll_ddr0_clk,
+ CLK_PLL_DDR0, /* id */
+ "pll_ddr0", pll_ddr0_parents, /* name, parents */
+ 0x20, /* offset */
+ 8, 5, 0, 0, /* n factor */
+ 4, 2, 0, 0, /* k factor */
+ 0, 2, 0, 0, /* m factor */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* p factor (fake) */
+ 31, /* gate */
+ 28, 1000, /* lock */
+ 20, /* update */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_LOCK); /* flags */
+
+static const char *pll_periph0_2x_parents[] = {"osc24M"};
+static const char *pll_periph0_parents[] = {"pll_periph0_2x"};
+NKMP_CLK(pll_periph0_2x_clk,
+ CLK_PLL_PERIPH0_2X, /* id */
+ "pll_periph0_2x", pll_periph0_2x_parents, /* name, parents */
+ 0x28, /* offset */
+ 8, 5, 0, 0, /* n factor */
+ 4, 2, 0, 0, /* k factor */
+ 0, 0, 2, AW_CLK_FACTOR_FIXED, /* m factor (fake) */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* p factor (fake) */
+ 31, /* gate */
+ 28, 1000, /* lock */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_LOCK); /* flags */
+FIXED_CLK(pll_periph0_clk,
+ CLK_PLL_PERIPH0, /* id */
+ "pll_periph0", /* name */
+ pll_periph0_parents, /* parent */
+ 0, /* freq */
+ 1, /* mult */
+ 2, /* div */
+ 0); /* flags */
+
+static const char *pll_periph1_2x_parents[] = {"osc24M"};
+static const char *pll_periph1_parents[] = {"pll_periph1_2x"};
+NKMP_CLK(pll_periph1_2x_clk,
+ CLK_PLL_PERIPH1_2X, /* id */
+ "pll_periph1_2x", pll_periph1_2x_parents, /* name, parents */
+ 0x2C, /* offset */
+ 8, 5, 0, 0, /* n factor */
+ 4, 2, 0, 0, /* k factor */
+ 0, 0, 2, AW_CLK_FACTOR_FIXED, /* m factor (fake) */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* p factor (fake) */
+ 31, /* gate */
+ 28, 1000, /* lock */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_LOCK); /* flags */
+FIXED_CLK(pll_periph1_clk,
+ CLK_PLL_PERIPH1, /* id */
+ "pll_periph1", /* name */
+ pll_periph1_parents, /* parent */
+ 0, /* freq */
+ 1, /* mult */
+ 2, /* div */
+ 0); /* flags */
+
+static const char *pll_video1_parents[] = {"osc24M"};
+FRAC_CLK(pll_video1_clk,
+ CLK_PLL_VIDEO1, /* id */
+ "pll_video1", pll_video1_parents, /* name, parents */
+ 0x30, /* offset */
+ 8, 7, 0, 0, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 31, 28, 1000, /* gate, lock, lock retries */
+ AW_CLK_HAS_LOCK, /* flags */
+ 270000000, 297000000, /* freq0, freq1 */
+ 24, 25, /* mode sel, freq sel */
+ 192000000, 600000000); /* min freq, max freq */
+
+static const char *pll_gpu_parents[] = {"osc24M"};
+FRAC_CLK(pll_gpu_clk,
+ CLK_PLL_GPU, /* id */
+ "pll_gpu", pll_gpu_parents, /* name, parents */
+ 0x38, /* offset */
+ 8, 7, 0, 0, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 31, 28, 1000, /* gate, lock, lock retries */
+ AW_CLK_HAS_LOCK, /* flags */
+ 270000000, 297000000, /* freq0, freq1 */
+ 24, 25, /* mode sel, freq sel */
+ 192000000, 600000000); /* min freq, max freq */
+
+static const char *pll_mipi_parents[] = {"pll_video0"};
+MIPI_CLK(pll_mipi_clk,
+ CLK_PLL_MIPI,
+ "pll_mipi", pll_mipi_parents,
+ 0x40,
+ 4, 2, AW_CLK_FACTOR_MIN_VALUE, 2,
+ 0, 3,
+ 8, 4,
+ 31, 28);
+
+static const char *pll_hsic_parents[] = {"osc24M"};
+FRAC_CLK(pll_hsic_clk,
+ CLK_PLL_HSIC, /* id */
+ "pll_hsic", pll_hsic_parents, /* name, parents */
+ 0x44, /* offset */
+ 8, 7, 0, 0, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 31, 28, 1000, /* gate, lock, lock retries */
+ AW_CLK_HAS_LOCK, /* flags */
+ 270000000, 297000000, /* freq0, freq1 */
+ 24, 25, /* mode sel, freq sel */
+ 192000000, 600000000); /* min freq, max freq */
+
+static const char *pll_de_parents[] = {"osc24M"};
+FRAC_CLK(pll_de_clk,
+ CLK_PLL_DE, /* id */
+ "pll_de", pll_de_parents, /* name, parents */
+ 0x48, /* offset */
+ 8, 7, 0, 0, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 31, 28, 1000, /* gate, lock, lock retries */
+ AW_CLK_HAS_LOCK, /* flags */
+ 270000000, 297000000, /* freq0, freq1 */
+ 24, 25, /* mode sel, freq sel */
+ 192000000, 600000000); /* min freq, max freq */
+
+static const char *pll_ddr1_parents[] = {"osc24M"};
+NKMP_CLK_WITH_UPDATE(pll_ddr1_clk,
+ CLK_PLL_DDR1, /* id */
+ "pll_ddr1", pll_ddr1_parents, /* name, parents */
+ 0x4C, /* offset */
+ 8, 7, 0, 0, /* n factor */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* k factor (fake) */
+ 0, 2, 0, 0, /* m factor */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* p factor (fake) */
+ 31, /* gate */
+ 28, 1000, /* lock */
+ 20, /* update */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_LOCK); /* flags */
+
+static const char *cpux_parents[] = {"osc32k", "osc24M", "pll_cpux"};
+MUX_CLK(cpux_clk,
+ CLK_CPUX, /* id */
+ "cpux", cpux_parents, /* name, parents */
+ 0x50, 16, 2); /* offset, shift, width */
+
+static const char *axi_parents[] = {"cpux"};
+DIV_CLK(axi_clk,
+ CLK_AXI, /* id */
+ "axi", axi_parents, /* name, parents */
+ 0x50, /* offset */
+ 0, 2, /* shift, width */
+ 0, NULL); /* flags, div table */
+
+static const char *apb_parents[] = {"cpux"};
+DIV_CLK(apb_clk,
+ CLK_APB, /* id */
+ "apb", apb_parents, /* name, parents */
+ 0x50, /* offset */
+ 8, 2, /* shift, width */
+ 0, NULL); /* flags, div table */
+
+static const char *ahb1_parents[] = {"osc32k", "osc24M", "axi", "pll_periph0"};
+PREDIV_CLK(ahb1_clk, CLK_AHB1, /* id */
+ "ahb1", ahb1_parents, /* name, parents */
+ 0x54, /* offset */
+ 12, 2, /* mux */
+ 4, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* div */
+ 6, 2, 0, AW_CLK_FACTOR_HAS_COND, /* prediv */
+ 12, 2, 3); /* prediv condition */
+
+static const char *apb1_parents[] = {"ahb1"};
+static struct clk_div_table apb1_div_table[] = {
+ { .value = 0, .divider = 2, },
+ { .value = 1, .divider = 2, },
+ { .value = 2, .divider = 4, },
+ { .value = 3, .divider = 8, },
+ { },
+};
+DIV_CLK(apb1_clk,
+ CLK_APB1, /* id */
+ "apb1", apb1_parents, /* name, parents */
+ 0x54, /* offset */
+ 8, 2, /* shift, width */
+ CLK_DIV_WITH_TABLE, /* flags */
+ apb1_div_table); /* div table */
+
+static const char *apb2_parents[] = {"osc32k", "osc24M", "pll_periph0_2x", "pll_periph0_2x"};
+NM_CLK(apb2_clk,
+ CLK_APB2, /* id */
+ "apb2", apb2_parents, /* name, parents */
+ 0x58, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 5, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 0, /* gate */
+ AW_CLK_HAS_MUX);
+
+static const char *ahb2_parents[] = {"ahb1", "pll_periph0"};
+PREDIV_CLK(ahb2_clk, CLK_AHB2, /* id */
+ "ahb2", ahb2_parents, /* name, parents */
+ 0x5c, /* offset */
+ 0, 2, /* mux */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* div */
+ 0, 0, 2, AW_CLK_FACTOR_HAS_COND | AW_CLK_FACTOR_FIXED, /* prediv */
+ 0, 2, 1); /* prediv condition */
+
+static const char *ths_parents[] = {"osc24M"};
+static struct clk_div_table ths_div_table[] = {
+ { .value = 0, .divider = 1, },
+ { .value = 1, .divider = 2, },
+ { .value = 2, .divider = 4, },
+ { .value = 3, .divider = 6, },
+ { },
+};
+DIV_CLK(ths_clk,
+ 0, /* id */
+ "thsdiv", ths_parents, /* name, parents */
+ 0x74, /* offset */
+ 0, 2, /* div shift, div width */
+ CLK_DIV_WITH_TABLE, /* flags */
+ ths_div_table); /* div table */
+
+static const char *mod_parents[] = {"osc24M", "pll_periph0_2x", "pll_periph1_2x"};
+NM_CLK(nand_clk,
+ CLK_NAND, "nand", mod_parents, /* id, name, parents */
+ 0x80, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX); /* flags */
+
+NM_CLK(mmc0_clk,
+ CLK_MMC0, "mmc0", mod_parents, /* id, name, parents */
+ 0x88, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX |
+ AW_CLK_REPARENT); /* flags */
+
+NM_CLK(mmc1_clk,
+ CLK_MMC1, "mmc1", mod_parents, /* id, name, parents */
+ 0x8c, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX |
+ AW_CLK_REPARENT); /* flags */
+
+NM_CLK(mmc2_clk,
+ CLK_MMC2, "mmc2", mod_parents, /* id, name, parents */
+ 0x90, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX |
+ AW_CLK_REPARENT); /* flags */
+
+static const char *ts_parents[] = {"osc24M", "pll_periph0"};
+NM_CLK(ts_clk,
+ CLK_TS, "ts", ts_parents, /* id, name, parents */
+ 0x98, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX); /* flags */
+
+NM_CLK(ce_clk,
+ CLK_CE, "ce", mod_parents, /* id, name, parents */
+ 0x9C, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX); /* flags */
+
+NM_CLK(spi0_clk,
+ CLK_SPI0, "spi0", mod_parents, /* id, name, parents */
+ 0xA0, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX |
+ AW_CLK_REPARENT); /* flags */
+
+NM_CLK(spi1_clk,
+ CLK_SPI1, "spi1", mod_parents, /* id, name, parents */
+ 0xA4, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX |
+ AW_CLK_REPARENT); /* flags */
+
+static const char *i2s_parents[] = {"pll_audio-8x", "pll_audio-4x", "pll_audio-2x", "pll_audio"};
+MUX_CLK(i2s0mux_clk,
+ 0, "i2s0mux", i2s_parents, /* id, name, parents */
+ 0xb0, 16, 2); /* offset, mux shift, mux width */
+MUX_CLK(i2s1mux_clk,
+ 0, "i2s1mux", i2s_parents, /* id, name, parents */
+ 0xb4, 16, 2); /* offset, mux shift, mux width */
+MUX_CLK(i2s2mux_clk,
+ 0, "i2s2mux", i2s_parents, /* id, name, parents */
+ 0xb8, 16, 2); /* offset, mux shift, mux width */
+
+static const char *spdif_parents[] = {"pll_audio"};
+M_CLK(spdif_clk,
+ CLK_SPDIF, "spdif", spdif_parents, /* id, name, parents */
+ 0xC0, /* offset */
+ 0, 4, 0, 0, /* m factor */
+ 0, 0, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE); /* flags */
+
+/* USBPHY clk sel */
+
+/* DRAM needs update bit */
+static const char *dram_parents[] = {"pll_ddr0", "pll_ddr1"};
+M_CLK(dram_clk,
+ CLK_DRAM, "dram", dram_parents, /* id, name, parents */
+ 0xF4, /* offset */
+ 0, 2, 0, 0, /* m factor */
+ 20, 2, /* mux */
+ 0, /* gate */
+ AW_CLK_HAS_MUX); /* flags */
+
+static const char *de_parents[] = {"pll_periph0_2x", "pll_de"};
+M_CLK(de_clk,
+ CLK_DE, "de", de_parents, /* id, name, parents */
+ 0x104, /* offset */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+static const char *tcon0_parents[] = {"pll_mipi", NULL, "pll_video0-2x"};
+MUX_CLK(tcon0_clk,
+ CLK_TCON0, /* id */
+ "tcon0", tcon0_parents, /* name, parents */
+ 0x118, 24, 2); /* offset, shift, width */
+
+static const char *tcon1_parents[] = {"pll_video0", NULL, "pll_video1"};
+M_CLK(tcon1_clk,
+ CLK_TCON1, "tcon1", tcon1_parents, /* id, name, parents */
+ 0x11C, /* offset */
+ 0, 5, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE |
+ AW_CLK_SET_PARENT); /* flags */
+
+static const char *deinterlace_parents[] = {"pll_periph0", "pll_periph1"};
+M_CLK(deinterlace_clk,
+ CLK_DEINTERLACE, "deinterlace", deinterlace_parents, /* id, name, parents */
+ 0x124, /* offset */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+static const char *csi_sclk_parents[] = {"pll_periph0", "pll_periph1"};
+M_CLK(csi_sclk_clk,
+ CLK_CSI_SCLK, "csi-sclk", csi_sclk_parents, /* id, name, parents */
+ 0x134, /* offset */
+ 16, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+static const char *csi_mclk_parents[] = {"osc24M", "pll_video0", "pll_periph1"};
+M_CLK(csi_mclk_clk,
+ CLK_CSI_MCLK, "csi-mclk", csi_mclk_parents, /* id, name, parents */
+ 0x134, /* offset */
+ 0, 4, 0, 0, /* m factor */
+ 8, 2, /* mux */
+ 15, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+static const char *ve_parents[] = {"pll_ve"};
+M_CLK(ve_clk,
+ CLK_VE, "ve", ve_parents, /* id, name, parents */
+ 0x13C, /* offset */
+ 16, 3, 0, 0, /* m factor */
+ 0, 0, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE); /* flags */
+
+static const char *hdmi_parents[] = {"pll_video0"};
+M_CLK(hdmi_clk,
+ CLK_HDMI, "hdmi", hdmi_parents, /* id, name, parents */
+ 0x150, /* offset */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE | AW_CLK_SET_PARENT); /* flags */
+
+static const char *mbus_parents[] = {"osc24M", "pll_periph0_2x", "pll_ddr0"};
+M_CLK(mbus_clk,
+ CLK_MBUS, "mbus", mbus_parents, /* id, name, parents */
+ 0x15C, /* offset */
+ 0, 3, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+static const char *gpu_parents[] = {"pll_gpu"};
+M_CLK(gpu_clk,
+ CLK_GPU, "gpu", gpu_parents, /* id, name, parents */
+ 0x1A0, /* offset */
+ 0, 2, 0, 0, /* m factor */
+ 0, 0, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE); /* flags */
+
+static struct aw_ccung_clk a64_ccu_clks[] = {
+ { .type = AW_CLK_NKMP, .clk.nkmp = &pll_cpux_clk},
+ { .type = AW_CLK_NKMP, .clk.nkmp = &pll_audio_clk},
+ { .type = AW_CLK_FRAC, .clk.frac = &pll_video0_clk},
+ { .type = AW_CLK_FRAC, .clk.frac = &pll_ve_clk},
+ { .type = AW_CLK_NKMP, .clk.nkmp = &pll_ddr0_clk},
+ { .type = AW_CLK_NKMP, .clk.nkmp = &pll_periph0_2x_clk},
+ { .type = AW_CLK_NKMP, .clk.nkmp = &pll_periph1_2x_clk},
+ { .type = AW_CLK_FRAC, .clk.frac = &pll_video1_clk},
+ { .type = AW_CLK_FRAC, .clk.frac = &pll_gpu_clk},
+ { .type = AW_CLK_MIPI, .clk.mipi = &pll_mipi_clk},
+ { .type = AW_CLK_FRAC, .clk.frac = &pll_hsic_clk},
+ { .type = AW_CLK_FRAC, .clk.frac = &pll_de_clk},
+ { .type = AW_CLK_NKMP, .clk.nkmp = &pll_ddr1_clk},
+
+ { .type = AW_CLK_NM, .clk.nm = &apb2_clk},
+ { .type = AW_CLK_NM, .clk.nm = &nand_clk},
+ { .type = AW_CLK_NM, .clk.nm = &mmc0_clk},
+ { .type = AW_CLK_NM, .clk.nm = &mmc1_clk},
+ { .type = AW_CLK_NM, .clk.nm = &mmc2_clk},
+ { .type = AW_CLK_NM, .clk.nm = &ts_clk},
+ { .type = AW_CLK_NM, .clk.nm = &ce_clk},
+ { .type = AW_CLK_NM, .clk.nm = &spi0_clk},
+ { .type = AW_CLK_NM, .clk.nm = &spi1_clk},
+ { .type = AW_CLK_M, .clk.m = &spdif_clk},
+ { .type = AW_CLK_M, .clk.m = &dram_clk},
+ { .type = AW_CLK_M, .clk.m = &de_clk},
+ { .type = AW_CLK_M, .clk.m = &tcon1_clk},
+ { .type = AW_CLK_M, .clk.m = &deinterlace_clk},
+ { .type = AW_CLK_M, .clk.m = &csi_sclk_clk},
+ { .type = AW_CLK_M, .clk.m = &csi_mclk_clk},
+ { .type = AW_CLK_M, .clk.m = &ve_clk},
+ { .type = AW_CLK_M, .clk.m = &hdmi_clk},
+ { .type = AW_CLK_M, .clk.m = &mbus_clk},
+ { .type = AW_CLK_M, .clk.m = &gpu_clk},
+ { .type = AW_CLK_PREDIV_MUX, .clk.prediv_mux = &ahb1_clk},
+ { .type = AW_CLK_PREDIV_MUX, .clk.prediv_mux = &ahb2_clk},
+ { .type = AW_CLK_MUX, .clk.mux = &cpux_clk},
+ { .type = AW_CLK_MUX, .clk.mux = &i2s0mux_clk},
+ { .type = AW_CLK_MUX, .clk.mux = &i2s1mux_clk},
+ { .type = AW_CLK_MUX, .clk.mux = &i2s2mux_clk},
+ { .type = AW_CLK_MUX, .clk.mux = &tcon0_clk},
+ { .type = AW_CLK_DIV, .clk.div = &axi_clk},
+ { .type = AW_CLK_DIV, .clk.div = &apb1_clk},
+ { .type = AW_CLK_DIV, .clk.div = &apb_clk},
+ { .type = AW_CLK_DIV, .clk.div = &ths_clk},
+ { .type = AW_CLK_FIXED, .clk.fixed = &osc12m_clk},
+ { .type = AW_CLK_FIXED, .clk.fixed = &pll_periph0_clk},
+ { .type = AW_CLK_FIXED, .clk.fixed = &pll_periph1_clk},
+ { .type = AW_CLK_FIXED, .clk.fixed = &pll_audio_2x_clk},
+ { .type = AW_CLK_FIXED, .clk.fixed = &pll_audio_4x_clk},
+ { .type = AW_CLK_FIXED, .clk.fixed = &pll_audio_8x_clk},
+ { .type = AW_CLK_FIXED, .clk.fixed = &pll_video0_2x_clk},
+};
+
+static struct aw_clk_init a64_init_clks[] = {
+ {"ahb1", "pll_periph0", 0, false},
+ {"ahb2", "pll_periph0", 0, false},
+ {"dram", "pll_ddr0", 0, false},
+ {"pll_de", NULL, 432000000, true},
+ {"de", "pll_de", 0, true},
+};
+
+static int
+ccu_a64_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "allwinner,sun50i-a64-ccu"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner A64 Clock Control Unit NG");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ccu_a64_attach(device_t dev)
+{
+ struct aw_ccung_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ sc->resets = a64_ccu_resets;
+ sc->nresets = nitems(a64_ccu_resets);
+ sc->gates = a64_ccu_gates;
+ sc->ngates = nitems(a64_ccu_gates);
+ sc->clks = a64_ccu_clks;
+ sc->nclks = nitems(a64_ccu_clks);
+ sc->clk_init = a64_init_clks;
+ sc->n_clk_init = nitems(a64_init_clks);
+
+ return (aw_ccung_attach(dev));
+}
+
+static device_method_t ccu_a64ng_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ccu_a64_probe),
+ DEVMETHOD(device_attach, ccu_a64_attach),
+
+ DEVMETHOD_END
+};
+
+static devclass_t ccu_a64ng_devclass;
+
+DEFINE_CLASS_1(ccu_a64ng, ccu_a64ng_driver, ccu_a64ng_methods,
+ sizeof(struct aw_ccung_softc), aw_ccung_driver);
+
+EARLY_DRIVER_MODULE(ccu_a64ng, simplebus, ccu_a64ng_driver,
+ ccu_a64ng_devclass, 0, 0, BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE);
diff --git a/sys/arm/allwinner/clkng/ccu_a83t.c b/sys/arm/allwinner/clkng/ccu_a83t.c
new file mode 100644
index 000000000000..c32b1d624161
--- /dev/null
+++ b/sys/arm/allwinner/clkng/ccu_a83t.c
@@ -0,0 +1,788 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2017 Kyle Evans <kevans@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <machine/bus.h>
+
+#include <dev/fdt/simplebus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/extres/clk/clk_div.h>
+#include <dev/extres/clk/clk_fixed.h>
+#include <dev/extres/clk/clk_mux.h>
+
+#include <arm/allwinner/clkng/aw_ccung.h>
+
+#include <dt-bindings/clock/sun8i-a83t-ccu.h>
+#include <dt-bindings/reset/sun8i-a83t-ccu.h>
+
+/* Non-exported clocks */
+
+#define CLK_PLL_C0CPUX 0
+#define CLK_PLL_C1CPUX 1
+#define CLK_PLL_AUDIO 2
+#define CLK_PLL_VIDEO0 3
+#define CLK_PLL_VE 4
+#define CLK_PLL_DDR 5
+
+#define CLK_PLL_GPU 7
+#define CLK_PLL_HSIC 8
+#define CLK_PLL_VIDEO1 10
+
+#define CLK_AXI0 13
+#define CLK_AXI1 14
+#define CLK_AHB1 15
+#define CLK_APB1 16
+#define CLK_APB2 17
+#define CLK_AHB2 18
+
+#define CLK_CCI400 58
+
+#define CLK_DRAM 82
+
+#define CLK_MBUS 95
+
+/* Non-exported fixed clocks */
+#define CLK_OSC_12M 150
+
+static struct aw_ccung_reset a83t_ccu_resets[] = {
+ CCU_RESET(RST_USB_PHY0, 0xcc, 0)
+ CCU_RESET(RST_USB_PHY1, 0xcc, 1)
+ CCU_RESET(RST_USB_HSIC, 0xcc, 2)
+
+ CCU_RESET(RST_DRAM, 0xf4, 31)
+ CCU_RESET(RST_MBUS, 0xfc, 31)
+
+ CCU_RESET(RST_BUS_MIPI_DSI, 0x2c0, 1)
+ CCU_RESET(RST_BUS_SS, 0x2c0, 5)
+ CCU_RESET(RST_BUS_DMA, 0x2c0, 6)
+ CCU_RESET(RST_BUS_MMC0, 0x2c0, 8)
+ CCU_RESET(RST_BUS_MMC1, 0x2c0, 9)
+ CCU_RESET(RST_BUS_MMC2, 0x2c0, 10)
+ CCU_RESET(RST_BUS_NAND, 0x2c0, 13)
+ CCU_RESET(RST_BUS_DRAM, 0x2c0, 14)
+ CCU_RESET(RST_BUS_EMAC, 0x2c0, 17)
+ CCU_RESET(RST_BUS_HSTIMER, 0x2c0, 19)
+ CCU_RESET(RST_BUS_SPI0, 0x2c0, 20)
+ CCU_RESET(RST_BUS_SPI1, 0x2c0, 21)
+ CCU_RESET(RST_BUS_OTG, 0x2c0, 24)
+ CCU_RESET(RST_BUS_EHCI0, 0x2c0, 26)
+ CCU_RESET(RST_BUS_EHCI1, 0x2c0, 27)
+ CCU_RESET(RST_BUS_OHCI0, 0x2c0, 29)
+
+ CCU_RESET(RST_BUS_VE, 0x2c4, 0)
+ CCU_RESET(RST_BUS_TCON0, 0x2c4, 4)
+ CCU_RESET(RST_BUS_TCON1, 0x2c4, 5)
+ CCU_RESET(RST_BUS_CSI, 0x2c4, 8)
+ CCU_RESET(RST_BUS_HDMI0, 0x2c4, 10)
+ CCU_RESET(RST_BUS_HDMI1, 0x2c4, 11)
+ CCU_RESET(RST_BUS_DE, 0x2c4, 12)
+ CCU_RESET(RST_BUS_GPU, 0x2c4, 20)
+ CCU_RESET(RST_BUS_MSGBOX, 0x2c4, 21)
+ CCU_RESET(RST_BUS_SPINLOCK, 0x2c4, 22)
+
+ CCU_RESET(RST_BUS_LVDS, 0x2c8, 0)
+
+ CCU_RESET(RST_BUS_SPDIF, 0x2d0, 1)
+ CCU_RESET(RST_BUS_I2S0, 0x2d0, 12)
+ CCU_RESET(RST_BUS_I2S1, 0x2d0, 13)
+ CCU_RESET(RST_BUS_I2S2, 0x2d0, 14)
+ CCU_RESET(RST_BUS_TDM, 0x2d0, 15)
+
+ CCU_RESET(RST_BUS_I2C0, 0x2d8, 0)
+ CCU_RESET(RST_BUS_I2C1, 0x2d8, 1)
+ CCU_RESET(RST_BUS_I2C2, 0x2d8, 2)
+ CCU_RESET(RST_BUS_UART0, 0x2d8, 16)
+ CCU_RESET(RST_BUS_UART1, 0x2d8, 17)
+ CCU_RESET(RST_BUS_UART2, 0x2d8, 18)
+ CCU_RESET(RST_BUS_UART3, 0x2d8, 19)
+ CCU_RESET(RST_BUS_UART4, 0x2d8, 20)
+};
+
+static struct aw_ccung_gate a83t_ccu_gates[] = {
+ CCU_GATE(CLK_BUS_MIPI_DSI, "bus-mipi-dsi", "ahb1", 0x60, 1)
+ CCU_GATE(CLK_BUS_SS, "bus-ss", "ahb1", 0x60, 5)
+ CCU_GATE(CLK_BUS_DMA, "bus-dma", "ahb1", 0x60, 6)
+ CCU_GATE(CLK_BUS_MMC0, "bus-mmc0", "ahb1", 0x60, 8)
+ CCU_GATE(CLK_BUS_MMC1, "bus-mmc1", "ahb1", 0x60, 9)
+ CCU_GATE(CLK_BUS_MMC2, "bus-mmc2", "ahb1", 0x60, 10)
+ CCU_GATE(CLK_BUS_NAND, "bus-nand", "ahb1", 0x60, 13)
+ CCU_GATE(CLK_BUS_DRAM, "bus-dram", "ahb1", 0x60, 14)
+ CCU_GATE(CLK_BUS_EMAC, "bus-emac", "ahb1", 0x60, 17)
+ CCU_GATE(CLK_BUS_HSTIMER, "bus-hstimer", "ahb1", 0x60, 19)
+ CCU_GATE(CLK_BUS_SPI0, "bus-spi0", "ahb1", 0x60, 20)
+ CCU_GATE(CLK_BUS_SPI1, "bus-spi1", "ahb1", 0x60, 21)
+ CCU_GATE(CLK_BUS_OTG, "bus-otg", "ahb1", 0x60, 24)
+ CCU_GATE(CLK_BUS_EHCI0, "bus-ehci0", "ahb2", 0x60, 26)
+ CCU_GATE(CLK_BUS_EHCI1, "bus-ehci1", "ahb2", 0x60, 27)
+ CCU_GATE(CLK_BUS_OHCI0, "bus-ohci0", "ahb2", 0x60, 29)
+
+ CCU_GATE(CLK_BUS_VE, "bus-ve", "ahb1", 0x64, 0)
+ CCU_GATE(CLK_BUS_TCON0, "bus-tcon0", "ahb1", 0x64, 4)
+ CCU_GATE(CLK_BUS_TCON1, "bus-tcon1", "ahb1", 0x64, 5)
+ CCU_GATE(CLK_BUS_CSI, "bus-csi", "ahb1", 0x64, 8)
+ CCU_GATE(CLK_BUS_HDMI, "bus-hdmi", "ahb1", 0x64, 11)
+ CCU_GATE(CLK_BUS_DE, "bus-de", "ahb1", 0x64, 12)
+ CCU_GATE(CLK_BUS_GPU, "bus-gpu", "ahb1", 0x64, 20)
+ CCU_GATE(CLK_BUS_MSGBOX, "bus-msgbox", "ahb1", 0x64, 21)
+ CCU_GATE(CLK_BUS_SPINLOCK, "bus-spinlock", "ahb1", 0x64, 22)
+
+ CCU_GATE(CLK_BUS_SPDIF, "bus-spdif", "apb1", 0x68, 1)
+ CCU_GATE(CLK_BUS_PIO, "bus-pio", "apb1", 0x68, 5)
+ CCU_GATE(CLK_BUS_I2S0, "bus-i2s0", "apb1", 0x68, 12)
+ CCU_GATE(CLK_BUS_I2S1, "bus-i2s1", "apb1", 0x68, 13)
+ CCU_GATE(CLK_BUS_I2S2, "bus-i2s2", "apb1", 0x68, 14)
+ CCU_GATE(CLK_BUS_TDM, "bus-tdm", "apb1", 0x68, 15)
+
+ CCU_GATE(CLK_BUS_I2C0, "bus-i2c0", "apb2", 0x6c, 0)
+ CCU_GATE(CLK_BUS_I2C1, "bus-i2c1", "apb2", 0x6c, 1)
+ CCU_GATE(CLK_BUS_I2C2, "bus-i2c2", "apb2", 0x6c, 2)
+ CCU_GATE(CLK_BUS_UART0, "bus-uart0", "apb2", 0x6c, 16)
+ CCU_GATE(CLK_BUS_UART1, "bus-uart1", "apb2", 0x6c, 17)
+ CCU_GATE(CLK_BUS_UART2, "bus-uart2", "apb2", 0x6c, 18)
+ CCU_GATE(CLK_BUS_UART3, "bus-uart3", "apb2", 0x6c, 19)
+ CCU_GATE(CLK_BUS_UART4, "bus-uart4", "apb2", 0x6c, 20)
+
+ CCU_GATE(CLK_USB_PHY0, "usb-phy0", "osc24M", 0xcc, 8)
+ CCU_GATE(CLK_USB_PHY1, "usb-phy1", "osc24M", 0xcc, 9)
+ CCU_GATE(CLK_USB_HSIC, "usb-hsic", "pll_hsic", 0xcc, 10)
+ CCU_GATE(CLK_USB_HSIC_12M, "usb-hsic-12M", "osc12M", 0xcc, 11)
+ CCU_GATE(CLK_USB_OHCI0, "usb-ohci0", "osc12M", 0xcc, 16)
+
+ CCU_GATE(CLK_DRAM_VE, "dram-ve", "dram", 0x100, 0)
+ CCU_GATE(CLK_DRAM_CSI, "dram-csi", "dram", 0x100, 1)
+
+ CCU_GATE(CLK_CSI_MISC, "csi-misc", "osc24M", 0x130, 16)
+ CCU_GATE(CLK_MIPI_CSI, "mipi-csi", "osc24M", 0x130, 31)
+
+ CCU_GATE(CLK_AVS, "avs", "osc24M", 0x144, 31)
+
+ CCU_GATE(CLK_HDMI_SLOW, "hdmi-ddc", "osc24M", 0x154, 31)
+};
+
+static const char *osc12m_parents[] = {"osc24M"};
+FIXED_CLK(osc12m_clk,
+ CLK_OSC_12M, /* id */
+ "osc12M", osc12m_parents, /* name, parents */
+ 0, /* freq */
+ 1, /* mult */
+ 2, /* div */
+ 0); /* flags */
+
+/* CPU PLL are 24Mhz * N / P */
+static const char *pll_c0cpux_parents[] = {"osc24M"};
+static const char *pll_c1cpux_parents[] = {"osc24M"};
+NKMP_CLK(pll_c0cpux_clk,
+ CLK_PLL_C0CPUX, /* id */
+ "pll_c0cpux", pll_c0cpux_parents, /* name, parents */
+ 0x00, /* offset */
+ 8, 8, 0, AW_CLK_FACTOR_ZERO_BASED, /* n factor */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* k factor (fake) */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* m factor */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* p factor (fake) */
+ 0, 0, /* lock */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_SCALE_CHANGE); /* flags */
+NKMP_CLK(pll_c1cpux_clk,
+ CLK_PLL_C1CPUX, /* id */
+ "pll_c1cpux", pll_c1cpux_parents, /* name, parents */
+ 0x04, /* offset */
+ 8, 8, 0, AW_CLK_FACTOR_ZERO_BASED, /* n factor */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* k factor (fake) */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* m factor */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* p factor (fake) */
+ 0, 0, /* lock */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_SCALE_CHANGE); /* flags */
+
+static const char *pll_audio_parents[] = {"osc24M"};
+NKMP_CLK(pll_audio_clk,
+ CLK_PLL_AUDIO, /* id */
+ "pll_audio", pll_audio_parents, /* name, parents */
+ 0x08, /* offset */
+ 8, 8, 0, AW_CLK_FACTOR_ZERO_BASED, /* n factor */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* k factor (fake) */
+ 16, 1, 0, 0, /* m factor */
+ 18, 1, 0, 0, /* p factor */
+ 31, /* gate */
+ 0, 0, /* lock */
+ AW_CLK_HAS_GATE); /* flags */
+
+static const char *pll_video0_parents[] = {"osc24M"};
+NKMP_CLK(pll_video0_clk,
+ CLK_PLL_VIDEO0, /* id */
+ "pll_video0", pll_video0_parents, /* name, parents */
+ 0x10, /* offset */
+ 8, 8, 0, AW_CLK_FACTOR_ZERO_BASED, /* n factor */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* k factor (fake) */
+ 16, 1, 0, 0, /* m factor */
+ 0, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* p factor */
+ 31, /* gate */
+ 0, 0, /* lock */
+ AW_CLK_HAS_GATE); /* flags */
+
+static const char *pll_ve_parents[] = {"osc24M"};
+NKMP_CLK(pll_ve_clk,
+ CLK_PLL_VE, /* id */
+ "pll_ve", pll_ve_parents, /* name, parents */
+ 0x18, /* offset */
+ 8, 8, 0, AW_CLK_FACTOR_ZERO_BASED, /* n factor */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* k factor (fake) */
+ 16, 1, 0, 0, /* m factor */
+ 18, 1, 0, 0, /* p factor */
+ 31, /* gate */
+ 0, 0, /* lock */
+ AW_CLK_HAS_GATE); /* flags */
+
+static const char *pll_ddr_parents[] = {"osc24M"};
+NKMP_CLK(pll_ddr_clk,
+ CLK_PLL_DDR, /* id */
+ "pll_ddr", pll_ddr_parents, /* name, parents */
+ 0x20, /* offset */
+ 8, 5, 0, 0, /* n factor */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* k factor (fake) */
+ 16, 1, 0, 0, /* m factor */
+ 18, 1, 0, 0, /* p factor */
+ 31, /* gate */
+ 0, 0, /* lock */
+ AW_CLK_HAS_GATE); /* flags */
+
+static const char *pll_periph_parents[] = {"osc24M"};
+NKMP_CLK(pll_periph_clk,
+ CLK_PLL_PERIPH, /* id */
+ "pll_periph", pll_periph_parents, /* name, parents */
+ 0x28, /* offset */
+ 8, 8, 0, AW_CLK_FACTOR_ZERO_BASED, /* n factor */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* k factor (fake) */
+ 16, 1, 1, 0, /* m factor */
+ 18, 1, 1, 0, /* p factor */
+ 31, /* gate */
+ 0, 0, /* lock */
+ AW_CLK_HAS_GATE); /* flags */
+
+static const char *pll_gpu_parents[] = {"osc24M"};
+NKMP_CLK(pll_gpu_clk,
+ CLK_PLL_GPU, /* id */
+ "pll_gpu", pll_gpu_parents, /* name, parents */
+ 0x38, /* offset */
+ 8, 8, 0, AW_CLK_FACTOR_ZERO_BASED, /* n factor */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* k factor (fake) */
+ 16, 1, 1, 0, /* m factor */
+ 18, 1, 1, 0, /* p factor */
+ 31, /* gate */
+ 0, 0, /* lock */
+ AW_CLK_HAS_GATE); /* flags */
+
+static const char *pll_hsic_parents[] = {"osc24M"};
+NKMP_CLK(pll_hsic_clk,
+ CLK_PLL_HSIC, /* id */
+ "pll_hsic", pll_hsic_parents, /* name, parents */
+ 0x44, /* offset */
+ 8, 8, 0, AW_CLK_FACTOR_ZERO_BASED, /* n factor */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* k factor (fake) */
+ 16, 1, 1, 0, /* m factor */
+ 18, 1, 1, 0, /* p factor */
+ 31, /* gate */
+ 0, 0, /* lock */
+ AW_CLK_HAS_GATE); /* flags */
+
+static const char *pll_de_parents[] = {"osc24M"};
+NKMP_CLK(pll_de_clk,
+ CLK_PLL_DE, /* id */
+ "pll_de", pll_de_parents, /* name, parents */
+ 0x48, /* offset */
+ 8, 8, 0, AW_CLK_FACTOR_ZERO_BASED, /* n factor */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* k factor (fake) */
+ 16, 1, 1, 0, /* m factor */
+ 18, 1, 1, 0, /* p factor */
+ 31, /* gate */
+ 0, 0, /* lock */
+ AW_CLK_HAS_GATE); /* flags */
+
+static const char *pll_video1_parents[] = {"osc24M"};
+NKMP_CLK(pll_video1_clk,
+ CLK_PLL_VIDEO1, /* id */
+ "pll_video1", pll_video1_parents, /* name, parents */
+ 0x4c, /* offset */
+ 8, 8, 0, AW_CLK_FACTOR_ZERO_BASED, /* n factor */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* k factor (fake) */
+ 16, 1, 1, 0, /* m factor */
+ 0, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* p factor */
+ 31, /* gate */
+ 0, 0, /* lock */
+ AW_CLK_HAS_GATE); /* flags */
+
+static const char *c0cpux_parents[] = {"osc24M", "pll_c0cpux"};
+MUX_CLK(c0cpux_clk,
+ CLK_C0CPUX, /* id */
+ "c0cpux", c0cpux_parents, /* name, parents */
+ 0x50, 12, 1); /* offset, shift, width */
+
+static const char *c1cpux_parents[] = {"osc24M", "pll_c1cpux"};
+MUX_CLK(c1cpux_clk,
+ CLK_C1CPUX, /* id */
+ "c1cpux", c1cpux_parents, /* name, parents */
+ 0x50, 28, 1); /* offset, shift, width */
+
+static const char *axi0_parents[] = {"c0cpux"};
+DIV_CLK(axi0_clk,
+ CLK_AXI0, /* id */
+ "axi0", axi0_parents, /* name, parents */
+ 0x50, /* offset */
+ 0, 2, /* shift, width */
+ 0, NULL); /* flags, div table */
+
+static const char *axi1_parents[] = {"c1cpux"};
+DIV_CLK(axi1_clk,
+ CLK_AXI1, /* id */
+ "axi1", axi1_parents, /* name, parents */
+ 0x50, /* offset */
+ 16, 2, /* shift, width */
+ 0, NULL); /* flags, div table */
+
+static const char *ahb1_parents[] = {"osc16M-d512", "osc24M", "pll_periph", "pll_periph"};
+PREDIV_CLK_WITH_MASK(ahb1_clk,
+ CLK_AHB1, /* id */
+ "ahb1", ahb1_parents, /* name, parents */
+ 0x54, /* offset */
+ 12, 2, /* mux */
+ 4, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* div */
+ 6, 2, 0, AW_CLK_FACTOR_HAS_COND, /* prediv */
+ (2 << 12), (2 << 12)); /* prediv condition */
+
+static const char *apb1_parents[] = {"ahb1"};
+DIV_CLK(apb1_clk,
+ CLK_APB1, /* id */
+ "apb1", apb1_parents, /* name, parents */
+ 0x54, /* offset */
+ 8, 2, /* shift, width */
+ 0, NULL); /* flags, div table */
+
+static const char *apb2_parents[] = {"osc16M-d512", "osc24M", "pll_periph", "pll_periph"};
+NM_CLK(apb2_clk,
+ CLK_APB2, /* id */
+ "apb2", apb2_parents, /* name, parents */
+ 0x58, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 5, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 0, /* gate */
+ AW_CLK_HAS_MUX);
+
+static const char *ahb2_parents[] = {"ahb1", "pll_periph"};
+PREDIV_CLK(ahb2_clk,
+ CLK_AHB2, /* id */
+ "ahb2", ahb2_parents, /* name, parents */
+ 0x5c,
+ 0, 2, /* mux */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* div (fake) */
+ 0, 0, 2, AW_CLK_FACTOR_HAS_COND | AW_CLK_FACTOR_FIXED, /* prediv */
+ 0, 2, 1); /* prediv cond */
+
+/* Actually has a divider, but we don't use it */
+static const char *cci400_parents[] = {"osc24M", "pll_periph", "pll_hsic"};
+MUX_CLK(cci400_clk,
+ CLK_CCI400, /* id */
+ "cci400", cci400_parents, /* name, parents */
+ 0x78, 24, 2); /* offset, shift, width */
+
+static const char *mod_parents[] = {"osc24M", "pll_periph"};
+
+NM_CLK(nand_clk,
+ CLK_NAND, /* id */
+ "nand", mod_parents, /* name, parents */
+ 0x80, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX);
+
+NM_CLK(mmc0_clk,
+ CLK_MMC0, /* id */
+ "mmc0", mod_parents, /* name, parents */
+ 0x88, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX |
+ AW_CLK_REPARENT);
+NM_CLK(mmc1_clk,
+ CLK_MMC1, /* id */
+ "mmc1", mod_parents, /* name, parents */
+ 0x8c, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX |
+ AW_CLK_REPARENT);
+NM_CLK(mmc2_clk,
+ CLK_MMC2, /* id */
+ "mmc2", mod_parents, /* name, parents */
+ 0x90, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX |
+ AW_CLK_REPARENT);
+
+NM_CLK(ss_clk,
+ CLK_SS, /* id */
+ "ss", mod_parents, /* name, parents */
+ 0x9c, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX);
+
+NM_CLK(spi0_clk,
+ CLK_SPI0, /* id */
+ "spi0", mod_parents, /* name, parents */
+ 0xa0, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX);
+NM_CLK(spi1_clk,
+ CLK_SPI1, /* id */
+ "spi1", mod_parents, /* name, parents */
+ 0xa4, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX);
+
+static const char *daudio_parents[] = {"pll_audio"};
+NM_CLK(i2s0_clk,
+ CLK_I2S0, /* id */
+ "i2s0", daudio_parents, /* name, parents */
+ 0xb0, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 4, 0, 0, /* m factor */
+ 0, 0, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE);
+NM_CLK(i2s1_clk,
+ CLK_I2S1, /* id */
+ "i2s1", daudio_parents, /* name, parents */
+ 0xb4, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 4, 0, 0, /* m factor */
+ 0, 0, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE);
+NM_CLK(i2s2_clk,
+ CLK_I2S2, /* id */
+ "i2s2", daudio_parents, /* name, parents */
+ 0xb8, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 4, 0, 0, /* m factor */
+ 0, 0, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE);
+
+static const char *tdm_parents[] = {"pll_audio"};
+NM_CLK(tdm_clk,
+ CLK_TDM, /* id */
+ "tdm", tdm_parents, /* name, parents */
+ 0xbc, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 4, 0, 0, /* m factor */
+ 0, 0, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE);
+
+static const char *spdif_parents[] = {"pll_audio"};
+NM_CLK(spdif_clk,
+ CLK_SPDIF, /* id */
+ "spdif", spdif_parents, /* name, parents */
+ 0xc0, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 4, 0, 0, /* m factor */
+ 0, 0, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE);
+
+static const char *dram_parents[] = {"pll_ddr"};
+NM_CLK(dram_clk,
+ CLK_DRAM, /* id */
+ "dram", dram_parents, /* name, parents */
+ 0xf4, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 4, 0, 0, /* m factor */
+ 0, 0, /* mux */
+ 0, /* gate */
+ 0);
+
+static const char *tcon0_parents[] = {"pll_video0"};
+MUX_CLK(tcon0_clk,
+ CLK_TCON0, /* id */
+ "tcon0", tcon0_parents, /* name, parents */
+ 0x118, 24, 2); /* offset, shift, width */
+
+static const char *tcon1_parents[] = {"pll_video1"};
+NM_CLK(tcon1_clk,
+ CLK_TCON1, /* id */
+ "tcon1", tcon1_parents, /* name, parents */
+ 0x11c, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 4, 0, 0, /* m factor */
+ 0, 0, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE);
+
+static const char *csi_mclk_parents[] = {"pll_de", "osc24M"};
+NM_CLK(csi_mclk_clk,
+ CLK_CSI_MCLK, /* id */
+ "csi-mclk", csi_mclk_parents, /* name, parents */
+ 0x134, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 4, 0, 0, /* m factor */
+ 8, 3, /* mux */
+ 15, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE);
+
+static const char *csi_sclk_parents[] = {"pll_periph", "pll_ve"};
+NM_CLK(csi_sclk_clk,
+ CLK_CSI_SCLK, /* id */
+ "csi-sclk", csi_sclk_parents, /* name, parents */
+ 0x134, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 16, 4, 0, 0, /* m factor */
+ 24, 3, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE);
+
+static const char *ve_parents[] = {"pll_ve"};
+NM_CLK(ve_clk,
+ CLK_VE, /* id */
+ "ve", ve_parents, /* name, parents */
+ 0x13c, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 16, 3, 0, 0, /* m factor */
+ 0, 0, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE);
+
+static const char *hdmi_parents[] = {"pll_video1"};
+NM_CLK(hdmi_clk,
+ CLK_HDMI, /* id */
+ "hdmi", hdmi_parents, /* name, parents */
+ 0x150, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE);
+
+static const char *mbus_parents[] = {"osc24M", "pll_periph", "pll_ddr"};
+NM_CLK(mbus_clk,
+ CLK_MBUS, /* id */
+ "mbus", mbus_parents, /* name, parents */
+ 0x15c, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 3, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE);
+
+static const char *mipi_dsi0_parents[] = {"pll_video0"};
+NM_CLK(mipi_dsi0_clk,
+ CLK_MIPI_DSI0, /* id */
+ "mipi-dsi0", mipi_dsi0_parents, /* name, parents */
+ 0x168, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 4, 0, 0, /* m factor */
+ 24, 4, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE);
+
+static const char *mipi_dsi1_parents[] = {"osc24M", "pll_video0"};
+NM_CLK(mipi_dsi1_clk,
+ CLK_MIPI_DSI1, /* id */
+ "mipi-dsi1", mipi_dsi1_parents, /* name, parents */
+ 0x16c, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 4, 0, 0, /* m factor */
+ 24, 4, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE);
+
+static const char *gpu_core_parents[] = {"pll_gpu"};
+NM_CLK(gpu_core_clk,
+ CLK_GPU_CORE, /* id */
+ "gpu-core", gpu_core_parents, /* name, parents */
+ 0x1a0, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 3, 0, 0, /* m factor */
+ 0, 0, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE);
+
+static const char *gpu_memory_parents[] = {"pll_gpu", "pll_periph"};
+NM_CLK(gpu_memory_clk,
+ CLK_GPU_MEMORY, /* id */
+ "gpu-memory", gpu_memory_parents, /* name, parents */
+ 0x1a4, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 3, 0, 0, /* m factor */
+ 24, 1, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE);
+
+static const char *gpu_hyd_parents[] = {"pll_gpu"};
+NM_CLK(gpu_hyd_clk,
+ CLK_GPU_HYD, /* id */
+ "gpu-hyd", gpu_hyd_parents, /* name, parents */
+ 0x1a0, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 3, 0, 0, /* m factor */
+ 0, 0, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE);
+
+static struct aw_ccung_clk a83t_clks[] = {
+ { .type = AW_CLK_NKMP, .clk.nkmp = &pll_audio_clk},
+ { .type = AW_CLK_NKMP, .clk.nkmp = &pll_video0_clk},
+ { .type = AW_CLK_NKMP, .clk.nkmp = &pll_ve_clk},
+ { .type = AW_CLK_NKMP, .clk.nkmp = &pll_ddr_clk},
+ { .type = AW_CLK_NKMP, .clk.nkmp = &pll_periph_clk},
+ { .type = AW_CLK_NKMP, .clk.nkmp = &pll_gpu_clk},
+ { .type = AW_CLK_NKMP, .clk.nkmp = &pll_hsic_clk},
+ { .type = AW_CLK_NKMP, .clk.nkmp = &pll_de_clk},
+ { .type = AW_CLK_NKMP, .clk.nkmp = &pll_video1_clk},
+ { .type = AW_CLK_NKMP, .clk.nkmp = &pll_c0cpux_clk},
+ { .type = AW_CLK_NKMP, .clk.nkmp = &pll_c1cpux_clk},
+ { .type = AW_CLK_NM, .clk.nm = &apb2_clk},
+ { .type = AW_CLK_NM, .clk.nm = &nand_clk},
+ { .type = AW_CLK_NM, .clk.nm = &mmc0_clk},
+ { .type = AW_CLK_NM, .clk.nm = &mmc1_clk},
+ { .type = AW_CLK_NM, .clk.nm = &mmc2_clk},
+ { .type = AW_CLK_NM, .clk.nm = &ss_clk},
+ { .type = AW_CLK_NM, .clk.nm = &spi0_clk},
+ { .type = AW_CLK_NM, .clk.nm = &spi1_clk},
+ { .type = AW_CLK_NM, .clk.nm = &i2s0_clk},
+ { .type = AW_CLK_NM, .clk.nm = &i2s1_clk},
+ { .type = AW_CLK_NM, .clk.nm = &i2s2_clk},
+ { .type = AW_CLK_NM, .clk.nm = &tdm_clk},
+ { .type = AW_CLK_NM, .clk.nm = &spdif_clk},
+ { .type = AW_CLK_NM, .clk.nm = &dram_clk},
+ { .type = AW_CLK_NM, .clk.nm = &tcon1_clk},
+ { .type = AW_CLK_NM, .clk.nm = &csi_mclk_clk},
+ { .type = AW_CLK_NM, .clk.nm = &csi_sclk_clk},
+ { .type = AW_CLK_NM, .clk.nm = &ve_clk},
+ { .type = AW_CLK_NM, .clk.nm = &hdmi_clk},
+ { .type = AW_CLK_NM, .clk.nm = &mbus_clk},
+ { .type = AW_CLK_NM, .clk.nm = &mipi_dsi0_clk},
+ { .type = AW_CLK_NM, .clk.nm = &mipi_dsi1_clk},
+ { .type = AW_CLK_NM, .clk.nm = &gpu_core_clk},
+ { .type = AW_CLK_NM, .clk.nm = &gpu_memory_clk},
+ { .type = AW_CLK_NM, .clk.nm = &gpu_hyd_clk},
+ { .type = AW_CLK_PREDIV_MUX, .clk.prediv_mux = &ahb1_clk},
+ { .type = AW_CLK_PREDIV_MUX, .clk.prediv_mux = &ahb2_clk},
+ { .type = AW_CLK_MUX, .clk.mux = &c0cpux_clk},
+ { .type = AW_CLK_MUX, .clk.mux = &c1cpux_clk},
+ { .type = AW_CLK_MUX, .clk.mux = &cci400_clk},
+ { .type = AW_CLK_MUX, .clk.mux = &tcon0_clk},
+ { .type = AW_CLK_DIV, .clk.div = &axi0_clk},
+ { .type = AW_CLK_DIV, .clk.div = &axi1_clk},
+ { .type = AW_CLK_DIV, .clk.div = &apb1_clk},
+ { .type = AW_CLK_FIXED, .clk.fixed = &osc12m_clk},
+};
+
+static struct aw_clk_init a83t_init_clks[] = {
+ {"ahb1", "pll_periph", 0, false},
+ {"ahb2", "ahb1", 0, false},
+ {"dram", "pll_ddr", 0, false},
+};
+
+static int
+ccu_a83t_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "allwinner,sun8i-a83t-ccu"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner A83T Clock Control Unit NG");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ccu_a83t_attach(device_t dev)
+{
+ struct aw_ccung_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ sc->resets = a83t_ccu_resets;
+ sc->nresets = nitems(a83t_ccu_resets);
+ sc->gates = a83t_ccu_gates;
+ sc->ngates = nitems(a83t_ccu_gates);
+ sc->clks = a83t_clks;
+ sc->nclks = nitems(a83t_clks);
+ sc->clk_init = a83t_init_clks;
+ sc->n_clk_init = nitems(a83t_init_clks);
+
+ return (aw_ccung_attach(dev));
+}
+
+static device_method_t ccu_a83tng_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ccu_a83t_probe),
+ DEVMETHOD(device_attach, ccu_a83t_attach),
+
+ DEVMETHOD_END
+};
+
+static devclass_t ccu_a83tng_devclass;
+
+DEFINE_CLASS_1(ccu_a83tng, ccu_a83tng_driver, ccu_a83tng_methods,
+ sizeof(struct aw_ccung_softc), aw_ccung_driver);
+
+EARLY_DRIVER_MODULE(ccu_a83tng, simplebus, ccu_a83tng_driver,
+ ccu_a83tng_devclass, 0, 0, BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE);
diff --git a/sys/arm/allwinner/clkng/ccu_de2.c b/sys/arm/allwinner/clkng/ccu_de2.c
new file mode 100644
index 000000000000..84a2681abeb9
--- /dev/null
+++ b/sys/arm/allwinner/clkng/ccu_de2.c
@@ -0,0 +1,240 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2018 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <machine/bus.h>
+
+#include <dev/fdt/simplebus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#ifdef __aarch64__
+#include "opt_soc.h"
+#endif
+
+#include <dev/extres/clk/clk_div.h>
+#include <dev/extres/clk/clk_fixed.h>
+#include <dev/extres/clk/clk_mux.h>
+
+#include <dev/extres/hwreset/hwreset.h>
+
+#include <arm/allwinner/clkng/aw_ccung.h>
+
+#include <dt-bindings/clock/sun8i-de2.h>
+#include <dt-bindings/reset/sun8i-de2.h>
+
+enum CCU_DE2 {
+ H3_CCU = 1,
+ A64_CCU,
+};
+
+/* Non exported clocks */
+#define CLK_MIXER0_DIV 3
+#define CLK_MIXER1_DIV 4
+#define CLK_WB_DIV 5
+
+static struct aw_ccung_reset h3_de2_ccu_resets[] = {
+ CCU_RESET(RST_MIXER0, 0x08, 0)
+ CCU_RESET(RST_WB, 0x08, 2)
+};
+
+static struct aw_ccung_reset a64_de2_ccu_resets[] = {
+ CCU_RESET(RST_MIXER0, 0x08, 0)
+ CCU_RESET(RST_MIXER1, 0x08, 1)
+ CCU_RESET(RST_WB, 0x08, 2)
+};
+
+static struct aw_ccung_gate h3_de2_ccu_gates[] = {
+ CCU_GATE(CLK_BUS_MIXER0, "mixer0", "mixer0-div", 0x00, 0)
+ CCU_GATE(CLK_BUS_WB, "wb", "wb-div", 0x00, 2)
+
+ CCU_GATE(CLK_MIXER0, "bus-mixer0", "bus-de", 0x04, 0)
+ CCU_GATE(CLK_WB, "bus-wb", "bus-de", 0x04, 2)
+};
+
+static struct aw_ccung_gate a64_de2_ccu_gates[] = {
+ CCU_GATE(CLK_BUS_MIXER0, "mixer0", "mixer0-div", 0x00, 0)
+ CCU_GATE(CLK_BUS_MIXER1, "mixer1", "mixer1-div", 0x00, 1)
+ CCU_GATE(CLK_BUS_WB, "wb", "wb-div", 0x00, 2)
+
+ CCU_GATE(CLK_MIXER0, "bus-mixer0", "bus-de", 0x04, 0)
+ CCU_GATE(CLK_MIXER1, "bus-mixer1", "bus-de", 0x04, 1)
+ CCU_GATE(CLK_WB, "bus-wb", "bus-de", 0x04, 2)
+};
+
+static const char *div_parents[] = {"de"};
+
+NM_CLK(mixer0_div_clk,
+ CLK_MIXER0_DIV, /* id */
+ "mixer0-div", div_parents, /* names, parents */
+ 0x0C, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* N factor (fake)*/
+ 0, 4, 0, 0, /* M flags */
+ 0, 0, /* mux */
+ 0, /* gate */
+ AW_CLK_SCALE_CHANGE); /* flags */
+
+NM_CLK(mixer1_div_clk,
+ CLK_MIXER1_DIV, /* id */
+ "mixer1-div", div_parents, /* names, parents */
+ 0x0C, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* N factor (fake)*/
+ 4, 4, 0, 0, /* M flags */
+ 0, 0, /* mux */
+ 0, /* gate */
+ AW_CLK_SCALE_CHANGE); /* flags */
+
+NM_CLK(wb_div_clk,
+ CLK_WB_DIV, /* id */
+ "wb-div", div_parents, /* names, parents */
+ 0x0C, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* N factor (fake)*/
+ 8, 4, 0, 0, /* M flags */
+ 0, 0, /* mux */
+ 0, /* gate */
+ AW_CLK_SCALE_CHANGE); /* flags */
+
+static struct aw_ccung_clk h3_de2_ccu_clks[] = {
+ { .type = AW_CLK_NM, .clk.nm = &mixer0_div_clk},
+ { .type = AW_CLK_NM, .clk.nm = &wb_div_clk},
+};
+
+static struct aw_ccung_clk a64_de2_ccu_clks[] = {
+ { .type = AW_CLK_NM, .clk.nm = &mixer0_div_clk},
+ { .type = AW_CLK_NM, .clk.nm = &mixer1_div_clk},
+ { .type = AW_CLK_NM, .clk.nm = &wb_div_clk},
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"allwinner,sun8i-h3-de2-clk", H3_CCU},
+ {"allwinner,sun50i-a64-de2-clk", A64_CCU},
+ {NULL, 0}
+};
+
+static int
+ccu_de2_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner DE2 Clock Control Unit");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ccu_de2_attach(device_t dev)
+{
+ struct aw_ccung_softc *sc;
+ phandle_t node;
+ clk_t mod, bus;
+ hwreset_t rst_de;
+ enum CCU_DE2 type;
+
+ sc = device_get_softc(dev);
+ node = ofw_bus_get_node(dev);
+
+ type = (enum CCU_DE2)ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+
+ switch (type) {
+ case H3_CCU:
+ sc->resets = h3_de2_ccu_resets;
+ sc->nresets = nitems(h3_de2_ccu_resets);
+ sc->gates = h3_de2_ccu_gates;
+ sc->ngates = nitems(h3_de2_ccu_gates);
+ sc->clks = h3_de2_ccu_clks;
+ sc->nclks = nitems(h3_de2_ccu_clks);
+ break;
+ case A64_CCU:
+ sc->resets = a64_de2_ccu_resets;
+ sc->nresets = nitems(a64_de2_ccu_resets);
+ sc->gates = a64_de2_ccu_gates;
+ sc->ngates = nitems(a64_de2_ccu_gates);
+ sc->clks = a64_de2_ccu_clks;
+ sc->nclks = nitems(a64_de2_ccu_clks);
+ break;
+ }
+
+ if (hwreset_get_by_ofw_idx(dev, node, 0, &rst_de) != 0) {
+ device_printf(dev, "Cannot get de reset\n");
+ return (ENXIO);
+ }
+ if (hwreset_deassert(rst_de) != 0) {
+ device_printf(dev, "Cannot de-assert de reset\n");
+ return (ENXIO);
+ }
+
+ if (clk_get_by_ofw_name(dev, node, "mod", &mod) != 0) {
+ device_printf(dev, "Cannot get mod clock\n");
+ return (ENXIO);
+ }
+ if (clk_enable(mod) != 0) {
+ device_printf(dev, "Cannot enable mod clock\n");
+ return (ENXIO);
+ }
+
+ if (clk_get_by_ofw_name(dev, node, "bus", &bus) != 0) {
+ device_printf(dev, "Cannot get bus clock\n");
+ return (ENXIO);
+ }
+ if (clk_enable(bus) != 0) {
+ device_printf(dev, "Cannot enable bus clock\n");
+ return (ENXIO);
+ }
+
+ return (aw_ccung_attach(dev));
+}
+
+static device_method_t ccu_de2_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ccu_de2_probe),
+ DEVMETHOD(device_attach, ccu_de2_attach),
+
+ DEVMETHOD_END
+};
+
+static devclass_t ccu_de2ng_devclass;
+
+DEFINE_CLASS_1(ccu_de2, ccu_de2_driver, ccu_de2_methods,
+ sizeof(struct aw_ccung_softc), aw_ccung_driver);
+
+EARLY_DRIVER_MODULE(ccu_de2, simplebus, ccu_de2_driver,
+ ccu_de2ng_devclass, 0, 0, BUS_PASS_RESOURCE + BUS_PASS_ORDER_LAST);
diff --git a/sys/arm/allwinner/clkng/ccu_h3.c b/sys/arm/allwinner/clkng/ccu_h3.c
new file mode 100644
index 000000000000..72e257c98d73
--- /dev/null
+++ b/sys/arm/allwinner/clkng/ccu_h3.c
@@ -0,0 +1,794 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2017,2018 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <machine/bus.h>
+
+#include <dev/fdt/simplebus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <dev/extres/clk/clk_div.h>
+#include <dev/extres/clk/clk_fixed.h>
+#include <dev/extres/clk/clk_mux.h>
+
+#if defined(__aarch64__)
+#include "opt_soc.h"
+#endif
+
+#include <arm/allwinner/clkng/aw_ccung.h>
+
+#include <dt-bindings/clock/sun8i-h3-ccu.h>
+#include <dt-bindings/reset/sun8i-h3-ccu.h>
+
+/* Non-exported resets */
+#define RST_BUS_SCR 53
+
+/* Non-exported clocks */
+#define CLK_PLL_CPUX 0
+#define CLK_PLL_AUDIO_BASE 1
+#define CLK_PLL_AUDIO 2
+#define CLK_PLL_AUDIO_2X 3
+#define CLK_PLL_AUDIO_4X 4
+#define CLK_PLL_AUDIO_8X 5
+#define CLK_PLL_VIDEO 6
+#define CLK_PLL_VE 7
+#define CLK_PLL_DDR 8
+#define CLK_PLL_PERIPH0_2X 10
+#define CLK_PLL_GPU 11
+#define CLK_PLL_PERIPH1 12
+#define CLK_PLL_DE 13
+
+#define CLK_AXI 15
+#define CLK_AHB1 16
+#define CLK_APB1 17
+#define CLK_APB2 18
+#define CLK_AHB2 19
+
+#define CLK_BUS_SCR 66
+
+#define CLK_USBPHY0 88
+#define CLK_USBPHY1 89
+#define CLK_USBPHY2 90
+#define CLK_USBPHY3 91
+#define CLK_USBOHCI0 92
+#define CLK_USBOHCI1 93
+#define CLK_USBOHCI2 94
+#define CLK_USBOHCI3 95
+#define CLK_DRAM 96
+
+#define CLK_MBUS 113
+
+static struct aw_ccung_reset h3_ccu_resets[] = {
+ CCU_RESET(RST_USB_PHY0, 0xcc, 0)
+ CCU_RESET(RST_USB_PHY1, 0xcc, 1)
+ CCU_RESET(RST_USB_PHY2, 0xcc, 2)
+ CCU_RESET(RST_USB_PHY3, 0xcc, 3)
+
+ CCU_RESET(RST_MBUS, 0xfc, 31)
+
+ CCU_RESET(RST_BUS_CE, 0x2c0, 5)
+ CCU_RESET(RST_BUS_DMA, 0x2c0, 6)
+ CCU_RESET(RST_BUS_MMC0, 0x2c0, 8)
+ CCU_RESET(RST_BUS_MMC1, 0x2c0, 9)
+ CCU_RESET(RST_BUS_MMC2, 0x2c0, 10)
+ CCU_RESET(RST_BUS_NAND, 0x2c0, 13)
+ CCU_RESET(RST_BUS_DRAM, 0x2c0, 14)
+ CCU_RESET(RST_BUS_EMAC, 0x2c0, 17)
+ CCU_RESET(RST_BUS_TS, 0x2c0, 18)
+ CCU_RESET(RST_BUS_HSTIMER, 0x2c0, 19)
+ CCU_RESET(RST_BUS_SPI0, 0x2c0, 20)
+ CCU_RESET(RST_BUS_SPI1, 0x2c0, 21)
+ CCU_RESET(RST_BUS_OTG, 0x2c0, 23)
+ CCU_RESET(RST_BUS_EHCI0, 0x2c0, 24)
+ CCU_RESET(RST_BUS_EHCI1, 0x2c0, 25)
+ CCU_RESET(RST_BUS_EHCI2, 0x2c0, 26)
+ CCU_RESET(RST_BUS_EHCI3, 0x2c0, 27)
+ CCU_RESET(RST_BUS_OHCI0, 0x2c0, 28)
+ CCU_RESET(RST_BUS_OHCI1, 0x2c0, 29)
+ CCU_RESET(RST_BUS_OHCI2, 0x2c0, 30)
+ CCU_RESET(RST_BUS_OHCI3, 0x2c0, 31)
+
+ CCU_RESET(RST_BUS_VE, 0x2c4, 0)
+ CCU_RESET(RST_BUS_TCON0, 0x2c4, 3)
+ CCU_RESET(RST_BUS_TCON1, 0x2c4, 4)
+ CCU_RESET(RST_BUS_DEINTERLACE, 0x2c4, 5)
+ CCU_RESET(RST_BUS_CSI, 0x2c4, 8)
+ CCU_RESET(RST_BUS_TVE, 0x2c4, 9)
+ CCU_RESET(RST_BUS_HDMI0, 0x2c4, 10)
+ CCU_RESET(RST_BUS_HDMI1, 0x2c4, 11)
+ CCU_RESET(RST_BUS_DE, 0x2c4, 12)
+ CCU_RESET(RST_BUS_GPU, 0x2c4, 20)
+ CCU_RESET(RST_BUS_MSGBOX, 0x2c4, 21)
+ CCU_RESET(RST_BUS_SPINLOCK, 0x2c4, 22)
+ CCU_RESET(RST_BUS_DBG, 0x2c4, 31)
+
+ CCU_RESET(RST_BUS_EPHY, 0x2c8, 2)
+
+ CCU_RESET(RST_BUS_CODEC, 0x2d0, 0)
+ CCU_RESET(RST_BUS_SPDIF, 0x2d0, 1)
+ CCU_RESET(RST_BUS_THS, 0x2d0, 8)
+ CCU_RESET(RST_BUS_I2S0, 0x2d0, 12)
+ CCU_RESET(RST_BUS_I2S1, 0x2d0, 13)
+ CCU_RESET(RST_BUS_I2S2, 0x2d0, 14)
+
+ CCU_RESET(RST_BUS_I2C0, 0x2d8, 0)
+ CCU_RESET(RST_BUS_I2C1, 0x2d8, 1)
+ CCU_RESET(RST_BUS_I2C2, 0x2d8, 2)
+ CCU_RESET(RST_BUS_UART0, 0x2d8, 16)
+ CCU_RESET(RST_BUS_UART1, 0x2d8, 17)
+ CCU_RESET(RST_BUS_UART2, 0x2d8, 18)
+ CCU_RESET(RST_BUS_UART3, 0x2d8, 19)
+ CCU_RESET(RST_BUS_SCR, 0x2d8, 20)
+};
+
+static struct aw_ccung_gate h3_ccu_gates[] = {
+ CCU_GATE(CLK_BUS_CE, "bus-ce", "ahb1", 0x60, 5)
+ CCU_GATE(CLK_BUS_DMA, "bus-dma", "ahb1", 0x60, 6)
+ CCU_GATE(CLK_BUS_MMC0, "bus-mmc0", "ahb1", 0x60, 8)
+ CCU_GATE(CLK_BUS_MMC1, "bus-mmc1", "ahb1", 0x60, 9)
+ CCU_GATE(CLK_BUS_MMC2, "bus-mmc2", "ahb1", 0x60, 10)
+ CCU_GATE(CLK_BUS_NAND, "bus-nand", "ahb1", 0x60, 13)
+ CCU_GATE(CLK_BUS_DRAM, "bus-dram", "ahb1", 0x60, 14)
+ CCU_GATE(CLK_BUS_EMAC, "bus-emac", "ahb2", 0x60, 17)
+ CCU_GATE(CLK_BUS_TS, "bus-ts", "ahb1", 0x60, 18)
+ CCU_GATE(CLK_BUS_HSTIMER, "bus-hstimer", "ahb1", 0x60, 19)
+ CCU_GATE(CLK_BUS_SPI0, "bus-spi0", "ahb1", 0x60, 20)
+ CCU_GATE(CLK_BUS_SPI1, "bus-spi1", "ahb1", 0x60, 21)
+ CCU_GATE(CLK_BUS_OTG, "bus-otg", "ahb1", 0x60, 23)
+ CCU_GATE(CLK_BUS_EHCI0, "bus-ehci0", "ahb1", 0x60, 24)
+ CCU_GATE(CLK_BUS_EHCI1, "bus-ehci1", "ahb2", 0x60, 25)
+ CCU_GATE(CLK_BUS_EHCI2, "bus-ehci2", "ahb2", 0x60, 26)
+ CCU_GATE(CLK_BUS_EHCI3, "bus-ehci3", "ahb2", 0x60, 27)
+ CCU_GATE(CLK_BUS_OHCI0, "bus-ohci0", "ahb1", 0x60, 28)
+ CCU_GATE(CLK_BUS_OHCI1, "bus-ohci1", "ahb2", 0x60, 29)
+ CCU_GATE(CLK_BUS_OHCI2, "bus-ohci2", "ahb2", 0x60, 30)
+ CCU_GATE(CLK_BUS_OHCI3, "bus-ohci3", "ahb2", 0x60, 31)
+
+ CCU_GATE(CLK_BUS_VE, "bus-ve", "ahb1", 0x64, 0)
+ CCU_GATE(CLK_BUS_TCON0, "bus-tcon0", "ahb1", 0x64, 3)
+ CCU_GATE(CLK_BUS_TCON1, "bus-tcon1", "ahb1", 0x64, 4)
+ CCU_GATE(CLK_BUS_DEINTERLACE, "bus-deinterlace", "ahb1", 0x64, 5)
+ CCU_GATE(CLK_BUS_CSI, "bus-csi", "ahb1", 0x64, 8)
+ CCU_GATE(CLK_BUS_TVE, "bus-tve", "ahb1", 0x64, 9)
+ CCU_GATE(CLK_BUS_HDMI, "bus-hdmi", "ahb1", 0x64, 11)
+ CCU_GATE(CLK_BUS_DE, "bus-de", "ahb1", 0x64, 12)
+ CCU_GATE(CLK_BUS_GPU, "bus-gpu", "ahb1", 0x64, 20)
+ CCU_GATE(CLK_BUS_MSGBOX, "bus-msgbox", "ahb1", 0x64, 21)
+ CCU_GATE(CLK_BUS_SPINLOCK, "bus-spinlock", "ahb1", 0x64, 22)
+
+ CCU_GATE(CLK_BUS_CODEC, "bus-codec", "apb1", 0x68, 0)
+ CCU_GATE(CLK_BUS_SPDIF, "bus-spdif", "apb1", 0x68, 1)
+ CCU_GATE(CLK_BUS_PIO, "bus-pio", "apb1", 0x68, 5)
+ CCU_GATE(CLK_BUS_THS, "bus-ths", "apb1", 0x68, 8)
+ CCU_GATE(CLK_BUS_I2S0, "bus-i2s0", "apb1", 0x68, 12)
+ CCU_GATE(CLK_BUS_I2S1, "bus-i2s1", "apb1", 0x68, 13)
+ CCU_GATE(CLK_BUS_I2S2, "bus-i2s2", "apb1", 0x68, 14)
+
+ CCU_GATE(CLK_BUS_I2C0, "bus-i2c0", "apb2", 0x6c, 0)
+ CCU_GATE(CLK_BUS_I2C1, "bus-i2c1", "apb2", 0x6c, 1)
+ CCU_GATE(CLK_BUS_I2C2, "bus-i2c2", "apb2", 0x6c, 2)
+ CCU_GATE(CLK_BUS_UART0, "bus-uart0", "apb2", 0x6c, 16)
+ CCU_GATE(CLK_BUS_UART1, "bus-uart1", "apb2", 0x6c, 17)
+ CCU_GATE(CLK_BUS_UART2, "bus-uart2", "apb2", 0x6c, 18)
+ CCU_GATE(CLK_BUS_UART3, "bus-uart3", "apb2", 0x6c, 19)
+ CCU_GATE(CLK_BUS_SCR, "bus-scr", "apb2", 0x6c, 20)
+
+ CCU_GATE(CLK_BUS_EPHY, "bus-ephy", "ahb1", 0x70, 0)
+ CCU_GATE(CLK_BUS_DBG, "bus-dbg", "ahb1", 0x70, 7)
+
+ CCU_GATE(CLK_USBPHY0, "usb-phy0", "osc24M", 0xcc, 8)
+ CCU_GATE(CLK_USBPHY1, "usb-phy1", "osc24M", 0xcc, 9)
+ CCU_GATE(CLK_USBPHY2, "usb-phy2", "osc24M", 0xcc, 10)
+ CCU_GATE(CLK_USBPHY3, "usb-phy3", "osc24M", 0xcc, 11)
+ CCU_GATE(CLK_USBOHCI0, "usb-ohci0", "osc24M", 0xcc, 16)
+ CCU_GATE(CLK_USBOHCI1, "usb-ohci1", "osc24M", 0xcc, 17)
+ CCU_GATE(CLK_USBOHCI2, "usb-ohci2", "osc24M", 0xcc, 18)
+ CCU_GATE(CLK_USBOHCI3, "usb-ohci3", "osc24M", 0xcc, 19)
+
+ CCU_GATE(CLK_THS, "ths", "thsdiv", 0x74, 31)
+ CCU_GATE(CLK_I2S0, "i2s0", "i2s0mux", 0xB0, 31)
+ CCU_GATE(CLK_I2S1, "i2s1", "i2s1mux", 0xB4, 31)
+ CCU_GATE(CLK_I2S2, "i2s2", "i2s2mux", 0xB8, 31)
+
+ CCU_GATE(CLK_DRAM_VE, "dram-ve", "dram", 0x100, 0)
+ CCU_GATE(CLK_DRAM_CSI, "dram-csi", "dram", 0x100, 1)
+ CCU_GATE(CLK_DRAM_DEINTERLACE, "dram-deinterlace", "dram", 0x100, 2)
+ CCU_GATE(CLK_DRAM_TS, "dram-ts", "dram", 0x100, 3)
+
+ CCU_GATE(CLK_AC_DIG, "ac-dig", "pll_audio", 0x140, 31)
+
+ CCU_GATE(CLK_AVS, "avs", "osc24M", 0x144, 31)
+
+ CCU_GATE(CLK_CSI_MISC, "csi-misc", "osc24M", 0x130, 31)
+
+ CCU_GATE(CLK_HDMI_DDC, "hdmi-ddc", "osc24M", 0x154, 31)
+};
+
+static const char *pll_cpux_parents[] = {"osc24M"};
+NKMP_CLK(pll_cpux_clk,
+ CLK_PLL_CPUX, /* id */
+ "pll_cpux", pll_cpux_parents, /* name, parents */
+ 0x00, /* offset */
+ 8, 5, 0, 0, /* n factor */
+ 4, 2, 0, 0, /* k factor */
+ 0, 2, 0, 0, /* m factor */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* p factor */
+ 31, /* gate */
+ 28, 1000, /* lock */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_LOCK | AW_CLK_SCALE_CHANGE); /* flags */
+
+static const char *pll_audio_parents[] = {"osc24M"};
+NKMP_CLK(pll_audio_clk,
+ CLK_PLL_AUDIO, /* id */
+ "pll_audio", pll_audio_parents, /* name, parents */
+ 0x08, /* offset */
+ 8, 7, 0, 0, /* n factor */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* k factor (fake) */
+ 0, 5, 0, 0, /* m factor */
+ 16, 4, 0, 0, /* p factor */
+ 31, /* gate */
+ 28, 1000, /* lock */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_LOCK); /* flags */
+
+static const char *pll_audio_mult_parents[] = {"pll_audio"};
+FIXED_CLK(pll_audio_2x_clk,
+ CLK_PLL_AUDIO_2X, /* id */
+ "pll_audio-2x", /* name */
+ pll_audio_mult_parents, /* parent */
+ 0, /* freq */
+ 2, /* mult */
+ 1, /* div */
+ 0); /* flags */
+FIXED_CLK(pll_audio_4x_clk,
+ CLK_PLL_AUDIO_4X, /* id */
+ "pll_audio-4x", /* name */
+ pll_audio_mult_parents, /* parent */
+ 0, /* freq */
+ 4, /* mult */
+ 1, /* div */
+ 0); /* flags */
+FIXED_CLK(pll_audio_8x_clk,
+ CLK_PLL_AUDIO_8X, /* id */
+ "pll_audio-8x", /* name */
+ pll_audio_mult_parents, /* parent */
+ 0, /* freq */
+ 8, /* mult */
+ 1, /* div */
+ 0); /* flags */
+
+static const char *pll_video_parents[] = {"osc24M"};
+FRAC_CLK(pll_video_clk,
+ CLK_PLL_VIDEO, /* id */
+ "pll_video", pll_video_parents, /* name, parents */
+ 0x10, /* offset */
+ 8, 7, 0, 0, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 31, 28, 1000, /* gate, lock, lock retries */
+ AW_CLK_HAS_LOCK, /* flags */
+ 270000000, 297000000, /* freq0, freq1 */
+ 24, 25, /* mode sel, freq sel */
+ 192000000, 600000000); /* min freq, max freq */
+
+static const char *pll_ve_parents[] = {"osc24M"};
+FRAC_CLK(pll_ve_clk,
+ CLK_PLL_VE, /* id */
+ "pll_ve", pll_ve_parents, /* name, parents */
+ 0x18, /* offset */
+ 8, 7, 0, 0, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 31, 28, 1000, /* gate, lock, lock retries */
+ AW_CLK_HAS_LOCK, /* flags */
+ 270000000, 297000000, /* freq0, freq1 */
+ 24, 25, /* mode sel, freq sel */
+ 192000000, 600000000); /* min freq, max freq */
+
+static const char *pll_ddr_parents[] = {"osc24M"};
+NKMP_CLK_WITH_UPDATE(pll_ddr_clk,
+ CLK_PLL_DDR, /* id */
+ "pll_ddr", pll_ddr_parents, /* name, parents */
+ 0x20, /* offset */
+ 8, 5, 0, 0, /* n factor */
+ 4, 2, 0, 0, /* k factor */
+ 0, 2, 0, 0, /* m factor */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* p factor (fake) */
+ 31, /* gate */
+ 28, 1000, /* lock */
+ 20, /* update */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_LOCK); /* flags */
+
+static const char *pll_periph0_parents[] = {"osc24M"};
+static const char *pll_periph0_2x_parents[] = {"pll_periph0"};
+NKMP_CLK(pll_periph0_clk,
+ CLK_PLL_PERIPH0, /* id */
+ "pll_periph0", pll_periph0_parents, /* name, parents */
+ 0x28, /* offset */
+ 8, 5, 0, 0, /* n factor */
+ 4, 2, 0, 0, /* k factor */
+ 0, 0, 2, AW_CLK_FACTOR_FIXED, /* m factor (fake) */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* p factor (fake) */
+ 31, /* gate */
+ 28, 1000, /* lock */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_LOCK); /* flags */
+FIXED_CLK(pll_periph0_2x_clk,
+ CLK_PLL_PERIPH0_2X, /* id */
+ "pll_periph0-2x", /* name */
+ pll_periph0_2x_parents, /* parent */
+ 0, /* freq */
+ 2, /* mult */
+ 1, /* div */
+ 0); /* flags */
+
+static const char *pll_gpu_parents[] = {"osc24M"};
+FRAC_CLK(pll_gpu_clk,
+ CLK_PLL_GPU, /* id */
+ "pll_gpu", pll_gpu_parents, /* name, parents */
+ 0x38, /* offset */
+ 8, 7, 0, 0, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 31, 28, 1000, /* gate, lock, lock retries */
+ AW_CLK_HAS_LOCK, /* flags */
+ 270000000, 297000000, /* freq0, freq1 */
+ 24, 25, /* mode sel, freq sel */
+ 192000000, 600000000); /* min freq, max freq */
+
+static const char *pll_periph1_parents[] = {"osc24M"};
+NKMP_CLK(pll_periph1_clk,
+ CLK_PLL_PERIPH1, /* id */
+ "pll_periph1", pll_periph1_parents, /* name, parents */
+ 0x44, /* offset */
+ 8, 5, 0, 0, /* n factor */
+ 4, 2, 0, 0, /* k factor */
+ 0, 0, 2, AW_CLK_FACTOR_FIXED, /* m factor (fake) */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* p factor (fake) */
+ 31, /* gate */
+ 28, 1000, /* lock */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_LOCK); /* flags */
+
+static const char *pll_de_parents[] = {"osc24M"};
+FRAC_CLK(pll_de_clk,
+ CLK_PLL_DE, /* id */
+ "pll_de", pll_de_parents, /* name, parents */
+ 0x48, /* offset */
+ 8, 7, 0, 0, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 31, 28, 1000, /* gate, lock, lock retries */
+ AW_CLK_HAS_LOCK, /* flags */
+ 270000000, 297000000, /* freq0, freq1 */
+ 24, 25, /* mode sel, freq sel */
+ 192000000, 600000000); /* min freq, max freq */
+
+static const char *cpux_parents[] = {"osc32k", "osc24M", "pll_cpux", "pll_cpux"};
+MUX_CLK(cpux_clk,
+ CLK_CPUX, /* id */
+ "cpux", cpux_parents, /* name, parents */
+ 0x50, 16, 2); /* offset, shift, width */
+
+static const char *axi_parents[] = {"cpux"};
+DIV_CLK(axi_clk,
+ CLK_AXI, /* id */
+ "axi", axi_parents, /* name, parents */
+ 0x50, /* offset */
+ 0, 2, /* shift, width */
+ 0, NULL); /* flags, div table */
+
+static const char *ahb1_parents[] = {"osc32k", "osc24M", "axi", "pll_periph0"};
+PREDIV_CLK(ahb1_clk, CLK_AHB1, /* id */
+ "ahb1", ahb1_parents, /* name, parents */
+ 0x54, /* offset */
+ 12, 2, /* mux */
+ 4, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* div */
+ 6, 2, 0, AW_CLK_FACTOR_HAS_COND, /* prediv */
+ 12, 2, 3); /* prediv condition */
+
+static const char *apb1_parents[] = {"ahb1"};
+static struct clk_div_table apb1_div_table[] = {
+ { .value = 0, .divider = 2, },
+ { .value = 1, .divider = 2, },
+ { .value = 2, .divider = 4, },
+ { .value = 3, .divider = 8, },
+ { },
+};
+DIV_CLK(apb1_clk,
+ CLK_APB1, /* id */
+ "apb1", apb1_parents, /* name, parents */
+ 0x54, /* offset */
+ 8, 2, /* shift, width */
+ CLK_DIV_WITH_TABLE, /* flags */
+ apb1_div_table); /* div table */
+
+static const char *apb2_parents[] = {"osc32k", "osc24M", "pll_periph0", "pll_periph0"};
+NM_CLK(apb2_clk,
+ CLK_APB2, /* id */
+ "apb2", apb2_parents, /* name, parents */
+ 0x58, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 5, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 0, /* gate */
+ AW_CLK_HAS_MUX);
+
+static const char *ahb2_parents[] = {"ahb1", "pll_periph0"};
+PREDIV_CLK(ahb2_clk, CLK_AHB2, /* id */
+ "ahb2", ahb2_parents, /* name, parents */
+ 0x5c, /* offset */
+ 0, 2, /* mux */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* div */
+ 0, 0, 2, AW_CLK_FACTOR_HAS_COND | AW_CLK_FACTOR_FIXED, /* prediv */
+ 0, 2, 1); /* prediv condition */
+
+static const char *ths_parents[] = {"osc24M"};
+static struct clk_div_table ths_div_table[] = {
+ { .value = 0, .divider = 1, },
+ { .value = 1, .divider = 2, },
+ { .value = 2, .divider = 4, },
+ { .value = 3, .divider = 6, },
+ { },
+};
+DIV_CLK(thsdiv_clk,
+ 0, /* id */
+ "thsdiv", ths_parents, /* name, parents */
+ 0x74, /* offset */
+ 0, 2, /* shift, width */
+ CLK_DIV_WITH_TABLE, /* flags */
+ ths_div_table); /* div table */
+
+static const char *mod_parents[] = {"osc24M", "pll_periph0", "pll_periph1"};
+NM_CLK(nand_clk,
+ CLK_NAND, "nand", mod_parents, /* id, name, parents */
+ 0x80, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX); /* flags */
+
+NM_CLK(mmc0_clk,
+ CLK_MMC0, "mmc0", mod_parents, /* id, name, parents */
+ 0x88, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX |
+ AW_CLK_REPARENT); /* flags */
+
+NM_CLK(mmc1_clk,
+ CLK_MMC1, "mmc1", mod_parents, /* id, name, parents */
+ 0x8c, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX |
+ AW_CLK_REPARENT); /* flags */
+
+NM_CLK(mmc2_clk,
+ CLK_MMC2, "mmc2", mod_parents, /* id, name, parents */
+ 0x90, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX |
+ AW_CLK_REPARENT); /* flags */
+
+static const char *ts_parents[] = {"osc24M", "pll_periph0"};
+NM_CLK(ts_clk,
+ CLK_TS, "ts", ts_parents, /* id, name, parents */
+ 0x98, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX); /* flags */
+
+NM_CLK(ce_clk,
+ CLK_CE, "ce", mod_parents, /* id, name, parents */
+ 0x9C, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX); /* flags */
+
+NM_CLK(spi0_clk,
+ CLK_SPI0, "spi0", mod_parents, /* id, name, parents */
+ 0xA0, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX |
+ AW_CLK_REPARENT); /* flags */
+
+NM_CLK(spi1_clk,
+ CLK_SPI1, "spi1", mod_parents, /* id, name, parents */
+ 0xA4, /* offset */
+ 16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX |
+ AW_CLK_REPARENT); /* flags */
+
+static const char *i2s_parents[] = {"pll_audio-8x", "pll_audio-4x", "pll_audio-2x", "pll_audio"};
+MUX_CLK(i2s0mux_clk,
+ 0, "i2s0mux", i2s_parents, /* id, name, parents */
+ 0xb0, 16, 2); /* offset, mux shift, mux width */
+MUX_CLK(i2s1mux_clk,
+ 0, "i2s1mux", i2s_parents, /* id, name, parents */
+ 0xb4, 16, 2); /* offset, mux shift, mux width */
+MUX_CLK(i2s2mux_clk,
+ 0, "i2s2mux", i2s_parents, /* id, name, parents */
+ 0xb8, 16, 2); /* offset, mux shift, mux width */
+
+static const char *spdif_parents[] = {"pll_audio"};
+NM_CLK(spdif_clk,
+ CLK_SPDIF, "spdif", spdif_parents, /* id, name, parents */
+ 0xC0, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake); */
+ 0, 4, 0, 0, /* m factor */
+ 0, 0, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE); /* flags */
+
+static const char *dram_parents[] = {"pll_ddr", "pll_periph0-2x"};
+NM_CLK(dram_clk,
+ CLK_DRAM, "dram", dram_parents, /* id, name, parents */
+ 0xF4, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 4, 0, 0, /* m factor */
+ 20, 2, /* mux */
+ 0, /* gate */
+ AW_CLK_HAS_MUX); /* flags */
+
+static const char *de_parents[] = {"pll_periph0-2x", "pll_de"};
+NM_CLK(de_clk,
+ CLK_DE, "de", de_parents, /* id, name, parents */
+ 0x104, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+static const char *tcon0_parents[] = {"pll_video"};
+NM_CLK(tcon0_clk,
+ CLK_TCON0, "tcon0", tcon0_parents, /* id, name, parents */
+ 0x118, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+static const char *tve_parents[] = {"pll_de", "pll_periph1"};
+NM_CLK(tve_clk,
+ CLK_TVE, "tve", tve_parents, /* id, name, parents */
+ 0x120, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+static const char *deinterlace_parents[] = {"pll_periph0", "pll_periph1"};
+NM_CLK(deinterlace_clk,
+ CLK_DEINTERLACE, "deinterlace", deinterlace_parents, /* id, name, parents */
+ 0x124, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+static const char *csi_sclk_parents[] = {"pll_periph0", "pll_periph1"};
+NM_CLK(csi_sclk_clk,
+ CLK_CSI_SCLK, "csi-sclk", csi_sclk_parents, /* id, name, parents */
+ 0x134, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 16, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+static const char *csi_mclk_parents[] = {"osc24M", "pll_video", "pll_periph1"};
+NM_CLK(csi_mclk_clk,
+ CLK_CSI_MCLK, "csi-mclk", csi_mclk_parents, /* id, name, parents */
+ 0x134, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 4, 0, 0, /* m factor */
+ 8, 2, /* mux */
+ 15, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+static const char *ve_parents[] = {"pll_ve"};
+NM_CLK(ve_clk,
+ CLK_VE, "ve", ve_parents, /* id, name, parents */
+ 0x13C, /* offset */
+ 16, 3, 0, 0, /* n factor */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* m factor (fake) */
+ 0, 0, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE); /* flags */
+
+static const char *hdmi_parents[] = {"pll_video"};
+NM_CLK(hdmi_clk,
+ CLK_HDMI, "hdmi", hdmi_parents, /* id, name, parents */
+ 0x150, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+static const char *mbus_parents[] = {"osc24M", "pll_periph0-2x", "pll_ddr"};
+NM_CLK(mbus_clk,
+ CLK_MBUS, "mbus", mbus_parents, /* id, name, parents */
+ 0x15C, /* offset */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
+ 0, 3, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
+
+static const char *gpu_parents[] = {"pll_gpu"};
+NM_CLK(gpu_clk,
+ CLK_GPU, "gpu", gpu_parents, /* id, name, parents */
+ 0x1A0, /* offset */
+ 0, 2, 0, 0, /* n factor */
+ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* m factor (fake) */
+ 0, 0, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE); /* flags */
+
+static struct aw_ccung_clk h3_ccu_clks[] = {
+ { .type = AW_CLK_NKMP, .clk.nkmp = &pll_cpux_clk},
+ { .type = AW_CLK_NKMP, .clk.nkmp = &pll_audio_clk},
+ { .type = AW_CLK_NKMP, .clk.nkmp = &pll_periph0_clk},
+ { .type = AW_CLK_NKMP, .clk.nkmp = &pll_periph1_clk},
+ { .type = AW_CLK_NKMP, .clk.nkmp = &pll_ddr_clk},
+ { .type = AW_CLK_FRAC, .clk.frac = &pll_video_clk},
+ { .type = AW_CLK_FRAC, .clk.frac = &pll_ve_clk},
+ { .type = AW_CLK_FRAC, .clk.frac = &pll_gpu_clk},
+ { .type = AW_CLK_FRAC, .clk.frac = &pll_de_clk},
+ { .type = AW_CLK_NM, .clk.nm = &apb2_clk},
+ { .type = AW_CLK_NM, .clk.nm = &nand_clk},
+ { .type = AW_CLK_NM, .clk.nm = &mmc0_clk},
+ { .type = AW_CLK_NM, .clk.nm = &mmc1_clk},
+ { .type = AW_CLK_NM, .clk.nm = &mmc2_clk},
+ { .type = AW_CLK_NM, .clk.nm = &ts_clk},
+ { .type = AW_CLK_NM, .clk.nm = &ce_clk},
+ { .type = AW_CLK_NM, .clk.nm = &spi0_clk},
+ { .type = AW_CLK_NM, .clk.nm = &spi1_clk},
+ { .type = AW_CLK_NM, .clk.nm = &spdif_clk},
+ { .type = AW_CLK_NM, .clk.nm = &dram_clk},
+ { .type = AW_CLK_NM, .clk.nm = &de_clk},
+ { .type = AW_CLK_NM, .clk.nm = &tcon0_clk},
+ { .type = AW_CLK_NM, .clk.nm = &tve_clk},
+ { .type = AW_CLK_NM, .clk.nm = &deinterlace_clk},
+ { .type = AW_CLK_NM, .clk.nm = &csi_sclk_clk},
+ { .type = AW_CLK_NM, .clk.nm = &csi_mclk_clk},
+ { .type = AW_CLK_NM, .clk.nm = &ve_clk},
+ { .type = AW_CLK_NM, .clk.nm = &hdmi_clk},
+ { .type = AW_CLK_NM, .clk.nm = &mbus_clk},
+ { .type = AW_CLK_NM, .clk.nm = &gpu_clk},
+ { .type = AW_CLK_PREDIV_MUX, .clk.prediv_mux = &ahb1_clk},
+ { .type = AW_CLK_PREDIV_MUX, .clk.prediv_mux = &ahb2_clk},
+ { .type = AW_CLK_MUX, .clk.mux = &cpux_clk},
+ { .type = AW_CLK_MUX, .clk.mux = &i2s0mux_clk},
+ { .type = AW_CLK_MUX, .clk.mux = &i2s1mux_clk},
+ { .type = AW_CLK_MUX, .clk.mux = &i2s2mux_clk},
+ { .type = AW_CLK_DIV, .clk.div = &axi_clk},
+ { .type = AW_CLK_DIV, .clk.div = &apb1_clk},
+ { .type = AW_CLK_DIV, .clk.div = &thsdiv_clk},
+ { .type = AW_CLK_FIXED, .clk.fixed = &pll_periph0_2x_clk},
+ { .type = AW_CLK_FIXED, .clk.fixed = &pll_audio_2x_clk},
+ { .type = AW_CLK_FIXED, .clk.fixed = &pll_audio_4x_clk},
+ { .type = AW_CLK_FIXED, .clk.fixed = &pll_audio_8x_clk},
+};
+
+static struct aw_clk_init h3_init_clks[] = {
+ {"ahb1", "pll_periph0", 0, false},
+ {"ahb2", "pll_periph0", 0, false},
+ {"dram", "pll_ddr", 0, false},
+};
+
+static struct ofw_compat_data compat_data[] = {
+#if defined(SOC_ALLWINNER_H3)
+ { "allwinner,sun8i-h3-ccu", 1 },
+#endif
+#if defined(SOC_ALLWINNER_H5)
+ { "allwinner,sun50i-h5-ccu", 1 },
+#endif
+ { NULL, 0},
+};
+
+static int
+ccu_h3_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner H3/H5 Clock Control Unit NG");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ccu_h3_attach(device_t dev)
+{
+ struct aw_ccung_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ sc->resets = h3_ccu_resets;
+ sc->nresets = nitems(h3_ccu_resets);
+ sc->gates = h3_ccu_gates;
+ sc->ngates = nitems(h3_ccu_gates);
+ sc->clks = h3_ccu_clks;
+ sc->nclks = nitems(h3_ccu_clks);
+ sc->clk_init = h3_init_clks;
+ sc->n_clk_init = nitems(h3_init_clks);
+
+ return (aw_ccung_attach(dev));
+}
+
+static device_method_t ccu_h3ng_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ccu_h3_probe),
+ DEVMETHOD(device_attach, ccu_h3_attach),
+
+ DEVMETHOD_END
+};
+
+static devclass_t ccu_h3ng_devclass;
+
+DEFINE_CLASS_1(ccu_h3ng, ccu_h3ng_driver, ccu_h3ng_methods,
+ sizeof(struct aw_ccung_softc), aw_ccung_driver);
+
+EARLY_DRIVER_MODULE(ccu_h3ng, simplebus, ccu_h3ng_driver,
+ ccu_h3ng_devclass, 0, 0, BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE);
diff --git a/sys/arm/allwinner/clkng/ccu_h6.c b/sys/arm/allwinner/clkng/ccu_h6.c
new file mode 100644
index 000000000000..dbecaaff0e6b
--- /dev/null
+++ b/sys/arm/allwinner/clkng/ccu_h6.c
@@ -0,0 +1,501 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2019 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <machine/bus.h>
+
+#include <dev/fdt/simplebus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/extres/clk/clk_div.h>
+#include <dev/extres/clk/clk_fixed.h>
+#include <dev/extres/clk/clk_mux.h>
+
+#include <arm/allwinner/clkng/aw_ccung.h>
+
+#include <dt-bindings/clock/sun50i-h6-ccu.h>
+#include <dt-bindings/reset/sun50i-h6-ccu.h>
+
+/* Non-exported clocks */
+#define CLK_OSC_12M 0
+#define CLK_PLL_CPUX 1
+#define CLK_PLL_DDR0 2
+#define CLK_PLL_PERIPH0_2X 4
+#define CLK_PLL_PERIPH0_4X 5
+#define CLK_PLL_PERIPH1 6
+#define CLK_PLL_PERIPH1_2X 7
+#define CLK_PLL_PERIPH1_4X 8
+#define CLK_PLL_GPU 9
+#define CLK_PLL_VIDEO0 10
+#define CLK_PLL_VIDEO0_4X 11
+#define CLK_PLL_VIDEO1 12
+#define CLK_PLL_VIDEO1_4X 13
+#define CLK_PLL_VE 14
+#define CLK_PLL_DE 14
+#define CLK_PLL_HSIC 16
+
+#define CLK_PSI_AHB1_AHB2 24
+#define CLK_AHB3 25
+#define CLK_APB2 27
+
+static struct aw_ccung_reset h6_ccu_resets[] = {
+ /* PSI_BGR_REG */
+ CCU_RESET(RST_BUS_PSI, 0x79c, 16)
+
+ /* SMHC_BGR_REG */
+ CCU_RESET(RST_BUS_MMC0, 0x84c, 16)
+ CCU_RESET(RST_BUS_MMC1, 0x84c, 17)
+ CCU_RESET(RST_BUS_MMC2, 0x84c, 18)
+
+ /* UART_BGR_REG */
+ CCU_RESET(RST_BUS_UART0, 0x90c, 16)
+ CCU_RESET(RST_BUS_UART1, 0x90c, 17)
+ CCU_RESET(RST_BUS_UART2, 0x90c, 18)
+ CCU_RESET(RST_BUS_UART3, 0x90c, 19)
+
+ /* TWI_BGR_REG */
+ CCU_RESET(RST_BUS_I2C0, 0x91c, 16)
+ CCU_RESET(RST_BUS_I2C1, 0x91c, 17)
+ CCU_RESET(RST_BUS_I2C2, 0x91c, 18)
+ CCU_RESET(RST_BUS_I2C3, 0x91c, 19)
+
+ /* EMAC_BGR_REG */
+ CCU_RESET(RST_BUS_EMAC, 0x97c, 16)
+
+ /* USB0_CLK_REG */
+ CCU_RESET(RST_USB_PHY0, 0xa70, 30)
+
+ /* USB1_CLK_REG */
+ CCU_RESET(RST_USB_PHY1, 0xa74, 30)
+
+ /* USB3_CLK_REG */
+ CCU_RESET(RST_USB_HSIC, 0xa7c, 28)
+ CCU_RESET(RST_USB_PHY3, 0xa7c, 30)
+
+ /* USB_BGR_REG */
+ CCU_RESET(RST_BUS_OHCI0, 0xa8c, 16)
+ CCU_RESET(RST_BUS_OHCI3, 0xa8c, 19)
+ CCU_RESET(RST_BUS_EHCI0, 0xa8c, 20)
+ CCU_RESET(RST_BUS_XHCI, 0xa8c, 21)
+ CCU_RESET(RST_BUS_EHCI3, 0xa8c, 23)
+ CCU_RESET(RST_BUS_OTG, 0xa8c, 24)
+};
+
+static struct aw_ccung_gate h6_ccu_gates[] = {
+ /* PSI_BGR_REG */
+ CCU_GATE(CLK_BUS_PSI, "bus-psi", "psi_ahb1_ahb2", 0x79c, 0)
+
+ /* SMHC_BGR_REG */
+ CCU_GATE(CLK_BUS_MMC0, "bus-mmc0", "ahb3", 0x84c, 0)
+ CCU_GATE(CLK_BUS_MMC1, "bus-mmc1", "ahb3", 0x84c, 1)
+ CCU_GATE(CLK_BUS_MMC2, "bus-mmc2", "ahb3", 0x84c, 2)
+
+ /* UART_BGR_REG Enabling the gate enable weir behavior ... */
+ /* CCU_GATE(CLK_BUS_UART0, "bus-uart0", "apb2", 0x90c, 0) */
+ /* CCU_GATE(CLK_BUS_UART1, "bus-uart1", "apb2", 0x90c, 1) */
+ /* CCU_GATE(CLK_BUS_UART2, "bus-uart2", "apb2", 0x90c, 2) */
+ /* CCU_GATE(CLK_BUS_UART3, "bus-uart3", "apb2", 0x90c, 3) */
+
+ /* TWI_BGR_REG */
+ CCU_GATE(CLK_BUS_I2C0, "bus-i2c0", "apb2", 0x91c, 0)
+ CCU_GATE(CLK_BUS_I2C1, "bus-i2c1", "apb2", 0x91c, 1)
+ CCU_GATE(CLK_BUS_I2C2, "bus-i2c2", "apb2", 0x91c, 2)
+ CCU_GATE(CLK_BUS_I2C3, "bus-i2c3", "apb2", 0x91c, 3)
+
+ /* EMAC_BGR_REG */
+ CCU_GATE(CLK_BUS_EMAC, "bus-emac", "ahb3", 0x97c, 0)
+
+ /* USB0_CLK_REG */
+ CCU_GATE(CLK_USB_PHY0, "usb-phy0", "ahb3", 0xa70, 29)
+ CCU_GATE(CLK_USB_OHCI0, "usb-ohci0", "ahb3", 0xa70, 31)
+
+ /* USB1_CLK_REG */
+ CCU_GATE(CLK_USB_PHY1, "usb-phy1", "ahb3", 0xa74, 29)
+
+ /* USB3_CLK_REG */
+ CCU_GATE(CLK_USB_HSIC, "usb-hsic", "ahb3", 0xa7c, 26)
+ CCU_GATE(CLK_USB_HSIC_12M, "usb-hsic-12M", "ahb3", 0xa7c, 27)
+ CCU_GATE(CLK_USB_PHY3, "usb-phy3", "ahb3", 0xa7c, 29)
+ CCU_GATE(CLK_USB_OHCI3, "usb-ohci3", "ahb3", 0xa7c, 31)
+
+ /* USB_BGR_REG */
+ CCU_GATE(CLK_BUS_OHCI0, "bus-ohci0", "ahb3", 0xa8c, 0)
+ CCU_GATE(CLK_BUS_OHCI3, "bus-ohci3", "ahb3", 0xa8c, 3)
+ CCU_GATE(CLK_BUS_EHCI0, "bus-ehci0", "ahb3", 0xa8c, 4)
+ CCU_GATE(CLK_BUS_XHCI, "bus-xhci", "ahb3", 0xa8c, 5)
+ CCU_GATE(CLK_BUS_EHCI3, "bus-ehci3", "ahb3", 0xa8c, 7)
+ CCU_GATE(CLK_BUS_OTG, "bus-otg", "ahb3", 0xa8c, 8)
+};
+
+static const char *osc12m_parents[] = {"osc24M"};
+FIXED_CLK(osc12m_clk,
+ CLK_OSC_12M, /* id */
+ "osc12M", /* name */
+ osc12m_parents, /* parent */
+ 0, /* freq */
+ 1, /* mult */
+ 2, /* div */
+ 0); /* flags */
+
+static const char *pll_cpux_parents[] = {"osc24M"};
+NP_CLK(pll_cpux_clk,
+ CLK_PLL_CPUX, /* id */
+ "pll_cpux", pll_cpux_parents, /* name, parents */
+ 0x00, /* offset */
+ 8, 7, 0, 0, /* n factor */
+ 0, 2, 0, 0, /* p factor */
+ 31, /* gate */
+ 28, 1000, /* lock */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_LOCK); /* flags */
+
+static const char *pll_ddr0_parents[] = {"osc24M"};
+NMM_CLK(pll_ddr0_clk,
+ CLK_PLL_DDR0, /* id */
+ "pll_ddr0", pll_ddr0_parents, /* name, parents */
+ 0x10, /* offset */
+ 8, 7, 0, 0, /* n factor */
+ 0, 1, 0, 0, /* m0 factor */
+ 1, 1, 0, 0, /* m1 factor */
+ 31, /* gate */
+ 28, 1000, /* lock */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_LOCK); /* flags */
+
+static const char *pll_peri0_4x_parents[] = {"osc24M"};
+NMM_CLK(pll_peri0_4x_clk,
+ CLK_PLL_PERIPH0_4X, /* id */
+ "pll_periph0_4x", pll_peri0_4x_parents, /* name, parents */
+ 0x20, /* offset */
+ 8, 7, 0, 0, /* n factor */
+ 0, 1, 0, 0, /* m0 factor */
+ 1, 1, 0, 0, /* m1 factor */
+ 31, /* gate */
+ 28, 1000, /* lock */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_LOCK); /* flags */
+static const char *pll_peri0_2x_parents[] = {"pll_periph0_4x"};
+FIXED_CLK(pll_peri0_2x_clk,
+ CLK_PLL_PERIPH0_2X, /* id */
+ "pll_periph0_2x", /* name */
+ pll_peri0_2x_parents, /* parent */
+ 0, /* freq */
+ 1, /* mult */
+ 2, /* div */
+ 0); /* flags */
+static const char *pll_peri0_parents[] = {"pll_periph0_4x"};
+FIXED_CLK(pll_peri0_clk,
+ CLK_PLL_PERIPH0, /* id */
+ "pll_periph0", /* name */
+ pll_peri0_parents, /* parent */
+ 0, /* freq */
+ 1, /* mult */
+ 4, /* div */
+ 0); /* flags */
+
+static const char *pll_peri1_4x_parents[] = {"osc24M"};
+NMM_CLK(pll_peri1_4x_clk,
+ CLK_PLL_PERIPH1_4X, /* id */
+ "pll_periph1_4x", pll_peri1_4x_parents, /* name, parents */
+ 0x28, /* offset */
+ 8, 7, 0, 0, /* n factor */
+ 0, 1, 0, 0, /* m0 factor */
+ 1, 1, 0, 0, /* m1 factor */
+ 31, /* gate */
+ 28, 1000, /* lock */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_LOCK); /* flags */
+static const char *pll_peri1_2x_parents[] = {"pll_periph1_4x"};
+FIXED_CLK(pll_peri1_2x_clk,
+ CLK_PLL_PERIPH1_2X, /* id */
+ "pll_periph1_2x", /* name */
+ pll_peri1_2x_parents, /* parent */
+ 0, /* freq */
+ 1, /* mult */
+ 2, /* div */
+ 0); /* flags */
+static const char *pll_peri1_parents[] = {"pll_periph1_4x"};
+FIXED_CLK(pll_peri1_clk,
+ CLK_PLL_PERIPH1, /* id */
+ "pll_periph1", /* name */
+ pll_peri1_parents, /* parent */
+ 0, /* freq */
+ 1, /* mult */
+ 4, /* div */
+ 0); /* flags */
+
+static const char *pll_gpu_parents[] = {"osc24M"};
+NMM_CLK(pll_gpu_clk,
+ CLK_PLL_GPU, /* id */
+ "pll_gpu", pll_gpu_parents, /* name, parents */
+ 0x30, /* offset */
+ 8, 7, 0, 0, /* n factor */
+ 0, 1, 0, 0, /* m0 factor */
+ 1, 1, 0, 0, /* m1 factor */
+ 31, /* gate */
+ 28, 1000, /* lock */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_LOCK); /* flags */
+
+static const char *pll_video0_4x_parents[] = {"osc24M"};
+NMM_CLK(pll_video0_4x_clk,
+ CLK_PLL_VIDEO0_4X, /* id */
+ "pll_video0_4x", pll_video0_4x_parents, /* name, parents */
+ 0x40, /* offset */
+ 8, 7, 0, 0, /* n factor */
+ 0, 1, 0, 0, /* m0 factor */
+ 1, 1, 0, 0, /* m1 factor */
+ 31, /* gate */
+ 28, 1000, /* lock */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_LOCK); /* flags */
+static const char *pll_video0_parents[] = {"pll_video0_4x"};
+FIXED_CLK(pll_video0_clk,
+ CLK_PLL_VIDEO0, /* id */
+ "pll_video0", /* name */
+ pll_video0_parents, /* parent */
+ 0, /* freq */
+ 1, /* mult */
+ 4, /* div */
+ 0); /* flags */
+
+static const char *pll_video1_4x_parents[] = {"osc24M"};
+NMM_CLK(pll_video1_4x_clk,
+ CLK_PLL_VIDEO1_4X, /* id */
+ "pll_video1_4x", pll_video1_4x_parents, /* name, parents */
+ 0x48, /* offset */
+ 8, 7, 0, 0, /* n factor */
+ 0, 1, 0, 0, /* m0 factor */
+ 1, 1, 0, 0, /* m1 factor */
+ 31, /* gate */
+ 28, 1000, /* lock */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_LOCK); /* flags */
+static const char *pll_video1_parents[] = {"pll_video1_4x"};
+FIXED_CLK(pll_video1_clk,
+ CLK_PLL_VIDEO1, /* id */
+ "pll_video1", /* name */
+ pll_video1_parents, /* parent */
+ 0, /* freq */
+ 1, /* mult */
+ 4, /* div */
+ 0); /* flags */
+
+static const char *pll_ve_parents[] = {"osc24M"};
+NMM_CLK(pll_ve_clk,
+ CLK_PLL_VE, /* id */
+ "pll_ve", pll_ve_parents, /* name, parents */
+ 0x58, /* offset */
+ 8, 7, 0, 0, /* n factor */
+ 0, 1, 0, 0, /* m0 factor */
+ 1, 1, 0, 0, /* m1 factor */
+ 31, /* gate */
+ 28, 1000, /* lock */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_LOCK); /* flags */
+
+static const char *pll_de_parents[] = {"osc24M"};
+NMM_CLK(pll_de_clk,
+ CLK_PLL_DE, /* id */
+ "pll_de", pll_de_parents, /* name, parents */
+ 0x60, /* offset */
+ 8, 7, 0, 0, /* n factor */
+ 0, 1, 0, 0, /* m0 factor */
+ 1, 1, 0, 0, /* m1 factor */
+ 31, /* gate */
+ 28, 1000, /* lock */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_LOCK); /* flags */
+
+static const char *pll_hsic_parents[] = {"osc24M"};
+NMM_CLK(pll_hsic_clk,
+ CLK_PLL_HSIC, /* id */
+ "pll_hsic", pll_hsic_parents, /* name, parents */
+ 0x70, /* offset */
+ 8, 7, 0, 0, /* n factor */
+ 0, 1, 0, 0, /* m0 factor */
+ 1, 1, 0, 0, /* m1 factor */
+ 31, /* gate */
+ 28, 1000, /* lock */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_LOCK); /* flags */
+
+/* PLL_AUDIO missing */
+
+/* CPUX_AXI missing */
+
+static const char *psi_ahb1_ahb2_parents[] = {"osc24M", "osc32k", "iosc", "pll_periph0"};
+NM_CLK(psi_ahb1_ahb2_clk,
+ CLK_PSI_AHB1_AHB2, "psi_ahb1_ahb2", psi_ahb1_ahb2_parents, /* id, name, parents */
+ 0x510, /* offset */
+ 8, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 2, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 0, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_REPARENT); /* flags */
+
+static const char *ahb3_parents[] = {"osc24M", "osc32k", "psi_ahb1_ahb2", "pll_periph0"};
+NM_CLK(ahb3_clk,
+ CLK_AHB3, "ahb3", ahb3_parents, /* id, name, parents */
+ 0x51C, /* offset */
+ 8, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 2, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 0, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_REPARENT); /* flags */
+
+static const char *apb1_parents[] = {"osc24M", "osc32k", "psi_ahb1_ahb2", "pll_periph0"};
+NM_CLK(apb1_clk,
+ CLK_APB1, "apb1", apb1_parents, /* id, name, parents */
+ 0x520, /* offset */
+ 8, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 2, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 0, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_REPARENT); /* flags */
+
+static const char *apb2_parents[] = {"osc24M", "osc32k", "psi_ahb1_ahb2", "pll_periph0"};
+NM_CLK(apb2_clk,
+ CLK_APB2, "apb2", apb2_parents, /* id, name, parents */
+ 0x524, /* offset */
+ 8, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 2, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 0, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_REPARENT); /* flags */
+
+/* Missing MBUS clock */
+
+static const char *mod_parents[] = {"osc24M", "pll_periph0_2x", "pll_periph1_2x"};
+NM_CLK(mmc0_clk,
+ CLK_MMC0, "mmc0", mod_parents, /* id, name, parents */
+ 0x830, /* offset */
+ 8, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX |
+ AW_CLK_REPARENT); /* flags */
+
+NM_CLK(mmc1_clk,
+ CLK_MMC1, "mmc1", mod_parents, /* id, name, parents */
+ 0x834, /* offset */
+ 8, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX |
+ AW_CLK_REPARENT); /* flags */
+
+NM_CLK(mmc2_clk,
+ CLK_MMC2, "mmc2", mod_parents, /* id, name, parents */
+ 0x838, /* offset */
+ 8, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
+ 0, 4, 0, 0, /* m factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_GATE | AW_CLK_HAS_MUX |
+ AW_CLK_REPARENT); /* flags */
+
+static struct aw_ccung_clk h6_ccu_clks[] = {
+ { .type = AW_CLK_NP, .clk.np = &pll_cpux_clk},
+ { .type = AW_CLK_NMM, .clk.nmm = &pll_ddr0_clk},
+ { .type = AW_CLK_NMM, .clk.nmm = &pll_peri0_4x_clk},
+ { .type = AW_CLK_NMM, .clk.nmm = &pll_peri1_4x_clk},
+ { .type = AW_CLK_NMM, .clk.nmm = &pll_gpu_clk},
+ { .type = AW_CLK_NMM, .clk.nmm = &pll_video0_4x_clk},
+ { .type = AW_CLK_NMM, .clk.nmm = &pll_video1_4x_clk},
+ { .type = AW_CLK_NMM, .clk.nmm = &pll_ve_clk},
+ { .type = AW_CLK_NMM, .clk.nmm = &pll_de_clk},
+ { .type = AW_CLK_NMM, .clk.nmm = &pll_hsic_clk},
+
+ { .type = AW_CLK_NM, .clk.nm = &psi_ahb1_ahb2_clk},
+ { .type = AW_CLK_NM, .clk.nm = &ahb3_clk},
+ { .type = AW_CLK_NM, .clk.nm = &apb1_clk},
+ { .type = AW_CLK_NM, .clk.nm = &apb2_clk},
+
+ { .type = AW_CLK_NM, .clk.nm = &mmc0_clk},
+ { .type = AW_CLK_NM, .clk.nm = &mmc1_clk},
+ { .type = AW_CLK_NM, .clk.nm = &mmc2_clk},
+
+ { .type = AW_CLK_FIXED, .clk.fixed = &osc12m_clk},
+ { .type = AW_CLK_FIXED, .clk.fixed = &pll_peri0_2x_clk},
+ { .type = AW_CLK_FIXED, .clk.fixed = &pll_peri0_clk},
+ { .type = AW_CLK_FIXED, .clk.fixed = &pll_peri1_2x_clk},
+ { .type = AW_CLK_FIXED, .clk.fixed = &pll_peri1_clk},
+ { .type = AW_CLK_FIXED, .clk.fixed = &pll_video0_clk},
+ { .type = AW_CLK_FIXED, .clk.fixed = &pll_video1_clk},
+};
+
+static int
+ccu_h6_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "allwinner,sun50i-h6-ccu"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner H6 Clock Control Unit NG");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ccu_h6_attach(device_t dev)
+{
+ struct aw_ccung_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ sc->resets = h6_ccu_resets;
+ sc->nresets = nitems(h6_ccu_resets);
+ sc->gates = h6_ccu_gates;
+ sc->ngates = nitems(h6_ccu_gates);
+ sc->clks = h6_ccu_clks;
+ sc->nclks = nitems(h6_ccu_clks);
+
+ return (aw_ccung_attach(dev));
+}
+
+static device_method_t ccu_h6ng_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ccu_h6_probe),
+ DEVMETHOD(device_attach, ccu_h6_attach),
+
+ DEVMETHOD_END
+};
+
+static devclass_t ccu_h6ng_devclass;
+
+DEFINE_CLASS_1(ccu_h6ng, ccu_h6ng_driver, ccu_h6ng_methods,
+ sizeof(struct aw_ccung_softc), aw_ccung_driver);
+
+EARLY_DRIVER_MODULE(ccu_h6ng, simplebus, ccu_h6ng_driver,
+ ccu_h6ng_devclass, 0, 0, BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE);
diff --git a/sys/arm/allwinner/clkng/ccu_h6_r.c b/sys/arm/allwinner/clkng/ccu_h6_r.c
new file mode 100644
index 000000000000..1f1ca8c1054a
--- /dev/null
+++ b/sys/arm/allwinner/clkng/ccu_h6_r.c
@@ -0,0 +1,172 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2019 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <machine/bus.h>
+
+#include <dev/fdt/simplebus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/extres/clk/clk_div.h>
+#include <dev/extres/clk/clk_fixed.h>
+#include <dev/extres/clk/clk_mux.h>
+
+#include <arm/allwinner/clkng/aw_ccung.h>
+
+#include <dt-bindings/clock/sun50i-h6-r-ccu.h>
+#include <dt-bindings/reset/sun50i-h6-r-ccu.h>
+
+/* Non-exported clocks */
+#define CLK_R_AHB 1
+#define CLK_R_APB2 3
+
+static struct aw_ccung_reset ccu_sun50i_h6_r_resets[] = {
+ CCU_RESET(RST_R_APB1_TIMER, 0x11c, 16)
+ CCU_RESET(RST_R_APB1_TWD, 0x12c, 16)
+ CCU_RESET(RST_R_APB1_PWM, 0x13c, 16)
+ CCU_RESET(RST_R_APB2_UART, 0x18c, 16)
+ CCU_RESET(RST_R_APB2_I2C, 0x19c, 16)
+ CCU_RESET(RST_R_APB1_IR, 0x1cc, 16)
+ CCU_RESET(RST_R_APB1_W1, 0x1ec, 16)
+};
+
+static struct aw_ccung_gate ccu_sun50i_h6_r_gates[] = {
+ CCU_GATE(CLK_R_APB1_TIMER, "r_apb1-timer", "r_apb1", 0x11c, 0)
+ CCU_GATE(CLK_R_APB1_TWD, "r_apb1-twd", "r_apb1", 0x12c, 0)
+ CCU_GATE(CLK_R_APB1_PWM, "r_apb1-pwm", "r_apb1", 0x13c, 0)
+ CCU_GATE(CLK_R_APB2_UART, "r_apb1-uart", "r_apb2", 0x18c, 0)
+ CCU_GATE(CLK_R_APB2_I2C, "r_apb1-i2c", "r_apb2", 0x19c, 0)
+ CCU_GATE(CLK_R_APB1_IR, "r_apb1-ir", "r_apb1", 0x1cc, 0)
+ CCU_GATE(CLK_R_APB1_W1, "r_apb1-w1", "r_apb1", 0x1ec, 0)
+};
+
+static const char *ar100_parents[] = {"osc24M", "osc32k", "pll_periph0", "iosc"};
+PREDIV_CLK(ar100_clk, CLK_AR100, /* id */
+ "ar100", ar100_parents, /* name, parents */
+ 0x00, /* offset */
+ 16, 2, /* mux */
+ 4, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* div */
+ 8, 5, 0, AW_CLK_FACTOR_HAS_COND, /* prediv */
+ 16, 2, 2); /* prediv condition */
+
+static const char *r_ahb_parents[] = {"ar100"};
+FIXED_CLK(r_ahb_clk,
+ CLK_R_AHB, /* id */
+ "r_ahb", /* name */
+ r_ahb_parents, /* parent */
+ 0, /* freq */
+ 1, /* mult */
+ 1, /* div */
+ 0); /* flags */
+
+static const char *r_apb1_parents[] = {"r_ahb"};
+DIV_CLK(r_apb1_clk,
+ CLK_R_APB1, /* id */
+ "r_apb1", r_apb1_parents, /* name, parents */
+ 0x0c, /* offset */
+ 0, 2, /* shift, width */
+ 0, NULL); /* flags, div table */
+
+static const char *r_apb2_parents[] = {"osc24M", "osc32k", "pll_periph0", "iosc"};
+PREDIV_CLK(r_apb2_clk, CLK_R_APB2, /* id */
+ "r_apb2", r_apb2_parents, /* name, parents */
+ 0x10, /* offset */
+ 16, 2, /* mux */
+ 4, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* div */
+ 8, 5, 0, AW_CLK_FACTOR_HAS_COND, /* prediv */
+ 16, 2, 2); /* prediv condition */
+
+static struct aw_ccung_clk clks[] = {
+ { .type = AW_CLK_PREDIV_MUX, .clk.prediv_mux = &ar100_clk},
+ { .type = AW_CLK_FIXED, .clk.fixed = &r_ahb_clk},
+ { .type = AW_CLK_DIV, .clk.div = &r_apb1_clk},
+ { .type = AW_CLK_PREDIV_MUX, .clk.prediv_mux = &r_apb2_clk},
+};
+
+static struct ofw_compat_data compat_data[] = {
+ { "allwinner,sun50i-h6-r-ccu", 1 },
+ { NULL, 0},
+};
+
+static int
+ccu_sun50i_h6_r_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner SUN50I_H6_R Clock Control Unit NG");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ccu_sun50i_h6_r_attach(device_t dev)
+{
+ struct aw_ccung_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ sc->resets = ccu_sun50i_h6_r_resets;
+ sc->nresets = nitems(ccu_sun50i_h6_r_resets);
+ sc->gates = ccu_sun50i_h6_r_gates;
+ sc->ngates = nitems(ccu_sun50i_h6_r_gates);
+ sc->clks = clks;
+ sc->nclks = nitems(clks);
+
+ return (aw_ccung_attach(dev));
+}
+
+static device_method_t ccu_sun50i_h6_r_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ccu_sun50i_h6_r_probe),
+ DEVMETHOD(device_attach, ccu_sun50i_h6_r_attach),
+
+ DEVMETHOD_END
+};
+
+static devclass_t ccu_sun50i_h6_r_devclass;
+
+DEFINE_CLASS_1(ccu_sun50i_h6_r, ccu_sun50i_h6_r_driver, ccu_sun50i_h6_r_methods,
+ sizeof(struct aw_ccung_softc), aw_ccung_driver);
+
+EARLY_DRIVER_MODULE(ccu_sun50i_h6_r, simplebus, ccu_sun50i_h6_r_driver,
+ ccu_sun50i_h6_r_devclass, 0, 0, BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE);
diff --git a/sys/arm/allwinner/clkng/ccu_sun8i_r.c b/sys/arm/allwinner/clkng/ccu_sun8i_r.c
new file mode 100644
index 000000000000..05558f4dc73e
--- /dev/null
+++ b/sys/arm/allwinner/clkng/ccu_sun8i_r.c
@@ -0,0 +1,265 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2017,2018 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <machine/bus.h>
+
+#include <dev/fdt/simplebus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#if defined(__aarch64__)
+#include "opt_soc.h"
+#endif
+
+#include <dev/extres/clk/clk_div.h>
+#include <dev/extres/clk/clk_fixed.h>
+#include <dev/extres/clk/clk_mux.h>
+
+#include <arm/allwinner/clkng/aw_ccung.h>
+
+#include <dt-bindings/clock/sun8i-r-ccu.h>
+#include <dt-bindings/reset/sun8i-r-ccu.h>
+
+/* Non-exported clocks */
+#define CLK_AHB0 1
+#define CLK_APB0 2
+
+static struct aw_ccung_reset ccu_sun8i_r_resets[] = {
+ CCU_RESET(RST_APB0_IR, 0xb0, 1)
+ CCU_RESET(RST_APB0_TIMER, 0xb0, 2)
+ CCU_RESET(RST_APB0_RSB, 0xb0, 3)
+ CCU_RESET(RST_APB0_UART, 0xb0, 4)
+ CCU_RESET(RST_APB0_I2C, 0xb0, 6)
+};
+
+static struct aw_ccung_gate ccu_sun8i_r_gates[] = {
+ CCU_GATE(CLK_APB0_PIO, "apb0-pio", "apb0", 0x28, 0)
+ CCU_GATE(CLK_APB0_IR, "apb0-ir", "apb0", 0x28, 1)
+ CCU_GATE(CLK_APB0_TIMER, "apb0-timer", "apb0", 0x28, 2)
+ CCU_GATE(CLK_APB0_RSB, "apb0-rsb", "apb0", 0x28, 3)
+ CCU_GATE(CLK_APB0_UART, "apb0-uart", "apb0", 0x28, 4)
+ CCU_GATE(CLK_APB0_I2C, "apb0-i2c", "apb0", 0x28, 6)
+ CCU_GATE(CLK_APB0_TWD, "apb0-twd", "apb0", 0x28, 7)
+};
+
+static const char *ar100_parents[] = {"osc32k", "osc24M", "pll_periph0", "iosc"};
+static const char *a83t_ar100_parents[] = {"osc16M-d512", "osc24M", "pll_periph", "osc16M"};
+PREDIV_CLK(ar100_clk, CLK_AR100, /* id */
+ "ar100", ar100_parents, /* name, parents */
+ 0x00, /* offset */
+ 16, 2, /* mux */
+ 4, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* div */
+ 8, 5, 0, AW_CLK_FACTOR_HAS_COND, /* prediv */
+ 16, 2, 2); /* prediv condition */
+PREDIV_CLK(a83t_ar100_clk, CLK_AR100, /* id */
+ "ar100", a83t_ar100_parents, /* name, parents */
+ 0x00, /* offset */
+ 16, 2, /* mux */
+ 4, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* div */
+ 8, 5, 0, AW_CLK_FACTOR_HAS_COND, /* prediv */
+ 16, 2, 2); /* prediv condition */
+
+static const char *ahb0_parents[] = {"ar100"};
+FIXED_CLK(ahb0_clk,
+ CLK_AHB0, /* id */
+ "ahb0", /* name */
+ ahb0_parents, /* parent */
+ 0, /* freq */
+ 1, /* mult */
+ 1, /* div */
+ 0); /* flags */
+
+static const char *apb0_parents[] = {"ahb0"};
+DIV_CLK(apb0_clk,
+ CLK_APB0, /* id */
+ "apb0", apb0_parents, /* name, parents */
+ 0x0c, /* offset */
+ 0, 2, /* shift, width */
+ 0, NULL); /* flags, div table */
+
+static const char *r_ccu_ir_parents[] = {"osc32k", "osc24M"};
+NM_CLK(r_ccu_ir_clk,
+ CLK_IR, /* id */
+ "ir", r_ccu_ir_parents, /* names, parents */
+ 0x54, /* offset */
+ 0, 4, 0, 0, /* N factor */
+ 16, 2, 0, 0, /* M factor */
+ 24, 2, /* mux */
+ 31, /* gate */
+ AW_CLK_HAS_MUX | AW_CLK_REPARENT | AW_CLK_HAS_GATE);/* flags */
+
+static const char *a83t_ir_parents[] = {"osc16M", "osc24M"};
+static struct aw_clk_nm_def a83t_ir_clk = {
+ .clkdef = {
+ .id = CLK_IR,
+ .name = "ir",
+ .parent_names = a83t_ir_parents,
+ .parent_cnt = nitems(a83t_ir_parents),
+ },
+ .offset = 0x54,
+ .n = {.shift = 0, .width = 4, .flags = AW_CLK_FACTOR_POWER_OF_TWO, },
+ .m = {.shift = 16, .width = 2},
+ .prediv = {
+ .cond_shift = 24,
+ .cond_width = 2,
+ .cond_value = 0,
+ .value = 16
+ },
+ .mux_shift = 24,
+ .mux_width = 2,
+ .flags = AW_CLK_HAS_MUX | AW_CLK_HAS_PREDIV,
+};
+
+static struct aw_ccung_clk clks[] = {
+ { .type = AW_CLK_PREDIV_MUX, .clk.prediv_mux = &ar100_clk},
+ { .type = AW_CLK_DIV, .clk.div = &apb0_clk},
+ { .type = AW_CLK_FIXED, .clk.fixed = &ahb0_clk},
+ { .type = AW_CLK_NM, .clk.nm = &r_ccu_ir_clk},
+};
+
+static struct aw_ccung_clk a83t_clks[] = {
+ { .type = AW_CLK_PREDIV_MUX, .clk.prediv_mux = &a83t_ar100_clk},
+ { .type = AW_CLK_DIV, .clk.div = &apb0_clk},
+ { .type = AW_CLK_FIXED, .clk.fixed = &ahb0_clk},
+ { .type = AW_CLK_NM, .clk.nm = &a83t_ir_clk},
+};
+
+static struct ofw_compat_data compat_data[] = {
+#if defined(SOC_ALLWINNER_H3) || defined(SOC_ALLWINNER_H5)
+ { "allwinner,sun8i-h3-r-ccu", 1 },
+#endif
+#if defined(SOC_ALLWINNER_A64)
+ { "allwinner,sun50i-a64-r-ccu", 1 },
+#endif
+ { NULL, 0},
+};
+
+static int
+ccu_sun8i_r_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner SUN8I_R Clock Control Unit NG");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ccu_sun8i_r_attach(device_t dev)
+{
+ struct aw_ccung_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ sc->resets = ccu_sun8i_r_resets;
+ sc->nresets = nitems(ccu_sun8i_r_resets);
+ sc->gates = ccu_sun8i_r_gates;
+ sc->ngates = nitems(ccu_sun8i_r_gates);
+ sc->clks = clks;
+ sc->nclks = nitems(clks);
+
+ return (aw_ccung_attach(dev));
+}
+
+static device_method_t ccu_sun8i_r_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ccu_sun8i_r_probe),
+ DEVMETHOD(device_attach, ccu_sun8i_r_attach),
+
+ DEVMETHOD_END
+};
+
+static devclass_t ccu_sun8i_r_devclass;
+
+DEFINE_CLASS_1(ccu_sun8i_r, ccu_sun8i_r_driver, ccu_sun8i_r_methods,
+ sizeof(struct aw_ccung_softc), aw_ccung_driver);
+
+EARLY_DRIVER_MODULE(ccu_sun8i_r, simplebus, ccu_sun8i_r_driver,
+ ccu_sun8i_r_devclass, 0, 0, BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE);
+
+static int
+ccu_a83t_r_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "allwinner,sun8i-a83t-r-ccu"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner A83T_R Clock Control Unit NG");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ccu_a83t_r_attach(device_t dev)
+{
+ struct aw_ccung_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ sc->resets = ccu_sun8i_r_resets;
+ sc->nresets = nitems(ccu_sun8i_r_resets);
+ sc->gates = ccu_sun8i_r_gates;
+ sc->ngates = nitems(ccu_sun8i_r_gates);
+ sc->clks = a83t_clks;
+ sc->nclks = nitems(a83t_clks);
+
+ return (aw_ccung_attach(dev));
+}
+
+static device_method_t ccu_a83t_r_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ccu_a83t_r_probe),
+ DEVMETHOD(device_attach, ccu_a83t_r_attach),
+
+ DEVMETHOD_END
+};
+
+static devclass_t ccu_a83t_r_devclass;
+
+DEFINE_CLASS_1(ccu_a83t_r, ccu_a83t_r_driver, ccu_a83t_r_methods,
+ sizeof(struct aw_ccung_softc), aw_ccung_driver);
+
+EARLY_DRIVER_MODULE(ccu_a83t_r, simplebus, ccu_a83t_r_driver,
+ ccu_a83t_r_devclass, 0, 0, BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE);
diff --git a/sys/arm/allwinner/files.allwinner b/sys/arm/allwinner/files.allwinner
new file mode 100644
index 000000000000..6148c9787c06
--- /dev/null
+++ b/sys/arm/allwinner/files.allwinner
@@ -0,0 +1,47 @@
+# $FreeBSD$
+
+arm/allwinner/a10_ahci.c optional ahci
+arm/allwinner/a10_codec.c optional sound
+arm/allwinner/a10_dmac.c optional a10_dmac
+arm/allwinner/a31_dmac.c optional a31_dmac
+arm/allwinner/a10_sramc.c optional SOC_ALLWINNER_A10
+arm/allwinner/aw_gpio.c optional gpio
+arm/allwinner/aw_if_dwc.c optional dwc
+arm/allwinner/aw_machdep.c standard
+arm/allwinner/aw_mmc.c optional mmc | mmccam
+arm/allwinner/aw_mp.c optional smp
+arm/allwinner/aw_nmi.c standard
+arm/allwinner/aw_rsb.c optional rsb | p2wi
+arm/allwinner/aw_rtc.c optional aw_rtc
+arm/allwinner/aw_syscon.c optional ext_resources syscon
+arm/allwinner/aw_ts.c optional aw_thermal
+arm/allwinner/aw_usbphy.c optional ehci | ohci
+arm/allwinner/aw_wdog.c optional aw_wdog
+arm/allwinner/axp209.c optional axp209
+arm/allwinner/axp81x.c optional axp81x
+arm/allwinner/if_awg.c optional awg ext_resources syscon
+arm/allwinner/if_emac.c optional emac
+arm/allwinner/sunxi_dma_if.m optional a10_dmac | a31_dmac
+dev/iicbus/twsi/a10_twsi.c optional twsi
+dev/usb/controller/generic_ohci.c optional ohci
+dev/usb/controller/generic_usb_if.m optional ohci
+dev/usb/controller/generic_ehci.c optional ehci
+dev/usb/controller/generic_ehci_fdt.c optional ehci
+dev/usb/controller/musb_otg_allwinner.c optional musb
+arm/allwinner/aw_sid.c optional aw_sid
+arm/allwinner/aw_thermal.c optional aw_thermal
+arm/allwinner/aw_cir.c optional aw_cir evdev
+
+arm/allwinner/aw_reset.c standard
+arm/allwinner/aw_ccu.c standard
+arm/allwinner/aw_gmacclk.c standard
+
+arm/allwinner/clkng/aw_ccung.c standard
+arm/allwinner/clkng/aw_clk_frac.c standard
+arm/allwinner/clkng/aw_clk_m.c standard
+arm/allwinner/clkng/aw_clk_mipi.c standard
+arm/allwinner/clkng/aw_clk_nkmp.c standard
+arm/allwinner/clkng/aw_clk_nm.c standard
+arm/allwinner/clkng/aw_clk_np.c standard
+arm/allwinner/clkng/aw_clk_nmm.c standard
+arm/allwinner/clkng/aw_clk_prediv_mux.c standard
diff --git a/sys/arm/allwinner/files.allwinner_up b/sys/arm/allwinner/files.allwinner_up
new file mode 100644
index 000000000000..c776c3de791e
--- /dev/null
+++ b/sys/arm/allwinner/files.allwinner_up
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+arm/allwinner/a10_timer.c standard
diff --git a/sys/arm/allwinner/h3/files.h3 b/sys/arm/allwinner/h3/files.h3
new file mode 100644
index 000000000000..bce6ca07c553
--- /dev/null
+++ b/sys/arm/allwinner/h3/files.h3
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+arm/allwinner/clkng/ccu_h3.c standard
+arm/allwinner/clkng/ccu_sun8i_r.c standard
+arm/allwinner/h3/h3_padconf.c standard
+arm/allwinner/h3/h3_r_padconf.c standard
diff --git a/sys/arm/allwinner/h3/h3_padconf.c b/sys/arm/allwinner/h3/h3_padconf.c
new file mode 100644
index 000000000000..4d2960ce4c9f
--- /dev/null
+++ b/sys/arm/allwinner/h3/h3_padconf.c
@@ -0,0 +1,150 @@
+/*-
+ * Copyright (c) 2016-2017 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/types.h>
+
+#if defined(__aarch64__)
+#include "opt_soc.h"
+#endif
+
+#include <arm/allwinner/allwinner_pinctrl.h>
+
+#if defined(SOC_ALLWINNER_H3) || defined(SOC_ALLWINNER_H5)
+
+const static struct allwinner_pins h3_pins[] = {
+ {"PA0", 0, 0, {"gpio_in", "gpio_out", "uart2", "jtag", NULL, NULL, "pa_eint0", NULL}, 6, 0},
+ {"PA1", 0, 1, {"gpio_in", "gpio_out", "uart2", "jtag", NULL, NULL, "pa_eint1", NULL}, 6, 1},
+ {"PA2", 0, 2, {"gpio_in", "gpio_out", "uart2", "jtag", NULL, NULL, "pa_eint2", NULL}, 6, 2},
+ {"PA3", 0, 3, {"gpio_in", "gpio_out", "uart2", "jtag", NULL, NULL, "pa_eint3", NULL}, 6, 3},
+ {"PA4", 0, 4, {"gpio_in", "gpio_out", "uart0", NULL, NULL, NULL, "pa_eint4", NULL}, 6, 4},
+ {"PA5", 0, 5, {"gpio_in", "gpio_out", "uart0", "pwm0", NULL, NULL, "pa_eint5", NULL}, 6, 5},
+ {"PA6", 0, 6, {"gpio_in", "gpio_out", "sim", NULL, NULL, NULL, "pa_eint6", NULL}, 6, 6},
+ {"PA7", 0, 7, {"gpio_in", "gpio_out", "sim", NULL, NULL, NULL, "pa_eint7", NULL}, 6, 7},
+ {"PA8", 0, 8, {"gpio_in", "gpio_out", "sim", NULL, NULL, NULL, "pa_eint8", NULL}, 6, 8},
+ {"PA9", 0, 9, {"gpio_in", "gpio_out", "sim", NULL, NULL, NULL, "pa_eint9", NULL}, 6, 9},
+ {"PA10", 0, 10, {"gpio_in", "gpio_out", "sim", NULL, NULL, NULL, "pa_eint10", NULL}, 6, 10},
+ {"PA11", 0, 11, {"gpio_in", "gpio_out", "i2c0", "di", NULL, NULL, "pa_eint11", NULL}, 6, 11},
+ {"PA12", 0, 12, {"gpio_in", "gpio_out", "i2c0", "di", NULL, NULL, "pa_eint12", NULL}, 6, 12},
+ {"PA13", 0, 13, {"gpio_in", "gpio_out", "spi1", "uart3", NULL, NULL, "pa_eint13", NULL}, 6, 13},
+ {"PA14", 0, 14, {"gpio_in", "gpio_out", "spi1", "uart3", NULL, NULL, "pa_eint14", NULL}, 6, 14},
+ {"PA15", 0, 15, {"gpio_in", "gpio_out", "spi1", "uart3", NULL, NULL, "pa_eint15", NULL}, 6, 15},
+ {"PA16", 0, 16, {"gpio_in", "gpio_out", "spi1", "uart3", NULL, NULL, "pa_eint16", NULL}, 6, 16},
+ {"PA17", 0, 17, {"gpio_in", "gpio_out", "spdif", NULL, NULL, NULL, "pa_eint17", NULL}, 6, 17},
+ {"PA18", 0, 18, {"gpio_in", "gpio_out", "i2s0", "i2c1", NULL, NULL, "pa_eint18", NULL}, 6, 18},
+ {"PA19", 0, 19, {"gpio_in", "gpio_out", "i2s0", "i2c1", NULL, NULL, "pa_eint19", NULL}, 6, 19},
+ {"PA20", 0, 20, {"gpio_in", "gpio_out", "i2s0", "sim", NULL, NULL, "pa_eint20", NULL}, 6, 20},
+ {"PA21", 0, 21, {"gpio_in", "gpio_out", "i2s0", "sim", NULL, NULL, "pa_eint21", NULL}, 6, 21},
+
+ {"PC0", 2, 0, {"gpio_in", "gpio_out", "nand", "spi0", NULL, NULL, NULL, NULL}},
+ {"PC1", 2, 1, {"gpio_in", "gpio_out", "nand", "spi0", NULL, NULL, NULL, NULL}},
+ {"PC2", 2, 2, {"gpio_in", "gpio_out", "nand", "spi0", NULL, NULL, NULL, NULL}},
+ {"PC3", 2, 3, {"gpio_in", "gpio_out", "nand", "spi0", NULL, NULL, NULL, NULL}},
+ {"PC4", 2, 4, {"gpio_in", "gpio_out", "nand", NULL, NULL, NULL, NULL, NULL}},
+ {"PC5", 2, 5, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC6", 2, 6, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC7", 2, 7, {"gpio_in", "gpio_out", "nand", NULL, NULL, NULL, NULL, NULL}},
+ {"PC8", 2, 8, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC9", 2, 9, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC10", 2, 10, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC11", 2, 11, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC12", 2, 12, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC13", 2, 13, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC14", 2, 14, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC15", 2, 15, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}},
+ {"PC16", 2, 16, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}},
+
+ {"PD0", 3, 0, {"gpio_in", "gpio_out", "emac", NULL, NULL, NULL, NULL, NULL}},
+ {"PD1", 3, 1, {"gpio_in", "gpio_out", "emac", NULL, NULL, NULL, NULL, NULL}},
+ {"PD2", 3, 2, {"gpio_in", "gpio_out", "emac", NULL, NULL, NULL, NULL, NULL}},
+ {"PD3", 3, 3, {"gpio_in", "gpio_out", "emac", NULL, NULL, NULL, NULL, NULL}},
+ {"PD4", 3, 4, {"gpio_in", "gpio_out", "emac", NULL, NULL, NULL, NULL, NULL}},
+ {"PD5", 3, 5, {"gpio_in", "gpio_out", "emac", NULL, NULL, NULL, NULL, NULL}},
+ {"PD6", 3, 6, {"gpio_in", "gpio_out", "emac", NULL, NULL, NULL, NULL, NULL}},
+ {"PD7", 3, 7, {"gpio_in", "gpio_out", "emac", NULL, NULL, NULL, NULL, NULL}},
+ {"PD8", 3, 8, {"gpio_in", "gpio_out", "emac", NULL, NULL, NULL, NULL, NULL}},
+ {"PD9", 3, 9, {"gpio_in", "gpio_out", "emac", NULL, NULL, NULL, NULL, NULL}},
+ {"PD10", 3, 10, {"gpio_in", "gpio_out", "emac", NULL, NULL, NULL, NULL, NULL}},
+ {"PD11", 3, 11, {"gpio_in", "gpio_out", "emac", NULL, NULL, NULL, NULL, NULL}},
+ {"PD12", 3, 12, {"gpio_in", "gpio_out", "emac", NULL, NULL, NULL, NULL, NULL}},
+ {"PD13", 3, 13, {"gpio_in", "gpio_out", "emac", NULL, NULL, NULL, NULL, NULL}},
+ {"PD14", 3, 14, {"gpio_in", "gpio_out", "emac", NULL, NULL, NULL, NULL, NULL}},
+ {"PD15", 3, 15, {"gpio_in", "gpio_out", "emac", NULL, NULL, NULL, NULL, NULL}},
+ {"PD16", 3, 16, {"gpio_in", "gpio_out", "emac", NULL, NULL, NULL, NULL, NULL}},
+ {"PD17", 3, 17, {"gpio_in", "gpio_out", "emac", NULL, NULL, NULL, NULL, NULL}},
+
+ {"PE0", 4, 0, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}},
+ {"PE1", 4, 1, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}},
+ {"PE2", 4, 2, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}},
+ {"PE3", 4, 3, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}},
+ {"PE4", 4, 4, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}},
+ {"PE5", 4, 5, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}},
+ {"PE6", 4, 6, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}},
+ {"PE7", 4, 7, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}},
+ {"PE8", 4, 8, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}},
+ {"PE9", 4, 9, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}},
+ {"PE10", 4, 10, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}},
+ {"PE11", 4, 11, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}},
+ {"PE12", 4, 12, {"gpio_in", "gpio_out", "csi", "i2c2", NULL, NULL, NULL, NULL}},
+ {"PE13", 4, 13, {"gpio_in", "gpio_out", "csi", "i2c2", NULL, NULL, NULL, NULL}},
+ {"PE14", 4, 14, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}},
+ {"PE15", 4, 15, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}},
+
+ {"PF0", 5, 0, {"gpio_in", "gpio_out", "mmc0", "jtag", NULL, NULL, NULL, NULL}},
+ {"PF1", 5, 1, {"gpio_in", "gpio_out", "mmc0", "jtag", NULL, NULL, NULL, NULL}},
+ {"PF2", 5, 2, {"gpio_in", "gpio_out", "mmc0", "uart0", NULL, NULL, NULL, NULL}},
+ {"PF3", 5, 3, {"gpio_in", "gpio_out", "mmc0", "jtag", NULL, NULL, NULL, NULL}},
+ {"PF4", 5, 4, {"gpio_in", "gpio_out", "mmc0", "uart0", NULL, NULL, NULL, NULL}},
+ {"PF5", 5, 5, {"gpio_in", "gpio_out", "mmc0", "jtag", NULL, NULL, NULL, NULL}},
+ {"PF6", 5, 6, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}},
+
+ {"PG0", 6, 0, {"gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "pg_eint0", NULL}, 6, 0, 1},
+ {"PG1", 6, 1, {"gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "pg_eint1", NULL}, 6, 1, 1},
+ {"PG2", 6, 2, {"gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "pg_eint2", NULL}, 6, 2, 1},
+ {"PG3", 6, 3, {"gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "pg_eint3", NULL}, 6, 3, 1},
+ {"PG4", 6, 4, {"gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "pg_eint4", NULL}, 6, 4, 1},
+ {"PG5", 6, 5, {"gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "pg_eint5", NULL}, 6, 5, 1},
+ {"PG6", 6, 6, {"gpio_in", "gpio_out", "uart1", NULL, NULL, NULL, "pg_eint6", NULL}, 6, 6, 1},
+ {"PG7", 6, 7, {"gpio_in", "gpio_out", "uart1", NULL, NULL, NULL, "pg_eint7", NULL}, 6, 7, 1},
+ {"PG8", 6, 8, {"gpio_in", "gpio_out", "uart1", NULL, NULL, NULL, "pg_eint8", NULL}, 6, 8, 1},
+ {"PG9", 6, 9, {"gpio_in", "gpio_out", "uart1", NULL, NULL, NULL, "pg_eint9", NULL}, 6, 9, 1},
+ {"PG10", 6, 10, {"gpio_in", "gpio_out", "i2s1", NULL, NULL, NULL, "pg_eint10", NULL}, 6, 10, 1},
+ {"PG11", 6, 11, {"gpio_in", "gpio_out", "i2s1", NULL, NULL, NULL, "pg_eint11", NULL}, 6, 11, 1},
+ {"PG12", 6, 12, {"gpio_in", "gpio_out", "i2s1", NULL, NULL, NULL, "pg_eint12", NULL}, 6, 12, 1},
+ {"PG13", 6, 13, {"gpio_in", "gpio_out", "i2s1", NULL, NULL, NULL, "pg_eint13", NULL}, 6, 13, 1},
+};
+
+const struct allwinner_padconf h3_padconf = {
+ .npins = nitems(h3_pins),
+ .pins = h3_pins,
+};
+
+#endif /* SOC_ALLWINNER_H3 */
diff --git a/sys/arm/allwinner/h3/h3_r_padconf.c b/sys/arm/allwinner/h3/h3_r_padconf.c
new file mode 100644
index 000000000000..64413ad46e66
--- /dev/null
+++ b/sys/arm/allwinner/h3/h3_r_padconf.c
@@ -0,0 +1,63 @@
+/*-
+ * Copyright (c) 2016-2017 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/types.h>
+
+#if defined(__aarch64__)
+#include "opt_soc.h"
+#endif
+
+#include <arm/allwinner/allwinner_pinctrl.h>
+
+#if defined(SOC_ALLWINNER_H3) || defined(SOC_ALLWINNER_H5)
+
+const static struct allwinner_pins h3_r_pins[] = {
+ {"PL0", 0, 0, {"gpio_in", "gpio_out", "s_twi", NULL, NULL, NULL, "pl_eint0", NULL}, 6, 0, 0},
+ {"PL1", 0, 1, {"gpio_in", "gpio_out", "s_twi", NULL, NULL, NULL, "pl_eint1", NULL}, 6, 1, 0},
+ {"PL2", 0, 2, {"gpio_in", "gpio_out", "s_uart", NULL, NULL, NULL, "pl_eint2", NULL}, 6, 2, 0},
+ {"PL3", 0, 3, {"gpio_in", "gpio_out", "s_uart", NULL, NULL, NULL, "pl_eint3", NULL}, 6, 3, 0},
+ {"PL4", 0, 4, {"gpio_in", "gpio_out", "s_jtag", NULL, NULL, NULL, "pl_eint4", NULL}, 6, 4, 0},
+ {"PL5", 0, 5, {"gpio_in", "gpio_out", "s_jtag", NULL, NULL, NULL, "pl_eint5", NULL}, 6, 5, 0},
+ {"PL6", 0, 6, {"gpio_in", "gpio_out", "s_jtag", NULL, NULL, NULL, "pl_eint6", NULL}, 6, 6, 0},
+ {"PL7", 0, 7, {"gpio_in", "gpio_out", "s_jtag", NULL, NULL, NULL, "pl_eint7", NULL}, 6, 7, 0},
+ {"PL8", 0, 8, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, "pl_eint8", NULL}, 6, 8, 0},
+ {"PL9", 0, 9, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, "pl_eint9", NULL}, 6, 9, 0},
+ {"PL10", 0, 10, {"gpio_in", "gpio_out", "s_pwm", NULL, NULL, NULL, "pl_eint10", NULL}, 6, 10, 0},
+ {"PL11", 0, 11, {"gpio_in", "gpio_out", "s_cir_rx", NULL, NULL, NULL, "pl_eint11", NULL}, 6, 11, 0},
+};
+
+const struct allwinner_padconf h3_r_padconf = {
+ .npins = nitems(h3_r_pins),
+ .pins = h3_r_pins,
+};
+
+#endif /* SOC_ALLWINNER_H3 */
diff --git a/sys/arm/allwinner/h6/h6_padconf.c b/sys/arm/allwinner/h6/h6_padconf.c
new file mode 100644
index 000000000000..0b17632e495b
--- /dev/null
+++ b/sys/arm/allwinner/h6/h6_padconf.c
@@ -0,0 +1,126 @@
+/*-
+ * Copyright (c) 2019 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/types.h>
+
+#include <arm/allwinner/allwinner_pinctrl.h>
+
+#include "opt_soc.h"
+
+static const struct allwinner_pins h6_pins[] = {
+ { "PC0", 2, 0, { "gpio_in", "gpio_out", "nand", NULL, "spi0" } },
+ { "PC1", 2, 1, { "gpio_in", "gpio_out", "nand", "mmc2" } },
+ { "PC2", 2, 2, { "gpio_in", "gpio_out", "nand", NULL, "spi0" } },
+ { "PC3", 2, 3, { "gpio_in", "gpio_out", "nand", NULL, "spi0" } },
+ { "PC4", 2, 4, { "gpio_in", "gpio_out", "nand", "mmc2" } },
+ { "PC5", 2, 5, { "gpio_in", "gpio_out", "nand", "mmc2", "spi0" } },
+ { "PC6", 2, 6, { "gpio_in", "gpio_out", "nand", "mmc2", "spi0" } },
+ { "PC7", 2, 7, { "gpio_in", "gpio_out", "nand", "mmc2", "spi0" } },
+ { "PC8", 2, 8, { "gpio_in", "gpio_out", "nand", "mmc2" } },
+ { "PC9", 2, 9, { "gpio_in", "gpio_out", "nand", "mmc2" } },
+ { "PC10", 2, 10, { "gpio_in", "gpio_out", "nand", "mmc2" } },
+ { "PC11", 2, 11, { "gpio_in", "gpio_out", "nand", "mmc2" } },
+ { "PC12", 2, 12, { "gpio_in", "gpio_out", "nand", "mmc2" } },
+ { "PC13", 2, 13, { "gpio_in", "gpio_out", "nand", "mmc2" } },
+ { "PC14", 2, 14, { "gpio_in", "gpio_out", "nand", "mmc2" } },
+ { "PC15", 2, 15, { "gpio_in", "gpio_out", "nand", "mmc2" } },
+ { "PC16", 2, 16, { "gpio_in", "gpio_out", "nand", } },
+
+ { "PD0", 3, 0, { "gpio_in", "gpio_out", "lcd0", "ts0", "csi", "emac" } },
+ { "PD1", 3, 1, { "gpio_in", "gpio_out", "lcd0", "ts0", "csi", "emac" } },
+ { "PD2", 3, 2, { "gpio_in", "gpio_out", "lcd0", "ts0", "csi", "emac" } },
+ { "PD3", 3, 3, { "gpio_in", "gpio_out", "lcd0", "ts0", "csi", "emac" } },
+ { "PD4", 3, 4, { "gpio_in", "gpio_out", "lcd0", "ts0", "csi", "emac" } },
+ { "PD5", 3, 5, { "gpio_in", "gpio_out", "lcd0", "ts0", "csi", "emac" } },
+ { "PD6", 3, 6, { "gpio_in", "gpio_out", "lcd0", "ts0", "csi", "emac" } },
+ { "PD7", 3, 7, { "gpio_in", "gpio_out", "lcd0", "ts0", "csi", "emac" } },
+ { "PD8", 3, 8, { "gpio_in", "gpio_out", "lcd0", "ts0", "csi", "emac" } },
+ { "PD9", 3, 9, { "gpio_in", "gpio_out", "lcd0", "ts0", "csi", "emac" } },
+ { "PD10", 3, 10, { "gpio_in", "gpio_out", "lcd0", "ts0", "csi", "emac" } },
+ { "PD11", 3, 11, { "gpio_in", "gpio_out", "lcd0", "ts0", "csi", "emac" } },
+ { "PD12", 3, 12, { "gpio_in", "gpio_out", "lcd0", "ts1", "csi", "emac" } },
+ { "PD13", 3, 13, { "gpio_in", "gpio_out", "lcd0", "ts1", "csi", "emac" } },
+ { "PD14", 3, 14, { "gpio_in", "gpio_out", "lcd0", "ts1", "dmic", "csi" } },
+ { "PD15", 3, 15, { "gpio_in", "gpio_out", "lcd0", "ts1", "dmic", "csi" } },
+ { "PD16", 3, 16, { "gpio_in", "gpio_out", "lcd0", "ts1", "dmic" } },
+ { "PD17", 3, 17, { "gpio_in", "gpio_out", "lcd0", "ts2", "dmic" } },
+ { "PD18", 3, 18, { "gpio_in", "gpio_out", "lcd0", "ts2", "dmic" } },
+ { "PD19", 3, 19, { "gpio_in", "gpio_out", "lcd0", "ts2", "uart2", "emac" } },
+ { "PD20", 3, 20, { "gpio_in", "gpio_out", "lcd0", "ts2", "uart2", "emac" } },
+ { "PD21", 3, 21, { "gpio_in", "gpio_out", "lcd0", "ts2", "uart2" } },
+ { "PD22", 3, 22, { "gpio_in", "gpio_out", "pwm0", "ts3", "uart2" } },
+ { "PD23", 3, 23, { "gpio_in", "gpio_out", "i2c2", "ts3", "uart3", "jtag" } },
+ { "PD24", 3, 24, { "gpio_in", "gpio_out", "i2c2", "ts3", "uart3", "jtag" } },
+ { "PD25", 3, 25, { "gpio_in", "gpio_out", "i2c0", "ts3", "uart3", "jtag" } },
+ { "PD26", 3, 26, { "gpio_in", "gpio_out", "i2c0", "ts3", "uart3", "jtag" } },
+
+ { "PF0", 5, 0, { "gpio_in", "gpio_out", "mmc0", "jtag", NULL, NULL, "pf_eint0" }, 6, 0, 5 },
+ { "PF1", 5, 1, { "gpio_in", "gpio_out", "mmc0", "jtag", NULL, NULL, "pf_eint1" }, 6, 1, 5 },
+ { "PF2", 5, 2, { "gpio_in", "gpio_out", "mmc0", "uart0", NULL, NULL, "pf_eint2" }, 6, 2, 5 },
+ { "PF3", 5, 3, { "gpio_in", "gpio_out", "mmc0", "jtag", NULL, NULL, "pf_eint3" }, 6, 3, 5 },
+ { "PF4", 5, 4, { "gpio_in", "gpio_out", "mmc0", "uart0", NULL, NULL, "pf_eint4" }, 6, 4, 5 },
+ { "PF5", 5, 5, { "gpio_in", "gpio_out", "mmc0", "jtag", NULL, NULL, "pf_eint5" }, 6, 5, 5 },
+ { "PF6", 5, 6, { "gpio_in", "gpio_out", NULL, NULL, NULL, NULL, "pf_eint6" }, 6, 6, 5 },
+
+ { "PG0", 6, 0, { "gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "pg_eint0" }, 6, 0, 6},
+ { "PG1", 6, 1, { "gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "pg_eint1" }, 6, 1, 6},
+ { "PG2", 6, 2, { "gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "pg_eint2" }, 6, 2, 6},
+ { "PG3", 6, 3, { "gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "pg_eint3" }, 6, 3, 6},
+ { "PG4", 6, 4, { "gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "pg_eint4" }, 6, 4, 6},
+ { "PG5", 6, 5, { "gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "pg_eint5" }, 6, 5, 6},
+ { "PG6", 6, 6, { "gpio_in", "gpio_out", "uart1", NULL, NULL, NULL, "pg_eint6" }, 6, 6, 6},
+ { "PG7", 6, 7, { "gpio_in", "gpio_out", "uart1", NULL, NULL, NULL, "pg_eint7" }, 6, 7, 6},
+ { "PG8", 6, 8, { "gpio_in", "gpio_out", "uart1", NULL, "sim0", NULL, "pg_eint8" }, 6, 8, 6},
+ { "PG9", 6, 9, { "gpio_in", "gpio_out", "uart1", NULL, "sim0", NULL, "pg_eint9" }, 6, 9, 6},
+ { "PG10", 6, 10, { "gpio_in", "gpio_out", "i2s2", "h_i2s2", "sim0", NULL, "pg_eint10" }, 6, 10, 6},
+ { "PG11", 6, 11, { "gpio_in", "gpio_out", "i2s2", "h_i2s2", "sim0", NULL, "pg_eint11" }, 6, 11, 6},
+ { "PG12", 6, 12, { "gpio_in", "gpio_out", "i2s2", "h_i2s2", "sim0", NULL, "pg_eint12" }, 6, 12, 6},
+ { "PG13", 6, 13, { "gpio_in", "gpio_out", "i2s2", "h_i2s2", "sim0", NULL, "pg_eint13" }, 6, 13, 6},
+ { "PG14", 6, 14, { "gpio_in", "gpio_out", "i2s2", "h_i2s2", "sim0", NULL, "pg_eint14" }, 6, 13, 6},
+
+ { "PH0", 7, 0, { "gpio_in", "gpio_out", "uart0", "i2s0", "h_i2s0", "sim1", "ph_eint0" }, 6, 0, 7},
+ { "PH1", 7, 1, { "gpio_in", "gpio_out", "uart0", "i2s0", "h_i2s0", "sim1", "ph_eint1" }, 6, 1, 7},
+ { "PH2", 7, 2, { "gpio_in", "gpio_out", "cir", "i2s0", "h_i2s0", "sim1", "ph_eint2" }, 6, 2, 7},
+ { "PH3", 7, 3, { "gpio_in", "gpio_out", "spi1", "i2s0", "h_i2s0", "sim1", "ph_eint3" }, 6, 3, 7},
+ { "PH4", 7, 4, { "gpio_in", "gpio_out", "spi1", "i2s0", "h_i2s0", "sim1", "ph_eint4" }, 6, 4, 7},
+ { "PH5", 7, 5, { "gpio_in", "gpio_out", "spi1", "spdif", "i2c1", "sim1", "ph_eint5" }, 6, 5, 7},
+ { "PH6", 7, 6, { "gpio_in", "gpio_out", "spi1", "spdif", "i2c1", "sim1", "ph_eint6" }, 6, 6, 7},
+ { "PH7", 7, 7, { "gpio_in", "gpio_out", NULL, "spdif", NULL, NULL, "ph_eint7" }, 6, 7, 7},
+ { "PH8", 7, 8, { "gpio_in", "gpio_out", "hdmi", NULL, NULL, NULL, "ph_eint8" }, 6, 8, 7},
+ { "PH9", 7, 9, { "gpio_in", "gpio_out", "hdmi", NULL, NULL, NULL, "ph_eint9" }, 6, 9, 7},
+ { "PH10", 7, 10, { "gpio_in", "gpio_out", "hdmi", NULL, NULL, NULL, "ph_eint10" }, 6, 10, 7},
+};
+
+const struct allwinner_padconf h6_padconf = {
+ .npins = nitems(h6_pins),
+ .pins = h6_pins,
+};
diff --git a/sys/arm/allwinner/h6/h6_r_padconf.c b/sys/arm/allwinner/h6/h6_r_padconf.c
new file mode 100644
index 000000000000..1ef6729a33c7
--- /dev/null
+++ b/sys/arm/allwinner/h6/h6_r_padconf.c
@@ -0,0 +1,60 @@
+/*-
+ * Copyright (c) 2019 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/types.h>
+
+#include <arm/allwinner/allwinner_pinctrl.h>
+
+const static struct allwinner_pins h6_r_pins[] = {
+ {"PL0", 0, 0, {"gpio_in", "gpio_out", NULL, "s_i2c", NULL, NULL, "pl_eint0", NULL}, 6, 0, 0},
+ {"PL1", 0, 1, {"gpio_in", "gpio_out", NULL, "s_i2c", NULL, NULL, "pl_eint1", NULL}, 6, 1, 0},
+ {"PL2", 0, 2, {"gpio_in", "gpio_out", "s_uart", NULL, NULL, NULL, "pl_eint2", NULL}, 6, 2, 0},
+ {"PL3", 0, 3, {"gpio_in", "gpio_out", "s_uart", NULL, NULL, NULL, "pl_eint3", NULL}, 6, 3, 0},
+ {"PL4", 0, 4, {"gpio_in", "gpio_out", "s_jtag", NULL, NULL, NULL, "pl_eint4", NULL}, 6, 4, 0},
+ {"PL5", 0, 5, {"gpio_in", "gpio_out", "s_jtag", NULL, NULL, NULL, "pl_eint5", NULL}, 6, 5, 0},
+ {"PL6", 0, 6, {"gpio_in", "gpio_out", "s_jtag", NULL, NULL, NULL, "pl_eint6", NULL}, 6, 6, 0},
+ {"PL7", 0, 7, {"gpio_in", "gpio_out", "s_jtag", NULL, NULL, NULL, "pl_eint7", NULL}, 6, 7, 0},
+ {"PL8", 0, 8, {"gpio_in", "gpio_out", "s_i2c", NULL, NULL, NULL, "pl_eint8", NULL}, 6, 8, 0},
+ {"PL9", 0, 9, {"gpio_in", "gpio_out", "s_cir", NULL, NULL, NULL, "pl_eint9", NULL}, 6, 9, 0},
+ {"PL10", 0, 10, {"gpio_in", "gpio_out", "s_spdif", NULL, NULL, NULL, "pl_eint10", NULL}, 6, 10, 0},
+
+ {"PM0", 0, 0, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, "pm_eint0", NULL}, 6, 0, 1},
+ {"PM1", 0, 0, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, "pm_eint1", NULL}, 6, 1, 1},
+ {"PM2", 0, 0, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, "pm_eint2", NULL}, 6, 2, 1},
+ {"PM3", 0, 0, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, "pm_eint3", NULL}, 6, 3, 1},
+ {"PM4", 0, 0, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, "pm_eint4", NULL}, 6, 4, 1},
+};
+
+const struct allwinner_padconf h6_r_padconf = {
+ .npins = nitems(h6_r_pins),
+ .pins = h6_r_pins,
+};
diff --git a/sys/arm/allwinner/if_awg.c b/sys/arm/allwinner/if_awg.c
new file mode 100644
index 000000000000..f130cedaa1de
--- /dev/null
+++ b/sys/arm/allwinner/if_awg.c
@@ -0,0 +1,2025 @@
+/*-
+ * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Allwinner Gigabit Ethernet MAC (EMAC) controller
+ */
+
+#include "opt_device_polling.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/kernel.h>
+#include <sys/endian.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/module.h>
+#include <sys/gpio.h>
+
+#include <net/bpf.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <net/if_var.h>
+
+#include <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/allwinner/if_awgreg.h>
+#include <arm/allwinner/aw_sid.h>
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/extres/regulator/regulator.h>
+#include <dev/extres/syscon/syscon.h>
+
+#include "syscon_if.h"
+#include "miibus_if.h"
+#include "gpio_if.h"
+
+#define RD4(sc, reg) bus_read_4((sc)->res[_RES_EMAC], (reg))
+#define WR4(sc, reg, val) bus_write_4((sc)->res[_RES_EMAC], (reg), (val))
+
+#define AWG_LOCK(sc) mtx_lock(&(sc)->mtx)
+#define AWG_UNLOCK(sc) mtx_unlock(&(sc)->mtx);
+#define AWG_ASSERT_LOCKED(sc) mtx_assert(&(sc)->mtx, MA_OWNED)
+#define AWG_ASSERT_UNLOCKED(sc) mtx_assert(&(sc)->mtx, MA_NOTOWNED)
+
+#define DESC_ALIGN 4
+#define TX_DESC_COUNT 1024
+#define TX_DESC_SIZE (sizeof(struct emac_desc) * TX_DESC_COUNT)
+#define RX_DESC_COUNT 256
+#define RX_DESC_SIZE (sizeof(struct emac_desc) * RX_DESC_COUNT)
+
+#define DESC_OFF(n) ((n) * sizeof(struct emac_desc))
+#define TX_NEXT(n) (((n) + 1) & (TX_DESC_COUNT - 1))
+#define TX_SKIP(n, o) (((n) + (o)) & (TX_DESC_COUNT - 1))
+#define RX_NEXT(n) (((n) + 1) & (RX_DESC_COUNT - 1))
+
+#define TX_MAX_SEGS 20
+
+#define SOFT_RST_RETRY 1000
+#define MII_BUSY_RETRY 1000
+#define MDIO_FREQ 2500000
+
+#define BURST_LEN_DEFAULT 8
+#define RX_TX_PRI_DEFAULT 0
+#define PAUSE_TIME_DEFAULT 0x400
+#define TX_INTERVAL_DEFAULT 64
+#define RX_BATCH_DEFAULT 64
+
+/* syscon EMAC clock register */
+#define EMAC_CLK_REG 0x30
+#define EMAC_CLK_EPHY_ADDR (0x1f << 20) /* H3 */
+#define EMAC_CLK_EPHY_ADDR_SHIFT 20
+#define EMAC_CLK_EPHY_LED_POL (1 << 17) /* H3 */
+#define EMAC_CLK_EPHY_SHUTDOWN (1 << 16) /* H3 */
+#define EMAC_CLK_EPHY_SELECT (1 << 15) /* H3 */
+#define EMAC_CLK_RMII_EN (1 << 13)
+#define EMAC_CLK_ETXDC (0x7 << 10)
+#define EMAC_CLK_ETXDC_SHIFT 10
+#define EMAC_CLK_ERXDC (0x1f << 5)
+#define EMAC_CLK_ERXDC_SHIFT 5
+#define EMAC_CLK_PIT (0x1 << 2)
+#define EMAC_CLK_PIT_MII (0 << 2)
+#define EMAC_CLK_PIT_RGMII (1 << 2)
+#define EMAC_CLK_SRC (0x3 << 0)
+#define EMAC_CLK_SRC_MII (0 << 0)
+#define EMAC_CLK_SRC_EXT_RGMII (1 << 0)
+#define EMAC_CLK_SRC_RGMII (2 << 0)
+
+/* Burst length of RX and TX DMA transfers */
+static int awg_burst_len = BURST_LEN_DEFAULT;
+TUNABLE_INT("hw.awg.burst_len", &awg_burst_len);
+
+/* RX / TX DMA priority. If 1, RX DMA has priority over TX DMA. */
+static int awg_rx_tx_pri = RX_TX_PRI_DEFAULT;
+TUNABLE_INT("hw.awg.rx_tx_pri", &awg_rx_tx_pri);
+
+/* Pause time field in the transmitted control frame */
+static int awg_pause_time = PAUSE_TIME_DEFAULT;
+TUNABLE_INT("hw.awg.pause_time", &awg_pause_time);
+
+/* Request a TX interrupt every <n> descriptors */
+static int awg_tx_interval = TX_INTERVAL_DEFAULT;
+TUNABLE_INT("hw.awg.tx_interval", &awg_tx_interval);
+
+/* Maximum number of mbufs to send to if_input */
+static int awg_rx_batch = RX_BATCH_DEFAULT;
+TUNABLE_INT("hw.awg.rx_batch", &awg_rx_batch);
+
+enum awg_type {
+ EMAC_A83T = 1,
+ EMAC_H3,
+ EMAC_A64,
+};
+
+static struct ofw_compat_data compat_data[] = {
+ { "allwinner,sun8i-a83t-emac", EMAC_A83T },
+ { "allwinner,sun8i-h3-emac", EMAC_H3 },
+ { "allwinner,sun50i-a64-emac", EMAC_A64 },
+ { NULL, 0 }
+};
+
+struct awg_bufmap {
+ bus_dmamap_t map;
+ struct mbuf *mbuf;
+};
+
+struct awg_txring {
+ bus_dma_tag_t desc_tag;
+ bus_dmamap_t desc_map;
+ struct emac_desc *desc_ring;
+ bus_addr_t desc_ring_paddr;
+ bus_dma_tag_t buf_tag;
+ struct awg_bufmap buf_map[TX_DESC_COUNT];
+ u_int cur, next, queued;
+ u_int segs;
+};
+
+struct awg_rxring {
+ bus_dma_tag_t desc_tag;
+ bus_dmamap_t desc_map;
+ struct emac_desc *desc_ring;
+ bus_addr_t desc_ring_paddr;
+ bus_dma_tag_t buf_tag;
+ struct awg_bufmap buf_map[RX_DESC_COUNT];
+ bus_dmamap_t buf_spare_map;
+ u_int cur;
+};
+
+enum {
+ _RES_EMAC,
+ _RES_IRQ,
+ _RES_SYSCON,
+ _RES_NITEMS
+};
+
+struct awg_softc {
+ struct resource *res[_RES_NITEMS];
+ struct mtx mtx;
+ if_t ifp;
+ device_t dev;
+ device_t miibus;
+ struct callout stat_ch;
+ void *ih;
+ u_int mdc_div_ratio_m;
+ int link;
+ int if_flags;
+ enum awg_type type;
+ struct syscon *syscon;
+
+ struct awg_txring tx;
+ struct awg_rxring rx;
+};
+
+static struct resource_spec awg_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE },
+ { SYS_RES_MEMORY, 1, RF_ACTIVE | RF_OPTIONAL },
+ { -1, 0 }
+};
+
+static void awg_txeof(struct awg_softc *sc);
+static void awg_start_locked(struct awg_softc *sc);
+
+static void awg_tick(void *softc);
+
+static int awg_parse_delay(device_t dev, uint32_t *tx_delay,
+ uint32_t *rx_delay);
+static uint32_t syscon_read_emac_clk_reg(device_t dev);
+static void syscon_write_emac_clk_reg(device_t dev, uint32_t val);
+static phandle_t awg_get_phy_node(device_t dev);
+static bool awg_has_internal_phy(device_t dev);
+
+/*
+ * MII functions
+ */
+
+static int
+awg_miibus_readreg(device_t dev, int phy, int reg)
+{
+ struct awg_softc *sc;
+ int retry, val;
+
+ sc = device_get_softc(dev);
+ val = 0;
+
+ WR4(sc, EMAC_MII_CMD,
+ (sc->mdc_div_ratio_m << MDC_DIV_RATIO_M_SHIFT) |
+ (phy << PHY_ADDR_SHIFT) |
+ (reg << PHY_REG_ADDR_SHIFT) |
+ MII_BUSY);
+ for (retry = MII_BUSY_RETRY; retry > 0; retry--) {
+ if ((RD4(sc, EMAC_MII_CMD) & MII_BUSY) == 0) {
+ val = RD4(sc, EMAC_MII_DATA);
+ break;
+ }
+ DELAY(10);
+ }
+
+ if (retry == 0)
+ device_printf(dev, "phy read timeout, phy=%d reg=%d\n",
+ phy, reg);
+
+ return (val);
+}
+
+static int
+awg_miibus_writereg(device_t dev, int phy, int reg, int val)
+{
+ struct awg_softc *sc;
+ int retry;
+
+ sc = device_get_softc(dev);
+
+ WR4(sc, EMAC_MII_DATA, val);
+ WR4(sc, EMAC_MII_CMD,
+ (sc->mdc_div_ratio_m << MDC_DIV_RATIO_M_SHIFT) |
+ (phy << PHY_ADDR_SHIFT) |
+ (reg << PHY_REG_ADDR_SHIFT) |
+ MII_WR | MII_BUSY);
+ for (retry = MII_BUSY_RETRY; retry > 0; retry--) {
+ if ((RD4(sc, EMAC_MII_CMD) & MII_BUSY) == 0)
+ break;
+ DELAY(10);
+ }
+
+ if (retry == 0)
+ device_printf(dev, "phy write timeout, phy=%d reg=%d\n",
+ phy, reg);
+
+ return (0);
+}
+
+static void
+awg_miibus_statchg(device_t dev)
+{
+ struct awg_softc *sc;
+ struct mii_data *mii;
+ uint32_t val;
+
+ sc = device_get_softc(dev);
+
+ AWG_ASSERT_LOCKED(sc);
+
+ if ((if_getdrvflags(sc->ifp) & IFF_DRV_RUNNING) == 0)
+ return;
+ mii = device_get_softc(sc->miibus);
+
+ if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) ==
+ (IFM_ACTIVE | IFM_AVALID)) {
+ switch (IFM_SUBTYPE(mii->mii_media_active)) {
+ case IFM_1000_T:
+ case IFM_1000_SX:
+ case IFM_100_TX:
+ case IFM_10_T:
+ sc->link = 1;
+ break;
+ default:
+ sc->link = 0;
+ break;
+ }
+ } else
+ sc->link = 0;
+
+ if (sc->link == 0)
+ return;
+
+ val = RD4(sc, EMAC_BASIC_CTL_0);
+ val &= ~(BASIC_CTL_SPEED | BASIC_CTL_DUPLEX);
+
+ if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T ||
+ IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_SX)
+ val |= BASIC_CTL_SPEED_1000 << BASIC_CTL_SPEED_SHIFT;
+ else if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX)
+ val |= BASIC_CTL_SPEED_100 << BASIC_CTL_SPEED_SHIFT;
+ else
+ val |= BASIC_CTL_SPEED_10 << BASIC_CTL_SPEED_SHIFT;
+
+ if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0)
+ val |= BASIC_CTL_DUPLEX;
+
+ WR4(sc, EMAC_BASIC_CTL_0, val);
+
+ val = RD4(sc, EMAC_RX_CTL_0);
+ val &= ~RX_FLOW_CTL_EN;
+ if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0)
+ val |= RX_FLOW_CTL_EN;
+ WR4(sc, EMAC_RX_CTL_0, val);
+
+ val = RD4(sc, EMAC_TX_FLOW_CTL);
+ val &= ~(PAUSE_TIME|TX_FLOW_CTL_EN);
+ if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0)
+ val |= TX_FLOW_CTL_EN;
+ if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0)
+ val |= awg_pause_time << PAUSE_TIME_SHIFT;
+ WR4(sc, EMAC_TX_FLOW_CTL, val);
+}
+
+/*
+ * Media functions
+ */
+
+static void
+awg_media_status(if_t ifp, struct ifmediareq *ifmr)
+{
+ struct awg_softc *sc;
+ struct mii_data *mii;
+
+ sc = if_getsoftc(ifp);
+ mii = device_get_softc(sc->miibus);
+
+ AWG_LOCK(sc);
+ mii_pollstat(mii);
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+ AWG_UNLOCK(sc);
+}
+
+static int
+awg_media_change(if_t ifp)
+{
+ struct awg_softc *sc;
+ struct mii_data *mii;
+ int error;
+
+ sc = if_getsoftc(ifp);
+ mii = device_get_softc(sc->miibus);
+
+ AWG_LOCK(sc);
+ error = mii_mediachg(mii);
+ AWG_UNLOCK(sc);
+
+ return (error);
+}
+
+/*
+ * Core functions
+ */
+
+/* Bit Reversal - http://aggregate.org/MAGIC/#Bit%20Reversal */
+static uint32_t
+bitrev32(uint32_t x)
+{
+ x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1));
+ x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2));
+ x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4));
+ x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8));
+
+ return (x >> 16) | (x << 16);
+}
+
+static u_int
+awg_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
+{
+ uint32_t crc, hashreg, hashbit, *hash = arg;
+
+ crc = ether_crc32_le(LLADDR(sdl), ETHER_ADDR_LEN) & 0x7f;
+ crc = bitrev32(~crc) >> 26;
+ hashreg = (crc >> 5);
+ hashbit = (crc & 0x1f);
+ hash[hashreg] |= (1 << hashbit);
+
+ return (1);
+}
+
+static void
+awg_setup_rxfilter(struct awg_softc *sc)
+{
+ uint32_t val, hash[2], machi, maclo;
+ uint8_t *eaddr;
+ if_t ifp;
+
+ AWG_ASSERT_LOCKED(sc);
+
+ ifp = sc->ifp;
+ val = 0;
+ hash[0] = hash[1] = 0;
+
+ if (if_getflags(ifp) & IFF_PROMISC)
+ val |= DIS_ADDR_FILTER;
+ else if (if_getflags(ifp) & IFF_ALLMULTI) {
+ val |= RX_ALL_MULTICAST;
+ hash[0] = hash[1] = ~0;
+ } else if (if_foreach_llmaddr(ifp, awg_hash_maddr, hash) > 0)
+ val |= HASH_MULTICAST;
+
+ /* Write our unicast address */
+ eaddr = IF_LLADDR(ifp);
+ machi = (eaddr[5] << 8) | eaddr[4];
+ maclo = (eaddr[3] << 24) | (eaddr[2] << 16) | (eaddr[1] << 8) |
+ (eaddr[0] << 0);
+ WR4(sc, EMAC_ADDR_HIGH(0), machi);
+ WR4(sc, EMAC_ADDR_LOW(0), maclo);
+
+ /* Multicast hash filters */
+ WR4(sc, EMAC_RX_HASH_0, hash[1]);
+ WR4(sc, EMAC_RX_HASH_1, hash[0]);
+
+ /* RX frame filter config */
+ WR4(sc, EMAC_RX_FRM_FLT, val);
+}
+
+static void
+awg_setup_core(struct awg_softc *sc)
+{
+ uint32_t val;
+
+ AWG_ASSERT_LOCKED(sc);
+ /* Configure DMA burst length and priorities */
+ val = awg_burst_len << BASIC_CTL_BURST_LEN_SHIFT;
+ if (awg_rx_tx_pri)
+ val |= BASIC_CTL_RX_TX_PRI;
+ WR4(sc, EMAC_BASIC_CTL_1, val);
+
+}
+
+static void
+awg_enable_mac(struct awg_softc *sc, bool enable)
+{
+ uint32_t tx, rx;
+
+ AWG_ASSERT_LOCKED(sc);
+
+ tx = RD4(sc, EMAC_TX_CTL_0);
+ rx = RD4(sc, EMAC_RX_CTL_0);
+ if (enable) {
+ tx |= TX_EN;
+ rx |= RX_EN | CHECK_CRC;
+ } else {
+ tx &= ~TX_EN;
+ rx &= ~(RX_EN | CHECK_CRC);
+ }
+
+ WR4(sc, EMAC_TX_CTL_0, tx);
+ WR4(sc, EMAC_RX_CTL_0, rx);
+}
+
+static void
+awg_get_eaddr(device_t dev, uint8_t *eaddr)
+{
+ struct awg_softc *sc;
+ uint32_t maclo, machi, rnd;
+ u_char rootkey[16];
+ uint32_t rootkey_size;
+
+ sc = device_get_softc(dev);
+
+ machi = RD4(sc, EMAC_ADDR_HIGH(0)) & 0xffff;
+ maclo = RD4(sc, EMAC_ADDR_LOW(0));
+
+ rootkey_size = sizeof(rootkey);
+ if (maclo == 0xffffffff && machi == 0xffff) {
+ /* MAC address in hardware is invalid, create one */
+ if (aw_sid_get_fuse(AW_SID_FUSE_ROOTKEY, rootkey,
+ &rootkey_size) == 0 &&
+ (rootkey[3] | rootkey[12] | rootkey[13] | rootkey[14] |
+ rootkey[15]) != 0) {
+ /* MAC address is derived from the root key in SID */
+ maclo = (rootkey[13] << 24) | (rootkey[12] << 16) |
+ (rootkey[3] << 8) | 0x02;
+ machi = (rootkey[15] << 8) | rootkey[14];
+ } else {
+ /* Create one */
+ rnd = arc4random();
+ maclo = 0x00f2 | (rnd & 0xffff0000);
+ machi = rnd & 0xffff;
+ }
+ }
+
+ eaddr[0] = maclo & 0xff;
+ eaddr[1] = (maclo >> 8) & 0xff;
+ eaddr[2] = (maclo >> 16) & 0xff;
+ eaddr[3] = (maclo >> 24) & 0xff;
+ eaddr[4] = machi & 0xff;
+ eaddr[5] = (machi >> 8) & 0xff;
+}
+
+/*
+ * DMA functions
+ */
+
+static void
+awg_enable_dma_intr(struct awg_softc *sc)
+{
+ /* Enable interrupts */
+ WR4(sc, EMAC_INT_EN, RX_INT_EN | TX_INT_EN | TX_BUF_UA_INT_EN);
+}
+
+static void
+awg_disable_dma_intr(struct awg_softc *sc)
+{
+ /* Disable interrupts */
+ WR4(sc, EMAC_INT_EN, 0);
+}
+
+static void
+awg_init_dma(struct awg_softc *sc)
+{
+ uint32_t val;
+
+ AWG_ASSERT_LOCKED(sc);
+
+ /* Enable interrupts */
+#ifdef DEVICE_POLLING
+ if ((if_getcapenable(sc->ifp) & IFCAP_POLLING) == 0)
+ awg_enable_dma_intr(sc);
+ else
+ awg_disable_dma_intr(sc);
+#else
+ awg_enable_dma_intr(sc);
+#endif
+
+ /* Enable transmit DMA */
+ val = RD4(sc, EMAC_TX_CTL_1);
+ WR4(sc, EMAC_TX_CTL_1, val | TX_DMA_EN | TX_MD | TX_NEXT_FRAME);
+
+ /* Enable receive DMA */
+ val = RD4(sc, EMAC_RX_CTL_1);
+ WR4(sc, EMAC_RX_CTL_1, val | RX_DMA_EN | RX_MD);
+}
+
+static void
+awg_stop_dma(struct awg_softc *sc)
+{
+ uint32_t val;
+
+ AWG_ASSERT_LOCKED(sc);
+
+ /* Stop transmit DMA and flush data in the TX FIFO */
+ val = RD4(sc, EMAC_TX_CTL_1);
+ val &= ~TX_DMA_EN;
+ val |= FLUSH_TX_FIFO;
+ WR4(sc, EMAC_TX_CTL_1, val);
+
+ /* Disable interrupts */
+ awg_disable_dma_intr(sc);
+
+ /* Disable transmit DMA */
+ val = RD4(sc, EMAC_TX_CTL_1);
+ WR4(sc, EMAC_TX_CTL_1, val & ~TX_DMA_EN);
+
+ /* Disable receive DMA */
+ val = RD4(sc, EMAC_RX_CTL_1);
+ WR4(sc, EMAC_RX_CTL_1, val & ~RX_DMA_EN);
+}
+
+static int
+awg_encap(struct awg_softc *sc, struct mbuf **mp)
+{
+ bus_dmamap_t map;
+ bus_dma_segment_t segs[TX_MAX_SEGS];
+ int error, nsegs, cur, first, last, i;
+ u_int csum_flags;
+ uint32_t flags, status;
+ struct mbuf *m;
+
+ cur = first = sc->tx.cur;
+ map = sc->tx.buf_map[first].map;
+
+ m = *mp;
+ error = bus_dmamap_load_mbuf_sg(sc->tx.buf_tag, map, m, segs,
+ &nsegs, BUS_DMA_NOWAIT);
+ if (error == EFBIG) {
+ m = m_collapse(m, M_NOWAIT, TX_MAX_SEGS);
+ if (m == NULL) {
+ device_printf(sc->dev, "awg_encap: m_collapse failed\n");
+ m_freem(*mp);
+ *mp = NULL;
+ return (ENOMEM);
+ }
+ *mp = m;
+ error = bus_dmamap_load_mbuf_sg(sc->tx.buf_tag, map, m,
+ segs, &nsegs, BUS_DMA_NOWAIT);
+ if (error != 0) {
+ m_freem(*mp);
+ *mp = NULL;
+ }
+ }
+ if (error != 0) {
+ device_printf(sc->dev, "awg_encap: bus_dmamap_load_mbuf_sg failed\n");
+ return (error);
+ }
+ if (nsegs == 0) {
+ m_freem(*mp);
+ *mp = NULL;
+ return (EIO);
+ }
+
+ if (sc->tx.queued + nsegs > TX_DESC_COUNT) {
+ bus_dmamap_unload(sc->tx.buf_tag, map);
+ return (ENOBUFS);
+ }
+
+ bus_dmamap_sync(sc->tx.buf_tag, map, BUS_DMASYNC_PREWRITE);
+
+ flags = TX_FIR_DESC;
+ status = 0;
+ if ((m->m_pkthdr.csum_flags & CSUM_IP) != 0) {
+ if ((m->m_pkthdr.csum_flags & (CSUM_TCP|CSUM_UDP)) != 0)
+ csum_flags = TX_CHECKSUM_CTL_FULL;
+ else
+ csum_flags = TX_CHECKSUM_CTL_IP;
+ flags |= (csum_flags << TX_CHECKSUM_CTL_SHIFT);
+ }
+
+ for (i = 0; i < nsegs; i++) {
+ sc->tx.segs++;
+ if (i == nsegs - 1) {
+ flags |= TX_LAST_DESC;
+ /*
+ * Can only request TX completion
+ * interrupt on last descriptor.
+ */
+ if (sc->tx.segs >= awg_tx_interval) {
+ sc->tx.segs = 0;
+ flags |= TX_INT_CTL;
+ }
+ }
+
+ sc->tx.desc_ring[cur].addr = htole32((uint32_t)segs[i].ds_addr);
+ sc->tx.desc_ring[cur].size = htole32(flags | segs[i].ds_len);
+ sc->tx.desc_ring[cur].status = htole32(status);
+
+ flags &= ~TX_FIR_DESC;
+ /*
+ * Setting of the valid bit in the first descriptor is
+ * deferred until the whole chain is fully set up.
+ */
+ status = TX_DESC_CTL;
+
+ ++sc->tx.queued;
+ cur = TX_NEXT(cur);
+ }
+
+ sc->tx.cur = cur;
+
+ /* Store mapping and mbuf in the last segment */
+ last = TX_SKIP(cur, TX_DESC_COUNT - 1);
+ sc->tx.buf_map[first].map = sc->tx.buf_map[last].map;
+ sc->tx.buf_map[last].map = map;
+ sc->tx.buf_map[last].mbuf = m;
+
+ /*
+ * The whole mbuf chain has been DMA mapped,
+ * fix the first descriptor.
+ */
+ sc->tx.desc_ring[first].status = htole32(TX_DESC_CTL);
+
+ return (0);
+}
+
+static void
+awg_clean_txbuf(struct awg_softc *sc, int index)
+{
+ struct awg_bufmap *bmap;
+
+ --sc->tx.queued;
+
+ bmap = &sc->tx.buf_map[index];
+ if (bmap->mbuf != NULL) {
+ bus_dmamap_sync(sc->tx.buf_tag, bmap->map,
+ BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(sc->tx.buf_tag, bmap->map);
+ m_freem(bmap->mbuf);
+ bmap->mbuf = NULL;
+ }
+}
+
+static void
+awg_setup_rxdesc(struct awg_softc *sc, int index, bus_addr_t paddr)
+{
+ uint32_t status, size;
+
+ status = RX_DESC_CTL;
+ size = MCLBYTES - 1;
+
+ sc->rx.desc_ring[index].addr = htole32((uint32_t)paddr);
+ sc->rx.desc_ring[index].size = htole32(size);
+ sc->rx.desc_ring[index].status = htole32(status);
+}
+
+static void
+awg_reuse_rxdesc(struct awg_softc *sc, int index)
+{
+
+ sc->rx.desc_ring[index].status = htole32(RX_DESC_CTL);
+}
+
+static int
+awg_newbuf_rx(struct awg_softc *sc, int index)
+{
+ struct mbuf *m;
+ bus_dma_segment_t seg;
+ bus_dmamap_t map;
+ int nsegs;
+
+ m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
+ if (m == NULL)
+ return (ENOBUFS);
+
+ m->m_pkthdr.len = m->m_len = m->m_ext.ext_size;
+ m_adj(m, ETHER_ALIGN);
+
+ if (bus_dmamap_load_mbuf_sg(sc->rx.buf_tag, sc->rx.buf_spare_map,
+ m, &seg, &nsegs, BUS_DMA_NOWAIT) != 0) {
+ m_freem(m);
+ return (ENOBUFS);
+ }
+
+ if (sc->rx.buf_map[index].mbuf != NULL) {
+ bus_dmamap_sync(sc->rx.buf_tag, sc->rx.buf_map[index].map,
+ BUS_DMASYNC_POSTREAD);
+ bus_dmamap_unload(sc->rx.buf_tag, sc->rx.buf_map[index].map);
+ }
+ map = sc->rx.buf_map[index].map;
+ sc->rx.buf_map[index].map = sc->rx.buf_spare_map;
+ sc->rx.buf_spare_map = map;
+ bus_dmamap_sync(sc->rx.buf_tag, sc->rx.buf_map[index].map,
+ BUS_DMASYNC_PREREAD);
+
+ sc->rx.buf_map[index].mbuf = m;
+ awg_setup_rxdesc(sc, index, seg.ds_addr);
+
+ return (0);
+}
+
+static void
+awg_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+ if (error != 0)
+ return;
+ *(bus_addr_t *)arg = segs[0].ds_addr;
+}
+
+static int
+awg_setup_dma(device_t dev)
+{
+ struct awg_softc *sc;
+ int error, i;
+
+ sc = device_get_softc(dev);
+
+ /* Setup TX ring */
+ error = bus_dma_tag_create(
+ bus_get_dma_tag(dev), /* Parent tag */
+ DESC_ALIGN, 0, /* alignment, boundary */
+ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ TX_DESC_SIZE, 1, /* maxsize, nsegs */
+ TX_DESC_SIZE, /* maxsegsize */
+ 0, /* flags */
+ NULL, NULL, /* lockfunc, lockarg */
+ &sc->tx.desc_tag);
+ if (error != 0) {
+ device_printf(dev, "cannot create TX descriptor ring tag\n");
+ return (error);
+ }
+
+ error = bus_dmamem_alloc(sc->tx.desc_tag, (void **)&sc->tx.desc_ring,
+ BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->tx.desc_map);
+ if (error != 0) {
+ device_printf(dev, "cannot allocate TX descriptor ring\n");
+ return (error);
+ }
+
+ error = bus_dmamap_load(sc->tx.desc_tag, sc->tx.desc_map,
+ sc->tx.desc_ring, TX_DESC_SIZE, awg_dmamap_cb,
+ &sc->tx.desc_ring_paddr, 0);
+ if (error != 0) {
+ device_printf(dev, "cannot load TX descriptor ring\n");
+ return (error);
+ }
+
+ for (i = 0; i < TX_DESC_COUNT; i++)
+ sc->tx.desc_ring[i].next =
+ htole32(sc->tx.desc_ring_paddr + DESC_OFF(TX_NEXT(i)));
+
+ error = bus_dma_tag_create(
+ bus_get_dma_tag(dev), /* Parent tag */
+ 1, 0, /* alignment, boundary */
+ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ MCLBYTES, TX_MAX_SEGS, /* maxsize, nsegs */
+ MCLBYTES, /* maxsegsize */
+ 0, /* flags */
+ NULL, NULL, /* lockfunc, lockarg */
+ &sc->tx.buf_tag);
+ if (error != 0) {
+ device_printf(dev, "cannot create TX buffer tag\n");
+ return (error);
+ }
+
+ sc->tx.queued = 0;
+ for (i = 0; i < TX_DESC_COUNT; i++) {
+ error = bus_dmamap_create(sc->tx.buf_tag, 0,
+ &sc->tx.buf_map[i].map);
+ if (error != 0) {
+ device_printf(dev, "cannot create TX buffer map\n");
+ return (error);
+ }
+ }
+
+ /* Setup RX ring */
+ error = bus_dma_tag_create(
+ bus_get_dma_tag(dev), /* Parent tag */
+ DESC_ALIGN, 0, /* alignment, boundary */
+ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ RX_DESC_SIZE, 1, /* maxsize, nsegs */
+ RX_DESC_SIZE, /* maxsegsize */
+ 0, /* flags */
+ NULL, NULL, /* lockfunc, lockarg */
+ &sc->rx.desc_tag);
+ if (error != 0) {
+ device_printf(dev, "cannot create RX descriptor ring tag\n");
+ return (error);
+ }
+
+ error = bus_dmamem_alloc(sc->rx.desc_tag, (void **)&sc->rx.desc_ring,
+ BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->rx.desc_map);
+ if (error != 0) {
+ device_printf(dev, "cannot allocate RX descriptor ring\n");
+ return (error);
+ }
+
+ error = bus_dmamap_load(sc->rx.desc_tag, sc->rx.desc_map,
+ sc->rx.desc_ring, RX_DESC_SIZE, awg_dmamap_cb,
+ &sc->rx.desc_ring_paddr, 0);
+ if (error != 0) {
+ device_printf(dev, "cannot load RX descriptor ring\n");
+ return (error);
+ }
+
+ error = bus_dma_tag_create(
+ bus_get_dma_tag(dev), /* Parent tag */
+ 1, 0, /* alignment, boundary */
+ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ MCLBYTES, 1, /* maxsize, nsegs */
+ MCLBYTES, /* maxsegsize */
+ 0, /* flags */
+ NULL, NULL, /* lockfunc, lockarg */
+ &sc->rx.buf_tag);
+ if (error != 0) {
+ device_printf(dev, "cannot create RX buffer tag\n");
+ return (error);
+ }
+
+ error = bus_dmamap_create(sc->rx.buf_tag, 0, &sc->rx.buf_spare_map);
+ if (error != 0) {
+ device_printf(dev,
+ "cannot create RX buffer spare map\n");
+ return (error);
+ }
+
+ for (i = 0; i < RX_DESC_COUNT; i++) {
+ sc->rx.desc_ring[i].next =
+ htole32(sc->rx.desc_ring_paddr + DESC_OFF(RX_NEXT(i)));
+
+ error = bus_dmamap_create(sc->rx.buf_tag, 0,
+ &sc->rx.buf_map[i].map);
+ if (error != 0) {
+ device_printf(dev, "cannot create RX buffer map\n");
+ return (error);
+ }
+ sc->rx.buf_map[i].mbuf = NULL;
+ error = awg_newbuf_rx(sc, i);
+ if (error != 0) {
+ device_printf(dev, "cannot create RX buffer\n");
+ return (error);
+ }
+ }
+ bus_dmamap_sync(sc->rx.desc_tag, sc->rx.desc_map,
+ BUS_DMASYNC_PREWRITE);
+
+ /* Write transmit and receive descriptor base address registers */
+ WR4(sc, EMAC_TX_DMA_LIST, sc->tx.desc_ring_paddr);
+ WR4(sc, EMAC_RX_DMA_LIST, sc->rx.desc_ring_paddr);
+
+ return (0);
+}
+
+static void
+awg_dma_start_tx(struct awg_softc *sc)
+{
+ uint32_t val;
+
+ AWG_ASSERT_LOCKED(sc);
+
+ /* Start and run TX DMA */
+ val = RD4(sc, EMAC_TX_CTL_1);
+ WR4(sc, EMAC_TX_CTL_1, val | TX_DMA_START);
+}
+
+/*
+ * if_ functions
+ */
+
+static void
+awg_start_locked(struct awg_softc *sc)
+{
+ struct mbuf *m;
+ if_t ifp;
+ int cnt, err;
+
+ AWG_ASSERT_LOCKED(sc);
+
+ if (!sc->link)
+ return;
+
+ ifp = sc->ifp;
+
+ if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) !=
+ IFF_DRV_RUNNING)
+ return;
+
+ for (cnt = 0; ; cnt++) {
+ m = if_dequeue(ifp);
+ if (m == NULL)
+ break;
+
+ err = awg_encap(sc, &m);
+ if (err != 0) {
+ if (err == ENOBUFS)
+ if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0);
+ if (m != NULL)
+ if_sendq_prepend(ifp, m);
+ break;
+ }
+ if_bpfmtap(ifp, m);
+ }
+
+ if (cnt != 0) {
+ bus_dmamap_sync(sc->tx.desc_tag, sc->tx.desc_map,
+ BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+
+ awg_dma_start_tx(sc);
+ }
+}
+
+static void
+awg_start(if_t ifp)
+{
+ struct awg_softc *sc;
+
+ sc = if_getsoftc(ifp);
+
+ AWG_LOCK(sc);
+ awg_start_locked(sc);
+ AWG_UNLOCK(sc);
+}
+
+static void
+awg_init_locked(struct awg_softc *sc)
+{
+ struct mii_data *mii;
+ if_t ifp;
+
+ mii = device_get_softc(sc->miibus);
+ ifp = sc->ifp;
+
+ AWG_ASSERT_LOCKED(sc);
+
+ if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
+ return;
+
+ awg_setup_rxfilter(sc);
+ awg_setup_core(sc);
+ awg_enable_mac(sc, true);
+ awg_init_dma(sc);
+
+ if_setdrvflagbits(ifp, IFF_DRV_RUNNING, IFF_DRV_OACTIVE);
+
+ mii_mediachg(mii);
+ callout_reset(&sc->stat_ch, hz, awg_tick, sc);
+}
+
+static void
+awg_init(void *softc)
+{
+ struct awg_softc *sc;
+
+ sc = softc;
+
+ AWG_LOCK(sc);
+ awg_init_locked(sc);
+ AWG_UNLOCK(sc);
+}
+
+static void
+awg_stop(struct awg_softc *sc)
+{
+ if_t ifp;
+ uint32_t val;
+ int i;
+
+ AWG_ASSERT_LOCKED(sc);
+
+ ifp = sc->ifp;
+
+ callout_stop(&sc->stat_ch);
+
+ awg_stop_dma(sc);
+ awg_enable_mac(sc, false);
+
+ sc->link = 0;
+
+ /* Finish handling transmitted buffers */
+ awg_txeof(sc);
+
+ /* Release any untransmitted buffers. */
+ for (i = sc->tx.next; sc->tx.queued > 0; i = TX_NEXT(i)) {
+ val = le32toh(sc->tx.desc_ring[i].status);
+ if ((val & TX_DESC_CTL) != 0)
+ break;
+ awg_clean_txbuf(sc, i);
+ }
+ sc->tx.next = i;
+ for (; sc->tx.queued > 0; i = TX_NEXT(i)) {
+ sc->tx.desc_ring[i].status = 0;
+ awg_clean_txbuf(sc, i);
+ }
+ sc->tx.cur = sc->tx.next;
+ bus_dmamap_sync(sc->tx.desc_tag, sc->tx.desc_map,
+ BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+
+ /* Setup RX buffers for reuse */
+ bus_dmamap_sync(sc->rx.desc_tag, sc->rx.desc_map,
+ BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
+
+ for (i = sc->rx.cur; ; i = RX_NEXT(i)) {
+ val = le32toh(sc->rx.desc_ring[i].status);
+ if ((val & RX_DESC_CTL) != 0)
+ break;
+ awg_reuse_rxdesc(sc, i);
+ }
+ sc->rx.cur = i;
+ bus_dmamap_sync(sc->rx.desc_tag, sc->rx.desc_map,
+ BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+
+ if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+}
+
+static int
+awg_ioctl(if_t ifp, u_long cmd, caddr_t data)
+{
+ struct awg_softc *sc;
+ struct mii_data *mii;
+ struct ifreq *ifr;
+ int flags, mask, error;
+
+ sc = if_getsoftc(ifp);
+ mii = device_get_softc(sc->miibus);
+ ifr = (struct ifreq *)data;
+ error = 0;
+
+ switch (cmd) {
+ case SIOCSIFFLAGS:
+ AWG_LOCK(sc);
+ if (if_getflags(ifp) & IFF_UP) {
+ if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
+ flags = if_getflags(ifp) ^ sc->if_flags;
+ if ((flags & (IFF_PROMISC|IFF_ALLMULTI)) != 0)
+ awg_setup_rxfilter(sc);
+ } else
+ awg_init_locked(sc);
+ } else {
+ if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
+ awg_stop(sc);
+ }
+ sc->if_flags = if_getflags(ifp);
+ AWG_UNLOCK(sc);
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
+ AWG_LOCK(sc);
+ awg_setup_rxfilter(sc);
+ AWG_UNLOCK(sc);
+ }
+ break;
+ case SIOCSIFMEDIA:
+ case SIOCGIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd);
+ break;
+ case SIOCSIFCAP:
+ mask = ifr->ifr_reqcap ^ if_getcapenable(ifp);
+#ifdef DEVICE_POLLING
+ if (mask & IFCAP_POLLING) {
+ if ((ifr->ifr_reqcap & IFCAP_POLLING) != 0) {
+ error = ether_poll_register(awg_poll, ifp);
+ if (error != 0)
+ break;
+ AWG_LOCK(sc);
+ awg_disable_dma_intr(sc);
+ if_setcapenablebit(ifp, IFCAP_POLLING, 0);
+ AWG_UNLOCK(sc);
+ } else {
+ error = ether_poll_deregister(ifp);
+ AWG_LOCK(sc);
+ awg_enable_dma_intr(sc);
+ if_setcapenablebit(ifp, 0, IFCAP_POLLING);
+ AWG_UNLOCK(sc);
+ }
+ }
+#endif
+ if (mask & IFCAP_VLAN_MTU)
+ if_togglecapenable(ifp, IFCAP_VLAN_MTU);
+ if (mask & IFCAP_RXCSUM)
+ if_togglecapenable(ifp, IFCAP_RXCSUM);
+ if (mask & IFCAP_TXCSUM)
+ if_togglecapenable(ifp, IFCAP_TXCSUM);
+ if ((if_getcapenable(ifp) & IFCAP_TXCSUM) != 0)
+ if_sethwassistbits(ifp, CSUM_IP | CSUM_UDP | CSUM_TCP, 0);
+ else
+ if_sethwassistbits(ifp, 0, CSUM_IP | CSUM_UDP | CSUM_TCP);
+ break;
+ default:
+ error = ether_ioctl(ifp, cmd, data);
+ break;
+ }
+
+ return (error);
+}
+
+/*
+ * Interrupts functions
+ */
+
+static int
+awg_rxintr(struct awg_softc *sc)
+{
+ if_t ifp;
+ struct mbuf *m, *mh, *mt;
+ int error, index, len, cnt, npkt;
+ uint32_t status;
+
+ ifp = sc->ifp;
+ mh = mt = NULL;
+ cnt = 0;
+ npkt = 0;
+
+ bus_dmamap_sync(sc->rx.desc_tag, sc->rx.desc_map,
+ BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
+
+ for (index = sc->rx.cur; ; index = RX_NEXT(index)) {
+ status = le32toh(sc->rx.desc_ring[index].status);
+ if ((status & RX_DESC_CTL) != 0)
+ break;
+
+ len = (status & RX_FRM_LEN) >> RX_FRM_LEN_SHIFT;
+
+ if (len == 0) {
+ if ((status & (RX_NO_ENOUGH_BUF_ERR | RX_OVERFLOW_ERR)) != 0)
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
+ awg_reuse_rxdesc(sc, index);
+ continue;
+ }
+
+ m = sc->rx.buf_map[index].mbuf;
+
+ error = awg_newbuf_rx(sc, index);
+ if (error != 0) {
+ if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
+ awg_reuse_rxdesc(sc, index);
+ continue;
+ }
+
+ m->m_pkthdr.rcvif = ifp;
+ m->m_pkthdr.len = len;
+ m->m_len = len;
+ if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
+
+ if ((if_getcapenable(ifp) & IFCAP_RXCSUM) != 0 &&
+ (status & RX_FRM_TYPE) != 0) {
+ m->m_pkthdr.csum_flags = CSUM_IP_CHECKED;
+ if ((status & RX_HEADER_ERR) == 0)
+ m->m_pkthdr.csum_flags |= CSUM_IP_VALID;
+ if ((status & RX_PAYLOAD_ERR) == 0) {
+ m->m_pkthdr.csum_flags |=
+ CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
+ m->m_pkthdr.csum_data = 0xffff;
+ }
+ }
+
+ m->m_nextpkt = NULL;
+ if (mh == NULL)
+ mh = m;
+ else
+ mt->m_nextpkt = m;
+ mt = m;
+ ++cnt;
+ ++npkt;
+
+ if (cnt == awg_rx_batch) {
+ AWG_UNLOCK(sc);
+ if_input(ifp, mh);
+ AWG_LOCK(sc);
+ mh = mt = NULL;
+ cnt = 0;
+ }
+ }
+
+ if (index != sc->rx.cur) {
+ bus_dmamap_sync(sc->rx.desc_tag, sc->rx.desc_map,
+ BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+ }
+
+ if (mh != NULL) {
+ AWG_UNLOCK(sc);
+ if_input(ifp, mh);
+ AWG_LOCK(sc);
+ }
+
+ sc->rx.cur = index;
+
+ return (npkt);
+}
+
+static void
+awg_txeof(struct awg_softc *sc)
+{
+ struct emac_desc *desc;
+ uint32_t status, size;
+ if_t ifp;
+ int i, prog;
+
+ AWG_ASSERT_LOCKED(sc);
+
+ bus_dmamap_sync(sc->tx.desc_tag, sc->tx.desc_map,
+ BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
+
+ ifp = sc->ifp;
+
+ prog = 0;
+ for (i = sc->tx.next; sc->tx.queued > 0; i = TX_NEXT(i)) {
+ desc = &sc->tx.desc_ring[i];
+ status = le32toh(desc->status);
+ if ((status & TX_DESC_CTL) != 0)
+ break;
+ size = le32toh(desc->size);
+ if (size & TX_LAST_DESC) {
+ if ((status & (TX_HEADER_ERR | TX_PAYLOAD_ERR)) != 0)
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ else
+ if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
+ }
+ prog++;
+ awg_clean_txbuf(sc, i);
+ }
+
+ if (prog > 0) {
+ sc->tx.next = i;
+ if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
+ }
+}
+
+static void
+awg_intr(void *arg)
+{
+ struct awg_softc *sc;
+ uint32_t val;
+
+ sc = arg;
+
+ AWG_LOCK(sc);
+ val = RD4(sc, EMAC_INT_STA);
+ WR4(sc, EMAC_INT_STA, val);
+
+ if (val & RX_INT)
+ awg_rxintr(sc);
+
+ if (val & TX_INT)
+ awg_txeof(sc);
+
+ if (val & (TX_INT | TX_BUF_UA_INT)) {
+ if (!if_sendq_empty(sc->ifp))
+ awg_start_locked(sc);
+ }
+
+ AWG_UNLOCK(sc);
+}
+
+#ifdef DEVICE_POLLING
+static int
+awg_poll(if_t ifp, enum poll_cmd cmd, int count)
+{
+ struct awg_softc *sc;
+ uint32_t val;
+ int rx_npkts;
+
+ sc = if_getsoftc(ifp);
+ rx_npkts = 0;
+
+ AWG_LOCK(sc);
+
+ if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0) {
+ AWG_UNLOCK(sc);
+ return (0);
+ }
+
+ rx_npkts = awg_rxintr(sc);
+ awg_txeof(sc);
+ if (!if_sendq_empty(ifp))
+ awg_start_locked(sc);
+
+ if (cmd == POLL_AND_CHECK_STATUS) {
+ val = RD4(sc, EMAC_INT_STA);
+ if (val != 0)
+ WR4(sc, EMAC_INT_STA, val);
+ }
+
+ AWG_UNLOCK(sc);
+
+ return (rx_npkts);
+}
+#endif
+
+/*
+ * syscon functions
+ */
+static uint32_t
+syscon_read_emac_clk_reg(device_t dev)
+{
+ struct awg_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (sc->syscon != NULL)
+ return (SYSCON_READ_4(sc->syscon, EMAC_CLK_REG));
+ else if (sc->res[_RES_SYSCON] != NULL)
+ return (bus_read_4(sc->res[_RES_SYSCON], 0));
+
+ return (0);
+}
+
+static void
+syscon_write_emac_clk_reg(device_t dev, uint32_t val)
+{
+ struct awg_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (sc->syscon != NULL)
+ SYSCON_WRITE_4(sc->syscon, EMAC_CLK_REG, val);
+ else if (sc->res[_RES_SYSCON] != NULL)
+ bus_write_4(sc->res[_RES_SYSCON], 0, val);
+}
+
+/*
+ * PHY functions
+ */
+
+static phandle_t
+awg_get_phy_node(device_t dev)
+{
+ phandle_t node;
+ pcell_t phy_handle;
+
+ node = ofw_bus_get_node(dev);
+ if (OF_getencprop(node, "phy-handle", (void *)&phy_handle,
+ sizeof(phy_handle)) <= 0)
+ return (0);
+
+ return (OF_node_from_xref(phy_handle));
+}
+
+static bool
+awg_has_internal_phy(device_t dev)
+{
+ phandle_t node, phy_node;
+
+ node = ofw_bus_get_node(dev);
+ /* Legacy binding */
+ if (OF_hasprop(node, "allwinner,use-internal-phy"))
+ return (true);
+
+ phy_node = awg_get_phy_node(dev);
+ return (phy_node != 0 && ofw_bus_node_is_compatible(OF_parent(phy_node),
+ "allwinner,sun8i-h3-mdio-internal") != 0);
+}
+
+static int
+awg_parse_delay(device_t dev, uint32_t *tx_delay, uint32_t *rx_delay)
+{
+ phandle_t node;
+ uint32_t delay;
+
+ if (tx_delay == NULL || rx_delay == NULL)
+ return (EINVAL);
+ *tx_delay = *rx_delay = 0;
+ node = ofw_bus_get_node(dev);
+
+ if (OF_getencprop(node, "tx-delay", &delay, sizeof(delay)) >= 0)
+ *tx_delay = delay;
+ else if (OF_getencprop(node, "allwinner,tx-delay-ps", &delay,
+ sizeof(delay)) >= 0) {
+ if ((delay % 100) != 0) {
+ device_printf(dev, "tx-delay-ps is not a multiple of 100\n");
+ return (EDOM);
+ }
+ *tx_delay = delay / 100;
+ }
+ if (*tx_delay > 7) {
+ device_printf(dev, "tx-delay out of range\n");
+ return (ERANGE);
+ }
+
+ if (OF_getencprop(node, "rx-delay", &delay, sizeof(delay)) >= 0)
+ *rx_delay = delay;
+ else if (OF_getencprop(node, "allwinner,rx-delay-ps", &delay,
+ sizeof(delay)) >= 0) {
+ if ((delay % 100) != 0) {
+ device_printf(dev, "rx-delay-ps is not within documented domain\n");
+ return (EDOM);
+ }
+ *rx_delay = delay / 100;
+ }
+ if (*rx_delay > 31) {
+ device_printf(dev, "rx-delay out of range\n");
+ return (ERANGE);
+ }
+
+ return (0);
+}
+
+static int
+awg_setup_phy(device_t dev)
+{
+ struct awg_softc *sc;
+ clk_t clk_tx, clk_tx_parent;
+ const char *tx_parent_name;
+ char *phy_type;
+ phandle_t node;
+ uint32_t reg, tx_delay, rx_delay;
+ int error;
+ bool use_syscon;
+
+ sc = device_get_softc(dev);
+ node = ofw_bus_get_node(dev);
+ use_syscon = false;
+
+ if (OF_getprop_alloc(node, "phy-mode", (void **)&phy_type) == 0)
+ return (0);
+
+ if (sc->syscon != NULL || sc->res[_RES_SYSCON] != NULL)
+ use_syscon = true;
+
+ if (bootverbose)
+ device_printf(dev, "PHY type: %s, conf mode: %s\n", phy_type,
+ use_syscon ? "reg" : "clk");
+
+ if (use_syscon) {
+ /*
+ * Abstract away writing to syscon for devices like the pine64.
+ * For the pine64, we get dtb from U-Boot and it still uses the
+ * legacy setup of specifying syscon register in emac node
+ * rather than as its own node and using an xref in emac.
+ * These abstractions can go away once U-Boot dts is up-to-date.
+ */
+ reg = syscon_read_emac_clk_reg(dev);
+ reg &= ~(EMAC_CLK_PIT | EMAC_CLK_SRC | EMAC_CLK_RMII_EN);
+ if (strncmp(phy_type, "rgmii", 5) == 0)
+ reg |= EMAC_CLK_PIT_RGMII | EMAC_CLK_SRC_RGMII;
+ else if (strcmp(phy_type, "rmii") == 0)
+ reg |= EMAC_CLK_RMII_EN;
+ else
+ reg |= EMAC_CLK_PIT_MII | EMAC_CLK_SRC_MII;
+
+ /*
+ * Fail attach if we fail to parse either of the delay
+ * parameters. If we don't have the proper delay to write to
+ * syscon, then awg likely won't function properly anyways.
+ * Lack of delay is not an error!
+ */
+ error = awg_parse_delay(dev, &tx_delay, &rx_delay);
+ if (error != 0)
+ goto fail;
+
+ /* Default to 0 and we'll increase it if we need to. */
+ reg &= ~(EMAC_CLK_ETXDC | EMAC_CLK_ERXDC);
+ if (tx_delay > 0)
+ reg |= (tx_delay << EMAC_CLK_ETXDC_SHIFT);
+ if (rx_delay > 0)
+ reg |= (rx_delay << EMAC_CLK_ERXDC_SHIFT);
+
+ if (sc->type == EMAC_H3) {
+ if (awg_has_internal_phy(dev)) {
+ reg |= EMAC_CLK_EPHY_SELECT;
+ reg &= ~EMAC_CLK_EPHY_SHUTDOWN;
+ if (OF_hasprop(node,
+ "allwinner,leds-active-low"))
+ reg |= EMAC_CLK_EPHY_LED_POL;
+ else
+ reg &= ~EMAC_CLK_EPHY_LED_POL;
+
+ /* Set internal PHY addr to 1 */
+ reg &= ~EMAC_CLK_EPHY_ADDR;
+ reg |= (1 << EMAC_CLK_EPHY_ADDR_SHIFT);
+ } else {
+ reg &= ~EMAC_CLK_EPHY_SELECT;
+ }
+ }
+
+ if (bootverbose)
+ device_printf(dev, "EMAC clock: 0x%08x\n", reg);
+ syscon_write_emac_clk_reg(dev, reg);
+ } else {
+ if (strncmp(phy_type, "rgmii", 5) == 0)
+ tx_parent_name = "emac_int_tx";
+ else
+ tx_parent_name = "mii_phy_tx";
+
+ /* Get the TX clock */
+ error = clk_get_by_ofw_name(dev, 0, "tx", &clk_tx);
+ if (error != 0) {
+ device_printf(dev, "cannot get tx clock\n");
+ goto fail;
+ }
+
+ /* Find the desired parent clock based on phy-mode property */
+ error = clk_get_by_name(dev, tx_parent_name, &clk_tx_parent);
+ if (error != 0) {
+ device_printf(dev, "cannot get clock '%s'\n",
+ tx_parent_name);
+ goto fail;
+ }
+
+ /* Set TX clock parent */
+ error = clk_set_parent_by_clk(clk_tx, clk_tx_parent);
+ if (error != 0) {
+ device_printf(dev, "cannot set tx clock parent\n");
+ goto fail;
+ }
+
+ /* Enable TX clock */
+ error = clk_enable(clk_tx);
+ if (error != 0) {
+ device_printf(dev, "cannot enable tx clock\n");
+ goto fail;
+ }
+ }
+
+ error = 0;
+
+fail:
+ OF_prop_free(phy_type);
+ return (error);
+}
+
+static int
+awg_setup_extres(device_t dev)
+{
+ struct awg_softc *sc;
+ phandle_t node, phy_node;
+ hwreset_t rst_ahb, rst_ephy;
+ clk_t clk_ahb, clk_ephy;
+ regulator_t reg;
+ uint64_t freq;
+ int error, div;
+
+ sc = device_get_softc(dev);
+ rst_ahb = rst_ephy = NULL;
+ clk_ahb = clk_ephy = NULL;
+ reg = NULL;
+ node = ofw_bus_get_node(dev);
+ phy_node = awg_get_phy_node(dev);
+
+ if (phy_node == 0 && OF_hasprop(node, "phy-handle")) {
+ error = ENXIO;
+ device_printf(dev, "cannot get phy handle\n");
+ goto fail;
+ }
+
+ /* Get AHB clock and reset resources */
+ error = hwreset_get_by_ofw_name(dev, 0, "stmmaceth", &rst_ahb);
+ if (error != 0)
+ error = hwreset_get_by_ofw_name(dev, 0, "ahb", &rst_ahb);
+ if (error != 0) {
+ device_printf(dev, "cannot get ahb reset\n");
+ goto fail;
+ }
+ if (hwreset_get_by_ofw_name(dev, 0, "ephy", &rst_ephy) != 0)
+ if (phy_node == 0 || hwreset_get_by_ofw_idx(dev, phy_node, 0,
+ &rst_ephy) != 0)
+ rst_ephy = NULL;
+ error = clk_get_by_ofw_name(dev, 0, "stmmaceth", &clk_ahb);
+ if (error != 0)
+ error = clk_get_by_ofw_name(dev, 0, "ahb", &clk_ahb);
+ if (error != 0) {
+ device_printf(dev, "cannot get ahb clock\n");
+ goto fail;
+ }
+ if (clk_get_by_ofw_name(dev, 0, "ephy", &clk_ephy) != 0)
+ if (phy_node == 0 || clk_get_by_ofw_index(dev, phy_node, 0,
+ &clk_ephy) != 0)
+ clk_ephy = NULL;
+
+ if (OF_hasprop(node, "syscon") && syscon_get_by_ofw_property(dev, node,
+ "syscon", &sc->syscon) != 0) {
+ device_printf(dev, "cannot get syscon driver handle\n");
+ goto fail;
+ }
+
+ /* Configure PHY for MII or RGMII mode */
+ if (awg_setup_phy(dev) != 0)
+ goto fail;
+
+ /* Enable clocks */
+ error = clk_enable(clk_ahb);
+ if (error != 0) {
+ device_printf(dev, "cannot enable ahb clock\n");
+ goto fail;
+ }
+ if (clk_ephy != NULL) {
+ error = clk_enable(clk_ephy);
+ if (error != 0) {
+ device_printf(dev, "cannot enable ephy clock\n");
+ goto fail;
+ }
+ }
+
+ /* De-assert reset */
+ error = hwreset_deassert(rst_ahb);
+ if (error != 0) {
+ device_printf(dev, "cannot de-assert ahb reset\n");
+ goto fail;
+ }
+ if (rst_ephy != NULL) {
+ /*
+ * The ephy reset is left de-asserted by U-Boot. Assert it
+ * here to make sure that we're in a known good state going
+ * into the PHY reset.
+ */
+ hwreset_assert(rst_ephy);
+ error = hwreset_deassert(rst_ephy);
+ if (error != 0) {
+ device_printf(dev, "cannot de-assert ephy reset\n");
+ goto fail;
+ }
+ }
+
+ /* Enable PHY regulator if applicable */
+ if (regulator_get_by_ofw_property(dev, 0, "phy-supply", &reg) == 0) {
+ error = regulator_enable(reg);
+ if (error != 0) {
+ device_printf(dev, "cannot enable PHY regulator\n");
+ goto fail;
+ }
+ }
+
+ /* Determine MDC clock divide ratio based on AHB clock */
+ error = clk_get_freq(clk_ahb, &freq);
+ if (error != 0) {
+ device_printf(dev, "cannot get AHB clock frequency\n");
+ goto fail;
+ }
+ div = freq / MDIO_FREQ;
+ if (div <= 16)
+ sc->mdc_div_ratio_m = MDC_DIV_RATIO_M_16;
+ else if (div <= 32)
+ sc->mdc_div_ratio_m = MDC_DIV_RATIO_M_32;
+ else if (div <= 64)
+ sc->mdc_div_ratio_m = MDC_DIV_RATIO_M_64;
+ else if (div <= 128)
+ sc->mdc_div_ratio_m = MDC_DIV_RATIO_M_128;
+ else {
+ device_printf(dev, "cannot determine MDC clock divide ratio\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ if (bootverbose)
+ device_printf(dev, "AHB frequency %ju Hz, MDC div: 0x%x\n",
+ (uintmax_t)freq, sc->mdc_div_ratio_m);
+
+ return (0);
+
+fail:
+ if (reg != NULL)
+ regulator_release(reg);
+ if (clk_ephy != NULL)
+ clk_release(clk_ephy);
+ if (clk_ahb != NULL)
+ clk_release(clk_ahb);
+ if (rst_ephy != NULL)
+ hwreset_release(rst_ephy);
+ if (rst_ahb != NULL)
+ hwreset_release(rst_ahb);
+ return (error);
+}
+
+#ifdef AWG_DEBUG
+static void
+awg_dump_regs(device_t dev)
+{
+ static const struct {
+ const char *name;
+ u_int reg;
+ } regs[] = {
+ { "BASIC_CTL_0", EMAC_BASIC_CTL_0 },
+ { "BASIC_CTL_1", EMAC_BASIC_CTL_1 },
+ { "INT_STA", EMAC_INT_STA },
+ { "INT_EN", EMAC_INT_EN },
+ { "TX_CTL_0", EMAC_TX_CTL_0 },
+ { "TX_CTL_1", EMAC_TX_CTL_1 },
+ { "TX_FLOW_CTL", EMAC_TX_FLOW_CTL },
+ { "TX_DMA_LIST", EMAC_TX_DMA_LIST },
+ { "RX_CTL_0", EMAC_RX_CTL_0 },
+ { "RX_CTL_1", EMAC_RX_CTL_1 },
+ { "RX_DMA_LIST", EMAC_RX_DMA_LIST },
+ { "RX_FRM_FLT", EMAC_RX_FRM_FLT },
+ { "RX_HASH_0", EMAC_RX_HASH_0 },
+ { "RX_HASH_1", EMAC_RX_HASH_1 },
+ { "MII_CMD", EMAC_MII_CMD },
+ { "ADDR_HIGH0", EMAC_ADDR_HIGH(0) },
+ { "ADDR_LOW0", EMAC_ADDR_LOW(0) },
+ { "TX_DMA_STA", EMAC_TX_DMA_STA },
+ { "TX_DMA_CUR_DESC", EMAC_TX_DMA_CUR_DESC },
+ { "TX_DMA_CUR_BUF", EMAC_TX_DMA_CUR_BUF },
+ { "RX_DMA_STA", EMAC_RX_DMA_STA },
+ { "RX_DMA_CUR_DESC", EMAC_RX_DMA_CUR_DESC },
+ { "RX_DMA_CUR_BUF", EMAC_RX_DMA_CUR_BUF },
+ { "RGMII_STA", EMAC_RGMII_STA },
+ };
+ struct awg_softc *sc;
+ unsigned int n;
+
+ sc = device_get_softc(dev);
+
+ for (n = 0; n < nitems(regs); n++)
+ device_printf(dev, " %-20s %08x\n", regs[n].name,
+ RD4(sc, regs[n].reg));
+}
+#endif
+
+#define GPIO_ACTIVE_LOW 1
+
+static int
+awg_phy_reset(device_t dev)
+{
+ pcell_t gpio_prop[4], delay_prop[3];
+ phandle_t node, gpio_node;
+ device_t gpio;
+ uint32_t pin, flags;
+ uint32_t pin_value;
+
+ node = ofw_bus_get_node(dev);
+ if (OF_getencprop(node, "allwinner,reset-gpio", gpio_prop,
+ sizeof(gpio_prop)) <= 0)
+ return (0);
+
+ if (OF_getencprop(node, "allwinner,reset-delays-us", delay_prop,
+ sizeof(delay_prop)) <= 0)
+ return (ENXIO);
+
+ gpio_node = OF_node_from_xref(gpio_prop[0]);
+ if ((gpio = OF_device_from_xref(gpio_prop[0])) == NULL)
+ return (ENXIO);
+
+ if (GPIO_MAP_GPIOS(gpio, node, gpio_node, nitems(gpio_prop) - 1,
+ gpio_prop + 1, &pin, &flags) != 0)
+ return (ENXIO);
+
+ pin_value = GPIO_PIN_LOW;
+ if (OF_hasprop(node, "allwinner,reset-active-low"))
+ pin_value = GPIO_PIN_HIGH;
+
+ if (flags & GPIO_ACTIVE_LOW)
+ pin_value = !pin_value;
+
+ GPIO_PIN_SETFLAGS(gpio, pin, GPIO_PIN_OUTPUT);
+ GPIO_PIN_SET(gpio, pin, pin_value);
+ DELAY(delay_prop[0]);
+ GPIO_PIN_SET(gpio, pin, !pin_value);
+ DELAY(delay_prop[1]);
+ GPIO_PIN_SET(gpio, pin, pin_value);
+ DELAY(delay_prop[2]);
+
+ return (0);
+}
+
+static int
+awg_reset(device_t dev)
+{
+ struct awg_softc *sc;
+ int retry;
+
+ sc = device_get_softc(dev);
+
+ /* Reset PHY if necessary */
+ if (awg_phy_reset(dev) != 0) {
+ device_printf(dev, "failed to reset PHY\n");
+ return (ENXIO);
+ }
+
+ /* Soft reset all registers and logic */
+ WR4(sc, EMAC_BASIC_CTL_1, BASIC_CTL_SOFT_RST);
+
+ /* Wait for soft reset bit to self-clear */
+ for (retry = SOFT_RST_RETRY; retry > 0; retry--) {
+ if ((RD4(sc, EMAC_BASIC_CTL_1) & BASIC_CTL_SOFT_RST) == 0)
+ break;
+ DELAY(10);
+ }
+ if (retry == 0) {
+ device_printf(dev, "soft reset timed out\n");
+#ifdef AWG_DEBUG
+ awg_dump_regs(dev);
+#endif
+ return (ETIMEDOUT);
+ }
+
+ return (0);
+}
+
+/*
+ * Stats
+ */
+
+static void
+awg_tick(void *softc)
+{
+ struct awg_softc *sc;
+ struct mii_data *mii;
+ if_t ifp;
+ int link;
+
+ sc = softc;
+ ifp = sc->ifp;
+ mii = device_get_softc(sc->miibus);
+
+ AWG_ASSERT_LOCKED(sc);
+
+ if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0)
+ return;
+
+ link = sc->link;
+ mii_tick(mii);
+ if (sc->link && !link)
+ awg_start_locked(sc);
+
+ callout_reset(&sc->stat_ch, hz, awg_tick, sc);
+}
+
+/*
+ * Probe/attach functions
+ */
+
+static int
+awg_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner Gigabit Ethernet");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+awg_attach(device_t dev)
+{
+ uint8_t eaddr[ETHER_ADDR_LEN];
+ struct awg_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+
+ if (bus_alloc_resources(dev, awg_spec, sc->res) != 0) {
+ device_printf(dev, "cannot allocate resources for device\n");
+ return (ENXIO);
+ }
+
+ mtx_init(&sc->mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF);
+ callout_init_mtx(&sc->stat_ch, &sc->mtx, 0);
+
+ /* Setup clocks and regulators */
+ error = awg_setup_extres(dev);
+ if (error != 0)
+ return (error);
+
+ /* Read MAC address before resetting the chip */
+ awg_get_eaddr(dev, eaddr);
+
+ /* Soft reset EMAC core */
+ error = awg_reset(dev);
+ if (error != 0)
+ return (error);
+
+ /* Setup DMA descriptors */
+ error = awg_setup_dma(dev);
+ if (error != 0)
+ return (error);
+
+ /* Install interrupt handler */
+ error = bus_setup_intr(dev, sc->res[_RES_IRQ],
+ INTR_TYPE_NET | INTR_MPSAFE, NULL, awg_intr, sc, &sc->ih);
+ if (error != 0) {
+ device_printf(dev, "cannot setup interrupt handler\n");
+ return (error);
+ }
+
+ /* Setup ethernet interface */
+ sc->ifp = if_alloc(IFT_ETHER);
+ if_setsoftc(sc->ifp, sc);
+ if_initname(sc->ifp, device_get_name(dev), device_get_unit(dev));
+ if_setflags(sc->ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
+ if_setstartfn(sc->ifp, awg_start);
+ if_setioctlfn(sc->ifp, awg_ioctl);
+ if_setinitfn(sc->ifp, awg_init);
+ if_setsendqlen(sc->ifp, TX_DESC_COUNT - 1);
+ if_setsendqready(sc->ifp);
+ if_sethwassist(sc->ifp, CSUM_IP | CSUM_UDP | CSUM_TCP);
+ if_setcapabilities(sc->ifp, IFCAP_VLAN_MTU | IFCAP_HWCSUM);
+ if_setcapenable(sc->ifp, if_getcapabilities(sc->ifp));
+#ifdef DEVICE_POLLING
+ if_setcapabilitiesbit(sc->ifp, IFCAP_POLLING, 0);
+#endif
+
+ /* Attach MII driver */
+ error = mii_attach(dev, &sc->miibus, sc->ifp, awg_media_change,
+ awg_media_status, BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY,
+ MIIF_DOPAUSE);
+ if (error != 0) {
+ device_printf(dev, "cannot attach PHY\n");
+ return (error);
+ }
+
+ /* Attach ethernet interface */
+ ether_ifattach(sc->ifp, eaddr);
+
+ return (0);
+}
+
+static device_method_t awg_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, awg_probe),
+ DEVMETHOD(device_attach, awg_attach),
+
+ /* MII interface */
+ DEVMETHOD(miibus_readreg, awg_miibus_readreg),
+ DEVMETHOD(miibus_writereg, awg_miibus_writereg),
+ DEVMETHOD(miibus_statchg, awg_miibus_statchg),
+
+ DEVMETHOD_END
+};
+
+static driver_t awg_driver = {
+ "awg",
+ awg_methods,
+ sizeof(struct awg_softc),
+};
+
+static devclass_t awg_devclass;
+
+DRIVER_MODULE(awg, simplebus, awg_driver, awg_devclass, 0, 0);
+DRIVER_MODULE(miibus, awg, miibus_driver, miibus_devclass, 0, 0);
+MODULE_DEPEND(awg, ether, 1, 1, 1);
+MODULE_DEPEND(awg, miibus, 1, 1, 1);
+MODULE_DEPEND(awg, aw_sid, 1, 1, 1);
+SIMPLEBUS_PNP_INFO(compat_data);
diff --git a/sys/arm/allwinner/if_awgreg.h b/sys/arm/allwinner/if_awgreg.h
new file mode 100644
index 000000000000..b71e05cb39de
--- /dev/null
+++ b/sys/arm/allwinner/if_awgreg.h
@@ -0,0 +1,181 @@
+/*-
+ * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Allwinner Gigabit Ethernet
+ */
+
+#ifndef __IF_AWGREG_H__
+#define __IF_AWGREG_H__
+
+#define EMAC_BASIC_CTL_0 0x00
+#define BASIC_CTL_SPEED (0x3 << 2)
+#define BASIC_CTL_SPEED_SHIFT 2
+#define BASIC_CTL_SPEED_1000 0
+#define BASIC_CTL_SPEED_10 2
+#define BASIC_CTL_SPEED_100 3
+#define BASIC_CTL_LOOPBACK (1 << 1)
+#define BASIC_CTL_DUPLEX (1 << 0)
+#define EMAC_BASIC_CTL_1 0x04
+#define BASIC_CTL_BURST_LEN (0x3f << 24)
+#define BASIC_CTL_BURST_LEN_SHIFT 24
+#define BASIC_CTL_RX_TX_PRI (1 << 1)
+#define BASIC_CTL_SOFT_RST (1 << 0)
+#define EMAC_INT_STA 0x08
+#define RX_BUF_UA_INT (1 << 10)
+#define RX_INT (1 << 8)
+#define TX_UNDERFLOW_INT (1 << 4)
+#define TX_BUF_UA_INT (1 << 2)
+#define TX_DMA_STOPPED_INT (1 << 1)
+#define TX_INT (1 << 0)
+#define EMAC_INT_EN 0x0c
+#define RX_BUF_UA_INT_EN (1 << 10)
+#define RX_INT_EN (1 << 8)
+#define TX_UNDERFLOW_INT_EN (1 << 4)
+#define TX_BUF_UA_INT_EN (1 << 2)
+#define TX_DMA_STOPPED_INT_EN (1 << 1)
+#define TX_INT_EN (1 << 0)
+#define EMAC_TX_CTL_0 0x10
+#define TX_EN (1 << 31)
+#define EMAC_TX_CTL_1 0x14
+#define TX_DMA_START (1 << 31)
+#define TX_DMA_EN (1 << 30)
+#define TX_NEXT_FRAME (1 << 2)
+#define TX_MD (1 << 1)
+#define FLUSH_TX_FIFO (1 << 0)
+#define EMAC_TX_FLOW_CTL 0x1c
+#define PAUSE_TIME (0xffff << 4)
+#define PAUSE_TIME_SHIFT 4
+#define TX_FLOW_CTL_EN (1 << 0)
+#define EMAC_TX_DMA_LIST 0x20
+#define EMAC_RX_CTL_0 0x24
+#define RX_EN (1 << 31)
+#define JUMBO_FRM_EN (1 << 29)
+#define STRIP_FCS (1 << 28)
+#define CHECK_CRC (1 << 27)
+#define RX_FLOW_CTL_EN (1 << 16)
+#define EMAC_RX_CTL_1 0x28
+#define RX_DMA_START (1 << 31)
+#define RX_DMA_EN (1 << 30)
+#define RX_MD (1 << 1)
+#define EMAC_RX_DMA_LIST 0x34
+#define EMAC_RX_FRM_FLT 0x38
+#define DIS_ADDR_FILTER (1 << 31)
+#define DIS_BROADCAST (1 << 17)
+#define RX_ALL_MULTICAST (1 << 16)
+#define CTL_FRM_FILTER (0x3 << 12)
+#define CTL_FRM_FILTER_SHIFT 12
+#define HASH_MULTICAST (1 << 9)
+#define HASH_UNICAST (1 << 8)
+#define SA_FILTER_EN (1 << 6)
+#define SA_INV_FILTER (1 << 5)
+#define DA_INV_FILTER (1 << 4)
+#define FLT_MD (1 << 1)
+#define RX_ALL (1 << 0)
+#define EMAC_RX_HASH_0 0x40
+#define EMAC_RX_HASH_1 0x44
+#define EMAC_MII_CMD 0x48
+#define MDC_DIV_RATIO_M (0x7 << 20)
+#define MDC_DIV_RATIO_M_16 0
+#define MDC_DIV_RATIO_M_32 1
+#define MDC_DIV_RATIO_M_64 2
+#define MDC_DIV_RATIO_M_128 3
+#define MDC_DIV_RATIO_M_SHIFT 20
+#define PHY_ADDR (0x1f << 12)
+#define PHY_ADDR_SHIFT 12
+#define PHY_REG_ADDR (0x1f << 4)
+#define PHY_REG_ADDR_SHIFT 4
+#define MII_WR (1 << 1)
+#define MII_BUSY (1 << 0)
+#define EMAC_MII_DATA 0x4c
+#define EMAC_ADDR_HIGH(n) (0x50 + (n) * 8)
+#define EMAC_ADDR_LOW(n) (0x54 + (n) * 8)
+#define EMAC_TX_DMA_STA 0xb0
+#define EMAC_TX_DMA_CUR_DESC 0xb4
+#define EMAC_TX_DMA_CUR_BUF 0xb8
+#define EMAC_RX_DMA_STA 0xc0
+#define EMAC_RX_DMA_CUR_DESC 0xc4
+#define EMAC_RX_DMA_CUR_BUF 0xc8
+#define EMAC_RGMII_STA 0xd0
+
+struct emac_desc {
+ uint32_t status;
+/* Transmit */
+#define TX_DESC_CTL (1 << 31)
+#define TX_HEADER_ERR (1 << 16)
+#define TX_LENGTH_ERR (1 << 14)
+#define TX_PAYLOAD_ERR (1 << 12)
+#define TX_CRS_ERR (1 << 10)
+#define TX_COL_ERR_0 (1 << 9)
+#define TX_COL_ERR_1 (1 << 8)
+#define TX_COL_CNT (0xf << 3)
+#define TX_COL_CNT_SHIFT 3
+#define TX_DEFER_ERR (1 << 2)
+#define TX_UNDERFLOW_ERR (1 << 1)
+#define TX_DEFER (1 << 0)
+/* Receive */
+#define RX_DESC_CTL (1 << 31)
+#define RX_DAF_FAIL (1 << 30)
+#define RX_FRM_LEN (0x3fff << 16)
+#define RX_FRM_LEN_SHIFT 16
+#define RX_NO_ENOUGH_BUF_ERR (1 << 14)
+#define RX_SAF_FAIL (1 << 13)
+#define RX_OVERFLOW_ERR (1 << 11)
+#define RX_FIR_DESC (1 << 9)
+#define RX_LAST_DESC (1 << 8)
+#define RX_HEADER_ERR (1 << 7)
+#define RX_COL_ERR (1 << 6)
+#define RX_FRM_TYPE (1 << 5)
+#define RX_LENGTH_ERR (1 << 4)
+#define RX_PHY_ERR (1 << 3)
+#define RX_CRC_ERR (1 << 1)
+#define RX_PAYLOAD_ERR (1 << 0)
+
+ uint32_t size;
+/* Transmit */
+#define TX_INT_CTL (1 << 31)
+#define TX_LAST_DESC (1 << 30)
+#define TX_FIR_DESC (1 << 29)
+#define TX_CHECKSUM_CTL (0x3 << 27)
+#define TX_CHECKSUM_CTL_IP 1
+#define TX_CHECKSUM_CTL_NO_PSE 2
+#define TX_CHECKSUM_CTL_FULL 3
+#define TX_CHECKSUM_CTL_SHIFT 27
+#define TX_CRC_CTL (1 << 26)
+#define TX_BUF_SIZE (0xfff << 0)
+#define TX_BUF_SIZE_SHIFT 0
+/* Receive */
+#define RX_INT_CTL (1 << 31)
+#define RX_BUF_SIZE (0xfff << 0)
+#define RX_BUF_SIZE_SHIFT 0
+
+ uint32_t addr;
+
+ uint32_t next;
+} __packed;
+
+#endif /* !__IF_AWGREG_H__ */
diff --git a/sys/arm/allwinner/if_emac.c b/sys/arm/allwinner/if_emac.c
new file mode 100644
index 000000000000..73e7e889916a
--- /dev/null
+++ b/sys/arm/allwinner/if_emac.c
@@ -0,0 +1,1203 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Ganbold Tsagaankhuu <ganbold@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/* A10/A20 EMAC driver */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/mbuf.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+#include <sys/gpio.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/intr.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_arp.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <net/if_mib.h>
+#include <net/ethernet.h>
+#include <net/if_vlan_var.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#endif
+
+#include <net/bpf.h>
+#include <net/bpfdesc.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+#include <arm/allwinner/if_emacreg.h>
+#include <arm/allwinner/aw_sid.h>
+
+#include <dev/extres/clk/clk.h>
+
+#include "miibus_if.h"
+
+#include "gpio_if.h"
+
+#include "a10_sramc.h"
+
+struct emac_softc {
+ struct ifnet *emac_ifp;
+ device_t emac_dev;
+ device_t emac_miibus;
+ bus_space_handle_t emac_handle;
+ bus_space_tag_t emac_tag;
+ struct resource *emac_res;
+ struct resource *emac_irq;
+ void *emac_intrhand;
+ clk_t emac_clk;
+ int emac_if_flags;
+ struct mtx emac_mtx;
+ struct callout emac_tick_ch;
+ int emac_watchdog_timer;
+ int emac_rx_process_limit;
+ int emac_link;
+ uint32_t emac_fifo_mask;
+};
+
+static int emac_probe(device_t);
+static int emac_attach(device_t);
+static int emac_detach(device_t);
+static int emac_shutdown(device_t);
+static int emac_suspend(device_t);
+static int emac_resume(device_t);
+
+static int emac_sys_setup(struct emac_softc *);
+static void emac_reset(struct emac_softc *);
+
+static void emac_init_locked(struct emac_softc *);
+static void emac_start_locked(struct ifnet *);
+static void emac_init(void *);
+static void emac_stop_locked(struct emac_softc *);
+static void emac_intr(void *);
+static int emac_ioctl(struct ifnet *, u_long, caddr_t);
+
+static void emac_rxeof(struct emac_softc *, int);
+static void emac_txeof(struct emac_softc *, uint32_t);
+
+static int emac_miibus_readreg(device_t, int, int);
+static int emac_miibus_writereg(device_t, int, int, int);
+static void emac_miibus_statchg(device_t);
+
+static int emac_ifmedia_upd(struct ifnet *);
+static void emac_ifmedia_sts(struct ifnet *, struct ifmediareq *);
+
+static int sysctl_int_range(SYSCTL_HANDLER_ARGS, int, int);
+static int sysctl_hw_emac_proc_limit(SYSCTL_HANDLER_ARGS);
+
+#define EMAC_READ_REG(sc, reg) \
+ bus_space_read_4(sc->emac_tag, sc->emac_handle, reg)
+#define EMAC_WRITE_REG(sc, reg, val) \
+ bus_space_write_4(sc->emac_tag, sc->emac_handle, reg, val)
+
+static int
+emac_sys_setup(struct emac_softc *sc)
+{
+ int error;
+
+ /* Activate EMAC clock. */
+ error = clk_get_by_ofw_index(sc->emac_dev, 0, 0, &sc->emac_clk);
+ if (error != 0) {
+ device_printf(sc->emac_dev, "cannot get clock\n");
+ return (error);
+ }
+ error = clk_enable(sc->emac_clk);
+ if (error != 0) {
+ device_printf(sc->emac_dev, "cannot enable clock\n");
+ return (error);
+ }
+
+ /* Map sram. */
+ a10_map_to_emac();
+
+ return (0);
+}
+
+static void
+emac_get_hwaddr(struct emac_softc *sc, uint8_t *hwaddr)
+{
+ uint32_t val0, val1, rnd;
+ u_char rootkey[16];
+ size_t rootkey_size;
+
+ /*
+ * Try to get MAC address from running hardware.
+ * If there is something non-zero there just use it.
+ *
+ * Otherwise set the address to a convenient locally assigned address,
+ * using the SID rootkey.
+ * This is was uboot does so we end up with the same mac as if uboot
+ * did set it.
+ * If we can't get the root key, generate a random one,
+ * 'bsd' + random 24 low-order bits. 'b' is 0x62, which has the locally
+ * assigned bit set, and the broadcast/multicast bit clear.
+ */
+ val0 = EMAC_READ_REG(sc, EMAC_MAC_A0);
+ val1 = EMAC_READ_REG(sc, EMAC_MAC_A1);
+ if ((val0 | val1) != 0 && (val0 | val1) != 0xffffff) {
+ hwaddr[0] = (val1 >> 16) & 0xff;
+ hwaddr[1] = (val1 >> 8) & 0xff;
+ hwaddr[2] = (val1 >> 0) & 0xff;
+ hwaddr[3] = (val0 >> 16) & 0xff;
+ hwaddr[4] = (val0 >> 8) & 0xff;
+ hwaddr[5] = (val0 >> 0) & 0xff;
+ } else {
+ rootkey_size = sizeof(rootkey);
+ if (aw_sid_get_fuse(AW_SID_FUSE_ROOTKEY, rootkey,
+ &rootkey_size) == 0) {
+ hwaddr[0] = 0x2;
+ hwaddr[1] = rootkey[3];
+ hwaddr[2] = rootkey[12];
+ hwaddr[3] = rootkey[13];
+ hwaddr[4] = rootkey[14];
+ hwaddr[5] = rootkey[15];
+ }
+ else {
+ rnd = arc4random() & 0x00ffffff;
+ hwaddr[0] = 'b';
+ hwaddr[1] = 's';
+ hwaddr[2] = 'd';
+ hwaddr[3] = (rnd >> 16) & 0xff;
+ hwaddr[4] = (rnd >> 8) & 0xff;
+ hwaddr[5] = (rnd >> 0) & 0xff;
+ }
+ }
+ if (bootverbose)
+ printf("MAC address: %s\n", ether_sprintf(hwaddr));
+}
+
+static u_int
+emac_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
+{
+ uint32_t h, *hashes = arg;
+
+ h = ether_crc32_be(LLADDR(sdl), ETHER_ADDR_LEN) >> 26;
+ hashes[h >> 5] |= 1 << (h & 0x1f);
+
+ return (1);
+}
+
+static void
+emac_set_rx_mode(struct emac_softc *sc)
+{
+ struct ifnet *ifp;
+ uint32_t hashes[2];
+ uint32_t rcr = 0;
+
+ EMAC_ASSERT_LOCKED(sc);
+
+ ifp = sc->emac_ifp;
+
+ rcr = EMAC_READ_REG(sc, EMAC_RX_CTL);
+
+ /* Unicast packet and DA filtering */
+ rcr |= EMAC_RX_UCAD;
+ rcr |= EMAC_RX_DAF;
+
+ hashes[0] = 0;
+ hashes[1] = 0;
+ if (ifp->if_flags & IFF_ALLMULTI) {
+ hashes[0] = 0xffffffff;
+ hashes[1] = 0xffffffff;
+ } else
+ if_foreach_llmaddr(ifp, emac_hash_maddr, hashes);
+ rcr |= EMAC_RX_MCO;
+ rcr |= EMAC_RX_MHF;
+ EMAC_WRITE_REG(sc, EMAC_RX_HASH0, hashes[0]);
+ EMAC_WRITE_REG(sc, EMAC_RX_HASH1, hashes[1]);
+
+ if (ifp->if_flags & IFF_BROADCAST) {
+ rcr |= EMAC_RX_BCO;
+ rcr |= EMAC_RX_MCO;
+ }
+
+ if (ifp->if_flags & IFF_PROMISC)
+ rcr |= EMAC_RX_PA;
+ else
+ rcr |= EMAC_RX_UCAD;
+
+ EMAC_WRITE_REG(sc, EMAC_RX_CTL, rcr);
+}
+
+static void
+emac_reset(struct emac_softc *sc)
+{
+
+ EMAC_WRITE_REG(sc, EMAC_CTL, 0);
+ DELAY(200);
+ EMAC_WRITE_REG(sc, EMAC_CTL, 1);
+ DELAY(200);
+}
+
+static void
+emac_drain_rxfifo(struct emac_softc *sc)
+{
+ uint32_t data;
+
+ while (EMAC_READ_REG(sc, EMAC_RX_FBC) > 0)
+ data = EMAC_READ_REG(sc, EMAC_RX_IO_DATA);
+}
+
+static void
+emac_txeof(struct emac_softc *sc, uint32_t status)
+{
+ struct ifnet *ifp;
+
+ EMAC_ASSERT_LOCKED(sc);
+
+ ifp = sc->emac_ifp;
+ status &= (EMAC_TX_FIFO0 | EMAC_TX_FIFO1);
+ sc->emac_fifo_mask &= ~status;
+ if (status == (EMAC_TX_FIFO0 | EMAC_TX_FIFO1))
+ if_inc_counter(ifp, IFCOUNTER_OPACKETS, 2);
+ else
+ if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+
+ /* Unarm watchdog timer if no TX */
+ sc->emac_watchdog_timer = 0;
+}
+
+static void
+emac_rxeof(struct emac_softc *sc, int count)
+{
+ struct ifnet *ifp;
+ struct mbuf *m, *m0;
+ uint32_t reg_val, rxcount;
+ int16_t len;
+ uint16_t status;
+ int i;
+
+ ifp = sc->emac_ifp;
+ for (; count > 0 &&
+ (ifp->if_drv_flags & IFF_DRV_RUNNING) != 0; count--) {
+ /*
+ * Race warning: The first packet might arrive with
+ * the interrupts disabled, but the second will fix
+ */
+ rxcount = EMAC_READ_REG(sc, EMAC_RX_FBC);
+ if (!rxcount) {
+ /* Had one stuck? */
+ rxcount = EMAC_READ_REG(sc, EMAC_RX_FBC);
+ if (!rxcount)
+ return;
+ }
+ /* Check packet header */
+ reg_val = EMAC_READ_REG(sc, EMAC_RX_IO_DATA);
+ if (reg_val != EMAC_PACKET_HEADER) {
+ /* Packet header is wrong */
+ if (bootverbose)
+ if_printf(ifp, "wrong packet header\n");
+ /* Disable RX */
+ reg_val = EMAC_READ_REG(sc, EMAC_CTL);
+ reg_val &= ~EMAC_CTL_RX_EN;
+ EMAC_WRITE_REG(sc, EMAC_CTL, reg_val);
+
+ /* Flush RX FIFO */
+ reg_val = EMAC_READ_REG(sc, EMAC_RX_CTL);
+ reg_val |= EMAC_RX_FLUSH_FIFO;
+ EMAC_WRITE_REG(sc, EMAC_RX_CTL, reg_val);
+ for (i = 100; i > 0; i--) {
+ DELAY(100);
+ if ((EMAC_READ_REG(sc, EMAC_RX_CTL) &
+ EMAC_RX_FLUSH_FIFO) == 0)
+ break;
+ }
+ if (i == 0) {
+ device_printf(sc->emac_dev,
+ "flush FIFO timeout\n");
+ /* Reinitialize controller */
+ emac_init_locked(sc);
+ return;
+ }
+ /* Enable RX */
+ reg_val = EMAC_READ_REG(sc, EMAC_CTL);
+ reg_val |= EMAC_CTL_RX_EN;
+ EMAC_WRITE_REG(sc, EMAC_CTL, reg_val);
+
+ return;
+ }
+
+ /* Get packet size and status */
+ reg_val = EMAC_READ_REG(sc, EMAC_RX_IO_DATA);
+ len = reg_val & 0xffff;
+ status = (reg_val >> 16) & 0xffff;
+
+ if (len < 64 || (status & EMAC_PKT_OK) == 0) {
+ if (bootverbose)
+ if_printf(ifp,
+ "bad packet: len = %i status = %i\n",
+ len, status);
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
+ emac_drain_rxfifo(sc);
+ continue;
+ }
+#if 0
+ if (status & (EMAC_CRCERR | EMAC_LENERR)) {
+ good_packet = 0;
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
+ if (status & EMAC_CRCERR)
+ if_printf(ifp, "crc error\n");
+ if (status & EMAC_LENERR)
+ if_printf(ifp, "length error\n");
+ }
+#endif
+ m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
+ if (m == NULL) {
+ emac_drain_rxfifo(sc);
+ return;
+ }
+ m->m_len = m->m_pkthdr.len = MCLBYTES;
+
+ /* Copy entire frame to mbuf first. */
+ bus_space_read_multi_4(sc->emac_tag, sc->emac_handle,
+ EMAC_RX_IO_DATA, mtod(m, uint32_t *), roundup2(len, 4) / 4);
+
+ m->m_pkthdr.rcvif = ifp;
+ m->m_len = m->m_pkthdr.len = len - ETHER_CRC_LEN;
+
+ /*
+ * Emac controller needs strict aligment, so to avoid
+ * copying over an entire frame to align, we allocate
+ * a new mbuf and copy ethernet header + IP header to
+ * the new mbuf. The new mbuf is prepended into the
+ * existing mbuf chain.
+ */
+ if (m->m_len <= (MHLEN - ETHER_HDR_LEN)) {
+ bcopy(m->m_data, m->m_data + ETHER_HDR_LEN, m->m_len);
+ m->m_data += ETHER_HDR_LEN;
+ } else if (m->m_len <= (MCLBYTES - ETHER_HDR_LEN) &&
+ m->m_len > (MHLEN - ETHER_HDR_LEN)) {
+ MGETHDR(m0, M_NOWAIT, MT_DATA);
+ if (m0 != NULL) {
+ len = ETHER_HDR_LEN + m->m_pkthdr.l2hlen;
+ bcopy(m->m_data, m0->m_data, len);
+ m->m_data += len;
+ m->m_len -= len;
+ m0->m_len = len;
+ M_MOVE_PKTHDR(m0, m);
+ m0->m_next = m;
+ m = m0;
+ } else {
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
+ m_freem(m);
+ m = NULL;
+ continue;
+ }
+ } else if (m->m_len > EMAC_MAC_MAXF) {
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
+ m_freem(m);
+ m = NULL;
+ continue;
+ }
+ if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
+ EMAC_UNLOCK(sc);
+ (*ifp->if_input)(ifp, m);
+ EMAC_LOCK(sc);
+ }
+}
+
+static void
+emac_watchdog(struct emac_softc *sc)
+{
+ struct ifnet *ifp;
+
+ EMAC_ASSERT_LOCKED(sc);
+
+ if (sc->emac_watchdog_timer == 0 || --sc->emac_watchdog_timer)
+ return;
+
+ ifp = sc->emac_ifp;
+
+ if (sc->emac_link == 0) {
+ if (bootverbose)
+ if_printf(sc->emac_ifp, "watchdog timeout "
+ "(missed link)\n");
+ } else
+ if_printf(sc->emac_ifp, "watchdog timeout -- resetting\n");
+
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ emac_init_locked(sc);
+ if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+ emac_start_locked(ifp);
+}
+
+static void
+emac_tick(void *arg)
+{
+ struct emac_softc *sc;
+ struct mii_data *mii;
+
+ sc = (struct emac_softc *)arg;
+ mii = device_get_softc(sc->emac_miibus);
+ mii_tick(mii);
+
+ emac_watchdog(sc);
+ callout_reset(&sc->emac_tick_ch, hz, emac_tick, sc);
+}
+
+static void
+emac_init(void *xcs)
+{
+ struct emac_softc *sc;
+
+ sc = (struct emac_softc *)xcs;
+ EMAC_LOCK(sc);
+ emac_init_locked(sc);
+ EMAC_UNLOCK(sc);
+}
+
+static void
+emac_init_locked(struct emac_softc *sc)
+{
+ struct ifnet *ifp;
+ struct mii_data *mii;
+ uint32_t reg_val;
+ uint8_t *eaddr;
+
+ EMAC_ASSERT_LOCKED(sc);
+
+ ifp = sc->emac_ifp;
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
+ return;
+
+ /* Flush RX FIFO */
+ reg_val = EMAC_READ_REG(sc, EMAC_RX_CTL);
+ reg_val |= EMAC_RX_FLUSH_FIFO;
+ EMAC_WRITE_REG(sc, EMAC_RX_CTL, reg_val);
+ DELAY(1);
+
+ /* Soft reset MAC */
+ reg_val = EMAC_READ_REG(sc, EMAC_MAC_CTL0);
+ reg_val &= (~EMAC_MAC_CTL0_SOFT_RST);
+ EMAC_WRITE_REG(sc, EMAC_MAC_CTL0, reg_val);
+
+ /* Set MII clock */
+ reg_val = EMAC_READ_REG(sc, EMAC_MAC_MCFG);
+ reg_val &= (~(0xf << 2));
+ reg_val |= (0xd << 2);
+ EMAC_WRITE_REG(sc, EMAC_MAC_MCFG, reg_val);
+
+ /* Clear RX counter */
+ EMAC_WRITE_REG(sc, EMAC_RX_FBC, 0);
+
+ /* Disable all interrupt and clear interrupt status */
+ EMAC_WRITE_REG(sc, EMAC_INT_CTL, 0);
+ reg_val = EMAC_READ_REG(sc, EMAC_INT_STA);
+ EMAC_WRITE_REG(sc, EMAC_INT_STA, reg_val);
+ DELAY(1);
+
+ /* Set up TX */
+ reg_val = EMAC_READ_REG(sc, EMAC_TX_MODE);
+ reg_val |= EMAC_TX_AB_M;
+ reg_val &= EMAC_TX_TM;
+ EMAC_WRITE_REG(sc, EMAC_TX_MODE, reg_val);
+
+ /* Set up RX */
+ reg_val = EMAC_READ_REG(sc, EMAC_RX_CTL);
+ reg_val |= EMAC_RX_SETUP;
+ reg_val &= EMAC_RX_TM;
+ EMAC_WRITE_REG(sc, EMAC_RX_CTL, reg_val);
+
+ /* Set up MAC CTL0. */
+ reg_val = EMAC_READ_REG(sc, EMAC_MAC_CTL0);
+ reg_val |= EMAC_MAC_CTL0_SETUP;
+ EMAC_WRITE_REG(sc, EMAC_MAC_CTL0, reg_val);
+
+ /* Set up MAC CTL1. */
+ reg_val = EMAC_READ_REG(sc, EMAC_MAC_CTL1);
+ reg_val |= EMAC_MAC_CTL1_SETUP;
+ EMAC_WRITE_REG(sc, EMAC_MAC_CTL1, reg_val);
+
+ /* Set up IPGT */
+ EMAC_WRITE_REG(sc, EMAC_MAC_IPGT, EMAC_MAC_IPGT_FD);
+
+ /* Set up IPGR */
+ EMAC_WRITE_REG(sc, EMAC_MAC_IPGR, EMAC_MAC_NBTB_IPG2 |
+ (EMAC_MAC_NBTB_IPG1 << 8));
+
+ /* Set up Collison window */
+ EMAC_WRITE_REG(sc, EMAC_MAC_CLRT, EMAC_MAC_RM | (EMAC_MAC_CW << 8));
+
+ /* Set up Max Frame Length */
+ EMAC_WRITE_REG(sc, EMAC_MAC_MAXF, EMAC_MAC_MFL);
+
+ /* Setup ethernet address */
+ eaddr = IF_LLADDR(ifp);
+ EMAC_WRITE_REG(sc, EMAC_MAC_A1, eaddr[0] << 16 |
+ eaddr[1] << 8 | eaddr[2]);
+ EMAC_WRITE_REG(sc, EMAC_MAC_A0, eaddr[3] << 16 |
+ eaddr[4] << 8 | eaddr[5]);
+
+ /* Setup rx filter */
+ emac_set_rx_mode(sc);
+
+ /* Enable RX/TX0/RX Hlevel interrupt */
+ reg_val = EMAC_READ_REG(sc, EMAC_INT_CTL);
+ reg_val |= EMAC_INT_EN;
+ EMAC_WRITE_REG(sc, EMAC_INT_CTL, reg_val);
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+
+ sc->emac_link = 0;
+
+ /* Switch to the current media. */
+ mii = device_get_softc(sc->emac_miibus);
+ mii_mediachg(mii);
+
+ callout_reset(&sc->emac_tick_ch, hz, emac_tick, sc);
+}
+
+static void
+emac_start(struct ifnet *ifp)
+{
+ struct emac_softc *sc;
+
+ sc = ifp->if_softc;
+ EMAC_LOCK(sc);
+ emac_start_locked(ifp);
+ EMAC_UNLOCK(sc);
+}
+
+static void
+emac_start_locked(struct ifnet *ifp)
+{
+ struct emac_softc *sc;
+ struct mbuf *m, *m0;
+ uint32_t fifo, reg;
+
+ sc = ifp->if_softc;
+ if (ifp->if_drv_flags & IFF_DRV_OACTIVE)
+ return;
+ if (sc->emac_fifo_mask == (EMAC_TX_FIFO0 | EMAC_TX_FIFO1))
+ return;
+ if (sc->emac_link == 0)
+ return;
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
+ return;
+
+ /* Select channel */
+ if (sc->emac_fifo_mask & EMAC_TX_FIFO0)
+ fifo = 1;
+ else
+ fifo = 0;
+ sc->emac_fifo_mask |= (1 << fifo);
+ if (sc->emac_fifo_mask == (EMAC_TX_FIFO0 | EMAC_TX_FIFO1))
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ EMAC_WRITE_REG(sc, EMAC_TX_INS, fifo);
+
+ /*
+ * Emac controller wants 4 byte aligned TX buffers.
+ * We have to copy pretty much all the time.
+ */
+ if (m->m_next != NULL || (mtod(m, uintptr_t) & 3) != 0) {
+ m0 = m_defrag(m, M_NOWAIT);
+ if (m0 == NULL) {
+ m_freem(m);
+ m = NULL;
+ return;
+ }
+ m = m0;
+ }
+ /* Write data */
+ bus_space_write_multi_4(sc->emac_tag, sc->emac_handle,
+ EMAC_TX_IO_DATA, mtod(m, uint32_t *),
+ roundup2(m->m_len, 4) / 4);
+
+ /* Send the data lengh. */
+ reg = (fifo == 0) ? EMAC_TX_PL0 : EMAC_TX_PL1;
+ EMAC_WRITE_REG(sc, reg, m->m_len);
+
+ /* Start translate from fifo to phy. */
+ reg = (fifo == 0) ? EMAC_TX_CTL0 : EMAC_TX_CTL1;
+ EMAC_WRITE_REG(sc, reg, EMAC_READ_REG(sc, reg) | 1);
+
+ /* Set timeout */
+ sc->emac_watchdog_timer = 5;
+
+ /* Data have been sent to hardware, it is okay to free the mbuf now. */
+ BPF_MTAP(ifp, m);
+ m_freem(m);
+}
+
+static void
+emac_stop_locked(struct emac_softc *sc)
+{
+ struct ifnet *ifp;
+ uint32_t reg_val;
+
+ EMAC_ASSERT_LOCKED(sc);
+
+ ifp = sc->emac_ifp;
+ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+ sc->emac_link = 0;
+
+ /* Disable all interrupt and clear interrupt status */
+ EMAC_WRITE_REG(sc, EMAC_INT_CTL, 0);
+ reg_val = EMAC_READ_REG(sc, EMAC_INT_STA);
+ EMAC_WRITE_REG(sc, EMAC_INT_STA, reg_val);
+
+ /* Disable RX/TX */
+ reg_val = EMAC_READ_REG(sc, EMAC_CTL);
+ reg_val &= ~(EMAC_CTL_RST | EMAC_CTL_TX_EN | EMAC_CTL_RX_EN);
+ EMAC_WRITE_REG(sc, EMAC_CTL, reg_val);
+
+ callout_stop(&sc->emac_tick_ch);
+}
+
+static void
+emac_intr(void *arg)
+{
+ struct emac_softc *sc;
+ struct ifnet *ifp;
+ uint32_t reg_val;
+
+ sc = (struct emac_softc *)arg;
+ EMAC_LOCK(sc);
+
+ /* Disable all interrupts */
+ EMAC_WRITE_REG(sc, EMAC_INT_CTL, 0);
+ /* Get EMAC interrupt status */
+ reg_val = EMAC_READ_REG(sc, EMAC_INT_STA);
+ /* Clear ISR status */
+ EMAC_WRITE_REG(sc, EMAC_INT_STA, reg_val);
+
+ /* Received incoming packet */
+ if (reg_val & EMAC_INT_STA_RX)
+ emac_rxeof(sc, sc->emac_rx_process_limit);
+
+ /* Transmit Interrupt check */
+ if (reg_val & EMAC_INT_STA_TX) {
+ emac_txeof(sc, reg_val);
+ ifp = sc->emac_ifp;
+ if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+ emac_start_locked(ifp);
+ }
+
+ /* Re-enable interrupt mask */
+ reg_val = EMAC_READ_REG(sc, EMAC_INT_CTL);
+ reg_val |= EMAC_INT_EN;
+ EMAC_WRITE_REG(sc, EMAC_INT_CTL, reg_val);
+ EMAC_UNLOCK(sc);
+}
+
+static int
+emac_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
+{
+ struct emac_softc *sc;
+ struct mii_data *mii;
+ struct ifreq *ifr;
+ int error = 0;
+
+ sc = ifp->if_softc;
+ ifr = (struct ifreq *)data;
+
+ switch (command) {
+ case SIOCSIFFLAGS:
+ EMAC_LOCK(sc);
+ if (ifp->if_flags & IFF_UP) {
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
+ if ((ifp->if_flags ^ sc->emac_if_flags) &
+ (IFF_PROMISC | IFF_ALLMULTI))
+ emac_set_rx_mode(sc);
+ } else
+ emac_init_locked(sc);
+ } else {
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
+ emac_stop_locked(sc);
+ }
+ sc->emac_if_flags = ifp->if_flags;
+ EMAC_UNLOCK(sc);
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ EMAC_LOCK(sc);
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ emac_set_rx_mode(sc);
+ }
+ EMAC_UNLOCK(sc);
+ break;
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ mii = device_get_softc(sc->emac_miibus);
+ error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
+ break;
+ default:
+ error = ether_ioctl(ifp, command, data);
+ break;
+ }
+ return (error);
+}
+
+static int
+emac_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-a10-emac"))
+ return (ENXIO);
+
+ device_set_desc(dev, "A10/A20 EMAC ethernet controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+emac_detach(device_t dev)
+{
+ struct emac_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->emac_ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ if (device_is_attached(dev)) {
+ ether_ifdetach(sc->emac_ifp);
+ EMAC_LOCK(sc);
+ emac_stop_locked(sc);
+ EMAC_UNLOCK(sc);
+ callout_drain(&sc->emac_tick_ch);
+ }
+
+ if (sc->emac_intrhand != NULL)
+ bus_teardown_intr(sc->emac_dev, sc->emac_irq,
+ sc->emac_intrhand);
+
+ if (sc->emac_miibus != NULL) {
+ device_delete_child(sc->emac_dev, sc->emac_miibus);
+ bus_generic_detach(sc->emac_dev);
+ }
+
+ if (sc->emac_clk != NULL)
+ clk_disable(sc->emac_clk);
+
+ if (sc->emac_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->emac_res);
+
+ if (sc->emac_irq != NULL)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->emac_irq);
+
+ if (sc->emac_ifp != NULL)
+ if_free(sc->emac_ifp);
+
+ if (mtx_initialized(&sc->emac_mtx))
+ mtx_destroy(&sc->emac_mtx);
+
+ return (0);
+}
+
+static int
+emac_shutdown(device_t dev)
+{
+
+ return (emac_suspend(dev));
+}
+
+static int
+emac_suspend(device_t dev)
+{
+ struct emac_softc *sc;
+ struct ifnet *ifp;
+
+ sc = device_get_softc(dev);
+
+ EMAC_LOCK(sc);
+ ifp = sc->emac_ifp;
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
+ emac_stop_locked(sc);
+ EMAC_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+emac_resume(device_t dev)
+{
+ struct emac_softc *sc;
+ struct ifnet *ifp;
+
+ sc = device_get_softc(dev);
+
+ EMAC_LOCK(sc);
+ ifp = sc->emac_ifp;
+ if ((ifp->if_flags & IFF_UP) != 0) {
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ emac_init_locked(sc);
+ }
+ EMAC_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+emac_attach(device_t dev)
+{
+ struct emac_softc *sc;
+ struct ifnet *ifp;
+ int error, rid;
+ uint8_t eaddr[ETHER_ADDR_LEN];
+
+ sc = device_get_softc(dev);
+ sc->emac_dev = dev;
+
+ error = 0;
+ mtx_init(&sc->emac_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
+ MTX_DEF);
+ callout_init_mtx(&sc->emac_tick_ch, &sc->emac_mtx, 0);
+
+ rid = 0;
+ sc->emac_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->emac_res == NULL) {
+ device_printf(dev, "unable to map memory\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ sc->emac_tag = rman_get_bustag(sc->emac_res);
+ sc->emac_handle = rman_get_bushandle(sc->emac_res);
+
+ rid = 0;
+ sc->emac_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_SHAREABLE | RF_ACTIVE);
+ if (sc->emac_irq == NULL) {
+ device_printf(dev, "cannot allocate IRQ resources.\n");
+ error = ENXIO;
+ goto fail;
+ }
+ /* Create device sysctl node. */
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
+ OID_AUTO, "process_limit",
+ CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
+ &sc->emac_rx_process_limit, 0, sysctl_hw_emac_proc_limit, "I",
+ "max number of Rx events to process");
+
+ sc->emac_rx_process_limit = EMAC_PROC_DEFAULT;
+ error = resource_int_value(device_get_name(dev), device_get_unit(dev),
+ "process_limit", &sc->emac_rx_process_limit);
+ if (error == 0) {
+ if (sc->emac_rx_process_limit < EMAC_PROC_MIN ||
+ sc->emac_rx_process_limit > EMAC_PROC_MAX) {
+ device_printf(dev, "process_limit value out of range; "
+ "using default: %d\n", EMAC_PROC_DEFAULT);
+ sc->emac_rx_process_limit = EMAC_PROC_DEFAULT;
+ }
+ }
+ /* Setup EMAC */
+ error = emac_sys_setup(sc);
+ if (error != 0)
+ goto fail;
+
+ emac_reset(sc);
+
+ ifp = sc->emac_ifp = if_alloc(IFT_ETHER);
+ if (ifp == NULL) {
+ device_printf(dev, "unable to allocate ifp\n");
+ error = ENOSPC;
+ goto fail;
+ }
+ ifp->if_softc = sc;
+
+ /* Setup MII */
+ error = mii_attach(dev, &sc->emac_miibus, ifp, emac_ifmedia_upd,
+ emac_ifmedia_sts, BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY, 0);
+ if (error != 0) {
+ device_printf(dev, "PHY probe failed\n");
+ goto fail;
+ }
+
+ if_initname(ifp, device_get_name(dev), device_get_unit(dev));
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ ifp->if_start = emac_start;
+ ifp->if_ioctl = emac_ioctl;
+ ifp->if_init = emac_init;
+ IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
+
+ /* Get MAC address */
+ emac_get_hwaddr(sc, eaddr);
+ ether_ifattach(ifp, eaddr);
+
+ /* VLAN capability setup. */
+ ifp->if_capabilities |= IFCAP_VLAN_MTU;
+ ifp->if_capenable = ifp->if_capabilities;
+ /* Tell the upper layer we support VLAN over-sized frames. */
+ ifp->if_hdrlen = sizeof(struct ether_vlan_header);
+
+ error = bus_setup_intr(dev, sc->emac_irq, INTR_TYPE_NET | INTR_MPSAFE,
+ NULL, emac_intr, sc, &sc->emac_intrhand);
+ if (error != 0) {
+ device_printf(dev, "could not set up interrupt handler.\n");
+ ether_ifdetach(ifp);
+ goto fail;
+ }
+
+fail:
+ if (error != 0)
+ emac_detach(dev);
+ return (error);
+}
+
+static boolean_t
+emac_miibus_iowait(struct emac_softc *sc)
+{
+ uint32_t timeout;
+
+ for (timeout = 100; timeout != 0; --timeout) {
+ DELAY(100);
+ if ((EMAC_READ_REG(sc, EMAC_MAC_MIND) & 0x1) == 0)
+ return (true);
+ }
+
+ return (false);
+}
+
+/*
+ * The MII bus interface
+ */
+static int
+emac_miibus_readreg(device_t dev, int phy, int reg)
+{
+ struct emac_softc *sc;
+ int rval;
+
+ sc = device_get_softc(dev);
+
+ /* Issue phy address and reg */
+ EMAC_WRITE_REG(sc, EMAC_MAC_MADR, (phy << 8) | reg);
+ /* Pull up the phy io line */
+ EMAC_WRITE_REG(sc, EMAC_MAC_MCMD, 0x1);
+ if (!emac_miibus_iowait(sc)) {
+ device_printf(dev, "timeout waiting for mii read\n");
+ return (0);
+ }
+ /* Push down the phy io line */
+ EMAC_WRITE_REG(sc, EMAC_MAC_MCMD, 0x0);
+ /* Read data */
+ rval = EMAC_READ_REG(sc, EMAC_MAC_MRDD);
+
+ return (rval);
+}
+
+static int
+emac_miibus_writereg(device_t dev, int phy, int reg, int data)
+{
+ struct emac_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ /* Issue phy address and reg */
+ EMAC_WRITE_REG(sc, EMAC_MAC_MADR, (phy << 8) | reg);
+ /* Write data */
+ EMAC_WRITE_REG(sc, EMAC_MAC_MWTD, data);
+ /* Pull up the phy io line */
+ EMAC_WRITE_REG(sc, EMAC_MAC_MCMD, 0x1);
+ if (!emac_miibus_iowait(sc)) {
+ device_printf(dev, "timeout waiting for mii write\n");
+ return (0);
+ }
+ /* Push down the phy io line */
+ EMAC_WRITE_REG(sc, EMAC_MAC_MCMD, 0x0);
+
+ return (0);
+}
+
+static void
+emac_miibus_statchg(device_t dev)
+{
+ struct emac_softc *sc;
+ struct mii_data *mii;
+ struct ifnet *ifp;
+ uint32_t reg_val;
+
+ sc = device_get_softc(dev);
+
+ mii = device_get_softc(sc->emac_miibus);
+ ifp = sc->emac_ifp;
+ if (mii == NULL || ifp == NULL ||
+ (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ return;
+
+ sc->emac_link = 0;
+ if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) ==
+ (IFM_ACTIVE | IFM_AVALID)) {
+ switch (IFM_SUBTYPE(mii->mii_media_active)) {
+ case IFM_10_T:
+ case IFM_100_TX:
+ sc->emac_link = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ /* Program MACs with resolved speed/duplex. */
+ if (sc->emac_link != 0) {
+ reg_val = EMAC_READ_REG(sc, EMAC_MAC_IPGT);
+ if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) {
+ reg_val &= ~EMAC_MAC_IPGT_HD;
+ reg_val |= EMAC_MAC_IPGT_FD;
+ } else {
+ reg_val &= ~EMAC_MAC_IPGT_FD;
+ reg_val |= EMAC_MAC_IPGT_HD;
+ }
+ EMAC_WRITE_REG(sc, EMAC_MAC_IPGT, reg_val);
+ /* Enable RX/TX */
+ reg_val = EMAC_READ_REG(sc, EMAC_CTL);
+ reg_val |= EMAC_CTL_RST | EMAC_CTL_TX_EN | EMAC_CTL_RX_EN;
+ EMAC_WRITE_REG(sc, EMAC_CTL, reg_val);
+ } else {
+ /* Disable RX/TX */
+ reg_val = EMAC_READ_REG(sc, EMAC_CTL);
+ reg_val &= ~(EMAC_CTL_RST | EMAC_CTL_TX_EN | EMAC_CTL_RX_EN);
+ EMAC_WRITE_REG(sc, EMAC_CTL, reg_val);
+ }
+}
+
+static int
+emac_ifmedia_upd(struct ifnet *ifp)
+{
+ struct emac_softc *sc;
+ struct mii_data *mii;
+ struct mii_softc *miisc;
+ int error;
+
+ sc = ifp->if_softc;
+ mii = device_get_softc(sc->emac_miibus);
+ EMAC_LOCK(sc);
+ LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
+ PHY_RESET(miisc);
+ error = mii_mediachg(mii);
+ EMAC_UNLOCK(sc);
+
+ return (error);
+}
+
+static void
+emac_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ struct emac_softc *sc;
+ struct mii_data *mii;
+
+ sc = ifp->if_softc;
+ mii = device_get_softc(sc->emac_miibus);
+
+ EMAC_LOCK(sc);
+ mii_pollstat(mii);
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+ EMAC_UNLOCK(sc);
+}
+
+static device_method_t emac_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, emac_probe),
+ DEVMETHOD(device_attach, emac_attach),
+ DEVMETHOD(device_detach, emac_detach),
+ DEVMETHOD(device_shutdown, emac_shutdown),
+ DEVMETHOD(device_suspend, emac_suspend),
+ DEVMETHOD(device_resume, emac_resume),
+
+ /* bus interface, for miibus */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+
+ /* MII interface */
+ DEVMETHOD(miibus_readreg, emac_miibus_readreg),
+ DEVMETHOD(miibus_writereg, emac_miibus_writereg),
+ DEVMETHOD(miibus_statchg, emac_miibus_statchg),
+
+ DEVMETHOD_END
+};
+
+static driver_t emac_driver = {
+ "emac",
+ emac_methods,
+ sizeof(struct emac_softc)
+};
+
+static devclass_t emac_devclass;
+
+DRIVER_MODULE(emac, simplebus, emac_driver, emac_devclass, 0, 0);
+DRIVER_MODULE(miibus, emac, miibus_driver, miibus_devclass, 0, 0);
+MODULE_DEPEND(emac, miibus, 1, 1, 1);
+MODULE_DEPEND(emac, ether, 1, 1, 1);
+
+static int
+sysctl_int_range(SYSCTL_HANDLER_ARGS, int low, int high)
+{
+ int error, value;
+
+ if (arg1 == NULL)
+ return (EINVAL);
+ value = *(int *)arg1;
+ error = sysctl_handle_int(oidp, &value, 0, req);
+ if (error || req->newptr == NULL)
+ return (error);
+ if (value < low || value > high)
+ return (EINVAL);
+ *(int *)arg1 = value;
+
+ return (0);
+}
+
+static int
+sysctl_hw_emac_proc_limit(SYSCTL_HANDLER_ARGS)
+{
+
+ return (sysctl_int_range(oidp, arg1, arg2, req,
+ EMAC_PROC_MIN, EMAC_PROC_MAX));
+}
diff --git a/sys/arm/allwinner/if_emacreg.h b/sys/arm/allwinner/if_emacreg.h
new file mode 100644
index 000000000000..f84e92c56e04
--- /dev/null
+++ b/sys/arm/allwinner/if_emacreg.h
@@ -0,0 +1,244 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (C) 2013 Ganbold Tsagaankhuu <ganbold@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __IF_EMACREG_H__
+#define __IF_EMACREG_H__
+
+/*
+ * EMAC register definitions
+ */
+#define EMAC_CTL 0x00
+#define EMAC_CTL_RST (1 << 0)
+#define EMAC_CTL_TX_EN (1 << 1)
+#define EMAC_CTL_RX_EN (1 << 2)
+
+#define EMAC_TX_MODE 0x04
+#define EMAC_TX_FLOW 0x08
+#define EMAC_TX_CTL0 0x0C
+#define EMAC_TX_CTL1 0x10
+#define EMAC_TX_INS 0x14
+#define EMAC_TX_PL0 0x18
+#define EMAC_TX_PL1 0x1C
+#define EMAC_TX_STA 0x20
+#define EMAC_TX_IO_DATA 0x24
+#define EMAC_TX_IO_DATA1 0x28
+#define EMAC_TX_TSVL0 0x2C
+#define EMAC_TX_TSVH0 0x30
+#define EMAC_TX_TSVL1 0x34
+#define EMAC_TX_TSVH1 0x38
+#define EMAC_TX_FIFO0 (1 << 0)
+#define EMAC_TX_FIFO1 (1 << 1)
+
+#define EMAC_RX_CTL 0x3C
+#define EMAC_RX_HASH0 0x40
+#define EMAC_RX_HASH1 0x44
+#define EMAC_RX_STA 0x48
+#define EMAC_RX_IO_DATA 0x4C
+#define EMAC_RX_FBC 0x50
+
+#define EMAC_INT_CTL 0x54
+#define EMAC_INT_STA 0x58
+#define EMAC_INT_STA_TX (EMAC_TX_FIFO0 | EMAC_TX_FIFO1)
+#define EMAC_INT_STA_RX 0x100
+#define EMAC_INT_EN (0xf << 0) | (1 << 8)
+
+#define EMAC_MAC_CTL0 0x5C
+#define EMAC_MAC_CTL1 0x60
+#define EMAC_MAC_IPGT 0x64
+#define EMAC_MAC_IPGR 0x68
+#define EMAC_MAC_CLRT 0x6C
+#define EMAC_MAC_MAXF 0x70
+#define EMAC_MAC_SUPP 0x74
+#define EMAC_MAC_TEST 0x78
+#define EMAC_MAC_MCFG 0x7C
+#define EMAC_MAC_MCMD 0x80
+#define EMAC_MAC_MADR 0x84
+#define EMAC_MAC_MWTD 0x88
+#define EMAC_MAC_MRDD 0x8C
+#define EMAC_MAC_MIND 0x90
+#define EMAC_MAC_SSRR 0x94
+#define EMAC_MAC_A0 0x98
+#define EMAC_MAC_A1 0x9C
+#define EMAC_MAC_A2 0xA0
+
+#define EMAC_SAFX_L0 0xA4
+#define EMAC_SAFX_H0 0xA8
+#define EMAC_SAFX_L1 0xAC
+#define EMAC_SAFX_H1 0xB0
+#define EMAC_SAFX_L2 0xB4
+#define EMAC_SAFX_H2 0xB8
+#define EMAC_SAFX_L3 0xBC
+#define EMAC_SAFX_H3 0xC0
+
+#define EMAC_PHY_DUPLEX (1 << 8)
+
+/*
+ * Each received packet has 8 bytes header:
+ * Byte 0: Packet valid flag: 0x01 valid, 0x00 not valid
+ * Byte 1: 0x43 -> Ascii code 'C'
+ * Byte 2: 0x41 -> Ascii code 'A'
+ * Byte 3: 0x4d -> Ascii code 'M'
+ * Byte 4: High byte of received packet's status
+ * Byte 5: Low byte of received packet's status
+ * Byte 6: High byte of packet size
+ * Byte 7: Low byte of packet size
+ */
+#define EMAC_PACKET_HEADER (0x0143414d)
+
+/* Aborted frame enable */
+#define EMAC_TX_AB_M (1 << 0)
+
+/* 0: Enable CPU mode for TX, 1: DMA */
+#define EMAC_TX_TM ~(1 << 1)
+
+/* 0: DRQ asserted, 1: DRQ automatically */
+#define EMAC_RX_DRQ_MODE (1 << 1)
+
+/* 0: Enable CPU mode for RX, 1: DMA */
+#define EMAC_RX_TM ~(1 << 2)
+
+/* Pass all Frames */
+#define EMAC_RX_PA (1 << 4)
+
+/* Pass Control Frames */
+#define EMAC_RX_PCF (1 << 5)
+
+/* Pass Frames with CRC Error */
+#define EMAC_RX_PCRCE (1 << 6)
+
+/* Pass Frames with Length Error */
+#define EMAC_RX_PLE (1 << 7)
+
+/* Pass Frames length out of range */
+#define EMAC_RX_POR (1 << 8)
+
+/* Accept unicast Packets */
+#define EMAC_RX_UCAD (1 << 16)
+
+/* Enable DA Filtering */
+#define EMAC_RX_DAF (1 << 17)
+
+/* Accept multicast Packets */
+#define EMAC_RX_MCO (1 << 20)
+
+/* Enable Hash filter */
+#define EMAC_RX_MHF (1 << 21)
+
+/* Accept Broadcast Packets */
+#define EMAC_RX_BCO (1 << 22)
+
+/* Enable SA Filtering */
+#define EMAC_RX_SAF (1 << 24)
+
+/* Inverse Filtering */
+#define EMAC_RX_SAIF (1 << 25)
+
+#define EMAC_RX_SETUP (EMAC_RX_POR | EMAC_RX_UCAD | \
+ EMAC_RX_DAF | EMAC_RX_MCO | EMAC_RX_BCO)
+
+/* Enable Receive Flow Control */
+#define EMAC_MAC_CTL0_RFC (1 << 2)
+
+/* Enable Transmit Flow Control */
+#define EMAC_MAC_CTL0_TFC (1 << 3)
+
+/* Enable soft reset */
+#define EMAC_MAC_CTL0_SOFT_RST (1 << 15)
+
+#define EMAC_MAC_CTL0_SETUP (EMAC_MAC_CTL0_RFC | EMAC_MAC_CTL0_TFC)
+
+/* Enable duplex */
+#define EMAC_MAC_CTL1_DUP (1 << 0)
+
+/* Enable MAC Frame Length Checking */
+#define EMAC_MAC_CTL1_FLC (1 << 1)
+
+/* Enable Huge Frame */
+#define EMAC_MAC_CTL1_HF (1 << 2)
+
+/* Enable MAC Delayed CRC */
+#define EMAC_MAC_CTL1_DCRC (1 << 3)
+
+/* Enable MAC CRC */
+#define EMAC_MAC_CTL1_CRC (1 << 4)
+
+/* Enable MAC PAD Short frames */
+#define EMAC_MAC_CTL1_PC (1 << 5)
+
+/* Enable MAC PAD Short frames and append CRC */
+#define EMAC_MAC_CTL1_VC (1 << 6)
+
+/* Enable MAC auto detect Short frames */
+#define EMAC_MAC_CTL1_ADP (1 << 7)
+
+#define EMAC_MAC_CTL1_PRE (1 << 8)
+#define EMAC_MAC_CTL1_LPE (1 << 9)
+
+/* Enable no back off */
+#define EMAC_MAC_CTL1_NB (1 << 12)
+
+#define EMAC_MAC_CTL1_BNB (1 << 13)
+#define EMAC_MAC_CTL1_ED (1 << 14)
+
+#define EMAC_MAC_CTL1_SETUP (EMAC_MAC_CTL1_FLC | EMAC_MAC_CTL1_CRC | \
+ EMAC_MAC_CTL1_PC)
+
+/* half duplex */
+#define EMAC_MAC_IPGT_HD 0x12
+
+/* full duplex */
+#define EMAC_MAC_IPGT_FD 0x15
+
+#define EMAC_MAC_NBTB_IPG1 0xC
+#define EMAC_MAC_NBTB_IPG2 0x12
+
+#define EMAC_MAC_CW 0x37
+#define EMAC_MAC_RM 0xF
+
+#define EMAC_MAC_MFL 0x0600
+
+/* Receive status */
+#define EMAC_CRCERR (1 << 4)
+#define EMAC_LENERR (3 << 5)
+#define EMAC_PKT_OK (1 << 7)
+
+#define EMAC_RX_FLUSH_FIFO (1 << 3)
+#define EMAC_PHY_RESET (1 << 15)
+#define EMAC_PHY_PWRDOWN (1 << 11)
+
+#define EMAC_PROC_MIN 16
+#define EMAC_PROC_MAX 255
+#define EMAC_PROC_DEFAULT 64
+
+#define EMAC_LOCK(cs) mtx_lock(&(sc)->emac_mtx)
+#define EMAC_UNLOCK(cs) mtx_unlock(&(sc)->emac_mtx)
+#define EMAC_ASSERT_LOCKED(sc) mtx_assert(&(sc)->emac_mtx, MA_OWNED);
+
+#endif /* __IF_EMACREG_H__ */
diff --git a/sys/arm/allwinner/std.allwinner b/sys/arm/allwinner/std.allwinner
new file mode 100644
index 000000000000..4ba2ec7f0e1d
--- /dev/null
+++ b/sys/arm/allwinner/std.allwinner
@@ -0,0 +1,12 @@
+# Allwinner common options
+#$FreeBSD$
+
+cpu CPU_CORTEXA
+machine arm armv7
+makeoptions CONF_CFLAGS="-march=armv7a"
+
+files "../allwinner/files.allwinner"
+files "../allwinner/a20/files.a20"
+files "../allwinner/a31/files.a31"
+files "../allwinner/a83t/files.a83t"
+files "../allwinner/h3/files.h3"
diff --git a/sys/arm/allwinner/std.allwinner_up b/sys/arm/allwinner/std.allwinner_up
new file mode 100644
index 000000000000..3fdfb7bba156
--- /dev/null
+++ b/sys/arm/allwinner/std.allwinner_up
@@ -0,0 +1,11 @@
+# Allwinner Uniprocessor common options
+#$FreeBSD$
+
+cpu CPU_CORTEXA
+machine arm armv7
+makeoptions CONF_CFLAGS="-march=armv7a"
+
+files "../allwinner/files.allwinner_up"
+files "../allwinner/files.allwinner"
+files "../allwinner/a10/files.a10"
+files "../allwinner/a13/files.a13"
diff --git a/sys/arm/allwinner/sunxi_dma_if.m b/sys/arm/allwinner/sunxi_dma_if.m
new file mode 100644
index 000000000000..6e283b93cf41
--- /dev/null
+++ b/sys/arm/allwinner/sunxi_dma_if.m
@@ -0,0 +1,98 @@
+#-
+# Copyright (c) 2016 Jared D. McNeill <jmcneill@invisible.ca>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+
+#include <sys/bus.h>
+
+INTERFACE sunxi_dma;
+
+HEADER {
+ #include <machine/bus.h>
+
+ struct sunxi_dma_config {
+ unsigned int dst_width;
+ unsigned int dst_burst_len;
+ unsigned int dst_drqtype;
+ bool dst_noincr;
+ unsigned int dst_blksize; /* DDMA-only */
+ unsigned int dst_wait_cyc; /* DDMA-only */
+ unsigned int src_width;
+ unsigned int src_burst_len;
+ unsigned int src_drqtype;
+ bool src_noincr;
+ unsigned int src_blksize; /* DDMA-only */
+ unsigned int src_wait_cyc; /* DDMA-only */
+ };
+
+ typedef void (*sunxi_dma_callback)(void *);
+}
+
+#
+# Allocate DMA channel
+#
+METHOD void * alloc {
+ device_t dev;
+ bool dedicated;
+ sunxi_dma_callback callback;
+ void *callback_arg;
+};
+
+#
+# Free DMA channel
+#
+METHOD void free {
+ device_t dev;
+ void *dmachan;
+};
+
+#
+# Set DMA channel configuration
+#
+METHOD int set_config {
+ device_t dev;
+ void *dmachan;
+ const struct sunxi_dma_config *cfg;
+};
+
+#
+# Start DMA channel transfer
+#
+METHOD int transfer {
+ device_t dev;
+ void *dmachan;
+ bus_addr_t src;
+ bus_addr_t dst;
+ size_t nbytes;
+};
+
+#
+# Halt DMA channel transfer
+#
+METHOD void halt {
+ device_t dev;
+ void *dmachan;
+};
diff --git a/sys/arm/altera/socfpga/files.socfpga b/sys/arm/altera/socfpga/files.socfpga
new file mode 100644
index 000000000000..56c1d16f1f46
--- /dev/null
+++ b/sys/arm/altera/socfpga/files.socfpga
@@ -0,0 +1,20 @@
+# $FreeBSD$
+
+arm/altera/socfpga/socfpga_common.c standard
+arm/altera/socfpga/socfpga_machdep.c standard
+arm/altera/socfpga/socfpga_manager.c standard
+arm/altera/socfpga/socfpga_rstmgr.c standard
+arm/altera/socfpga/socfpga_mp.c optional smp
+
+dev/mmc/host/dwmmc.c optional dwmmc
+dev/mmc/host/dwmmc_altera.c optional dwmmc
+
+# Arria 10
+arm/altera/socfpga/socfpga_a10_manager.c standard
+
+# BERI specific
+dev/beri/beri_ring.c optional beri_ring
+dev/beri/beri_mem.c optional beri_mem
+dev/beri/virtio/virtio.c optional beri_vtblk | vtbe
+dev/beri/virtio/virtio_block.c optional beri_vtblk
+dev/beri/virtio/network/if_vtbe.c optional vtbe
diff --git a/sys/arm/altera/socfpga/socfpga_a10_manager.c b/sys/arm/altera/socfpga/socfpga_a10_manager.c
new file mode 100644
index 000000000000..8ccaa4b6b473
--- /dev/null
+++ b/sys/arm/altera/socfpga/socfpga_a10_manager.c
@@ -0,0 +1,443 @@
+/*-
+ * Copyright (c) 2017 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Intel Arria 10 FPGA Manager.
+ * Chapter 4, Arria 10 Hard Processor System Technical Reference Manual.
+ * Chapter A, FPGA Reconfiguration.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/conf.h>
+#include <sys/uio.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <arm/altera/socfpga/socfpga_common.h>
+
+#define FPGAMGR_DCLKCNT 0x8 /* DCLK Count Register */
+#define FPGAMGR_DCLKSTAT 0xC /* DCLK Status Register */
+#define FPGAMGR_GPO 0x10 /* General-Purpose Output Register */
+#define FPGAMGR_GPI 0x14 /* General-Purpose Input Register */
+#define FPGAMGR_MISCI 0x18 /* Miscellaneous Input Register */
+#define IMGCFG_CTRL_00 0x70
+#define S2F_CONDONE_OE (1 << 24)
+#define S2F_NSTATUS_OE (1 << 16)
+#define CTRL_00_NCONFIG (1 << 8)
+#define CTRL_00_NENABLE_CONDONE (1 << 2)
+#define CTRL_00_NENABLE_NSTATUS (1 << 1)
+#define CTRL_00_NENABLE_NCONFIG (1 << 0)
+#define IMGCFG_CTRL_01 0x74
+#define CTRL_01_S2F_NCE (1 << 24)
+#define CTRL_01_S2F_PR_REQUEST (1 << 16)
+#define CTRL_01_S2F_NENABLE_CONFIG (1 << 0)
+#define IMGCFG_CTRL_02 0x78
+#define CTRL_02_CDRATIO_S 16
+#define CTRL_02_CDRATIO_M (0x3 << CTRL_02_CDRATIO_S)
+#define CTRL_02_CFGWIDTH_16 (0 << 24)
+#define CTRL_02_CFGWIDTH_32 (1 << 24)
+#define CTRL_02_EN_CFG_DATA (1 << 8)
+#define CTRL_02_EN_CFG_CTRL (1 << 0)
+#define IMGCFG_STAT 0x80
+#define F2S_PR_ERROR (1 << 11)
+#define F2S_PR_DONE (1 << 10)
+#define F2S_PR_READY (1 << 9)
+#define F2S_MSEL_S 16
+#define F2S_MSEL_M (0x7 << F2S_MSEL_S)
+#define MSEL_PASSIVE_FAST 0
+#define MSEL_PASSIVE_SLOW 1
+#define F2S_NCONFIG_PIN (1 << 12)
+#define F2S_CONDONE_OE (1 << 7)
+#define F2S_NSTATUS_PIN (1 << 4)
+#define F2S_CONDONE_PIN (1 << 6)
+#define F2S_USERMODE (1 << 2)
+
+struct fpgamgr_a10_softc {
+ struct resource *res[2];
+ bus_space_tag_t bst_data;
+ bus_space_handle_t bsh_data;
+ struct cdev *mgr_cdev;
+ device_t dev;
+};
+
+static struct resource_spec fpgamgr_a10_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_MEMORY, 1, RF_ACTIVE },
+ { -1, 0 }
+};
+
+static int
+fpga_wait_dclk_pulses(struct fpgamgr_a10_softc *sc, int npulses)
+{
+ int tout;
+
+ /* Clear done bit, if any */
+ if (READ4(sc, FPGAMGR_DCLKSTAT) != 0)
+ WRITE4(sc, FPGAMGR_DCLKSTAT, 0x1);
+
+ /* Request DCLK pulses */
+ WRITE4(sc, FPGAMGR_DCLKCNT, npulses);
+
+ /* Wait finish */
+ tout = 1000;
+ while (tout > 0) {
+ if (READ4(sc, FPGAMGR_DCLKSTAT) == 1) {
+ WRITE4(sc, FPGAMGR_DCLKSTAT, 0x1);
+ break;
+ }
+ tout--;
+ DELAY(10);
+ }
+ if (tout == 0) {
+ device_printf(sc->dev,
+ "Error: dclkpulses wait timeout\n");
+ return (1);
+ }
+
+ return (0);
+}
+
+static int
+fpga_open(struct cdev *dev, int flags __unused,
+ int fmt __unused, struct thread *td __unused)
+{
+ struct fpgamgr_a10_softc *sc;
+ int tout;
+ int msel;
+ int reg;
+
+ sc = dev->si_drv1;
+
+ /* Step 1 */
+ reg = READ4(sc, IMGCFG_STAT);
+ if ((reg & F2S_USERMODE) == 0) {
+ device_printf(sc->dev, "Error: invalid mode\n");
+ return (ENXIO);
+ };
+
+ /* Step 2 */
+ reg = READ4(sc, IMGCFG_STAT);
+ msel = (reg & F2S_MSEL_M) >> F2S_MSEL_S;
+ if ((msel != MSEL_PASSIVE_FAST) && \
+ (msel != MSEL_PASSIVE_SLOW)) {
+ device_printf(sc->dev,
+ "Error: invalid msel %d\n", msel);
+ return (ENXIO);
+ };
+
+ /*
+ * Step 3.
+ * TODO: add support for compressed, encrypted images.
+ */
+ reg = READ4(sc, IMGCFG_CTRL_02);
+ reg &= ~(CTRL_02_CDRATIO_M);
+ WRITE4(sc, IMGCFG_CTRL_02, reg);
+
+ reg = READ4(sc, IMGCFG_CTRL_02);
+ reg &= ~CTRL_02_CFGWIDTH_32;
+ WRITE4(sc, IMGCFG_CTRL_02, reg);
+
+ /* Step 4. a */
+ reg = READ4(sc, IMGCFG_CTRL_01);
+ reg &= ~CTRL_01_S2F_PR_REQUEST;
+ WRITE4(sc, IMGCFG_CTRL_01, reg);
+
+ reg = READ4(sc, IMGCFG_CTRL_00);
+ reg |= CTRL_00_NCONFIG;
+ WRITE4(sc, IMGCFG_CTRL_00, reg);
+
+ /* b */
+ reg = READ4(sc, IMGCFG_CTRL_01);
+ reg &= ~CTRL_01_S2F_NCE;
+ WRITE4(sc, IMGCFG_CTRL_01, reg);
+
+ /* c */
+ reg = READ4(sc, IMGCFG_CTRL_02);
+ reg |= CTRL_02_EN_CFG_CTRL;
+ WRITE4(sc, IMGCFG_CTRL_02, reg);
+
+ /* d */
+ reg = READ4(sc, IMGCFG_CTRL_00);
+ reg &= ~S2F_CONDONE_OE;
+ reg &= ~S2F_NSTATUS_OE;
+ reg |= CTRL_00_NCONFIG;
+ reg |= CTRL_00_NENABLE_NSTATUS;
+ reg |= CTRL_00_NENABLE_CONDONE;
+ reg &= ~CTRL_00_NENABLE_NCONFIG;
+ WRITE4(sc, IMGCFG_CTRL_00, reg);
+
+ /* Step 5 */
+ reg = READ4(sc, IMGCFG_CTRL_01);
+ reg &= ~CTRL_01_S2F_NENABLE_CONFIG;
+ WRITE4(sc, IMGCFG_CTRL_01, reg);
+
+ /* Step 6 */
+ fpga_wait_dclk_pulses(sc, 0x100);
+
+ /* Step 7. a */
+ reg = READ4(sc, IMGCFG_CTRL_01);
+ reg |= CTRL_01_S2F_PR_REQUEST;
+ WRITE4(sc, IMGCFG_CTRL_01, reg);
+
+ /* b, c */
+ fpga_wait_dclk_pulses(sc, 0x7ff);
+
+ /* Step 8 */
+ tout = 10;
+ while (tout--) {
+ reg = READ4(sc, IMGCFG_STAT);
+ if (reg & F2S_PR_ERROR) {
+ device_printf(sc->dev,
+ "Error: PR failed on open.\n");
+ return (ENXIO);
+ }
+ if (reg & F2S_PR_READY) {
+ break;
+ }
+ }
+ if (tout == 0) {
+ device_printf(sc->dev,
+ "Error: Timeout waiting PR ready bit.\n");
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static int
+fpga_close(struct cdev *dev, int flags __unused,
+ int fmt __unused, struct thread *td __unused)
+{
+ struct fpgamgr_a10_softc *sc;
+ int tout;
+ int reg;
+
+ sc = dev->si_drv1;
+
+ /* Step 10 */
+ tout = 10;
+ while (tout--) {
+ reg = READ4(sc, IMGCFG_STAT);
+ if (reg & F2S_PR_ERROR) {
+ device_printf(sc->dev,
+ "Error: PR failed.\n");
+ return (ENXIO);
+ }
+ if (reg & F2S_PR_DONE) {
+ break;
+ }
+ }
+
+ /* Step 11 */
+ reg = READ4(sc, IMGCFG_CTRL_01);
+ reg &= ~CTRL_01_S2F_PR_REQUEST;
+ WRITE4(sc, IMGCFG_CTRL_01, reg);
+
+ /* Step 12, 13 */
+ fpga_wait_dclk_pulses(sc, 0x100);
+
+ /* Step 14 */
+ reg = READ4(sc, IMGCFG_CTRL_02);
+ reg &= ~CTRL_02_EN_CFG_CTRL;
+ WRITE4(sc, IMGCFG_CTRL_02, reg);
+
+ /* Step 15 */
+ reg = READ4(sc, IMGCFG_CTRL_01);
+ reg |= CTRL_01_S2F_NCE;
+ WRITE4(sc, IMGCFG_CTRL_01, reg);
+
+ /* Step 16 */
+ reg = READ4(sc, IMGCFG_CTRL_01);
+ reg |= CTRL_01_S2F_NENABLE_CONFIG;
+ WRITE4(sc, IMGCFG_CTRL_01, reg);
+
+ /* Step 17 */
+ reg = READ4(sc, IMGCFG_STAT);
+ if ((reg & F2S_USERMODE) == 0) {
+ device_printf(sc->dev,
+ "Error: invalid mode\n");
+ return (ENXIO);
+ };
+
+ if ((reg & F2S_CONDONE_PIN) == 0) {
+ device_printf(sc->dev,
+ "Error: configuration not done\n");
+ return (ENXIO);
+ };
+
+ if ((reg & F2S_NSTATUS_PIN) == 0) {
+ device_printf(sc->dev,
+ "Error: nstatus pin\n");
+ return (ENXIO);
+ };
+
+ return (0);
+}
+
+static int
+fpga_write(struct cdev *dev, struct uio *uio, int ioflag)
+{
+ struct fpgamgr_a10_softc *sc;
+ uint32_t buffer;
+
+ sc = dev->si_drv1;
+
+ /*
+ * Step 9.
+ * Device supports 4-byte writes only.
+ */
+
+ while (uio->uio_resid >= 4) {
+ uiomove(&buffer, 4, uio);
+ bus_space_write_4(sc->bst_data, sc->bsh_data,
+ 0x0, buffer);
+ }
+
+ switch (uio->uio_resid) {
+ case 3:
+ uiomove(&buffer, 3, uio);
+ buffer &= 0xffffff;
+ bus_space_write_4(sc->bst_data, sc->bsh_data,
+ 0x0, buffer);
+ break;
+ case 2:
+ uiomove(&buffer, 2, uio);
+ buffer &= 0xffff;
+ bus_space_write_4(sc->bst_data, sc->bsh_data,
+ 0x0, buffer);
+ break;
+ case 1:
+ uiomove(&buffer, 1, uio);
+ buffer &= 0xff;
+ bus_space_write_4(sc->bst_data, sc->bsh_data,
+ 0x0, buffer);
+ break;
+ default:
+ break;
+ };
+
+ return (0);
+}
+
+static int
+fpga_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags,
+ struct thread *td)
+{
+
+ return (0);
+}
+
+static struct cdevsw fpga_cdevsw = {
+ .d_version = D_VERSION,
+ .d_open = fpga_open,
+ .d_close = fpga_close,
+ .d_write = fpga_write,
+ .d_ioctl = fpga_ioctl,
+ .d_name = "FPGA Manager",
+};
+
+static int
+fpgamgr_a10_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "altr,socfpga-a10-fpga-mgr"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Arria 10 FPGA Manager");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+fpgamgr_a10_attach(device_t dev)
+{
+ struct fpgamgr_a10_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ if (bus_alloc_resources(dev, fpgamgr_a10_spec, sc->res)) {
+ device_printf(dev, "Could not allocate resources.\n");
+ return (ENXIO);
+ }
+
+ /* Memory interface */
+ sc->bst_data = rman_get_bustag(sc->res[1]);
+ sc->bsh_data = rman_get_bushandle(sc->res[1]);
+
+ sc->mgr_cdev = make_dev(&fpga_cdevsw, 0, UID_ROOT, GID_WHEEL,
+ 0600, "fpga%d", device_get_unit(sc->dev));
+
+ if (sc->mgr_cdev == NULL) {
+ device_printf(dev, "Failed to create character device.\n");
+ return (ENXIO);
+ }
+
+ sc->mgr_cdev->si_drv1 = sc;
+
+ return (0);
+}
+
+static device_method_t fpgamgr_a10_methods[] = {
+ DEVMETHOD(device_probe, fpgamgr_a10_probe),
+ DEVMETHOD(device_attach, fpgamgr_a10_attach),
+ { 0, 0 }
+};
+
+static driver_t fpgamgr_a10_driver = {
+ "fpgamgr_a10",
+ fpgamgr_a10_methods,
+ sizeof(struct fpgamgr_a10_softc),
+};
+
+static devclass_t fpgamgr_a10_devclass;
+
+DRIVER_MODULE(fpgamgr_a10, simplebus, fpgamgr_a10_driver,
+ fpgamgr_a10_devclass, 0, 0);
diff --git a/sys/arm/altera/socfpga/socfpga_common.c b/sys/arm/altera/socfpga/socfpga_common.c
new file mode 100644
index 000000000000..2d466275ff16
--- /dev/null
+++ b/sys/arm/altera/socfpga/socfpga_common.c
@@ -0,0 +1,44 @@
+/*-
+ * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+
+#include <dev/ofw/openfirm.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+
+#include <arm/altera/socfpga/socfpga_rstmgr.h>
diff --git a/sys/arm/altera/socfpga/socfpga_common.h b/sys/arm/altera/socfpga/socfpga_common.h
new file mode 100644
index 000000000000..2b4e97bbb462
--- /dev/null
+++ b/sys/arm/altera/socfpga/socfpga_common.h
@@ -0,0 +1,38 @@
+/*-
+ * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define READ4(_sc, _reg) bus_read_4((_sc)->res[0], _reg)
+#define READ2(_sc, _reg) bus_read_2((_sc)->res[0], _reg)
+#define READ1(_sc, _reg) bus_read_1((_sc)->res[0], _reg)
+#define WRITE4(_sc, _reg, _val) bus_write_4((_sc)->res[0], _reg, _val)
+#define WRITE2(_sc, _reg, _val) bus_write_2((_sc)->res[0], _reg, _val)
+#define WRITE1(_sc, _reg, _val) bus_write_1((_sc)->res[0], _reg, _val)
diff --git a/sys/arm/altera/socfpga/socfpga_l3regs.h b/sys/arm/altera/socfpga/socfpga_l3regs.h
new file mode 100644
index 000000000000..b4ef6f6d980d
--- /dev/null
+++ b/sys/arm/altera/socfpga/socfpga_l3regs.h
@@ -0,0 +1,54 @@
+/*-
+ * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define L3REGS_REMAP 0x0 /* Remap */
+#define REMAP_LWHPS2FPGA (1 << 4)
+#define REMAP_HPS2FPGA (1 << 3)
+#define REMAP_MPUZERO (1 << 0)
+#define L3REGS_L4MAIN 0x8 /* L4 main peripherals security */
+#define L3REGS_L4SP 0xC /* L4 SP Peripherals Security */
+#define L3REGS_L4MP 0x10 /* L4 MP Peripherals Security */
+#define L3REGS_L4OSC1 0x14 /* L4 OSC1 Peripherals Security */
+#define L3REGS_L4SPIM 0x18 /* L4 SPIM Peripherals Security */
+#define L3REGS_STM 0x1C /* STM Peripheral Security */
+#define L3REGS_LWHPS2FPGAREGS 0x20 /* LWHPS2FPGA AXI Bridge Security */
+#define L3REGS_USB1 0x28 /* USB1 Peripheral Security */
+#define L3REGS_NANDDATA 0x2C /* NAND Flash Controller Data Sec */
+#define L3REGS_USB0 0x80 /* USB0 Peripheral Security */
+#define L3REGS_NANDREGS 0x84 /* NAND Flash Controller Security */
+#define L3REGS_QSPIDATA 0x88 /* QSPI Flash Controller Data Sec */
+#define L3REGS_FPGAMGRDATA 0x8C /* FPGA Manager Data Peripheral Sec */
+#define L3REGS_HPS2FPGAREGS 0x90 /* HPS2FPGA AXI Bridge Perip. Sec */
+#define L3REGS_ACP 0x94 /* MPU ACP Peripheral Security */
+#define L3REGS_ROM 0x98 /* ROM Peripheral Security */
+#define L3REGS_OCRAM 0x9C /* On-chip RAM Peripheral Security */
+#define L3REGS_SDRDATA 0xA0 /* SDRAM Data Peripheral Security */
diff --git a/sys/arm/altera/socfpga/socfpga_machdep.c b/sys/arm/altera/socfpga/socfpga_machdep.c
new file mode 100644
index 000000000000..4a0d9ecca294
--- /dev/null
+++ b/sys/arm/altera/socfpga/socfpga_machdep.c
@@ -0,0 +1,176 @@
+/*-
+ * Copyright (c) 2014-2017 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "opt_platform.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/devmap.h>
+
+#include <vm/vm.h>
+
+#include <dev/ofw/openfirm.h>
+
+#include <machine/armreg.h>
+#include <machine/bus.h>
+#include <machine/fdt.h>
+#include <machine/machdep.h>
+#include <machine/platform.h>
+#include <machine/platformvar.h>
+
+#include <arm/altera/socfpga/socfpga_mp.h>
+#include <arm/altera/socfpga/socfpga_rstmgr.h>
+
+#include "platform_if.h"
+
+#if defined(SOC_ALTERA_CYCLONE5)
+static int
+socfpga_devmap_init(platform_t plat)
+{
+
+ /* UART */
+ devmap_add_entry(0xffc00000, 0x100000);
+
+ /*
+ * USB OTG
+ *
+ * We use static device map for USB due to some bug in the Altera
+ * which throws Translation Fault (P) exception on high load.
+ * It might be caused due to some power save options being turned
+ * on or something else.
+ */
+ devmap_add_entry(0xffb00000, 0x100000);
+
+ /* dwmmc */
+ devmap_add_entry(0xff700000, 0x100000);
+
+ /* scu */
+ devmap_add_entry(0xfff00000, 0x100000);
+
+ /* FPGA memory window, 256MB */
+ devmap_add_entry(0xd0000000, 0x10000000);
+
+ return (0);
+}
+#endif
+
+#if defined(SOC_ALTERA_ARRIA10)
+static int
+socfpga_a10_devmap_init(platform_t plat)
+{
+
+ /* UART */
+ devmap_add_entry(0xffc00000, 0x100000);
+
+ /* USB OTG */
+ devmap_add_entry(0xffb00000, 0x100000);
+
+ /* dwmmc */
+ devmap_add_entry(0xff800000, 0x100000);
+
+ /* scu */
+ devmap_add_entry(0xfff00000, 0x100000);
+
+ return (0);
+}
+#endif
+
+static void
+_socfpga_cpu_reset(bus_size_t reg)
+{
+ uint32_t paddr;
+ bus_addr_t vaddr;
+ phandle_t node;
+
+ if (rstmgr_warmreset(reg) == 0)
+ goto end;
+
+ node = OF_finddevice("/soc/rstmgr");
+ if (node == -1)
+ goto end;
+
+ if ((OF_getencprop(node, "reg", &paddr, sizeof(paddr))) > 0) {
+ if (bus_space_map(fdtbus_bs_tag, paddr, 0x8, 0, &vaddr) == 0) {
+ bus_space_write_4(fdtbus_bs_tag, vaddr,
+ reg, CTRL_SWWARMRSTREQ);
+ }
+ }
+
+end:
+ while (1);
+}
+
+#if defined(SOC_ALTERA_CYCLONE5)
+static void
+socfpga_cpu_reset(platform_t plat)
+{
+
+ _socfpga_cpu_reset(RSTMGR_CTRL);
+}
+#endif
+
+#if defined(SOC_ALTERA_ARRIA10)
+static void
+socfpga_a10_cpu_reset(platform_t plat)
+{
+
+ _socfpga_cpu_reset(RSTMGR_A10_CTRL);
+}
+#endif
+
+#if defined(SOC_ALTERA_CYCLONE5)
+static platform_method_t socfpga_methods[] = {
+ PLATFORMMETHOD(platform_devmap_init, socfpga_devmap_init),
+ PLATFORMMETHOD(platform_cpu_reset, socfpga_cpu_reset),
+#ifdef SMP
+ PLATFORMMETHOD(platform_mp_setmaxid, socfpga_mp_setmaxid),
+ PLATFORMMETHOD(platform_mp_start_ap, socfpga_mp_start_ap),
+#endif
+ PLATFORMMETHOD_END,
+};
+FDT_PLATFORM_DEF(socfpga, "socfpga", 0, "altr,socfpga-cyclone5", 200);
+#endif
+
+#if defined(SOC_ALTERA_ARRIA10)
+static platform_method_t socfpga_a10_methods[] = {
+ PLATFORMMETHOD(platform_devmap_init, socfpga_a10_devmap_init),
+ PLATFORMMETHOD(platform_cpu_reset, socfpga_a10_cpu_reset),
+#ifdef SMP
+ PLATFORMMETHOD(platform_mp_setmaxid, socfpga_mp_setmaxid),
+ PLATFORMMETHOD(platform_mp_start_ap, socfpga_a10_mp_start_ap),
+#endif
+ PLATFORMMETHOD_END,
+};
+FDT_PLATFORM_DEF(socfpga_a10, "socfpga", 0, "altr,socfpga-arria10", 200);
+#endif
diff --git a/sys/arm/altera/socfpga/socfpga_manager.c b/sys/arm/altera/socfpga/socfpga_manager.c
new file mode 100644
index 000000000000..9334c5547894
--- /dev/null
+++ b/sys/arm/altera/socfpga/socfpga_manager.c
@@ -0,0 +1,431 @@
+/*-
+ * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Altera FPGA Manager.
+ * Chapter 4, Cyclone V Device Handbook (CV-5V2 2014.07.22)
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/conf.h>
+#include <sys/uio.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <arm/altera/socfpga/socfpga_common.h>
+
+/* FPGA Manager Module Registers */
+#define FPGAMGR_STAT 0x0 /* Status Register */
+#define STAT_MSEL_MASK 0x1f
+#define STAT_MSEL_SHIFT 3
+#define STAT_MODE_SHIFT 0
+#define STAT_MODE_MASK 0x7
+#define FPGAMGR_CTRL 0x4 /* Control Register */
+#define CTRL_AXICFGEN (1 << 8)
+#define CTRL_CDRATIO_MASK 0x3
+#define CTRL_CDRATIO_SHIFT 6
+#define CTRL_CFGWDTH_MASK 1
+#define CTRL_CFGWDTH_SHIFT 9
+#define CTRL_NCONFIGPULL (1 << 2)
+#define CTRL_NCE (1 << 1)
+#define CTRL_EN (1 << 0)
+#define FPGAMGR_DCLKCNT 0x8 /* DCLK Count Register */
+#define FPGAMGR_DCLKSTAT 0xC /* DCLK Status Register */
+#define FPGAMGR_GPO 0x10 /* General-Purpose Output Register */
+#define FPGAMGR_GPI 0x14 /* General-Purpose Input Register */
+#define FPGAMGR_MISCI 0x18 /* Miscellaneous Input Register */
+
+/* Configuration Monitor (MON) Registers */
+#define GPIO_INTEN 0x830 /* Interrupt Enable Register */
+#define GPIO_INTMASK 0x834 /* Interrupt Mask Register */
+#define GPIO_INTTYPE_LEVEL 0x838 /* Interrupt Level Register */
+#define GPIO_INT_POLARITY 0x83C /* Interrupt Polarity Register */
+#define GPIO_INTSTATUS 0x840 /* Interrupt Status Register */
+#define GPIO_RAW_INTSTATUS 0x844 /* Raw Interrupt Status Register */
+#define GPIO_PORTA_EOI 0x84C /* Clear Interrupt Register */
+#define PORTA_EOI_NS (1 << 0)
+#define GPIO_EXT_PORTA 0x850 /* External Port A Register */
+#define EXT_PORTA_CDP (1 << 10) /* Configuration done */
+#define GPIO_LS_SYNC 0x860 /* Synchronization Level Register */
+#define GPIO_VER_ID_CODE 0x86C /* GPIO Version Register */
+#define GPIO_CONFIG_REG2 0x870 /* Configuration Register 2 */
+#define GPIO_CONFIG_REG1 0x874 /* Configuration Register 1 */
+
+#define MSEL_PP16_FAST_NOAES_NODC 0x0
+#define MSEL_PP16_FAST_AES_NODC 0x1
+#define MSEL_PP16_FAST_AESOPT_DC 0x2
+#define MSEL_PP16_SLOW_NOAES_NODC 0x4
+#define MSEL_PP16_SLOW_AES_NODC 0x5
+#define MSEL_PP16_SLOW_AESOPT_DC 0x6
+#define MSEL_PP32_FAST_NOAES_NODC 0x8
+#define MSEL_PP32_FAST_AES_NODC 0x9
+#define MSEL_PP32_FAST_AESOPT_DC 0xa
+#define MSEL_PP32_SLOW_NOAES_NODC 0xc
+#define MSEL_PP32_SLOW_AES_NODC 0xd
+#define MSEL_PP32_SLOW_AESOPT_DC 0xe
+
+#define CFGWDTH_16 0
+#define CFGWDTH_32 1
+
+#define CDRATIO_1 0
+#define CDRATIO_2 1
+#define CDRATIO_4 2
+#define CDRATIO_8 3
+
+#define FPGAMGR_MODE_POWEROFF 0x0
+#define FPGAMGR_MODE_RESET 0x1
+#define FPGAMGR_MODE_CONFIG 0x2
+#define FPGAMGR_MODE_INIT 0x3
+#define FPGAMGR_MODE_USER 0x4
+
+struct cfgmgr_mode {
+ int msel;
+ int cfgwdth;
+ int cdratio;
+};
+
+static struct cfgmgr_mode cfgmgr_modes[] = {
+ { MSEL_PP16_FAST_NOAES_NODC, CFGWDTH_16, CDRATIO_1 },
+ { MSEL_PP16_FAST_AES_NODC, CFGWDTH_16, CDRATIO_2 },
+ { MSEL_PP16_FAST_AESOPT_DC, CFGWDTH_16, CDRATIO_4 },
+ { MSEL_PP16_SLOW_NOAES_NODC, CFGWDTH_16, CDRATIO_1 },
+ { MSEL_PP16_SLOW_AES_NODC, CFGWDTH_16, CDRATIO_2 },
+ { MSEL_PP16_SLOW_AESOPT_DC, CFGWDTH_16, CDRATIO_4 },
+ { MSEL_PP32_FAST_NOAES_NODC, CFGWDTH_32, CDRATIO_1 },
+ { MSEL_PP32_FAST_AES_NODC, CFGWDTH_32, CDRATIO_4 },
+ { MSEL_PP32_FAST_AESOPT_DC, CFGWDTH_32, CDRATIO_8 },
+ { MSEL_PP32_SLOW_NOAES_NODC, CFGWDTH_32, CDRATIO_1 },
+ { MSEL_PP32_SLOW_AES_NODC, CFGWDTH_32, CDRATIO_4 },
+ { MSEL_PP32_SLOW_AESOPT_DC, CFGWDTH_32, CDRATIO_8 },
+ { -1, -1, -1 },
+};
+
+struct fpgamgr_softc {
+ struct resource *res[3];
+ bus_space_tag_t bst_data;
+ bus_space_handle_t bsh_data;
+ struct cdev *mgr_cdev;
+ device_t dev;
+};
+
+static struct resource_spec fpgamgr_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_MEMORY, 1, RF_ACTIVE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+static int
+fpgamgr_state_get(struct fpgamgr_softc *sc)
+{
+ int reg;
+
+ reg = READ4(sc, FPGAMGR_STAT);
+ reg >>= STAT_MODE_SHIFT;
+ reg &= STAT_MODE_MASK;
+
+ return reg;
+}
+
+static int
+fpgamgr_state_wait(struct fpgamgr_softc *sc, int state)
+{
+ int tout;
+
+ tout = 1000;
+ while (tout > 0) {
+ if (fpgamgr_state_get(sc) == state)
+ break;
+ tout--;
+ DELAY(10);
+ }
+ if (tout == 0) {
+ return (1);
+ }
+
+ return (0);
+}
+
+static int
+fpga_open(struct cdev *dev, int flags __unused,
+ int fmt __unused, struct thread *td __unused)
+{
+ struct fpgamgr_softc *sc;
+ struct cfgmgr_mode *mode;
+ int msel;
+ int reg;
+ int i;
+
+ sc = dev->si_drv1;
+
+ msel = READ4(sc, FPGAMGR_STAT);
+ msel >>= STAT_MSEL_SHIFT;
+ msel &= STAT_MSEL_MASK;
+
+ mode = NULL;
+ for (i = 0; cfgmgr_modes[i].msel != -1; i++) {
+ if (msel == cfgmgr_modes[i].msel) {
+ mode = &cfgmgr_modes[i];
+ break;
+ }
+ }
+ if (mode == NULL) {
+ device_printf(sc->dev, "Can't configure: unknown mode\n");
+ return (ENXIO);
+ }
+
+ reg = READ4(sc, FPGAMGR_CTRL);
+ reg &= ~(CTRL_CDRATIO_MASK << CTRL_CDRATIO_SHIFT);
+ reg |= (mode->cdratio << CTRL_CDRATIO_SHIFT);
+ reg &= ~(CTRL_CFGWDTH_MASK << CTRL_CFGWDTH_SHIFT);
+ reg |= (mode->cfgwdth << CTRL_CFGWDTH_SHIFT);
+ reg &= ~(CTRL_NCE);
+ WRITE4(sc, FPGAMGR_CTRL, reg);
+
+ /* Enable configuration */
+ reg = READ4(sc, FPGAMGR_CTRL);
+ reg |= (CTRL_EN);
+ WRITE4(sc, FPGAMGR_CTRL, reg);
+
+ /* Reset FPGA */
+ reg = READ4(sc, FPGAMGR_CTRL);
+ reg |= (CTRL_NCONFIGPULL);
+ WRITE4(sc, FPGAMGR_CTRL, reg);
+
+ /* Wait reset state */
+ if (fpgamgr_state_wait(sc, FPGAMGR_MODE_RESET)) {
+ device_printf(sc->dev, "Can't get RESET state\n");
+ return (ENXIO);
+ }
+
+ /* Release from reset */
+ reg = READ4(sc, FPGAMGR_CTRL);
+ reg &= ~(CTRL_NCONFIGPULL);
+ WRITE4(sc, FPGAMGR_CTRL, reg);
+
+ if (fpgamgr_state_wait(sc, FPGAMGR_MODE_CONFIG)) {
+ device_printf(sc->dev, "Can't get CONFIG state\n");
+ return (ENXIO);
+ }
+
+ /* Clear nSTATUS edge interrupt */
+ WRITE4(sc, GPIO_PORTA_EOI, PORTA_EOI_NS);
+
+ /* Enter configuration state */
+ reg = READ4(sc, FPGAMGR_CTRL);
+ reg |= (CTRL_AXICFGEN);
+ WRITE4(sc, FPGAMGR_CTRL, reg);
+
+ return (0);
+}
+
+static int
+fpga_wait_dclk_pulses(struct fpgamgr_softc *sc, int npulses)
+{
+ int tout;
+
+ /* Clear done bit, if any */
+ if (READ4(sc, FPGAMGR_DCLKSTAT) != 0)
+ WRITE4(sc, FPGAMGR_DCLKSTAT, 0x1);
+
+ /* Request DCLK pulses */
+ WRITE4(sc, FPGAMGR_DCLKCNT, npulses);
+
+ /* Wait finish */
+ tout = 1000;
+ while (tout > 0) {
+ if (READ4(sc, FPGAMGR_DCLKSTAT) == 1) {
+ WRITE4(sc, FPGAMGR_DCLKSTAT, 0x1);
+ break;
+ }
+ tout--;
+ DELAY(10);
+ }
+ if (tout == 0) {
+ return (1);
+ }
+
+ return (0);
+}
+
+static int
+fpga_close(struct cdev *dev, int flags __unused,
+ int fmt __unused, struct thread *td __unused)
+{
+ struct fpgamgr_softc *sc;
+ int reg;
+
+ sc = dev->si_drv1;
+
+ reg = READ4(sc, GPIO_EXT_PORTA);
+ if ((reg & EXT_PORTA_CDP) == 0) {
+ device_printf(sc->dev, "Err: configuration failed\n");
+ return (ENXIO);
+ }
+
+ /* Exit configuration state */
+ reg = READ4(sc, FPGAMGR_CTRL);
+ reg &= ~(CTRL_AXICFGEN);
+ WRITE4(sc, FPGAMGR_CTRL, reg);
+
+ /* Wait dclk pulses */
+ if (fpga_wait_dclk_pulses(sc, 4)) {
+ device_printf(sc->dev, "Can't proceed 4 dclk pulses\n");
+ return (ENXIO);
+ }
+
+ if (fpgamgr_state_wait(sc, FPGAMGR_MODE_USER)) {
+ device_printf(sc->dev, "Can't get USER mode\n");
+ return (ENXIO);
+ }
+
+ /* Disable configuration */
+ reg = READ4(sc, FPGAMGR_CTRL);
+ reg &= ~(CTRL_EN);
+ WRITE4(sc, FPGAMGR_CTRL, reg);
+
+ return (0);
+}
+
+static int
+fpga_write(struct cdev *dev, struct uio *uio, int ioflag)
+{
+ struct fpgamgr_softc *sc;
+ int buffer;
+
+ sc = dev->si_drv1;
+
+ /*
+ * Device supports 4-byte copy only.
+ * TODO: add padding for <4 bytes.
+ */
+
+ while (uio->uio_resid > 0) {
+ uiomove(&buffer, 4, uio);
+ bus_space_write_4(sc->bst_data, sc->bsh_data,
+ 0x0, buffer);
+ }
+
+ return (0);
+}
+
+static int
+fpga_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags,
+ struct thread *td)
+{
+
+ return (0);
+}
+
+static struct cdevsw fpga_cdevsw = {
+ .d_version = D_VERSION,
+ .d_open = fpga_open,
+ .d_close = fpga_close,
+ .d_write = fpga_write,
+ .d_ioctl = fpga_ioctl,
+ .d_name = "FPGA Manager",
+};
+
+static int
+fpgamgr_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "altr,socfpga-fpga-mgr"))
+ return (ENXIO);
+
+ device_set_desc(dev, "FPGA Manager");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+fpgamgr_attach(device_t dev)
+{
+ struct fpgamgr_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ if (bus_alloc_resources(dev, fpgamgr_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ /* Memory interface */
+ sc->bst_data = rman_get_bustag(sc->res[1]);
+ sc->bsh_data = rman_get_bushandle(sc->res[1]);
+
+ sc->mgr_cdev = make_dev(&fpga_cdevsw, 0, UID_ROOT, GID_WHEEL,
+ 0600, "fpga%d", device_get_unit(sc->dev));
+
+ if (sc->mgr_cdev == NULL) {
+ device_printf(dev, "Failed to create character device.\n");
+ return (ENXIO);
+ }
+
+ sc->mgr_cdev->si_drv1 = sc;
+
+ return (0);
+}
+
+static device_method_t fpgamgr_methods[] = {
+ DEVMETHOD(device_probe, fpgamgr_probe),
+ DEVMETHOD(device_attach, fpgamgr_attach),
+ { 0, 0 }
+};
+
+static driver_t fpgamgr_driver = {
+ "fpgamgr",
+ fpgamgr_methods,
+ sizeof(struct fpgamgr_softc),
+};
+
+static devclass_t fpgamgr_devclass;
+
+DRIVER_MODULE(fpgamgr, simplebus, fpgamgr_driver, fpgamgr_devclass, 0, 0);
diff --git a/sys/arm/altera/socfpga/socfpga_mp.c b/sys/arm/altera/socfpga/socfpga_mp.c
new file mode 100644
index 000000000000..2b3a6dfe61c7
--- /dev/null
+++ b/sys/arm/altera/socfpga/socfpga_mp.c
@@ -0,0 +1,232 @@
+/*-
+ * Copyright (c) 2014-2017 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "opt_platform.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/smp.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/cpu.h>
+#include <machine/smp.h>
+#include <machine/fdt.h>
+#include <machine/intr.h>
+#include <machine/platformvar.h>
+
+#include <arm/altera/socfpga/socfpga_mp.h>
+#include <arm/altera/socfpga/socfpga_rstmgr.h>
+
+#define SCU_PHYSBASE 0xFFFEC000
+#define SCU_PHYSBASE_A10 0xFFFFC000
+#define SCU_SIZE 0x100
+
+#define SCU_CONTROL_REG 0x00
+#define SCU_CONTROL_ENABLE (1 << 0)
+#define SCU_CONFIG_REG 0x04
+#define SCU_CONFIG_REG_NCPU_MASK 0x03
+#define SCU_CPUPOWER_REG 0x08
+#define SCU_INV_TAGS_REG 0x0c
+#define SCU_DIAG_CONTROL 0x30
+#define SCU_DIAG_DISABLE_MIGBIT (1 << 0)
+#define SCU_FILTER_START_REG 0x40
+#define SCU_FILTER_END_REG 0x44
+#define SCU_SECURE_ACCESS_REG 0x50
+#define SCU_NONSECURE_ACCESS_REG 0x54
+
+#define RSTMGR_PHYSBASE 0xFFD05000
+#define RSTMGR_SIZE 0x100
+
+#define RAM_PHYSBASE 0x0
+#define RAM_SIZE 0x1000
+
+#define SOCFPGA_ARRIA10 1
+#define SOCFPGA_CYCLONE5 2
+
+extern char *mpentry_addr;
+static void socfpga_trampoline(void);
+
+static void
+socfpga_trampoline(void)
+{
+
+ __asm __volatile(
+ "ldr pc, 1f\n"
+ ".globl mpentry_addr\n"
+ "mpentry_addr:\n"
+ "1: .space 4\n");
+}
+
+void
+socfpga_mp_setmaxid(platform_t plat)
+{
+ int hwcpu, ncpu;
+
+ /* If we've already set this don't bother to do it again. */
+ if (mp_ncpus != 0)
+ return;
+
+ hwcpu = 2;
+
+ ncpu = hwcpu;
+ TUNABLE_INT_FETCH("hw.ncpu", &ncpu);
+ if (ncpu < 1 || ncpu > hwcpu)
+ ncpu = hwcpu;
+
+ mp_ncpus = ncpu;
+ mp_maxid = ncpu - 1;
+}
+
+static void
+_socfpga_mp_start_ap(uint32_t platid)
+{
+ bus_space_handle_t scu, rst, ram;
+ int reg;
+
+ switch (platid) {
+#if defined(SOC_ALTERA_ARRIA10)
+ case SOCFPGA_ARRIA10:
+ if (bus_space_map(fdtbus_bs_tag, SCU_PHYSBASE_A10,
+ SCU_SIZE, 0, &scu) != 0)
+ panic("Couldn't map the SCU\n");
+ break;
+#endif
+#if defined(SOC_ALTERA_CYCLONE5)
+ case SOCFPGA_CYCLONE5:
+ if (bus_space_map(fdtbus_bs_tag, SCU_PHYSBASE,
+ SCU_SIZE, 0, &scu) != 0)
+ panic("Couldn't map the SCU\n");
+ break;
+#endif
+ default:
+ panic("Unknown platform id %d\n", platid);
+ }
+
+ if (bus_space_map(fdtbus_bs_tag, RSTMGR_PHYSBASE,
+ RSTMGR_SIZE, 0, &rst) != 0)
+ panic("Couldn't map the reset manager (RSTMGR)\n");
+ if (bus_space_map(fdtbus_bs_tag, RAM_PHYSBASE,
+ RAM_SIZE, 0, &ram) != 0)
+ panic("Couldn't map the first physram page\n");
+
+ /* Invalidate SCU cache tags */
+ bus_space_write_4(fdtbus_bs_tag, scu,
+ SCU_INV_TAGS_REG, 0x0000ffff);
+
+ /*
+ * Erratum ARM/MP: 764369 (problems with cache maintenance).
+ * Setting the "disable-migratory bit" in the undocumented SCU
+ * Diagnostic Control Register helps work around the problem.
+ */
+ reg = bus_space_read_4(fdtbus_bs_tag, scu, SCU_DIAG_CONTROL);
+ reg |= (SCU_DIAG_DISABLE_MIGBIT);
+ bus_space_write_4(fdtbus_bs_tag, scu, SCU_DIAG_CONTROL, reg);
+
+ /* Put CPU1 to reset state */
+ switch (platid) {
+#if defined(SOC_ALTERA_ARRIA10)
+ case SOCFPGA_ARRIA10:
+ bus_space_write_4(fdtbus_bs_tag, rst,
+ RSTMGR_A10_MPUMODRST, MPUMODRST_CPU1);
+ break;
+#endif
+#if defined(SOC_ALTERA_CYCLONE5)
+ case SOCFPGA_CYCLONE5:
+ bus_space_write_4(fdtbus_bs_tag, rst,
+ RSTMGR_MPUMODRST, MPUMODRST_CPU1);
+ break;
+#endif
+ default:
+ panic("Unknown platform id %d\n", platid);
+ }
+
+ /* Enable the SCU, then clean the cache on this core */
+ reg = bus_space_read_4(fdtbus_bs_tag, scu, SCU_CONTROL_REG);
+ reg |= (SCU_CONTROL_ENABLE);
+ bus_space_write_4(fdtbus_bs_tag, scu, SCU_CONTROL_REG, reg);
+
+ /* Set up trampoline code */
+ mpentry_addr = (char *)pmap_kextract((vm_offset_t)mpentry);
+ bus_space_write_region_4(fdtbus_bs_tag, ram, 0,
+ (uint32_t *)&socfpga_trampoline, 8);
+
+ dcache_wbinv_poc_all();
+
+ /* Put CPU1 out from reset */
+ switch (platid) {
+#if defined(SOC_ALTERA_ARRIA10)
+ case SOCFPGA_ARRIA10:
+ bus_space_write_4(fdtbus_bs_tag, rst,
+ RSTMGR_A10_MPUMODRST, 0);
+ break;
+#endif
+#if defined(SOC_ALTERA_CYCLONE5)
+ case SOCFPGA_CYCLONE5:
+ bus_space_write_4(fdtbus_bs_tag, rst,
+ RSTMGR_MPUMODRST, 0);
+ break;
+#endif
+ default:
+ panic("Unknown platform id %d\n", platid);
+ }
+
+ dsb();
+ sev();
+
+ bus_space_unmap(fdtbus_bs_tag, scu, SCU_SIZE);
+ bus_space_unmap(fdtbus_bs_tag, rst, RSTMGR_SIZE);
+ bus_space_unmap(fdtbus_bs_tag, ram, RAM_SIZE);
+}
+
+#if defined(SOC_ALTERA_ARRIA10)
+void
+socfpga_a10_mp_start_ap(platform_t plat)
+{
+
+ _socfpga_mp_start_ap(SOCFPGA_ARRIA10);
+}
+#endif
+
+#if defined(SOC_ALTERA_CYCLONE5)
+void
+socfpga_mp_start_ap(platform_t plat)
+{
+
+ _socfpga_mp_start_ap(SOCFPGA_CYCLONE5);
+}
+#endif
diff --git a/sys/arm/altera/socfpga/socfpga_mp.h b/sys/arm/altera/socfpga/socfpga_mp.h
new file mode 100644
index 000000000000..bf3014d4983d
--- /dev/null
+++ b/sys/arm/altera/socfpga/socfpga_mp.h
@@ -0,0 +1,35 @@
+/*-
+ * Copyright (c) 2017 Andrew Turner <andrew@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _SOCFPGA_MP_H_
+#define _SOCFPGA_MP_H_
+
+void socfpga_mp_setmaxid(platform_t);
+void socfpga_mp_start_ap(platform_t);
+void socfpga_a10_mp_start_ap(platform_t);
+
+#endif /* _SOCFPGA_MP_H_ */
diff --git a/sys/arm/altera/socfpga/socfpga_rstmgr.c b/sys/arm/altera/socfpga/socfpga_rstmgr.c
new file mode 100644
index 000000000000..0416de6691eb
--- /dev/null
+++ b/sys/arm/altera/socfpga/socfpga_rstmgr.c
@@ -0,0 +1,260 @@
+/*-
+ * Copyright (c) 2014-2017 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * SOCFPGA Reset Manager.
+ * Chapter 3, Cyclone V Device Handbook (CV-5V2 2014.07.22)
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/sysctl.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <arm/altera/socfpga/socfpga_common.h>
+#include <arm/altera/socfpga/socfpga_rstmgr.h>
+#include <arm/altera/socfpga/socfpga_l3regs.h>
+
+struct rstmgr_softc {
+ struct resource *res[1];
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+ device_t dev;
+};
+
+struct rstmgr_softc *rstmgr_sc;
+
+static struct resource_spec rstmgr_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+enum {
+ RSTMGR_SYSCTL_FPGA2HPS,
+ RSTMGR_SYSCTL_LWHPS2FPGA,
+ RSTMGR_SYSCTL_HPS2FPGA
+};
+
+static int
+l3remap(struct rstmgr_softc *sc, int remap, int enable)
+{
+ uint32_t paddr;
+ bus_addr_t vaddr;
+ phandle_t node;
+ int reg;
+
+ /*
+ * Control whether bridge is visible to L3 masters or not.
+ * Register is write-only.
+ */
+
+ reg = REMAP_MPUZERO;
+ if (enable)
+ reg |= (remap);
+ else
+ reg &= ~(remap);
+
+ node = OF_finddevice("l3regs");
+ if (node == -1) {
+ device_printf(sc->dev, "Can't find l3regs node\n");
+ return (1);
+ }
+
+ if ((OF_getencprop(node, "reg", &paddr, sizeof(paddr))) > 0) {
+ if (bus_space_map(fdtbus_bs_tag, paddr, 0x4, 0, &vaddr) == 0) {
+ bus_space_write_4(fdtbus_bs_tag, vaddr,
+ L3REGS_REMAP, reg);
+ return (0);
+ }
+ }
+
+ return (1);
+}
+
+static int
+rstmgr_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ struct rstmgr_softc *sc;
+ int enable;
+ int remap;
+ int err;
+ int reg;
+ int bit;
+
+ sc = arg1;
+
+ switch (arg2) {
+ case RSTMGR_SYSCTL_FPGA2HPS:
+ bit = BRGMODRST_FPGA2HPS;
+ remap = 0;
+ break;
+ case RSTMGR_SYSCTL_LWHPS2FPGA:
+ bit = BRGMODRST_LWHPS2FPGA;
+ remap = REMAP_LWHPS2FPGA;
+ break;
+ case RSTMGR_SYSCTL_HPS2FPGA:
+ bit = BRGMODRST_HPS2FPGA;
+ remap = REMAP_HPS2FPGA;
+ break;
+ default:
+ return (1);
+ }
+
+ reg = READ4(sc, RSTMGR_BRGMODRST);
+ enable = reg & bit ? 0 : 1;
+
+ err = sysctl_handle_int(oidp, &enable, 0, req);
+ if (err || !req->newptr)
+ return (err);
+
+ if (enable == 1)
+ reg &= ~(bit);
+ else if (enable == 0)
+ reg |= (bit);
+ else
+ return (EINVAL);
+
+ WRITE4(sc, RSTMGR_BRGMODRST, reg);
+ l3remap(sc, remap, enable);
+
+ return (0);
+}
+
+int
+rstmgr_warmreset(uint32_t reg)
+{
+ struct rstmgr_softc *sc;
+
+ sc = rstmgr_sc;
+ if (sc == NULL)
+ return (1);
+
+ /* Request warm reset */
+ WRITE4(sc, reg, CTRL_SWWARMRSTREQ);
+
+ return (0);
+}
+
+static int
+rstmgr_add_sysctl(struct rstmgr_softc *sc)
+{
+ struct sysctl_oid_list *children;
+ struct sysctl_ctx_list *ctx;
+
+ ctx = device_get_sysctl_ctx(sc->dev);
+ children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev));
+
+ SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "fpga2hps",
+ CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
+ sc, RSTMGR_SYSCTL_FPGA2HPS,
+ rstmgr_sysctl, "I", "Enable fpga2hps bridge");
+ SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "lwhps2fpga",
+ CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
+ sc, RSTMGR_SYSCTL_LWHPS2FPGA,
+ rstmgr_sysctl, "I", "Enable lwhps2fpga bridge");
+ SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "hps2fpga",
+ CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
+ sc, RSTMGR_SYSCTL_HPS2FPGA,
+ rstmgr_sysctl, "I", "Enable hps2fpga bridge");
+
+ return (0);
+}
+
+static int
+rstmgr_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "altr,rst-mgr"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Reset Manager");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+rstmgr_attach(device_t dev)
+{
+ struct rstmgr_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ if (bus_alloc_resources(dev, rstmgr_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ /* Memory interface */
+ sc->bst = rman_get_bustag(sc->res[0]);
+ sc->bsh = rman_get_bushandle(sc->res[0]);
+
+ rstmgr_sc = sc;
+ rstmgr_add_sysctl(sc);
+
+ return (0);
+}
+
+static device_method_t rstmgr_methods[] = {
+ DEVMETHOD(device_probe, rstmgr_probe),
+ DEVMETHOD(device_attach, rstmgr_attach),
+ { 0, 0 }
+};
+
+static driver_t rstmgr_driver = {
+ "rstmgr",
+ rstmgr_methods,
+ sizeof(struct rstmgr_softc),
+};
+
+static devclass_t rstmgr_devclass;
+
+DRIVER_MODULE(rstmgr, simplebus, rstmgr_driver, rstmgr_devclass, 0, 0);
diff --git a/sys/arm/altera/socfpga/socfpga_rstmgr.h b/sys/arm/altera/socfpga/socfpga_rstmgr.h
new file mode 100644
index 000000000000..1980bcf393f6
--- /dev/null
+++ b/sys/arm/altera/socfpga/socfpga_rstmgr.h
@@ -0,0 +1,50 @@
+/*-
+ * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define RSTMGR_STAT 0x0 /* Status */
+#define RSTMGR_CTRL 0x4 /* Control */
+#define CTRL_SWWARMRSTREQ (1 << 1) /* Trigger warm reset */
+#define RSTMGR_COUNTS 0x8 /* Reset Cycles Count */
+#define RSTMGR_MPUMODRST 0x10 /* MPU Module Reset */
+#define MPUMODRST_CPU1 (1 << 1)
+#define RSTMGR_PERMODRST 0x14 /* Peripheral Module Reset */
+#define RSTMGR_PER2MODRST 0x18 /* Peripheral 2 Module Reset */
+#define RSTMGR_BRGMODRST 0x1C /* Bridge Module Reset */
+#define BRGMODRST_FPGA2HPS (1 << 2)
+#define BRGMODRST_LWHPS2FPGA (1 << 1)
+#define BRGMODRST_HPS2FPGA (1 << 0)
+#define RSTMGR_MISCMODRST 0x20 /* Miscellaneous Module Reset */
+
+#define RSTMGR_A10_CTRL 0xC /* Control */
+#define RSTMGR_A10_MPUMODRST 0x20 /* MPU Module Reset */
+
+int rstmgr_warmreset(uint32_t reg);
diff --git a/sys/arm/altera/socfpga/std.socfpga b/sys/arm/altera/socfpga/std.socfpga
new file mode 100644
index 000000000000..dc5cf704640b
--- /dev/null
+++ b/sys/arm/altera/socfpga/std.socfpga
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+cpu CPU_CORTEXA
+machine arm armv7
+makeoptions CONF_CFLAGS="-march=armv7a"
+
+files "../altera/socfpga/files.socfpga"
diff --git a/sys/arm/annapurna/alpine/alpine_ccu.c b/sys/arm/annapurna/alpine/alpine_ccu.c
new file mode 100644
index 000000000000..1e99cb5bf67e
--- /dev/null
+++ b/sys/arm/annapurna/alpine/alpine_ccu.c
@@ -0,0 +1,130 @@
+/*-
+ * Copyright (c) 2015,2016 Annapurna Labs Ltd. and affiliates
+ * All rights reserved.
+ *
+ * Developed by Semihalf.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/rman.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/resource.h>
+#include <sys/systm.h>
+
+#include <machine/bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#define AL_CCU_SNOOP_CONTROL_IOFAB_0_OFFSET 0x4000
+#define AL_CCU_SNOOP_CONTROL_IOFAB_1_OFFSET 0x5000
+#define AL_CCU_SPECULATION_CONTROL_OFFSET 0x4
+
+static struct resource_spec al_ccu_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+struct al_ccu_softc {
+ struct resource *res;
+};
+
+static int al_ccu_probe(device_t dev);
+static int al_ccu_attach(device_t dev);
+static int al_ccu_detach(device_t dev);
+
+static device_method_t al_ccu_methods[] = {
+ DEVMETHOD(device_probe, al_ccu_probe),
+ DEVMETHOD(device_attach, al_ccu_attach),
+ DEVMETHOD(device_detach, al_ccu_detach),
+ { 0, 0 }
+};
+
+static driver_t al_ccu_driver = {
+ "ccu",
+ al_ccu_methods,
+ sizeof(struct al_ccu_softc)
+};
+
+static devclass_t al_ccu_devclass;
+
+EARLY_DRIVER_MODULE(al_ccu, simplebus, al_ccu_driver,
+ al_ccu_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_MIDDLE);
+EARLY_DRIVER_MODULE(al_ccu, ofwbus, al_ccu_driver,
+ al_ccu_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_MIDDLE);
+
+static int
+al_ccu_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "annapurna-labs,al-ccu"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Alpine CCU");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+al_ccu_attach(device_t dev)
+{
+ struct al_ccu_softc *sc;
+ int err;
+
+ sc = device_get_softc(dev);
+
+ err = bus_alloc_resources(dev, al_ccu_spec, &sc->res);
+ if (err != 0) {
+ device_printf(dev, "could not allocate resources\n");
+ return (err);
+ }
+
+ /* Enable cache snoop */
+ bus_write_4(sc->res, AL_CCU_SNOOP_CONTROL_IOFAB_0_OFFSET, 1);
+ bus_write_4(sc->res, AL_CCU_SNOOP_CONTROL_IOFAB_1_OFFSET, 1);
+
+ /* Disable speculative fetches from masters */
+ bus_write_4(sc->res, AL_CCU_SPECULATION_CONTROL_OFFSET, 7);
+
+ return (0);
+}
+
+static int
+al_ccu_detach(device_t dev)
+{
+ struct al_ccu_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ bus_release_resources(dev, al_ccu_spec, &sc->res);
+
+ return (0);
+}
diff --git a/sys/arm/annapurna/alpine/alpine_machdep.c b/sys/arm/annapurna/alpine/alpine_machdep.c
new file mode 100644
index 000000000000..1b31da41d2ca
--- /dev/null
+++ b/sys/arm/annapurna/alpine/alpine_machdep.c
@@ -0,0 +1,162 @@
+/*-
+ * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com>
+ * Copyright (c) 2015 Semihalf
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "opt_ddb.h"
+#include "opt_platform.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/devmap.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+#include <machine/platformvar.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/openfirm.h>
+
+#include <arm/annapurna/alpine/alpine_mp.h>
+
+#include "platform_if.h"
+
+#define WDTLOAD 0x000
+#define LOAD_MIN 0x00000001
+#define LOAD_MAX 0xFFFFFFFF
+#define WDTVALUE 0x004
+#define WDTCONTROL 0x008
+/* control register masks */
+#define INT_ENABLE (1 << 0)
+#define RESET_ENABLE (1 << 1)
+#define WDTLOCK 0xC00
+#define UNLOCK 0x1ACCE551
+#define LOCK 0x00000001
+
+bus_addr_t al_devmap_pa;
+bus_addr_t al_devmap_size;
+
+static int
+alpine_get_devmap_base(bus_addr_t *pa, bus_addr_t *size)
+{
+ phandle_t node;
+
+ if ((node = OF_finddevice("/")) == -1)
+ return (ENXIO);
+
+ if ((node = fdt_find_compatible(node, "simple-bus", 1)) == 0)
+ return (ENXIO);
+
+ return fdt_get_range(node, 0, pa, size);
+}
+
+static int
+alpine_get_wdt_base(uint32_t *pbase, uint32_t *psize)
+{
+ phandle_t node;
+ u_long base = 0;
+ u_long size = 0;
+
+ if (pbase == NULL || psize == NULL)
+ return (EINVAL);
+
+ if ((node = OF_finddevice("/")) == -1)
+ return (EFAULT);
+
+ if ((node = fdt_find_compatible(node, "simple-bus", 1)) == 0)
+ return (EFAULT);
+
+ if ((node =
+ fdt_find_compatible(node, "arm,sp805", 1)) == 0)
+ return (EFAULT);
+
+ if (fdt_regsize(node, &base, &size))
+ return (EFAULT);
+
+ *pbase = base;
+ *psize = size;
+
+ return (0);
+}
+
+/*
+ * Construct devmap table with DT-derived config data.
+ */
+static int
+alpine_devmap_init(platform_t plat)
+{
+ alpine_get_devmap_base(&al_devmap_pa, &al_devmap_size);
+ devmap_add_entry(al_devmap_pa, al_devmap_size);
+ return (0);
+}
+
+static void
+alpine_cpu_reset(platform_t plat)
+{
+ uint32_t wdbase, wdsize;
+ bus_addr_t wdbaddr;
+ int ret;
+
+ ret = alpine_get_wdt_base(&wdbase, &wdsize);
+ if (ret) {
+ printf("Unable to get WDT base, do power down manually...");
+ goto infinite;
+ }
+
+ ret = bus_space_map(fdtbus_bs_tag, al_devmap_pa + wdbase,
+ wdsize, 0, &wdbaddr);
+ if (ret) {
+ printf("Unable to map WDT base, do power down manually...");
+ goto infinite;
+ }
+
+ bus_space_write_4(fdtbus_bs_tag, wdbaddr, WDTLOCK, UNLOCK);
+ bus_space_write_4(fdtbus_bs_tag, wdbaddr, WDTLOAD, LOAD_MIN);
+ bus_space_write_4(fdtbus_bs_tag, wdbaddr, WDTCONTROL,
+ INT_ENABLE | RESET_ENABLE);
+
+infinite:
+ while (1) {}
+}
+
+static platform_method_t alpine_methods[] = {
+ PLATFORMMETHOD(platform_devmap_init, alpine_devmap_init),
+ PLATFORMMETHOD(platform_cpu_reset, alpine_cpu_reset),
+
+#ifdef SMP
+ PLATFORMMETHOD(platform_mp_start_ap, alpine_mp_start_ap),
+ PLATFORMMETHOD(platform_mp_setmaxid, alpine_mp_setmaxid),
+#endif
+ PLATFORMMETHOD_END,
+};
+FDT_PLATFORM_DEF(alpine, "alpine", 0, "annapurna,alpine", 200);
diff --git a/sys/arm/annapurna/alpine/alpine_machdep_mp.c b/sys/arm/annapurna/alpine/alpine_machdep_mp.c
new file mode 100644
index 000000000000..7e6ea3479570
--- /dev/null
+++ b/sys/arm/annapurna/alpine/alpine_machdep_mp.c
@@ -0,0 +1,252 @@
+/*-
+ * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com>
+ * Copyright (c) 2015 Semihalf
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/smp.h>
+#include <sys/cpuset.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/smp.h>
+#include <machine/fdt.h>
+#include <machine/intr.h>
+#include <machine/cpu-v6.h>
+#include <machine/platformvar.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_cpu.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/annapurna/alpine/alpine_mp.h>
+
+#define AL_CPU_RESUME_WATERMARK_REG 0x00
+#define AL_CPU_RESUME_FLAGS_REG 0x04
+#define AL_CPU_RESUME_PCPU_RADDR_REG(cpu) (0x08 + 0x04 + 8*(cpu))
+#define AL_CPU_RESUME_PCPU_FLAGS(cpu) (0x08 + 8*(cpu))
+
+/* Per-CPU flags */
+#define AL_CPU_RESUME_FLG_PERCPU_DONT_RESUME (1 << 2)
+
+/* The expected magic number for validating the resume addresses */
+#define AL_CPU_RESUME_MAGIC_NUM 0xf0e1d200
+#define AL_CPU_RESUME_MAGIC_NUM_MASK 0xffffff00
+
+/* The expected minimal version number for validating the capabilities */
+#define AL_CPU_RESUME_MIN_VER 0x000000c3
+#define AL_CPU_RESUME_MIN_VER_MASK 0x000000ff
+
+/* Field controlling the boot-up of companion cores */
+#define AL_NB_INIT_CONTROL (0x8)
+#define AL_NB_CONFIG_STATUS_PWR_CTRL(cpu) (0x2020 + (cpu)*0x100)
+
+extern bus_addr_t al_devmap_pa;
+extern bus_addr_t al_devmap_size;
+
+extern void mpentry(void);
+
+static int platform_mp_get_core_cnt(void);
+static int alpine_get_cpu_resume_base(u_long *pbase, u_long *psize);
+static int alpine_get_nb_base(u_long *pbase, u_long *psize);
+static boolean_t alpine_validate_cpu(u_int, phandle_t, u_int, pcell_t *);
+
+static boolean_t
+alpine_validate_cpu(u_int id, phandle_t child, u_int addr_cell, pcell_t *reg)
+{
+ return ofw_bus_node_is_compatible(child, "arm,cortex-a15");
+}
+
+static int
+platform_mp_get_core_cnt(void)
+{
+ static int ncores = 0;
+ int nchilds;
+ uint32_t reg;
+
+ /* Calculate ncores value only once */
+ if (ncores)
+ return (ncores);
+
+ reg = cp15_l2ctlr_get();
+ ncores = CPUV7_L2CTLR_NPROC(reg);
+
+ nchilds = ofw_cpu_early_foreach(alpine_validate_cpu, false);
+
+ /* Limit CPUs if DTS has configured less than available */
+ if ((nchilds > 0) && (nchilds < ncores)) {
+ printf("SMP: limiting number of active CPUs to %d out of %d\n",
+ nchilds, ncores);
+ ncores = nchilds;
+ }
+
+ return (ncores);
+}
+
+void
+alpine_mp_setmaxid(platform_t plat)
+{
+
+ mp_ncpus = platform_mp_get_core_cnt();
+ mp_maxid = mp_ncpus - 1;
+}
+
+static int
+alpine_get_cpu_resume_base(u_long *pbase, u_long *psize)
+{
+ phandle_t node;
+ u_long base = 0;
+ u_long size = 0;
+
+ if (pbase == NULL || psize == NULL)
+ return (EINVAL);
+
+ if ((node = OF_finddevice("/")) == -1)
+ return (EFAULT);
+
+ if ((node =
+ ofw_bus_find_compatible(node, "annapurna-labs,al-cpu-resume")) == 0)
+ return (EFAULT);
+
+ if (fdt_regsize(node, &base, &size))
+ return (EFAULT);
+
+ *pbase = base;
+ *psize = size;
+
+ return (0);
+}
+
+static int
+alpine_get_nb_base(u_long *pbase, u_long *psize)
+{
+ phandle_t node;
+ u_long base = 0;
+ u_long size = 0;
+
+ if (pbase == NULL || psize == NULL)
+ return (EINVAL);
+
+ if ((node = OF_finddevice("/")) == -1)
+ return (EFAULT);
+
+ if ((node =
+ ofw_bus_find_compatible(node, "annapurna-labs,al-nb-service")) == 0)
+ return (EFAULT);
+
+ if (fdt_regsize(node, &base, &size))
+ return (EFAULT);
+
+ *pbase = base;
+ *psize = size;
+
+ return (0);
+}
+
+void
+alpine_mp_start_ap(platform_t plat)
+{
+ uint32_t physaddr;
+ vm_offset_t vaddr;
+ uint32_t val;
+ uint32_t start_mask;
+ u_long cpu_resume_base;
+ u_long nb_base;
+ u_long cpu_resume_size;
+ u_long nb_size;
+ bus_addr_t cpu_resume_baddr;
+ bus_addr_t nb_baddr;
+ int a;
+
+ if (alpine_get_cpu_resume_base(&cpu_resume_base, &cpu_resume_size))
+ panic("Couldn't resolve cpu_resume_base address\n");
+
+ if (alpine_get_nb_base(&nb_base, &nb_size))
+ panic("Couldn't resolve_nb_base address\n");
+
+ /* Proceed with start addresses for additional CPUs */
+ if (bus_space_map(fdtbus_bs_tag, al_devmap_pa + cpu_resume_base,
+ cpu_resume_size, 0, &cpu_resume_baddr))
+ panic("Couldn't map CPU-resume area");
+ if (bus_space_map(fdtbus_bs_tag, al_devmap_pa + nb_base,
+ nb_size, 0, &nb_baddr))
+ panic("Couldn't map NB-service area");
+
+ /* Proceed with start addresses for additional CPUs */
+ val = bus_space_read_4(fdtbus_bs_tag, cpu_resume_baddr,
+ AL_CPU_RESUME_WATERMARK_REG);
+ if (((val & AL_CPU_RESUME_MAGIC_NUM_MASK) != AL_CPU_RESUME_MAGIC_NUM) ||
+ ((val & AL_CPU_RESUME_MIN_VER_MASK) < AL_CPU_RESUME_MIN_VER)) {
+ panic("CPU-resume device is not compatible");
+ }
+
+ vaddr = (vm_offset_t)mpentry;
+ physaddr = pmap_kextract(vaddr);
+
+ for (a = 1; a < platform_mp_get_core_cnt(); a++) {
+ /* Power up the core */
+ bus_space_write_4(fdtbus_bs_tag, nb_baddr,
+ AL_NB_CONFIG_STATUS_PWR_CTRL(a), 0);
+ mb();
+
+ /* Enable resume */
+ val = bus_space_read_4(fdtbus_bs_tag, cpu_resume_baddr,
+ AL_CPU_RESUME_PCPU_FLAGS(a));
+ val &= ~AL_CPU_RESUME_FLG_PERCPU_DONT_RESUME;
+ bus_space_write_4(fdtbus_bs_tag, cpu_resume_baddr,
+ AL_CPU_RESUME_PCPU_FLAGS(a), val);
+ mb();
+
+ /* Set resume physical address */
+ bus_space_write_4(fdtbus_bs_tag, cpu_resume_baddr,
+ AL_CPU_RESUME_PCPU_RADDR_REG(a), physaddr);
+ mb();
+ }
+
+ /* Release cores from reset */
+ if (bus_space_map(fdtbus_bs_tag, al_devmap_pa + nb_base,
+ nb_size, 0, &nb_baddr))
+ panic("Couldn't map NB-service area");
+
+ start_mask = (1 << platform_mp_get_core_cnt()) - 1;
+
+ /* Release cores from reset */
+ val = bus_space_read_4(fdtbus_bs_tag, nb_baddr, AL_NB_INIT_CONTROL);
+ val |= start_mask;
+ bus_space_write_4(fdtbus_bs_tag, nb_baddr, AL_NB_INIT_CONTROL, val);
+ dsb();
+
+ bus_space_unmap(fdtbus_bs_tag, nb_baddr, nb_size);
+ bus_space_unmap(fdtbus_bs_tag, cpu_resume_baddr, cpu_resume_size);
+}
diff --git a/sys/arm/annapurna/alpine/alpine_mp.h b/sys/arm/annapurna/alpine/alpine_mp.h
new file mode 100644
index 000000000000..736b6cd36b89
--- /dev/null
+++ b/sys/arm/annapurna/alpine/alpine_mp.h
@@ -0,0 +1,35 @@
+/*-
+ * Copyright (c) 2017 Andrew Turner
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _ANNAPURNA_ALPINE_MP_H_
+#define _ANNAPURNA_ALPINE_MP_H_
+
+void alpine_mp_start_ap(platform_t plat);
+void alpine_mp_setmaxid(platform_t plat);
+
+#endif /* _ANNAPURNA_ALPINE_MP_H_ */
diff --git a/sys/arm/annapurna/alpine/alpine_nb_service.c b/sys/arm/annapurna/alpine/alpine_nb_service.c
new file mode 100644
index 000000000000..156d8e406df5
--- /dev/null
+++ b/sys/arm/annapurna/alpine/alpine_nb_service.c
@@ -0,0 +1,128 @@
+/*-
+ * Copyright (c) 2015,2016 Annapurna Labs Ltd. and affiliates
+ * All rights reserved.
+ *
+ * Developed by Semihalf.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/rman.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/resource.h>
+#include <sys/systm.h>
+
+#include <machine/bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#define AL_NB_ACF_MISC_OFFSET 0xD0
+#define AL_NB_ACF_MISC_READ_BYPASS (1 << 30)
+
+static struct resource_spec nb_service_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+struct nb_service_softc {
+ struct resource *res;
+};
+
+static int nb_service_probe(device_t dev);
+static int nb_service_attach(device_t dev);
+static int nb_service_detach(device_t dev);
+
+static device_method_t nb_service_methods[] = {
+ DEVMETHOD(device_probe, nb_service_probe),
+ DEVMETHOD(device_attach, nb_service_attach),
+ DEVMETHOD(device_detach, nb_service_detach),
+ { 0, 0 }
+};
+
+static driver_t nb_service_driver = {
+ "nb_service",
+ nb_service_methods,
+ sizeof(struct nb_service_softc)
+};
+
+static devclass_t nb_service_devclass;
+
+EARLY_DRIVER_MODULE(nb_service, simplebus, nb_service_driver,
+ nb_service_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_MIDDLE);
+EARLY_DRIVER_MODULE(nb_service, ofwbus, nb_service_driver,
+ nb_service_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_MIDDLE);
+
+static int
+nb_service_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "annapurna-labs,al-nb-service"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Alpine North Bridge Service");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+nb_service_attach(device_t dev)
+{
+ struct nb_service_softc *sc;
+ uint32_t val;
+ int err;
+
+ sc = device_get_softc(dev);
+
+ err = bus_alloc_resources(dev, nb_service_spec, &sc->res);
+ if (err != 0) {
+ device_printf(dev, "could not allocate resources\n");
+ return (err);
+ }
+
+ /* Do not allow reads to bypass writes to different addresses */
+ val = bus_read_4(sc->res, AL_NB_ACF_MISC_OFFSET);
+ val &= ~AL_NB_ACF_MISC_READ_BYPASS;
+ bus_write_4(sc->res, AL_NB_ACF_MISC_OFFSET, val);
+
+ return (0);
+}
+
+static int
+nb_service_detach(device_t dev)
+{
+ struct nb_service_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ bus_release_resources(dev, nb_service_spec, &sc->res);
+
+ return (0);
+}
diff --git a/sys/arm/annapurna/alpine/alpine_pci.c b/sys/arm/annapurna/alpine/alpine_pci.c
new file mode 100644
index 000000000000..043ae19e0cb3
--- /dev/null
+++ b/sys/arm/annapurna/alpine/alpine_pci.c
@@ -0,0 +1,159 @@
+/*-
+ * Copyright (c) 2015,2016 Annapurna Labs Ltd. and affiliates
+ * All rights reserved.
+ *
+ * Developed by Semihalf.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Alpine PCI/PCI-Express controller driver.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/intr.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/pci/pci_host_generic.h>
+#include <dev/pci/pci_host_generic_fdt.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+
+#include "pcib_if.h"
+
+#include "contrib/alpine-hal/al_hal_unit_adapter_regs.h"
+#include "contrib/alpine-hal/al_hal_pcie.h"
+#include "contrib/alpine-hal/al_hal_pcie_axi_reg.h"
+
+#define ANNAPURNA_VENDOR_ID 0x1c36
+
+/* Forward prototypes */
+static int al_pcib_probe(device_t);
+static int al_pcib_attach(device_t);
+static void al_pcib_fixup(device_t);
+
+static struct ofw_compat_data compat_data[] = {
+ {"annapurna-labs,al-internal-pcie", true},
+ {"annapurna-labs,alpine-internal-pcie", true},
+ {NULL, false}
+};
+
+/*
+ * Bus interface definitions.
+ */
+static device_method_t al_pcib_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, al_pcib_probe),
+ DEVMETHOD(device_attach, al_pcib_attach),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(pcib, al_pcib_driver, al_pcib_methods,
+ sizeof(struct generic_pcie_fdt_softc), generic_pcie_fdt_driver);
+
+static devclass_t anpa_pcib_devclass;
+
+DRIVER_MODULE(alpine_pcib, simplebus, al_pcib_driver, anpa_pcib_devclass, 0, 0);
+DRIVER_MODULE(alpine_pcib, ofwbus, al_pcib_driver, anpa_pcib_devclass, 0, 0);
+
+static int
+al_pcib_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+ return (ENXIO);
+
+ device_set_desc(dev,
+ "Annapurna-Labs Integrated Internal PCI-E Controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+al_pcib_attach(device_t dev)
+{
+ int rv;
+
+ rv = pci_host_generic_attach(dev);
+
+ /* Annapurna quirk: configure vendor-specific registers */
+ if (rv == 0)
+ al_pcib_fixup(dev);
+
+ return (rv);
+}
+
+static void
+al_pcib_fixup(device_t dev)
+{
+ uint32_t val;
+ uint16_t vid;
+ uint8_t hdrtype;
+ int bus, slot, func, maxfunc;
+
+ /* Fixup is only needed on bus 0 */
+ bus = 0;
+ for (slot = 0; slot <= PCI_SLOTMAX; slot++) {
+ maxfunc = 0;
+ for (func = 0; func <= maxfunc; func++) {
+ hdrtype = PCIB_READ_CONFIG(dev, bus, slot, func,
+ PCIR_HDRTYPE, 1);
+
+ if ((hdrtype & PCIM_HDRTYPE) > PCI_MAXHDRTYPE)
+ continue;
+
+ if (func == 0 && (hdrtype & PCIM_MFDEV) != 0)
+ maxfunc = PCI_FUNCMAX;
+
+ vid = PCIB_READ_CONFIG(dev, bus, slot, func,
+ PCIR_VENDOR, 2);
+ if (vid == ANNAPURNA_VENDOR_ID) {
+ val = PCIB_READ_CONFIG(dev, bus, slot, func,
+ AL_PCI_AXI_CFG_AND_CTR_0, 4);
+ val |= PCIE_AXI_PF_AXI_ATTR_OVRD_FUNC_CTRL_2_PF_VEC_PH_VEC_OVRD_FROM_AXUSER_MASK;
+ PCIB_WRITE_CONFIG(dev, bus, slot, func,
+ AL_PCI_AXI_CFG_AND_CTR_0, val, 4);
+
+ val = PCIB_READ_CONFIG(dev, bus, slot, func,
+ AL_PCI_APP_CONTROL, 4);
+ val &= ~0xffff;
+ val |= PCIE_AXI_PF_AXI_ATTR_OVRD_FUNC_CTRL_4_PF_VEC_MEM_ADDR54_63_SEL_TGTID_MASK;
+ PCIB_WRITE_CONFIG(dev, bus, slot, func,
+ AL_PCI_APP_CONTROL, val, 4);
+ }
+ }
+ }
+}
diff --git a/sys/arm/annapurna/alpine/alpine_pci_msix.c b/sys/arm/annapurna/alpine/alpine_pci_msix.c
new file mode 100644
index 000000000000..a988a00d1912
--- /dev/null
+++ b/sys/arm/annapurna/alpine/alpine_pci_msix.c
@@ -0,0 +1,394 @@
+/*-
+ * Copyright (c) 2015,2016 Annapurna Labs Ltd. and affiliates
+ * All rights reserved.
+ *
+ * Developed by Semihalf.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/vmem.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "msi_if.h"
+#include "pic_if.h"
+
+#define AL_SPI_INTR 0
+#define AL_EDGE_HIGH 1
+#define ERR_NOT_IN_MAP -1
+#define IRQ_OFFSET 1
+#define GIC_INTR_CELL_CNT 3
+#define INTR_RANGE_COUNT 2
+#define MAX_MSIX_COUNT 160
+
+static int al_msix_attach(device_t);
+static int al_msix_probe(device_t);
+
+static msi_alloc_msi_t al_msix_alloc_msi;
+static msi_release_msi_t al_msix_release_msi;
+static msi_alloc_msix_t al_msix_alloc_msix;
+static msi_release_msix_t al_msix_release_msix;
+static msi_map_msi_t al_msix_map_msi;
+
+static int al_find_intr_pos_in_map(device_t, struct intr_irqsrc *);
+
+static struct ofw_compat_data compat_data[] = {
+ {"annapurna-labs,al-msix", true},
+ {"annapurna-labs,alpine-msix", true},
+ {NULL, false}
+};
+
+/*
+ * Bus interface definitions.
+ */
+static device_method_t al_msix_methods[] = {
+ DEVMETHOD(device_probe, al_msix_probe),
+ DEVMETHOD(device_attach, al_msix_attach),
+
+ /* Interrupt controller interface */
+ DEVMETHOD(msi_alloc_msi, al_msix_alloc_msi),
+ DEVMETHOD(msi_release_msi, al_msix_release_msi),
+ DEVMETHOD(msi_alloc_msix, al_msix_alloc_msix),
+ DEVMETHOD(msi_release_msix, al_msix_release_msix),
+ DEVMETHOD(msi_map_msi, al_msix_map_msi),
+
+ DEVMETHOD_END
+};
+
+struct al_msix_softc {
+ bus_addr_t base_addr;
+ struct resource *res;
+ uint32_t irq_min;
+ uint32_t irq_max;
+ uint32_t irq_count;
+ struct mtx msi_mtx;
+ vmem_t *irq_alloc;
+ device_t gic_dev;
+ /* Table of isrcs maps isrc pointer to vmem_alloc'd irq number */
+ struct intr_irqsrc *isrcs[MAX_MSIX_COUNT];
+};
+
+static driver_t al_msix_driver = {
+ "al_msix",
+ al_msix_methods,
+ sizeof(struct al_msix_softc),
+};
+
+devclass_t al_msix_devclass;
+
+DRIVER_MODULE(al_msix, ofwbus, al_msix_driver, al_msix_devclass, 0, 0);
+DRIVER_MODULE(al_msix, simplebus, al_msix_driver, al_msix_devclass, 0, 0);
+
+MALLOC_DECLARE(M_AL_MSIX);
+MALLOC_DEFINE(M_AL_MSIX, "al_msix", "Alpine MSIX");
+
+static int
+al_msix_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+ return (ENXIO);
+
+ device_set_desc(dev, "Annapurna-Labs MSI-X Controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+al_msix_attach(device_t dev)
+{
+ struct al_msix_softc *sc;
+ device_t gic_dev;
+ phandle_t iparent;
+ phandle_t node;
+ intptr_t xref;
+ int interrupts[INTR_RANGE_COUNT];
+ int nintr, i, rid;
+ uint32_t icells, *intr;
+
+ sc = device_get_softc(dev);
+
+ node = ofw_bus_get_node(dev);
+ xref = OF_xref_from_node(node);
+ OF_device_register_xref(xref, dev);
+
+ rid = 0;
+ sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
+ if (sc->res == NULL) {
+ device_printf(dev, "Failed to allocate resource\n");
+ return (ENXIO);
+ }
+
+ sc->base_addr = (bus_addr_t)rman_get_start(sc->res);
+
+ /* Register this device to handle MSI interrupts */
+ if (intr_msi_register(dev, xref) != 0) {
+ device_printf(dev, "could not register MSI-X controller\n");
+ return (ENXIO);
+ }
+ else
+ device_printf(dev, "MSI-X controller registered\n");
+
+ /* Find root interrupt controller */
+ iparent = ofw_bus_find_iparent(node);
+ if (iparent == 0) {
+ device_printf(dev, "No interrupt-parrent found. "
+ "Error in DTB\n");
+ return (ENXIO);
+ } else {
+ /* While at parent - store interrupt cells prop */
+ if (OF_searchencprop(OF_node_from_xref(iparent),
+ "#interrupt-cells", &icells, sizeof(icells)) == -1) {
+ device_printf(dev, "DTB: Missing #interrupt-cells "
+ "property in GIC node\n");
+ return (ENXIO);
+ }
+ }
+
+ gic_dev = OF_device_from_xref(iparent);
+ if (gic_dev == NULL) {
+ device_printf(dev, "Cannot find GIC device\n");
+ return (ENXIO);
+ }
+ sc->gic_dev = gic_dev;
+
+ /* Manually read range of interrupts from DTB */
+ nintr = OF_getencprop_alloc_multi(node, "interrupts", sizeof(*intr),
+ (void **)&intr);
+ if (nintr == 0) {
+ device_printf(dev, "Cannot read interrupts prop from DTB\n");
+ return (ENXIO);
+ } else if ((nintr / icells) != INTR_RANGE_COUNT) {
+ /* Supposed to have min and max value only */
+ device_printf(dev, "Unexpected count of interrupts "
+ "in DTB node\n");
+ return (EINVAL);
+ }
+
+ /* Read interrupt range values */
+ for (i = 0; i < INTR_RANGE_COUNT; i++)
+ interrupts[i] = intr[(i * icells) + IRQ_OFFSET];
+
+ sc->irq_min = interrupts[0];
+ sc->irq_max = interrupts[1];
+ sc->irq_count = (sc->irq_max - sc->irq_min + 1);
+
+ if (sc->irq_count > MAX_MSIX_COUNT) {
+ device_printf(dev, "Available MSI-X count exceeds buffer size."
+ " Capping to %d\n", MAX_MSIX_COUNT);
+ sc->irq_count = MAX_MSIX_COUNT;
+ }
+
+ mtx_init(&sc->msi_mtx, "msi_mtx", NULL, MTX_DEF);
+
+ sc->irq_alloc = vmem_create("Alpine MSI-X IRQs", 0, sc->irq_count,
+ 1, 0, M_FIRSTFIT | M_WAITOK);
+
+ device_printf(dev, "MSI-X SPI IRQ %d-%d\n", sc->irq_min, sc->irq_max);
+
+ return (bus_generic_attach(dev));
+}
+
+static int
+al_find_intr_pos_in_map(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct al_msix_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+ for (i = 0; i < MAX_MSIX_COUNT; i++)
+ if (sc->isrcs[i] == isrc)
+ return (i);
+ return (ERR_NOT_IN_MAP);
+}
+
+static int
+al_msix_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc,
+ uint64_t *addr, uint32_t *data)
+{
+ struct al_msix_softc *sc;
+ int i, spi;
+
+ sc = device_get_softc(dev);
+
+ i = al_find_intr_pos_in_map(dev, isrc);
+ if (i == ERR_NOT_IN_MAP)
+ return (EINVAL);
+
+ spi = sc->irq_min + i;
+
+ /*
+ * MSIX message address format:
+ * [63:20] - MSIx TBAR
+ * Same value as the MSIx Translation Base Address Register
+ * [19] - WFE_EXIT
+ * Once set by MSIx message, an EVENTI is signal to the CPUs
+ * cluster specified by ‘Local GIC Target List’
+ * [18:17] - Target GIC ID
+ * Specifies which IO-GIC (external shared GIC) is targeted
+ * 0: Local GIC, as specified by the Local GIC Target List
+ * 1: IO-GIC 0
+ * 2: Reserved
+ * 3: Reserved
+ * [16:13] - Local GIC Target List
+ * Specifies the Local GICs list targeted by this MSIx
+ * message.
+ * [16] If set, SPIn is set in Cluster 0 local GIC
+ * [15:13] Reserved
+ * [15] If set, SPIn is set in Cluster 1 local GIC
+ * [14] If set, SPIn is set in Cluster 2 local GIC
+ * [13] If set, SPIn is set in Cluster 3 local GIC
+ * [12:3] - SPIn
+ * Specifies the SPI (Shared Peripheral Interrupt) index to
+ * be set in target GICs
+ * Notes:
+ * If targeting any local GIC than only SPI[249:0] are valid
+ * [2] - Function vector
+ * MSI Data vector extension hint
+ * [1:0] - Reserved
+ * Must be set to zero
+ */
+ *addr = (uint64_t)sc->base_addr + (uint64_t)((1 << 16) + (spi << 3));
+ *data = 0;
+
+ if (bootverbose)
+ device_printf(dev, "MSI mapping: SPI: %d addr: %jx data: %x\n",
+ spi, (uintmax_t)*addr, *data);
+ return (0);
+}
+
+static int
+al_msix_alloc_msi(device_t dev, device_t child, int count, int maxcount,
+ device_t *pic, struct intr_irqsrc **srcs)
+{
+ struct intr_map_data_fdt *fdt_data;
+ struct al_msix_softc *sc;
+ vmem_addr_t irq_base;
+ int error;
+ u_int i, j;
+
+ sc = device_get_softc(dev);
+
+ if ((powerof2(count) == 0) || (count > 8))
+ return (EINVAL);
+
+ if (vmem_alloc(sc->irq_alloc, count, M_FIRSTFIT | M_NOWAIT,
+ &irq_base) != 0)
+ return (ENOMEM);
+
+ /* Fabricate OFW data to get ISRC from GIC and return it */
+ fdt_data = malloc(sizeof(*fdt_data) +
+ GIC_INTR_CELL_CNT * sizeof(pcell_t), M_AL_MSIX, M_WAITOK);
+ fdt_data->hdr.type = INTR_MAP_DATA_FDT;
+ fdt_data->iparent = 0;
+ fdt_data->ncells = GIC_INTR_CELL_CNT;
+ fdt_data->cells[0] = AL_SPI_INTR; /* code for SPI interrupt */
+ fdt_data->cells[1] = 0; /* SPI number (uninitialized) */
+ fdt_data->cells[2] = AL_EDGE_HIGH; /* trig = edge, pol = high */
+
+ mtx_lock(&sc->msi_mtx);
+
+ for (i = irq_base; i < irq_base + count; i++) {
+ fdt_data->cells[1] = sc->irq_min + i;
+ error = PIC_MAP_INTR(sc->gic_dev,
+ (struct intr_map_data *)fdt_data, srcs);
+ if (error) {
+ for (j = irq_base; j < i; j++)
+ sc->isrcs[j] = NULL;
+ mtx_unlock(&sc->msi_mtx);
+ vmem_free(sc->irq_alloc, irq_base, count);
+ free(fdt_data, M_AL_MSIX);
+ return (error);
+ }
+
+ sc->isrcs[i] = *srcs;
+ srcs++;
+ }
+
+ mtx_unlock(&sc->msi_mtx);
+ free(fdt_data, M_AL_MSIX);
+
+ if (bootverbose)
+ device_printf(dev,
+ "MSI-X allocation: start SPI %d, count %d\n",
+ (int)irq_base + sc->irq_min, count);
+
+ *pic = sc->gic_dev;
+
+ return (0);
+}
+
+static int
+al_msix_release_msi(device_t dev, device_t child, int count,
+ struct intr_irqsrc **srcs)
+{
+ struct al_msix_softc *sc;
+ int i, pos;
+
+ sc = device_get_softc(dev);
+
+ mtx_lock(&sc->msi_mtx);
+
+ pos = al_find_intr_pos_in_map(dev, *srcs);
+ vmem_free(sc->irq_alloc, pos, count);
+ for (i = 0; i < count; i++) {
+ pos = al_find_intr_pos_in_map(dev, *srcs);
+ if (pos != ERR_NOT_IN_MAP)
+ sc->isrcs[pos] = NULL;
+ srcs++;
+ }
+
+ mtx_unlock(&sc->msi_mtx);
+
+ return (0);
+}
+
+static int
+al_msix_alloc_msix(device_t dev, device_t child, device_t *pic,
+ struct intr_irqsrc **isrcp)
+{
+
+ return (al_msix_alloc_msi(dev, child, 1, 1, pic, isrcp));
+}
+
+static int
+al_msix_release_msix(device_t dev, device_t child, struct intr_irqsrc *isrc)
+{
+
+ return (al_msix_release_msi(dev, child, 1, &isrc));
+}
diff --git a/sys/arm/annapurna/alpine/alpine_serdes.c b/sys/arm/annapurna/alpine/alpine_serdes.c
new file mode 100644
index 000000000000..882b01a8da4f
--- /dev/null
+++ b/sys/arm/annapurna/alpine/alpine_serdes.c
@@ -0,0 +1,225 @@
+/*-
+ * Copyright (c) 2015,2016 Annapurna Labs Ltd. and affiliates
+ * All rights reserved.
+ *
+ * Developed by Semihalf.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/conf.h>
+#include <sys/resource.h>
+
+#include <machine/bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "al_serdes.h"
+#include "alpine_serdes.h"
+
+#define SERDES_NUM_GROUPS 5
+
+static void *serdes_base;
+static uint32_t serdes_grp_offset[] = {0, 0x400, 0x800, 0xc00, 0x2000};
+
+static struct alpine_serdes_eth_group_mode {
+ struct mtx lock;
+ enum alpine_serdes_eth_mode mode;
+ bool mode_set;
+} alpine_serdes_eth_group_mode[SERDES_NUM_GROUPS];
+
+static int al_serdes_probe(device_t dev);
+static int al_serdes_attach(device_t dev);
+static int al_serdes_detach(device_t dev);
+
+static struct resource_spec al_serdes_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+struct al_serdes_softc {
+ struct resource *res;
+};
+
+static device_method_t al_serdes_methods[] = {
+ DEVMETHOD(device_probe, al_serdes_probe),
+ DEVMETHOD(device_attach, al_serdes_attach),
+ DEVMETHOD(device_detach, al_serdes_detach),
+
+ DEVMETHOD_END
+};
+
+static driver_t al_serdes_driver = {
+ "serdes",
+ al_serdes_methods,
+ sizeof(struct al_serdes_softc)
+};
+
+static devclass_t al_serdes_devclass;
+
+DRIVER_MODULE(al_serdes, simplebus, al_serdes_driver,
+ al_serdes_devclass, 0, 0);
+DRIVER_MODULE(al_serdes, ofwbus, al_serdes_driver,
+ al_serdes_devclass, 0, 0);
+
+static int
+al_serdes_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "annapurna-labs,al-serdes"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Alpine Serdes");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+al_serdes_attach(device_t dev)
+{
+ struct al_serdes_softc *sc;
+ int err;
+
+ sc = device_get_softc(dev);
+
+ err = bus_alloc_resources(dev, al_serdes_spec, &sc->res);
+ if (err != 0) {
+ device_printf(dev, "could not allocate resources\n");
+ return (err);
+ }
+
+ /* Initialize Serdes group locks and mode */
+ for (int i = 0; i < nitems(alpine_serdes_eth_group_mode); i++) {
+ mtx_init(&alpine_serdes_eth_group_mode[i].lock, "AlSerdesMtx",
+ NULL, MTX_DEF);
+ alpine_serdes_eth_group_mode[i].mode_set = false;
+ }
+
+ serdes_base = (void *)rman_get_bushandle(sc->res);
+
+ return (0);
+}
+
+static int
+al_serdes_detach(device_t dev)
+{
+ struct al_serdes_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ bus_release_resources(dev, al_serdes_spec, &sc->res);
+
+ for (int i = 0; i < nitems(alpine_serdes_eth_group_mode); i++) {
+ mtx_destroy(&alpine_serdes_eth_group_mode[i].lock);
+ alpine_serdes_eth_group_mode[i].mode_set = false;
+ }
+
+ return (0);
+}
+
+void *
+alpine_serdes_resource_get(uint32_t group)
+{
+ void *base;
+
+ base = NULL;
+ if (group >= SERDES_NUM_GROUPS)
+ return (NULL);
+
+ if (serdes_base != NULL)
+ base = (void *)((uintptr_t)serdes_base +
+ serdes_grp_offset[group]);
+
+ return (base);
+}
+
+int
+alpine_serdes_eth_mode_set(uint32_t group, enum alpine_serdes_eth_mode mode)
+{
+ struct alpine_serdes_eth_group_mode *group_mode;
+
+ group_mode = &alpine_serdes_eth_group_mode[group];
+
+ if (serdes_base == NULL)
+ return (EINVAL);
+
+ if (group >= SERDES_NUM_GROUPS)
+ return (EINVAL);
+
+ mtx_lock(&group_mode->lock);
+
+ if (!group_mode->mode_set || (group_mode->mode != mode)) {
+ struct al_serdes_grp_obj obj;
+
+ al_serdes_handle_grp_init(alpine_serdes_resource_get(group),
+ group, &obj);
+
+ if (mode == ALPINE_SERDES_ETH_MODE_SGMII)
+ obj.mode_set_sgmii(&obj);
+ else
+ obj.mode_set_kr(&obj);
+
+ group_mode->mode = mode;
+ group_mode->mode_set = true;
+ }
+
+ mtx_unlock(&group_mode->lock);
+
+ return (0);
+}
+
+void
+alpine_serdes_eth_group_lock(uint32_t group)
+{
+ struct alpine_serdes_eth_group_mode *group_mode;
+
+ group_mode = &alpine_serdes_eth_group_mode[group];
+
+ if (mtx_initialized(&group_mode->lock) == 0)
+ return;
+
+ mtx_lock(&group_mode->lock);
+}
+
+void
+alpine_serdes_eth_group_unlock(uint32_t group)
+{
+ struct alpine_serdes_eth_group_mode *group_mode;
+
+ group_mode = &alpine_serdes_eth_group_mode[group];
+
+ if (mtx_initialized(&group_mode->lock) == 0)
+ return;
+
+ mtx_unlock(&group_mode->lock);
+}
diff --git a/sys/arm/annapurna/alpine/alpine_serdes.h b/sys/arm/annapurna/alpine/alpine_serdes.h
new file mode 100644
index 000000000000..034bdadfb15e
--- /dev/null
+++ b/sys/arm/annapurna/alpine/alpine_serdes.h
@@ -0,0 +1,61 @@
+/*-
+ * Copyright (c) 2015,2016 Annapurna Labs Ltd. and affiliates
+ * All rights reserved.
+ *
+ * Developed by Semihalf.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __ALPINE_SERDES_H__
+#define __ALPINE_SERDES_H__
+
+/* SerDes ETH mode */
+enum alpine_serdes_eth_mode {
+ ALPINE_SERDES_ETH_MODE_SGMII,
+ ALPINE_SERDES_ETH_MODE_KR,
+};
+
+/*
+ * Get SerDes group regs base, to be used in relevant Alpine drivers.
+ * Valid group is 0..3.
+ * Returns virtual base address of the group regs base.
+ */
+void *alpine_serdes_resource_get(uint32_t group);
+
+/*
+ * Set SerDes ETH mode for an entire group, unless already set
+ * Valid group is 0..3.
+ * Returns 0 upon success.
+ */
+int alpine_serdes_eth_mode_set(uint32_t group,
+ enum alpine_serdes_eth_mode mode);
+
+/* Lock the all serdes group for using common registers */
+void alpine_serdes_eth_group_lock(uint32_t group);
+
+/* Unlock the all serdes group for using common registers */
+void alpine_serdes_eth_group_unlock(uint32_t group);
+
+#endif /* __ALPINE_SERDES_H__ */
diff --git a/sys/arm/annapurna/alpine/files.alpine b/sys/arm/annapurna/alpine/files.alpine
new file mode 100644
index 000000000000..6724b45ec505
--- /dev/null
+++ b/sys/arm/annapurna/alpine/files.alpine
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+arm/versatile/sp804.c standard
+dev/uart/uart_dev_ns8250.c optional uart
+
+arm/annapurna/alpine/alpine_machdep.c standard
+arm/annapurna/alpine/alpine_machdep_mp.c optional smp
diff --git a/sys/arm/annapurna/alpine/std.alpine b/sys/arm/annapurna/alpine/std.alpine
new file mode 100644
index 000000000000..e8c14f837cea
--- /dev/null
+++ b/sys/arm/annapurna/alpine/std.alpine
@@ -0,0 +1,13 @@
+# $FreeBSD$
+
+cpu CPU_CORTEXA
+machine arm armv7
+makeoptions CONF_CFLAGS="-march=armv7a -DAL_HAVE_TYPES"
+
+makeoptions KERNVIRTADDR=0xa0200000
+options KERNVIRTADDR=0xa0200000
+
+makeoptions KERNBASE=0xa0000000
+options KERNBASE=0xa0000000
+
+files "../annapurna/alpine/files.alpine"
diff --git a/sys/arm/arm/autoconf.c b/sys/arm/arm/autoconf.c
new file mode 100644
index 000000000000..672575b75701
--- /dev/null
+++ b/sys/arm/arm/autoconf.c
@@ -0,0 +1,101 @@
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)autoconf.c 7.1 (Berkeley) 5/9/91
+ * from: FreeBSD: src/sys/i386/i386/autoconf.c,v 1.156
+ */
+
+/*
+ * Setup the system to run on the current machine.
+ *
+ * Configure() is called at boot time and initializes the vba
+ * device tables and the memory controller monitoring. Available
+ * devices are determined (from possibilities mentioned in ioconf.c),
+ * and the drivers are initialized.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/disklabel.h>
+#include <sys/reboot.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/cons.h>
+
+static void configure_first (void *);
+static void configure (void *);
+static void configure_final (void *);
+
+SYSINIT(configure1, SI_SUB_CONFIGURE, SI_ORDER_FIRST, configure_first, NULL);
+/* SI_ORDER_SECOND is hookable */
+SYSINIT(configure2, SI_SUB_CONFIGURE, SI_ORDER_THIRD, configure, NULL);
+/* SI_ORDER_MIDDLE is hookable */
+SYSINIT(configure3, SI_SUB_CONFIGURE, SI_ORDER_ANY, configure_final, NULL);
+
+device_t nexus_dev;
+
+/*
+ * Determine i/o configuration for a machine.
+ */
+static void
+configure_first(void *dummy)
+{
+
+ device_add_child(root_bus, "nexus", 0);
+}
+
+static void
+configure(void *dummy)
+{
+
+ root_bus_configure();
+}
+
+static void
+configure_final(void *dummy)
+{
+
+ enable_interrupts(PSR_I | PSR_F);
+ cninit_finish();
+ cold = 0;
+}
diff --git a/sys/arm/arm/bcopy_page.S b/sys/arm/arm/bcopy_page.S
new file mode 100644
index 000000000000..e718a06109b4
--- /dev/null
+++ b/sys/arm/arm/bcopy_page.S
@@ -0,0 +1,142 @@
+/* $NetBSD: bcopy_page.S,v 1.7 2003/10/13 21:03:13 scw Exp $ */
+
+/*-
+ * Copyright (c) 1995 Scott Stevens
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Scott Stevens.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * RiscBSD kernel project
+ *
+ * bcopy_page.S
+ *
+ * page optimised bcopy and bzero routines
+ *
+ * Created : 08/04/95
+ */
+
+#include <machine/asm.h>
+
+__FBSDID("$FreeBSD$");
+
+#include "assym.inc"
+
+
+/*
+ * armv5e version of bcopy_page
+ */
+ENTRY(bcopy_page)
+ pld [r0]
+ stmfd sp!, {r4, r5}
+ _SAVE({r4, r5})
+ mov ip, #32
+ ldr r2, [r0], #0x04 /* 0x00 */
+ ldr r3, [r0], #0x04 /* 0x04 */
+1: pld [r0, #0x18] /* Prefetch 0x20 */
+ ldr r4, [r0], #0x04 /* 0x08 */
+ ldr r5, [r0], #0x04 /* 0x0c */
+ strd r2, [r1], #0x08
+ ldr r2, [r0], #0x04 /* 0x10 */
+ ldr r3, [r0], #0x04 /* 0x14 */
+ strd r4, [r1], #0x08
+ ldr r4, [r0], #0x04 /* 0x18 */
+ ldr r5, [r0], #0x04 /* 0x1c */
+ strd r2, [r1], #0x08
+ ldr r2, [r0], #0x04 /* 0x20 */
+ ldr r3, [r0], #0x04 /* 0x24 */
+ pld [r0, #0x18] /* Prefetch 0x40 */
+ strd r4, [r1], #0x08
+ ldr r4, [r0], #0x04 /* 0x28 */
+ ldr r5, [r0], #0x04 /* 0x2c */
+ strd r2, [r1], #0x08
+ ldr r2, [r0], #0x04 /* 0x30 */
+ ldr r3, [r0], #0x04 /* 0x34 */
+ strd r4, [r1], #0x08
+ ldr r4, [r0], #0x04 /* 0x38 */
+ ldr r5, [r0], #0x04 /* 0x3c */
+ strd r2, [r1], #0x08
+ ldr r2, [r0], #0x04 /* 0x40 */
+ ldr r3, [r0], #0x04 /* 0x44 */
+ pld [r0, #0x18] /* Prefetch 0x60 */
+ strd r4, [r1], #0x08
+ ldr r4, [r0], #0x04 /* 0x48 */
+ ldr r5, [r0], #0x04 /* 0x4c */
+ strd r2, [r1], #0x08
+ ldr r2, [r0], #0x04 /* 0x50 */
+ ldr r3, [r0], #0x04 /* 0x54 */
+ strd r4, [r1], #0x08
+ ldr r4, [r0], #0x04 /* 0x58 */
+ ldr r5, [r0], #0x04 /* 0x5c */
+ strd r2, [r1], #0x08
+ ldr r2, [r0], #0x04 /* 0x60 */
+ ldr r3, [r0], #0x04 /* 0x64 */
+ pld [r0, #0x18] /* Prefetch 0x80 */
+ strd r4, [r1], #0x08
+ ldr r4, [r0], #0x04 /* 0x68 */
+ ldr r5, [r0], #0x04 /* 0x6c */
+ strd r2, [r1], #0x08
+ ldr r2, [r0], #0x04 /* 0x70 */
+ ldr r3, [r0], #0x04 /* 0x74 */
+ strd r4, [r1], #0x08
+ ldr r4, [r0], #0x04 /* 0x78 */
+ ldr r5, [r0], #0x04 /* 0x7c */
+ strd r2, [r1], #0x08
+ subs ip, ip, #0x01
+ ldrgt r2, [r0], #0x04 /* 0x80 */
+ ldrgt r3, [r0], #0x04 /* 0x84 */
+ strd r4, [r1], #0x08
+ bgt 1b
+ ldmfd sp!, {r4, r5}
+ RET
+END(bcopy_page)
+
+/*
+ * armv5e version of bzero_page
+ */
+ENTRY(bzero_page)
+ mov r1, #PAGE_SIZE
+ mov r2, #0
+ mov r3, #0
+1: strd r2, [r0], #8 /* 32 */
+ strd r2, [r0], #8
+ strd r2, [r0], #8
+ strd r2, [r0], #8
+ strd r2, [r0], #8 /* 64 */
+ strd r2, [r0], #8
+ strd r2, [r0], #8
+ strd r2, [r0], #8
+ strd r2, [r0], #8 /* 96 */
+ strd r2, [r0], #8
+ strd r2, [r0], #8
+ strd r2, [r0], #8
+ strd r2, [r0], #8 /* 128 */
+ strd r2, [r0], #8
+ strd r2, [r0], #8
+ strd r2, [r0], #8
+ subs r1, r1, #128
+ bne 1b
+ RET
+END(bzero_page)
diff --git a/sys/arm/arm/bcopyinout.S b/sys/arm/arm/bcopyinout.S
new file mode 100644
index 000000000000..2a9d48f989ea
--- /dev/null
+++ b/sys/arm/arm/bcopyinout.S
@@ -0,0 +1,129 @@
+/* $NetBSD: bcopyinout.S,v 1.11 2003/10/13 21:22:40 scw Exp $ */
+
+/*-
+ * Copyright (c) 2002 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Allen Briggs for Wasabi Systems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the NetBSD Project by
+ * Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#include "assym.inc"
+
+#include <machine/asm.h>
+#include <sys/errno.h>
+
+.L_arm_memcpy:
+ .word _C_LABEL(_arm_memcpy)
+.L_min_memcpy_size:
+ .word _C_LABEL(_min_memcpy_size)
+
+__FBSDID("$FreeBSD$");
+#include <arm/arm/bcopyinout_xscale.S>
+
+/*
+ * int badaddr_read_1(const uint8_t *src, uint8_t *dest)
+ *
+ * Copies a single 8-bit value from src to dest, returning 0 on success,
+ * else EFAULT if a page fault occurred.
+ */
+ENTRY(badaddr_read_1)
+ GET_PCB(r2)
+ ldr r2, [r2]
+
+ ldr ip, [r2, #PCB_ONFAULT]
+ adr r3, 1f
+ str r3, [r2, #PCB_ONFAULT]
+ nop
+ nop
+ nop
+ ldrb r3, [r0]
+ nop
+ nop
+ nop
+ strb r3, [r1]
+ mov r0, #0 /* No fault */
+1: str ip, [r2, #PCB_ONFAULT]
+ RET
+END(badaddr_read_1)
+
+/*
+ * int badaddr_read_2(const uint16_t *src, uint16_t *dest)
+ *
+ * Copies a single 16-bit value from src to dest, returning 0 on success,
+ * else EFAULT if a page fault occurred.
+ */
+ENTRY(badaddr_read_2)
+ GET_PCB(r2)
+ ldr r2, [r2]
+
+ ldr ip, [r2, #PCB_ONFAULT]
+ adr r3, 1f
+ str r3, [r2, #PCB_ONFAULT]
+ nop
+ nop
+ nop
+ ldrh r3, [r0]
+ nop
+ nop
+ nop
+ strh r3, [r1]
+ mov r0, #0 /* No fault */
+1: str ip, [r2, #PCB_ONFAULT]
+ RET
+END(badaddr_read_2)
+
+/*
+ * int badaddr_read_4(const uint32_t *src, uint32_t *dest)
+ *
+ * Copies a single 32-bit value from src to dest, returning 0 on success,
+ * else EFAULT if a page fault occurred.
+ */
+ENTRY(badaddr_read_4)
+ GET_PCB(r2)
+ ldr r2, [r2]
+
+ ldr ip, [r2, #PCB_ONFAULT]
+ adr r3, 1f
+ str r3, [r2, #PCB_ONFAULT]
+ nop
+ nop
+ nop
+ ldr r3, [r0]
+ nop
+ nop
+ nop
+ str r3, [r1]
+ mov r0, #0 /* No fault */
+1: str ip, [r2, #PCB_ONFAULT]
+ RET
+END(badaddr_read_4)
+
diff --git a/sys/arm/arm/bcopyinout_xscale.S b/sys/arm/arm/bcopyinout_xscale.S
new file mode 100644
index 000000000000..e592b901201f
--- /dev/null
+++ b/sys/arm/arm/bcopyinout_xscale.S
@@ -0,0 +1,819 @@
+/* $NetBSD: bcopyinout_xscale.S,v 1.3 2003/12/15 09:27:18 scw Exp $ */
+
+/*-
+ * Copyright 2003 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Steve C. Woodford for Wasabi Systems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the NetBSD Project by
+ * Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <machine/asm.h>
+__FBSDID("$FreeBSD$");
+
+ .syntax unified
+ .text
+ .align 2
+
+#define GET_PCB(tmp) \
+ mrc p15, 0, tmp, c13, c0, 4; \
+ add tmp, tmp, #(TD_PCB)
+
+/*
+ * r0 = user space address
+ * r1 = kernel space address
+ * r2 = length
+ *
+ * Copies bytes from user space to kernel space
+ */
+ENTRY(copyin)
+ cmp r2, #0x00
+ movle r0, #0x00
+ movle pc, lr /* Bail early if length is <= 0 */
+
+ adds r3, r0, r2
+ movcs r0, #EFAULT
+ RETc(cs)
+
+ ldr r12, =(VM_MAXUSER_ADDRESS + 1)
+ cmp r3, r12
+ movcs r0, #EFAULT
+ RETc(cs)
+
+ ldr r3, .L_arm_memcpy
+ ldr r3, [r3]
+ cmp r3, #0
+ beq .Lnormal
+ ldr r3, .L_min_memcpy_size
+ ldr r3, [r3]
+ cmp r2, r3
+ blt .Lnormal
+ stmfd sp!, {r0-r2, r4, lr}
+ mov r3, r0
+ mov r0, r1
+ mov r1, r3
+ mov r3, #2 /* SRC_IS_USER */
+ ldr r4, .L_arm_memcpy
+ mov lr, pc
+ ldr pc, [r4]
+ cmp r0, #0
+ ldmfd sp!, {r0-r2, r4, lr}
+ moveq r0, #0
+ RETeq
+
+.Lnormal:
+ stmfd sp!, {r10-r11, lr}
+
+ GET_PCB(r10)
+ ldr r10, [r10]
+
+ mov r3, #0x00
+ adr ip, .Lcopyin_fault
+ ldr r11, [r10, #PCB_ONFAULT]
+ str ip, [r10, #PCB_ONFAULT]
+ bl .Lcopyin_guts
+ str r11, [r10, #PCB_ONFAULT]
+ mov r0, #0x00
+ ldmfd sp!, {r10-r11, pc}
+
+.Lcopyin_fault:
+ ldr r0, =EFAULT
+ str r11, [r10, #PCB_ONFAULT]
+ cmp r3, #0x00
+ ldmfdgt sp!, {r4-r7} /* r3 > 0 Restore r4-r7 */
+ ldmfdlt sp!, {r4-r9} /* r3 < 0 Restore r4-r9 */
+ ldmfd sp!, {r10-r11, pc}
+
+.Lcopyin_guts:
+ pld [r0]
+ /* Word-align the destination buffer */
+ ands ip, r1, #0x03 /* Already word aligned? */
+ beq .Lcopyin_wordaligned /* Yup */
+ rsb ip, ip, #0x04
+ cmp r2, ip /* Enough bytes left to align it? */
+ blt .Lcopyin_l4_2 /* Nope. Just copy bytewise */
+ sub r2, r2, ip
+ rsbs ip, ip, #0x03
+ addne pc, pc, ip, lsl #3
+ nop
+ ldrbt ip, [r0], #0x01
+ strb ip, [r1], #0x01
+ ldrbt ip, [r0], #0x01
+ strb ip, [r1], #0x01
+ ldrbt ip, [r0], #0x01
+ strb ip, [r1], #0x01
+ cmp r2, #0x00 /* All done? */
+ RETeq
+
+ /* Destination buffer is now word aligned */
+.Lcopyin_wordaligned:
+ ands ip, r0, #0x03 /* Is src also word-aligned? */
+ bne .Lcopyin_bad_align /* Nope. Things just got bad */
+ cmp r2, #0x08 /* Less than 8 bytes remaining? */
+ blt .Lcopyin_w_less_than8
+
+ /* Quad-align the destination buffer */
+ tst r1, #0x07 /* Already quad aligned? */
+ ldrtne ip, [r0], #0x04
+ strne ip, [r1], #0x04
+ subne r2, r2, #0x04
+ stmfd sp!, {r4-r9} /* Free up some registers */
+ mov r3, #-1 /* Signal restore r4-r9 */
+
+ /* Destination buffer quad aligned, source is word aligned */
+ subs r2, r2, #0x80
+ blt .Lcopyin_w_lessthan128
+
+ /* Copy 128 bytes at a time */
+.Lcopyin_w_loop128:
+ ldrt r4, [r0], #0x04 /* LD:00-03 */
+ ldrt r5, [r0], #0x04 /* LD:04-07 */
+ pld [r0, #0x18] /* Prefetch 0x20 */
+ ldrt r6, [r0], #0x04 /* LD:08-0b */
+ ldrt r7, [r0], #0x04 /* LD:0c-0f */
+ ldrt r8, [r0], #0x04 /* LD:10-13 */
+ ldrt r9, [r0], #0x04 /* LD:14-17 */
+ strd r4, [r1], #0x08 /* ST:00-07 */
+ ldrt r4, [r0], #0x04 /* LD:18-1b */
+ ldrt r5, [r0], #0x04 /* LD:1c-1f */
+ strd r6, [r1], #0x08 /* ST:08-0f */
+ ldrt r6, [r0], #0x04 /* LD:20-23 */
+ ldrt r7, [r0], #0x04 /* LD:24-27 */
+ pld [r0, #0x18] /* Prefetch 0x40 */
+ strd r8, [r1], #0x08 /* ST:10-17 */
+ ldrt r8, [r0], #0x04 /* LD:28-2b */
+ ldrt r9, [r0], #0x04 /* LD:2c-2f */
+ strd r4, [r1], #0x08 /* ST:18-1f */
+ ldrt r4, [r0], #0x04 /* LD:30-33 */
+ ldrt r5, [r0], #0x04 /* LD:34-37 */
+ strd r6, [r1], #0x08 /* ST:20-27 */
+ ldrt r6, [r0], #0x04 /* LD:38-3b */
+ ldrt r7, [r0], #0x04 /* LD:3c-3f */
+ strd r8, [r1], #0x08 /* ST:28-2f */
+ ldrt r8, [r0], #0x04 /* LD:40-43 */
+ ldrt r9, [r0], #0x04 /* LD:44-47 */
+ pld [r0, #0x18] /* Prefetch 0x60 */
+ strd r4, [r1], #0x08 /* ST:30-37 */
+ ldrt r4, [r0], #0x04 /* LD:48-4b */
+ ldrt r5, [r0], #0x04 /* LD:4c-4f */
+ strd r6, [r1], #0x08 /* ST:38-3f */
+ ldrt r6, [r0], #0x04 /* LD:50-53 */
+ ldrt r7, [r0], #0x04 /* LD:54-57 */
+ strd r8, [r1], #0x08 /* ST:40-47 */
+ ldrt r8, [r0], #0x04 /* LD:58-5b */
+ ldrt r9, [r0], #0x04 /* LD:5c-5f */
+ strd r4, [r1], #0x08 /* ST:48-4f */
+ ldrt r4, [r0], #0x04 /* LD:60-63 */
+ ldrt r5, [r0], #0x04 /* LD:64-67 */
+ pld [r0, #0x18] /* Prefetch 0x80 */
+ strd r6, [r1], #0x08 /* ST:50-57 */
+ ldrt r6, [r0], #0x04 /* LD:68-6b */
+ ldrt r7, [r0], #0x04 /* LD:6c-6f */
+ strd r8, [r1], #0x08 /* ST:58-5f */
+ ldrt r8, [r0], #0x04 /* LD:70-73 */
+ ldrt r9, [r0], #0x04 /* LD:74-77 */
+ strd r4, [r1], #0x08 /* ST:60-67 */
+ ldrt r4, [r0], #0x04 /* LD:78-7b */
+ ldrt r5, [r0], #0x04 /* LD:7c-7f */
+ strd r6, [r1], #0x08 /* ST:68-6f */
+ strd r8, [r1], #0x08 /* ST:70-77 */
+ subs r2, r2, #0x80
+ strd r4, [r1], #0x08 /* ST:78-7f */
+ bge .Lcopyin_w_loop128
+
+.Lcopyin_w_lessthan128:
+ adds r2, r2, #0x80 /* Adjust for extra sub */
+ ldmfdeq sp!, {r4-r9}
+ RETeq
+ subs r2, r2, #0x20
+ blt .Lcopyin_w_lessthan32
+
+ /* Copy 32 bytes at a time */
+.Lcopyin_w_loop32:
+ ldrt r4, [r0], #0x04
+ ldrt r5, [r0], #0x04
+ pld [r0, #0x18]
+ ldrt r6, [r0], #0x04
+ ldrt r7, [r0], #0x04
+ ldrt r8, [r0], #0x04
+ ldrt r9, [r0], #0x04
+ strd r4, [r1], #0x08
+ ldrt r4, [r0], #0x04
+ ldrt r5, [r0], #0x04
+ strd r6, [r1], #0x08
+ strd r8, [r1], #0x08
+ subs r2, r2, #0x20
+ strd r4, [r1], #0x08
+ bge .Lcopyin_w_loop32
+
+.Lcopyin_w_lessthan32:
+ adds r2, r2, #0x20 /* Adjust for extra sub */
+ ldmfdeq sp!, {r4-r9}
+ RETeq /* Return now if done */
+
+ and r4, r2, #0x18
+ rsb r5, r4, #0x18
+ subs r2, r2, r4
+ add pc, pc, r5, lsl #1
+ nop
+
+ /* At least 24 bytes remaining */
+ ldrt r4, [r0], #0x04
+ ldrt r5, [r0], #0x04
+ nop
+ strd r4, [r1], #0x08
+
+ /* At least 16 bytes remaining */
+ ldrt r4, [r0], #0x04
+ ldrt r5, [r0], #0x04
+ nop
+ strd r4, [r1], #0x08
+
+ /* At least 8 bytes remaining */
+ ldrt r4, [r0], #0x04
+ ldrt r5, [r0], #0x04
+ nop
+ strd r4, [r1], #0x08
+
+ /* Less than 8 bytes remaining */
+ ldmfd sp!, {r4-r9}
+ RETeq /* Return now if done */
+ mov r3, #0x00
+
+.Lcopyin_w_less_than8:
+ subs r2, r2, #0x04
+ ldrtge ip, [r0], #0x04
+ strge ip, [r1], #0x04
+ RETeq /* Return now if done */
+ addlt r2, r2, #0x04
+ ldrbt ip, [r0], #0x01
+ cmp r2, #0x02
+ ldrbtge r2, [r0], #0x01
+ strb ip, [r1], #0x01
+ ldrbtgt ip, [r0]
+ strbge r2, [r1], #0x01
+ strbgt ip, [r1]
+ RET
+
+/*
+ * At this point, it has not been possible to word align both buffers.
+ * The destination buffer (r1) is word aligned, but the source buffer
+ * (r0) is not.
+ */
+.Lcopyin_bad_align:
+ stmfd sp!, {r4-r7}
+ mov r3, #0x01
+ bic r0, r0, #0x03
+ cmp ip, #2
+ ldrt ip, [r0], #0x04
+ bgt .Lcopyin_bad3
+ beq .Lcopyin_bad2
+ b .Lcopyin_bad1
+
+.Lcopyin_bad1_loop16:
+ mov r4, ip, lsr #8
+ ldrt r5, [r0], #0x04
+ pld [r0, #0x018]
+ ldrt r6, [r0], #0x04
+ ldrt r7, [r0], #0x04
+ ldrt ip, [r0], #0x04
+ orr r4, r4, r5, lsl #24
+ mov r5, r5, lsr #8
+ orr r5, r5, r6, lsl #24
+ mov r6, r6, lsr #8
+ orr r6, r6, r7, lsl #24
+ mov r7, r7, lsr #8
+ orr r7, r7, ip, lsl #24
+ str r4, [r1], #0x04
+ str r5, [r1], #0x04
+ str r6, [r1], #0x04
+ str r7, [r1], #0x04
+.Lcopyin_bad1:
+ subs r2, r2, #0x10
+ bge .Lcopyin_bad1_loop16
+
+ adds r2, r2, #0x10
+ ldmfdeq sp!, {r4-r7}
+ RETeq /* Return now if done */
+ subs r2, r2, #0x04
+ sublt r0, r0, #0x03
+ blt .Lcopyin_l4
+
+.Lcopyin_bad1_loop4:
+ mov r4, ip, lsr #8
+ ldrt ip, [r0], #0x04
+ subs r2, r2, #0x04
+ orr r4, r4, ip, lsl #24
+ str r4, [r1], #0x04
+ bge .Lcopyin_bad1_loop4
+ sub r0, r0, #0x03
+ b .Lcopyin_l4
+
+.Lcopyin_bad2_loop16:
+ mov r4, ip, lsr #16
+ ldrt r5, [r0], #0x04
+ pld [r0, #0x018]
+ ldrt r6, [r0], #0x04
+ ldrt r7, [r0], #0x04
+ ldrt ip, [r0], #0x04
+ orr r4, r4, r5, lsl #16
+ mov r5, r5, lsr #16
+ orr r5, r5, r6, lsl #16
+ mov r6, r6, lsr #16
+ orr r6, r6, r7, lsl #16
+ mov r7, r7, lsr #16
+ orr r7, r7, ip, lsl #16
+ str r4, [r1], #0x04
+ str r5, [r1], #0x04
+ str r6, [r1], #0x04
+ str r7, [r1], #0x04
+.Lcopyin_bad2:
+ subs r2, r2, #0x10
+ bge .Lcopyin_bad2_loop16
+
+ adds r2, r2, #0x10
+ ldmfdeq sp!, {r4-r7}
+ RETeq /* Return now if done */
+ subs r2, r2, #0x04
+ sublt r0, r0, #0x02
+ blt .Lcopyin_l4
+
+.Lcopyin_bad2_loop4:
+ mov r4, ip, lsr #16
+ ldrt ip, [r0], #0x04
+ subs r2, r2, #0x04
+ orr r4, r4, ip, lsl #16
+ str r4, [r1], #0x04
+ bge .Lcopyin_bad2_loop4
+ sub r0, r0, #0x02
+ b .Lcopyin_l4
+
+.Lcopyin_bad3_loop16:
+ mov r4, ip, lsr #24
+ ldrt r5, [r0], #0x04
+ pld [r0, #0x018]
+ ldrt r6, [r0], #0x04
+ ldrt r7, [r0], #0x04
+ ldrt ip, [r0], #0x04
+ orr r4, r4, r5, lsl #8
+ mov r5, r5, lsr #24
+ orr r5, r5, r6, lsl #8
+ mov r6, r6, lsr #24
+ orr r6, r6, r7, lsl #8
+ mov r7, r7, lsr #24
+ orr r7, r7, ip, lsl #8
+ str r4, [r1], #0x04
+ str r5, [r1], #0x04
+ str r6, [r1], #0x04
+ str r7, [r1], #0x04
+.Lcopyin_bad3:
+ subs r2, r2, #0x10
+ bge .Lcopyin_bad3_loop16
+
+ adds r2, r2, #0x10
+ ldmfdeq sp!, {r4-r7}
+ RETeq /* Return now if done */
+ subs r2, r2, #0x04
+ sublt r0, r0, #0x01
+ blt .Lcopyin_l4
+
+.Lcopyin_bad3_loop4:
+ mov r4, ip, lsr #24
+ ldrt ip, [r0], #0x04
+ subs r2, r2, #0x04
+ orr r4, r4, ip, lsl #8
+ str r4, [r1], #0x04
+ bge .Lcopyin_bad3_loop4
+ sub r0, r0, #0x01
+
+.Lcopyin_l4:
+ ldmfd sp!, {r4-r7}
+ mov r3, #0x00
+ adds r2, r2, #0x04
+ RETeq
+.Lcopyin_l4_2:
+ rsbs r2, r2, #0x03
+ addne pc, pc, r2, lsl #3
+ nop
+ ldrbt ip, [r0], #0x01
+ strb ip, [r1], #0x01
+ ldrbt ip, [r0], #0x01
+ strb ip, [r1], #0x01
+ ldrbt ip, [r0]
+ strb ip, [r1]
+ RET
+END(copyin)
+
+/*
+ * r0 = kernel space address
+ * r1 = user space address
+ * r2 = length
+ *
+ * Copies bytes from kernel space to user space
+ */
+ENTRY(copyout)
+ cmp r2, #0x00
+ movle r0, #0x00
+ movle pc, lr /* Bail early if length is <= 0 */
+
+ adds r3, r1, r2
+ movcs r0, #EFAULT
+ RETc(cs)
+
+ ldr r12, =(VM_MAXUSER_ADDRESS + 1)
+ cmp r3, r12
+ movcs r0, #EFAULT
+ RETc(cs)
+
+ ldr r3, .L_arm_memcpy
+ ldr r3, [r3]
+ cmp r3, #0
+ beq .Lnormale
+ ldr r3, .L_min_memcpy_size
+ ldr r3, [r3]
+ cmp r2, r3
+ blt .Lnormale
+ stmfd sp!, {r0-r2, r4, lr}
+ mov r3, r0
+ mov r0, r1
+ mov r1, r3
+ mov r3, #1 /* DST_IS_USER */
+ ldr r4, .L_arm_memcpy
+ mov lr, pc
+ ldr pc, [r4]
+ cmp r0, #0
+ ldmfd sp!, {r0-r2, r4, lr}
+ moveq r0, #0
+ RETeq
+
+.Lnormale:
+ stmfd sp!, {r10-r11, lr}
+
+ GET_PCB(r10)
+ ldr r10, [r10]
+
+ mov r3, #0x00
+ adr ip, .Lcopyout_fault
+ ldr r11, [r10, #PCB_ONFAULT]
+ str ip, [r10, #PCB_ONFAULT]
+ bl .Lcopyout_guts
+ str r11, [r10, #PCB_ONFAULT]
+ mov r0, #0x00
+ ldmfd sp!, {r10-r11, pc}
+
+.Lcopyout_fault:
+ ldr r0, =EFAULT
+ str r11, [r10, #PCB_ONFAULT]
+ cmp r3, #0x00
+ ldmfdgt sp!, {r4-r7} /* r3 > 0 Restore r4-r7 */
+ ldmfdlt sp!, {r4-r9} /* r3 < 0 Restore r4-r9 */
+ ldmfd sp!, {r10-r11, pc}
+
+.Lcopyout_guts:
+ pld [r0]
+ /* Word-align the destination buffer */
+ ands ip, r1, #0x03 /* Already word aligned? */
+ beq .Lcopyout_wordaligned /* Yup */
+ rsb ip, ip, #0x04
+ cmp r2, ip /* Enough bytes left to align it? */
+ blt .Lcopyout_l4_2 /* Nope. Just copy bytewise */
+ sub r2, r2, ip
+ rsbs ip, ip, #0x03
+ addne pc, pc, ip, lsl #3
+ nop
+ ldrb ip, [r0], #0x01
+ strbt ip, [r1], #0x01
+ ldrb ip, [r0], #0x01
+ strbt ip, [r1], #0x01
+ ldrb ip, [r0], #0x01
+ strbt ip, [r1], #0x01
+ cmp r2, #0x00 /* All done? */
+ RETeq
+
+ /* Destination buffer is now word aligned */
+.Lcopyout_wordaligned:
+ ands ip, r0, #0x03 /* Is src also word-aligned? */
+ bne .Lcopyout_bad_align /* Nope. Things just got bad */
+ cmp r2, #0x08 /* Less than 8 bytes remaining? */
+ blt .Lcopyout_w_less_than8
+
+ /* Quad-align the destination buffer */
+ tst r0, #0x07 /* Already quad aligned? */
+ ldrne ip, [r0], #0x04
+ subne r2, r2, #0x04
+ strtne ip, [r1], #0x04
+
+ stmfd sp!, {r4-r9} /* Free up some registers */
+ mov r3, #-1 /* Signal restore r4-r9 */
+
+ /* Destination buffer word aligned, source is quad aligned */
+ subs r2, r2, #0x80
+ blt .Lcopyout_w_lessthan128
+
+ /* Copy 128 bytes at a time */
+.Lcopyout_w_loop128:
+ ldrd r4, [r0], #0x08 /* LD:00-07 */
+ pld [r0, #0x18] /* Prefetch 0x20 */
+ ldrd r6, [r0], #0x08 /* LD:08-0f */
+ ldrd r8, [r0], #0x08 /* LD:10-17 */
+ strt r4, [r1], #0x04 /* ST:00-03 */
+ strt r5, [r1], #0x04 /* ST:04-07 */
+ ldrd r4, [r0], #0x08 /* LD:18-1f */
+ strt r6, [r1], #0x04 /* ST:08-0b */
+ strt r7, [r1], #0x04 /* ST:0c-0f */
+ ldrd r6, [r0], #0x08 /* LD:20-27 */
+ pld [r0, #0x18] /* Prefetch 0x40 */
+ strt r8, [r1], #0x04 /* ST:10-13 */
+ strt r9, [r1], #0x04 /* ST:14-17 */
+ ldrd r8, [r0], #0x08 /* LD:28-2f */
+ strt r4, [r1], #0x04 /* ST:18-1b */
+ strt r5, [r1], #0x04 /* ST:1c-1f */
+ ldrd r4, [r0], #0x08 /* LD:30-37 */
+ strt r6, [r1], #0x04 /* ST:20-23 */
+ strt r7, [r1], #0x04 /* ST:24-27 */
+ ldrd r6, [r0], #0x08 /* LD:38-3f */
+ strt r8, [r1], #0x04 /* ST:28-2b */
+ strt r9, [r1], #0x04 /* ST:2c-2f */
+ ldrd r8, [r0], #0x08 /* LD:40-47 */
+ pld [r0, #0x18] /* Prefetch 0x60 */
+ strt r4, [r1], #0x04 /* ST:30-33 */
+ strt r5, [r1], #0x04 /* ST:34-37 */
+ ldrd r4, [r0], #0x08 /* LD:48-4f */
+ strt r6, [r1], #0x04 /* ST:38-3b */
+ strt r7, [r1], #0x04 /* ST:3c-3f */
+ ldrd r6, [r0], #0x08 /* LD:50-57 */
+ strt r8, [r1], #0x04 /* ST:40-43 */
+ strt r9, [r1], #0x04 /* ST:44-47 */
+ ldrd r8, [r0], #0x08 /* LD:58-4f */
+ strt r4, [r1], #0x04 /* ST:48-4b */
+ strt r5, [r1], #0x04 /* ST:4c-4f */
+ ldrd r4, [r0], #0x08 /* LD:60-67 */
+ pld [r0, #0x18] /* Prefetch 0x80 */
+ strt r6, [r1], #0x04 /* ST:50-53 */
+ strt r7, [r1], #0x04 /* ST:54-57 */
+ ldrd r6, [r0], #0x08 /* LD:68-6f */
+ strt r8, [r1], #0x04 /* ST:58-5b */
+ strt r9, [r1], #0x04 /* ST:5c-5f */
+ ldrd r8, [r0], #0x08 /* LD:70-77 */
+ strt r4, [r1], #0x04 /* ST:60-63 */
+ strt r5, [r1], #0x04 /* ST:64-67 */
+ ldrd r4, [r0], #0x08 /* LD:78-7f */
+ strt r6, [r1], #0x04 /* ST:68-6b */
+ strt r7, [r1], #0x04 /* ST:6c-6f */
+ strt r8, [r1], #0x04 /* ST:70-73 */
+ strt r9, [r1], #0x04 /* ST:74-77 */
+ subs r2, r2, #0x80
+ strt r4, [r1], #0x04 /* ST:78-7b */
+ strt r5, [r1], #0x04 /* ST:7c-7f */
+ bge .Lcopyout_w_loop128
+
+.Lcopyout_w_lessthan128:
+ adds r2, r2, #0x80 /* Adjust for extra sub */
+ ldmfdeq sp!, {r4-r9}
+ RETeq /* Return now if done */
+ subs r2, r2, #0x20
+ blt .Lcopyout_w_lessthan32
+
+ /* Copy 32 bytes at a time */
+.Lcopyout_w_loop32:
+ ldrd r4, [r0], #0x08
+ pld [r0, #0x18]
+ ldrd r6, [r0], #0x08
+ ldrd r8, [r0], #0x08
+ strt r4, [r1], #0x04
+ strt r5, [r1], #0x04
+ ldrd r4, [r0], #0x08
+ strt r6, [r1], #0x04
+ strt r7, [r1], #0x04
+ strt r8, [r1], #0x04
+ strt r9, [r1], #0x04
+ subs r2, r2, #0x20
+ strt r4, [r1], #0x04
+ strt r5, [r1], #0x04
+ bge .Lcopyout_w_loop32
+
+.Lcopyout_w_lessthan32:
+ adds r2, r2, #0x20 /* Adjust for extra sub */
+ ldmfdeq sp!, {r4-r9}
+ RETeq /* Return now if done */
+
+ and r4, r2, #0x18
+ rsb r5, r4, #0x18
+ subs r2, r2, r4
+ add pc, pc, r5, lsl #1
+ nop
+
+ /* At least 24 bytes remaining */
+ ldrd r4, [r0], #0x08
+ strt r4, [r1], #0x04
+ strt r5, [r1], #0x04
+ nop
+
+ /* At least 16 bytes remaining */
+ ldrd r4, [r0], #0x08
+ strt r4, [r1], #0x04
+ strt r5, [r1], #0x04
+ nop
+
+ /* At least 8 bytes remaining */
+ ldrd r4, [r0], #0x08
+ strt r4, [r1], #0x04
+ strt r5, [r1], #0x04
+ nop
+
+ /* Less than 8 bytes remaining */
+ ldmfd sp!, {r4-r9}
+ RETeq /* Return now if done */
+ mov r3, #0x00
+
+.Lcopyout_w_less_than8:
+ subs r2, r2, #0x04
+ ldrge ip, [r0], #0x04
+ strtge ip, [r1], #0x04
+ RETeq /* Return now if done */
+ addlt r2, r2, #0x04
+ ldrb ip, [r0], #0x01
+ cmp r2, #0x02
+ ldrbge r2, [r0], #0x01
+ strbt ip, [r1], #0x01
+ ldrbgt ip, [r0]
+ strbtge r2, [r1], #0x01
+ strbtgt ip, [r1]
+ RET
+
+/*
+ * At this point, it has not been possible to word align both buffers.
+ * The destination buffer (r1) is word aligned, but the source buffer
+ * (r0) is not.
+ */
+.Lcopyout_bad_align:
+ stmfd sp!, {r4-r7}
+ mov r3, #0x01
+ bic r0, r0, #0x03
+ cmp ip, #2
+ ldr ip, [r0], #0x04
+ bgt .Lcopyout_bad3
+ beq .Lcopyout_bad2
+ b .Lcopyout_bad1
+
+.Lcopyout_bad1_loop16:
+ mov r4, ip, lsr #8
+ ldr r5, [r0], #0x04
+ pld [r0, #0x018]
+ ldr r6, [r0], #0x04
+ ldr r7, [r0], #0x04
+ ldr ip, [r0], #0x04
+ orr r4, r4, r5, lsl #24
+ mov r5, r5, lsr #8
+ orr r5, r5, r6, lsl #24
+ mov r6, r6, lsr #8
+ orr r6, r6, r7, lsl #24
+ mov r7, r7, lsr #8
+ orr r7, r7, ip, lsl #24
+ strt r4, [r1], #0x04
+ strt r5, [r1], #0x04
+ strt r6, [r1], #0x04
+ strt r7, [r1], #0x04
+.Lcopyout_bad1:
+ subs r2, r2, #0x10
+ bge .Lcopyout_bad1_loop16
+
+ adds r2, r2, #0x10
+ ldmfdeq sp!, {r4-r7}
+ RETeq /* Return now if done */
+ subs r2, r2, #0x04
+ sublt r0, r0, #0x03
+ blt .Lcopyout_l4
+
+.Lcopyout_bad1_loop4:
+ mov r4, ip, lsr #8
+ ldr ip, [r0], #0x04
+ subs r2, r2, #0x04
+ orr r4, r4, ip, lsl #24
+ strt r4, [r1], #0x04
+ bge .Lcopyout_bad1_loop4
+ sub r0, r0, #0x03
+ b .Lcopyout_l4
+
+.Lcopyout_bad2_loop16:
+ mov r4, ip, lsr #16
+ ldr r5, [r0], #0x04
+ pld [r0, #0x018]
+ ldr r6, [r0], #0x04
+ ldr r7, [r0], #0x04
+ ldr ip, [r0], #0x04
+ orr r4, r4, r5, lsl #16
+ mov r5, r5, lsr #16
+ orr r5, r5, r6, lsl #16
+ mov r6, r6, lsr #16
+ orr r6, r6, r7, lsl #16
+ mov r7, r7, lsr #16
+ orr r7, r7, ip, lsl #16
+ strt r4, [r1], #0x04
+ strt r5, [r1], #0x04
+ strt r6, [r1], #0x04
+ strt r7, [r1], #0x04
+.Lcopyout_bad2:
+ subs r2, r2, #0x10
+ bge .Lcopyout_bad2_loop16
+
+ adds r2, r2, #0x10
+ ldmfdeq sp!, {r4-r7}
+ RETeq /* Return now if done */
+ subs r2, r2, #0x04
+ sublt r0, r0, #0x02
+ blt .Lcopyout_l4
+
+.Lcopyout_bad2_loop4:
+ mov r4, ip, lsr #16
+ ldr ip, [r0], #0x04
+ subs r2, r2, #0x04
+ orr r4, r4, ip, lsl #16
+ strt r4, [r1], #0x04
+ bge .Lcopyout_bad2_loop4
+ sub r0, r0, #0x02
+ b .Lcopyout_l4
+
+.Lcopyout_bad3_loop16:
+ mov r4, ip, lsr #24
+ ldr r5, [r0], #0x04
+ pld [r0, #0x018]
+ ldr r6, [r0], #0x04
+ ldr r7, [r0], #0x04
+ ldr ip, [r0], #0x04
+ orr r4, r4, r5, lsl #8
+ mov r5, r5, lsr #24
+ orr r5, r5, r6, lsl #8
+ mov r6, r6, lsr #24
+ orr r6, r6, r7, lsl #8
+ mov r7, r7, lsr #24
+ orr r7, r7, ip, lsl #8
+ strt r4, [r1], #0x04
+ strt r5, [r1], #0x04
+ strt r6, [r1], #0x04
+ strt r7, [r1], #0x04
+.Lcopyout_bad3:
+ subs r2, r2, #0x10
+ bge .Lcopyout_bad3_loop16
+
+ adds r2, r2, #0x10
+ ldmfdeq sp!, {r4-r7}
+ RETeq /* Return now if done */
+ subs r2, r2, #0x04
+ sublt r0, r0, #0x01
+ blt .Lcopyout_l4
+
+.Lcopyout_bad3_loop4:
+ mov r4, ip, lsr #24
+ ldr ip, [r0], #0x04
+ subs r2, r2, #0x04
+ orr r4, r4, ip, lsl #8
+ strt r4, [r1], #0x04
+ bge .Lcopyout_bad3_loop4
+ sub r0, r0, #0x01
+
+.Lcopyout_l4:
+ ldmfd sp!, {r4-r7}
+ mov r3, #0x00
+ adds r2, r2, #0x04
+ RETeq
+.Lcopyout_l4_2:
+ rsbs r2, r2, #0x03
+ addne pc, pc, r2, lsl #3
+ nop
+ ldrb ip, [r0], #0x01
+ strbt ip, [r1], #0x01
+ ldrb ip, [r0], #0x01
+ strbt ip, [r1], #0x01
+ ldrb ip, [r0]
+ strbt ip, [r1]
+ RET
+END(copyout)
+
diff --git a/sys/arm/arm/blockio.S b/sys/arm/arm/blockio.S
new file mode 100644
index 000000000000..eb049c1d6cf2
--- /dev/null
+++ b/sys/arm/arm/blockio.S
@@ -0,0 +1,596 @@
+/* $NetBSD: blockio.S,v 1.5 2002/08/15 01:38:16 briggs Exp $ */
+
+/*-
+ * Copyright (c) 2001 Ben Harris.
+ * Copyright (c) 1994 Mark Brinicombe.
+ * Copyright (c) 1994 Brini.
+ * All rights reserved.
+ *
+ * This code is derived from software written for Brini by Mark Brinicombe
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Brini.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * RiscBSD kernel project
+ *
+ * blockio.S
+ *
+ * optimised block read/write from/to IO routines.
+ *
+ * Created : 08/10/94
+ * Modified : 22/01/99 -- R.Earnshaw
+ * Faster, and small tweaks for StrongARM
+ */
+
+#include <machine/asm.h>
+
+__FBSDID("$FreeBSD$");
+
+ .syntax unified
+
+/*
+ * Read bytes from an I/O address into a block of memory
+ *
+ * r0 = address to read from (IO)
+ * r1 = address to write to (memory)
+ * r2 = length
+ */
+
+/* This code will look very familiar if you've read _memcpy(). */
+ENTRY(read_multi_1)
+ mov ip, sp
+ stmfd sp!, {fp, ip, lr, pc}
+ sub fp, ip, #4
+ subs r2, r2, #4 /* r2 = length - 4 */
+ blt .Lrm1_l4 /* less than 4 bytes */
+ ands r12, r1, #3
+ beq .Lrm1_main /* aligned destination */
+ rsb r12, r12, #4
+ cmp r12, #2
+ ldrb r3, [r0]
+ strb r3, [r1], #1
+ ldrbge r3, [r0]
+ strbge r3, [r1], #1
+ ldrbgt r3, [r0]
+ strbgt r3, [r1], #1
+ subs r2, r2, r12
+ blt .Lrm1_l4
+.Lrm1_main:
+.Lrm1loop:
+ ldrb r3, [r0]
+ ldrb r12, [r0]
+ orr r3, r3, r12, lsl #8
+ ldrb r12, [r0]
+ orr r3, r3, r12, lsl #16
+ ldrb r12, [r0]
+ orr r3, r3, r12, lsl #24
+ str r3, [r1], #4
+ subs r2, r2, #4
+ bge .Lrm1loop
+.Lrm1_l4:
+ adds r2, r2, #4 /* r2 = length again */
+ ldmdbeq fp, {fp, sp, pc}
+ RETeq
+ cmp r2, #2
+ ldrb r3, [r0]
+ strb r3, [r1], #1
+ ldrbge r3, [r0]
+ strbge r3, [r1], #1
+ ldrbgt r3, [r0]
+ strbgt r3, [r1], #1
+ ldmdb fp, {fp, sp, pc}
+END(read_multi_1)
+
+/*
+ * Write bytes to an I/O address from a block of memory
+ *
+ * r0 = address to write to (IO)
+ * r1 = address to read from (memory)
+ * r2 = length
+ */
+
+/* This code will look very familiar if you've read _memcpy(). */
+ENTRY(write_multi_1)
+ mov ip, sp
+ stmfd sp!, {fp, ip, lr, pc}
+ sub fp, ip, #4
+ subs r2, r2, #4 /* r2 = length - 4 */
+ blt .Lwm1_l4 /* less than 4 bytes */
+ ands r12, r1, #3
+ beq .Lwm1_main /* aligned source */
+ rsb r12, r12, #4
+ cmp r12, #2
+ ldrb r3, [r1], #1
+ strb r3, [r0]
+ ldrbge r3, [r1], #1
+ strbge r3, [r0]
+ ldrbgt r3, [r1], #1
+ strbgt r3, [r0]
+ subs r2, r2, r12
+ blt .Lwm1_l4
+.Lwm1_main:
+.Lwm1loop:
+ ldr r3, [r1], #4
+ strb r3, [r0]
+ mov r3, r3, lsr #8
+ strb r3, [r0]
+ mov r3, r3, lsr #8
+ strb r3, [r0]
+ mov r3, r3, lsr #8
+ strb r3, [r0]
+ subs r2, r2, #4
+ bge .Lwm1loop
+.Lwm1_l4:
+ adds r2, r2, #4 /* r2 = length again */
+ ldmdbeq fp, {fp, sp, pc}
+ cmp r2, #2
+ ldrb r3, [r1], #1
+ strb r3, [r0]
+ ldrbge r3, [r1], #1
+ strbge r3, [r0]
+ ldrbgt r3, [r1], #1
+ strbgt r3, [r0]
+ ldmdb fp, {fp, sp, pc}
+END(write_multi_1)
+
+/*
+ * Reads short ints (16 bits) from an I/O address into a block of memory
+ *
+ * r0 = address to read from (IO)
+ * r1 = address to write to (memory)
+ * r2 = length
+ */
+
+ENTRY(insw)
+/* Make sure that we have a positive length */
+ cmp r2, #0x00000000
+ movle pc, lr
+
+/* If the destination address and the size is word aligned, do it fast */
+
+ tst r2, #0x00000001
+ tsteq r1, #0x00000003
+ beq .Lfastinsw
+
+/* Non aligned insw */
+
+.Linswloop:
+ ldr r3, [r0]
+ subs r2, r2, #0x00000001 /* Loop test in load delay slot */
+ strb r3, [r1], #0x0001
+ mov r3, r3, lsr #8
+ strb r3, [r1], #0x0001
+ bgt .Linswloop
+
+ RET
+
+/* Word aligned insw */
+
+.Lfastinsw:
+
+.Lfastinswloop:
+ ldr r3, [r0, #0x0002] /* take advantage of nonaligned
+ * word accesses */
+ ldr ip, [r0]
+ mov r3, r3, lsr #16 /* Put the two shorts together */
+ orr r3, r3, ip, lsl #16
+ str r3, [r1], #0x0004 /* Store */
+ subs r2, r2, #0x00000002 /* Next */
+ bgt .Lfastinswloop
+
+ RET
+END(insw)
+
+/*
+ * Writes short ints (16 bits) from a block of memory to an I/O address
+ *
+ * r0 = address to write to (IO)
+ * r1 = address to read from (memory)
+ * r2 = length
+ */
+
+ENTRY(outsw)
+/* Make sure that we have a positive length */
+ cmp r2, #0x00000000
+ movle pc, lr
+
+/* If the destination address and the size is word aligned, do it fast */
+
+ tst r2, #0x00000001
+ tsteq r1, #0x00000003
+ beq .Lfastoutsw
+
+/* Non aligned outsw */
+
+.Loutswloop:
+ ldrb r3, [r1], #0x0001
+ ldrb ip, [r1], #0x0001
+ subs r2, r2, #0x00000001 /* Loop test in load delay slot */
+ orr r3, r3, ip, lsl #8
+ orr r3, r3, r3, lsl #16
+ str r3, [r0]
+ bgt .Loutswloop
+
+ RET
+
+/* Word aligned outsw */
+
+.Lfastoutsw:
+
+.Lfastoutswloop:
+ ldr r3, [r1], #0x0004 /* r3 = (H)(L) */
+ subs r2, r2, #0x00000002 /* Loop test in load delay slot */
+
+ eor ip, r3, r3, lsr #16 /* ip = (H)(H^L) */
+ eor r3, r3, ip, lsl #16 /* r3 = (H^H^L)(L) = (L)(L) */
+ eor ip, ip, r3, lsr #16 /* ip = (H)(H^L^L) = (H)(H) */
+
+ str r3, [r0]
+ str ip, [r0]
+
+/* mov ip, r3, lsl #16
+ * orr ip, ip, ip, lsr #16
+ * str ip, [r0]
+ *
+ * mov ip, r3, lsr #16
+ * orr ip, ip, ip, lsl #16
+ * str ip, [r0]
+ */
+
+ bgt .Lfastoutswloop
+
+ RET
+END(outsw)
+
+/*
+ * reads short ints (16 bits) from an I/O address into a block of memory
+ * with a length garenteed to be a multiple of 16 bytes
+ * with a word aligned destination address
+ *
+ * r0 = address to read from (IO)
+ * r1 = address to write to (memory)
+ * r2 = length
+ */
+
+ENTRY(insw16)
+/* Make sure that we have a positive length */
+ cmp r2, #0x00000000
+ movle pc, lr
+
+/* If the destination address is word aligned and the size suitably
+ aligned, do it fast */
+
+ tst r2, #0x00000007
+ tsteq r1, #0x00000003
+
+ bne _C_LABEL(insw)
+
+/* Word aligned insw */
+
+ stmfd sp!, {r4,r5,lr}
+
+.Linsw16loop:
+ ldr r3, [r0, #0x0002] /* take advantage of nonaligned
+ * word accesses */
+ ldr lr, [r0]
+ mov r3, r3, lsr #16 /* Put the two shorts together */
+ orr r3, r3, lr, lsl #16
+
+ ldr r4, [r0, #0x0002] /* take advantage of nonaligned
+ * word accesses */
+ ldr lr, [r0]
+ mov r4, r4, lsr #16 /* Put the two shorts together */
+ orr r4, r4, lr, lsl #16
+
+ ldr r5, [r0, #0x0002] /* take advantage of nonaligned
+ * word accesses */
+ ldr lr, [r0]
+ mov r5, r5, lsr #16 /* Put the two shorts together */
+ orr r5, r5, lr, lsl #16
+
+ ldr ip, [r0, #0x0002] /* take advantage of nonaligned
+ * word accesses */
+ ldr lr, [r0]
+ mov ip, ip, lsr #16 /* Put the two shorts together */
+ orr ip, ip, lr, lsl #16
+
+ stmia r1!, {r3-r5,ip}
+ subs r2, r2, #0x00000008 /* Next */
+ bgt .Linsw16loop
+
+ ldmfd sp!, {r4,r5,pc} /* Restore regs and go home */
+END(insw16)
+
+/*
+ * Writes short ints (16 bits) from a block of memory to an I/O address
+ *
+ * r0 = address to write to (IO)
+ * r1 = address to read from (memory)
+ * r2 = length
+ */
+
+ENTRY(outsw16)
+/* Make sure that we have a positive length */
+ cmp r2, #0x00000000
+ movle pc, lr
+
+/* If the destination address is word aligned and the size suitably
+ aligned, do it fast */
+
+ tst r2, #0x00000007
+ tsteq r1, #0x00000003
+
+ bne _C_LABEL(outsw)
+
+/* Word aligned outsw */
+
+ stmfd sp!, {r4,r5,lr}
+
+.Loutsw16loop:
+ ldmia r1!, {r4,r5,ip,lr}
+
+ eor r3, r4, r4, lsl #16 /* r3 = (A^B)(B) */
+ eor r4, r4, r3, lsr #16 /* r4 = (A)(B^A^B) = (A)(A) */
+ eor r3, r3, r4, lsl #16 /* r3 = (A^B^A)(B) = (B)(B) */
+ str r3, [r0]
+ str r4, [r0]
+
+/* mov r3, r4, lsl #16
+ * orr r3, r3, r3, lsr #16
+ * str r3, [r0]
+ *
+ * mov r3, r4, lsr #16
+ * orr r3, r3, r3, lsl #16
+ * str r3, [r0]
+ */
+
+ eor r3, r5, r5, lsl #16 /* r3 = (A^B)(B) */
+ eor r5, r5, r3, lsr #16 /* r4 = (A)(B^A^B) = (A)(A) */
+ eor r3, r3, r5, lsl #16 /* r3 = (A^B^A)(B) = (B)(B) */
+ str r3, [r0]
+ str r5, [r0]
+
+ eor r3, ip, ip, lsl #16 /* r3 = (A^B)(B) */
+ eor ip, ip, r3, lsr #16 /* r4 = (A)(B^A^B) = (A)(A) */
+ eor r3, r3, ip, lsl #16 /* r3 = (A^B^A)(B) = (B)(B) */
+ str r3, [r0]
+ str ip, [r0]
+
+ eor r3, lr, lr, lsl #16 /* r3 = (A^B)(B) */
+ eor lr, lr, r3, lsr #16 /* r4 = (A)(B^A^B) = (A)(A) */
+ eor r3, r3, lr, lsl #16 /* r3 = (A^B^A)(B) = (B)(B) */
+ str r3, [r0]
+ str lr, [r0]
+
+ subs r2, r2, #0x00000008
+ bgt .Loutsw16loop
+
+ ldmfd sp!, {r4,r5,pc} /* and go home */
+END(outsw16)
+
+/*
+ * reads short ints (16 bits) from an I/O address into a block of memory
+ * The I/O address is assumed to be mapped multiple times in a block of
+ * 8 words.
+ * The destination address should be word aligned.
+ *
+ * r0 = address to read from (IO)
+ * r1 = address to write to (memory)
+ * r2 = length
+ */
+
+ENTRY(inswm8)
+/* Make sure that we have a positive length */
+ cmp r2, #0x00000000
+ movle pc, lr
+
+/* If the destination address is word aligned and the size suitably
+ aligned, do it fast */
+
+ tst r1, #0x00000003
+
+ bne _C_LABEL(insw)
+
+/* Word aligned insw */
+
+ stmfd sp!, {r4-r9,lr}
+
+ mov lr, #0xff000000
+ orr lr, lr, #0x00ff0000
+
+.Linswm8_loop8:
+ cmp r2, #8
+ bcc .Linswm8_l8
+
+ ldmia r0, {r3-r9,ip}
+
+ bic r3, r3, lr
+ orr r3, r3, r4, lsl #16
+ bic r5, r5, lr
+ orr r4, r5, r6, lsl #16
+ bic r7, r7, lr
+ orr r5, r7, r8, lsl #16
+ bic r9, r9, lr
+ orr r6, r9, ip, lsl #16
+
+ stmia r1!, {r3-r6}
+
+ subs r2, r2, #0x00000008 /* Next */
+ bne .Linswm8_loop8
+ beq .Linswm8_l1
+
+.Linswm8_l8:
+ cmp r2, #4
+ bcc .Linswm8_l4
+
+ ldmia r0, {r3-r6}
+
+ bic r3, r3, lr
+ orr r3, r3, r4, lsl #16
+ bic r5, r5, lr
+ orr r4, r5, r6, lsl #16
+
+ stmia r1!, {r3-r4}
+
+ subs r2, r2, #0x00000004
+ beq .Linswm8_l1
+
+.Linswm8_l4:
+ cmp r2, #2
+ bcc .Linswm8_l2
+
+ ldmia r0, {r3-r4}
+
+ bic r3, r3, lr
+ orr r3, r3, r4, lsl #16
+ str r3, [r1], #0x0004
+
+ subs r2, r2, #0x00000002
+ beq .Linswm8_l1
+
+.Linswm8_l2:
+ cmp r2, #1
+ bcc .Linswm8_l1
+
+ ldr r3, [r0]
+ subs r2, r2, #0x00000001 /* Test in load delay slot */
+ /* XXX, why don't we use result? */
+
+ strb r3, [r1], #0x0001
+ mov r3, r3, lsr #8
+ strb r3, [r1], #0x0001
+
+
+.Linswm8_l1:
+ ldmfd sp!, {r4-r9,pc} /* And go home */
+END(inswm8)
+
+/*
+ * write short ints (16 bits) to an I/O address from a block of memory
+ * The I/O address is assumed to be mapped multiple times in a block of
+ * 8 words.
+ * The source address should be word aligned.
+ *
+ * r0 = address to read to (IO)
+ * r1 = address to write from (memory)
+ * r2 = length
+ */
+
+ENTRY(outswm8)
+/* Make sure that we have a positive length */
+ cmp r2, #0x00000000
+ movle pc, lr
+
+/* If the destination address is word aligned and the size suitably
+ aligned, do it fast */
+
+ tst r1, #0x00000003
+
+ bne _C_LABEL(outsw)
+
+/* Word aligned outsw */
+
+ stmfd sp!, {r4-r8,lr}
+
+.Loutswm8_loop8:
+ cmp r2, #8
+ bcc .Loutswm8_l8
+
+ ldmia r1!, {r3,r5,r7,ip}
+
+ eor r4, r3, r3, lsr #16 /* r4 = (A)(A^B) */
+ eor r3, r3, r4, lsl #16 /* r3 = (A^A^B)(B) = (B)(B) */
+ eor r4, r4, r3, lsr #16 /* r4 = (A)(B^A^B) = (A)(A) */
+
+ eor r6, r5, r5, lsr #16 /* r6 = (A)(A^B) */
+ eor r5, r5, r6, lsl #16 /* r5 = (A^A^B)(B) = (B)(B) */
+ eor r6, r6, r5, lsr #16 /* r6 = (A)(B^A^B) = (A)(A) */
+
+ eor r8, r7, r7, lsr #16 /* r8 = (A)(A^B) */
+ eor r7, r7, r8, lsl #16 /* r7 = (A^A^B)(B) = (B)(B) */
+ eor r8, r8, r7, lsr #16 /* r8 = (A)(B^A^B) = (A)(A) */
+
+ eor lr, ip, ip, lsr #16 /* lr = (A)(A^B) */
+ eor ip, ip, lr, lsl #16 /* ip = (A^A^B)(B) = (B)(B) */
+ eor lr, lr, ip, lsr #16 /* lr = (A)(B^A^B) = (A)(A) */
+
+ stmia r0, {r3-r8,ip,lr}
+
+ subs r2, r2, #0x00000008 /* Next */
+ bne .Loutswm8_loop8
+ beq .Loutswm8_l1
+
+.Loutswm8_l8:
+ cmp r2, #4
+ bcc .Loutswm8_l4
+
+ ldmia r1!, {r3-r4}
+
+ eor r6, r3, r3, lsr #16 /* r6 = (A)(A^B) */
+ eor r5, r3, r6, lsl #16 /* r5 = (A^A^B)(B) = (B)(B) */
+ eor r6, r6, r5, lsr #16 /* r6 = (A)(B^A^B) = (A)(A) */
+
+ eor r8, r4, r4, lsr #16 /* r8 = (A)(A^B) */
+ eor r7, r4, r8, lsl #16 /* r7 = (A^A^B)(B) = (B)(B) */
+ eor r8, r8, r7, lsr #16 /* r8 = (A)(B^A^B) = (A)(A) */
+
+ stmia r0, {r5-r8}
+
+ subs r2, r2, #0x00000004
+ beq .Loutswm8_l1
+
+.Loutswm8_l4:
+ cmp r2, #2
+ bcc .Loutswm8_l2
+
+ ldr r3, [r1], #0x0004 /* r3 = (A)(B) */
+ subs r2, r2, #0x00000002 /* Done test in Load delay slot */
+
+ eor r5, r3, r3, lsr #16 /* r5 = (A)(A^B)*/
+ eor r4, r3, r5, lsl #16 /* r4 = (A^A^B)(B) = (B)(B) */
+ eor r5, r5, r4, lsr #16 /* r5 = (A)(B^A^B) = (A)(A) */
+
+ stmia r0, {r4, r5}
+
+ beq .Loutswm8_l1
+
+.Loutswm8_l2:
+ cmp r2, #1
+ bcc .Loutswm8_l1
+
+ ldrb r3, [r1], #0x0001
+ ldrb r4, [r1], #0x0001
+ subs r2, r2, #0x00000001 /* Done test in load delay slot */
+ /* XXX This test isn't used? */
+ orr r3, r3, r4, lsl #8
+ orr r3, r3, r3, lsl #16
+ str r3, [r0]
+
+.Loutswm8_l1:
+ ldmfd sp!, {r4-r8,pc} /* And go home */
+END(outswm8)
+
diff --git a/sys/arm/arm/bus_space_asm_generic.S b/sys/arm/arm/bus_space_asm_generic.S
new file mode 100644
index 000000000000..711f92185200
--- /dev/null
+++ b/sys/arm/arm/bus_space_asm_generic.S
@@ -0,0 +1,359 @@
+/* $NetBSD: bus_space_asm_generic.S,v 1.3 2003/03/27 19:46:14 mycroft Exp $ */
+
+/*-
+ * Copyright (c) 1997 Causality Limited.
+ * Copyright (c) 1997 Mark Brinicombe.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Mark Brinicombe
+ * for the NetBSD Project.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <machine/asm.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Generic bus_space functions.
+ */
+
+/*
+ * read single
+ */
+
+ENTRY(generic_bs_r_1)
+ ldrb r0, [r1, r2]
+ RET
+END(generic_bs_r_1)
+
+ENTRY(generic_bs_r_2)
+ ldrh r0, [r1, r2]
+ RET
+END(generic_bs_r_2)
+
+ENTRY(generic_bs_r_4)
+ ldr r0, [r1, r2]
+ RET
+END(generic_bs_r_4)
+
+/*
+ * write single
+ */
+
+ENTRY(generic_bs_w_1)
+ strb r3, [r1, r2]
+ RET
+END(generic_bs_w_1)
+
+ENTRY(generic_bs_w_2)
+ strh r3, [r1, r2]
+ RET
+END(generic_bs_w_2)
+
+ENTRY(generic_bs_w_4)
+ str r3, [r1, r2]
+ RET
+END(generic_bs_w_4)
+
+/*
+ * read multiple
+ */
+
+ENTRY(generic_bs_rm_1)
+ add r0, r1, r2
+ mov r1, r3
+ ldr r2, [sp, #0]
+ teq r2, #0
+ RETeq
+
+1: ldrb r3, [r0]
+ strb r3, [r1], #1
+ subs r2, r2, #1
+ bne 1b
+
+ RET
+END(generic_bs_rm_1)
+
+ENTRY(generic_bs_rm_2)
+ add r0, r1, r2
+ mov r1, r3
+ ldr r2, [sp, #0]
+ teq r2, #0
+ RETeq
+
+1: ldrh r3, [r0]
+ strh r3, [r1], #2
+ subs r2, r2, #1
+ bne 1b
+
+ RET
+END(generic_bs_rm_2)
+
+ENTRY(generic_bs_rm_4)
+ add r0, r1, r2
+ mov r1, r3
+ ldr r2, [sp, #0]
+ teq r2, #0
+ RETeq
+
+1: ldr r3, [r0]
+ str r3, [r1], #4
+ subs r2, r2, #1
+ bne 1b
+
+ RET
+END(generic_bs_rm_4)
+
+/*
+ * write multiple
+ */
+
+ENTRY(generic_bs_wm_1)
+ add r0, r1, r2
+ mov r1, r3
+ ldr r2, [sp, #0]
+ teq r2, #0
+ RETeq
+
+1: ldrb r3, [r1], #1
+ strb r3, [r0]
+ subs r2, r2, #1
+ bne 1b
+
+ RET
+END(generic_bs_wm_1)
+
+ENTRY(generic_bs_wm_2)
+ add r0, r1, r2
+ mov r1, r3
+ ldr r2, [sp, #0]
+ teq r2, #0
+ RETeq
+
+1: ldrh r3, [r1], #2
+ strh r3, [r0]
+ subs r2, r2, #1
+ bne 1b
+
+ RET
+END(generic_bs_wm_2)
+
+ENTRY(generic_bs_wm_4)
+ add r0, r1, r2
+ mov r1, r3
+ ldr r2, [sp, #0]
+ teq r2, #0
+ RETeq
+
+1: ldr r3, [r1], #4
+ str r3, [r0]
+ subs r2, r2, #1
+ bne 1b
+
+ RET
+END(generic_bs_wm_4)
+
+/*
+ * read region
+ */
+
+ENTRY(generic_bs_rr_1)
+ add r0, r1, r2
+ mov r1, r3
+ ldr r2, [sp, #0]
+ teq r2, #0
+ RETeq
+
+1: ldrb r3, [r0], #1
+ strb r3, [r1], #1
+ subs r2, r2, #1
+ bne 1b
+
+ RET
+END(generic_bs_rr_1)
+
+ENTRY(generic_bs_rr_2)
+ add r0, r1, r2
+ mov r1, r3
+ ldr r2, [sp, #0]
+ teq r2, #0
+ RETeq
+
+1: ldrh r3, [r0], #2
+ strh r3, [r1], #2
+ subs r2, r2, #1
+ bne 1b
+
+ RET
+END(generic_bs_rr_2)
+
+ENTRY(generic_bs_rr_4)
+ add r0, r1, r2
+ mov r1, r3
+ ldr r2, [sp, #0]
+ teq r2, #0
+ RETeq
+
+1: ldr r3, [r0], #4
+ str r3, [r1], #4
+ subs r2, r2, #1
+ bne 1b
+
+ RET
+END(generic_bs_rr_4)
+
+/*
+ * write region.
+ */
+
+ENTRY(generic_bs_wr_1)
+ add r0, r1, r2
+ mov r1, r3
+ ldr r2, [sp, #0]
+ teq r2, #0
+ RETeq
+
+1: ldrb r3, [r1], #1
+ strb r3, [r0], #1
+ subs r2, r2, #1
+ bne 1b
+
+ RET
+END(generic_bs_wr_1)
+
+ENTRY(generic_bs_wr_2)
+ add r0, r1, r2
+ mov r1, r3
+ ldr r2, [sp, #0]
+ teq r2, #0
+ RETeq
+
+1: ldrh r3, [r1], #2
+ strh r3, [r0], #2
+ subs r2, r2, #1
+ bne 1b
+
+ RET
+END(generic_bs_wr_2)
+
+ENTRY(generic_bs_wr_4)
+ add r0, r1, r2
+ mov r1, r3
+ ldr r2, [sp, #0]
+ teq r2, #0
+ RETeq
+
+1: ldr r3, [r1], #4
+ str r3, [r0], #4
+ subs r2, r2, #1
+ bne 1b
+
+ RET
+END(generic_bs_wr_4)
+
+/*
+ * set region
+ */
+
+ENTRY(generic_bs_sr_1)
+ add r0, r1, r2
+ mov r1, r3
+ ldr r2, [sp, #0]
+ teq r2, #0
+ RETeq
+
+1: strb r1, [r0], #1
+ subs r2, r2, #1
+ bne 1b
+
+ RET
+END(generic_bs_sr_1)
+
+ENTRY(generic_bs_sr_2)
+ add r0, r1, r2
+ mov r1, r3
+ ldr r2, [sp, #0]
+ teq r2, #0
+ RETeq
+
+1: strh r1, [r0], #2
+ subs r2, r2, #1
+ bne 1b
+
+ RET
+END(generic_bs_sr_2)
+
+ENTRY(generic_bs_sr_4)
+ add r0, r1, r2
+ mov r1, r3
+ ldr r2, [sp, #0]
+ teq r2, #0
+ RETeq
+
+1: str r1, [r0], #4
+ subs r2, r2, #1
+ bne 1b
+
+ RET
+END(generic_bs_sr_4)
+
+/*
+ * copy region
+ */
+
+ENTRY(generic_bs_c_2)
+ add r0, r1, r2
+ ldr r2, [sp, #0]
+ add r1, r2, r3
+ ldr r2, [sp, #4]
+ teq r2, #0
+ RETeq
+
+ cmp r0, r1
+ blt 2f
+
+1: ldrh r3, [r0], #2
+ strh r3, [r1], #2
+ subs r2, r2, #1
+ bne 1b
+
+ RET
+
+2: add r0, r0, r2, lsl #1
+ add r1, r1, r2, lsl #1
+ sub r0, r0, #2
+ sub r1, r1, #2
+
+3: ldrh r3, [r0], #-2
+ strh r3, [r1], #-2
+ subs r2, r2, #1
+ bne 3b
+
+ RET
+END(generic_bs_c_2)
+
diff --git a/sys/arm/arm/bus_space_base.c b/sys/arm/arm/bus_space_base.c
new file mode 100644
index 000000000000..0626f7de8b5b
--- /dev/null
+++ b/sys/arm/arm/bus_space_base.c
@@ -0,0 +1,158 @@
+/*-
+ * Copyright (C) 2008 MARVELL INTERNATIONAL LTD.
+ * All rights reserved.
+ *
+ * Developed by Semihalf.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of MARVELL nor the names of contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <machine/bus.h>
+
+#include "opt_platform.h"
+
+/* Prototypes for all the bus_space structure functions */
+bs_protos(generic);
+
+/*
+ * The bus space tag. This is constant for all instances, so
+ * we never have to explicitly "create" it.
+ */
+static struct bus_space arm_base_bus_space __aligned(CACHE_LINE_SIZE) = {
+ /* privdata is whatever the implementer wants; unused in base tag */
+ .bs_privdata = NULL,
+
+ /* mapping/unmapping */
+ .bs_map = generic_bs_map,
+ .bs_unmap = generic_bs_unmap,
+ .bs_subregion = generic_bs_subregion,
+
+ /* allocation/deallocation */
+ .bs_alloc = generic_bs_alloc,
+ .bs_free = generic_bs_free,
+
+ /* barrier */
+ .bs_barrier = generic_bs_barrier,
+
+ /* read (single) */
+ .bs_r_1 = NULL, /* Use inline code in bus.h */
+ .bs_r_2 = NULL, /* Use inline code in bus.h */
+ .bs_r_4 = NULL, /* Use inline code in bus.h */
+ .bs_r_8 = NULL, /* Use inline code in bus.h */
+
+ /* read multiple */
+ .bs_rm_1 = generic_bs_rm_1,
+ .bs_rm_2 = generic_bs_rm_2,
+ .bs_rm_4 = generic_bs_rm_4,
+ .bs_rm_8 = BS_UNIMPLEMENTED,
+
+ /* read region */
+ .bs_rr_1 = generic_bs_rr_1,
+ .bs_rr_2 = generic_bs_rr_2,
+ .bs_rr_4 = generic_bs_rr_4,
+ .bs_rr_8 = BS_UNIMPLEMENTED,
+
+ /* write (single) */
+ .bs_w_1 = NULL, /* Use inline code in bus.h */
+ .bs_w_2 = NULL, /* Use inline code in bus.h */
+ .bs_w_4 = NULL, /* Use inline code in bus.h */
+ .bs_w_8 = NULL, /* Use inline code in bus.h */
+
+ /* write multiple */
+ .bs_wm_1 = generic_bs_wm_1,
+ .bs_wm_2 = generic_bs_wm_2,
+ .bs_wm_4 = generic_bs_wm_4,
+ .bs_wm_8 = BS_UNIMPLEMENTED,
+
+ /* write region */
+ .bs_wr_1 = generic_bs_wr_1,
+ .bs_wr_2 = generic_bs_wr_2,
+ .bs_wr_4 = generic_bs_wr_4,
+ .bs_wr_8 = BS_UNIMPLEMENTED,
+
+ /* set multiple */
+ .bs_sm_1 = BS_UNIMPLEMENTED,
+ .bs_sm_2 = BS_UNIMPLEMENTED,
+ .bs_sm_4 = BS_UNIMPLEMENTED,
+ .bs_sm_8 = BS_UNIMPLEMENTED,
+
+ /* set region */
+ .bs_sr_1 = generic_bs_sr_1,
+ .bs_sr_2 = generic_bs_sr_2,
+ .bs_sr_4 = generic_bs_sr_4,
+ .bs_sr_8 = BS_UNIMPLEMENTED,
+
+ /* copy */
+ .bs_c_1 = BS_UNIMPLEMENTED,
+ .bs_c_2 = generic_bs_c_2,
+ .bs_c_4 = BS_UNIMPLEMENTED,
+ .bs_c_8 = BS_UNIMPLEMENTED,
+
+ /* read stream (single) */
+ .bs_r_1_s = NULL, /* Use inline code in bus.h */
+ .bs_r_2_s = NULL, /* Use inline code in bus.h */
+ .bs_r_4_s = NULL, /* Use inline code in bus.h */
+ .bs_r_8_s = NULL, /* Use inline code in bus.h */
+
+ /* read multiple stream */
+ .bs_rm_1_s = generic_bs_rm_1,
+ .bs_rm_2_s = generic_bs_rm_2,
+ .bs_rm_4_s = generic_bs_rm_4,
+ .bs_rm_8_s = BS_UNIMPLEMENTED,
+
+ /* read region stream */
+ .bs_rr_1_s = generic_bs_rr_1,
+ .bs_rr_2_s = generic_bs_rr_2,
+ .bs_rr_4_s = generic_bs_rr_4,
+ .bs_rr_8_s = BS_UNIMPLEMENTED,
+
+ /* write stream (single) */
+ .bs_w_1_s = NULL, /* Use inline code in bus.h */
+ .bs_w_2_s = NULL, /* Use inline code in bus.h */
+ .bs_w_4_s = NULL, /* Use inline code in bus.h */
+ .bs_w_8_s = NULL, /* Use inline code in bus.h */
+
+ /* write multiple stream */
+ .bs_wm_1_s = generic_bs_wm_1,
+ .bs_wm_2_s = generic_bs_wm_2,
+ .bs_wm_4_s = generic_bs_wm_4,
+ .bs_wm_8_s = BS_UNIMPLEMENTED,
+
+ /* write region stream */
+ .bs_wr_1_s = generic_bs_wr_1,
+ .bs_wr_2_s = generic_bs_wr_2,
+ .bs_wr_4_s = generic_bs_wr_4,
+ .bs_wr_8_s = BS_UNIMPLEMENTED,
+};
+
+#ifdef FDT
+bus_space_tag_t fdtbus_bs_tag = &arm_base_bus_space;
+#endif
+
diff --git a/sys/arm/arm/bus_space_generic.c b/sys/arm/arm/bus_space_generic.c
new file mode 100644
index 000000000000..f4bf434d6f5f
--- /dev/null
+++ b/sys/arm/arm/bus_space_generic.c
@@ -0,0 +1,134 @@
+/* $NetBSD: obio_space.c,v 1.6 2003/07/15 00:25:05 lukem Exp $ */
+
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 2001, 2002, 2003 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Jason R. Thorpe for Wasabi Systems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the NetBSD Project by
+ * Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/devmap.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_extern.h>
+
+#include <machine/bus.h>
+#include <machine/cpufunc.h>
+
+void
+generic_bs_unimplemented(void)
+{
+
+ panic("unimplemented bus_space function called");
+}
+
+/* Prototypes for all the bus_space structure functions */
+bs_protos(generic);
+
+int
+generic_bs_map(bus_space_tag_t t, bus_addr_t bpa, bus_size_t size, int flags,
+ bus_space_handle_t *bshp)
+{
+ void *va;
+
+ /*
+ * We don't even examine the passed-in flags. For ARM, the CACHEABLE
+ * flag doesn't make sense (we create VM_MEMATTR_DEVICE mappings), and
+ * the LINEAR flag is just implied because we use kva_alloc(size).
+ */
+ if ((va = pmap_mapdev(bpa, size)) == NULL)
+ return (ENOMEM);
+ *bshp = (bus_space_handle_t)va;
+ return (0);
+}
+
+int
+generic_bs_alloc(bus_space_tag_t t, bus_addr_t rstart, bus_addr_t rend, bus_size_t size,
+ bus_size_t alignment, bus_size_t boundary, int flags, bus_addr_t *bpap,
+ bus_space_handle_t *bshp)
+{
+
+ panic("generic_bs_alloc(): not implemented");
+}
+
+void
+generic_bs_unmap(bus_space_tag_t t, bus_space_handle_t h, bus_size_t size)
+{
+
+ pmap_unmapdev((vm_offset_t)h, size);
+}
+
+void
+generic_bs_free(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t size)
+{
+
+ panic("generic_bs_free(): not implemented");
+}
+
+int
+generic_bs_subregion(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
+ bus_size_t size, bus_space_handle_t *nbshp)
+{
+
+ *nbshp = bsh + offset;
+ return (0);
+}
+
+void
+generic_bs_barrier(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
+ bus_size_t len, int flags)
+{
+
+ /*
+ * dsb() will drain the L1 write buffer and establish a memory access
+ * barrier point on platforms where that has meaning. On a write we
+ * also need to drain the L2 write buffer, because most on-chip memory
+ * mapped devices are downstream of the L2 cache. Note that this needs
+ * to be done even for memory mapped as Device type, because while
+ * Device memory is not cached, writes to it are still buffered.
+ */
+ dsb();
+ if (flags & BUS_SPACE_BARRIER_WRITE) {
+ cpu_l2cache_drain_writebuf();
+ }
+}
diff --git a/sys/arm/arm/busdma_machdep.c b/sys/arm/arm/busdma_machdep.c
new file mode 100644
index 000000000000..7d978ca0163d
--- /dev/null
+++ b/sys/arm/arm/busdma_machdep.c
@@ -0,0 +1,1750 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012-2015 Ian Lepore
+ * Copyright (c) 2010 Mark Tinguely
+ * Copyright (c) 2004 Olivier Houchard
+ * Copyright (c) 2002 Peter Grehan
+ * Copyright (c) 1997, 1998 Justin T. Gibbs.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification, immediately at the beginning of the file.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * From i386/busdma_machdep.c 191438 2009-04-23 20:24:19Z jhb
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/bus.h>
+#include <sys/busdma_bufalloc.h>
+#include <sys/counter.h>
+#include <sys/interrupt.h>
+#include <sys/kernel.h>
+#include <sys/ktr.h>
+#include <sys/lock.h>
+#include <sys/memdesc.h>
+#include <sys/proc.h>
+#include <sys/mutex.h>
+#include <sys/sysctl.h>
+#include <sys/uio.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/vm_page.h>
+#include <vm/vm_phys.h>
+#include <vm/vm_map.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_kern.h>
+
+#include <machine/atomic.h>
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/md_var.h>
+
+#define BUSDMA_DCACHE_ALIGN cpuinfo.dcache_line_size
+#define BUSDMA_DCACHE_MASK cpuinfo.dcache_line_mask
+
+#define MAX_BPAGES 64
+#define MAX_DMA_SEGMENTS 4096
+#define BUS_DMA_EXCL_BOUNCE BUS_DMA_BUS2
+#define BUS_DMA_ALIGN_BOUNCE BUS_DMA_BUS3
+#define BUS_DMA_COULD_BOUNCE (BUS_DMA_EXCL_BOUNCE | BUS_DMA_ALIGN_BOUNCE)
+#define BUS_DMA_MIN_ALLOC_COMP BUS_DMA_BUS4
+
+struct bounce_zone;
+
+struct bus_dma_tag {
+ bus_dma_tag_t parent;
+ bus_size_t alignment;
+ bus_addr_t boundary;
+ bus_addr_t lowaddr;
+ bus_addr_t highaddr;
+ bus_dma_filter_t *filter;
+ void *filterarg;
+ bus_size_t maxsize;
+ u_int nsegments;
+ bus_size_t maxsegsz;
+ int flags;
+ int ref_count;
+ int map_count;
+ bus_dma_lock_t *lockfunc;
+ void *lockfuncarg;
+ struct bounce_zone *bounce_zone;
+};
+
+struct bounce_page {
+ vm_offset_t vaddr; /* kva of bounce buffer */
+ bus_addr_t busaddr; /* Physical address */
+ vm_offset_t datavaddr; /* kva of client data */
+ vm_page_t datapage; /* physical page of client data */
+ vm_offset_t dataoffs; /* page offset of client data */
+ bus_size_t datacount; /* client data count */
+ STAILQ_ENTRY(bounce_page) links;
+};
+
+struct sync_list {
+ vm_offset_t vaddr; /* kva of client data */
+ bus_addr_t paddr; /* physical address */
+ vm_page_t pages; /* starting page of client data */
+ bus_size_t datacount; /* client data count */
+};
+
+int busdma_swi_pending;
+
+struct bounce_zone {
+ STAILQ_ENTRY(bounce_zone) links;
+ STAILQ_HEAD(bp_list, bounce_page) bounce_page_list;
+ int total_bpages;
+ int free_bpages;
+ int reserved_bpages;
+ int active_bpages;
+ int total_bounced;
+ int total_deferred;
+ int map_count;
+ bus_size_t alignment;
+ bus_addr_t lowaddr;
+ char zoneid[8];
+ char lowaddrid[20];
+ struct sysctl_ctx_list sysctl_tree;
+ struct sysctl_oid *sysctl_tree_top;
+};
+
+static struct mtx bounce_lock;
+static int total_bpages;
+static int busdma_zonecount;
+static uint32_t tags_total;
+static uint32_t maps_total;
+static uint32_t maps_dmamem;
+static uint32_t maps_coherent;
+static counter_u64_t maploads_total;
+static counter_u64_t maploads_bounced;
+static counter_u64_t maploads_coherent;
+static counter_u64_t maploads_dmamem;
+static counter_u64_t maploads_mbuf;
+static counter_u64_t maploads_physmem;
+
+static STAILQ_HEAD(, bounce_zone) bounce_zone_list;
+
+SYSCTL_NODE(_hw, OID_AUTO, busdma, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
+ "Busdma parameters");
+SYSCTL_UINT(_hw_busdma, OID_AUTO, tags_total, CTLFLAG_RD, &tags_total, 0,
+ "Number of active tags");
+SYSCTL_UINT(_hw_busdma, OID_AUTO, maps_total, CTLFLAG_RD, &maps_total, 0,
+ "Number of active maps");
+SYSCTL_UINT(_hw_busdma, OID_AUTO, maps_dmamem, CTLFLAG_RD, &maps_dmamem, 0,
+ "Number of active maps for bus_dmamem_alloc buffers");
+SYSCTL_UINT(_hw_busdma, OID_AUTO, maps_coherent, CTLFLAG_RD, &maps_coherent, 0,
+ "Number of active maps with BUS_DMA_COHERENT flag set");
+SYSCTL_COUNTER_U64(_hw_busdma, OID_AUTO, maploads_total, CTLFLAG_RD,
+ &maploads_total, "Number of load operations performed");
+SYSCTL_COUNTER_U64(_hw_busdma, OID_AUTO, maploads_bounced, CTLFLAG_RD,
+ &maploads_bounced, "Number of load operations that used bounce buffers");
+SYSCTL_COUNTER_U64(_hw_busdma, OID_AUTO, maploads_coherent, CTLFLAG_RD,
+ &maploads_dmamem, "Number of load operations on BUS_DMA_COHERENT memory");
+SYSCTL_COUNTER_U64(_hw_busdma, OID_AUTO, maploads_dmamem, CTLFLAG_RD,
+ &maploads_dmamem, "Number of load operations on bus_dmamem_alloc buffers");
+SYSCTL_COUNTER_U64(_hw_busdma, OID_AUTO, maploads_mbuf, CTLFLAG_RD,
+ &maploads_mbuf, "Number of load operations for mbufs");
+SYSCTL_COUNTER_U64(_hw_busdma, OID_AUTO, maploads_physmem, CTLFLAG_RD,
+ &maploads_physmem, "Number of load operations on physical buffers");
+SYSCTL_INT(_hw_busdma, OID_AUTO, total_bpages, CTLFLAG_RD, &total_bpages, 0,
+ "Total bounce pages");
+
+struct bus_dmamap {
+ struct bp_list bpages;
+ int pagesneeded;
+ int pagesreserved;
+ bus_dma_tag_t dmat;
+ struct memdesc mem;
+ bus_dmamap_callback_t *callback;
+ void *callback_arg;
+ int flags;
+#define DMAMAP_COHERENT (1 << 0)
+#define DMAMAP_DMAMEM_ALLOC (1 << 1)
+#define DMAMAP_MBUF (1 << 2)
+ STAILQ_ENTRY(bus_dmamap) links;
+ bus_dma_segment_t *segments;
+ int sync_count;
+ struct sync_list slist[];
+};
+
+static STAILQ_HEAD(, bus_dmamap) bounce_map_waitinglist;
+static STAILQ_HEAD(, bus_dmamap) bounce_map_callbacklist;
+
+static void init_bounce_pages(void *dummy);
+static int alloc_bounce_zone(bus_dma_tag_t dmat);
+static int alloc_bounce_pages(bus_dma_tag_t dmat, u_int numpages);
+static int reserve_bounce_pages(bus_dma_tag_t dmat, bus_dmamap_t map,
+ int commit);
+static bus_addr_t add_bounce_page(bus_dma_tag_t dmat, bus_dmamap_t map,
+ vm_offset_t vaddr, bus_addr_t addr, bus_size_t size);
+static void free_bounce_page(bus_dma_tag_t dmat, struct bounce_page *bpage);
+static void _bus_dmamap_count_pages(bus_dma_tag_t dmat, pmap_t pmap,
+ bus_dmamap_t map, void *buf, bus_size_t buflen, int flags);
+static void _bus_dmamap_count_phys(bus_dma_tag_t dmat, bus_dmamap_t map,
+ vm_paddr_t buf, bus_size_t buflen, int flags);
+static int _bus_dmamap_reserve_pages(bus_dma_tag_t dmat, bus_dmamap_t map,
+ int flags);
+static void dma_preread_safe(vm_offset_t va, vm_paddr_t pa, vm_size_t size);
+static void dma_dcache_sync(struct sync_list *sl, bus_dmasync_op_t op);
+
+static busdma_bufalloc_t coherent_allocator; /* Cache of coherent buffers */
+static busdma_bufalloc_t standard_allocator; /* Cache of standard buffers */
+
+MALLOC_DEFINE(M_BUSDMA, "busdma", "busdma metadata");
+MALLOC_DEFINE(M_BOUNCE, "bounce", "busdma bounce pages");
+
+static void
+busdma_init(void *dummy)
+{
+ int uma_flags;
+
+ maploads_total = counter_u64_alloc(M_WAITOK);
+ maploads_bounced = counter_u64_alloc(M_WAITOK);
+ maploads_coherent = counter_u64_alloc(M_WAITOK);
+ maploads_dmamem = counter_u64_alloc(M_WAITOK);
+ maploads_mbuf = counter_u64_alloc(M_WAITOK);
+ maploads_physmem = counter_u64_alloc(M_WAITOK);
+
+ uma_flags = 0;
+
+ /* Create a cache of buffers in standard (cacheable) memory. */
+ standard_allocator = busdma_bufalloc_create("buffer",
+ BUSDMA_DCACHE_ALIGN,/* minimum_alignment */
+ NULL, /* uma_alloc func */
+ NULL, /* uma_free func */
+ uma_flags); /* uma_zcreate_flags */
+
+#ifdef INVARIANTS
+ /*
+ * Force UMA zone to allocate service structures like
+ * slabs using own allocator. uma_debug code performs
+ * atomic ops on uma_slab_t fields and safety of this
+ * operation is not guaranteed for write-back caches
+ */
+ uma_flags = UMA_ZONE_NOTOUCH;
+#endif
+ /*
+ * Create a cache of buffers in uncacheable memory, to implement the
+ * BUS_DMA_COHERENT (and potentially BUS_DMA_NOCACHE) flag.
+ */
+ coherent_allocator = busdma_bufalloc_create("coherent",
+ BUSDMA_DCACHE_ALIGN,/* minimum_alignment */
+ busdma_bufalloc_alloc_uncacheable,
+ busdma_bufalloc_free_uncacheable,
+ uma_flags); /* uma_zcreate_flags */
+}
+
+/*
+ * This init historically used SI_SUB_VM, but now the init code requires
+ * malloc(9) using M_BUSDMA memory and the pcpu zones for counter(9), which get
+ * set up by SI_SUB_KMEM and SI_ORDER_LAST, so we'll go right after that by
+ * using SI_SUB_KMEM+1.
+ */
+SYSINIT(busdma, SI_SUB_KMEM+1, SI_ORDER_FIRST, busdma_init, NULL);
+
+/*
+ * This routine checks the exclusion zone constraints from a tag against the
+ * physical RAM available on the machine. If a tag specifies an exclusion zone
+ * but there's no RAM in that zone, then we avoid allocating resources to bounce
+ * a request, and we can use any memory allocator (as opposed to needing
+ * kmem_alloc_contig() just because it can allocate pages in an address range).
+ *
+ * Most tags have BUS_SPACE_MAXADDR or BUS_SPACE_MAXADDR_32BIT (they are the
+ * same value on 32-bit architectures) as their lowaddr constraint, and we can't
+ * possibly have RAM at an address higher than the highest address we can
+ * express, so we take a fast out.
+ */
+static int
+exclusion_bounce_check(vm_offset_t lowaddr, vm_offset_t highaddr)
+{
+ int i;
+
+ if (lowaddr >= BUS_SPACE_MAXADDR)
+ return (0);
+
+ for (i = 0; phys_avail[i] && phys_avail[i + 1]; i += 2) {
+ if ((lowaddr >= phys_avail[i] && lowaddr < phys_avail[i + 1]) ||
+ (lowaddr < phys_avail[i] && highaddr >= phys_avail[i]))
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Return true if the tag has an exclusion zone that could lead to bouncing.
+ */
+static __inline int
+exclusion_bounce(bus_dma_tag_t dmat)
+{
+
+ return (dmat->flags & BUS_DMA_EXCL_BOUNCE);
+}
+
+/*
+ * Return true if the given address does not fall on the alignment boundary.
+ */
+static __inline int
+alignment_bounce(bus_dma_tag_t dmat, bus_addr_t addr)
+{
+
+ return (addr & (dmat->alignment - 1));
+}
+
+/*
+ * Return true if the DMA should bounce because the start or end does not fall
+ * on a cacheline boundary (which would require a partial cacheline flush).
+ * COHERENT memory doesn't trigger cacheline flushes. Memory allocated by
+ * bus_dmamem_alloc() is always aligned to cacheline boundaries, and there's a
+ * strict rule that such memory cannot be accessed by the CPU while DMA is in
+ * progress (or by multiple DMA engines at once), so that it's always safe to do
+ * full cacheline flushes even if that affects memory outside the range of a
+ * given DMA operation that doesn't involve the full allocated buffer. If we're
+ * mapping an mbuf, that follows the same rules as a buffer we allocated.
+ */
+static __inline int
+cacheline_bounce(bus_dmamap_t map, bus_addr_t addr, bus_size_t size)
+{
+
+ if (map->flags & (DMAMAP_DMAMEM_ALLOC | DMAMAP_COHERENT | DMAMAP_MBUF))
+ return (0);
+ return ((addr | size) & BUSDMA_DCACHE_MASK);
+}
+
+/*
+ * Return true if we might need to bounce the DMA described by addr and size.
+ *
+ * This is used to quick-check whether we need to do the more expensive work of
+ * checking the DMA page-by-page looking for alignment and exclusion bounces.
+ *
+ * Note that the addr argument might be either virtual or physical. It doesn't
+ * matter because we only look at the low-order bits, which are the same in both
+ * address spaces and maximum alignment of generic buffer is limited up to page
+ * size.
+ * Bouncing of buffers allocated by bus_dmamem_alloc()is not necessary, these
+ * always comply with the required rules (alignment, boundary, and address
+ * range).
+ */
+static __inline int
+might_bounce(bus_dma_tag_t dmat, bus_dmamap_t map, bus_addr_t addr,
+ bus_size_t size)
+{
+
+ KASSERT(map->flags & DMAMAP_DMAMEM_ALLOC ||
+ dmat->alignment <= PAGE_SIZE,
+ ("%s: unsupported alignment (0x%08lx) for buffer not "
+ "allocated by bus_dmamem_alloc()",
+ __func__, dmat->alignment));
+
+ return (!(map->flags & DMAMAP_DMAMEM_ALLOC) &&
+ ((dmat->flags & BUS_DMA_EXCL_BOUNCE) ||
+ alignment_bounce(dmat, addr) ||
+ cacheline_bounce(map, addr, size)));
+}
+
+/*
+ * Return true if we must bounce the DMA described by paddr and size.
+ *
+ * Bouncing can be triggered by DMA that doesn't begin and end on cacheline
+ * boundaries, or doesn't begin on an alignment boundary, or falls within the
+ * exclusion zone of any tag in the ancestry chain.
+ *
+ * For exclusions, walk the chain of tags comparing paddr to the exclusion zone
+ * within each tag. If the tag has a filter function, use it to decide whether
+ * the DMA needs to bounce, otherwise any DMA within the zone bounces.
+ */
+static int
+must_bounce(bus_dma_tag_t dmat, bus_dmamap_t map, bus_addr_t paddr,
+ bus_size_t size)
+{
+
+ if (cacheline_bounce(map, paddr, size))
+ return (1);
+
+ /*
+ * The tag already contains ancestors' alignment restrictions so this
+ * check doesn't need to be inside the loop.
+ */
+ if (alignment_bounce(dmat, paddr))
+ return (1);
+
+ /*
+ * Even though each tag has an exclusion zone that is a superset of its
+ * own and all its ancestors' exclusions, the exclusion zone of each tag
+ * up the chain must be checked within the loop, because the busdma
+ * rules say the filter function is called only when the address lies
+ * within the low-highaddr range of the tag that filterfunc belongs to.
+ */
+ while (dmat != NULL && exclusion_bounce(dmat)) {
+ if ((paddr >= dmat->lowaddr && paddr <= dmat->highaddr) &&
+ (dmat->filter == NULL ||
+ dmat->filter(dmat->filterarg, paddr) != 0))
+ return (1);
+ dmat = dmat->parent;
+ }
+
+ return (0);
+}
+
+/*
+ * Convenience function for manipulating driver locks from busdma (during
+ * busdma_swi, for example). Drivers that don't provide their own locks
+ * should specify &Giant to dmat->lockfuncarg. Drivers that use their own
+ * non-mutex locking scheme don't have to use this at all.
+ */
+void
+busdma_lock_mutex(void *arg, bus_dma_lock_op_t op)
+{
+ struct mtx *dmtx;
+
+ dmtx = (struct mtx *)arg;
+ switch (op) {
+ case BUS_DMA_LOCK:
+ mtx_lock(dmtx);
+ break;
+ case BUS_DMA_UNLOCK:
+ mtx_unlock(dmtx);
+ break;
+ default:
+ panic("Unknown operation 0x%x for busdma_lock_mutex!", op);
+ }
+}
+
+/*
+ * dflt_lock should never get called. It gets put into the dma tag when
+ * lockfunc == NULL, which is only valid if the maps that are associated
+ * with the tag are meant to never be defered.
+ * XXX Should have a way to identify which driver is responsible here.
+ */
+static void
+dflt_lock(void *arg, bus_dma_lock_op_t op)
+{
+
+ panic("driver error: busdma dflt_lock called");
+}
+
+/*
+ * Allocate a device specific dma_tag.
+ */
+int
+bus_dma_tag_create(bus_dma_tag_t parent, bus_size_t alignment,
+ bus_addr_t boundary, bus_addr_t lowaddr, bus_addr_t highaddr,
+ bus_dma_filter_t *filter, void *filterarg, bus_size_t maxsize,
+ int nsegments, bus_size_t maxsegsz, int flags, bus_dma_lock_t *lockfunc,
+ void *lockfuncarg, bus_dma_tag_t *dmat)
+{
+ bus_dma_tag_t newtag;
+ int error = 0;
+
+ /* Basic sanity checking. */
+ KASSERT(boundary == 0 || powerof2(boundary),
+ ("dma tag boundary %lu, must be a power of 2", boundary));
+ KASSERT(boundary == 0 || boundary >= maxsegsz,
+ ("dma tag boundary %lu is < maxsegsz %lu\n", boundary, maxsegsz));
+ KASSERT(alignment != 0 && powerof2(alignment),
+ ("dma tag alignment %lu, must be non-zero power of 2", alignment));
+ KASSERT(maxsegsz != 0, ("dma tag maxsegsz must not be zero"));
+
+ /* Return a NULL tag on failure */
+ *dmat = NULL;
+
+ newtag = (bus_dma_tag_t)malloc(sizeof(*newtag), M_BUSDMA,
+ M_ZERO | M_NOWAIT);
+ if (newtag == NULL) {
+ CTR4(KTR_BUSDMA, "%s returned tag %p tag flags 0x%x error %d",
+ __func__, newtag, 0, error);
+ return (ENOMEM);
+ }
+
+ newtag->parent = parent;
+ newtag->alignment = alignment;
+ newtag->boundary = boundary;
+ newtag->lowaddr = trunc_page((vm_paddr_t)lowaddr) + (PAGE_SIZE - 1);
+ newtag->highaddr = trunc_page((vm_paddr_t)highaddr) +
+ (PAGE_SIZE - 1);
+ newtag->filter = filter;
+ newtag->filterarg = filterarg;
+ newtag->maxsize = maxsize;
+ newtag->nsegments = nsegments;
+ newtag->maxsegsz = maxsegsz;
+ newtag->flags = flags;
+ newtag->ref_count = 1; /* Count ourself */
+ newtag->map_count = 0;
+ if (lockfunc != NULL) {
+ newtag->lockfunc = lockfunc;
+ newtag->lockfuncarg = lockfuncarg;
+ } else {
+ newtag->lockfunc = dflt_lock;
+ newtag->lockfuncarg = NULL;
+ }
+
+ /* Take into account any restrictions imposed by our parent tag */
+ if (parent != NULL) {
+ newtag->lowaddr = MIN(parent->lowaddr, newtag->lowaddr);
+ newtag->highaddr = MAX(parent->highaddr, newtag->highaddr);
+ newtag->alignment = MAX(parent->alignment, newtag->alignment);
+ newtag->flags |= parent->flags & BUS_DMA_COULD_BOUNCE;
+ newtag->flags |= parent->flags & BUS_DMA_COHERENT;
+ if (newtag->boundary == 0)
+ newtag->boundary = parent->boundary;
+ else if (parent->boundary != 0)
+ newtag->boundary = MIN(parent->boundary,
+ newtag->boundary);
+ if (newtag->filter == NULL) {
+ /*
+ * Short circuit to looking at our parent directly
+ * since we have encapsulated all of its information
+ */
+ newtag->filter = parent->filter;
+ newtag->filterarg = parent->filterarg;
+ newtag->parent = parent->parent;
+ }
+ if (newtag->parent != NULL)
+ atomic_add_int(&parent->ref_count, 1);
+ }
+
+ if (exclusion_bounce_check(newtag->lowaddr, newtag->highaddr))
+ newtag->flags |= BUS_DMA_EXCL_BOUNCE;
+ if (alignment_bounce(newtag, 1))
+ newtag->flags |= BUS_DMA_ALIGN_BOUNCE;
+
+ /*
+ * Any request can auto-bounce due to cacheline alignment, in addition
+ * to any alignment or boundary specifications in the tag, so if the
+ * ALLOCNOW flag is set, there's always work to do.
+ */
+ if ((flags & BUS_DMA_ALLOCNOW) != 0) {
+ struct bounce_zone *bz;
+ /*
+ * Round size up to a full page, and add one more page because
+ * there can always be one more boundary crossing than the
+ * number of pages in a transfer.
+ */
+ maxsize = roundup2(maxsize, PAGE_SIZE) + PAGE_SIZE;
+
+ if ((error = alloc_bounce_zone(newtag)) != 0) {
+ free(newtag, M_BUSDMA);
+ return (error);
+ }
+ bz = newtag->bounce_zone;
+
+ if (ptoa(bz->total_bpages) < maxsize) {
+ int pages;
+
+ pages = atop(maxsize) - bz->total_bpages;
+
+ /* Add pages to our bounce pool */
+ if (alloc_bounce_pages(newtag, pages) < pages)
+ error = ENOMEM;
+ }
+ /* Performed initial allocation */
+ newtag->flags |= BUS_DMA_MIN_ALLOC_COMP;
+ } else
+ newtag->bounce_zone = NULL;
+
+ if (error != 0) {
+ free(newtag, M_BUSDMA);
+ } else {
+ atomic_add_32(&tags_total, 1);
+ *dmat = newtag;
+ }
+ CTR4(KTR_BUSDMA, "%s returned tag %p tag flags 0x%x error %d",
+ __func__, newtag, (newtag != NULL ? newtag->flags : 0), error);
+ return (error);
+}
+
+void
+bus_dma_template_clone(bus_dma_template_t *t, bus_dma_tag_t dmat)
+{
+
+ if (t == NULL || dmat == NULL)
+ return;
+
+ t->parent = dmat->parent;
+ t->alignment = dmat->alignment;
+ t->boundary = dmat->boundary;
+ t->lowaddr = dmat->lowaddr;
+ t->highaddr = dmat->highaddr;
+ t->maxsize = dmat->maxsize;
+ t->nsegments = dmat->nsegments;
+ t->maxsegsize = dmat->maxsegsz;
+ t->flags = dmat->flags;
+ t->lockfunc = dmat->lockfunc;
+ t->lockfuncarg = dmat->lockfuncarg;
+}
+
+int
+bus_dma_tag_set_domain(bus_dma_tag_t dmat, int domain)
+{
+
+ return (0);
+}
+
+int
+bus_dma_tag_destroy(bus_dma_tag_t dmat)
+{
+ bus_dma_tag_t dmat_copy;
+ int error;
+
+ error = 0;
+ dmat_copy = dmat;
+
+ if (dmat != NULL) {
+ if (dmat->map_count != 0) {
+ error = EBUSY;
+ goto out;
+ }
+
+ while (dmat != NULL) {
+ bus_dma_tag_t parent;
+
+ parent = dmat->parent;
+ atomic_subtract_int(&dmat->ref_count, 1);
+ if (dmat->ref_count == 0) {
+ atomic_subtract_32(&tags_total, 1);
+ free(dmat, M_BUSDMA);
+ /*
+ * Last reference count, so
+ * release our reference
+ * count on our parent.
+ */
+ dmat = parent;
+ } else
+ dmat = NULL;
+ }
+ }
+out:
+ CTR3(KTR_BUSDMA, "%s tag %p error %d", __func__, dmat_copy, error);
+ return (error);
+}
+
+static int
+allocate_bz_and_pages(bus_dma_tag_t dmat, bus_dmamap_t mapp)
+{
+ struct bounce_zone *bz;
+ int maxpages;
+ int error;
+
+ if (dmat->bounce_zone == NULL)
+ if ((error = alloc_bounce_zone(dmat)) != 0)
+ return (error);
+ bz = dmat->bounce_zone;
+ /* Initialize the new map */
+ STAILQ_INIT(&(mapp->bpages));
+
+ /*
+ * Attempt to add pages to our pool on a per-instance basis up to a sane
+ * limit. Even if the tag isn't flagged as COULD_BOUNCE due to
+ * alignment and boundary constraints, it could still auto-bounce due to
+ * cacheline alignment, which requires at most two bounce pages.
+ */
+ if (dmat->flags & BUS_DMA_COULD_BOUNCE)
+ maxpages = MAX_BPAGES;
+ else
+ maxpages = 2 * bz->map_count;
+ if ((dmat->flags & BUS_DMA_MIN_ALLOC_COMP) == 0 ||
+ (bz->map_count > 0 && bz->total_bpages < maxpages)) {
+ int pages;
+
+ pages = atop(roundup2(dmat->maxsize, PAGE_SIZE)) + 1;
+ pages = MIN(maxpages - bz->total_bpages, pages);
+ pages = MAX(pages, 2);
+ if (alloc_bounce_pages(dmat, pages) < pages)
+ return (ENOMEM);
+
+ if ((dmat->flags & BUS_DMA_MIN_ALLOC_COMP) == 0)
+ dmat->flags |= BUS_DMA_MIN_ALLOC_COMP;
+ }
+ bz->map_count++;
+ return (0);
+}
+
+static bus_dmamap_t
+allocate_map(bus_dma_tag_t dmat, int mflags)
+{
+ int mapsize, segsize;
+ bus_dmamap_t map;
+
+ /*
+ * Allocate the map. The map structure ends with an embedded
+ * variable-sized array of sync_list structures. Following that
+ * we allocate enough extra space to hold the array of bus_dma_segments.
+ */
+ KASSERT(dmat->nsegments <= MAX_DMA_SEGMENTS,
+ ("cannot allocate %u dma segments (max is %u)",
+ dmat->nsegments, MAX_DMA_SEGMENTS));
+ segsize = sizeof(struct bus_dma_segment) * dmat->nsegments;
+ mapsize = sizeof(*map) + sizeof(struct sync_list) * dmat->nsegments;
+ map = malloc(mapsize + segsize, M_BUSDMA, mflags | M_ZERO);
+ if (map == NULL) {
+ CTR3(KTR_BUSDMA, "%s: tag %p error %d", __func__, dmat, ENOMEM);
+ return (NULL);
+ }
+ map->segments = (bus_dma_segment_t *)((uintptr_t)map + mapsize);
+ STAILQ_INIT(&map->bpages);
+ return (map);
+}
+
+/*
+ * Allocate a handle for mapping from kva/uva/physical
+ * address space into bus device space.
+ */
+int
+bus_dmamap_create(bus_dma_tag_t dmat, int flags, bus_dmamap_t *mapp)
+{
+ bus_dmamap_t map;
+ int error = 0;
+
+ *mapp = map = allocate_map(dmat, M_NOWAIT);
+ if (map == NULL) {
+ CTR3(KTR_BUSDMA, "%s: tag %p error %d", __func__, dmat, ENOMEM);
+ return (ENOMEM);
+ }
+
+ /*
+ * Bouncing might be required if the driver asks for an exclusion
+ * region, a data alignment that is stricter than 1, or DMA that begins
+ * or ends with a partial cacheline. Whether bouncing will actually
+ * happen can't be known until mapping time, but we need to pre-allocate
+ * resources now because we might not be allowed to at mapping time.
+ */
+ error = allocate_bz_and_pages(dmat, map);
+ if (error != 0) {
+ free(map, M_BUSDMA);
+ *mapp = NULL;
+ return (error);
+ }
+ if (map->flags & DMAMAP_COHERENT)
+ atomic_add_32(&maps_coherent, 1);
+ atomic_add_32(&maps_total, 1);
+ dmat->map_count++;
+
+ return (0);
+}
+
+/*
+ * Destroy a handle for mapping from kva/uva/physical
+ * address space into bus device space.
+ */
+int
+bus_dmamap_destroy(bus_dma_tag_t dmat, bus_dmamap_t map)
+{
+
+ if (STAILQ_FIRST(&map->bpages) != NULL || map->sync_count != 0) {
+ CTR3(KTR_BUSDMA, "%s: tag %p error %d",
+ __func__, dmat, EBUSY);
+ return (EBUSY);
+ }
+ if (dmat->bounce_zone)
+ dmat->bounce_zone->map_count--;
+ if (map->flags & DMAMAP_COHERENT)
+ atomic_subtract_32(&maps_coherent, 1);
+ atomic_subtract_32(&maps_total, 1);
+ free(map, M_BUSDMA);
+ dmat->map_count--;
+ CTR2(KTR_BUSDMA, "%s: tag %p error 0", __func__, dmat);
+ return (0);
+}
+
+/*
+ * Allocate a piece of memory that can be efficiently mapped into bus device
+ * space based on the constraints listed in the dma tag. Returns a pointer to
+ * the allocated memory, and a pointer to an associated bus_dmamap.
+ */
+int
+bus_dmamem_alloc(bus_dma_tag_t dmat, void **vaddr, int flags,
+ bus_dmamap_t *mapp)
+{
+ busdma_bufalloc_t ba;
+ struct busdma_bufzone *bufzone;
+ bus_dmamap_t map;
+ vm_memattr_t memattr;
+ int mflags;
+
+ if (flags & BUS_DMA_NOWAIT)
+ mflags = M_NOWAIT;
+ else
+ mflags = M_WAITOK;
+ if (flags & BUS_DMA_ZERO)
+ mflags |= M_ZERO;
+
+ *mapp = map = allocate_map(dmat, mflags);
+ if (map == NULL) {
+ CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d",
+ __func__, dmat, dmat->flags, ENOMEM);
+ return (ENOMEM);
+ }
+ map->flags = DMAMAP_DMAMEM_ALLOC;
+
+ /* For coherent memory, set the map flag that disables sync ops. */
+ if (flags & BUS_DMA_COHERENT)
+ map->flags |= DMAMAP_COHERENT;
+
+ /*
+ * Choose a busdma buffer allocator based on memory type flags.
+ * If the tag's COHERENT flag is set, that means normal memory
+ * is already coherent, use the normal allocator.
+ */
+ if ((flags & BUS_DMA_COHERENT) &&
+ ((dmat->flags & BUS_DMA_COHERENT) == 0)) {
+ memattr = VM_MEMATTR_UNCACHEABLE;
+ ba = coherent_allocator;
+ } else {
+ memattr = VM_MEMATTR_DEFAULT;
+ ba = standard_allocator;
+ }
+
+ /*
+ * Try to find a bufzone in the allocator that holds a cache of buffers
+ * of the right size for this request. If the buffer is too big to be
+ * held in the allocator cache, this returns NULL.
+ */
+ bufzone = busdma_bufalloc_findzone(ba, dmat->maxsize);
+
+ /*
+ * Allocate the buffer from the uma(9) allocator if...
+ * - It's small enough to be in the allocator (bufzone not NULL).
+ * - The alignment constraint isn't larger than the allocation size
+ * (the allocator aligns buffers to their size boundaries).
+ * - There's no need to handle lowaddr/highaddr exclusion zones.
+ * else allocate non-contiguous pages if...
+ * - The page count that could get allocated doesn't exceed
+ * nsegments also when the maximum segment size is less
+ * than PAGE_SIZE.
+ * - The alignment constraint isn't larger than a page boundary.
+ * - There are no boundary-crossing constraints.
+ * else allocate a block of contiguous pages because one or more of the
+ * constraints is something that only the contig allocator can fulfill.
+ */
+ if (bufzone != NULL && dmat->alignment <= bufzone->size &&
+ !exclusion_bounce(dmat)) {
+ *vaddr = uma_zalloc(bufzone->umazone, mflags);
+ } else if (dmat->nsegments >=
+ howmany(dmat->maxsize, MIN(dmat->maxsegsz, PAGE_SIZE)) &&
+ dmat->alignment <= PAGE_SIZE &&
+ (dmat->boundary % PAGE_SIZE) == 0) {
+ *vaddr = (void *)kmem_alloc_attr(dmat->maxsize, mflags, 0,
+ dmat->lowaddr, memattr);
+ } else {
+ *vaddr = (void *)kmem_alloc_contig(dmat->maxsize, mflags, 0,
+ dmat->lowaddr, dmat->alignment, dmat->boundary, memattr);
+ }
+ if (*vaddr == NULL) {
+ CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d",
+ __func__, dmat, dmat->flags, ENOMEM);
+ free(map, M_BUSDMA);
+ *mapp = NULL;
+ return (ENOMEM);
+ }
+ if (map->flags & DMAMAP_COHERENT)
+ atomic_add_32(&maps_coherent, 1);
+ atomic_add_32(&maps_dmamem, 1);
+ atomic_add_32(&maps_total, 1);
+ dmat->map_count++;
+
+ CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d",
+ __func__, dmat, dmat->flags, 0);
+ return (0);
+}
+
+/*
+ * Free a piece of memory that was allocated via bus_dmamem_alloc, along with
+ * its associated map.
+ */
+void
+bus_dmamem_free(bus_dma_tag_t dmat, void *vaddr, bus_dmamap_t map)
+{
+ struct busdma_bufzone *bufzone;
+ busdma_bufalloc_t ba;
+
+ if ((map->flags & DMAMAP_COHERENT) &&
+ ((dmat->flags & BUS_DMA_COHERENT) == 0))
+ ba = coherent_allocator;
+ else
+ ba = standard_allocator;
+
+ bufzone = busdma_bufalloc_findzone(ba, dmat->maxsize);
+
+ if (bufzone != NULL && dmat->alignment <= bufzone->size &&
+ !exclusion_bounce(dmat))
+ uma_zfree(bufzone->umazone, vaddr);
+ else
+ kmem_free((vm_offset_t)vaddr, dmat->maxsize);
+
+ dmat->map_count--;
+ if (map->flags & DMAMAP_COHERENT)
+ atomic_subtract_32(&maps_coherent, 1);
+ atomic_subtract_32(&maps_total, 1);
+ atomic_subtract_32(&maps_dmamem, 1);
+ free(map, M_BUSDMA);
+ CTR3(KTR_BUSDMA, "%s: tag %p flags 0x%x", __func__, dmat, dmat->flags);
+}
+
+static void
+_bus_dmamap_count_phys(bus_dma_tag_t dmat, bus_dmamap_t map, vm_paddr_t buf,
+ bus_size_t buflen, int flags)
+{
+ bus_addr_t curaddr;
+ bus_size_t sgsize;
+
+ if (map->pagesneeded == 0) {
+ CTR5(KTR_BUSDMA, "lowaddr= %d, boundary= %d, alignment= %d"
+ " map= %p, pagesneeded= %d",
+ dmat->lowaddr, dmat->boundary, dmat->alignment,
+ map, map->pagesneeded);
+ /*
+ * Count the number of bounce pages
+ * needed in order to complete this transfer
+ */
+ curaddr = buf;
+ while (buflen != 0) {
+ sgsize = MIN(buflen, dmat->maxsegsz);
+ if (must_bounce(dmat, map, curaddr, sgsize) != 0) {
+ sgsize = MIN(sgsize,
+ PAGE_SIZE - (curaddr & PAGE_MASK));
+ map->pagesneeded++;
+ }
+ curaddr += sgsize;
+ buflen -= sgsize;
+ }
+ CTR1(KTR_BUSDMA, "pagesneeded= %d", map->pagesneeded);
+ }
+}
+
+static void
+_bus_dmamap_count_pages(bus_dma_tag_t dmat, pmap_t pmap, bus_dmamap_t map,
+ void *buf, bus_size_t buflen, int flags)
+{
+ vm_offset_t vaddr;
+ vm_offset_t vendaddr;
+ bus_addr_t paddr;
+
+ if (map->pagesneeded == 0) {
+ CTR5(KTR_BUSDMA, "lowaddr= %d, boundary= %d, alignment= %d"
+ " map= %p, pagesneeded= %d",
+ dmat->lowaddr, dmat->boundary, dmat->alignment,
+ map, map->pagesneeded);
+ /*
+ * Count the number of bounce pages
+ * needed in order to complete this transfer
+ */
+ vaddr = (vm_offset_t)buf;
+ vendaddr = (vm_offset_t)buf + buflen;
+
+ while (vaddr < vendaddr) {
+ if (__predict_true(pmap == kernel_pmap))
+ paddr = pmap_kextract(vaddr);
+ else
+ paddr = pmap_extract(pmap, vaddr);
+ if (must_bounce(dmat, map, paddr,
+ min(vendaddr - vaddr, (PAGE_SIZE - ((vm_offset_t)vaddr &
+ PAGE_MASK)))) != 0) {
+ map->pagesneeded++;
+ }
+ vaddr += (PAGE_SIZE - ((vm_offset_t)vaddr & PAGE_MASK));
+ }
+ CTR1(KTR_BUSDMA, "pagesneeded= %d", map->pagesneeded);
+ }
+}
+
+static int
+_bus_dmamap_reserve_pages(bus_dma_tag_t dmat, bus_dmamap_t map, int flags)
+{
+
+ /* Reserve Necessary Bounce Pages */
+ mtx_lock(&bounce_lock);
+ if (flags & BUS_DMA_NOWAIT) {
+ if (reserve_bounce_pages(dmat, map, 0) != 0) {
+ map->pagesneeded = 0;
+ mtx_unlock(&bounce_lock);
+ return (ENOMEM);
+ }
+ } else {
+ if (reserve_bounce_pages(dmat, map, 1) != 0) {
+ /* Queue us for resources */
+ STAILQ_INSERT_TAIL(&bounce_map_waitinglist, map, links);
+ mtx_unlock(&bounce_lock);
+ return (EINPROGRESS);
+ }
+ }
+ mtx_unlock(&bounce_lock);
+
+ return (0);
+}
+
+/*
+ * Add a single contiguous physical range to the segment list.
+ */
+static int
+_bus_dmamap_addseg(bus_dma_tag_t dmat, bus_dmamap_t map, bus_addr_t curaddr,
+ bus_size_t sgsize, bus_dma_segment_t *segs, int *segp)
+{
+ bus_addr_t baddr, bmask;
+ int seg;
+
+ /*
+ * Make sure we don't cross any boundaries.
+ */
+ bmask = ~(dmat->boundary - 1);
+ if (dmat->boundary > 0) {
+ baddr = (curaddr + dmat->boundary) & bmask;
+ if (sgsize > (baddr - curaddr))
+ sgsize = (baddr - curaddr);
+ }
+
+ /*
+ * Insert chunk into a segment, coalescing with
+ * previous segment if possible.
+ */
+ seg = *segp;
+ if (seg == -1) {
+ seg = 0;
+ segs[seg].ds_addr = curaddr;
+ segs[seg].ds_len = sgsize;
+ } else {
+ if (curaddr == segs[seg].ds_addr + segs[seg].ds_len &&
+ (segs[seg].ds_len + sgsize) <= dmat->maxsegsz &&
+ (dmat->boundary == 0 ||
+ (segs[seg].ds_addr & bmask) == (curaddr & bmask)))
+ segs[seg].ds_len += sgsize;
+ else {
+ if (++seg >= dmat->nsegments)
+ return (0);
+ segs[seg].ds_addr = curaddr;
+ segs[seg].ds_len = sgsize;
+ }
+ }
+ *segp = seg;
+ return (sgsize);
+}
+
+/*
+ * Utility function to load a physical buffer. segp contains
+ * the starting segment on entrace, and the ending segment on exit.
+ */
+int
+_bus_dmamap_load_phys(bus_dma_tag_t dmat, bus_dmamap_t map, vm_paddr_t buf,
+ bus_size_t buflen, int flags, bus_dma_segment_t *segs, int *segp)
+{
+ bus_addr_t curaddr;
+ bus_addr_t sl_end = 0;
+ bus_size_t sgsize;
+ struct sync_list *sl;
+ int error;
+
+ if (segs == NULL)
+ segs = map->segments;
+
+ counter_u64_add(maploads_total, 1);
+ counter_u64_add(maploads_physmem, 1);
+
+ if (might_bounce(dmat, map, (bus_addr_t)buf, buflen)) {
+ _bus_dmamap_count_phys(dmat, map, buf, buflen, flags);
+ if (map->pagesneeded != 0) {
+ counter_u64_add(maploads_bounced, 1);
+ error = _bus_dmamap_reserve_pages(dmat, map, flags);
+ if (error)
+ return (error);
+ }
+ }
+
+ sl = map->slist + map->sync_count - 1;
+
+ while (buflen > 0) {
+ curaddr = buf;
+ sgsize = MIN(buflen, dmat->maxsegsz);
+ if (map->pagesneeded != 0 && must_bounce(dmat, map, curaddr,
+ sgsize)) {
+ sgsize = MIN(sgsize, PAGE_SIZE - (curaddr & PAGE_MASK));
+ curaddr = add_bounce_page(dmat, map, 0, curaddr,
+ sgsize);
+ } else if ((dmat->flags & BUS_DMA_COHERENT) == 0) {
+ if (map->sync_count > 0)
+ sl_end = sl->paddr + sl->datacount;
+
+ if (map->sync_count == 0 || curaddr != sl_end) {
+ if (++map->sync_count > dmat->nsegments)
+ break;
+ sl++;
+ sl->vaddr = 0;
+ sl->paddr = curaddr;
+ sl->datacount = sgsize;
+ sl->pages = PHYS_TO_VM_PAGE(curaddr);
+ KASSERT(sl->pages != NULL,
+ ("%s: page at PA:0x%08lx is not in "
+ "vm_page_array", __func__, curaddr));
+ } else
+ sl->datacount += sgsize;
+ }
+ sgsize = _bus_dmamap_addseg(dmat, map, curaddr, sgsize, segs,
+ segp);
+ if (sgsize == 0)
+ break;
+ buf += sgsize;
+ buflen -= sgsize;
+ }
+
+ /*
+ * Did we fit?
+ */
+ if (buflen != 0) {
+ bus_dmamap_unload(dmat, map);
+ return (EFBIG); /* XXX better return value here? */
+ }
+ return (0);
+}
+
+int
+_bus_dmamap_load_ma(bus_dma_tag_t dmat, bus_dmamap_t map,
+ struct vm_page **ma, bus_size_t tlen, int ma_offs, int flags,
+ bus_dma_segment_t *segs, int *segp)
+{
+
+ return (bus_dmamap_load_ma_triv(dmat, map, ma, tlen, ma_offs, flags,
+ segs, segp));
+}
+
+/*
+ * Utility function to load a linear buffer. segp contains
+ * the starting segment on entrance, and the ending segment on exit.
+ */
+int
+_bus_dmamap_load_buffer(bus_dma_tag_t dmat, bus_dmamap_t map, void *buf,
+ bus_size_t buflen, pmap_t pmap, int flags, bus_dma_segment_t *segs,
+ int *segp)
+{
+ bus_size_t sgsize;
+ bus_addr_t curaddr;
+ bus_addr_t sl_pend = 0;
+ vm_offset_t kvaddr, vaddr, sl_vend = 0;
+ struct sync_list *sl;
+ int error;
+
+ counter_u64_add(maploads_total, 1);
+ if (map->flags & DMAMAP_COHERENT)
+ counter_u64_add(maploads_coherent, 1);
+ if (map->flags & DMAMAP_DMAMEM_ALLOC)
+ counter_u64_add(maploads_dmamem, 1);
+
+ if (segs == NULL)
+ segs = map->segments;
+
+ if (flags & BUS_DMA_LOAD_MBUF) {
+ counter_u64_add(maploads_mbuf, 1);
+ map->flags |= DMAMAP_MBUF;
+ }
+
+ if (might_bounce(dmat, map, (bus_addr_t)buf, buflen)) {
+ _bus_dmamap_count_pages(dmat, pmap, map, buf, buflen, flags);
+ if (map->pagesneeded != 0) {
+ counter_u64_add(maploads_bounced, 1);
+ error = _bus_dmamap_reserve_pages(dmat, map, flags);
+ if (error)
+ return (error);
+ }
+ }
+
+ sl = map->slist + map->sync_count - 1;
+ vaddr = (vm_offset_t)buf;
+
+ while (buflen > 0) {
+ /*
+ * Get the physical address for this segment.
+ */
+ if (__predict_true(pmap == kernel_pmap)) {
+ curaddr = pmap_kextract(vaddr);
+ kvaddr = vaddr;
+ } else {
+ curaddr = pmap_extract(pmap, vaddr);
+ kvaddr = 0;
+ }
+
+ /*
+ * Compute the segment size, and adjust counts.
+ */
+ sgsize = PAGE_SIZE - (curaddr & PAGE_MASK);
+ if (sgsize > dmat->maxsegsz)
+ sgsize = dmat->maxsegsz;
+ if (buflen < sgsize)
+ sgsize = buflen;
+
+ if (map->pagesneeded != 0 && must_bounce(dmat, map, curaddr,
+ sgsize)) {
+ curaddr = add_bounce_page(dmat, map, kvaddr, curaddr,
+ sgsize);
+ } else if ((dmat->flags & BUS_DMA_COHERENT) == 0) {
+ if (map->sync_count > 0) {
+ sl_pend = sl->paddr + sl->datacount;
+ sl_vend = sl->vaddr + sl->datacount;
+ }
+
+ if (map->sync_count == 0 ||
+ (kvaddr != 0 && kvaddr != sl_vend) ||
+ (curaddr != sl_pend)) {
+ if (++map->sync_count > dmat->nsegments)
+ goto cleanup;
+ sl++;
+ sl->vaddr = kvaddr;
+ sl->paddr = curaddr;
+ if (kvaddr != 0) {
+ sl->pages = NULL;
+ } else {
+ sl->pages = PHYS_TO_VM_PAGE(curaddr);
+ KASSERT(sl->pages != NULL,
+ ("%s: page at PA:0x%08lx is not "
+ "in vm_page_array", __func__,
+ curaddr));
+ }
+ sl->datacount = sgsize;
+ } else
+ sl->datacount += sgsize;
+ }
+ sgsize = _bus_dmamap_addseg(dmat, map, curaddr, sgsize, segs,
+ segp);
+ if (sgsize == 0)
+ break;
+ vaddr += sgsize;
+ buflen -= sgsize;
+ }
+
+cleanup:
+ /*
+ * Did we fit?
+ */
+ if (buflen != 0) {
+ bus_dmamap_unload(dmat, map);
+ return (EFBIG); /* XXX better return value here? */
+ }
+ return (0);
+}
+
+void
+_bus_dmamap_waitok(bus_dma_tag_t dmat, bus_dmamap_t map, struct memdesc *mem,
+ bus_dmamap_callback_t *callback, void *callback_arg)
+{
+
+ map->mem = *mem;
+ map->dmat = dmat;
+ map->callback = callback;
+ map->callback_arg = callback_arg;
+}
+
+bus_dma_segment_t *
+_bus_dmamap_complete(bus_dma_tag_t dmat, bus_dmamap_t map,
+ bus_dma_segment_t *segs, int nsegs, int error)
+{
+
+ if (segs == NULL)
+ segs = map->segments;
+ return (segs);
+}
+
+/*
+ * Release the mapping held by map.
+ */
+void
+bus_dmamap_unload(bus_dma_tag_t dmat, bus_dmamap_t map)
+{
+ struct bounce_page *bpage;
+ struct bounce_zone *bz;
+
+ if ((bz = dmat->bounce_zone) != NULL) {
+ while ((bpage = STAILQ_FIRST(&map->bpages)) != NULL) {
+ STAILQ_REMOVE_HEAD(&map->bpages, links);
+ free_bounce_page(dmat, bpage);
+ }
+
+ bz = dmat->bounce_zone;
+ bz->free_bpages += map->pagesreserved;
+ bz->reserved_bpages -= map->pagesreserved;
+ map->pagesreserved = 0;
+ map->pagesneeded = 0;
+ }
+ map->sync_count = 0;
+ map->flags &= ~DMAMAP_MBUF;
+}
+
+static void
+dma_preread_safe(vm_offset_t va, vm_paddr_t pa, vm_size_t size)
+{
+ /*
+ * Write back any partial cachelines immediately before and
+ * after the DMA region. We don't need to round the address
+ * down to the nearest cacheline or specify the exact size,
+ * as dcache_wb_poc() will do the rounding for us and works
+ * at cacheline granularity.
+ */
+ if (va & BUSDMA_DCACHE_MASK)
+ dcache_wb_poc(va, pa, 1);
+ if ((va + size) & BUSDMA_DCACHE_MASK)
+ dcache_wb_poc(va + size, pa + size, 1);
+
+ dcache_inv_poc_dma(va, pa, size);
+}
+
+static void
+dma_dcache_sync(struct sync_list *sl, bus_dmasync_op_t op)
+{
+ uint32_t len, offset;
+ vm_page_t m;
+ vm_paddr_t pa;
+ vm_offset_t va, tempva;
+ bus_size_t size;
+
+ offset = sl->paddr & PAGE_MASK;
+ m = sl->pages;
+ size = sl->datacount;
+ pa = sl->paddr;
+
+ for ( ; size != 0; size -= len, pa += len, offset = 0, ++m) {
+ tempva = 0;
+ if (sl->vaddr == 0) {
+ len = min(PAGE_SIZE - offset, size);
+ tempva = pmap_quick_enter_page(m);
+ va = tempva | offset;
+ KASSERT(pa == (VM_PAGE_TO_PHYS(m) | offset),
+ ("unexpected vm_page_t phys: 0x%08x != 0x%08x",
+ VM_PAGE_TO_PHYS(m) | offset, pa));
+ } else {
+ len = sl->datacount;
+ va = sl->vaddr;
+ }
+
+ switch (op) {
+ case BUS_DMASYNC_PREWRITE:
+ case BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD:
+ dcache_wb_poc(va, pa, len);
+ break;
+ case BUS_DMASYNC_PREREAD:
+ /*
+ * An mbuf may start in the middle of a cacheline. There
+ * will be no cpu writes to the beginning of that line
+ * (which contains the mbuf header) while dma is in
+ * progress. Handle that case by doing a writeback of
+ * just the first cacheline before invalidating the
+ * overall buffer. Any mbuf in a chain may have this
+ * misalignment. Buffers which are not mbufs bounce if
+ * they are not aligned to a cacheline.
+ */
+ dma_preread_safe(va, pa, len);
+ break;
+ case BUS_DMASYNC_POSTREAD:
+ case BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE:
+ dcache_inv_poc(va, pa, len);
+ break;
+ default:
+ panic("unsupported combination of sync operations: "
+ "0x%08x\n", op);
+ }
+
+ if (tempva != 0)
+ pmap_quick_remove_page(tempva);
+ }
+}
+
+void
+bus_dmamap_sync(bus_dma_tag_t dmat, bus_dmamap_t map, bus_dmasync_op_t op)
+{
+ struct bounce_page *bpage;
+ struct sync_list *sl, *end;
+ vm_offset_t datavaddr, tempvaddr;
+
+ if (op == BUS_DMASYNC_POSTWRITE)
+ return;
+
+ /*
+ * If the buffer was from user space, it is possible that this is not
+ * the same vm map, especially on a POST operation. It's not clear that
+ * dma on userland buffers can work at all right now. To be safe, until
+ * we're able to test direct userland dma, panic on a map mismatch.
+ */
+ if ((bpage = STAILQ_FIRST(&map->bpages)) != NULL) {
+ CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x op 0x%x "
+ "performing bounce", __func__, dmat, dmat->flags, op);
+
+ /*
+ * For PREWRITE do a writeback. Clean the caches from the
+ * innermost to the outermost levels.
+ */
+ if (op & BUS_DMASYNC_PREWRITE) {
+ while (bpage != NULL) {
+ tempvaddr = 0;
+ datavaddr = bpage->datavaddr;
+ if (datavaddr == 0) {
+ tempvaddr = pmap_quick_enter_page(
+ bpage->datapage);
+ datavaddr = tempvaddr | bpage->dataoffs;
+ }
+ bcopy((void *)datavaddr, (void *)bpage->vaddr,
+ bpage->datacount);
+ if (tempvaddr != 0)
+ pmap_quick_remove_page(tempvaddr);
+ if ((dmat->flags & BUS_DMA_COHERENT) == 0)
+ dcache_wb_poc(bpage->vaddr,
+ bpage->busaddr, bpage->datacount);
+ bpage = STAILQ_NEXT(bpage, links);
+ }
+ dmat->bounce_zone->total_bounced++;
+ }
+
+ /*
+ * Do an invalidate for PREREAD unless a writeback was already
+ * done above due to PREWRITE also being set. The reason for a
+ * PREREAD invalidate is to prevent dirty lines currently in the
+ * cache from being evicted during the DMA. If a writeback was
+ * done due to PREWRITE also being set there will be no dirty
+ * lines and the POSTREAD invalidate handles the rest. The
+ * invalidate is done from the innermost to outermost level. If
+ * L2 were done first, a dirty cacheline could be automatically
+ * evicted from L1 before we invalidated it, re-dirtying the L2.
+ */
+ if ((op & BUS_DMASYNC_PREREAD) && !(op & BUS_DMASYNC_PREWRITE)) {
+ bpage = STAILQ_FIRST(&map->bpages);
+ while (bpage != NULL) {
+ if ((dmat->flags & BUS_DMA_COHERENT) == 0)
+ dcache_inv_poc_dma(bpage->vaddr,
+ bpage->busaddr, bpage->datacount);
+ bpage = STAILQ_NEXT(bpage, links);
+ }
+ }
+
+ /*
+ * Re-invalidate the caches on a POSTREAD, even though they were
+ * already invalidated at PREREAD time. Aggressive prefetching
+ * due to accesses to other data near the dma buffer could have
+ * brought buffer data into the caches which is now stale. The
+ * caches are invalidated from the outermost to innermost; the
+ * prefetches could be happening right now, and if L1 were
+ * invalidated first, stale L2 data could be prefetched into L1.
+ */
+ if (op & BUS_DMASYNC_POSTREAD) {
+ while (bpage != NULL) {
+ if ((dmat->flags & BUS_DMA_COHERENT) == 0)
+ dcache_inv_poc(bpage->vaddr,
+ bpage->busaddr, bpage->datacount);
+ tempvaddr = 0;
+ datavaddr = bpage->datavaddr;
+ if (datavaddr == 0) {
+ tempvaddr = pmap_quick_enter_page(
+ bpage->datapage);
+ datavaddr = tempvaddr | bpage->dataoffs;
+ }
+ bcopy((void *)bpage->vaddr, (void *)datavaddr,
+ bpage->datacount);
+ if (tempvaddr != 0)
+ pmap_quick_remove_page(tempvaddr);
+ bpage = STAILQ_NEXT(bpage, links);
+ }
+ dmat->bounce_zone->total_bounced++;
+ }
+ }
+
+ /*
+ * For COHERENT memory no cache maintenance is necessary, but ensure all
+ * writes have reached memory for the PREWRITE case. No action is
+ * needed for a PREREAD without PREWRITE also set, because that would
+ * imply that the cpu had written to the COHERENT buffer and expected
+ * the dma device to see that change, and by definition a PREWRITE sync
+ * is required to make that happen.
+ */
+ if (map->flags & DMAMAP_COHERENT) {
+ if (op & BUS_DMASYNC_PREWRITE) {
+ dsb();
+ if ((dmat->flags & BUS_DMA_COHERENT) == 0)
+ cpu_l2cache_drain_writebuf();
+ }
+ return;
+ }
+
+ /*
+ * Cache maintenance for normal (non-COHERENT non-bounce) buffers. All
+ * the comments about the sequences for flushing cache levels in the
+ * bounce buffer code above apply here as well. In particular, the fact
+ * that the sequence is inner-to-outer for PREREAD invalidation and
+ * outer-to-inner for POSTREAD invalidation is not a mistake.
+ */
+ if (map->sync_count != 0) {
+ sl = &map->slist[0];
+ end = &map->slist[map->sync_count];
+ CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x op 0x%x "
+ "performing sync", __func__, dmat, dmat->flags, op);
+
+ for ( ; sl != end; ++sl)
+ dma_dcache_sync(sl, op);
+ }
+}
+
+static void
+init_bounce_pages(void *dummy __unused)
+{
+
+ total_bpages = 0;
+ STAILQ_INIT(&bounce_zone_list);
+ STAILQ_INIT(&bounce_map_waitinglist);
+ STAILQ_INIT(&bounce_map_callbacklist);
+ mtx_init(&bounce_lock, "bounce pages lock", NULL, MTX_DEF);
+}
+SYSINIT(bpages, SI_SUB_LOCK, SI_ORDER_ANY, init_bounce_pages, NULL);
+
+static struct sysctl_ctx_list *
+busdma_sysctl_tree(struct bounce_zone *bz)
+{
+
+ return (&bz->sysctl_tree);
+}
+
+static struct sysctl_oid *
+busdma_sysctl_tree_top(struct bounce_zone *bz)
+{
+
+ return (bz->sysctl_tree_top);
+}
+
+static int
+alloc_bounce_zone(bus_dma_tag_t dmat)
+{
+ struct bounce_zone *bz;
+
+ /* Check to see if we already have a suitable zone */
+ STAILQ_FOREACH(bz, &bounce_zone_list, links) {
+ if ((dmat->alignment <= bz->alignment) &&
+ (dmat->lowaddr >= bz->lowaddr)) {
+ dmat->bounce_zone = bz;
+ return (0);
+ }
+ }
+
+ if ((bz = (struct bounce_zone *)malloc(sizeof(*bz), M_BUSDMA,
+ M_NOWAIT | M_ZERO)) == NULL)
+ return (ENOMEM);
+
+ STAILQ_INIT(&bz->bounce_page_list);
+ bz->free_bpages = 0;
+ bz->reserved_bpages = 0;
+ bz->active_bpages = 0;
+ bz->lowaddr = dmat->lowaddr;
+ bz->alignment = MAX(dmat->alignment, PAGE_SIZE);
+ bz->map_count = 0;
+ snprintf(bz->zoneid, 8, "zone%d", busdma_zonecount);
+ busdma_zonecount++;
+ snprintf(bz->lowaddrid, 18, "%#jx", (uintmax_t)bz->lowaddr);
+ STAILQ_INSERT_TAIL(&bounce_zone_list, bz, links);
+ dmat->bounce_zone = bz;
+
+ sysctl_ctx_init(&bz->sysctl_tree);
+ bz->sysctl_tree_top = SYSCTL_ADD_NODE(&bz->sysctl_tree,
+ SYSCTL_STATIC_CHILDREN(_hw_busdma), OID_AUTO, bz->zoneid,
+ CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
+ if (bz->sysctl_tree_top == NULL) {
+ sysctl_ctx_free(&bz->sysctl_tree);
+ return (0); /* XXX error code? */
+ }
+
+ SYSCTL_ADD_INT(busdma_sysctl_tree(bz),
+ SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
+ "total_bpages", CTLFLAG_RD, &bz->total_bpages, 0,
+ "Total bounce pages");
+ SYSCTL_ADD_INT(busdma_sysctl_tree(bz),
+ SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
+ "free_bpages", CTLFLAG_RD, &bz->free_bpages, 0,
+ "Free bounce pages");
+ SYSCTL_ADD_INT(busdma_sysctl_tree(bz),
+ SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
+ "reserved_bpages", CTLFLAG_RD, &bz->reserved_bpages, 0,
+ "Reserved bounce pages");
+ SYSCTL_ADD_INT(busdma_sysctl_tree(bz),
+ SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
+ "active_bpages", CTLFLAG_RD, &bz->active_bpages, 0,
+ "Active bounce pages");
+ SYSCTL_ADD_INT(busdma_sysctl_tree(bz),
+ SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
+ "total_bounced", CTLFLAG_RD, &bz->total_bounced, 0,
+ "Total bounce requests (pages bounced)");
+ SYSCTL_ADD_INT(busdma_sysctl_tree(bz),
+ SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
+ "total_deferred", CTLFLAG_RD, &bz->total_deferred, 0,
+ "Total bounce requests that were deferred");
+ SYSCTL_ADD_STRING(busdma_sysctl_tree(bz),
+ SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
+ "lowaddr", CTLFLAG_RD, bz->lowaddrid, 0, "");
+ SYSCTL_ADD_ULONG(busdma_sysctl_tree(bz),
+ SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
+ "alignment", CTLFLAG_RD, &bz->alignment, "");
+
+ return (0);
+}
+
+static int
+alloc_bounce_pages(bus_dma_tag_t dmat, u_int numpages)
+{
+ struct bounce_zone *bz;
+ int count;
+
+ bz = dmat->bounce_zone;
+ count = 0;
+ while (numpages > 0) {
+ struct bounce_page *bpage;
+
+ bpage = (struct bounce_page *)malloc(sizeof(*bpage), M_BUSDMA,
+ M_NOWAIT | M_ZERO);
+
+ if (bpage == NULL)
+ break;
+ bpage->vaddr = (vm_offset_t)contigmalloc(PAGE_SIZE, M_BOUNCE,
+ M_NOWAIT, 0ul, bz->lowaddr, PAGE_SIZE, 0);
+ if (bpage->vaddr == 0) {
+ free(bpage, M_BUSDMA);
+ break;
+ }
+ bpage->busaddr = pmap_kextract(bpage->vaddr);
+ mtx_lock(&bounce_lock);
+ STAILQ_INSERT_TAIL(&bz->bounce_page_list, bpage, links);
+ total_bpages++;
+ bz->total_bpages++;
+ bz->free_bpages++;
+ mtx_unlock(&bounce_lock);
+ count++;
+ numpages--;
+ }
+ return (count);
+}
+
+static int
+reserve_bounce_pages(bus_dma_tag_t dmat, bus_dmamap_t map, int commit)
+{
+ struct bounce_zone *bz;
+ int pages;
+
+ mtx_assert(&bounce_lock, MA_OWNED);
+ bz = dmat->bounce_zone;
+ pages = MIN(bz->free_bpages, map->pagesneeded - map->pagesreserved);
+ if (commit == 0 && map->pagesneeded > (map->pagesreserved + pages))
+ return (map->pagesneeded - (map->pagesreserved + pages));
+ bz->free_bpages -= pages;
+ bz->reserved_bpages += pages;
+ map->pagesreserved += pages;
+ pages = map->pagesneeded - map->pagesreserved;
+
+ return (pages);
+}
+
+static bus_addr_t
+add_bounce_page(bus_dma_tag_t dmat, bus_dmamap_t map, vm_offset_t vaddr,
+ bus_addr_t addr, bus_size_t size)
+{
+ struct bounce_zone *bz;
+ struct bounce_page *bpage;
+
+ KASSERT(dmat->bounce_zone != NULL, ("no bounce zone in dma tag"));
+ KASSERT(map != NULL, ("add_bounce_page: bad map %p", map));
+
+ bz = dmat->bounce_zone;
+ if (map->pagesneeded == 0)
+ panic("add_bounce_page: map doesn't need any pages");
+ map->pagesneeded--;
+
+ if (map->pagesreserved == 0)
+ panic("add_bounce_page: map doesn't need any pages");
+ map->pagesreserved--;
+
+ mtx_lock(&bounce_lock);
+ bpage = STAILQ_FIRST(&bz->bounce_page_list);
+ if (bpage == NULL)
+ panic("add_bounce_page: free page list is empty");
+
+ STAILQ_REMOVE_HEAD(&bz->bounce_page_list, links);
+ bz->reserved_bpages--;
+ bz->active_bpages++;
+ mtx_unlock(&bounce_lock);
+
+ if (dmat->flags & BUS_DMA_KEEP_PG_OFFSET) {
+ /* Page offset needs to be preserved. */
+ bpage->vaddr |= addr & PAGE_MASK;
+ bpage->busaddr |= addr & PAGE_MASK;
+ }
+ bpage->datavaddr = vaddr;
+ bpage->datapage = PHYS_TO_VM_PAGE(addr);
+ bpage->dataoffs = addr & PAGE_MASK;
+ bpage->datacount = size;
+ STAILQ_INSERT_TAIL(&(map->bpages), bpage, links);
+ return (bpage->busaddr);
+}
+
+static void
+free_bounce_page(bus_dma_tag_t dmat, struct bounce_page *bpage)
+{
+ struct bus_dmamap *map;
+ struct bounce_zone *bz;
+
+ bz = dmat->bounce_zone;
+ bpage->datavaddr = 0;
+ bpage->datacount = 0;
+ if (dmat->flags & BUS_DMA_KEEP_PG_OFFSET) {
+ /*
+ * Reset the bounce page to start at offset 0. Other uses
+ * of this bounce page may need to store a full page of
+ * data and/or assume it starts on a page boundary.
+ */
+ bpage->vaddr &= ~PAGE_MASK;
+ bpage->busaddr &= ~PAGE_MASK;
+ }
+
+ mtx_lock(&bounce_lock);
+ STAILQ_INSERT_HEAD(&bz->bounce_page_list, bpage, links);
+ bz->free_bpages++;
+ bz->active_bpages--;
+ if ((map = STAILQ_FIRST(&bounce_map_waitinglist)) != NULL) {
+ if (reserve_bounce_pages(map->dmat, map, 1) == 0) {
+ STAILQ_REMOVE_HEAD(&bounce_map_waitinglist, links);
+ STAILQ_INSERT_TAIL(&bounce_map_callbacklist,
+ map, links);
+ busdma_swi_pending = 1;
+ bz->total_deferred++;
+ swi_sched(vm_ih, 0);
+ }
+ }
+ mtx_unlock(&bounce_lock);
+}
+
+void
+busdma_swi(void)
+{
+ bus_dma_tag_t dmat;
+ struct bus_dmamap *map;
+
+ mtx_lock(&bounce_lock);
+ while ((map = STAILQ_FIRST(&bounce_map_callbacklist)) != NULL) {
+ STAILQ_REMOVE_HEAD(&bounce_map_callbacklist, links);
+ mtx_unlock(&bounce_lock);
+ dmat = map->dmat;
+ dmat->lockfunc(dmat->lockfuncarg, BUS_DMA_LOCK);
+ bus_dmamap_load_mem(map->dmat, map, &map->mem, map->callback,
+ map->callback_arg, BUS_DMA_WAITOK);
+ dmat->lockfunc(dmat->lockfuncarg, BUS_DMA_UNLOCK);
+ mtx_lock(&bounce_lock);
+ }
+ mtx_unlock(&bounce_lock);
+}
diff --git a/sys/arm/arm/copystr.S b/sys/arm/arm/copystr.S
new file mode 100644
index 000000000000..b1237ceccd39
--- /dev/null
+++ b/sys/arm/arm/copystr.S
@@ -0,0 +1,130 @@
+/* $NetBSD: copystr.S,v 1.8 2002/10/13 14:54:48 bjh21 Exp $ */
+
+/*-
+ * Copyright (c) 1995 Mark Brinicombe.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Mark Brinicombe.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * copystr.S
+ *
+ * optimised and fault protected copystr functions
+ *
+ * Created : 16/05/95
+ */
+
+#include "assym.inc"
+#include <machine/asm.h>
+#include <machine/armreg.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/errno.h>
+
+ .text
+ .align 2
+
+#define GET_PCB(tmp) \
+ mrc p15, 0, tmp, c13, c0, 4; \
+ add tmp, tmp, #(TD_PCB)
+
+#define SAVE_REGS stmfd sp!, {r4-r6}
+#define RESTORE_REGS ldmfd sp!, {r4-r6}
+
+/*
+ * r0 - user space address
+ * r1 - kernel space address
+ * r2 - maxlens
+ * r3 - lencopied
+ *
+ * Copy string from user space to kernel space
+ */
+ENTRY(copyinstr)
+ SAVE_REGS
+
+ teq r2, #0x00000000
+ mov r6, #0x00000000
+ moveq r0, #ENAMETOOLONG
+ beq 2f
+
+ ldr r12, =VM_MAXUSER_ADDRESS
+
+ GET_PCB(r4)
+ ldr r4, [r4]
+
+#ifdef DIAGNOSTIC
+ teq r4, #0x00000000
+ beq .Lcopystrpcbfault
+#endif
+
+ adr r5, .Lcopystrfault
+ str r5, [r4, #PCB_ONFAULT]
+
+1:
+ cmp r0, r12
+ bcs .Lcopystrfault
+ ldrbt r5, [r0], #0x0001
+ add r6, r6, #0x00000001
+ teq r5, #0x00000000
+ strb r5, [r1], #0x0001
+ teqne r6, r2
+ bne 1b
+
+ mov r0, #0x00000000
+ str r0, [r4, #PCB_ONFAULT]
+
+ teq r5, #0x00000000
+ moveq r0, #0x00000000
+ movne r0, #ENAMETOOLONG
+
+2: teq r3, #0x00000000
+ strne r6, [r3]
+
+ RESTORE_REGS
+ RET
+END(copyinstr)
+
+/* A fault occurred during the copy */
+.Lcopystrfault:
+ mov r1, #0x00000000
+ str r1, [r4, #PCB_ONFAULT]
+ mov r0, #EFAULT
+ RESTORE_REGS
+ RET
+
+#ifdef DIAGNOSTIC
+.Lcopystrpcbfault:
+ mov r2, r1
+ mov r1, r0
+ adr r0, Lcopystrpcbfaulttext
+ bic sp, sp, #7 /* align stack to 8 bytes */
+ b _C_LABEL(panic)
+
+Lcopystrpcbfaulttext:
+ .asciz "No valid PCB during copyinoutstr() addr1=%08x addr2=%08x\n"
+ .align 2
+#endif
diff --git a/sys/arm/arm/cpu_asm-v6.S b/sys/arm/arm/cpu_asm-v6.S
new file mode 100644
index 000000000000..c62a7e7ca26f
--- /dev/null
+++ b/sys/arm/arm/cpu_asm-v6.S
@@ -0,0 +1,270 @@
+/*-
+ * Copyright 2014 Svatopluk Kraus <onwahe@gmail.com>
+ * Copyright 2014 Michal Meloun <meloun@miracle.cz>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#include "assym.inc"
+
+#include <machine/asm.h>
+#include <machine/asmacros.h>
+#include <machine/armreg.h>
+#include <machine/sysreg.h>
+
+#define GET_PCB(tmp) \
+ mrc CP15_TPIDRPRW(tmp); \
+ add tmp, tmp, #(TD_PCB)
+
+/*
+ * Define cache functions used by startup code, which counts on the fact that
+ * only r0-r3,r12 (ip) are modified and no stack space is used. These functions
+ * must be called with interrupts disabled. Moreover, these work only with
+ * caches integrated to CPU (accessible via CP15); systems with an external L2
+ * cache controller such as a PL310 need separate calls to that device driver
+ * to affect L2 caches. This is not a factor during early kernel startup, as
+ * any external L2 cache controller has not been enabled yet.
+ */
+
+/* Invalidate D cache to PoC. (aka all cache levels)*/
+ASENTRY_NP(dcache_inv_poc_all)
+#if __ARM_ARCH == 6
+ mcr CP15_DCIALL
+ DSB
+ bx lr
+#else
+ mrc CP15_CLIDR(r0)
+ ands r0, r0, #0x07000000
+ mov r0, r0, lsr #23 /* Get LoC 'naturally' aligned for */
+ beq 4f /* use in the CSSELR register below */
+
+1: sub r0, #2
+ mcr CP15_CSSELR(r0) /* set cache level */
+ isb
+ mrc CP15_CCSIDR(r0) /* read CCSIDR */
+
+ ubfx r2, r0, #13, #15 /* get num sets - 1 from CCSIDR */
+ ubfx r3, r0, #3, #10 /* get num ways - 1 from CCSIDR */
+ clz r1, r3 /* number of bits to MSB of way */
+ lsl r3, r3, r1 /* shift into position */
+ mov ip, #1
+ lsl ip, ip, r1 /* ip now contains the way decr */
+
+ ubfx r0, r0, #0, #3 /* get linesize from CCSIDR */
+ add r0, r0, #4 /* apply bias */
+ lsl r2, r2, r0 /* shift sets by log2(linesize) */
+ add r3, r3, r2 /* merge numsets - 1 with numways - 1 */
+ sub ip, ip, r2 /* subtract numsets - 1 from way decr */
+ mov r1, #1
+ lsl r1, r1, r0 /* r1 now contains the set decr */
+ mov r2, ip /* r2 now contains set way decr */
+
+ /* r3 = ways/sets, r2 = way decr, r1 = set decr, r0 and ip are free */
+2: mcr CP15_DCISW(r3) /* invalidate line */
+ movs r0, r3 /* get current way/set */
+ beq 3f /* at 0 means we are done */
+ movs r0, r0, lsl #10 /* clear way bits leaving only set bits*/
+ subne r3, r3, r1 /* non-zero?, decrement set */
+ subeq r3, r3, r2 /* zero?, decrement way and restore set count */
+ b 2b
+
+3:
+ mrc CP15_CSSELR(r0) /* get cache level */
+ teq r0, #0
+ bne 1b
+
+4: dsb /* wait for stores to finish */
+ mov r0, #0
+ mcr CP15_CSSELR(r0)
+ isb
+ bx lr
+#endif /* __ARM_ARCH == 6 */
+END(dcache_inv_poc_all)
+
+/* Invalidate D cache to PoU. (aka L1 cache only)*/
+ASENTRY_NP(dcache_inv_pou_all)
+#if __ARM_ARCH == 6
+ mcr CP15_DCIALL
+ DSB
+ bx lr
+#else
+ mrc CP15_CLIDR(r0)
+ ands r0, r0, #0x38000000
+ mov r0, r0, lsr #26 /* Get LoUU (naturally aligned) */
+ beq 4f
+
+1: sub r0, #2
+ mcr CP15_CSSELR(r0) /* set cache level */
+ isb
+ mrc CP15_CCSIDR(r0) /* read CCSIDR */
+
+ ubfx r2, r0, #13, #15 /* get num sets - 1 from CCSIDR */
+ ubfx r3, r0, #3, #10 /* get num ways - 1 from CCSIDR */
+ clz r1, r3 /* number of bits to MSB of way */
+ lsl r3, r3, r1 /* shift into position */
+ mov ip, #1
+ lsl ip, ip, r1 /* ip now contains the way decr */
+
+ ubfx r0, r0, #0, #3 /* get linesize from CCSIDR */
+ add r0, r0, #4 /* apply bias */
+ lsl r2, r2, r0 /* shift sets by log2(linesize) */
+ add r3, r3, r2 /* merge numsets - 1 with numways - 1 */
+ sub ip, ip, r2 /* subtract numsets - 1 from way decr */
+ mov r1, #1
+ lsl r1, r1, r0 /* r1 now contains the set decr */
+ mov r2, ip /* r2 now contains set way decr */
+
+ /* r3 = ways/sets, r2 = way decr, r1 = set decr, r0 and ip are free */
+2: mcr CP15_DCISW(r3) /* invalidate line */
+ movs r0, r3 /* get current way/set */
+ beq 3f /* at 0 means we are done */
+ movs r0, r0, lsl #10 /* clear way bits leaving only set bits*/
+ subne r3, r3, r1 /* non-zero?, decrement set */
+ subeq r3, r3, r2 /* zero?, decrement way and restore set count */
+ b 2b
+
+3:
+ mrc CP15_CSSELR(r0) /* get cache level */
+ teq r0, #0
+ bne 1b
+
+4: dsb /* wait for stores to finish */
+ mov r0, #0
+ mcr CP15_CSSELR(r0)
+ bx lr
+#endif
+END(dcache_inv_pou_all)
+
+/* Write back and Invalidate D cache to PoC. */
+ASENTRY_NP(dcache_wbinv_poc_all)
+#if __ARM_ARCH == 6
+ mcr CP15_DCCIALL
+ DSB
+ bx lr
+#else
+ mrc CP15_CLIDR(r0)
+ ands r0, r0, #0x07000000
+ beq 4f
+ mov r0, #0 /* Clean from inner to outer levels */
+
+1: mcr CP15_CSSELR(r0) /* set cache level */
+ isb
+ mrc CP15_CCSIDR(r0) /* read CCSIDR */
+
+ ubfx r2, r0, #13, #15 /* get num sets - 1 from CCSIDR */
+ ubfx r3, r0, #3, #10 /* get num ways - 1 from CCSIDR */
+ clz r1, r3 /* number of bits to MSB of way */
+ lsl r3, r3, r1 /* shift into position */
+ mov ip, #1
+ lsl ip, ip, r1 /* ip now contains the way decr */
+
+ ubfx r0, r0, #0, #3 /* get linesize from CCSIDR */
+ add r0, r0, #4 /* apply bias */
+ lsl r2, r2, r0 /* shift sets by log2(linesize) */
+ add r3, r3, r2 /* merge numsets - 1 with numways - 1 */
+ sub ip, ip, r2 /* subtract numsets - 1 from way decr */
+ mov r1, #1
+ lsl r1, r1, r0 /* r1 now contains the set decr */
+ mov r2, ip /* r2 now contains set way decr */
+
+ /* r3 = ways/sets, r2 = way decr, r1 = set decr, r0 and ip are free */
+2: mcr CP15_DCCISW(r3) /* clean and invalidate line */
+ movs r0, r3 /* get current way/set */
+ beq 3f /* at 0 means we are done */
+ movs r0, r0, lsl #10 /* clear way bits leaving only set bits*/
+ subne r3, r3, r1 /* non-zero?, decrement set */
+ subeq r3, r3, r2 /* zero?, decrement way and restore set count */
+ b 2b
+
+3:
+ mrc CP15_CSSELR(r0) /* get cache level */
+ add r0, r0, #2 /* next level */
+ mrc CP15_CLIDR(r1)
+ ands r1, r1, #0x07000000
+ mov r1, r1, lsr #23 /* Get LoC (naturally aligned) */
+ cmp r1, r0
+ bne 1b
+
+4: dsb /* wait for stores to finish */
+ mov r0, #0
+ mcr CP15_CSSELR(r0)
+ bx lr
+#endif /* __ARM_ARCH == 6 */
+END(dcache_wbinv_poc_all)
+
+ASENTRY_NP(dcache_wb_pou_checked)
+ ldr ip, .Lcpuinfo
+ ldr ip, [ip, #DCACHE_LINE_SIZE]
+
+ GET_PCB(r2)
+ ldr r2, [r2]
+
+ adr r3, _C_LABEL(cachebailout)
+ str r3, [r2, #PCB_ONFAULT]
+1:
+ mcr CP15_DCCMVAC(r0)
+ add r0, r0, ip
+ subs r1, r1, ip
+ bhi 1b
+ DSB
+ mov r0, #0
+ str r0, [r2, #PCB_ONFAULT]
+ mov r0, #1 /* cannot be faulting address */
+ RET
+
+.Lcpuinfo:
+ .word cpuinfo
+END(dcache_wb_pou_checked)
+
+ASENTRY_NP(icache_inv_pou_checked)
+ ldr ip, .Lcpuinfo
+ ldr ip, [ip, #ICACHE_LINE_SIZE]
+
+ GET_PCB(r2)
+ ldr r2, [r2]
+
+ adr r3, _C_LABEL(cachebailout)
+ str r3, [r2, #PCB_ONFAULT]
+
+1:
+ mcr CP15_ICIMVAU(r0)
+ add r0, r0, ip
+ subs r1, r1, ip
+ bhi 1b
+ DSB
+ ISB
+ mov r0, #0
+ str r0, [r2, #PCB_ONFAULT]
+ mov r0, #1 /* cannot be faulting address */
+ RET
+END(icache_inv_pou_checked)
+
+/* label must be global as trap-v6.c references it */
+ .global _C_LABEL(cachebailout)
+_C_LABEL(cachebailout):
+ DSB
+ ISB
+ mov r1, #0
+ str r1, [r2, #PCB_ONFAULT]
+ RET
diff --git a/sys/arm/arm/cpufunc.c b/sys/arm/arm/cpufunc.c
new file mode 100644
index 000000000000..3f773a3ece72
--- /dev/null
+++ b/sys/arm/arm/cpufunc.c
@@ -0,0 +1,349 @@
+/* $NetBSD: cpufunc.c,v 1.65 2003/11/05 12:53:15 scw Exp $ */
+
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * arm9 support code Copyright (C) 2001 ARM Ltd
+ * Copyright (c) 1997 Mark Brinicombe.
+ * Copyright (c) 1997 Causality Limited
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Causality Limited.
+ * 4. The name of Causality Limited may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CAUSALITY LIMITED ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL CAUSALITY LIMITED BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * RiscBSD kernel project
+ *
+ * cpufuncs.c
+ *
+ * C functions for supporting CPU / MMU / TLB specific operations.
+ *
+ * Created : 30/01/97
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/bus.h>
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/disassem.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <vm/uma.h>
+
+#include <machine/cpufunc.h>
+
+/* PRIMARY CACHE VARIABLES */
+
+int arm_dcache_align;
+int arm_dcache_align_mask;
+
+#ifdef CPU_MV_PJ4B
+static void pj4bv7_setup(void);
+#endif
+#if defined(CPU_ARM1176)
+static void arm11x6_setup(void);
+#endif
+#if defined(CPU_CORTEXA) || defined(CPU_KRAIT)
+static void cortexa_setup(void);
+#endif
+
+#ifdef CPU_MV_PJ4B
+struct cpu_functions pj4bv7_cpufuncs = {
+ /* Cache operations */
+ .cf_l2cache_wbinv_all = (void *)cpufunc_nullop,
+ .cf_l2cache_wbinv_range = (void *)cpufunc_nullop,
+ .cf_l2cache_inv_range = (void *)cpufunc_nullop,
+ .cf_l2cache_wb_range = (void *)cpufunc_nullop,
+ .cf_l2cache_drain_writebuf = (void *)cpufunc_nullop,
+
+ /* Other functions */
+ .cf_sleep = (void *)cpufunc_nullop,
+
+ /* Soft functions */
+ .cf_setup = pj4bv7_setup
+};
+#endif /* CPU_MV_PJ4B */
+
+#if defined(CPU_ARM1176)
+struct cpu_functions arm1176_cpufuncs = {
+ /* Cache operations */
+ .cf_l2cache_wbinv_all = (void *)cpufunc_nullop,
+ .cf_l2cache_wbinv_range = (void *)cpufunc_nullop,
+ .cf_l2cache_inv_range = (void *)cpufunc_nullop,
+ .cf_l2cache_wb_range = (void *)cpufunc_nullop,
+ .cf_l2cache_drain_writebuf = (void *)cpufunc_nullop,
+
+ /* Other functions */
+ .cf_sleep = arm11x6_sleep,
+
+ /* Soft functions */
+ .cf_setup = arm11x6_setup
+};
+#endif /*CPU_ARM1176 */
+
+#if defined(CPU_CORTEXA) || defined(CPU_KRAIT)
+struct cpu_functions cortexa_cpufuncs = {
+ /* Cache operations */
+
+ /*
+ * Note: For CPUs using the PL310 the L2 ops are filled in when the
+ * L2 cache controller is actually enabled.
+ */
+ .cf_l2cache_wbinv_all = cpufunc_nullop,
+ .cf_l2cache_wbinv_range = (void *)cpufunc_nullop,
+ .cf_l2cache_inv_range = (void *)cpufunc_nullop,
+ .cf_l2cache_wb_range = (void *)cpufunc_nullop,
+ .cf_l2cache_drain_writebuf = (void *)cpufunc_nullop,
+
+ /* Other functions */
+ .cf_sleep = armv7_cpu_sleep,
+
+ /* Soft functions */
+ .cf_setup = cortexa_setup
+};
+#endif /* CPU_CORTEXA || CPU_KRAIT */
+
+/*
+ * Global constants also used by locore.s
+ */
+
+struct cpu_functions cpufuncs;
+u_int cputype;
+
+static void get_cachetype_cp15(void);
+
+static void
+get_cachetype_cp15(void)
+{
+ u_int ctype, dsize, cpuid;
+ u_int clevel, csize, i, sel;
+ u_int multiplier;
+ u_char type;
+
+ ctype = cp15_ctr_get();
+ cpuid = cp15_midr_get();
+ /*
+ * ...and thus spake the ARM ARM:
+ *
+ * If an <opcode2> value corresponding to an unimplemented or
+ * reserved ID register is encountered, the System Control
+ * processor returns the value of the main ID register.
+ */
+ if (ctype == cpuid)
+ goto out;
+
+ if (CPU_CT_FORMAT(ctype) == CPU_CT_ARMV7) {
+ __asm __volatile("mrc p15, 1, %0, c0, c0, 1"
+ : "=r" (clevel));
+ i = 0;
+ while ((type = (clevel & 0x7)) && i < 7) {
+ if (type == CACHE_DCACHE || type == CACHE_UNI_CACHE ||
+ type == CACHE_SEP_CACHE) {
+ sel = i << 1;
+ __asm __volatile("mcr p15, 2, %0, c0, c0, 0"
+ : : "r" (sel));
+ __asm __volatile("mrc p15, 1, %0, c0, c0, 0"
+ : "=r" (csize));
+ arm_dcache_align = 1 <<
+ (CPUV7_CT_xSIZE_LEN(csize) + 4);
+ arm_dcache_align_mask = arm_dcache_align - 1;
+ }
+ if (type == CACHE_ICACHE || type == CACHE_SEP_CACHE) {
+ sel = (i << 1) | 1;
+ __asm __volatile("mcr p15, 2, %0, c0, c0, 0"
+ : : "r" (sel));
+ __asm __volatile("mrc p15, 1, %0, c0, c0, 0"
+ : "=r" (csize));
+ }
+ i++;
+ clevel >>= 3;
+ }
+ } else {
+ /*
+ * If you want to know how this code works, go read the ARM ARM.
+ */
+
+ dsize = CPU_CT_DSIZE(ctype);
+ multiplier = (dsize & CPU_CT_xSIZE_M) ? 3 : 2;
+ arm_dcache_align = 1U << (CPU_CT_xSIZE_LEN(dsize) + 3);
+ if (CPU_CT_xSIZE_ASSOC(dsize) == 0) {
+ if (dsize & CPU_CT_xSIZE_M)
+ arm_dcache_align = 0; /* not present */
+ }
+
+ out:
+ arm_dcache_align_mask = arm_dcache_align - 1;
+ }
+}
+
+/*
+ * Cannot panic here as we may not have a console yet ...
+ */
+
+int
+set_cpufuncs(void)
+{
+ cputype = cp15_midr_get();
+ cputype &= CPU_ID_CPU_MASK;
+
+#if defined(CPU_ARM1176)
+ if (cputype == CPU_ID_ARM1176JZS) {
+ cpufuncs = arm1176_cpufuncs;
+ get_cachetype_cp15();
+ goto out;
+ }
+#endif /* CPU_ARM1176 */
+#if defined(CPU_CORTEXA) || defined(CPU_KRAIT)
+ switch(cputype & CPU_ID_SCHEME_MASK) {
+ case CPU_ID_CORTEXA5:
+ case CPU_ID_CORTEXA7:
+ case CPU_ID_CORTEXA8:
+ case CPU_ID_CORTEXA9:
+ case CPU_ID_CORTEXA12:
+ case CPU_ID_CORTEXA15:
+ case CPU_ID_CORTEXA53:
+ case CPU_ID_CORTEXA57:
+ case CPU_ID_CORTEXA72:
+ case CPU_ID_KRAIT300:
+ cpufuncs = cortexa_cpufuncs;
+ get_cachetype_cp15();
+ goto out;
+ default:
+ break;
+ }
+#endif /* CPU_CORTEXA || CPU_KRAIT */
+
+#if defined(CPU_MV_PJ4B)
+ if (cputype == CPU_ID_MV88SV581X_V7 ||
+ cputype == CPU_ID_MV88SV584X_V7 ||
+ cputype == CPU_ID_ARM_88SV581X_V7) {
+ cpufuncs = pj4bv7_cpufuncs;
+ get_cachetype_cp15();
+ goto out;
+ }
+#endif /* CPU_MV_PJ4B */
+
+ /*
+ * Bzzzz. And the answer was ...
+ */
+ panic("No support for this CPU type (%08x) in kernel", cputype);
+ return(ARCHITECTURE_NOT_PRESENT);
+out:
+ uma_set_align(arm_dcache_align_mask);
+ return (0);
+}
+
+/*
+ * CPU Setup code
+ */
+
+
+#if defined(CPU_ARM1176) \
+ || defined(CPU_MV_PJ4B) \
+ || defined(CPU_CORTEXA) || defined(CPU_KRAIT)
+static __inline void
+cpu_scc_setup_ccnt(void)
+{
+/* This is how you give userland access to the CCNT and PMCn
+ * registers.
+ * BEWARE! This gives write access also, which may not be what
+ * you want!
+ */
+#ifdef _PMC_USER_READ_WRITE_
+ /* Set PMUSERENR[0] to allow userland access */
+ cp15_pmuserenr_set(1);
+#endif
+#if defined(CPU_ARM1176)
+ /* Set PMCR[2,0] to enable counters and reset CCNT */
+ cp15_pmcr_set(5);
+#else
+ /* Set up the PMCCNTR register as a cyclecounter:
+ * Set PMINTENCLR to 0xFFFFFFFF to block interrupts
+ * Set PMCR[2,0] to enable counters and reset CCNT
+ * Set PMCNTENSET to 0x80000000 to enable CCNT */
+ cp15_pminten_clr(0xFFFFFFFF);
+ cp15_pmcr_set(5);
+ cp15_pmcnten_set(0x80000000);
+#endif
+}
+#endif
+
+#if defined(CPU_ARM1176)
+static void
+arm11x6_setup(void)
+{
+ uint32_t auxctrl, auxctrl_wax;
+ uint32_t tmp, tmp2;
+ uint32_t cpuid;
+
+ cpuid = cp15_midr_get();
+
+ auxctrl = 0;
+ auxctrl_wax = ~0;
+
+ /*
+ * Enable an errata workaround
+ */
+ if ((cpuid & CPU_ID_CPU_MASK) == CPU_ID_ARM1176JZS) { /* ARM1176JZSr0 */
+ auxctrl = ARM1176_AUXCTL_PHD;
+ auxctrl_wax = ~ARM1176_AUXCTL_PHD;
+ }
+
+ tmp = cp15_actlr_get();
+ tmp2 = tmp;
+ tmp &= auxctrl_wax;
+ tmp |= auxctrl;
+ if (tmp != tmp2)
+ cp15_actlr_set(tmp);
+
+ cpu_scc_setup_ccnt();
+}
+#endif /* CPU_ARM1176 */
+
+#ifdef CPU_MV_PJ4B
+static void
+pj4bv7_setup(void)
+{
+
+ pj4b_config();
+ cpu_scc_setup_ccnt();
+}
+#endif /* CPU_MV_PJ4B */
+
+#if defined(CPU_CORTEXA) || defined(CPU_KRAIT)
+static void
+cortexa_setup(void)
+{
+
+ cpu_scc_setup_ccnt();
+}
+#endif /* CPU_CORTEXA || CPU_KRAIT */
diff --git a/sys/arm/arm/cpufunc_asm.S b/sys/arm/arm/cpufunc_asm.S
new file mode 100644
index 000000000000..94b34714bfe4
--- /dev/null
+++ b/sys/arm/arm/cpufunc_asm.S
@@ -0,0 +1,77 @@
+/* $NetBSD: cpufunc_asm.S,v 1.12 2003/09/06 09:14:52 rearnsha Exp $ */
+
+/*-
+ * Copyright (c) 1997,1998 Mark Brinicombe.
+ * Copyright (c) 1997 Causality Limited
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Causality Limited.
+ * 4. The name of Causality Limited may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CAUSALITY LIMITED ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL CAUSALITY LIMITED BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * RiscBSD kernel project
+ *
+ * cpufunc.S
+ *
+ * Assembly functions for CPU / MMU / TLB specific operations
+ *
+ * Created : 30/01/97
+ *
+ */
+
+#include <machine/asm.h>
+__FBSDID("$FreeBSD$");
+
+ .text
+ .align 2
+
+ENTRY(cpufunc_nullop)
+ RET
+END(cpufunc_nullop)
+
+/*
+ * Generic functions to read/modify/write the internal coprocessor registers
+ *
+ *
+ * Currently these registers are
+ * c1 - CPU Control
+ *
+ * All other registers are CPU architecture specific
+ */
+
+ENTRY(cpufunc_control)
+ mrc CP15_SCTLR(r3) /* Read the control register */
+ bic r2, r3, r0 /* Clear bits */
+ eor r2, r2, r1 /* XOR bits */
+
+
+ teq r2, r3 /* Only write if there is a change */
+ mcrne CP15_SCTLR(r2) /* Write new control register */
+ mov r0, r3 /* Return old value */
+
+ RET
+END(cpufunc_control)
+
diff --git a/sys/arm/arm/cpufunc_asm_arm11x6.S b/sys/arm/arm/cpufunc_asm_arm11x6.S
new file mode 100644
index 000000000000..8346211ec21e
--- /dev/null
+++ b/sys/arm/arm/cpufunc_asm_arm11x6.S
@@ -0,0 +1,88 @@
+/* $NetBSD: cpufunc_asm_arm11x6.S,v 1.1 2012/07/21 12:19:15 skrll Exp $ */
+
+/*
+ * Copyright (c) 2007 Microsoft
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Microsoft
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTERS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) 2012 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Eben Upton
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#include <machine/asm.h>
+__FBSDID("$FreeBSD$");
+
+ .cpu arm1176jz-s
+
+/*
+ * Preload the cache before issuing the WFI by conditionally disabling the
+ * mcr intstructions the first time around the loop. Ensure the function is
+ * cacheline aligned.
+ */
+ .arch armv6
+ .p2align 5
+
+ENTRY_NP(arm11x6_sleep)
+ mov r0, #0
+ mov r1, #2
+1:
+ subs r1, #1
+ nop
+ mcreq p15, 0, r0, c7, c10, 4 /* data sync barrier */
+ mcreq p15, 0, r0, c7, c0, 4 /* wait for interrupt */
+ nop
+ nop
+ nop
+ bne 1b
+ RET
+END(arm11x6_sleep)
diff --git a/sys/arm/arm/cpufunc_asm_armv7.S b/sys/arm/arm/cpufunc_asm_armv7.S
new file mode 100644
index 000000000000..62ee8f089cc7
--- /dev/null
+++ b/sys/arm/arm/cpufunc_asm_armv7.S
@@ -0,0 +1,44 @@
+/*-
+ * Copyright (c) 2010 Per Odlund <per.odlund@armagedon.se>
+ * Copyright (C) 2011 MARVELL INTERNATIONAL LTD.
+ * All rights reserved.
+ *
+ * Developed by Semihalf.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of MARVELL nor the names of contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <machine/asm.h>
+__FBSDID("$FreeBSD$");
+
+#include <machine/sysreg.h>
+
+ .cpu cortex-a8
+
+ENTRY(armv7_cpu_sleep)
+ dsb /* data synchronization barrier */
+ wfi /* wait for interrupt */
+ RET
+END(armv7_cpu_sleep)
diff --git a/sys/arm/arm/cpufunc_asm_pj4b.S b/sys/arm/arm/cpufunc_asm_pj4b.S
new file mode 100644
index 000000000000..97b7586f836e
--- /dev/null
+++ b/sys/arm/arm/cpufunc_asm_pj4b.S
@@ -0,0 +1,98 @@
+/*-
+ * Copyright (C) 2011 MARVELL INTERNATIONAL LTD.
+ * All rights reserved.
+ *
+ * Developed by Semihalf.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of MARVELL nor the names of contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <machine/asm.h>
+__FBSDID("$FreeBSD$");
+
+#include <machine/param.h>
+
+.Lpj4b_sf_ctrl_reg:
+ .word 0xf1021820
+
+ENTRY(pj4b_config)
+
+ /* Set Auxiliary Debug Modes Control 0 register */
+ mrc p15, 1, r0, c15, c1, 0
+ /* ARMADAXP errata fix: ARM-CPU-6136 */
+ bic r0, r0, #(1 << 12) /* LDSTM first issue is single word */
+
+ orr r0, r0, #(1 << 22) /* DVM_WAKEUP disable */
+ mcr p15, 1, r0, c15, c1, 0
+
+ /* Set Auxiliary Debug Modes Control 1 register */
+ mrc p15, 1, r0, c15, c1, 1
+ /* ARMADAXP errata fix: ARM-CPU-6409 */
+ bic r0, r0, #(1 << 2) /* Disable static branch prediction */
+
+ orr r0, r0, #(1 << 5) /* STREX backoff disable */
+ orr r0, r0, #(1 << 8) /* Internal parity handling disable */
+ orr r0, r0, #(1 << 16) /* Disable data transfer for clean line */
+ mcr p15, 1, r0, c15, c1, 1
+
+ /* Set Auxiliary Function Modes Control 0 register */
+ mrc p15, 1, r0, c15, c2, 0
+#if defined(SMP)
+ orr r0, r0, #(1 << 1) /* SMP/nAMP enabled (coherency) */
+#endif
+ orr r0, r0, #(1 << 2) /* L1 parite enable */
+ orr r0, r0, #(1 << 8) /* Cache and TLB maintenance broadcast enable */
+ mcr p15, 1, r0, c15, c2, 0
+
+ /* Set Auxiliary Debug Modes Control 2 register */
+ mrc p15, 1, r0, c15, c1, 2
+ bic r0, r0, #(1 << 23) /* Enable fast LDR */
+ orr r0, r0, #(1 << 25) /* Intervention Interleave disable */
+ orr r0, r0, #(1 << 27) /* Critical word first sequencing disable */
+ orr r0, r0, #(1 << 29) /* Disable MO device read / write */
+ orr r0, r0, #(1 << 30) /* L1 cache strict round-robin replacement policy*/
+ orr r0, r0, #(1 << 31) /* Enable write evict */
+ mcr p15, 1, r0, c15, c1, 2
+#if defined(SMP)
+ /* Set SMP mode in Auxiliary Control Register */
+ mrc p15, 0, r0, c1, c0, 1
+ orr r0, r0, #(1 << 5)
+ mcr p15, 0, r0, c1, c0, 1
+#endif
+
+ /* Load CPU number */
+ mrc p15, 0, r0, c0, c0, 5
+ and r0, r0, #0xf
+
+ /* SF Enable and invalidate */
+ ldr r1, .Lpj4b_sf_ctrl_reg
+ ldr r2, [r1, r0, lsl #8]
+ orr r2, r2, #(1 << 0)
+ bic r2, r2, #(1 << 8)
+ str r2, [r1, r0, lsl #8]
+
+ RET
+END(pj4b_config)
+
diff --git a/sys/arm/arm/cpuinfo.c b/sys/arm/arm/cpuinfo.c
new file mode 100644
index 000000000000..a14fc578cd05
--- /dev/null
+++ b/sys/arm/arm/cpuinfo.c
@@ -0,0 +1,528 @@
+/*-
+ * Copyright 2014 Svatopluk Kraus <onwahe@gmail.com>
+ * Copyright 2014 Michal Meloun <meloun@miracle.cz>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/pcpu.h>
+#include <sys/smp.h>
+#include <sys/sysctl.h>
+
+#include <machine/cpu.h>
+#include <machine/cpuinfo.h>
+#include <machine/elf.h>
+#include <machine/md_var.h>
+
+void reinit_mmu(uint32_t ttb, uint32_t aux_clr, uint32_t aux_set);
+
+int disable_bp_hardening;
+int spectre_v2_safe = 1;
+
+struct cpuinfo cpuinfo =
+{
+ /* Use safe defaults for start */
+ .dcache_line_size = 32,
+ .dcache_line_mask = 31,
+ .icache_line_size = 32,
+ .icache_line_mask = 31,
+};
+
+static SYSCTL_NODE(_hw, OID_AUTO, cpu, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
+ "CPU");
+static SYSCTL_NODE(_hw_cpu, OID_AUTO, quirks, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
+ "CPU quirks");
+
+/*
+ * Tunable CPU quirks.
+ * Be careful, ACTRL cannot be changed if CPU is started in secure
+ * mode(world) and write to ACTRL can cause exception!
+ * These quirks are intended for optimizing CPU performance, not for
+ * applying errata workarounds. Nobody can expect that CPU with unfixed
+ * errata is stable enough to execute the kernel until quirks are applied.
+ */
+static uint32_t cpu_quirks_actlr_mask;
+SYSCTL_INT(_hw_cpu_quirks, OID_AUTO, actlr_mask,
+ CTLFLAG_RDTUN | CTLFLAG_NOFETCH, &cpu_quirks_actlr_mask, 0,
+ "Bits to be masked in ACTLR");
+
+static uint32_t cpu_quirks_actlr_set;
+SYSCTL_INT(_hw_cpu_quirks, OID_AUTO, actlr_set,
+ CTLFLAG_RDTUN | CTLFLAG_NOFETCH, &cpu_quirks_actlr_set, 0,
+ "Bits to be set in ACTLR");
+
+/* Read and parse CPU id scheme */
+void
+cpuinfo_init(void)
+{
+ uint32_t tmp;
+
+ /*
+ * Prematurely fetch CPU quirks. Standard fetch for tunable
+ * sysctls is handled using SYSINIT, thus too late for boot CPU.
+ * Keep names in sync with sysctls.
+ */
+ TUNABLE_INT_FETCH("hw.cpu.quirks.actlr_mask", &cpu_quirks_actlr_mask);
+ TUNABLE_INT_FETCH("hw.cpu.quirks.actlr_set", &cpu_quirks_actlr_set);
+
+ cpuinfo.midr = cp15_midr_get();
+ /* Test old version id schemes first */
+ if ((cpuinfo.midr & CPU_ID_IMPLEMENTOR_MASK) == CPU_ID_ARM_LTD) {
+ if (CPU_ID_ISOLD(cpuinfo.midr)) {
+ /* obsolete ARMv2 or ARMv3 CPU */
+ cpuinfo.midr = 0;
+ return;
+ }
+ if (CPU_ID_IS7(cpuinfo.midr)) {
+ if ((cpuinfo.midr & (1 << 23)) == 0) {
+ /* obsolete ARMv3 CPU */
+ cpuinfo.midr = 0;
+ return;
+ }
+ /* ARMv4T CPU */
+ cpuinfo.architecture = 1;
+ cpuinfo.revision = (cpuinfo.midr >> 16) & 0x7F;
+ } else {
+ /* ARM new id scheme */
+ cpuinfo.architecture = (cpuinfo.midr >> 16) & 0x0F;
+ cpuinfo.revision = (cpuinfo.midr >> 20) & 0x0F;
+ }
+ } else {
+ /* non ARM -> must be new id scheme */
+ cpuinfo.architecture = (cpuinfo.midr >> 16) & 0x0F;
+ cpuinfo.revision = (cpuinfo.midr >> 20) & 0x0F;
+ }
+ /* Parse rest of MIDR */
+ cpuinfo.implementer = (cpuinfo.midr >> 24) & 0xFF;
+ cpuinfo.part_number = (cpuinfo.midr >> 4) & 0xFFF;
+ cpuinfo.patch = cpuinfo.midr & 0x0F;
+
+ /* CP15 c0,c0 regs 0-7 exist on all CPUs (although aliased with MIDR) */
+ cpuinfo.ctr = cp15_ctr_get();
+ cpuinfo.tcmtr = cp15_tcmtr_get();
+ cpuinfo.tlbtr = cp15_tlbtr_get();
+ cpuinfo.mpidr = cp15_mpidr_get();
+ cpuinfo.revidr = cp15_revidr_get();
+
+ /* if CPU is not v7 cpu id scheme */
+ if (cpuinfo.architecture != 0xF)
+ return;
+ cpuinfo.id_pfr0 = cp15_id_pfr0_get();
+ cpuinfo.id_pfr1 = cp15_id_pfr1_get();
+ cpuinfo.id_dfr0 = cp15_id_dfr0_get();
+ cpuinfo.id_afr0 = cp15_id_afr0_get();
+ cpuinfo.id_mmfr0 = cp15_id_mmfr0_get();
+ cpuinfo.id_mmfr1 = cp15_id_mmfr1_get();
+ cpuinfo.id_mmfr2 = cp15_id_mmfr2_get();
+ cpuinfo.id_mmfr3 = cp15_id_mmfr3_get();
+ cpuinfo.id_isar0 = cp15_id_isar0_get();
+ cpuinfo.id_isar1 = cp15_id_isar1_get();
+ cpuinfo.id_isar2 = cp15_id_isar2_get();
+ cpuinfo.id_isar3 = cp15_id_isar3_get();
+ cpuinfo.id_isar4 = cp15_id_isar4_get();
+ cpuinfo.id_isar5 = cp15_id_isar5_get();
+
+/* Not yet - CBAR only exist on ARM SMP Cortex A CPUs
+ cpuinfo.cbar = cp15_cbar_get();
+*/
+ if (CPU_CT_FORMAT(cpuinfo.ctr) == CPU_CT_ARMV7) {
+ cpuinfo.ccsidr = cp15_ccsidr_get();
+ cpuinfo.clidr = cp15_clidr_get();
+ }
+
+ /* Test if revidr is implemented */
+ if (cpuinfo.revidr == cpuinfo.midr)
+ cpuinfo.revidr = 0;
+
+ /* parsed bits of above registers */
+ /* id_mmfr0 */
+ cpuinfo.outermost_shareability = (cpuinfo.id_mmfr0 >> 8) & 0xF;
+ cpuinfo.shareability_levels = (cpuinfo.id_mmfr0 >> 12) & 0xF;
+ cpuinfo.auxiliary_registers = (cpuinfo.id_mmfr0 >> 20) & 0xF;
+ cpuinfo.innermost_shareability = (cpuinfo.id_mmfr0 >> 28) & 0xF;
+ /* id_mmfr2 */
+ cpuinfo.mem_barrier = (cpuinfo.id_mmfr2 >> 20) & 0xF;
+ /* id_mmfr3 */
+ cpuinfo.coherent_walk = (cpuinfo.id_mmfr3 >> 20) & 0xF;
+ cpuinfo.maintenance_broadcast =(cpuinfo.id_mmfr3 >> 12) & 0xF;
+ /* id_pfr1 */
+ cpuinfo.generic_timer_ext = (cpuinfo.id_pfr1 >> 16) & 0xF;
+ cpuinfo.virtualization_ext = (cpuinfo.id_pfr1 >> 12) & 0xF;
+ cpuinfo.security_ext = (cpuinfo.id_pfr1 >> 4) & 0xF;
+ /* mpidr */
+ cpuinfo.mp_ext = (cpuinfo.mpidr >> 31u) & 0x1;
+
+ /* L1 Cache sizes */
+ if (CPU_CT_FORMAT(cpuinfo.ctr) == CPU_CT_ARMV7) {
+ cpuinfo.dcache_line_size =
+ 1 << (CPU_CT_DMINLINE(cpuinfo.ctr) + 2);
+ cpuinfo.icache_line_size =
+ 1 << (CPU_CT_IMINLINE(cpuinfo.ctr) + 2);
+ } else {
+ cpuinfo.dcache_line_size =
+ 1 << (CPU_CT_xSIZE_LEN(CPU_CT_DSIZE(cpuinfo.ctr)) + 3);
+ cpuinfo.icache_line_size =
+ 1 << (CPU_CT_xSIZE_LEN(CPU_CT_ISIZE(cpuinfo.ctr)) + 3);
+ }
+ cpuinfo.dcache_line_mask = cpuinfo.dcache_line_size - 1;
+ cpuinfo.icache_line_mask = cpuinfo.icache_line_size - 1;
+
+ /* Fill AT_HWCAP bits. */
+ elf_hwcap |= HWCAP_HALF | HWCAP_FAST_MULT; /* Required for all CPUs */
+ elf_hwcap |= HWCAP_TLS | HWCAP_EDSP; /* Required for v6+ CPUs */
+
+ tmp = (cpuinfo.id_isar0 >> 24) & 0xF; /* Divide_instrs */
+ if (tmp >= 1)
+ elf_hwcap |= HWCAP_IDIVT;
+ if (tmp >= 2)
+ elf_hwcap |= HWCAP_IDIVA;
+
+ tmp = (cpuinfo.id_pfr0 >> 4) & 0xF; /* State1 */
+ if (tmp >= 1)
+ elf_hwcap |= HWCAP_THUMB;
+
+ tmp = (cpuinfo.id_pfr0 >> 12) & 0xF; /* State3 */
+ if (tmp >= 1)
+ elf_hwcap |= HWCAP_THUMBEE;
+
+ tmp = (cpuinfo.id_mmfr0 >> 0) & 0xF; /* VMSA */
+ if (tmp >= 5)
+ elf_hwcap |= HWCAP_LPAE;
+
+ /* Fill AT_HWCAP2 bits. */
+ tmp = (cpuinfo.id_isar5 >> 4) & 0xF; /* AES */
+ if (tmp >= 1)
+ elf_hwcap2 |= HWCAP2_AES;
+ if (tmp >= 2)
+ elf_hwcap2 |= HWCAP2_PMULL;
+
+ tmp = (cpuinfo.id_isar5 >> 8) & 0xF; /* SHA1 */
+ if (tmp >= 1)
+ elf_hwcap2 |= HWCAP2_SHA1;
+
+ tmp = (cpuinfo.id_isar5 >> 12) & 0xF; /* SHA2 */
+ if (tmp >= 1)
+ elf_hwcap2 |= HWCAP2_SHA2;
+
+ tmp = (cpuinfo.id_isar5 >> 16) & 0xF; /* CRC32 */
+ if (tmp >= 1)
+ elf_hwcap2 |= HWCAP2_CRC32;
+}
+
+/*
+ * Get bits that must be set or cleared in ACLR register.
+ * Note: Bits in ACLR register are IMPLEMENTATION DEFINED.
+ * Its expected that SCU is in operational state before this
+ * function is called.
+ */
+static void
+cpuinfo_get_actlr_modifier(uint32_t *actlr_mask, uint32_t *actlr_set)
+{
+
+ *actlr_mask = 0;
+ *actlr_set = 0;
+
+ if (cpuinfo.implementer == CPU_IMPLEMENTER_ARM) {
+ switch (cpuinfo.part_number) {
+ case CPU_ARCH_CORTEX_A75:
+ case CPU_ARCH_CORTEX_A73:
+ case CPU_ARCH_CORTEX_A72:
+ case CPU_ARCH_CORTEX_A57:
+ case CPU_ARCH_CORTEX_A53:
+ /* Nothing to do for AArch32 */
+ break;
+ case CPU_ARCH_CORTEX_A17:
+ case CPU_ARCH_CORTEX_A12: /* A12 is merged to A17 */
+ /*
+ * Enable SMP mode
+ */
+ *actlr_mask = (1 << 6);
+ *actlr_set = (1 << 6);
+ break;
+ case CPU_ARCH_CORTEX_A15:
+ /*
+ * Enable snoop-delayed exclusive handling
+ * Enable SMP mode
+ */
+ *actlr_mask = (1U << 31) |(1 << 6);
+ *actlr_set = (1U << 31) |(1 << 6);
+ break;
+ case CPU_ARCH_CORTEX_A9:
+ /*
+ * Disable exclusive L1/L2 cache control
+ * Enable SMP mode
+ * Enable Cache and TLB maintenance broadcast
+ */
+ *actlr_mask = (1 << 7) | (1 << 6) | (1 << 0);
+ *actlr_set = (1 << 6) | (1 << 0);
+ break;
+ case CPU_ARCH_CORTEX_A8:
+ /*
+ * Enable L2 cache
+ * Enable L1 data cache hardware alias checks
+ */
+ *actlr_mask = (1 << 1) | (1 << 0);
+ *actlr_set = (1 << 1);
+ break;
+ case CPU_ARCH_CORTEX_A7:
+ /*
+ * Enable SMP mode
+ */
+ *actlr_mask = (1 << 6);
+ *actlr_set = (1 << 6);
+ break;
+ case CPU_ARCH_CORTEX_A5:
+ /*
+ * Disable exclusive L1/L2 cache control
+ * Enable SMP mode
+ * Enable Cache and TLB maintenance broadcast
+ */
+ *actlr_mask = (1 << 7) | (1 << 6) | (1 << 0);
+ *actlr_set = (1 << 6) | (1 << 0);
+ break;
+ case CPU_ARCH_ARM1176:
+ /*
+ * Restrict cache size to 16KB
+ * Enable the return stack
+ * Enable dynamic branch prediction
+ * Enable static branch prediction
+ */
+ *actlr_mask = (1 << 6) | (1 << 2) | (1 << 1) | (1 << 0);
+ *actlr_set = (1 << 6) | (1 << 2) | (1 << 1) | (1 << 0);
+ break;
+ }
+ return;
+ }
+}
+
+/* Reinitialize MMU to final kernel mapping and apply all CPU quirks. */
+void
+cpuinfo_reinit_mmu(uint32_t ttb)
+{
+ uint32_t actlr_mask;
+ uint32_t actlr_set;
+
+ cpuinfo_get_actlr_modifier(&actlr_mask, &actlr_set);
+ actlr_mask |= cpu_quirks_actlr_mask;
+ actlr_set |= cpu_quirks_actlr_set;
+ reinit_mmu(ttb, actlr_mask, actlr_set);
+}
+
+static bool
+modify_actlr(uint32_t clear, uint32_t set)
+{
+ uint32_t reg, newreg;
+
+ reg = cp15_actlr_get();
+ newreg = reg;
+ newreg &= ~clear;
+ newreg |= set;
+ if (reg == newreg)
+ return (true);
+ cp15_actlr_set(newreg);
+
+ reg = cp15_actlr_get();
+ if (reg == newreg)
+ return (true);
+ return (false);
+}
+
+/* Apply/restore BP hardening on current core. */
+static int
+apply_bp_hardening(bool enable, int kind, bool actrl, uint32_t set_mask)
+{
+ if (enable) {
+ if (actrl && !modify_actlr(0, set_mask))
+ return (-1);
+ PCPU_SET(bp_harden_kind, kind);
+ } else {
+ PCPU_SET(bp_harden_kind, PCPU_BP_HARDEN_KIND_NONE);
+ if (actrl)
+ modify_actlr(~0, PCPU_GET(original_actlr));
+ spectre_v2_safe = 0;
+ }
+ return (0);
+}
+
+static void
+handle_bp_hardening(bool enable)
+{
+ int kind;
+ char *kind_str;
+
+ kind = PCPU_BP_HARDEN_KIND_NONE;
+ /*
+ * Note: Access to ACTRL is locked to secure world on most boards.
+ * This means that full BP hardening depends on updated u-boot/firmware
+ * or is impossible at all (if secure monitor is in on-chip ROM).
+ */
+ if (cpuinfo.implementer == CPU_IMPLEMENTER_ARM) {
+ switch (cpuinfo.part_number) {
+ case CPU_ARCH_CORTEX_A8:
+ /*
+ * For Cortex-A8, IBE bit must be set otherwise
+ * BPIALL is effectively NOP.
+ * Unfortunately, Cortex-A is also affected by
+ * ARM erratum 687067 which causes non-working
+ * BPIALL if IBE bit is set and 'Instruction L1 System
+ * Array Debug Register 0' is not 0.
+ * This register is not reset on power-up and is
+ * accessible only from secure world, so we cannot do
+ * nothing (nor detect) to fix this issue.
+ * I afraid that on chip ROM based secure monitor on
+ * AM335x (BeagleBone) doesn't reset this debug
+ * register.
+ */
+ kind = PCPU_BP_HARDEN_KIND_BPIALL;
+ if (apply_bp_hardening(enable, kind, true, 1 << 6) != 0)
+ goto actlr_err;
+ break;
+ break;
+
+ case CPU_ARCH_CORTEX_A9:
+ case CPU_ARCH_CORTEX_A12:
+ case CPU_ARCH_CORTEX_A17:
+ case CPU_ARCH_CORTEX_A57:
+ case CPU_ARCH_CORTEX_A72:
+ case CPU_ARCH_CORTEX_A73:
+ case CPU_ARCH_CORTEX_A75:
+ kind = PCPU_BP_HARDEN_KIND_BPIALL;
+ if (apply_bp_hardening(enable, kind, false, 0) != 0)
+ goto actlr_err;
+ break;
+
+ case CPU_ARCH_CORTEX_A15:
+ /*
+ * For Cortex-A15, set 'Enable invalidates of BTB' bit.
+ * Despite this, the BPIALL is still effectively NOP,
+ * but with this bit set, the ICIALLU also flushes
+ * branch predictor as side effect.
+ */
+ kind = PCPU_BP_HARDEN_KIND_ICIALLU;
+ if (apply_bp_hardening(enable, kind, true, 1 << 0) != 0)
+ goto actlr_err;
+ break;
+
+ default:
+ break;
+ }
+ } else if (cpuinfo.implementer == CPU_IMPLEMENTER_QCOM) {
+ printf("!!!WARNING!!! CPU(%d) is vulnerable to speculative "
+ "branch attacks. !!!\n"
+ "Qualcomm Krait cores are known (or believed) to be "
+ "vulnerable to \n"
+ "speculative branch attacks, no mitigation exists yet.\n",
+ PCPU_GET(cpuid));
+ goto unkonown_mitigation;
+ } else {
+ goto unkonown_mitigation;
+ }
+
+ if (bootverbose) {
+ switch (kind) {
+ case PCPU_BP_HARDEN_KIND_NONE:
+ kind_str = "not necessary";
+ break;
+ case PCPU_BP_HARDEN_KIND_BPIALL:
+ kind_str = "BPIALL";
+ break;
+ case PCPU_BP_HARDEN_KIND_ICIALLU:
+ kind_str = "ICIALLU";
+ break;
+ default:
+ panic("Unknown BP hardering kind (%d).", kind);
+ }
+ printf("CPU(%d) applied BP hardening: %s\n", PCPU_GET(cpuid),
+ kind_str);
+ }
+
+ return;
+
+unkonown_mitigation:
+ PCPU_SET(bp_harden_kind, PCPU_BP_HARDEN_KIND_NONE);
+ spectre_v2_safe = 0;
+ return;
+
+actlr_err:
+ PCPU_SET(bp_harden_kind, PCPU_BP_HARDEN_KIND_NONE);
+ spectre_v2_safe = 0;
+ printf("!!!WARNING!!! CPU(%d) is vulnerable to speculative branch "
+ "attacks. !!!\n"
+ "We cannot enable required bit(s) in ACTRL register\n"
+ "because it's locked by secure monitor and/or firmware.\n",
+ PCPU_GET(cpuid));
+}
+
+void
+cpuinfo_init_bp_hardening(void)
+{
+
+ /*
+ * Store original unmodified ACTRL, so we can restore it when
+ * BP hardening is disabled by sysctl.
+ */
+ PCPU_SET(original_actlr, cp15_actlr_get());
+ handle_bp_hardening(true);
+}
+
+static void
+bp_hardening_action(void *arg)
+{
+
+ handle_bp_hardening(disable_bp_hardening == 0);
+}
+
+static int
+sysctl_disable_bp_hardening(SYSCTL_HANDLER_ARGS)
+{
+ int rv;
+
+ rv = sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req);
+
+ if (!rv && req->newptr) {
+ spectre_v2_safe = 1;
+ dmb();
+#ifdef SMP
+ smp_rendezvous_cpus(all_cpus, smp_no_rendezvous_barrier,
+ bp_hardening_action, NULL, NULL);
+#else
+ bp_hardening_action(NULL);
+#endif
+ }
+
+ return (rv);
+}
+
+SYSCTL_PROC(_machdep, OID_AUTO, disable_bp_hardening,
+ CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
+ &disable_bp_hardening, 0, sysctl_disable_bp_hardening, "I",
+ "Disable BP hardening mitigation.");
+
+SYSCTL_INT(_machdep, OID_AUTO, spectre_v2_safe, CTLFLAG_RD,
+ &spectre_v2_safe, 0, "System is safe to Spectre Version 2 attacks");
diff --git a/sys/arm/arm/db_disasm.c b/sys/arm/arm/db_disasm.c
new file mode 100644
index 000000000000..012773415b40
--- /dev/null
+++ b/sys/arm/arm/db_disasm.c
@@ -0,0 +1,81 @@
+/* $NetBSD: db_disasm.c,v 1.4 2003/07/15 00:24:38 lukem Exp $ */
+
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 1996 Mark Brinicombe.
+ * Copyright (c) 1996 Brini.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Brini.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#include <sys/param.h>
+#include <machine/db_machdep.h>
+#include <ddb/ddb.h>
+#include <ddb/db_access.h>
+#include <ddb/db_sym.h>
+
+#include <machine/disassem.h>
+
+/* Glue code to interface db_disasm to the generic ARM disassembler */
+
+static u_int db_disasm_read_word(u_int);
+static void db_disasm_printaddr(u_int);
+
+static const disasm_interface_t db_disasm_interface = {
+ db_disasm_read_word,
+ db_disasm_printaddr,
+ db_printf
+};
+
+static u_int
+db_disasm_read_word(u_int address)
+{
+
+ return db_get_value(address, 4, 0);
+}
+
+static void
+db_disasm_printaddr(u_int address)
+{
+
+ db_printsym((db_addr_t)address, DB_STGY_ANY);
+}
+
+vm_offset_t
+db_disasm(vm_offset_t loc, bool altfmt)
+{
+
+ return disasm(&db_disasm_interface, loc, altfmt);
+}
+
+/* End of db_disasm.c */
diff --git a/sys/arm/arm/db_interface.c b/sys/arm/arm/db_interface.c
new file mode 100644
index 000000000000..73d0cc478617
--- /dev/null
+++ b/sys/arm/arm/db_interface.c
@@ -0,0 +1,327 @@
+/* $NetBSD: db_interface.c,v 1.33 2003/08/25 04:51:10 mrg Exp $ */
+
+/*-
+ * Copyright (c) 1996 Scott K. Stevens
+ *
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ *
+ * From: db_interface.c,v 2.4 1991/02/05 17:11:13 mrt (CMU)
+ */
+
+/*
+ * Interface to new debugger.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#include "opt_ddb.h"
+
+#include <sys/param.h>
+#include <sys/cons.h>
+#include <sys/proc.h>
+#include <sys/reboot.h>
+#include <sys/systm.h> /* just for boothowto */
+#include <sys/exec.h>
+#ifdef KDB
+#include <sys/kdb.h>
+#endif
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <vm/vm_map.h>
+#include <vm/vm_extern.h>
+
+#include <machine/db_machdep.h>
+#include <machine/cpu.h>
+#include <machine/machdep.h>
+#include <machine/vmparam.h>
+
+#include <ddb/ddb.h>
+#include <ddb/db_access.h>
+#include <ddb/db_command.h>
+#include <ddb/db_output.h>
+#include <ddb/db_variables.h>
+#include <ddb/db_sym.h>
+
+static int nil = 0;
+
+int db_access_und_sp (struct db_variable *, db_expr_t *, int);
+int db_access_abt_sp (struct db_variable *, db_expr_t *, int);
+int db_access_irq_sp (struct db_variable *, db_expr_t *, int);
+
+static db_varfcn_t db_frame;
+
+#define DB_OFFSET(x) (db_expr_t *)offsetof(struct trapframe, x)
+struct db_variable db_regs[] = {
+ { "spsr", DB_OFFSET(tf_spsr), db_frame },
+ { "r0", DB_OFFSET(tf_r0), db_frame },
+ { "r1", DB_OFFSET(tf_r1), db_frame },
+ { "r2", DB_OFFSET(tf_r2), db_frame },
+ { "r3", DB_OFFSET(tf_r3), db_frame },
+ { "r4", DB_OFFSET(tf_r4), db_frame },
+ { "r5", DB_OFFSET(tf_r5), db_frame },
+ { "r6", DB_OFFSET(tf_r6), db_frame },
+ { "r7", DB_OFFSET(tf_r7), db_frame },
+ { "r8", DB_OFFSET(tf_r8), db_frame },
+ { "r9", DB_OFFSET(tf_r9), db_frame },
+ { "r10", DB_OFFSET(tf_r10), db_frame },
+ { "r11", DB_OFFSET(tf_r11), db_frame },
+ { "r12", DB_OFFSET(tf_r12), db_frame },
+ { "usr_sp", DB_OFFSET(tf_usr_sp), db_frame },
+ { "usr_lr", DB_OFFSET(tf_usr_lr), db_frame },
+ { "svc_sp", DB_OFFSET(tf_svc_sp), db_frame },
+ { "svc_lr", DB_OFFSET(tf_svc_lr), db_frame },
+ { "pc", DB_OFFSET(tf_pc), db_frame },
+ { "und_sp", &nil, db_access_und_sp, },
+ { "abt_sp", &nil, db_access_abt_sp, },
+ { "irq_sp", &nil, db_access_irq_sp, },
+};
+
+struct db_variable *db_eregs = db_regs + nitems(db_regs);
+
+int
+db_access_und_sp(struct db_variable *vp, db_expr_t *valp, int rw)
+{
+
+ if (rw == DB_VAR_GET) {
+ *valp = get_stackptr(PSR_UND32_MODE);
+ return (1);
+ }
+ return (0);
+}
+
+int
+db_access_abt_sp(struct db_variable *vp, db_expr_t *valp, int rw)
+{
+
+ if (rw == DB_VAR_GET) {
+ *valp = get_stackptr(PSR_ABT32_MODE);
+ return (1);
+ }
+ return (0);
+}
+
+int
+db_access_irq_sp(struct db_variable *vp, db_expr_t *valp, int rw)
+{
+
+ if (rw == DB_VAR_GET) {
+ *valp = get_stackptr(PSR_IRQ32_MODE);
+ return (1);
+ }
+ return (0);
+}
+
+int db_frame(struct db_variable *vp, db_expr_t *valp, int rw)
+{
+ int *reg;
+
+ if (kdb_frame == NULL)
+ return (0);
+
+ reg = (int *)((uintptr_t)kdb_frame + (db_expr_t)vp->valuep);
+ if (rw == DB_VAR_GET)
+ *valp = *reg;
+ else
+ *reg = *valp;
+ return (1);
+}
+
+void
+db_show_mdpcpu(struct pcpu *pc)
+{
+
+ db_printf("curpmap = %p\n", pc->pc_curpmap);
+}
+
+int
+db_validate_address(vm_offset_t addr)
+{
+ struct proc *p = curproc;
+ struct pmap *pmap;
+
+ if (!p || !p->p_vmspace || !p->p_vmspace->vm_map.pmap ||
+#ifndef ARM32_NEW_VM_LAYOUT
+ addr >= VM_MAXUSER_ADDRESS
+#else
+ addr >= VM_MIN_KERNEL_ADDRESS
+#endif
+ )
+ pmap = kernel_pmap;
+ else
+ pmap = p->p_vmspace->vm_map.pmap;
+
+ return (pmap_extract(pmap, addr) == FALSE);
+}
+
+/*
+ * Read bytes from kernel address space for debugger.
+ */
+int
+db_read_bytes(addr, size, data)
+ vm_offset_t addr;
+ size_t size;
+ char *data;
+{
+ char *src = (char *)addr;
+
+ if (db_validate_address((u_int)src)) {
+ db_printf("address %p is invalid\n", src);
+ return (-1);
+ }
+
+ if (size == 4 && (addr & 3) == 0 && ((uintptr_t)data & 3) == 0) {
+ *((int*)data) = *((int*)src);
+ return (0);
+ }
+
+ if (size == 2 && (addr & 1) == 0 && ((uintptr_t)data & 1) == 0) {
+ *((short*)data) = *((short*)src);
+ return (0);
+ }
+
+ while (size-- > 0) {
+ if (db_validate_address((u_int)src)) {
+ db_printf("address %p is invalid\n", src);
+ return (-1);
+ }
+ *data++ = *src++;
+ }
+ return (0);
+}
+
+/*
+ * Write bytes to kernel address space for debugger.
+ */
+int
+db_write_bytes(vm_offset_t addr, size_t size, char *data)
+{
+ char *dst;
+ size_t loop;
+
+ dst = (char *)addr;
+ if (db_validate_address((u_int)dst)) {
+ db_printf("address %p is invalid\n", dst);
+ return (0);
+ }
+
+ if (size == 4 && (addr & 3) == 0 && ((uintptr_t)data & 3) == 0)
+ *((int*)dst) = *((int*)data);
+ else
+ if (size == 2 && (addr & 1) == 0 && ((uintptr_t)data & 1) == 0)
+ *((short*)dst) = *((short*)data);
+ else {
+ loop = size;
+ while (loop-- > 0) {
+ if (db_validate_address((u_int)dst)) {
+ db_printf("address %p is invalid\n", dst);
+ return (-1);
+ }
+ *dst++ = *data++;
+ }
+ }
+
+ /* make sure the caches and memory are in sync */
+ icache_sync(addr, size);
+
+ /* In case the current page tables have been modified ... */
+ tlb_flush_all();
+ return (0);
+}
+
+static u_int
+db_fetch_reg(int reg)
+{
+
+ switch (reg) {
+ case 0:
+ return (kdb_frame->tf_r0);
+ case 1:
+ return (kdb_frame->tf_r1);
+ case 2:
+ return (kdb_frame->tf_r2);
+ case 3:
+ return (kdb_frame->tf_r3);
+ case 4:
+ return (kdb_frame->tf_r4);
+ case 5:
+ return (kdb_frame->tf_r5);
+ case 6:
+ return (kdb_frame->tf_r6);
+ case 7:
+ return (kdb_frame->tf_r7);
+ case 8:
+ return (kdb_frame->tf_r8);
+ case 9:
+ return (kdb_frame->tf_r9);
+ case 10:
+ return (kdb_frame->tf_r10);
+ case 11:
+ return (kdb_frame->tf_r11);
+ case 12:
+ return (kdb_frame->tf_r12);
+ case 13:
+ return (kdb_frame->tf_svc_sp);
+ case 14:
+ return (kdb_frame->tf_svc_lr);
+ case 15:
+ return (kdb_frame->tf_pc);
+ default:
+ panic("db_fetch_reg: botch");
+ }
+}
+
+static u_int
+db_branch_taken_read_int(void *cookie __unused, vm_offset_t offset, u_int *val)
+{
+ u_int ret;
+
+ db_read_bytes(offset, 4, (char *)&ret);
+ *val = ret;
+
+ return (0);
+}
+
+static u_int
+db_branch_taken_fetch_reg(void *cookie __unused, int reg)
+{
+
+ return (db_fetch_reg(reg));
+}
+
+u_int
+branch_taken(u_int insn, db_addr_t pc)
+{
+ register_t new_pc;
+ int ret;
+
+ ret = arm_predict_branch(NULL, insn, (register_t)pc, &new_pc,
+ db_branch_taken_fetch_reg, db_branch_taken_read_int);
+
+ if (ret != 0)
+ kdb_reenter();
+
+ return (new_pc);
+}
diff --git a/sys/arm/arm/db_trace.c b/sys/arm/arm/db_trace.c
new file mode 100644
index 000000000000..195ed0f4e3d9
--- /dev/null
+++ b/sys/arm/arm/db_trace.c
@@ -0,0 +1,186 @@
+/* $NetBSD: db_trace.c,v 1.8 2003/01/17 22:28:48 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2000, 2001 Ben Harris
+ * Copyright (c) 1996 Scott K. Stevens
+ *
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+#include "opt_ddb.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <sys/proc.h>
+#include <sys/kdb.h>
+#include <sys/stack.h>
+
+#include <machine/armreg.h>
+#include <machine/asm.h>
+#include <machine/cpufunc.h>
+#include <machine/db_machdep.h>
+#include <machine/debug_monitor.h>
+#include <machine/pcb.h>
+#include <machine/stack.h>
+#include <machine/vmparam.h>
+
+#include <ddb/ddb.h>
+#include <ddb/db_access.h>
+#include <ddb/db_sym.h>
+#include <ddb/db_output.h>
+
+static void
+db_stack_trace_cmd(struct unwind_state *state)
+{
+ const char *name;
+ db_expr_t value;
+ db_expr_t offset;
+ c_db_sym_t sym;
+ u_int reg, i;
+ char *sep;
+ uint16_t upd_mask;
+ bool finished;
+
+ finished = false;
+ while (!finished) {
+ finished = unwind_stack_one(state, 1);
+
+ /* Print the frame details */
+ sym = db_search_symbol(state->start_pc, DB_STGY_ANY, &offset);
+ if (sym == C_DB_SYM_NULL) {
+ value = 0;
+ name = "(null)";
+ } else
+ db_symbol_values(sym, &name, &value);
+ db_printf("%s() at ", name);
+ db_printsym(state->start_pc, DB_STGY_PROC);
+ db_printf("\n");
+ db_printf("\t pc = 0x%08x lr = 0x%08x (", state->start_pc,
+ state->registers[LR]);
+ db_printsym(state->registers[LR], DB_STGY_PROC);
+ db_printf(")\n");
+ db_printf("\t sp = 0x%08x fp = 0x%08x",
+ state->registers[SP], state->registers[FP]);
+
+ /* Don't print the registers we have already printed */
+ upd_mask = state->update_mask &
+ ~((1 << SP) | (1 << FP) | (1 << LR) | (1 << PC));
+ sep = "\n\t";
+ for (i = 0, reg = 0; upd_mask != 0; upd_mask >>= 1, reg++) {
+ if ((upd_mask & 1) != 0) {
+ db_printf("%s%sr%d = 0x%08x", sep,
+ (reg < 10) ? " " : "", reg,
+ state->registers[reg]);
+ i++;
+ if (i == 2) {
+ sep = "\n\t";
+ i = 0;
+ } else
+ sep = " ";
+ }
+ }
+ db_printf("\n");
+
+ if (finished)
+ break;
+
+ /*
+ * Stop if directed to do so, or if we've unwound back to the
+ * kernel entry point, or if the unwind function didn't change
+ * anything (to avoid getting stuck in this loop forever).
+ * If the latter happens, it's an indication that the unwind
+ * information is incorrect somehow for the function named in
+ * the last frame printed before you see the unwind failure
+ * message (maybe it needs a STOP_UNWINDING).
+ */
+ if (state->registers[PC] < VM_MIN_KERNEL_ADDRESS) {
+ db_printf("Unable to unwind into user mode\n");
+ finished = true;
+ } else if (state->update_mask == 0) {
+ db_printf("Unwind failure (no registers changed)\n");
+ finished = true;
+ }
+ }
+}
+
+void
+db_md_list_watchpoints(void)
+{
+
+ dbg_show_watchpoint();
+}
+
+int
+db_md_clr_watchpoint(db_expr_t addr, db_expr_t size)
+{
+
+ return (dbg_remove_watchpoint(addr, size));
+}
+
+int
+db_md_set_watchpoint(db_expr_t addr, db_expr_t size)
+{
+
+ return (dbg_setup_watchpoint(addr, size, HW_WATCHPOINT_RW));
+}
+
+int
+db_trace_thread(struct thread *thr, int count)
+{
+ struct unwind_state state;
+ struct pcb *ctx;
+
+ if (thr != curthread) {
+ ctx = kdb_thr_ctx(thr);
+
+ state.registers[FP] = ctx->pcb_regs.sf_r11;
+ state.registers[SP] = ctx->pcb_regs.sf_sp;
+ state.registers[LR] = ctx->pcb_regs.sf_lr;
+ state.registers[PC] = ctx->pcb_regs.sf_pc;
+
+ db_stack_trace_cmd(&state);
+ } else
+ db_trace_self();
+ return (0);
+}
+
+void
+db_trace_self(void)
+{
+ struct unwind_state state;
+ uint32_t sp;
+
+ /* Read the stack pointer */
+ __asm __volatile("mov %0, sp" : "=&r" (sp));
+
+ state.registers[FP] = (uint32_t)__builtin_frame_address(0);
+ state.registers[SP] = sp;
+ state.registers[LR] = (uint32_t)__builtin_return_address(0);
+ state.registers[PC] = (uint32_t)db_trace_self;
+
+ db_stack_trace_cmd(&state);
+}
diff --git a/sys/arm/arm/debug_monitor.c b/sys/arm/arm/debug_monitor.c
new file mode 100644
index 000000000000..ddf3e8e67b25
--- /dev/null
+++ b/sys/arm/arm/debug_monitor.c
@@ -0,0 +1,1039 @@
+/*
+ * Copyright (c) 2015 Juniper Networks Inc.
+ * All rights reserved.
+ *
+ * Developed by Semihalf.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_ddb.h"
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/kdb.h>
+#include <sys/pcpu.h>
+#include <sys/smp.h>
+#include <sys/systm.h>
+
+#include <machine/atomic.h>
+#include <machine/armreg.h>
+#include <machine/cpu.h>
+#include <machine/debug_monitor.h>
+#include <machine/kdb.h>
+#include <machine/pcb.h>
+#include <machine/reg.h>
+
+#include <ddb/ddb.h>
+#include <ddb/db_access.h>
+#include <ddb/db_sym.h>
+
+enum dbg_t {
+ DBG_TYPE_BREAKPOINT = 0,
+ DBG_TYPE_WATCHPOINT = 1,
+};
+
+struct dbg_wb_conf {
+ enum dbg_t type;
+ enum dbg_access_t access;
+ db_addr_t address;
+ db_expr_t size;
+ u_int slot;
+};
+
+static int dbg_reset_state(void);
+static int dbg_setup_breakpoint(db_expr_t, db_expr_t, u_int);
+static int dbg_remove_breakpoint(u_int);
+static u_int dbg_find_slot(enum dbg_t, db_expr_t);
+static boolean_t dbg_check_slot_free(enum dbg_t, u_int);
+
+static int dbg_remove_xpoint(struct dbg_wb_conf *);
+static int dbg_setup_xpoint(struct dbg_wb_conf *);
+
+static int dbg_capable_var; /* Indicates that machine is capable of using
+ HW watchpoints/breakpoints */
+
+static uint32_t dbg_model; /* Debug Arch. Model */
+static boolean_t dbg_ossr; /* OS Save and Restore implemented */
+
+static uint32_t dbg_watchpoint_num;
+static uint32_t dbg_breakpoint_num;
+
+/* ID_DFR0 - Debug Feature Register 0 */
+#define ID_DFR0_CP_DEBUG_M_SHIFT 0
+#define ID_DFR0_CP_DEBUG_M_MASK (0xF << ID_DFR0_CP_DEBUG_M_SHIFT)
+#define ID_DFR0_CP_DEBUG_M_NS (0x0) /* Not supported */
+#define ID_DFR0_CP_DEBUG_M_V6 (0x2) /* v6 Debug arch. CP14 access */
+#define ID_DFR0_CP_DEBUG_M_V6_1 (0x3) /* v6.1 Debug arch. CP14 access */
+#define ID_DFR0_CP_DEBUG_M_V7 (0x4) /* v7 Debug arch. CP14 access */
+#define ID_DFR0_CP_DEBUG_M_V7_1 (0x5) /* v7.1 Debug arch. CP14 access */
+
+/* DBGDIDR - Debug ID Register */
+#define DBGDIDR_WRPS_SHIFT 28
+#define DBGDIDR_WRPS_MASK (0xF << DBGDIDR_WRPS_SHIFT)
+#define DBGDIDR_WRPS_NUM(reg) \
+ ((((reg) & DBGDIDR_WRPS_MASK) >> DBGDIDR_WRPS_SHIFT) + 1)
+
+#define DBGDIDR_BRPS_SHIFT 24
+#define DBGDIDR_BRPS_MASK (0xF << DBGDIDR_BRPS_SHIFT)
+#define DBGDIDR_BRPS_NUM(reg) \
+ ((((reg) & DBGDIDR_BRPS_MASK) >> DBGDIDR_BRPS_SHIFT) + 1)
+
+/* DBGPRSR - Device Powerdown and Reset Status Register */
+#define DBGPRSR_PU (1 << 0) /* Powerup status */
+
+/* DBGOSLSR - OS Lock Status Register */
+#define DBGOSLSR_OSLM0 (1 << 0)
+
+/* DBGOSDLR - OS Double Lock Register */
+#define DBGPRSR_DLK (1 << 0) /* OS Double Lock set */
+
+/* DBGDSCR - Debug Status and Control Register */
+#define DBGSCR_MDBG_EN (1 << 15) /* Monitor debug-mode enable */
+
+/* DBGWVR - Watchpoint Value Register */
+#define DBGWVR_ADDR_MASK (~0x3U)
+
+/* Watchpoints/breakpoints control register bitfields */
+#define DBG_WB_CTRL_LEN_1 (0x1 << 5)
+#define DBG_WB_CTRL_LEN_2 (0x3 << 5)
+#define DBG_WB_CTRL_LEN_4 (0xf << 5)
+#define DBG_WB_CTRL_LEN_8 (0xff << 5)
+#define DBG_WB_CTRL_LEN_MASK(x) ((x) & (0xff << 5))
+#define DBG_WB_CTRL_EXEC (0x0 << 3)
+#define DBG_WB_CTRL_LOAD (0x1 << 3)
+#define DBG_WB_CTRL_STORE (0x2 << 3)
+#define DBG_WB_CTRL_ACCESS_MASK(x) ((x) & (0x3 << 3))
+
+/* Common for breakpoint and watchpoint */
+#define DBG_WB_CTRL_PL1 (0x1 << 1)
+#define DBG_WB_CTRL_PL0 (0x2 << 1)
+#define DBG_WB_CTRL_PLX_MASK(x) ((x) & (0x3 << 1))
+#define DBG_WB_CTRL_E (0x1 << 0)
+
+/*
+ * Watchpoint/breakpoint helpers
+ */
+#define DBG_BKPT_BT_SLOT 0 /* Slot for branch taken */
+#define DBG_BKPT_BNT_SLOT 1 /* Slot for branch not taken */
+
+#define OP2_SHIFT 4
+
+/* Opc2 numbers for coprocessor instructions */
+#define DBG_WB_BVR 4
+#define DBG_WB_BCR 5
+#define DBG_WB_WVR 6
+#define DBG_WB_WCR 7
+
+#define DBG_REG_BASE_BVR (DBG_WB_BVR << OP2_SHIFT)
+#define DBG_REG_BASE_BCR (DBG_WB_BCR << OP2_SHIFT)
+#define DBG_REG_BASE_WVR (DBG_WB_WVR << OP2_SHIFT)
+#define DBG_REG_BASE_WCR (DBG_WB_WCR << OP2_SHIFT)
+
+#define DBG_WB_READ(cn, cm, op2, val) do { \
+ __asm __volatile("mrc p14, 0, %0, " #cn "," #cm "," #op2 : "=r" (val)); \
+} while (0)
+
+#define DBG_WB_WRITE(cn, cm, op2, val) do { \
+ __asm __volatile("mcr p14, 0, %0, " #cn "," #cm "," #op2 :: "r" (val)); \
+} while (0)
+
+#define READ_WB_REG_CASE(op2, m, val) \
+ case (((op2) << OP2_SHIFT) + m): \
+ DBG_WB_READ(c0, c ## m, op2, val); \
+ break
+
+#define WRITE_WB_REG_CASE(op2, m, val) \
+ case (((op2) << OP2_SHIFT) + m): \
+ DBG_WB_WRITE(c0, c ## m, op2, val); \
+ break
+
+#define SWITCH_CASES_READ_WB_REG(op2, val) \
+ READ_WB_REG_CASE(op2, 0, val); \
+ READ_WB_REG_CASE(op2, 1, val); \
+ READ_WB_REG_CASE(op2, 2, val); \
+ READ_WB_REG_CASE(op2, 3, val); \
+ READ_WB_REG_CASE(op2, 4, val); \
+ READ_WB_REG_CASE(op2, 5, val); \
+ READ_WB_REG_CASE(op2, 6, val); \
+ READ_WB_REG_CASE(op2, 7, val); \
+ READ_WB_REG_CASE(op2, 8, val); \
+ READ_WB_REG_CASE(op2, 9, val); \
+ READ_WB_REG_CASE(op2, 10, val); \
+ READ_WB_REG_CASE(op2, 11, val); \
+ READ_WB_REG_CASE(op2, 12, val); \
+ READ_WB_REG_CASE(op2, 13, val); \
+ READ_WB_REG_CASE(op2, 14, val); \
+ READ_WB_REG_CASE(op2, 15, val)
+
+#define SWITCH_CASES_WRITE_WB_REG(op2, val) \
+ WRITE_WB_REG_CASE(op2, 0, val); \
+ WRITE_WB_REG_CASE(op2, 1, val); \
+ WRITE_WB_REG_CASE(op2, 2, val); \
+ WRITE_WB_REG_CASE(op2, 3, val); \
+ WRITE_WB_REG_CASE(op2, 4, val); \
+ WRITE_WB_REG_CASE(op2, 5, val); \
+ WRITE_WB_REG_CASE(op2, 6, val); \
+ WRITE_WB_REG_CASE(op2, 7, val); \
+ WRITE_WB_REG_CASE(op2, 8, val); \
+ WRITE_WB_REG_CASE(op2, 9, val); \
+ WRITE_WB_REG_CASE(op2, 10, val); \
+ WRITE_WB_REG_CASE(op2, 11, val); \
+ WRITE_WB_REG_CASE(op2, 12, val); \
+ WRITE_WB_REG_CASE(op2, 13, val); \
+ WRITE_WB_REG_CASE(op2, 14, val); \
+ WRITE_WB_REG_CASE(op2, 15, val)
+
+static uint32_t
+dbg_wb_read_reg(int reg, int n)
+{
+ uint32_t val;
+
+ val = 0;
+
+ switch (reg + n) {
+ SWITCH_CASES_READ_WB_REG(DBG_WB_WVR, val);
+ SWITCH_CASES_READ_WB_REG(DBG_WB_WCR, val);
+ SWITCH_CASES_READ_WB_REG(DBG_WB_BVR, val);
+ SWITCH_CASES_READ_WB_REG(DBG_WB_BCR, val);
+ default:
+ db_printf(
+ "trying to read from CP14 reg. using wrong opc2 %d\n",
+ reg >> OP2_SHIFT);
+ }
+
+ return (val);
+}
+
+static void
+dbg_wb_write_reg(int reg, int n, uint32_t val)
+{
+
+ switch (reg + n) {
+ SWITCH_CASES_WRITE_WB_REG(DBG_WB_WVR, val);
+ SWITCH_CASES_WRITE_WB_REG(DBG_WB_WCR, val);
+ SWITCH_CASES_WRITE_WB_REG(DBG_WB_BVR, val);
+ SWITCH_CASES_WRITE_WB_REG(DBG_WB_BCR, val);
+ default:
+ db_printf(
+ "trying to write to CP14 reg. using wrong opc2 %d\n",
+ reg >> OP2_SHIFT);
+ }
+ isb();
+}
+
+static __inline boolean_t
+dbg_capable(void)
+{
+
+ return (atomic_cmpset_int(&dbg_capable_var, 0, 0) == 0);
+}
+
+boolean_t
+kdb_cpu_pc_is_singlestep(db_addr_t pc)
+{
+ /*
+ * XXX: If the platform fails to enable its debug arch.
+ * there will be no stepping capabilities
+ */
+ if (!dbg_capable())
+ return (FALSE);
+
+ if (dbg_find_slot(DBG_TYPE_BREAKPOINT, pc) != ~0U)
+ return (TRUE);
+
+ return (FALSE);
+}
+
+void
+kdb_cpu_set_singlestep(void)
+{
+ db_expr_t inst;
+ db_addr_t pc, brpc;
+ uint32_t wcr;
+ u_int i;
+
+ if (!dbg_capable())
+ return;
+
+ /*
+ * Disable watchpoints, e.g. stepping over watched instruction will
+ * trigger break exception instead of single-step exception and locks
+ * CPU on that instruction for ever.
+ */
+ for (i = 0; i < dbg_watchpoint_num; i++) {
+ wcr = dbg_wb_read_reg(DBG_REG_BASE_WCR, i);
+ if ((wcr & DBG_WB_CTRL_E) != 0) {
+ dbg_wb_write_reg(DBG_REG_BASE_WCR, i,
+ (wcr & ~DBG_WB_CTRL_E));
+ }
+ }
+
+ pc = PC_REGS();
+
+ inst = db_get_value(pc, sizeof(pc), FALSE);
+ if (inst_branch(inst) || inst_call(inst) || inst_return(inst)) {
+ brpc = branch_taken(inst, pc);
+ dbg_setup_breakpoint(brpc, INSN_SIZE, DBG_BKPT_BT_SLOT);
+ }
+ pc = next_instr_address(pc, 0);
+ dbg_setup_breakpoint(pc, INSN_SIZE, DBG_BKPT_BNT_SLOT);
+}
+
+void
+kdb_cpu_clear_singlestep(void)
+{
+ uint32_t wvr, wcr;
+ u_int i;
+
+ if (!dbg_capable())
+ return;
+
+ dbg_remove_breakpoint(DBG_BKPT_BT_SLOT);
+ dbg_remove_breakpoint(DBG_BKPT_BNT_SLOT);
+
+ /* Restore all watchpoints */
+ for (i = 0; i < dbg_watchpoint_num; i++) {
+ wcr = dbg_wb_read_reg(DBG_REG_BASE_WCR, i);
+ wvr = dbg_wb_read_reg(DBG_REG_BASE_WVR, i);
+ /* Watchpoint considered not empty if address value is not 0 */
+ if ((wvr & DBGWVR_ADDR_MASK) != 0) {
+ dbg_wb_write_reg(DBG_REG_BASE_WCR, i,
+ (wcr | DBG_WB_CTRL_E));
+ }
+ }
+}
+
+int
+dbg_setup_watchpoint(db_expr_t addr, db_expr_t size, enum dbg_access_t access)
+{
+ struct dbg_wb_conf conf;
+
+ if (access == HW_BREAKPOINT_X) {
+ db_printf("Invalid access type for watchpoint: %d\n", access);
+ return (EINVAL);
+ }
+
+ conf.address = addr;
+ conf.size = size;
+ conf.access = access;
+ conf.type = DBG_TYPE_WATCHPOINT;
+
+ return (dbg_setup_xpoint(&conf));
+}
+
+int
+dbg_remove_watchpoint(db_expr_t addr, db_expr_t size __unused)
+{
+ struct dbg_wb_conf conf;
+
+ conf.address = addr;
+ conf.type = DBG_TYPE_WATCHPOINT;
+
+ return (dbg_remove_xpoint(&conf));
+}
+
+static int
+dbg_setup_breakpoint(db_expr_t addr, db_expr_t size, u_int slot)
+{
+ struct dbg_wb_conf conf;
+
+ conf.address = addr;
+ conf.size = size;
+ conf.access = HW_BREAKPOINT_X;
+ conf.type = DBG_TYPE_BREAKPOINT;
+ conf.slot = slot;
+
+ return (dbg_setup_xpoint(&conf));
+}
+
+static int
+dbg_remove_breakpoint(u_int slot)
+{
+ struct dbg_wb_conf conf;
+
+ /* Slot already cleared. Don't recurse */
+ if (dbg_check_slot_free(DBG_TYPE_BREAKPOINT, slot))
+ return (0);
+
+ conf.slot = slot;
+ conf.type = DBG_TYPE_BREAKPOINT;
+
+ return (dbg_remove_xpoint(&conf));
+}
+
+static const char *
+dbg_watchtype_str(uint32_t type)
+{
+
+ switch (type) {
+ case DBG_WB_CTRL_EXEC:
+ return ("execute");
+ case DBG_WB_CTRL_STORE:
+ return ("write");
+ case DBG_WB_CTRL_LOAD:
+ return ("read");
+ case DBG_WB_CTRL_LOAD | DBG_WB_CTRL_STORE:
+ return ("read/write");
+ default:
+ return ("invalid");
+ }
+}
+
+static int
+dbg_watchtype_len(uint32_t len)
+{
+
+ switch (len) {
+ case DBG_WB_CTRL_LEN_1:
+ return (1);
+ case DBG_WB_CTRL_LEN_2:
+ return (2);
+ case DBG_WB_CTRL_LEN_4:
+ return (4);
+ case DBG_WB_CTRL_LEN_8:
+ return (8);
+ default:
+ return (0);
+ }
+}
+
+void
+dbg_show_watchpoint(void)
+{
+ uint32_t wcr, len, type;
+ uint32_t addr;
+ boolean_t is_enabled;
+ int i;
+
+ if (!dbg_capable()) {
+ db_printf("Architecture does not support HW "
+ "breakpoints/watchpoints\n");
+ return;
+ }
+
+ db_printf("\nhardware watchpoints:\n");
+ db_printf(" watch status type len address symbol\n");
+ db_printf(" ----- -------- ---------- --- ---------- ------------------\n");
+ for (i = 0; i < dbg_watchpoint_num; i++) {
+ wcr = dbg_wb_read_reg(DBG_REG_BASE_WCR, i);
+ if ((wcr & DBG_WB_CTRL_E) != 0)
+ is_enabled = TRUE;
+ else
+ is_enabled = FALSE;
+
+ type = DBG_WB_CTRL_ACCESS_MASK(wcr);
+ len = DBG_WB_CTRL_LEN_MASK(wcr);
+ addr = dbg_wb_read_reg(DBG_REG_BASE_WVR, i) & DBGWVR_ADDR_MASK;
+ db_printf(" %-5d %-8s %10s %3d 0x%08x ", i,
+ is_enabled ? "enabled" : "disabled",
+ is_enabled ? dbg_watchtype_str(type) : "",
+ is_enabled ? dbg_watchtype_len(len) : 0,
+ addr);
+ db_printsym((db_addr_t)addr, DB_STGY_ANY);
+ db_printf("\n");
+ }
+}
+
+static boolean_t
+dbg_check_slot_free(enum dbg_t type, u_int slot)
+{
+ uint32_t cr, vr;
+ uint32_t max;
+
+ switch(type) {
+ case DBG_TYPE_BREAKPOINT:
+ max = dbg_breakpoint_num;
+ cr = DBG_REG_BASE_BCR;
+ vr = DBG_REG_BASE_BVR;
+ break;
+ case DBG_TYPE_WATCHPOINT:
+ max = dbg_watchpoint_num;
+ cr = DBG_REG_BASE_WCR;
+ vr = DBG_REG_BASE_WVR;
+ break;
+ default:
+ db_printf("%s: Unsupported event type %d\n", __func__, type);
+ return (FALSE);
+ }
+
+ if (slot >= max) {
+ db_printf("%s: Invalid slot number %d, max %d\n",
+ __func__, slot, max - 1);
+ return (FALSE);
+ }
+
+ if ((dbg_wb_read_reg(cr, slot) & DBG_WB_CTRL_E) == 0 &&
+ (dbg_wb_read_reg(vr, slot) & DBGWVR_ADDR_MASK) == 0)
+ return (TRUE);
+
+ return (FALSE);
+}
+
+static u_int
+dbg_find_free_slot(enum dbg_t type)
+{
+ u_int max, i;
+
+ switch(type) {
+ case DBG_TYPE_BREAKPOINT:
+ max = dbg_breakpoint_num;
+ break;
+ case DBG_TYPE_WATCHPOINT:
+ max = dbg_watchpoint_num;
+ break;
+ default:
+ db_printf("Unsupported debug type\n");
+ return (~0U);
+ }
+
+ for (i = 0; i < max; i++) {
+ if (dbg_check_slot_free(type, i))
+ return (i);
+ }
+
+ return (~0U);
+}
+
+static u_int
+dbg_find_slot(enum dbg_t type, db_expr_t addr)
+{
+ uint32_t reg_addr, reg_ctrl;
+ u_int max, i;
+
+ switch(type) {
+ case DBG_TYPE_BREAKPOINT:
+ max = dbg_breakpoint_num;
+ reg_addr = DBG_REG_BASE_BVR;
+ reg_ctrl = DBG_REG_BASE_BCR;
+ break;
+ case DBG_TYPE_WATCHPOINT:
+ max = dbg_watchpoint_num;
+ reg_addr = DBG_REG_BASE_WVR;
+ reg_ctrl = DBG_REG_BASE_WCR;
+ break;
+ default:
+ db_printf("Unsupported debug type\n");
+ return (~0U);
+ }
+
+ for (i = 0; i < max; i++) {
+ if ((dbg_wb_read_reg(reg_addr, i) == addr) &&
+ ((dbg_wb_read_reg(reg_ctrl, i) & DBG_WB_CTRL_E) != 0))
+ return (i);
+ }
+
+ return (~0U);
+}
+
+static __inline boolean_t
+dbg_monitor_is_enabled(void)
+{
+
+ return ((cp14_dbgdscrint_get() & DBGSCR_MDBG_EN) != 0);
+}
+
+static int
+dbg_enable_monitor(void)
+{
+ uint32_t dbg_dscr;
+
+ /* Already enabled? Just return */
+ if (dbg_monitor_is_enabled())
+ return (0);
+
+ dbg_dscr = cp14_dbgdscrint_get();
+
+ switch (dbg_model) {
+ case ID_DFR0_CP_DEBUG_M_V6:
+ case ID_DFR0_CP_DEBUG_M_V6_1: /* fall through */
+ cp14_dbgdscr_v6_set(dbg_dscr | DBGSCR_MDBG_EN);
+ break;
+ case ID_DFR0_CP_DEBUG_M_V7: /* fall through */
+ case ID_DFR0_CP_DEBUG_M_V7_1:
+ cp14_dbgdscr_v7_set(dbg_dscr | DBGSCR_MDBG_EN);
+ break;
+ default:
+ break;
+ }
+ isb();
+
+ /* Verify that Monitor mode is set */
+ if (dbg_monitor_is_enabled())
+ return (0);
+
+ return (ENXIO);
+}
+
+static int
+dbg_setup_xpoint(struct dbg_wb_conf *conf)
+{
+ struct pcpu *pcpu;
+ struct dbreg *d;
+ const char *typestr;
+ uint32_t cr_size, cr_priv, cr_access;
+ uint32_t reg_ctrl, reg_addr, ctrl, addr;
+ boolean_t is_bkpt;
+ u_int cpu;
+ u_int i;
+
+ if (!dbg_capable())
+ return (ENXIO);
+
+ is_bkpt = (conf->type == DBG_TYPE_BREAKPOINT);
+ typestr = is_bkpt ? "breakpoint" : "watchpoint";
+
+ if (is_bkpt) {
+ if (dbg_breakpoint_num == 0) {
+ db_printf("Breakpoints not supported on this architecture\n");
+ return (ENXIO);
+ }
+ i = conf->slot;
+ if (!dbg_check_slot_free(DBG_TYPE_BREAKPOINT, i)) {
+ /*
+ * This should never happen. If it does it means that
+ * there is an erroneus scenario somewhere. Still, it can
+ * be done but let's inform the user.
+ */
+ db_printf("ERROR: Breakpoint already set. Replacing...\n");
+ }
+ } else {
+ i = dbg_find_free_slot(DBG_TYPE_WATCHPOINT);
+ if (i == ~0U) {
+ db_printf("Can not find slot for %s, max %d slots supported\n",
+ typestr, dbg_watchpoint_num);
+ return (ENXIO);
+ }
+ }
+
+ /* Kernel access only */
+ cr_priv = DBG_WB_CTRL_PL1;
+
+ switch(conf->size) {
+ case 1:
+ cr_size = DBG_WB_CTRL_LEN_1;
+ break;
+ case 2:
+ cr_size = DBG_WB_CTRL_LEN_2;
+ break;
+ case 4:
+ cr_size = DBG_WB_CTRL_LEN_4;
+ break;
+ case 8:
+ cr_size = DBG_WB_CTRL_LEN_8;
+ break;
+ default:
+ db_printf("Unsupported address size for %s\n", typestr);
+ return (EINVAL);
+ }
+
+ if (is_bkpt) {
+ cr_access = DBG_WB_CTRL_EXEC;
+ reg_ctrl = DBG_REG_BASE_BCR;
+ reg_addr = DBG_REG_BASE_BVR;
+ /* Always unlinked BKPT */
+ ctrl = (cr_size | cr_access | cr_priv | DBG_WB_CTRL_E);
+ } else {
+ switch(conf->access) {
+ case HW_WATCHPOINT_R:
+ cr_access = DBG_WB_CTRL_LOAD;
+ break;
+ case HW_WATCHPOINT_W:
+ cr_access = DBG_WB_CTRL_STORE;
+ break;
+ case HW_WATCHPOINT_RW:
+ cr_access = DBG_WB_CTRL_LOAD | DBG_WB_CTRL_STORE;
+ break;
+ default:
+ db_printf("Unsupported exception level for %s\n", typestr);
+ return (EINVAL);
+ }
+
+ reg_ctrl = DBG_REG_BASE_WCR;
+ reg_addr = DBG_REG_BASE_WVR;
+ ctrl = (cr_size | cr_access | cr_priv | DBG_WB_CTRL_E);
+ }
+
+ addr = conf->address;
+
+ dbg_wb_write_reg(reg_addr, i, addr);
+ dbg_wb_write_reg(reg_ctrl, i, ctrl);
+
+ /*
+ * Save watchpoint settings for all CPUs.
+ * We don't need to do the same with breakpoints since HW breakpoints
+ * are only used to perform single stepping.
+ */
+ if (!is_bkpt) {
+ CPU_FOREACH(cpu) {
+ pcpu = pcpu_find(cpu);
+ /* Fill out the settings for watchpoint */
+ d = (struct dbreg *)pcpu->pc_dbreg;
+ d->dbg_wvr[i] = addr;
+ d->dbg_wcr[i] = ctrl;
+ /* Skip update command for the current CPU */
+ if (cpu != PCPU_GET(cpuid))
+ pcpu->pc_dbreg_cmd = PC_DBREG_CMD_LOAD;
+ }
+ }
+ /* Ensure all data is written before waking other CPUs */
+ atomic_thread_fence_rel();
+
+ return (0);
+}
+
+static int
+dbg_remove_xpoint(struct dbg_wb_conf *conf)
+{
+ struct pcpu *pcpu;
+ struct dbreg *d;
+ uint32_t reg_ctrl, reg_addr, addr;
+ boolean_t is_bkpt;
+ u_int cpu;
+ u_int i;
+
+ if (!dbg_capable())
+ return (ENXIO);
+
+ is_bkpt = (conf->type == DBG_TYPE_BREAKPOINT);
+ addr = conf->address;
+
+ if (is_bkpt) {
+ i = conf->slot;
+ reg_ctrl = DBG_REG_BASE_BCR;
+ reg_addr = DBG_REG_BASE_BVR;
+ } else {
+ i = dbg_find_slot(DBG_TYPE_WATCHPOINT, addr);
+ if (i == ~0U) {
+ db_printf("Can not find watchpoint for address 0%x\n", addr);
+ return (EINVAL);
+ }
+ reg_ctrl = DBG_REG_BASE_WCR;
+ reg_addr = DBG_REG_BASE_WVR;
+ }
+
+ dbg_wb_write_reg(reg_ctrl, i, 0);
+ dbg_wb_write_reg(reg_addr, i, 0);
+
+ /*
+ * Save watchpoint settings for all CPUs.
+ * We don't need to do the same with breakpoints since HW breakpoints
+ * are only used to perform single stepping.
+ */
+ if (!is_bkpt) {
+ CPU_FOREACH(cpu) {
+ pcpu = pcpu_find(cpu);
+ /* Fill out the settings for watchpoint */
+ d = (struct dbreg *)pcpu->pc_dbreg;
+ d->dbg_wvr[i] = 0;
+ d->dbg_wcr[i] = 0;
+ /* Skip update command for the current CPU */
+ if (cpu != PCPU_GET(cpuid))
+ pcpu->pc_dbreg_cmd = PC_DBREG_CMD_LOAD;
+ }
+ /* Ensure all data is written before waking other CPUs */
+ atomic_thread_fence_rel();
+ }
+
+ return (0);
+}
+
+static __inline uint32_t
+dbg_get_debug_model(void)
+{
+ uint32_t dbg_m;
+
+ dbg_m = ((cpuinfo.id_dfr0 & ID_DFR0_CP_DEBUG_M_MASK) >>
+ ID_DFR0_CP_DEBUG_M_SHIFT);
+
+ return (dbg_m);
+}
+
+static __inline boolean_t
+dbg_get_ossr(void)
+{
+
+ switch (dbg_model) {
+ case ID_DFR0_CP_DEBUG_M_V7:
+ if ((cp14_dbgoslsr_get() & DBGOSLSR_OSLM0) != 0)
+ return (TRUE);
+
+ return (FALSE);
+ case ID_DFR0_CP_DEBUG_M_V7_1:
+ return (TRUE);
+ default:
+ return (FALSE);
+ }
+}
+
+static __inline boolean_t
+dbg_arch_supported(void)
+{
+ uint32_t dbg_didr;
+
+ switch (dbg_model) {
+ case ID_DFR0_CP_DEBUG_M_V6:
+ case ID_DFR0_CP_DEBUG_M_V6_1:
+ dbg_didr = cp14_dbgdidr_get();
+ /*
+ * read-all-zeroes is used by QEMU
+ * to indicate that ARMv6 debug support
+ * is not implemented. Real hardware has at
+ * least version bits set
+ */
+ if (dbg_didr == 0)
+ return (FALSE);
+ return (TRUE);
+ case ID_DFR0_CP_DEBUG_M_V7:
+ case ID_DFR0_CP_DEBUG_M_V7_1: /* fall through */
+ return (TRUE);
+ default:
+ /* We only support valid v6.x/v7.x modes through CP14 */
+ return (FALSE);
+ }
+}
+
+static __inline uint32_t
+dbg_get_wrp_num(void)
+{
+ uint32_t dbg_didr;
+
+ dbg_didr = cp14_dbgdidr_get();
+
+ return (DBGDIDR_WRPS_NUM(dbg_didr));
+}
+
+static __inline uint32_t
+dgb_get_brp_num(void)
+{
+ uint32_t dbg_didr;
+
+ dbg_didr = cp14_dbgdidr_get();
+
+ return (DBGDIDR_BRPS_NUM(dbg_didr));
+}
+
+static int
+dbg_reset_state(void)
+{
+ u_int cpuid;
+ size_t i;
+ int err;
+
+ cpuid = PCPU_GET(cpuid);
+ err = 0;
+
+ switch (dbg_model) {
+ case ID_DFR0_CP_DEBUG_M_V6:
+ case ID_DFR0_CP_DEBUG_M_V6_1: /* fall through */
+ /*
+ * Arch needs monitor mode selected and enabled
+ * to be able to access breakpoint/watchpoint registers.
+ */
+ err = dbg_enable_monitor();
+ if (err != 0)
+ return (err);
+ goto vectr_clr;
+ case ID_DFR0_CP_DEBUG_M_V7:
+ /* Is core power domain powered up? */
+ if ((cp14_dbgprsr_get() & DBGPRSR_PU) == 0)
+ err = ENXIO;
+
+ if (err != 0)
+ break;
+
+ if (dbg_ossr)
+ goto vectr_clr;
+ break;
+ case ID_DFR0_CP_DEBUG_M_V7_1:
+ /* Is double lock set? */
+ if ((cp14_dbgosdlr_get() & DBGPRSR_DLK) != 0)
+ err = ENXIO;
+
+ break;
+ default:
+ break;
+ }
+
+ if (err != 0) {
+ db_printf("Debug facility locked (CPU%d)\n", cpuid);
+ return (err);
+ }
+
+ /*
+ * DBGOSLAR is always implemented for v7.1 Debug Arch. however is
+ * optional for v7 (depends on OS save and restore support).
+ */
+ if (((dbg_model & ID_DFR0_CP_DEBUG_M_V7_1) != 0) || dbg_ossr) {
+ /*
+ * Clear OS lock.
+ * Writing any other value than 0xC5ACCESS will unlock.
+ */
+ cp14_dbgoslar_set(0);
+ isb();
+ }
+
+vectr_clr:
+ /*
+ * After reset we must ensure that DBGVCR has a defined value.
+ * Disable all vector catch events. Safe to use - required in all
+ * implementations.
+ */
+ cp14_dbgvcr_set(0);
+ isb();
+
+ /*
+ * We have limited number of {watch,break}points, each consists of
+ * two registers:
+ * - wcr/bcr regsiter configurates corresponding {watch,break}point
+ * behaviour
+ * - wvr/bvr register keeps address we are hunting for
+ *
+ * Reset all breakpoints and watchpoints.
+ */
+ for (i = 0; i < dbg_watchpoint_num; ++i) {
+ dbg_wb_write_reg(DBG_REG_BASE_WCR, i, 0);
+ dbg_wb_write_reg(DBG_REG_BASE_WVR, i, 0);
+ }
+
+ for (i = 0; i < dbg_breakpoint_num; ++i) {
+ dbg_wb_write_reg(DBG_REG_BASE_BCR, i, 0);
+ dbg_wb_write_reg(DBG_REG_BASE_BVR, i, 0);
+ }
+
+ return (0);
+}
+
+void
+dbg_monitor_init(void)
+{
+ int err;
+
+ /* Fetch ARM Debug Architecture model */
+ dbg_model = dbg_get_debug_model();
+
+ if (!dbg_arch_supported()) {
+ db_printf("ARM Debug Architecture not supported\n");
+ return;
+ }
+
+ if (bootverbose) {
+ db_printf("ARM Debug Architecture %s\n",
+ (dbg_model == ID_DFR0_CP_DEBUG_M_V6) ? "v6" :
+ (dbg_model == ID_DFR0_CP_DEBUG_M_V6_1) ? "v6.1" :
+ (dbg_model == ID_DFR0_CP_DEBUG_M_V7) ? "v7" :
+ (dbg_model == ID_DFR0_CP_DEBUG_M_V7_1) ? "v7.1" : "unknown");
+ }
+
+ /* Do we have OS Save and Restore mechanism? */
+ dbg_ossr = dbg_get_ossr();
+
+ /* Find out many breakpoints and watchpoints we can use */
+ dbg_watchpoint_num = dbg_get_wrp_num();
+ dbg_breakpoint_num = dgb_get_brp_num();
+
+ if (bootverbose) {
+ db_printf("%d watchpoints and %d breakpoints supported\n",
+ dbg_watchpoint_num, dbg_breakpoint_num);
+ }
+
+ err = dbg_reset_state();
+ if (err == 0) {
+ err = dbg_enable_monitor();
+ if (err == 0) {
+ atomic_set_int(&dbg_capable_var, 1);
+ return;
+ }
+ }
+
+ db_printf("HW Breakpoints/Watchpoints not enabled on CPU%d\n",
+ PCPU_GET(cpuid));
+}
+
+CTASSERT(sizeof(struct dbreg) == sizeof(((struct pcpu *)NULL)->pc_dbreg));
+
+void
+dbg_monitor_init_secondary(void)
+{
+ u_int cpuid;
+ int err;
+ /*
+ * This flag is set on the primary CPU
+ * and its meaning is valid for other CPUs too.
+ */
+ if (!dbg_capable())
+ return;
+
+ cpuid = PCPU_GET(cpuid);
+
+ err = dbg_reset_state();
+ if (err != 0) {
+ /*
+ * Something is very wrong.
+ * WPs/BPs will not work correctly on this CPU.
+ */
+ KASSERT(0, ("%s: Failed to reset Debug Architecture "
+ "state on CPU%d", __func__, cpuid));
+ /* Disable HW debug capabilities for all CPUs */
+ atomic_set_int(&dbg_capable_var, 0);
+ return;
+ }
+ err = dbg_enable_monitor();
+ if (err != 0) {
+ KASSERT(0, ("%s: Failed to enable Debug Monitor"
+ " on CPU%d", __func__, cpuid));
+ atomic_set_int(&dbg_capable_var, 0);
+ }
+}
+
+void
+dbg_resume_dbreg(void)
+{
+ struct dbreg *d;
+ u_int i;
+
+ /*
+ * This flag is set on the primary CPU
+ * and its meaning is valid for other CPUs too.
+ */
+ if (!dbg_capable())
+ return;
+
+ atomic_thread_fence_acq();
+
+ switch (PCPU_GET(dbreg_cmd)) {
+ case PC_DBREG_CMD_LOAD:
+ d = (struct dbreg *)PCPU_PTR(dbreg);
+
+ /* Restore watchpoints */
+ for (i = 0; i < dbg_watchpoint_num; i++) {
+ dbg_wb_write_reg(DBG_REG_BASE_WVR, i, d->dbg_wvr[i]);
+ dbg_wb_write_reg(DBG_REG_BASE_WCR, i, d->dbg_wcr[i]);
+ }
+
+ PCPU_SET(dbreg_cmd, PC_DBREG_CMD_NONE);
+ break;
+ }
+}
diff --git a/sys/arm/arm/disassem.c b/sys/arm/arm/disassem.c
new file mode 100644
index 000000000000..4eb5c6976bb9
--- /dev/null
+++ b/sys/arm/arm/disassem.c
@@ -0,0 +1,690 @@
+/* $NetBSD: disassem.c,v 1.14 2003/03/27 16:58:36 mycroft Exp $ */
+
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 1996 Mark Brinicombe.
+ * Copyright (c) 1996 Brini.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Brini.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * RiscBSD kernel project
+ *
+ * db_disasm.c
+ *
+ * Kernel disassembler
+ *
+ * Created : 10/02/96
+ *
+ * Structured after the sparc/sparc/db_disasm.c by David S. Miller &
+ * Paul Kranenburg
+ *
+ * This code is not complete. Not all instructions are disassembled.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#include <sys/param.h>
+
+#include <sys/systm.h>
+#include <machine/disassem.h>
+#include <machine/armreg.h>
+#include <ddb/ddb.h>
+
+/*
+ * General instruction format
+ *
+ * insn[cc][mod] [operands]
+ *
+ * Those fields with an uppercase format code indicate that the field
+ * follows directly after the instruction before the separator i.e.
+ * they modify the instruction rather than just being an operand to
+ * the instruction. The only exception is the writeback flag which
+ * follows a operand.
+ *
+ *
+ * 2 - print Operand 2 of a data processing instruction
+ * d - destination register (bits 12-15)
+ * n - n register (bits 16-19)
+ * s - s register (bits 8-11)
+ * o - indirect register rn (bits 16-19) (used by swap)
+ * m - m register (bits 0-3)
+ * a - address operand of ldr/str instruction
+ * l - register list for ldm/stm instruction
+ * f - 1st fp operand (register) (bits 12-14)
+ * g - 2nd fp operand (register) (bits 16-18)
+ * h - 3rd fp operand (register/immediate) (bits 0-4)
+ * b - branch address
+ * t - thumb branch address (bits 24, 0-23)
+ * k - breakpoint comment (bits 0-3, 8-19)
+ * X - block transfer type
+ * Y - block transfer type (r13 base)
+ * c - comment field bits(0-23)
+ * p - saved or current status register
+ * F - PSR transfer fields
+ * D - destination-is-r15 (P) flag on TST, TEQ, CMP, CMN
+ * L - co-processor transfer size
+ * S - set status flag
+ * P - fp precision
+ * Q - fp precision (for ldf/stf)
+ * R - fp rounding
+ * v - co-processor data transfer registers + addressing mode
+ * W - writeback flag
+ * x - instruction in hex
+ * # - co-processor number
+ * y - co-processor data processing registers
+ * z - co-processor register transfer registers
+ */
+
+struct arm32_insn {
+ u_int mask;
+ u_int pattern;
+ char* name;
+ char* format;
+};
+
+static const struct arm32_insn arm32_i[] = {
+ { 0x0fffffff, 0x0ff00000, "imb", "c" }, /* Before swi */
+ { 0x0fffffff, 0x0ff00001, "imbrange", "c" }, /* Before swi */
+ { 0x0f000000, 0x0f000000, "swi", "c" },
+ { 0xfe000000, 0xfa000000, "blx", "t" }, /* Before b and bl */
+ { 0x0f000000, 0x0a000000, "b", "b" },
+ { 0x0f000000, 0x0b000000, "bl", "b" },
+ { 0x0fe000f0, 0x00000090, "mul", "Snms" },
+ { 0x0fe000f0, 0x00200090, "mla", "Snmsd" },
+ { 0x0fe000f0, 0x00800090, "umull", "Sdnms" },
+ { 0x0fe000f0, 0x00c00090, "smull", "Sdnms" },
+ { 0x0fe000f0, 0x00a00090, "umlal", "Sdnms" },
+ { 0x0fe000f0, 0x00e00090, "smlal", "Sdnms" },
+ { 0x0d700000, 0x04200000, "strt", "daW" },
+ { 0x0d700000, 0x04300000, "ldrt", "daW" },
+ { 0x0d700000, 0x04600000, "strbt", "daW" },
+ { 0x0d700000, 0x04700000, "ldrbt", "daW" },
+ { 0x0c500000, 0x04000000, "str", "daW" },
+ { 0x0c500000, 0x04100000, "ldr", "daW" },
+ { 0x0c500000, 0x04400000, "strb", "daW" },
+ { 0x0c500000, 0x04500000, "ldrb", "daW" },
+ { 0x0fff0ff0, 0x06bf0fb0, "rev16", "dm" },
+ { 0xffffffff, 0xf57ff01f, "clrex", "c" },
+ { 0x0ff00ff0, 0x01800f90, "strex", "dmo" },
+ { 0x0ff00fff, 0x01900f9f, "ldrex", "do" },
+ { 0x0ff00ff0, 0x01a00f90, "strexd", "dmo" },
+ { 0x0ff00fff, 0x01b00f9f, "ldrexd", "do" },
+ { 0x0ff00ff0, 0x01c00f90, "strexb", "dmo" },
+ { 0x0ff00fff, 0x01d00f9f, "ldrexb", "do" },
+ { 0x0ff00ff0, 0x01e00f90, "strexh", "dmo" },
+ { 0x0ff00fff, 0x01f00f9f, "ldrexh", "do" },
+ { 0x0e1f0000, 0x080d0000, "stm", "YnWl" },/* separate out r13 base */
+ { 0x0e1f0000, 0x081d0000, "ldm", "YnWl" },/* separate out r13 base */
+ { 0x0e100000, 0x08000000, "stm", "XnWl" },
+ { 0x0e100000, 0x08100000, "ldm", "XnWl" },
+ { 0x0e1000f0, 0x00100090, "ldrb", "de" },
+ { 0x0e1000f0, 0x00000090, "strb", "de" },
+ { 0x0e1000f0, 0x001000d0, "ldrsb", "de" },
+ { 0x0e1000f0, 0x001000b0, "ldrh", "de" },
+ { 0x0e1000f0, 0x000000b0, "strh", "de" },
+ { 0x0e1000f0, 0x001000f0, "ldrsh", "de" },
+ { 0x0f200090, 0x00200090, "und", "x" }, /* Before data processing */
+ { 0x0e1000d0, 0x000000d0, "und", "x" }, /* Before data processing */
+ { 0x0ff00ff0, 0x01000090, "swp", "dmo" },
+ { 0x0ff00ff0, 0x01400090, "swpb", "dmo" },
+ { 0x0fbf0fff, 0x010f0000, "mrs", "dp" }, /* Before data processing */
+ { 0x0fb0fff0, 0x0120f000, "msr", "pFm" },/* Before data processing */
+ { 0x0fb0f000, 0x0320f000, "msr", "pF2" },/* Before data processing */
+ { 0x0ffffff0, 0x012fff10, "bx", "m" },
+ { 0x0fff0ff0, 0x016f0f10, "clz", "dm" },
+ { 0x0ffffff0, 0x012fff30, "blx", "m" },
+ { 0xfff000f0, 0xe1200070, "bkpt", "k" },
+ { 0x0de00000, 0x00000000, "and", "Sdn2" },
+ { 0x0de00000, 0x00200000, "eor", "Sdn2" },
+ { 0x0de00000, 0x00400000, "sub", "Sdn2" },
+ { 0x0de00000, 0x00600000, "rsb", "Sdn2" },
+ { 0x0de00000, 0x00800000, "add", "Sdn2" },
+ { 0x0de00000, 0x00a00000, "adc", "Sdn2" },
+ { 0x0de00000, 0x00c00000, "sbc", "Sdn2" },
+ { 0x0de00000, 0x00e00000, "rsc", "Sdn2" },
+ { 0x0df00000, 0x01100000, "tst", "Dn2" },
+ { 0x0df00000, 0x01300000, "teq", "Dn2" },
+ { 0x0de00000, 0x01400000, "cmp", "Dn2" },
+ { 0x0de00000, 0x01600000, "cmn", "Dn2" },
+ { 0x0de00000, 0x01800000, "orr", "Sdn2" },
+ { 0x0de00000, 0x01a00000, "mov", "Sd2" },
+ { 0x0de00000, 0x01c00000, "bic", "Sdn2" },
+ { 0x0de00000, 0x01e00000, "mvn", "Sd2" },
+ { 0x0ff08f10, 0x0e000100, "adf", "PRfgh" },
+ { 0x0ff08f10, 0x0e100100, "muf", "PRfgh" },
+ { 0x0ff08f10, 0x0e200100, "suf", "PRfgh" },
+ { 0x0ff08f10, 0x0e300100, "rsf", "PRfgh" },
+ { 0x0ff08f10, 0x0e400100, "dvf", "PRfgh" },
+ { 0x0ff08f10, 0x0e500100, "rdf", "PRfgh" },
+ { 0x0ff08f10, 0x0e600100, "pow", "PRfgh" },
+ { 0x0ff08f10, 0x0e700100, "rpw", "PRfgh" },
+ { 0x0ff08f10, 0x0e800100, "rmf", "PRfgh" },
+ { 0x0ff08f10, 0x0e900100, "fml", "PRfgh" },
+ { 0x0ff08f10, 0x0ea00100, "fdv", "PRfgh" },
+ { 0x0ff08f10, 0x0eb00100, "frd", "PRfgh" },
+ { 0x0ff08f10, 0x0ec00100, "pol", "PRfgh" },
+ { 0x0f008f10, 0x0e000100, "fpbop", "PRfgh" },
+ { 0x0ff08f10, 0x0e008100, "mvf", "PRfh" },
+ { 0x0ff08f10, 0x0e108100, "mnf", "PRfh" },
+ { 0x0ff08f10, 0x0e208100, "abs", "PRfh" },
+ { 0x0ff08f10, 0x0e308100, "rnd", "PRfh" },
+ { 0x0ff08f10, 0x0e408100, "sqt", "PRfh" },
+ { 0x0ff08f10, 0x0e508100, "log", "PRfh" },
+ { 0x0ff08f10, 0x0e608100, "lgn", "PRfh" },
+ { 0x0ff08f10, 0x0e708100, "exp", "PRfh" },
+ { 0x0ff08f10, 0x0e808100, "sin", "PRfh" },
+ { 0x0ff08f10, 0x0e908100, "cos", "PRfh" },
+ { 0x0ff08f10, 0x0ea08100, "tan", "PRfh" },
+ { 0x0ff08f10, 0x0eb08100, "asn", "PRfh" },
+ { 0x0ff08f10, 0x0ec08100, "acs", "PRfh" },
+ { 0x0ff08f10, 0x0ed08100, "atn", "PRfh" },
+ { 0x0f008f10, 0x0e008100, "fpuop", "PRfh" },
+ { 0x0e100f00, 0x0c000100, "stf", "QLv" },
+ { 0x0e100f00, 0x0c100100, "ldf", "QLv" },
+ { 0x0ff00f10, 0x0e000110, "flt", "PRgd" },
+ { 0x0ff00f10, 0x0e100110, "fix", "PRdh" },
+ { 0x0ff00f10, 0x0e200110, "wfs", "d" },
+ { 0x0ff00f10, 0x0e300110, "rfs", "d" },
+ { 0x0ff00f10, 0x0e400110, "wfc", "d" },
+ { 0x0ff00f10, 0x0e500110, "rfc", "d" },
+ { 0x0ff0ff10, 0x0e90f110, "cmf", "PRgh" },
+ { 0x0ff0ff10, 0x0eb0f110, "cnf", "PRgh" },
+ { 0x0ff0ff10, 0x0ed0f110, "cmfe", "PRgh" },
+ { 0x0ff0ff10, 0x0ef0f110, "cnfe", "PRgh" },
+ { 0xff100010, 0xfe000010, "mcr2", "#z" },
+ { 0x0f100010, 0x0e000010, "mcr", "#z" },
+ { 0xff100010, 0xfe100010, "mrc2", "#z" },
+ { 0x0f100010, 0x0e100010, "mrc", "#z" },
+ { 0xff000010, 0xfe000000, "cdp2", "#y" },
+ { 0x0f000010, 0x0e000000, "cdp", "#y" },
+ { 0xfe100090, 0xfc100000, "ldc2", "L#v" },
+ { 0x0e100090, 0x0c100000, "ldc", "L#v" },
+ { 0xfe100090, 0xfc000000, "stc2", "L#v" },
+ { 0x0e100090, 0x0c000000, "stc", "L#v" },
+ { 0x00000000, 0x00000000, NULL, NULL }
+};
+
+static char const arm32_insn_conditions[][4] = {
+ "eq", "ne", "cs", "cc",
+ "mi", "pl", "vs", "vc",
+ "hi", "ls", "ge", "lt",
+ "gt", "le", "", "nv"
+};
+
+static char const insn_block_transfers[][4] = {
+ "da", "ia", "db", "ib"
+};
+
+static char const insn_stack_block_transfers[][4] = {
+ "ed", "ea", "fd", "fa"
+};
+
+static char const op_shifts[][4] = {
+ "lsl", "lsr", "asr", "ror"
+};
+
+static char const insn_fpa_rounding[][2] = {
+ "", "p", "m", "z"
+};
+
+static char const insn_fpa_precision[][2] = {
+ "s", "d", "e", "p"
+};
+
+static char const insn_fpaconstants[][8] = {
+ "0.0", "1.0", "2.0", "3.0",
+ "4.0", "5.0", "0.5", "10.0"
+};
+
+#define insn_condition(x) arm32_insn_conditions[(x >> 28) & 0x0f]
+#define insn_blktrans(x) insn_block_transfers[(x >> 23) & 3]
+#define insn_stkblktrans(x) insn_stack_block_transfers[(x >> 23) & 3]
+#define op2_shift(x) op_shifts[(x >> 5) & 3]
+#define insn_fparnd(x) insn_fpa_rounding[(x >> 5) & 0x03]
+#define insn_fpaprec(x) insn_fpa_precision[(((x >> 18) & 2)|(x >> 7)) & 1]
+#define insn_fpaprect(x) insn_fpa_precision[(((x >> 21) & 2)|(x >> 15)) & 1]
+#define insn_fpaimm(x) insn_fpaconstants[x & 0x07]
+
+/* Local prototypes */
+static void disasm_register_shift(const disasm_interface_t *di, u_int insn);
+static void disasm_print_reglist(const disasm_interface_t *di, u_int insn);
+static void disasm_insn_ldrstr(const disasm_interface_t *di, u_int insn,
+ u_int loc);
+static void disasm_insn_ldrhstrh(const disasm_interface_t *di, u_int insn,
+ u_int loc);
+static void disasm_insn_ldcstc(const disasm_interface_t *di, u_int insn,
+ u_int loc);
+static u_int disassemble_readword(u_int address);
+static void disassemble_printaddr(u_int address);
+
+vm_offset_t
+disasm(const disasm_interface_t *di, vm_offset_t loc, int altfmt)
+{
+ const struct arm32_insn *i_ptr = arm32_i;
+
+ u_int insn;
+ int matchp;
+ int branch;
+ char* f_ptr;
+ int fmt;
+
+ fmt = 0;
+ matchp = 0;
+ insn = di->di_readword(loc);
+
+/* di->di_printf("loc=%08x insn=%08x : ", loc, insn);*/
+
+ while (i_ptr->name) {
+ if ((insn & i_ptr->mask) == i_ptr->pattern) {
+ matchp = 1;
+ break;
+ }
+ i_ptr++;
+ }
+
+ if (!matchp) {
+ di->di_printf("und%s\t%08x\n", insn_condition(insn), insn);
+ return(loc + INSN_SIZE);
+ }
+
+ /* If instruction forces condition code, don't print it. */
+ if ((i_ptr->mask & 0xf0000000) == 0xf0000000)
+ di->di_printf("%s", i_ptr->name);
+ else
+ di->di_printf("%s%s", i_ptr->name, insn_condition(insn));
+
+ f_ptr = i_ptr->format;
+
+ /* Insert tab if there are no instruction modifiers */
+
+ if (*(f_ptr) < 'A' || *(f_ptr) > 'Z') {
+ ++fmt;
+ di->di_printf("\t");
+ }
+
+ while (*f_ptr) {
+ switch (*f_ptr) {
+ /* 2 - print Operand 2 of a data processing instruction */
+ case '2':
+ if (insn & 0x02000000) {
+ int rotate= ((insn >> 7) & 0x1e);
+
+ di->di_printf("#0x%08x",
+ (insn & 0xff) << (32 - rotate) |
+ (insn & 0xff) >> rotate);
+ } else {
+ disasm_register_shift(di, insn);
+ }
+ break;
+ /* d - destination register (bits 12-15) */
+ case 'd':
+ di->di_printf("r%d", ((insn >> 12) & 0x0f));
+ break;
+ /* D - insert 'p' if Rd is R15 */
+ case 'D':
+ if (((insn >> 12) & 0x0f) == 15)
+ di->di_printf("p");
+ break;
+ /* n - n register (bits 16-19) */
+ case 'n':
+ di->di_printf("r%d", ((insn >> 16) & 0x0f));
+ break;
+ /* s - s register (bits 8-11) */
+ case 's':
+ di->di_printf("r%d", ((insn >> 8) & 0x0f));
+ break;
+ /* o - indirect register rn (bits 16-19) (used by swap) */
+ case 'o':
+ di->di_printf("[r%d]", ((insn >> 16) & 0x0f));
+ break;
+ /* m - m register (bits 0-4) */
+ case 'm':
+ di->di_printf("r%d", ((insn >> 0) & 0x0f));
+ break;
+ /* a - address operand of ldr/str instruction */
+ case 'a':
+ disasm_insn_ldrstr(di, insn, loc);
+ break;
+ /* e - address operand of ldrh/strh instruction */
+ case 'e':
+ disasm_insn_ldrhstrh(di, insn, loc);
+ break;
+ /* l - register list for ldm/stm instruction */
+ case 'l':
+ disasm_print_reglist(di, insn);
+ break;
+ /* f - 1st fp operand (register) (bits 12-14) */
+ case 'f':
+ di->di_printf("f%d", (insn >> 12) & 7);
+ break;
+ /* g - 2nd fp operand (register) (bits 16-18) */
+ case 'g':
+ di->di_printf("f%d", (insn >> 16) & 7);
+ break;
+ /* h - 3rd fp operand (register/immediate) (bits 0-4) */
+ case 'h':
+ if (insn & (1 << 3))
+ di->di_printf("#%s", insn_fpaimm(insn));
+ else
+ di->di_printf("f%d", insn & 7);
+ break;
+ /* b - branch address */
+ case 'b':
+ branch = ((insn << 2) & 0x03ffffff);
+ if (branch & 0x02000000)
+ branch |= 0xfc000000;
+ di->di_printaddr(loc + 8 + branch);
+ break;
+ /* t - blx address */
+ case 't':
+ branch = ((insn << 2) & 0x03ffffff) |
+ (insn >> 23 & 0x00000002);
+ if (branch & 0x02000000)
+ branch |= 0xfc000000;
+ di->di_printaddr(loc + 8 + branch);
+ break;
+ /* X - block transfer type */
+ case 'X':
+ di->di_printf("%s", insn_blktrans(insn));
+ break;
+ /* Y - block transfer type (r13 base) */
+ case 'Y':
+ di->di_printf("%s", insn_stkblktrans(insn));
+ break;
+ /* c - comment field bits(0-23) */
+ case 'c':
+ di->di_printf("0x%08x", (insn & 0x00ffffff));
+ break;
+ /* k - breakpoint comment (bits 0-3, 8-19) */
+ case 'k':
+ di->di_printf("0x%04x",
+ (insn & 0x000fff00) >> 4 | (insn & 0x0000000f));
+ break;
+ /* p - saved or current status register */
+ case 'p':
+ if (insn & 0x00400000)
+ di->di_printf("spsr");
+ else
+ di->di_printf("cpsr");
+ break;
+ /* F - PSR transfer fields */
+ case 'F':
+ di->di_printf("_");
+ if (insn & (1 << 16))
+ di->di_printf("c");
+ if (insn & (1 << 17))
+ di->di_printf("x");
+ if (insn & (1 << 18))
+ di->di_printf("s");
+ if (insn & (1 << 19))
+ di->di_printf("f");
+ break;
+ /* B - byte transfer flag */
+ case 'B':
+ if (insn & 0x00400000)
+ di->di_printf("b");
+ break;
+ /* L - co-processor transfer size */
+ case 'L':
+ if (insn & (1 << 22))
+ di->di_printf("l");
+ break;
+ /* S - set status flag */
+ case 'S':
+ if (insn & 0x00100000)
+ di->di_printf("s");
+ break;
+ /* P - fp precision */
+ case 'P':
+ di->di_printf("%s", insn_fpaprec(insn));
+ break;
+ /* Q - fp precision (for ldf/stf) */
+ case 'Q':
+ break;
+ /* R - fp rounding */
+ case 'R':
+ di->di_printf("%s", insn_fparnd(insn));
+ break;
+ /* W - writeback flag */
+ case 'W':
+ if (insn & (1 << 21))
+ di->di_printf("!");
+ break;
+ /* # - co-processor number */
+ case '#':
+ di->di_printf("p%d", (insn >> 8) & 0x0f);
+ break;
+ /* v - co-processor data transfer registers+addressing mode */
+ case 'v':
+ disasm_insn_ldcstc(di, insn, loc);
+ break;
+ /* x - instruction in hex */
+ case 'x':
+ di->di_printf("0x%08x", insn);
+ break;
+ /* y - co-processor data processing registers */
+ case 'y':
+ di->di_printf("%d, ", (insn >> 20) & 0x0f);
+
+ di->di_printf("c%d, c%d, c%d", (insn >> 12) & 0x0f,
+ (insn >> 16) & 0x0f, insn & 0x0f);
+
+ di->di_printf(", %d", (insn >> 5) & 0x07);
+ break;
+ /* z - co-processor register transfer registers */
+ case 'z':
+ di->di_printf("%d, ", (insn >> 21) & 0x07);
+ di->di_printf("r%d, c%d, c%d, %d",
+ (insn >> 12) & 0x0f, (insn >> 16) & 0x0f,
+ insn & 0x0f, (insn >> 5) & 0x07);
+
+/* if (((insn >> 5) & 0x07) != 0)
+ di->di_printf(", %d", (insn >> 5) & 0x07);*/
+ break;
+ default:
+ di->di_printf("[%c - unknown]", *f_ptr);
+ break;
+ }
+ if (*(f_ptr+1) >= 'A' && *(f_ptr+1) <= 'Z')
+ ++f_ptr;
+ else if (*(++f_ptr)) {
+ ++fmt;
+ if (fmt == 1)
+ di->di_printf("\t");
+ else
+ di->di_printf(", ");
+ }
+ }
+
+ di->di_printf("\n");
+
+ return(loc + INSN_SIZE);
+}
+
+static void
+disasm_register_shift(const disasm_interface_t *di, u_int insn)
+{
+ di->di_printf("r%d", (insn & 0x0f));
+ if ((insn & 0x00000ff0) == 0)
+ ;
+ else if ((insn & 0x00000ff0) == 0x00000060)
+ di->di_printf(", rrx");
+ else {
+ if (insn & 0x10)
+ di->di_printf(", %s r%d", op2_shift(insn),
+ (insn >> 8) & 0x0f);
+ else
+ di->di_printf(", %s #%d", op2_shift(insn),
+ (insn >> 7) & 0x1f);
+ }
+}
+
+static void
+disasm_print_reglist(const disasm_interface_t *di, u_int insn)
+{
+ int loop;
+ int start;
+ int comma;
+
+ di->di_printf("{");
+ start = -1;
+ comma = 0;
+
+ for (loop = 0; loop < 17; ++loop) {
+ if (start != -1) {
+ if (loop == 16 || !(insn & (1 << loop))) {
+ if (comma)
+ di->di_printf(", ");
+ else
+ comma = 1;
+ if (start == loop - 1)
+ di->di_printf("r%d", start);
+ else
+ di->di_printf("r%d-r%d", start, loop - 1);
+ start = -1;
+ }
+ } else {
+ if (insn & (1 << loop))
+ start = loop;
+ }
+ }
+ di->di_printf("}");
+
+ if (insn & (1 << 22))
+ di->di_printf("^");
+}
+
+static void
+disasm_insn_ldrstr(const disasm_interface_t *di, u_int insn, u_int loc)
+{
+ int offset;
+
+ offset = insn & 0xfff;
+ if ((insn & 0x032f0000) == 0x010f0000) {
+ /* rA = pc, immediate index */
+ if (insn & 0x00800000)
+ loc += offset;
+ else
+ loc -= offset;
+ di->di_printaddr(loc + 8);
+ } else {
+ di->di_printf("[r%d", (insn >> 16) & 0x0f);
+ if ((insn & 0x03000fff) != 0x01000000) {
+ di->di_printf("%s, ", (insn & (1 << 24)) ? "" : "]");
+ if (!(insn & 0x00800000))
+ di->di_printf("-");
+ if (insn & (1 << 25))
+ disasm_register_shift(di, insn);
+ else
+ di->di_printf("#0x%03x", offset);
+ }
+ if (insn & (1 << 24))
+ di->di_printf("]");
+ }
+}
+
+static void
+disasm_insn_ldrhstrh(const disasm_interface_t *di, u_int insn, u_int loc)
+{
+ int offset;
+
+ offset = ((insn & 0xf00) >> 4) | (insn & 0xf);
+ if ((insn & 0x004f0000) == 0x004f0000) {
+ /* rA = pc, immediate index */
+ if (insn & 0x00800000)
+ loc += offset;
+ else
+ loc -= offset;
+ di->di_printaddr(loc + 8);
+ } else {
+ di->di_printf("[r%d", (insn >> 16) & 0x0f);
+ if ((insn & 0x01400f0f) != 0x01400000) {
+ di->di_printf("%s, ", (insn & (1 << 24)) ? "" : "]");
+ if (!(insn & 0x00800000))
+ di->di_printf("-");
+ if (insn & (1 << 22))
+ di->di_printf("#0x%02x", offset);
+ else
+ di->di_printf("r%d", (insn & 0x0f));
+ }
+ if (insn & (1 << 24))
+ di->di_printf("]");
+ }
+}
+
+static void
+disasm_insn_ldcstc(const disasm_interface_t *di, u_int insn, u_int loc)
+{
+ if (((insn >> 8) & 0xf) == 1)
+ di->di_printf("f%d, ", (insn >> 12) & 0x07);
+ else
+ di->di_printf("c%d, ", (insn >> 12) & 0x0f);
+
+ di->di_printf("[r%d", (insn >> 16) & 0x0f);
+
+ di->di_printf("%s, ", (insn & (1 << 24)) ? "" : "]");
+
+ if (!(insn & (1 << 23)))
+ di->di_printf("-");
+
+ di->di_printf("#0x%03x", (insn & 0xff) << 2);
+
+ if (insn & (1 << 24))
+ di->di_printf("]");
+
+ if (insn & (1 << 21))
+ di->di_printf("!");
+}
+
+static u_int
+disassemble_readword(u_int address)
+{
+ return(*((u_int *)address));
+}
+
+static void
+disassemble_printaddr(u_int address)
+{
+ printf("0x%08x", address);
+}
+
+static const disasm_interface_t disassemble_di = {
+ disassemble_readword, disassemble_printaddr, db_printf
+};
+
+void
+disassemble(u_int address)
+{
+
+ (void)disasm(&disassemble_di, address, 0);
+}
+
+/* End of disassem.c */
diff --git a/sys/arm/arm/dump_machdep.c b/sys/arm/arm/dump_machdep.c
new file mode 100644
index 000000000000..ead54ca7b225
--- /dev/null
+++ b/sys/arm/arm/dump_machdep.c
@@ -0,0 +1,104 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2002 Marcel Moolenaar
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_watchdog.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/sysctl.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/kerneldump.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <machine/dump.h>
+#include <machine/elf.h>
+#include <machine/md_var.h>
+#include <machine/pcb.h>
+#include <machine/armreg.h>
+#include <machine/vmparam.h> /* For KERNVIRTADDR */
+
+int do_minidump = 1;
+SYSCTL_INT(_debug, OID_AUTO, minidump, CTLFLAG_RWTUN, &do_minidump, 0,
+ "Enable mini crash dumps");
+
+void
+dumpsys_wbinv_all(void)
+{
+
+ /*
+ * Make sure we write coherent data. Note that in the SMP case this
+ * only operates on the L1 cache of the current CPU, but all other CPUs
+ * have already been stopped, and their flush/invalidate was done as
+ * part of stopping.
+ */
+ dcache_wbinv_poc_all();
+#ifdef __XSCALE__
+ xscale_cache_clean_minidata();
+#endif
+}
+
+void
+dumpsys_map_chunk(vm_paddr_t pa, size_t chunk, void **va)
+{
+ vm_paddr_t a;
+ int i;
+
+ for (i = 0; i < chunk; i++) {
+ a = pa + i * PAGE_SIZE;
+ *va = pmap_kenter_temporary(trunc_page(a), i);
+ }
+}
+
+/*
+ * Add a header to be used by libkvm to get the va to pa delta
+ */
+int
+dumpsys_write_aux_headers(struct dumperinfo *di)
+{
+ Elf_Phdr phdr;
+ int error;
+
+ bzero(&phdr, sizeof(phdr));
+ phdr.p_type = PT_DUMP_DELTA;
+ phdr.p_flags = PF_R; /* XXX */
+ phdr.p_offset = 0;
+ phdr.p_vaddr = KERNVIRTADDR;
+ phdr.p_paddr = pmap_kextract(KERNVIRTADDR);
+ phdr.p_filesz = 0;
+ phdr.p_memsz = 0;
+ phdr.p_align = PAGE_SIZE;
+
+ error = dumpsys_buf_write(di, (char*)&phdr, sizeof(phdr));
+ return (error);
+}
diff --git a/sys/arm/arm/elf_machdep.c b/sys/arm/arm/elf_machdep.c
new file mode 100644
index 000000000000..d32dbb5baf90
--- /dev/null
+++ b/sys/arm/arm/elf_machdep.c
@@ -0,0 +1,336 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright 1996-1998 John D. Polstra.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/exec.h>
+#include <sys/imgact.h>
+#include <sys/linker.h>
+#include <sys/sysent.h>
+#include <sys/imgact_elf.h>
+#include <sys/proc.h>
+#include <sys/syscall.h>
+#include <sys/signalvar.h>
+#include <sys/vnode.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <vm/vm_param.h>
+
+#include <machine/elf.h>
+#include <machine/md_var.h>
+#include <machine/stack.h>
+#ifdef VFP
+#include <machine/vfp.h>
+#endif
+
+#include "opt_ddb.h" /* for OPT_DDB */
+#include "opt_global.h" /* for OPT_KDTRACE_HOOKS */
+#include "opt_stack.h" /* for OPT_STACK */
+
+static boolean_t elf32_arm_abi_supported(struct image_params *, int32_t *,
+ uint32_t *);
+
+u_long elf_hwcap;
+u_long elf_hwcap2;
+
+struct sysentvec elf32_freebsd_sysvec = {
+ .sv_size = SYS_MAXSYSCALL,
+ .sv_table = sysent,
+ .sv_transtrap = NULL,
+ .sv_fixup = __elfN(freebsd_fixup),
+ .sv_sendsig = sendsig,
+ .sv_sigcode = sigcode,
+ .sv_szsigcode = &szsigcode,
+ .sv_name = "FreeBSD ELF32",
+ .sv_coredump = __elfN(coredump),
+ .sv_imgact_try = NULL,
+ .sv_minsigstksz = MINSIGSTKSZ,
+ .sv_minuser = VM_MIN_ADDRESS,
+ .sv_maxuser = VM_MAXUSER_ADDRESS,
+ .sv_usrstack = USRSTACK,
+ .sv_psstrings = PS_STRINGS,
+ .sv_stackprot = VM_PROT_ALL,
+ .sv_copyout_auxargs = __elfN(freebsd_copyout_auxargs),
+ .sv_copyout_strings = exec_copyout_strings,
+ .sv_setregs = exec_setregs,
+ .sv_fixlimit = NULL,
+ .sv_maxssiz = NULL,
+ .sv_flags =
+ SV_ASLR | SV_SHP | SV_TIMEKEEP | SV_RNG_SEED_VER |
+ SV_ABI_FREEBSD | SV_ILP32,
+ .sv_set_syscall_retval = cpu_set_syscall_retval,
+ .sv_fetch_syscall_args = cpu_fetch_syscall_args,
+ .sv_syscallnames = syscallnames,
+ .sv_shared_page_base = SHAREDPAGE,
+ .sv_shared_page_len = PAGE_SIZE,
+ .sv_schedtail = NULL,
+ .sv_thread_detach = NULL,
+ .sv_trap = NULL,
+ .sv_hwcap = &elf_hwcap,
+ .sv_hwcap2 = &elf_hwcap2,
+};
+INIT_SYSENTVEC(elf32_sysvec, &elf32_freebsd_sysvec);
+
+static Elf32_Brandinfo freebsd_brand_info = {
+ .brand = ELFOSABI_FREEBSD,
+ .machine = EM_ARM,
+ .compat_3_brand = "FreeBSD",
+ .emul_path = NULL,
+ .interp_path = "/libexec/ld-elf.so.1",
+ .sysvec = &elf32_freebsd_sysvec,
+ .interp_newpath = NULL,
+ .brand_note = &elf32_freebsd_brandnote,
+ .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE,
+ .header_supported= elf32_arm_abi_supported,
+};
+
+SYSINIT(elf32, SI_SUB_EXEC, SI_ORDER_FIRST,
+ (sysinit_cfunc_t) elf32_insert_brand_entry,
+ &freebsd_brand_info);
+
+static boolean_t
+elf32_arm_abi_supported(struct image_params *imgp, int32_t *osrel __unused,
+ uint32_t *fctl0 __unused)
+{
+ const Elf_Ehdr *hdr = (const Elf_Ehdr *)imgp->image_header;
+
+ /*
+ * When configured for EABI, FreeBSD supports EABI vesions 4 and 5.
+ */
+ if (EF_ARM_EABI_VERSION(hdr->e_flags) < EF_ARM_EABI_FREEBSD_MIN) {
+ if (bootverbose)
+ uprintf("Attempting to execute non EABI binary (rev %d) image %s",
+ EF_ARM_EABI_VERSION(hdr->e_flags), imgp->args->fname);
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+void
+elf32_dump_thread(struct thread *td, void *dst, size_t *off)
+{
+#ifdef VFP
+ mcontext_vfp_t vfp;
+
+ if (dst != NULL) {
+ get_vfpcontext(td, &vfp);
+ *off = elf32_populate_note(NT_ARM_VFP, &vfp, dst, sizeof(vfp),
+ NULL);
+ } else
+ *off = elf32_populate_note(NT_ARM_VFP, NULL, NULL, sizeof(vfp),
+ NULL);
+#endif
+}
+
+bool
+elf_is_ifunc_reloc(Elf_Size r_info __unused)
+{
+
+ return (false);
+}
+
+/*
+ * It is possible for the compiler to emit relocations for unaligned data.
+ * We handle this situation with these inlines.
+ */
+#define RELOC_ALIGNED_P(x) \
+ (((uintptr_t)(x) & (sizeof(void *) - 1)) == 0)
+
+static __inline Elf_Addr
+load_ptr(Elf_Addr *where)
+{
+ Elf_Addr res;
+
+ if (RELOC_ALIGNED_P(where))
+ return *where;
+ memcpy(&res, where, sizeof(res));
+ return (res);
+}
+
+static __inline void
+store_ptr(Elf_Addr *where, Elf_Addr val)
+{
+ if (RELOC_ALIGNED_P(where))
+ *where = val;
+ else
+ memcpy(where, &val, sizeof(val));
+}
+#undef RELOC_ALIGNED_P
+
+/* Process one elf relocation with addend. */
+static int
+elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data,
+ int type, int local, elf_lookup_fn lookup)
+{
+ Elf_Addr *where;
+ Elf_Addr addr;
+ Elf_Addr addend;
+ Elf_Word rtype, symidx;
+ const Elf_Rel *rel;
+ const Elf_Rela *rela;
+ int error;
+
+ switch (type) {
+ case ELF_RELOC_REL:
+ rel = (const Elf_Rel *)data;
+ where = (Elf_Addr *) (relocbase + rel->r_offset);
+ addend = load_ptr(where);
+ rtype = ELF_R_TYPE(rel->r_info);
+ symidx = ELF_R_SYM(rel->r_info);
+ break;
+ case ELF_RELOC_RELA:
+ rela = (const Elf_Rela *)data;
+ where = (Elf_Addr *) (relocbase + rela->r_offset);
+ addend = rela->r_addend;
+ rtype = ELF_R_TYPE(rela->r_info);
+ symidx = ELF_R_SYM(rela->r_info);
+ break;
+ default:
+ panic("unknown reloc type %d\n", type);
+ }
+
+ if (local) {
+ if (rtype == R_ARM_RELATIVE) { /* A + B */
+ addr = elf_relocaddr(lf, relocbase + addend);
+ if (load_ptr(where) != addr)
+ store_ptr(where, addr);
+ }
+ return (0);
+ }
+
+ switch (rtype) {
+ case R_ARM_NONE: /* none */
+ break;
+
+ case R_ARM_ABS32:
+ error = lookup(lf, symidx, 1, &addr);
+ if (error != 0)
+ return (-1);
+ store_ptr(where, addr + load_ptr(where));
+ break;
+
+ case R_ARM_COPY: /* none */
+ /*
+ * There shouldn't be copy relocations in kernel
+ * objects.
+ */
+ printf("kldload: unexpected R_COPY relocation, "
+ "symbol index %d\n", symidx);
+ return (-1);
+ break;
+
+ case R_ARM_JUMP_SLOT:
+ error = lookup(lf, symidx, 1, &addr);
+ if (error == 0) {
+ store_ptr(where, addr);
+ return (0);
+ }
+ return (-1);
+ case R_ARM_RELATIVE:
+ break;
+
+ default:
+ printf("kldload: unexpected relocation type %d, "
+ "symbol index %d\n", rtype, symidx);
+ return (-1);
+ }
+ return(0);
+}
+
+int
+elf_reloc(linker_file_t lf, Elf_Addr relocbase, const void *data, int type,
+ elf_lookup_fn lookup)
+{
+
+ return (elf_reloc_internal(lf, relocbase, data, type, 0, lookup));
+}
+
+int
+elf_reloc_local(linker_file_t lf, Elf_Addr relocbase, const void *data,
+ int type, elf_lookup_fn lookup)
+{
+
+ return (elf_reloc_internal(lf, relocbase, data, type, 1, lookup));
+}
+
+int
+elf_cpu_load_file(linker_file_t lf)
+{
+
+ /*
+ * The pmap code does not do an icache sync upon establishing executable
+ * mappings in the kernel pmap. It's an optimization based on the fact
+ * that kernel memory allocations always have EXECUTABLE protection even
+ * when the memory isn't going to hold executable code. The only time
+ * kernel memory holding instructions does need a sync is after loading
+ * a kernel module, and that's when this function gets called.
+ *
+ * This syncs data and instruction caches after loading a module. We
+ * don't worry about the kernel itself (lf->id is 1) as locore.S did
+ * that on entry. Even if data cache maintenance was done by IO code,
+ * the relocation fixup process creates dirty cache entries that we must
+ * write back before doing icache sync. The instruction cache sync also
+ * invalidates the branch predictor cache on platforms that have one.
+ */
+ if (lf->id == 1)
+ return (0);
+ dcache_wb_pou((vm_offset_t)lf->address, (vm_size_t)lf->size);
+ icache_inv_all();
+
+#if defined(DDB) || defined(KDTRACE_HOOKS) || defined(STACK)
+ /*
+ * Inform the stack(9) code of the new module, so it can acquire its
+ * per-module unwind data.
+ */
+ unwind_module_loaded(lf);
+#endif
+
+ return (0);
+}
+
+int
+elf_cpu_parse_dynamic(caddr_t loadbase __unused, Elf_Dyn *dynamic __unused)
+{
+
+ return (0);
+}
+
+int
+elf_cpu_unload_file(linker_file_t lf)
+{
+
+#if defined(DDB) || defined(KDTRACE_HOOKS) || defined(STACK)
+ /* Inform the stack(9) code that this module is gone. */
+ unwind_module_unloaded(lf);
+#endif
+ return (0);
+}
diff --git a/sys/arm/arm/elf_note.S b/sys/arm/arm/elf_note.S
new file mode 100644
index 000000000000..ef7eb40587ee
--- /dev/null
+++ b/sys/arm/arm/elf_note.S
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 2014 Andrew Turner <andrew@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <machine/asm.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <machine/asmacros.h>
+
+/* An ELF note so GDB detects this as a FreeBSD elf file */
+ELFNOTE(.note.tag, 1, "FreeBSD", .long, __FreeBSD_version);
+
diff --git a/sys/arm/arm/exception.S b/sys/arm/arm/exception.S
new file mode 100644
index 000000000000..92e815b068fa
--- /dev/null
+++ b/sys/arm/arm/exception.S
@@ -0,0 +1,418 @@
+/* $NetBSD: exception.S,v 1.13 2003/10/31 16:30:15 scw Exp $ */
+
+/*-
+ * Copyright (c) 1994-1997 Mark Brinicombe.
+ * Copyright (c) 1994 Brini.
+ * All rights reserved.
+ *
+ * This code is derived from software written for Brini by Mark Brinicombe
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Brini.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * RiscBSD kernel project
+ *
+ * exception.S
+ *
+ * Low level handlers for exception vectors
+ *
+ * Created : 24/09/94
+ *
+ * Based on kate/display/abort.s
+ *
+ */
+
+#include "assym.inc"
+
+#include <machine/asm.h>
+#include <machine/armreg.h>
+#include <machine/asmacros.h>
+#include <machine/trap.h>
+
+__FBSDID("$FreeBSD$");
+
+#ifdef KDTRACE_HOOKS
+ .bss
+ .align 4
+ .global _C_LABEL(dtrace_invop_jump_addr)
+_C_LABEL(dtrace_invop_jump_addr):
+ .word 0
+ .word 0
+#endif
+
+ .text
+ .align 2
+
+/*
+ * ASM macros for pushing and pulling trapframes from the stack
+ *
+ * These macros are used to handle the irqframe and trapframe structures
+ * defined above.
+ */
+
+/*
+ * PUSHFRAME - macro to push a trap frame on the stack in the current mode
+ * Since the current mode is used, the SVC lr field is not defined.
+ */
+#define PUSHFRAME \
+ sub sp, sp, #4; /* Align the stack */ \
+ str lr, [sp, #-4]!; /* Push the return address */ \
+ sub sp, sp, #(4*17); /* Adjust the stack pointer */ \
+ stmia sp, {r0-r12}; /* Push the user mode registers */ \
+ add r0, sp, #(4*13); /* Adjust the stack pointer */ \
+ stmia r0, {r13-r14}^; /* Push the user mode registers */ \
+ mov r0, r0; /* NOP for previous instruction */ \
+ mrs r0, spsr; /* Put the SPSR on the stack */ \
+ str r0, [sp, #-4]!;
+
+/*
+ * PULLFRAME - macro to pull a trap frame from the stack in the current mode
+ * Since the current mode is used, the SVC lr field is ignored.
+ */
+
+#define PULLFRAME \
+ ldr r0, [sp], #4 ; /* Get the SPSR from stack */ \
+ msr spsr_fsxc, r0; \
+ clrex; \
+ ldmia sp, {r0-r14}^; /* Restore registers (usr mode) */ \
+ mov r0, r0; /* NOP for previous instruction */ \
+ add sp, sp, #(4*17); /* Adjust the stack pointer */ \
+ ldr lr, [sp], #4; /* Pull the return address */ \
+ add sp, sp, #4 /* Align the stack */
+
+/*
+ * PUSHFRAMEINSVC - macro to push a trap frame on the stack in SVC32 mode
+ * This should only be used if the processor is not currently in SVC32
+ * mode. The processor mode is switched to SVC mode and the trap frame is
+ * stored. The SVC lr field is used to store the previous value of
+ * lr in SVC mode.
+ */
+#define PUSHFRAMEINSVC \
+ stmdb sp, {r0-r3}; /* Save 4 registers */ \
+ mov r0, lr; /* Save xxx32 r14 */ \
+ mov r1, sp; /* Save xxx32 sp */ \
+ mrs r3, spsr; /* Save xxx32 spsr */ \
+ mrs r2, cpsr; /* Get the CPSR */ \
+ bic r2, r2, #(PSR_MODE); /* Fix for SVC mode */ \
+ orr r2, r2, #(PSR_SVC32_MODE); \
+ msr cpsr_c, r2; /* Punch into SVC mode */ \
+ mov r2, sp; /* Save SVC sp */ \
+ bic sp, sp, #7; /* Align sp to an 8-byte addrress */ \
+ sub sp, sp, #(4 * 17); /* Pad trapframe to keep alignment */ \
+ /* and for dtrace to emulate push/pop */ \
+ str r0, [sp, #-4]!; /* Push return address */ \
+ str lr, [sp, #-4]!; /* Push SVC lr */ \
+ str r2, [sp, #-4]!; /* Push SVC sp */ \
+ msr spsr_fsxc, r3; /* Restore correct spsr */ \
+ ldmdb r1, {r0-r3}; /* Restore 4 regs from xxx mode */ \
+ sub sp, sp, #(4*15); /* Adjust the stack pointer */ \
+ stmia sp, {r0-r12}; /* Push the user mode registers */ \
+ add r0, sp, #(4*13); /* Adjust the stack pointer */ \
+ stmia r0, {r13-r14}^; /* Push the user mode registers */ \
+ mov r0, r0; /* NOP for previous instruction */ \
+ mrs r0, spsr; /* Put the SPSR on the stack */ \
+ str r0, [sp, #-4]!
+
+/*
+ * PULLFRAMEFROMSVCANDEXIT - macro to pull a trap frame from the stack
+ * in SVC32 mode and restore the saved processor mode and PC.
+ * This should be used when the SVC lr register needs to be restored on
+ * exit.
+ */
+
+#define PULLFRAMEFROMSVCANDEXIT \
+ ldr r0, [sp], #4; /* Get the SPSR from stack */ \
+ msr spsr_fsxc, r0; /* restore SPSR */ \
+ clrex; \
+ ldmia sp, {r0-r14}^; /* Restore registers (usr mode) */ \
+ mov r0, r0; /* NOP for previous instruction */ \
+ add sp, sp, #(4*15); /* Adjust the stack pointer */ \
+ ldmia sp, {sp, lr, pc}^ /* Restore lr and exit */
+
+/*
+ * Unwind hints so we can unwind past functions that use
+ * PULLFRAMEFROMSVCANDEXIT. They are run in reverse order.
+ * As the last thing we do is restore the stack pointer
+ * we can ignore the padding at the end of struct trapframe.
+ */
+#define UNWINDSVCFRAME \
+ .save {r13-r15}; /* Restore sp, lr, pc */ \
+ .pad #(2*4); /* Skip user sp and lr */ \
+ .save {r0-r12}; /* Restore r0-r12 */ \
+ .pad #(4) /* Skip spsr */
+
+#define DO_AST \
+ ldr r0, [sp]; /* Get the SPSR from stack */ \
+ mrs r4, cpsr; /* save CPSR */ \
+ orr r1, r4, #(PSR_I|PSR_F); \
+ msr cpsr_c, r1; /* Disable interrupts */ \
+ and r0, r0, #(PSR_MODE); /* Returning to USR mode? */ \
+ teq r0, #(PSR_USR32_MODE); \
+ bne 2f; /* Nope, get out now */ \
+ bic r4, r4, #(PSR_I|PSR_F); \
+1: GET_CURTHREAD_PTR(r5); \
+ ldr r1, [r5, #(TD_FLAGS)]; \
+ and r1, r1, #(TDF_ASTPENDING|TDF_NEEDRESCHED); \
+ teq r1, #0; \
+ beq 2f; /* Nope. Just bail */ \
+ msr cpsr_c, r4; /* Restore interrupts */ \
+ mov r0, sp; \
+ bl _C_LABEL(ast); /* ast(frame) */ \
+ orr r0, r4, #(PSR_I|PSR_F); \
+ msr cpsr_c, r0; \
+ b 1b; \
+2:
+
+
+/*
+ * Entry point for a Software Interrupt (SWI).
+ *
+ * The hardware switches to svc32 mode on a swi, so we're already on the
+ * right stack; just build a trapframe and call the handler.
+ */
+ASENTRY_NP(swi_entry)
+ PUSHFRAME /* Build the trapframe on the */
+ mov r0, sp /* scv32 stack, pass it to the */
+ bl _C_LABEL(swi_handler) /* swi handler. */
+ /*
+ * The fork_trampoline() code in swtch.S aranges for the MI fork_exit()
+ * to return to swi_exit here, to return to userland. The net effect is
+ * that a newly created thread appears to return from a SWI just like
+ * the parent thread that created it.
+ */
+ASEENTRY_NP(swi_exit)
+ DO_AST /* Handle pending signals. */
+ PULLFRAME /* Deallocate trapframe. */
+ movs pc, lr /* Return to userland. */
+ STOP_UNWINDING /* Don't unwind into user mode. */
+EEND(swi_exit)
+END(swi_entry)
+
+/*
+ * Standard exception exit handler.
+ *
+ * This is used to return from all exceptions except SWI. It uses DO_AST and
+ * PULLFRAMEFROMSVCANDEXIT and can only be called if the exception entry code
+ * used PUSHFRAMEINSVC.
+ *
+ * If the return is to user mode, this uses DO_AST to deliver any pending
+ * signals and/or handle TDF_NEEDRESCHED first.
+ */
+ASENTRY_NP(exception_exit)
+ DO_AST /* Handle pending signals. */
+ PULLFRAMEFROMSVCANDEXIT /* Return. */
+ UNWINDSVCFRAME /* Special unwinding for exceptions. */
+END(exception_exit)
+
+/*
+ * Entry point for a Prefetch Abort exception.
+ *
+ * The hardware switches to the abort mode stack; we switch to svc32 before
+ * calling the handler, then return directly to the original mode/stack
+ * on exit (without transitioning back through the abort mode stack).
+ */
+ASENTRY_NP(prefetch_abort_entry)
+#ifdef __XSCALE__
+ nop /* Make absolutely sure any pending */
+ nop /* imprecise aborts have occurred. */
+#endif
+ sub lr, lr, #4 /* Adjust the lr. Transition to scv32 */
+ PUSHFRAMEINSVC /* mode stack, build trapframe there. */
+ adr lr, exception_exit /* Return from handler via standard */
+ mov r0, sp /* exception exit routine. Pass the */
+ mov r1, #1 /* Type flag */
+ b _C_LABEL(abort_handler)
+END(prefetch_abort_entry)
+
+/*
+ * Entry point for a Data Abort exception.
+ *
+ * The hardware switches to the abort mode stack; we switch to svc32 before
+ * calling the handler, then return directly to the original mode/stack
+ * on exit (without transitioning back through the abort mode stack).
+ */
+ASENTRY_NP(data_abort_entry)
+#ifdef __XSCALE__
+ nop /* Make absolutely sure any pending */
+ nop /* imprecise aborts have occurred. */
+#endif
+ sub lr, lr, #8 /* Adjust the lr. Transition to scv32 */
+ PUSHFRAMEINSVC /* mode stack, build trapframe there. */
+ adr lr, exception_exit /* Exception exit routine */
+ mov r0, sp /* Trapframe to the handler */
+ mov r1, #0 /* Type flag */
+ b _C_LABEL(abort_handler)
+END(data_abort_entry)
+
+/*
+ * Entry point for an Undefined Instruction exception.
+ *
+ * The hardware switches to the undefined mode stack; we switch to svc32 before
+ * calling the handler, then return directly to the original mode/stack
+ * on exit (without transitioning back through the undefined mode stack).
+ */
+ASENTRY_NP(undefined_entry)
+ PUSHFRAMEINSVC /* mode stack, build trapframe there. */
+ mov r4, r0 /* R0 contains SPSR */
+ adr lr, exception_exit /* Return from handler via standard */
+ mov r0, sp /* exception exit routine. pass frame */
+
+ ldr r2, [sp, #(TF_PC)] /* load pc */
+#if __ARM_ARCH >= 7
+ tst r4, #(PSR_T) /* test if PSR_T */
+ subne r2, r2, #(THUMB_INSN_SIZE)
+ subeq r2, r2, #(INSN_SIZE)
+#else
+ sub r2, r2, #(INSN_SIZE) /* fix pc */
+#endif
+ str r2, [sp, #TF_PC] /* store pc */
+
+#ifdef KDTRACE_HOOKS
+ /* Check if dtrace is enabled */
+ ldr r1, =_C_LABEL(dtrace_invop_jump_addr)
+ ldr r3, [r1]
+ cmp r3, #0
+ beq undefinedinstruction
+
+ and r4, r4, #(PSR_MODE) /* Mask out unneeded bits */
+ cmp r4, #(PSR_USR32_MODE) /* Check if we came from usermode */
+ beq undefinedinstruction
+
+ ldr r4, [r2] /* load instrution */
+ ldr r1, =FBT_BREAKPOINT /* load fbt inv op */
+ cmp r1, r4
+ bne undefinedinstruction
+
+ bx r3 /* call invop_jump_addr */
+#endif
+ b undefinedinstruction /* call stadnard handler */
+END(undefined_entry)
+
+/*
+ * Entry point for a normal IRQ.
+ *
+ * The hardware switches to the IRQ mode stack; we switch to svc32 before
+ * calling the handler, then return directly to the original mode/stack
+ * on exit (without transitioning back through the IRQ mode stack).
+ */
+ASENTRY_NP(irq_entry)
+ sub lr, lr, #4 /* Adjust the lr. Transition to scv32 */
+ PUSHFRAMEINSVC /* mode stack, build trapframe there. */
+ adr lr, exception_exit /* Return from handler via standard */
+ mov r0, sp /* exception exit routine. Pass the */
+ b _C_LABEL(intr_irq_handler)/* trapframe to the handler. */
+END(irq_entry)
+
+/*
+ * Entry point for an FIQ interrupt.
+ *
+ * We don't currently support FIQ handlers very much. Something can
+ * install itself in the FIQ vector using code (that may or may not work
+ * these days) in fiq.c. If nobody does that and an FIQ happens, this
+ * default handler just disables FIQs and otherwise ignores it.
+ */
+ASENTRY_NP(fiq_entry)
+ mrs r8, cpsr /* FIQ handling isn't supported, */
+ bic r8, #(PSR_F) /* just disable FIQ and return. */
+ msr cpsr_c, r8 /* The r8 we trash here is the */
+ subs pc, lr, #4 /* banked FIQ-mode r8. */
+END(fiq_entry)
+
+/*
+ * Entry point for an Address Exception exception.
+ * This is an arm26 exception that should never happen.
+ */
+ASENTRY_NP(addr_exception_entry)
+ mov r3, lr
+ mrs r2, spsr
+ mrs r1, cpsr
+ adr r0, Laddr_exception_msg
+ b _C_LABEL(panic)
+Laddr_exception_msg:
+ .asciz "Address Exception CPSR=0x%08x SPSR=0x%08x LR=0x%08x\n"
+ .balign 4
+END(addr_exception_entry)
+
+/*
+ * Entry point for the system Reset vector.
+ * This should never happen, so panic.
+ */
+ASENTRY_NP(reset_entry)
+ mov r1, lr
+ adr r0, Lreset_panicmsg
+ b _C_LABEL(panic)
+ /* NOTREACHED */
+Lreset_panicmsg:
+ .asciz "Reset vector called, LR = 0x%08x"
+ .balign 4
+END(reset_entry)
+
+/*
+ * page0 and page0_data -- An image of the ARM vectors which is copied to
+ * the ARM vectors page (high or low) as part of CPU initialization. The
+ * code that does the copy assumes that page0_data holds one 32-bit word
+ * of data for each of the predefined ARM vectors. It also assumes that
+ * page0_data follows the vectors in page0, but other stuff can appear
+ * between the two. We currently leave room between the two for some fiq
+ * handler code to be copied in.
+ */
+ .global _C_LABEL(page0), _C_LABEL(page0_data)
+
+_C_LABEL(page0):
+ ldr pc, .Lreset_entry
+ ldr pc, .Lundefined_entry
+ ldr pc, .Lswi_entry
+ ldr pc, .Lprefetch_abort_entry
+ ldr pc, .Ldata_abort_entry
+ ldr pc, .Laddr_exception_entry
+ ldr pc, .Lirq_entry
+.fiqv: ldr pc, .Lfiq_entry
+ .space 256 /* room for some fiq handler code */
+
+_C_LABEL(page0_data):
+.Lreset_entry: .word reset_entry
+.Lundefined_entry: .word undefined_entry
+.Lswi_entry: .word swi_entry
+.Lprefetch_abort_entry: .word prefetch_abort_entry
+.Ldata_abort_entry: .word data_abort_entry
+.Laddr_exception_entry: .word addr_exception_entry
+.Lirq_entry: .word irq_entry
+.Lfiq_entry: .word fiq_entry
+
+/*
+ * These items are used by the code in fiq.c to install what it calls the
+ * "null" handler. It's actually our default vector entry that just jumps
+ * to the default handler which just disables FIQs and returns.
+ */
+ .global _C_LABEL(fiq_nullhandler_code), _C_LABEL(fiq_nullhandler_size)
+
+_C_LABEL(fiq_nullhandler_code):
+ .word .fiqv
+_C_LABEL(fiq_nullhandler_size):
+ .word 4
+
+
diff --git a/sys/arm/arm/fiq.c b/sys/arm/arm/fiq.c
new file mode 100644
index 000000000000..05e42b0ae04a
--- /dev/null
+++ b/sys/arm/arm/fiq.c
@@ -0,0 +1,166 @@
+/* $NetBSD: fiq.c,v 1.5 2002/04/03 23:33:27 thorpej Exp $ */
+
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 2001, 2002 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Jason R. Thorpe for Wasabi Systems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the NetBSD Project by
+ * Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <machine/armreg.h>
+#include <machine/cpufunc.h>
+#include <machine/fiq.h>
+#include <vm/vm.h>
+#include <machine/pcb.h>
+#include <vm/pmap.h>
+#include <machine/cpu.h>
+
+TAILQ_HEAD(, fiqhandler) fiqhandler_stack =
+ TAILQ_HEAD_INITIALIZER(fiqhandler_stack);
+
+extern char *fiq_nullhandler_code;
+extern uint32_t fiq_nullhandler_size;
+
+/*
+ * fiq_installhandler:
+ *
+ * Actually install the FIQ handler down at the FIQ vector.
+ *
+ * The FIQ vector is fixed by the hardware definition as the
+ * seventh 32-bit word in the vector page.
+ *
+ * Note: If the FIQ is invoked via an extra layer of
+ * indirection, the actual FIQ code store lives in the
+ * data segment, so there is no need to manipulate
+ * the vector page's protection.
+ */
+static void
+fiq_installhandler(void *func, size_t size)
+{
+ const uint32_t fiqvector = 7 * sizeof(uint32_t);
+
+ memcpy((void *)(vector_page + fiqvector), func, size);
+ icache_sync((vm_offset_t) fiqvector, size);
+}
+
+/*
+ * fiq_claim:
+ *
+ * Claim the FIQ vector.
+ */
+int
+fiq_claim(struct fiqhandler *fh)
+{
+ struct fiqhandler *ofh;
+ u_int oldirqstate;
+ int error = 0;
+
+ if (fh->fh_size > 0x100)
+ return (EFBIG);
+
+ oldirqstate = disable_interrupts(PSR_F);
+
+ if ((ofh = TAILQ_FIRST(&fiqhandler_stack)) != NULL) {
+ if ((ofh->fh_flags & FH_CANPUSH) == 0) {
+ error = EBUSY;
+ goto out;
+ }
+
+ /* Save the previous FIQ handler's registers. */
+ if (ofh->fh_regs != NULL)
+ fiq_getregs(ofh->fh_regs);
+ }
+
+ /* Set FIQ mode registers to ours. */
+ if (fh->fh_regs != NULL)
+ fiq_setregs(fh->fh_regs);
+
+ TAILQ_INSERT_HEAD(&fiqhandler_stack, fh, fh_list);
+
+ /* Now copy the actual handler into place. */
+ fiq_installhandler(fh->fh_func, fh->fh_size);
+
+ /* Make sure FIQs are enabled when we return. */
+ oldirqstate &= ~PSR_F;
+
+ out:
+ restore_interrupts(oldirqstate);
+ return (error);
+}
+
+/*
+ * fiq_release:
+ *
+ * Release the FIQ vector.
+ */
+void
+fiq_release(struct fiqhandler *fh)
+{
+ u_int oldirqstate;
+ struct fiqhandler *ofh;
+
+ oldirqstate = disable_interrupts(PSR_F);
+
+ /*
+ * If we are the currently active FIQ handler, then we
+ * need to save our registers and pop the next one back
+ * into the vector.
+ */
+ if (fh == TAILQ_FIRST(&fiqhandler_stack)) {
+ if (fh->fh_regs != NULL)
+ fiq_getregs(fh->fh_regs);
+ TAILQ_REMOVE(&fiqhandler_stack, fh, fh_list);
+ if ((ofh = TAILQ_FIRST(&fiqhandler_stack)) != NULL) {
+ if (ofh->fh_regs != NULL)
+ fiq_setregs(ofh->fh_regs);
+ fiq_installhandler(ofh->fh_func, ofh->fh_size);
+ }
+ } else
+ TAILQ_REMOVE(&fiqhandler_stack, fh, fh_list);
+
+ if (TAILQ_FIRST(&fiqhandler_stack) == NULL) {
+ /* Copy the NULL handler back down into the vector. */
+ fiq_installhandler(fiq_nullhandler_code, fiq_nullhandler_size);
+
+ /* Make sure FIQs are disabled when we return. */
+ oldirqstate |= PSR_F;
+ }
+
+ restore_interrupts(oldirqstate);
+}
diff --git a/sys/arm/arm/fiq_subr.S b/sys/arm/arm/fiq_subr.S
new file mode 100644
index 000000000000..f6300b1f442a
--- /dev/null
+++ b/sys/arm/arm/fiq_subr.S
@@ -0,0 +1,94 @@
+/* $NetBSD: fiq_subr.S,v 1.3 2002/04/12 18:50:31 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2001 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Jason R. Thorpe for Wasabi Systems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the NetBSD Project by
+ * Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+#include <machine/asm.h>
+__FBSDID("$FreeBSD$");
+
+#include <machine/armreg.h>
+
+/*
+ * MODE_CHANGE_NOP should be inserted between a mode change and a
+ * banked register (R8--R15) access.
+ */
+#if defined(CPU_ARM2) || defined(CPU_ARM250)
+#define MODE_CHANGE_NOP mov r0, r0
+#else
+#define MODE_CHANGE_NOP /* Data sheet says ARM3 doesn't need it */
+#endif
+
+#define SWITCH_TO_FIQ_MODE \
+ mrs r2, cpsr ; \
+ mov r3, r2 ; \
+ bic r2, r2, #(PSR_MODE) ; \
+ orr r2, r2, #(PSR_FIQ32_MODE) ; \
+ msr cpsr_fsxc, r2
+
+#define BACK_TO_SVC_MODE \
+ msr cpsr_fsxc, r3
+
+/*
+ * fiq_getregs:
+ *
+ * Fetch the FIQ mode banked registers into the fiqhandler
+ * structure.
+ */
+ENTRY(fiq_getregs)
+ SWITCH_TO_FIQ_MODE
+
+ stmia r0, {r8-r13}
+
+ BACK_TO_SVC_MODE
+ RET
+END(fiq_getregs)
+
+/*
+ * fiq_setregs:
+ *
+ * Load the FIQ mode banked registers from the fiqhandler
+ * structure.
+ */
+ENTRY(fiq_setregs)
+ SWITCH_TO_FIQ_MODE
+
+ ldmia r0, {r8-r13}
+
+ BACK_TO_SVC_MODE
+ RET
+END(fiq_setregs)
+
diff --git a/sys/arm/arm/fusu.S b/sys/arm/arm/fusu.S
new file mode 100644
index 000000000000..1a77377cb148
--- /dev/null
+++ b/sys/arm/arm/fusu.S
@@ -0,0 +1,304 @@
+/* $NetBSD: fusu.S,v 1.10 2003/12/01 13:34:44 rearnsha Exp $ */
+
+/*-
+ * Copyright (c) 1996-1998 Mark Brinicombe.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Mark Brinicombe
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <machine/asm.h>
+#include <machine/armreg.h>
+#include "assym.inc"
+__FBSDID("$FreeBSD$");
+
+ .syntax unified
+
+#define GET_PCB(tmp) \
+ mrc p15, 0, tmp, c13, c0, 4; \
+ add tmp, tmp, #(TD_PCB)
+
+/*
+ * casueword32(volatile uint32_t *base, uint32_t oldval, uint32_t *oldvalp,
+ * uint32_t newval);
+ */
+
+ENTRY(casueword)
+EENTRY_NP(casueword32)
+ stmfd sp!, {r4, r5, r6}
+
+ ldr r4, =(VM_MAXUSER_ADDRESS-3)
+ cmp r0, r4
+ mvncs r0, #0
+ bcs 1f
+
+ GET_PCB(r6)
+ ldr r6, [r6]
+
+#ifdef DIAGNOSTIC
+ teq r6, #0x00000000
+ ldmfdeq sp!, {r4, r5, r6}
+ beq .Lfusupcbfault
+#endif
+
+ adr r4, .Lcasuwordfault
+ str r4, [r6, #PCB_ONFAULT]
+
+ mov r5, #1
+ ldrex r4, [r0]
+ cmp r4, r1
+ strexeq r5, r3, [r0]
+ str r4, [r2]
+ mov r0, #0
+ str r0, [r6, #PCB_ONFAULT]
+ mov r0, r5
+1:
+ ldmfd sp!, {r4, r5, r6}
+ RET
+EEND(casueword32)
+END(casueword)
+
+/*
+ * Handle faults from casuword. Clean up and return -1.
+ */
+
+.Lcasuwordfault:
+ mov r0, #0x00000000
+ str r0, [r6, #PCB_ONFAULT]
+ mvn r0, #0
+ ldmfd sp!, {r4, r5, r6}
+ RET
+
+/*
+ * fueword(caddr_t uaddr, long *val);
+ * Fetch an int from the user's address space.
+ */
+
+ENTRY(fueword)
+EENTRY_NP(fueword32)
+ ldr r3, =(VM_MAXUSER_ADDRESS-3)
+ cmp r0, r3
+ mvncs r0, #0
+ RETc(cs)
+
+ GET_PCB(r2)
+ ldr r2, [r2]
+
+#ifdef DIAGNOSTIC
+ teq r2, #0x00000000
+ beq .Lfusupcbfault
+#endif
+
+ adr r3, .Lfusufault
+ str r3, [r2, #PCB_ONFAULT]
+
+ ldrt r3, [r0]
+ str r3, [r1]
+
+ mov r0, #0x00000000
+ str r0, [r2, #PCB_ONFAULT]
+ RET
+EEND(fueword32)
+END(fueword)
+
+/*
+ * fusword(caddr_t uaddr);
+ * Fetch a short from the user's address space.
+ */
+
+ENTRY(fusword)
+ ldr r3, =(VM_MAXUSER_ADDRESS-1)
+ cmp r0, r3
+ mvncs r0, #0
+ RETc(cs)
+
+ GET_PCB(r2)
+ ldr r2, [r2]
+
+#ifdef DIAGNOSTIC
+ teq r2, #0x00000000
+ beq .Lfusupcbfault
+#endif
+
+ adr r1, .Lfusufault
+ str r1, [r2, #PCB_ONFAULT]
+
+ ldrbt r3, [r0], #1
+ ldrbt ip, [r0]
+ orr r0, r3, ip, asl #8
+ mov r1, #0x00000000
+ str r1, [r2, #PCB_ONFAULT]
+ RET
+END(fusword)
+
+/*
+ * fubyte(caddr_t uaddr);
+ * Fetch a byte from the user's address space.
+ */
+
+ENTRY(fubyte)
+ ldr r3, =VM_MAXUSER_ADDRESS
+ cmp r0, r3
+ mvncs r0, #0
+ RETc(cs)
+
+ GET_PCB(r2)
+ ldr r2, [r2]
+
+#ifdef DIAGNOSTIC
+ teq r2, #0x00000000
+ beq .Lfusupcbfault
+#endif
+
+ adr r1, .Lfusufault
+ str r1, [r2, #PCB_ONFAULT]
+
+ ldrbt r3, [r0]
+
+ mov r1, #0x00000000
+ str r1, [r2, #PCB_ONFAULT]
+ mov r0, r3
+ RET
+END(fubyte)
+
+/*
+ * Handle faults from [fs]u*(). Clean up and return -1.
+ */
+
+.Lfusufault:
+ mov r0, #0x00000000
+ str r0, [r2, #PCB_ONFAULT]
+ mvn r0, #0x00000000
+ RET
+
+#ifdef DIAGNOSTIC
+/*
+ * Handle earlier faults from [fs]u*(), due to no pcb
+ */
+
+.Lfusupcbfault:
+ mov r1, r0
+ adr r0, fusupcbfaulttext
+ b _C_LABEL(panic)
+
+fusupcbfaulttext:
+ .asciz "Yikes - no valid PCB during fusuxxx() addr=%08x\n"
+ .align 2
+#endif
+
+/*
+ * suword(caddr_t uaddr, int x);
+ * Store an int in the user's address space.
+ */
+
+ENTRY(suword)
+EENTRY_NP(suword32)
+ ldr r3, =(VM_MAXUSER_ADDRESS-3)
+ cmp r0, r3
+ mvncs r0, #0
+ RETc(cs)
+
+ GET_PCB(r2)
+ ldr r2, [r2]
+
+#ifdef DIAGNOSTIC
+ teq r2, #0x00000000
+ beq .Lfusupcbfault
+#endif
+
+ adr r3, .Lfusufault
+ str r3, [r2, #PCB_ONFAULT]
+
+ strt r1, [r0]
+
+ mov r0, #0x00000000
+ str r0, [r2, #PCB_ONFAULT]
+ RET
+EEND(suword32)
+END(suword)
+
+/*
+ * susword(caddr_t uaddr, short x);
+ * Store a short in the user's address space.
+ */
+
+ENTRY(susword)
+ ldr r3, =(VM_MAXUSER_ADDRESS-1)
+ cmp r0, r3
+ mvncs r0, #0
+ RETc(cs)
+
+ GET_PCB(r2)
+ ldr r2, [r2]
+
+#ifdef DIAGNOSTIC
+ teq r2, #0x00000000
+ beq .Lfusupcbfault
+#endif
+
+ adr r3, .Lfusufault
+ str r3, [r2, #PCB_ONFAULT]
+
+ strbt r1, [r0], #1
+ mov r1, r1, lsr #8
+ strbt r1, [r0]
+
+ mov r0, #0x00000000
+ str r0, [r2, #PCB_ONFAULT]
+ RET
+END(susword)
+
+/*
+ * subyte(caddr_t uaddr, char x);
+ * Store a byte in the user's address space.
+ */
+
+ENTRY(subyte)
+ ldr r3, =VM_MAXUSER_ADDRESS
+ cmp r0, r3
+ mvncs r0, #0
+ RETc(cs)
+
+ GET_PCB(r2)
+ ldr r2, [r2]
+
+
+#ifdef DIAGNOSTIC
+ teq r2, #0x00000000
+ beq .Lfusupcbfault
+#endif
+
+ adr r3, .Lfusufault
+ str r3, [r2, #PCB_ONFAULT]
+
+ strbt r1, [r0]
+ mov r0, #0x00000000
+ str r0, [r2, #PCB_ONFAULT]
+ RET
+END(subyte)
diff --git a/sys/arm/arm/gdb_machdep.c b/sys/arm/arm/gdb_machdep.c
new file mode 100644
index 000000000000..d42892972556
--- /dev/null
+++ b/sys/arm/arm/gdb_machdep.c
@@ -0,0 +1,130 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2006 Olivier Houchard
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kdb.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/signal.h>
+
+#include <machine/gdb_machdep.h>
+#include <machine/db_machdep.h>
+#include <machine/vmparam.h>
+#include <machine/pcb.h>
+#include <machine/trap.h>
+#include <machine/frame.h>
+#include <machine/endian.h>
+
+#include <gdb/gdb.h>
+
+static register_t stacktest;
+
+void *
+gdb_cpu_getreg(int regnum, size_t *regsz)
+{
+
+ *regsz = gdb_cpu_regsz(regnum);
+
+ if (kdb_thread == curthread) {
+ if (regnum < 13)
+ return (&kdb_frame->tf_r0 + regnum);
+ if (regnum == 13)
+ return (&kdb_frame->tf_svc_sp);
+ if (regnum == 14)
+ return (&kdb_frame->tf_svc_lr);
+ if (regnum == 15)
+ return (&kdb_frame->tf_pc);
+ if (regnum == 25)
+ return (&kdb_frame->tf_spsr);
+ }
+
+ switch (regnum) {
+ case 4: return (&kdb_thrctx->pcb_regs.sf_r4);
+ case 5: return (&kdb_thrctx->pcb_regs.sf_r5);
+ case 6: return (&kdb_thrctx->pcb_regs.sf_r6);
+ case 7: return (&kdb_thrctx->pcb_regs.sf_r7);
+ case 8: return (&kdb_thrctx->pcb_regs.sf_r8);
+ case 9: return (&kdb_thrctx->pcb_regs.sf_r9);
+ case 10: return (&kdb_thrctx->pcb_regs.sf_r10);
+ case 11: return (&kdb_thrctx->pcb_regs.sf_r11);
+ case 12: return (&kdb_thrctx->pcb_regs.sf_r12);
+ case 13: stacktest = kdb_thrctx->pcb_regs.sf_sp + 5 * 4;
+ return (&stacktest);
+ case 15:
+ /*
+ * On context switch, the PC is not put in the PCB, but
+ * we can retrieve it from the stack.
+ */
+ if (kdb_thrctx->pcb_regs.sf_sp > KERNBASE) {
+ kdb_thrctx->pcb_regs.sf_pc = *(register_t *)
+ (kdb_thrctx->pcb_regs.sf_sp + 4 * 4);
+ return (&kdb_thrctx->pcb_regs.sf_pc);
+ }
+ }
+
+ return (NULL);
+}
+
+void
+gdb_cpu_setreg(int regnum, void *val)
+{
+ if (kdb_thread != curthread)
+ return;
+
+ switch (regnum) {
+ case GDB_REG_PC:
+ kdb_frame->tf_pc = *(register_t *)val;
+ break;
+ case GDB_REG_SP:
+ kdb_frame->tf_svc_sp = *(register_t *)val;
+ break;
+ case GDB_REG_LR:
+ kdb_frame->tf_svc_lr = *(register_t *)val;
+ break;
+ default:
+ /* Write to the general purpose registers r0-r12. */
+ if (regnum >= 0 && regnum <= 12) {
+ *(&kdb_frame->tf_r0 + regnum) = *(register_t *)val;
+ }
+ break;
+ }
+}
+
+int
+gdb_cpu_signal(int type, int code)
+{
+
+ switch (type) {
+ case T_BREAKPOINT: return (SIGTRAP);
+ }
+ return (SIGEMT);
+}
diff --git a/sys/arm/arm/genassym.c b/sys/arm/arm/genassym.c
new file mode 100644
index 000000000000..e90bbff6549a
--- /dev/null
+++ b/sys/arm/arm/genassym.c
@@ -0,0 +1,144 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2004 Olivier Houchard
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#include <sys/param.h>
+#include <sys/cpuset.h>
+#include <sys/systm.h>
+#include <sys/assym.h>
+#include <sys/pcpu.h>
+#include <sys/proc.h>
+#include <sys/mbuf.h>
+#include <sys/vmmeter.h>
+#include <sys/bus.h>
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/pmap.h>
+#include <vm/vm_map.h>
+
+#include <machine/armreg.h>
+#include <machine/frame.h>
+#include <machine/pcb.h>
+#include <machine/cpu.h>
+#include <machine/proc.h>
+#include <machine/cpufunc.h>
+#include <machine/cpuinfo.h>
+#include <machine/intr.h>
+#include <machine/sysarch.h>
+#include <machine/vmparam.h> /* For KERNVIRTADDR */
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/ip_var.h>
+
+ASSYM(KERNBASE, KERNBASE);
+ASSYM(KERNVIRTADDR, KERNVIRTADDR);
+ASSYM(CPU_ASID_KERNEL,CPU_ASID_KERNEL);
+ASSYM(PCB_ONFAULT, offsetof(struct pcb, pcb_onfault));
+ASSYM(PCB_PAGEDIR, offsetof(struct pcb, pcb_pagedir));
+ASSYM(PCB_R4, offsetof(struct pcb, pcb_regs.sf_r4));
+ASSYM(PCB_R5, offsetof(struct pcb, pcb_regs.sf_r5));
+ASSYM(PCB_R6, offsetof(struct pcb, pcb_regs.sf_r6));
+ASSYM(PCB_R7, offsetof(struct pcb, pcb_regs.sf_r7));
+ASSYM(PCB_R8, offsetof(struct pcb, pcb_regs.sf_r8));
+ASSYM(PCB_R9, offsetof(struct pcb, pcb_regs.sf_r9));
+ASSYM(PCB_R10, offsetof(struct pcb, pcb_regs.sf_r10));
+ASSYM(PCB_R11, offsetof(struct pcb, pcb_regs.sf_r11));
+ASSYM(PCB_R12, offsetof(struct pcb, pcb_regs.sf_r12));
+ASSYM(PCB_SP, offsetof(struct pcb, pcb_regs.sf_sp));
+ASSYM(PCB_LR, offsetof(struct pcb, pcb_regs.sf_lr));
+ASSYM(PCB_PC, offsetof(struct pcb, pcb_regs.sf_pc));
+ASSYM(PCB_TPIDRURW, offsetof(struct pcb, pcb_regs.sf_tpidrurw));
+
+ASSYM(PC_CURPCB, offsetof(struct pcpu, pc_curpcb));
+ASSYM(PC_CURTHREAD, offsetof(struct pcpu, pc_curthread));
+ASSYM(M_LEN, offsetof(struct mbuf, m_len));
+ASSYM(M_DATA, offsetof(struct mbuf, m_data));
+ASSYM(M_NEXT, offsetof(struct mbuf, m_next));
+ASSYM(IP_SRC, offsetof(struct ip, ip_src));
+ASSYM(IP_DST, offsetof(struct ip, ip_dst));
+
+ASSYM(TD_PCB, offsetof(struct thread, td_pcb));
+ASSYM(TD_FLAGS, offsetof(struct thread, td_flags));
+ASSYM(TD_PROC, offsetof(struct thread, td_proc));
+ASSYM(TD_MD, offsetof(struct thread, td_md));
+ASSYM(TD_LOCK, offsetof(struct thread, td_lock));
+
+ASSYM(TF_SPSR, offsetof(struct trapframe, tf_spsr));
+ASSYM(TF_R0, offsetof(struct trapframe, tf_r0));
+ASSYM(TF_R1, offsetof(struct trapframe, tf_r1));
+ASSYM(TF_PC, offsetof(struct trapframe, tf_pc));
+ASSYM(P_PID, offsetof(struct proc, p_pid));
+ASSYM(P_FLAG, offsetof(struct proc, p_flag));
+
+ASSYM(SIGF_UC, offsetof(struct sigframe, sf_uc));
+
+#ifdef VFP
+ASSYM(PCB_VFPSTATE, offsetof(struct pcb, pcb_vfpstate));
+#endif
+
+ASSYM(PC_CURPMAP, offsetof(struct pcpu, pc_curpmap));
+ASSYM(PC_BP_HARDEN_KIND, offsetof(struct pcpu, pc_bp_harden_kind));
+ASSYM(PCPU_BP_HARDEN_KIND_NONE, PCPU_BP_HARDEN_KIND_NONE);
+ASSYM(PCPU_BP_HARDEN_KIND_BPIALL, PCPU_BP_HARDEN_KIND_BPIALL);
+ASSYM(PCPU_BP_HARDEN_KIND_ICIALLU, PCPU_BP_HARDEN_KIND_ICIALLU);
+
+ASSYM(PAGE_SIZE, PAGE_SIZE);
+#ifdef PMAP_INCLUDE_PTE_SYNC
+ASSYM(PMAP_INCLUDE_PTE_SYNC, 1);
+#endif
+ASSYM(TDF_ASTPENDING, TDF_ASTPENDING);
+ASSYM(TDF_NEEDRESCHED, TDF_NEEDRESCHED);
+
+ASSYM(MAXCOMLEN, MAXCOMLEN);
+ASSYM(MAXCPU, MAXCPU);
+ASSYM(_NCPUWORDS, _NCPUWORDS);
+ASSYM(PCPU_SIZE, sizeof(struct pcpu));
+ASSYM(P_VMSPACE, offsetof(struct proc, p_vmspace));
+ASSYM(VM_PMAP, offsetof(struct vmspace, vm_pmap));
+ASSYM(PM_ACTIVE, offsetof(struct pmap, pm_active));
+ASSYM(PC_CPUID, offsetof(struct pcpu, pc_cpuid));
+ASSYM(VM_MAXUSER_ADDRESS, VM_MAXUSER_ADDRESS);
+
+ASSYM(DCACHE_LINE_SIZE, offsetof(struct cpuinfo, dcache_line_size));
+ASSYM(DCACHE_LINE_MASK, offsetof(struct cpuinfo, dcache_line_mask));
+ASSYM(ICACHE_LINE_SIZE, offsetof(struct cpuinfo, icache_line_size));
+ASSYM(ICACHE_LINE_MASK, offsetof(struct cpuinfo, icache_line_mask));
+
+/*
+ * Emit the LOCORE_MAP_MB option as a #define only if the option was set.
+ */
+#include "opt_locore.h"
+
+#ifdef LOCORE_MAP_MB
+ASSYM(LOCORE_MAP_MB, LOCORE_MAP_MB);
+#endif
diff --git a/sys/arm/arm/generic_timer.c b/sys/arm/arm/generic_timer.c
new file mode 100644
index 000000000000..e3c54c237967
--- /dev/null
+++ b/sys/arm/arm/generic_timer.c
@@ -0,0 +1,599 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 2011 The FreeBSD Foundation
+ * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Based on mpcore_timer.c developed by Ben Gray <ben.r.gray@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/**
+ * Cortex-A7, Cortex-A15, ARMv8 and later Generic Timer
+ */
+
+#include "opt_acpi.h"
+#include "opt_platform.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/smp.h>
+#include <sys/vdso.h>
+#include <sys/watchdog.h>
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+#include <machine/md_var.h>
+
+#if defined(__arm__)
+#include <machine/machdep.h> /* For arm_set_delay */
+#endif
+
+#ifdef FDT
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#endif
+
+#ifdef DEV_ACPI
+#include <contrib/dev/acpica/include/acpi.h>
+#include <dev/acpica/acpivar.h>
+#endif
+
+#define GT_CTRL_ENABLE (1 << 0)
+#define GT_CTRL_INT_MASK (1 << 1)
+#define GT_CTRL_INT_STAT (1 << 2)
+#define GT_REG_CTRL 0
+#define GT_REG_TVAL 1
+
+#define GT_CNTKCTL_PL0PTEN (1 << 9) /* PL0 Physical timer reg access */
+#define GT_CNTKCTL_PL0VTEN (1 << 8) /* PL0 Virtual timer reg access */
+#define GT_CNTKCTL_EVNTI (0xf << 4) /* Virtual counter event bits */
+#define GT_CNTKCTL_EVNTDIR (1 << 3) /* Virtual counter event transition */
+#define GT_CNTKCTL_EVNTEN (1 << 2) /* Enables virtual counter events */
+#define GT_CNTKCTL_PL0VCTEN (1 << 1) /* PL0 CNTVCT and CNTFRQ access */
+#define GT_CNTKCTL_PL0PCTEN (1 << 0) /* PL0 CNTPCT and CNTFRQ access */
+
+struct arm_tmr_softc {
+ struct resource *res[4];
+ void *ihl[4];
+ uint64_t (*get_cntxct)(bool);
+ uint32_t clkfreq;
+ struct eventtimer et;
+ bool physical;
+};
+
+static struct arm_tmr_softc *arm_tmr_sc = NULL;
+
+static struct resource_spec timer_spec[] = {
+ { SYS_RES_IRQ, 0, RF_ACTIVE }, /* Secure */
+ { SYS_RES_IRQ, 1, RF_ACTIVE }, /* Non-secure */
+ { SYS_RES_IRQ, 2, RF_ACTIVE | RF_OPTIONAL }, /* Virt */
+ { SYS_RES_IRQ, 3, RF_ACTIVE | RF_OPTIONAL }, /* Hyp */
+ { -1, 0 }
+};
+
+static uint32_t arm_tmr_fill_vdso_timehands(struct vdso_timehands *vdso_th,
+ struct timecounter *tc);
+static void arm_tmr_do_delay(int usec, void *);
+
+static timecounter_get_t arm_tmr_get_timecount;
+
+static struct timecounter arm_tmr_timecount = {
+ .tc_name = "ARM MPCore Timecounter",
+ .tc_get_timecount = arm_tmr_get_timecount,
+ .tc_poll_pps = NULL,
+ .tc_counter_mask = ~0u,
+ .tc_frequency = 0,
+ .tc_quality = 1000,
+ .tc_fill_vdso_timehands = arm_tmr_fill_vdso_timehands,
+};
+
+#ifdef __arm__
+#define get_el0(x) cp15_## x ##_get()
+#define get_el1(x) cp15_## x ##_get()
+#define set_el0(x, val) cp15_## x ##_set(val)
+#define set_el1(x, val) cp15_## x ##_set(val)
+#else /* __aarch64__ */
+#define get_el0(x) READ_SPECIALREG(x ##_el0)
+#define get_el1(x) READ_SPECIALREG(x ##_el1)
+#define set_el0(x, val) WRITE_SPECIALREG(x ##_el0, val)
+#define set_el1(x, val) WRITE_SPECIALREG(x ##_el1, val)
+#endif
+
+static int
+get_freq(void)
+{
+ return (get_el0(cntfrq));
+}
+
+static uint64_t
+get_cntxct_a64_unstable(bool physical)
+{
+ uint64_t val
+;
+ isb();
+ if (physical) {
+ do {
+ val = get_el0(cntpct);
+ }
+ while (((val + 1) & 0x7FF) <= 1);
+ }
+ else {
+ do {
+ val = get_el0(cntvct);
+ }
+ while (((val + 1) & 0x7FF) <= 1);
+ }
+
+ return (val);
+}
+
+static uint64_t
+get_cntxct(bool physical)
+{
+ uint64_t val;
+
+ isb();
+ if (physical)
+ val = get_el0(cntpct);
+ else
+ val = get_el0(cntvct);
+
+ return (val);
+}
+
+static int
+set_ctrl(uint32_t val, bool physical)
+{
+
+ if (physical)
+ set_el0(cntp_ctl, val);
+ else
+ set_el0(cntv_ctl, val);
+ isb();
+
+ return (0);
+}
+
+static int
+set_tval(uint32_t val, bool physical)
+{
+
+ if (physical)
+ set_el0(cntp_tval, val);
+ else
+ set_el0(cntv_tval, val);
+ isb();
+
+ return (0);
+}
+
+static int
+get_ctrl(bool physical)
+{
+ uint32_t val;
+
+ if (physical)
+ val = get_el0(cntp_ctl);
+ else
+ val = get_el0(cntv_ctl);
+
+ return (val);
+}
+
+static void
+setup_user_access(void *arg __unused)
+{
+ uint32_t cntkctl;
+
+ cntkctl = get_el1(cntkctl);
+ cntkctl &= ~(GT_CNTKCTL_PL0PTEN | GT_CNTKCTL_PL0VTEN |
+ GT_CNTKCTL_EVNTEN);
+ if (arm_tmr_sc->physical) {
+ cntkctl |= GT_CNTKCTL_PL0PCTEN;
+ cntkctl &= ~GT_CNTKCTL_PL0VCTEN;
+ } else {
+ cntkctl |= GT_CNTKCTL_PL0VCTEN;
+ cntkctl &= ~GT_CNTKCTL_PL0PCTEN;
+ }
+ set_el1(cntkctl, cntkctl);
+ isb();
+}
+
+static void
+tmr_setup_user_access(void *arg __unused)
+{
+
+ if (arm_tmr_sc != NULL)
+ smp_rendezvous(NULL, setup_user_access, NULL, NULL);
+}
+SYSINIT(tmr_ua, SI_SUB_SMP, SI_ORDER_ANY, tmr_setup_user_access, NULL);
+
+static unsigned
+arm_tmr_get_timecount(struct timecounter *tc)
+{
+
+ return (arm_tmr_sc->get_cntxct(arm_tmr_sc->physical));
+}
+
+static int
+arm_tmr_start(struct eventtimer *et, sbintime_t first,
+ sbintime_t period __unused)
+{
+ struct arm_tmr_softc *sc;
+ int counts, ctrl;
+
+ sc = (struct arm_tmr_softc *)et->et_priv;
+
+ if (first != 0) {
+ counts = ((uint32_t)et->et_frequency * first) >> 32;
+ ctrl = get_ctrl(sc->physical);
+ ctrl &= ~GT_CTRL_INT_MASK;
+ ctrl |= GT_CTRL_ENABLE;
+ set_tval(counts, sc->physical);
+ set_ctrl(ctrl, sc->physical);
+ return (0);
+ }
+
+ return (EINVAL);
+
+}
+
+static void
+arm_tmr_disable(bool physical)
+{
+ int ctrl;
+
+ ctrl = get_ctrl(physical);
+ ctrl &= ~GT_CTRL_ENABLE;
+ set_ctrl(ctrl, physical);
+}
+
+static int
+arm_tmr_stop(struct eventtimer *et)
+{
+ struct arm_tmr_softc *sc;
+
+ sc = (struct arm_tmr_softc *)et->et_priv;
+ arm_tmr_disable(sc->physical);
+
+ return (0);
+}
+
+static int
+arm_tmr_intr(void *arg)
+{
+ struct arm_tmr_softc *sc;
+ int ctrl;
+
+ sc = (struct arm_tmr_softc *)arg;
+ ctrl = get_ctrl(sc->physical);
+ if (ctrl & GT_CTRL_INT_STAT) {
+ ctrl |= GT_CTRL_INT_MASK;
+ set_ctrl(ctrl, sc->physical);
+ }
+
+ if (sc->et.et_active)
+ sc->et.et_event_cb(&sc->et, sc->et.et_arg);
+
+ return (FILTER_HANDLED);
+}
+
+#ifdef FDT
+static int
+arm_tmr_fdt_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_is_compatible(dev, "arm,armv8-timer")) {
+ device_set_desc(dev, "ARMv8 Generic Timer");
+ return (BUS_PROBE_DEFAULT);
+ } else if (ofw_bus_is_compatible(dev, "arm,armv7-timer")) {
+ device_set_desc(dev, "ARMv7 Generic Timer");
+ return (BUS_PROBE_DEFAULT);
+ }
+
+ return (ENXIO);
+}
+#endif
+
+#ifdef DEV_ACPI
+static void
+arm_tmr_acpi_add_irq(device_t parent, device_t dev, int rid, u_int irq)
+{
+
+ BUS_SET_RESOURCE(parent, dev, SYS_RES_IRQ, rid, irq, 1);
+}
+
+static void
+arm_tmr_acpi_identify(driver_t *driver, device_t parent)
+{
+ ACPI_TABLE_GTDT *gtdt;
+ vm_paddr_t physaddr;
+ device_t dev;
+
+ physaddr = acpi_find_table(ACPI_SIG_GTDT);
+ if (physaddr == 0)
+ return;
+
+ gtdt = acpi_map_table(physaddr, ACPI_SIG_GTDT);
+ if (gtdt == NULL) {
+ device_printf(parent, "gic: Unable to map the GTDT\n");
+ return;
+ }
+
+ dev = BUS_ADD_CHILD(parent, BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE,
+ "generic_timer", -1);
+ if (dev == NULL) {
+ device_printf(parent, "add gic child failed\n");
+ goto out;
+ }
+
+ arm_tmr_acpi_add_irq(parent, dev, 0, gtdt->SecureEl1Interrupt);
+ arm_tmr_acpi_add_irq(parent, dev, 1, gtdt->NonSecureEl1Interrupt);
+ arm_tmr_acpi_add_irq(parent, dev, 2, gtdt->VirtualTimerInterrupt);
+
+out:
+ acpi_unmap_table(gtdt);
+}
+
+static int
+arm_tmr_acpi_probe(device_t dev)
+{
+
+ device_set_desc(dev, "ARM Generic Timer");
+ return (BUS_PROBE_NOWILDCARD);
+}
+#endif
+
+static int
+arm_tmr_attach(device_t dev)
+{
+ struct arm_tmr_softc *sc;
+#ifdef FDT
+ phandle_t node;
+ pcell_t clock;
+#endif
+ int error;
+ int i, first_timer, last_timer;
+
+ sc = device_get_softc(dev);
+ if (arm_tmr_sc)
+ return (ENXIO);
+
+ sc->get_cntxct = &get_cntxct;
+#ifdef FDT
+ /* Get the base clock frequency */
+ node = ofw_bus_get_node(dev);
+ if (node > 0) {
+ error = OF_getencprop(node, "clock-frequency", &clock,
+ sizeof(clock));
+ if (error > 0)
+ sc->clkfreq = clock;
+
+ if (OF_hasprop(node, "allwinner,sun50i-a64-unstable-timer")) {
+ sc->get_cntxct = &get_cntxct_a64_unstable;
+ if (bootverbose)
+ device_printf(dev,
+ "Enabling allwinner unstable timer workaround\n");
+ }
+ }
+#endif
+
+ if (sc->clkfreq == 0) {
+ /* Try to get clock frequency from timer */
+ sc->clkfreq = get_freq();
+ }
+
+ if (sc->clkfreq == 0) {
+ device_printf(dev, "No clock frequency specified\n");
+ return (ENXIO);
+ }
+
+ if (bus_alloc_resources(dev, timer_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+#ifdef __aarch64__
+ /* Use the virtual timer if we have one. */
+ if (sc->res[2] != NULL) {
+ sc->physical = false;
+ first_timer = 2;
+ last_timer = 2;
+ } else
+#endif
+ /* Otherwise set up the secure and non-secure physical timers. */
+ {
+ sc->physical = true;
+ first_timer = 0;
+ last_timer = 1;
+ }
+
+ arm_tmr_sc = sc;
+
+ /* Setup secure, non-secure and virtual IRQs handler */
+ for (i = first_timer; i <= last_timer; i++) {
+ /* If we do not have the interrupt, skip it. */
+ if (sc->res[i] == NULL)
+ continue;
+ error = bus_setup_intr(dev, sc->res[i], INTR_TYPE_CLK,
+ arm_tmr_intr, NULL, sc, &sc->ihl[i]);
+ if (error) {
+ device_printf(dev, "Unable to alloc int resource.\n");
+ return (ENXIO);
+ }
+ }
+
+ /* Disable the virtual timer until we are ready */
+ if (sc->res[2] != NULL)
+ arm_tmr_disable(false);
+ /* And the physical */
+ if (sc->physical)
+ arm_tmr_disable(true);
+
+ arm_tmr_timecount.tc_frequency = sc->clkfreq;
+ tc_init(&arm_tmr_timecount);
+
+ sc->et.et_name = "ARM MPCore Eventtimer";
+ sc->et.et_flags = ET_FLAGS_ONESHOT | ET_FLAGS_PERCPU;
+ sc->et.et_quality = 1000;
+
+ sc->et.et_frequency = sc->clkfreq;
+ sc->et.et_min_period = (0x00000010LLU << 32) / sc->et.et_frequency;
+ sc->et.et_max_period = (0xfffffffeLLU << 32) / sc->et.et_frequency;
+ sc->et.et_start = arm_tmr_start;
+ sc->et.et_stop = arm_tmr_stop;
+ sc->et.et_priv = sc;
+ et_register(&sc->et);
+
+#if defined(__arm__)
+ arm_set_delay(arm_tmr_do_delay, sc);
+#endif
+
+ return (0);
+}
+
+#ifdef FDT
+static device_method_t arm_tmr_fdt_methods[] = {
+ DEVMETHOD(device_probe, arm_tmr_fdt_probe),
+ DEVMETHOD(device_attach, arm_tmr_attach),
+ { 0, 0 }
+};
+
+static driver_t arm_tmr_fdt_driver = {
+ "generic_timer",
+ arm_tmr_fdt_methods,
+ sizeof(struct arm_tmr_softc),
+};
+
+static devclass_t arm_tmr_fdt_devclass;
+
+EARLY_DRIVER_MODULE(timer, simplebus, arm_tmr_fdt_driver, arm_tmr_fdt_devclass,
+ 0, 0, BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE);
+EARLY_DRIVER_MODULE(timer, ofwbus, arm_tmr_fdt_driver, arm_tmr_fdt_devclass,
+ 0, 0, BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE);
+#endif
+
+#ifdef DEV_ACPI
+static device_method_t arm_tmr_acpi_methods[] = {
+ DEVMETHOD(device_identify, arm_tmr_acpi_identify),
+ DEVMETHOD(device_probe, arm_tmr_acpi_probe),
+ DEVMETHOD(device_attach, arm_tmr_attach),
+ { 0, 0 }
+};
+
+static driver_t arm_tmr_acpi_driver = {
+ "generic_timer",
+ arm_tmr_acpi_methods,
+ sizeof(struct arm_tmr_softc),
+};
+
+static devclass_t arm_tmr_acpi_devclass;
+
+EARLY_DRIVER_MODULE(timer, acpi, arm_tmr_acpi_driver, arm_tmr_acpi_devclass,
+ 0, 0, BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE);
+#endif
+
+static void
+arm_tmr_do_delay(int usec, void *arg)
+{
+ struct arm_tmr_softc *sc = arg;
+ int32_t counts, counts_per_usec;
+ uint32_t first, last;
+
+ /* Get the number of times to count */
+ counts_per_usec = ((arm_tmr_timecount.tc_frequency / 1000000) + 1);
+
+ /*
+ * Clamp the timeout at a maximum value (about 32 seconds with
+ * a 66MHz clock). *Nobody* should be delay()ing for anywhere
+ * near that length of time and if they are, they should be hung
+ * out to dry.
+ */
+ if (usec >= (0x80000000U / counts_per_usec))
+ counts = (0x80000000U / counts_per_usec) - 1;
+ else
+ counts = usec * counts_per_usec;
+
+ first = sc->get_cntxct(sc->physical);
+
+ while (counts > 0) {
+ last = sc->get_cntxct(sc->physical);
+ counts -= (int32_t)(last - first);
+ first = last;
+ }
+}
+
+#if defined(__aarch64__)
+void
+DELAY(int usec)
+{
+ int32_t counts;
+
+ TSENTER();
+ /*
+ * Check the timers are setup, if not just
+ * use a for loop for the meantime
+ */
+ if (arm_tmr_sc == NULL) {
+ for (; usec > 0; usec--)
+ for (counts = 200; counts > 0; counts--)
+ /*
+ * Prevent the compiler from optimizing
+ * out the loop
+ */
+ cpufunc_nullop();
+ } else
+ arm_tmr_do_delay(usec, arm_tmr_sc);
+ TSEXIT();
+}
+#endif
+
+static uint32_t
+arm_tmr_fill_vdso_timehands(struct vdso_timehands *vdso_th,
+ struct timecounter *tc)
+{
+
+ vdso_th->th_algo = VDSO_TH_ALGO_ARM_GENTIM;
+ vdso_th->th_physical = arm_tmr_sc->physical;
+ bzero(vdso_th->th_res, sizeof(vdso_th->th_res));
+ return (1);
+}
diff --git a/sys/arm/arm/gic.c b/sys/arm/arm/gic.c
new file mode 100644
index 000000000000..1851e69644ed
--- /dev/null
+++ b/sys/arm/arm/gic.c
@@ -0,0 +1,1250 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 2011 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * Developed by Damjan Marion <damjan.marion@gmail.com>
+ *
+ * Based on OMAP4 GIC code by Ben Gray
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_acpi.h"
+#include "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/ktr.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/pcpu.h>
+#include <sys/proc.h>
+#include <sys/cpuset.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/smp.h>
+#include <sys/sched.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+#include <machine/smp.h>
+
+#ifdef FDT
+#include <dev/fdt/fdt_intr.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#endif
+
+#ifdef DEV_ACPI
+#include <contrib/dev/acpica/include/acpi.h>
+#include <dev/acpica/acpivar.h>
+#endif
+
+#include <arm/arm/gic.h>
+#include <arm/arm/gic_common.h>
+
+#include "pic_if.h"
+#include "msi_if.h"
+
+/* We are using GICv2 register naming */
+
+/* Distributor Registers */
+
+/* CPU Registers */
+#define GICC_CTLR 0x0000 /* v1 ICCICR */
+#define GICC_PMR 0x0004 /* v1 ICCPMR */
+#define GICC_BPR 0x0008 /* v1 ICCBPR */
+#define GICC_IAR 0x000C /* v1 ICCIAR */
+#define GICC_EOIR 0x0010 /* v1 ICCEOIR */
+#define GICC_RPR 0x0014 /* v1 ICCRPR */
+#define GICC_HPPIR 0x0018 /* v1 ICCHPIR */
+#define GICC_ABPR 0x001C /* v1 ICCABPR */
+#define GICC_IIDR 0x00FC /* v1 ICCIIDR*/
+
+/* TYPER Registers */
+#define GICD_TYPER_SECURITYEXT 0x400
+#define GIC_SUPPORT_SECEXT(_sc) \
+ ((_sc->typer & GICD_TYPER_SECURITYEXT) == GICD_TYPER_SECURITYEXT)
+
+#ifndef GIC_DEFAULT_ICFGR_INIT
+#define GIC_DEFAULT_ICFGR_INIT 0x00000000
+#endif
+
+struct gic_irqsrc {
+ struct intr_irqsrc gi_isrc;
+ uint32_t gi_irq;
+ enum intr_polarity gi_pol;
+ enum intr_trigger gi_trig;
+#define GI_FLAG_EARLY_EOI (1 << 0)
+#define GI_FLAG_MSI (1 << 1) /* This interrupt source should only */
+ /* be used for MSI/MSI-X interrupts */
+#define GI_FLAG_MSI_USED (1 << 2) /* This irq is already allocated */
+ /* for a MSI/MSI-X interrupt */
+ u_int gi_flags;
+};
+
+static u_int gic_irq_cpu;
+static int arm_gic_bind_intr(device_t dev, struct intr_irqsrc *isrc);
+
+#ifdef SMP
+static u_int sgi_to_ipi[GIC_LAST_SGI - GIC_FIRST_SGI + 1];
+static u_int sgi_first_unused = GIC_FIRST_SGI;
+#endif
+
+#define GIC_INTR_ISRC(sc, irq) (&sc->gic_irqs[irq].gi_isrc)
+
+static struct resource_spec arm_gic_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Distributor registers */
+ { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* CPU Interrupt Intf. registers */
+ { SYS_RES_IRQ, 0, RF_ACTIVE | RF_OPTIONAL }, /* Parent interrupt */
+ { -1, 0 }
+};
+
+#if defined(__arm__) && defined(INVARIANTS)
+static int gic_debug_spurious = 1;
+#else
+static int gic_debug_spurious = 0;
+#endif
+TUNABLE_INT("hw.gic.debug_spurious", &gic_debug_spurious);
+
+static u_int arm_gic_map[MAXCPU];
+
+static struct arm_gic_softc *gic_sc = NULL;
+
+/* CPU Interface */
+#define gic_c_read_4(_sc, _reg) \
+ bus_read_4((_sc)->gic_res[GIC_RES_CPU], (_reg))
+#define gic_c_write_4(_sc, _reg, _val) \
+ bus_write_4((_sc)->gic_res[GIC_RES_CPU], (_reg), (_val))
+/* Distributor Interface */
+#define gic_d_read_4(_sc, _reg) \
+ bus_read_4((_sc)->gic_res[GIC_RES_DIST], (_reg))
+#define gic_d_write_1(_sc, _reg, _val) \
+ bus_write_1((_sc)->gic_res[GIC_RES_DIST], (_reg), (_val))
+#define gic_d_write_4(_sc, _reg, _val) \
+ bus_write_4((_sc)->gic_res[GIC_RES_DIST], (_reg), (_val))
+
+static inline void
+gic_irq_unmask(struct arm_gic_softc *sc, u_int irq)
+{
+
+ gic_d_write_4(sc, GICD_ISENABLER(irq), GICD_I_MASK(irq));
+}
+
+static inline void
+gic_irq_mask(struct arm_gic_softc *sc, u_int irq)
+{
+
+ gic_d_write_4(sc, GICD_ICENABLER(irq), GICD_I_MASK(irq));
+}
+
+static uint8_t
+gic_cpu_mask(struct arm_gic_softc *sc)
+{
+ uint32_t mask;
+ int i;
+
+ /* Read the current cpuid mask by reading ITARGETSR{0..7} */
+ for (i = 0; i < 8; i++) {
+ mask = gic_d_read_4(sc, GICD_ITARGETSR(4 * i));
+ if (mask != 0)
+ break;
+ }
+ /* No mask found, assume we are on CPU interface 0 */
+ if (mask == 0)
+ return (1);
+
+ /* Collect the mask in the lower byte */
+ mask |= mask >> 16;
+ mask |= mask >> 8;
+
+ return (mask);
+}
+
+#ifdef SMP
+static void
+arm_gic_init_secondary(device_t dev)
+{
+ struct arm_gic_softc *sc = device_get_softc(dev);
+ u_int irq, cpu;
+
+ /* Set the mask so we can find this CPU to send it IPIs */
+ cpu = PCPU_GET(cpuid);
+ arm_gic_map[cpu] = gic_cpu_mask(sc);
+
+ for (irq = 0; irq < sc->nirqs; irq += 4)
+ gic_d_write_4(sc, GICD_IPRIORITYR(irq), 0);
+
+ /* Set all the interrupts to be in Group 0 (secure) */
+ for (irq = 0; GIC_SUPPORT_SECEXT(sc) && irq < sc->nirqs; irq += 32) {
+ gic_d_write_4(sc, GICD_IGROUPR(irq), 0);
+ }
+
+ /* Enable CPU interface */
+ gic_c_write_4(sc, GICC_CTLR, 1);
+
+ /* Set priority mask register. */
+ gic_c_write_4(sc, GICC_PMR, 0xff);
+
+ /* Enable interrupt distribution */
+ gic_d_write_4(sc, GICD_CTLR, 0x01);
+
+ /* Unmask attached SGI interrupts. */
+ for (irq = GIC_FIRST_SGI; irq <= GIC_LAST_SGI; irq++)
+ if (intr_isrc_init_on_cpu(GIC_INTR_ISRC(sc, irq), cpu))
+ gic_irq_unmask(sc, irq);
+
+ /* Unmask attached PPI interrupts. */
+ for (irq = GIC_FIRST_PPI; irq <= GIC_LAST_PPI; irq++)
+ if (intr_isrc_init_on_cpu(GIC_INTR_ISRC(sc, irq), cpu))
+ gic_irq_unmask(sc, irq);
+}
+#endif /* SMP */
+
+static int
+arm_gic_register_isrcs(struct arm_gic_softc *sc, uint32_t num)
+{
+ int error;
+ uint32_t irq;
+ struct gic_irqsrc *irqs;
+ struct intr_irqsrc *isrc;
+ const char *name;
+
+ irqs = malloc(num * sizeof(struct gic_irqsrc), M_DEVBUF,
+ M_WAITOK | M_ZERO);
+
+ name = device_get_nameunit(sc->gic_dev);
+ for (irq = 0; irq < num; irq++) {
+ irqs[irq].gi_irq = irq;
+ irqs[irq].gi_pol = INTR_POLARITY_CONFORM;
+ irqs[irq].gi_trig = INTR_TRIGGER_CONFORM;
+
+ isrc = &irqs[irq].gi_isrc;
+ if (irq <= GIC_LAST_SGI) {
+ error = intr_isrc_register(isrc, sc->gic_dev,
+ INTR_ISRCF_IPI, "%s,i%u", name, irq - GIC_FIRST_SGI);
+ } else if (irq <= GIC_LAST_PPI) {
+ error = intr_isrc_register(isrc, sc->gic_dev,
+ INTR_ISRCF_PPI, "%s,p%u", name, irq - GIC_FIRST_PPI);
+ } else {
+ error = intr_isrc_register(isrc, sc->gic_dev, 0,
+ "%s,s%u", name, irq - GIC_FIRST_SPI);
+ }
+ if (error != 0) {
+ /* XXX call intr_isrc_deregister() */
+ free(irqs, M_DEVBUF);
+ return (error);
+ }
+ }
+ sc->gic_irqs = irqs;
+ sc->nirqs = num;
+ return (0);
+}
+
+static void
+arm_gic_reserve_msi_range(device_t dev, u_int start, u_int count)
+{
+ struct arm_gic_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+
+ KASSERT((start + count) < sc->nirqs,
+ ("%s: Trying to allocate too many MSI IRQs: %d + %d > %d", __func__,
+ start, count, sc->nirqs));
+ for (i = 0; i < count; i++) {
+ KASSERT(sc->gic_irqs[start + i].gi_isrc.isrc_handlers == 0,
+ ("%s: MSI interrupt %d already has a handler", __func__,
+ count + i));
+ KASSERT(sc->gic_irqs[start + i].gi_pol == INTR_POLARITY_CONFORM,
+ ("%s: MSI interrupt %d already has a polarity", __func__,
+ count + i));
+ KASSERT(sc->gic_irqs[start + i].gi_trig == INTR_TRIGGER_CONFORM,
+ ("%s: MSI interrupt %d already has a trigger", __func__,
+ count + i));
+ sc->gic_irqs[start + i].gi_pol = INTR_POLARITY_HIGH;
+ sc->gic_irqs[start + i].gi_trig = INTR_TRIGGER_EDGE;
+ sc->gic_irqs[start + i].gi_flags |= GI_FLAG_MSI;
+ }
+}
+
+int
+arm_gic_attach(device_t dev)
+{
+ struct arm_gic_softc *sc;
+ int i;
+ uint32_t icciidr, mask, nirqs;
+
+ if (gic_sc)
+ return (ENXIO);
+
+ sc = device_get_softc(dev);
+
+ if (bus_alloc_resources(dev, arm_gic_spec, sc->gic_res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ sc->gic_dev = dev;
+ gic_sc = sc;
+
+ /* Initialize mutex */
+ mtx_init(&sc->mutex, "GIC lock", NULL, MTX_SPIN);
+
+ /* Disable interrupt forwarding to the CPU interface */
+ gic_d_write_4(sc, GICD_CTLR, 0x00);
+
+ /* Get the number of interrupts */
+ sc->typer = gic_d_read_4(sc, GICD_TYPER);
+ nirqs = GICD_TYPER_I_NUM(sc->typer);
+
+ if (arm_gic_register_isrcs(sc, nirqs)) {
+ device_printf(dev, "could not register irqs\n");
+ goto cleanup;
+ }
+
+ icciidr = gic_c_read_4(sc, GICC_IIDR);
+ device_printf(dev,
+ "pn 0x%x, arch 0x%x, rev 0x%x, implementer 0x%x irqs %u\n",
+ GICD_IIDR_PROD(icciidr), GICD_IIDR_VAR(icciidr),
+ GICD_IIDR_REV(icciidr), GICD_IIDR_IMPL(icciidr), sc->nirqs);
+ sc->gic_iidr = icciidr;
+
+ /* Set all global interrupts to be level triggered, active low. */
+ for (i = 32; i < sc->nirqs; i += 16) {
+ gic_d_write_4(sc, GICD_ICFGR(i), GIC_DEFAULT_ICFGR_INIT);
+ }
+
+ /* Disable all interrupts. */
+ for (i = 32; i < sc->nirqs; i += 32) {
+ gic_d_write_4(sc, GICD_ICENABLER(i), 0xFFFFFFFF);
+ }
+
+ /* Find the current cpu mask */
+ mask = gic_cpu_mask(sc);
+ /* Set the mask so we can find this CPU to send it IPIs */
+ arm_gic_map[PCPU_GET(cpuid)] = mask;
+ /* Set all four targets to this cpu */
+ mask |= mask << 8;
+ mask |= mask << 16;
+
+ for (i = 0; i < sc->nirqs; i += 4) {
+ gic_d_write_4(sc, GICD_IPRIORITYR(i), 0);
+ if (i > 32) {
+ gic_d_write_4(sc, GICD_ITARGETSR(i), mask);
+ }
+ }
+
+ /* Set all the interrupts to be in Group 0 (secure) */
+ for (i = 0; GIC_SUPPORT_SECEXT(sc) && i < sc->nirqs; i += 32) {
+ gic_d_write_4(sc, GICD_IGROUPR(i), 0);
+ }
+
+ /* Enable CPU interface */
+ gic_c_write_4(sc, GICC_CTLR, 1);
+
+ /* Set priority mask register. */
+ gic_c_write_4(sc, GICC_PMR, 0xff);
+
+ /* Enable interrupt distribution */
+ gic_d_write_4(sc, GICD_CTLR, 0x01);
+ return (0);
+
+cleanup:
+ arm_gic_detach(dev);
+ return(ENXIO);
+}
+
+int
+arm_gic_detach(device_t dev)
+{
+ struct arm_gic_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (sc->gic_irqs != NULL)
+ free(sc->gic_irqs, M_DEVBUF);
+
+ bus_release_resources(dev, arm_gic_spec, sc->gic_res);
+
+ return (0);
+}
+
+static int
+arm_gic_print_child(device_t bus, device_t child)
+{
+ struct resource_list *rl;
+ int rv;
+
+ rv = bus_print_child_header(bus, child);
+
+ rl = BUS_GET_RESOURCE_LIST(bus, child);
+ if (rl != NULL) {
+ rv += resource_list_print_type(rl, "mem", SYS_RES_MEMORY,
+ "%#jx");
+ rv += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd");
+ }
+
+ rv += bus_print_child_footer(bus, child);
+
+ return (rv);
+}
+
+static struct resource *
+arm_gic_alloc_resource(device_t bus, device_t child, int type, int *rid,
+ rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
+{
+ struct arm_gic_softc *sc;
+ struct resource_list_entry *rle;
+ struct resource_list *rl;
+ int j;
+
+ KASSERT(type == SYS_RES_MEMORY, ("Invalid resoure type %x", type));
+
+ sc = device_get_softc(bus);
+
+ /*
+ * Request for the default allocation with a given rid: use resource
+ * list stored in the local device info.
+ */
+ if (RMAN_IS_DEFAULT_RANGE(start, end)) {
+ rl = BUS_GET_RESOURCE_LIST(bus, child);
+
+ if (type == SYS_RES_IOPORT)
+ type = SYS_RES_MEMORY;
+
+ rle = resource_list_find(rl, type, *rid);
+ if (rle == NULL) {
+ if (bootverbose)
+ device_printf(bus, "no default resources for "
+ "rid = %d, type = %d\n", *rid, type);
+ return (NULL);
+ }
+ start = rle->start;
+ end = rle->end;
+ count = rle->count;
+ }
+
+ /* Remap through ranges property */
+ for (j = 0; j < sc->nranges; j++) {
+ if (start >= sc->ranges[j].bus && end <
+ sc->ranges[j].bus + sc->ranges[j].size) {
+ start -= sc->ranges[j].bus;
+ start += sc->ranges[j].host;
+ end -= sc->ranges[j].bus;
+ end += sc->ranges[j].host;
+ break;
+ }
+ }
+ if (j == sc->nranges && sc->nranges != 0) {
+ if (bootverbose)
+ device_printf(bus, "Could not map resource "
+ "%#jx-%#jx\n", (uintmax_t)start, (uintmax_t)end);
+
+ return (NULL);
+ }
+
+ return (bus_generic_alloc_resource(bus, child, type, rid, start, end,
+ count, flags));
+}
+
+static int
+arm_gic_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
+{
+ struct arm_gic_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ switch(which) {
+ case GIC_IVAR_HW_REV:
+ KASSERT(GICD_IIDR_VAR(sc->gic_iidr) < 3,
+ ("arm_gic_read_ivar: Unknown IIDR revision %u (%.08x)",
+ GICD_IIDR_VAR(sc->gic_iidr), sc->gic_iidr));
+ *result = GICD_IIDR_VAR(sc->gic_iidr);
+ return (0);
+ case GIC_IVAR_BUS:
+ KASSERT(sc->gic_bus != GIC_BUS_UNKNOWN,
+ ("arm_gic_read_ivar: Unknown bus type"));
+ KASSERT(sc->gic_bus <= GIC_BUS_MAX,
+ ("arm_gic_read_ivar: Invalid bus type %u", sc->gic_bus));
+ *result = sc->gic_bus;
+ return (0);
+ }
+
+ return (ENOENT);
+}
+
+int
+arm_gic_intr(void *arg)
+{
+ struct arm_gic_softc *sc = arg;
+ struct gic_irqsrc *gi;
+ uint32_t irq_active_reg, irq;
+ struct trapframe *tf;
+
+ irq_active_reg = gic_c_read_4(sc, GICC_IAR);
+ irq = irq_active_reg & 0x3FF;
+
+ /*
+ * 1. We do EOI here because recent read value from active interrupt
+ * register must be used for it. Another approach is to save this
+ * value into associated interrupt source.
+ * 2. EOI must be done on same CPU where interrupt has fired. Thus
+ * we must ensure that interrupted thread does not migrate to
+ * another CPU.
+ * 3. EOI cannot be delayed by any preemption which could happen on
+ * critical_exit() used in MI intr code, when interrupt thread is
+ * scheduled. See next point.
+ * 4. IPI_RENDEZVOUS assumes that no preemption is permitted during
+ * an action and any use of critical_exit() could break this
+ * assumption. See comments within smp_rendezvous_action().
+ * 5. We always return FILTER_HANDLED as this is an interrupt
+ * controller dispatch function. Otherwise, in cascaded interrupt
+ * case, the whole interrupt subtree would be masked.
+ */
+
+ if (irq >= sc->nirqs) {
+ if (gic_debug_spurious)
+ device_printf(sc->gic_dev,
+ "Spurious interrupt detected: last irq: %d on CPU%d\n",
+ sc->last_irq[PCPU_GET(cpuid)], PCPU_GET(cpuid));
+ return (FILTER_HANDLED);
+ }
+
+ tf = curthread->td_intr_frame;
+dispatch_irq:
+ gi = sc->gic_irqs + irq;
+ /*
+ * Note that GIC_FIRST_SGI is zero and is not used in 'if' statement
+ * as compiler complains that comparing u_int >= 0 is always true.
+ */
+ if (irq <= GIC_LAST_SGI) {
+#ifdef SMP
+ /* Call EOI for all IPI before dispatch. */
+ gic_c_write_4(sc, GICC_EOIR, irq_active_reg);
+ intr_ipi_dispatch(sgi_to_ipi[gi->gi_irq], tf);
+ goto next_irq;
+#else
+ device_printf(sc->gic_dev, "SGI %u on UP system detected\n",
+ irq - GIC_FIRST_SGI);
+ gic_c_write_4(sc, GICC_EOIR, irq_active_reg);
+ goto next_irq;
+#endif
+ }
+
+ if (gic_debug_spurious)
+ sc->last_irq[PCPU_GET(cpuid)] = irq;
+ if ((gi->gi_flags & GI_FLAG_EARLY_EOI) == GI_FLAG_EARLY_EOI)
+ gic_c_write_4(sc, GICC_EOIR, irq_active_reg);
+
+ if (intr_isrc_dispatch(&gi->gi_isrc, tf) != 0) {
+ gic_irq_mask(sc, irq);
+ if ((gi->gi_flags & GI_FLAG_EARLY_EOI) != GI_FLAG_EARLY_EOI)
+ gic_c_write_4(sc, GICC_EOIR, irq_active_reg);
+ device_printf(sc->gic_dev, "Stray irq %u disabled\n", irq);
+ }
+
+next_irq:
+ arm_irq_memory_barrier(irq);
+ irq_active_reg = gic_c_read_4(sc, GICC_IAR);
+ irq = irq_active_reg & 0x3FF;
+ if (irq < sc->nirqs)
+ goto dispatch_irq;
+
+ return (FILTER_HANDLED);
+}
+
+static void
+gic_config(struct arm_gic_softc *sc, u_int irq, enum intr_trigger trig,
+ enum intr_polarity pol)
+{
+ uint32_t reg;
+ uint32_t mask;
+
+ if (irq < GIC_FIRST_SPI)
+ return;
+
+ mtx_lock_spin(&sc->mutex);
+
+ reg = gic_d_read_4(sc, GICD_ICFGR(irq));
+ mask = (reg >> 2*(irq % 16)) & 0x3;
+
+ if (pol == INTR_POLARITY_LOW) {
+ mask &= ~GICD_ICFGR_POL_MASK;
+ mask |= GICD_ICFGR_POL_LOW;
+ } else if (pol == INTR_POLARITY_HIGH) {
+ mask &= ~GICD_ICFGR_POL_MASK;
+ mask |= GICD_ICFGR_POL_HIGH;
+ }
+
+ if (trig == INTR_TRIGGER_LEVEL) {
+ mask &= ~GICD_ICFGR_TRIG_MASK;
+ mask |= GICD_ICFGR_TRIG_LVL;
+ } else if (trig == INTR_TRIGGER_EDGE) {
+ mask &= ~GICD_ICFGR_TRIG_MASK;
+ mask |= GICD_ICFGR_TRIG_EDGE;
+ }
+
+ /* Set mask */
+ reg = reg & ~(0x3 << 2*(irq % 16));
+ reg = reg | (mask << 2*(irq % 16));
+ gic_d_write_4(sc, GICD_ICFGR(irq), reg);
+
+ mtx_unlock_spin(&sc->mutex);
+}
+
+static int
+gic_bind(struct arm_gic_softc *sc, u_int irq, cpuset_t *cpus)
+{
+ uint32_t cpu, end, mask;
+
+ end = min(mp_ncpus, 8);
+ for (cpu = end; cpu < MAXCPU; cpu++)
+ if (CPU_ISSET(cpu, cpus))
+ return (EINVAL);
+
+ for (mask = 0, cpu = 0; cpu < end; cpu++)
+ if (CPU_ISSET(cpu, cpus))
+ mask |= arm_gic_map[cpu];
+
+ gic_d_write_1(sc, GICD_ITARGETSR(0) + irq, mask);
+ return (0);
+}
+
+#ifdef FDT
+static int
+gic_map_fdt(device_t dev, u_int ncells, pcell_t *cells, u_int *irqp,
+ enum intr_polarity *polp, enum intr_trigger *trigp)
+{
+
+ if (ncells == 1) {
+ *irqp = cells[0];
+ *polp = INTR_POLARITY_CONFORM;
+ *trigp = INTR_TRIGGER_CONFORM;
+ return (0);
+ }
+ if (ncells == 3) {
+ u_int irq, tripol;
+
+ /*
+ * The 1st cell is the interrupt type:
+ * 0 = SPI
+ * 1 = PPI
+ * The 2nd cell contains the interrupt number:
+ * [0 - 987] for SPI
+ * [0 - 15] for PPI
+ * The 3rd cell is the flags, encoded as follows:
+ * bits[3:0] trigger type and level flags
+ * 1 = low-to-high edge triggered
+ * 2 = high-to-low edge triggered
+ * 4 = active high level-sensitive
+ * 8 = active low level-sensitive
+ * bits[15:8] PPI interrupt cpu mask
+ * Each bit corresponds to each of the 8 possible cpus
+ * attached to the GIC. A bit set to '1' indicated
+ * the interrupt is wired to that CPU.
+ */
+ switch (cells[0]) {
+ case 0:
+ irq = GIC_FIRST_SPI + cells[1];
+ /* SPI irq is checked later. */
+ break;
+ case 1:
+ irq = GIC_FIRST_PPI + cells[1];
+ if (irq > GIC_LAST_PPI) {
+ device_printf(dev, "unsupported PPI interrupt "
+ "number %u\n", cells[1]);
+ return (EINVAL);
+ }
+ break;
+ default:
+ device_printf(dev, "unsupported interrupt type "
+ "configuration %u\n", cells[0]);
+ return (EINVAL);
+ }
+
+ tripol = cells[2] & 0xff;
+ if (tripol & 0xf0 || (tripol & FDT_INTR_LOW_MASK &&
+ cells[0] == 0))
+ device_printf(dev, "unsupported trigger/polarity "
+ "configuration 0x%02x\n", tripol);
+
+ *irqp = irq;
+ *polp = INTR_POLARITY_CONFORM;
+ *trigp = tripol & FDT_INTR_EDGE_MASK ?
+ INTR_TRIGGER_EDGE : INTR_TRIGGER_LEVEL;
+ return (0);
+ }
+ return (EINVAL);
+}
+#endif
+
+static int
+gic_map_msi(device_t dev, struct intr_map_data_msi *msi_data, u_int *irqp,
+ enum intr_polarity *polp, enum intr_trigger *trigp)
+{
+ struct gic_irqsrc *gi;
+
+ /* Map a non-GICv2m MSI */
+ gi = (struct gic_irqsrc *)msi_data->isrc;
+ if (gi == NULL)
+ return (ENXIO);
+
+ *irqp = gi->gi_irq;
+
+ /* MSI/MSI-X interrupts are always edge triggered with high polarity */
+ *polp = INTR_POLARITY_HIGH;
+ *trigp = INTR_TRIGGER_EDGE;
+
+ return (0);
+}
+
+static int
+gic_map_intr(device_t dev, struct intr_map_data *data, u_int *irqp,
+ enum intr_polarity *polp, enum intr_trigger *trigp)
+{
+ u_int irq;
+ enum intr_polarity pol;
+ enum intr_trigger trig;
+ struct arm_gic_softc *sc;
+ struct intr_map_data_msi *dam;
+#ifdef FDT
+ struct intr_map_data_fdt *daf;
+#endif
+#ifdef DEV_ACPI
+ struct intr_map_data_acpi *daa;
+#endif
+
+ sc = device_get_softc(dev);
+ switch (data->type) {
+#ifdef FDT
+ case INTR_MAP_DATA_FDT:
+ daf = (struct intr_map_data_fdt *)data;
+ if (gic_map_fdt(dev, daf->ncells, daf->cells, &irq, &pol,
+ &trig) != 0)
+ return (EINVAL);
+ KASSERT(irq >= sc->nirqs ||
+ (sc->gic_irqs[irq].gi_flags & GI_FLAG_MSI) == 0,
+ ("%s: Attempting to map a MSI interrupt from FDT",
+ __func__));
+ break;
+#endif
+#ifdef DEV_ACPI
+ case INTR_MAP_DATA_ACPI:
+ daa = (struct intr_map_data_acpi *)data;
+ irq = daa->irq;
+ pol = daa->pol;
+ trig = daa->trig;
+ break;
+#endif
+ case INTR_MAP_DATA_MSI:
+ /* Non-GICv2m MSI */
+ dam = (struct intr_map_data_msi *)data;
+ if (gic_map_msi(dev, dam, &irq, &pol, &trig) != 0)
+ return (EINVAL);
+ break;
+ default:
+ return (ENOTSUP);
+ }
+
+ if (irq >= sc->nirqs)
+ return (EINVAL);
+ if (pol != INTR_POLARITY_CONFORM && pol != INTR_POLARITY_LOW &&
+ pol != INTR_POLARITY_HIGH)
+ return (EINVAL);
+ if (trig != INTR_TRIGGER_CONFORM && trig != INTR_TRIGGER_EDGE &&
+ trig != INTR_TRIGGER_LEVEL)
+ return (EINVAL);
+
+ *irqp = irq;
+ if (polp != NULL)
+ *polp = pol;
+ if (trigp != NULL)
+ *trigp = trig;
+ return (0);
+}
+
+static int
+arm_gic_map_intr(device_t dev, struct intr_map_data *data,
+ struct intr_irqsrc **isrcp)
+{
+ int error;
+ u_int irq;
+ struct arm_gic_softc *sc;
+
+ error = gic_map_intr(dev, data, &irq, NULL, NULL);
+ if (error == 0) {
+ sc = device_get_softc(dev);
+ *isrcp = GIC_INTR_ISRC(sc, irq);
+ }
+ return (error);
+}
+
+static int
+arm_gic_setup_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ struct arm_gic_softc *sc = device_get_softc(dev);
+ struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc;
+ enum intr_trigger trig;
+ enum intr_polarity pol;
+
+ if ((gi->gi_flags & GI_FLAG_MSI) == GI_FLAG_MSI) {
+ /* GICv2m MSI */
+ pol = gi->gi_pol;
+ trig = gi->gi_trig;
+ KASSERT(pol == INTR_POLARITY_HIGH,
+ ("%s: MSI interrupts must be active-high", __func__));
+ KASSERT(trig == INTR_TRIGGER_EDGE,
+ ("%s: MSI interrupts must be edge triggered", __func__));
+ } else if (data != NULL) {
+ u_int irq;
+
+ /* Get config for resource. */
+ if (gic_map_intr(dev, data, &irq, &pol, &trig) ||
+ gi->gi_irq != irq)
+ return (EINVAL);
+ } else {
+ pol = INTR_POLARITY_CONFORM;
+ trig = INTR_TRIGGER_CONFORM;
+ }
+
+ /* Compare config if this is not first setup. */
+ if (isrc->isrc_handlers != 0) {
+ if ((pol != INTR_POLARITY_CONFORM && pol != gi->gi_pol) ||
+ (trig != INTR_TRIGGER_CONFORM && trig != gi->gi_trig))
+ return (EINVAL);
+ else
+ return (0);
+ }
+
+ /* For MSI/MSI-X we should have already configured these */
+ if ((gi->gi_flags & GI_FLAG_MSI) == 0) {
+ if (pol == INTR_POLARITY_CONFORM)
+ pol = INTR_POLARITY_LOW; /* just pick some */
+ if (trig == INTR_TRIGGER_CONFORM)
+ trig = INTR_TRIGGER_EDGE; /* just pick some */
+
+ gi->gi_pol = pol;
+ gi->gi_trig = trig;
+
+ /* Edge triggered interrupts need an early EOI sent */
+ if (gi->gi_trig == INTR_TRIGGER_EDGE)
+ gi->gi_flags |= GI_FLAG_EARLY_EOI;
+ }
+
+ /*
+ * XXX - In case that per CPU interrupt is going to be enabled in time
+ * when SMP is already started, we need some IPI call which
+ * enables it on others CPUs. Further, it's more complicated as
+ * pic_enable_source() and pic_disable_source() should act on
+ * per CPU basis only. Thus, it should be solved here somehow.
+ */
+ if (isrc->isrc_flags & INTR_ISRCF_PPI)
+ CPU_SET(PCPU_GET(cpuid), &isrc->isrc_cpu);
+
+ gic_config(sc, gi->gi_irq, gi->gi_trig, gi->gi_pol);
+ arm_gic_bind_intr(dev, isrc);
+ return (0);
+}
+
+static int
+arm_gic_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc;
+
+ if (isrc->isrc_handlers == 0 && (gi->gi_flags & GI_FLAG_MSI) == 0) {
+ gi->gi_pol = INTR_POLARITY_CONFORM;
+ gi->gi_trig = INTR_TRIGGER_CONFORM;
+ }
+ return (0);
+}
+
+static void
+arm_gic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct arm_gic_softc *sc = device_get_softc(dev);
+ struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc;
+
+ arm_irq_memory_barrier(gi->gi_irq);
+ gic_irq_unmask(sc, gi->gi_irq);
+}
+
+static void
+arm_gic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct arm_gic_softc *sc = device_get_softc(dev);
+ struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc;
+
+ gic_irq_mask(sc, gi->gi_irq);
+}
+
+static void
+arm_gic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct arm_gic_softc *sc = device_get_softc(dev);
+ struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc;
+
+ arm_gic_disable_intr(dev, isrc);
+ gic_c_write_4(sc, GICC_EOIR, gi->gi_irq);
+}
+
+static void
+arm_gic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+
+ arm_irq_memory_barrier(0);
+ arm_gic_enable_intr(dev, isrc);
+}
+
+static void
+arm_gic_post_filter(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct arm_gic_softc *sc = device_get_softc(dev);
+ struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc;
+
+ /* EOI for edge-triggered done earlier. */
+ if ((gi->gi_flags & GI_FLAG_EARLY_EOI) == GI_FLAG_EARLY_EOI)
+ return;
+
+ arm_irq_memory_barrier(0);
+ gic_c_write_4(sc, GICC_EOIR, gi->gi_irq);
+}
+
+static int
+arm_gic_bind_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct arm_gic_softc *sc = device_get_softc(dev);
+ struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc;
+
+ if (gi->gi_irq < GIC_FIRST_SPI)
+ return (EINVAL);
+
+ if (CPU_EMPTY(&isrc->isrc_cpu)) {
+ gic_irq_cpu = intr_irq_next_cpu(gic_irq_cpu, &all_cpus);
+ CPU_SETOF(gic_irq_cpu, &isrc->isrc_cpu);
+ }
+ return (gic_bind(sc, gi->gi_irq, &isrc->isrc_cpu));
+}
+
+#ifdef SMP
+static void
+arm_gic_ipi_send(device_t dev, struct intr_irqsrc *isrc, cpuset_t cpus,
+ u_int ipi)
+{
+ struct arm_gic_softc *sc = device_get_softc(dev);
+ struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc;
+ uint32_t val = 0, i;
+
+ for (i = 0; i < MAXCPU; i++)
+ if (CPU_ISSET(i, &cpus))
+ val |= arm_gic_map[i] << GICD_SGI_TARGET_SHIFT;
+
+ gic_d_write_4(sc, GICD_SGIR, val | gi->gi_irq);
+}
+
+static int
+arm_gic_ipi_setup(device_t dev, u_int ipi, struct intr_irqsrc **isrcp)
+{
+ struct intr_irqsrc *isrc;
+ struct arm_gic_softc *sc = device_get_softc(dev);
+
+ if (sgi_first_unused > GIC_LAST_SGI)
+ return (ENOSPC);
+
+ isrc = GIC_INTR_ISRC(sc, sgi_first_unused);
+ sgi_to_ipi[sgi_first_unused++] = ipi;
+
+ CPU_SET(PCPU_GET(cpuid), &isrc->isrc_cpu);
+
+ *isrcp = isrc;
+ return (0);
+}
+#endif
+
+static device_method_t arm_gic_methods[] = {
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, arm_gic_print_child),
+ DEVMETHOD(bus_add_child, bus_generic_add_child),
+ DEVMETHOD(bus_alloc_resource, arm_gic_alloc_resource),
+ DEVMETHOD(bus_release_resource, bus_generic_release_resource),
+ DEVMETHOD(bus_activate_resource,bus_generic_activate_resource),
+ DEVMETHOD(bus_read_ivar, arm_gic_read_ivar),
+
+ /* Interrupt controller interface */
+ DEVMETHOD(pic_disable_intr, arm_gic_disable_intr),
+ DEVMETHOD(pic_enable_intr, arm_gic_enable_intr),
+ DEVMETHOD(pic_map_intr, arm_gic_map_intr),
+ DEVMETHOD(pic_setup_intr, arm_gic_setup_intr),
+ DEVMETHOD(pic_teardown_intr, arm_gic_teardown_intr),
+ DEVMETHOD(pic_post_filter, arm_gic_post_filter),
+ DEVMETHOD(pic_post_ithread, arm_gic_post_ithread),
+ DEVMETHOD(pic_pre_ithread, arm_gic_pre_ithread),
+#ifdef SMP
+ DEVMETHOD(pic_bind_intr, arm_gic_bind_intr),
+ DEVMETHOD(pic_init_secondary, arm_gic_init_secondary),
+ DEVMETHOD(pic_ipi_send, arm_gic_ipi_send),
+ DEVMETHOD(pic_ipi_setup, arm_gic_ipi_setup),
+#endif
+ { 0, 0 }
+};
+
+DEFINE_CLASS_0(gic, arm_gic_driver, arm_gic_methods,
+ sizeof(struct arm_gic_softc));
+
+/*
+ * GICv2m support -- the GICv2 MSI/MSI-X controller.
+ */
+
+#define GICV2M_MSI_TYPER 0x008
+#define MSI_TYPER_SPI_BASE(x) (((x) >> 16) & 0x3ff)
+#define MSI_TYPER_SPI_COUNT(x) (((x) >> 0) & 0x3ff)
+#define GICv2M_MSI_SETSPI_NS 0x040
+#define GICV2M_MSI_IIDR 0xFCC
+
+int
+arm_gicv2m_attach(device_t dev)
+{
+ struct arm_gicv2m_softc *sc;
+ uint32_t typer;
+ int rid;
+
+ sc = device_get_softc(dev);
+
+ rid = 0;
+ sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->sc_mem == NULL) {
+ device_printf(dev, "Unable to allocate resources\n");
+ return (ENXIO);
+ }
+
+ typer = bus_read_4(sc->sc_mem, GICV2M_MSI_TYPER);
+ sc->sc_spi_start = MSI_TYPER_SPI_BASE(typer);
+ sc->sc_spi_count = MSI_TYPER_SPI_COUNT(typer);
+ sc->sc_spi_end = sc->sc_spi_start + sc->sc_spi_count;
+
+ /* Reserve these interrupts for MSI/MSI-X use */
+ arm_gic_reserve_msi_range(device_get_parent(dev), sc->sc_spi_start,
+ sc->sc_spi_count);
+
+ mtx_init(&sc->sc_mutex, "GICv2m lock", NULL, MTX_DEF);
+
+ intr_msi_register(dev, sc->sc_xref);
+
+ if (bootverbose)
+ device_printf(dev, "using spi %u to %u\n", sc->sc_spi_start,
+ sc->sc_spi_start + sc->sc_spi_count - 1);
+
+ return (0);
+}
+
+static int
+arm_gicv2m_alloc_msi(device_t dev, device_t child, int count, int maxcount,
+ device_t *pic, struct intr_irqsrc **srcs)
+{
+ struct arm_gic_softc *psc;
+ struct arm_gicv2m_softc *sc;
+ int i, irq, end_irq;
+ bool found;
+
+ KASSERT(powerof2(count), ("%s: bad count", __func__));
+ KASSERT(powerof2(maxcount), ("%s: bad maxcount", __func__));
+
+ psc = device_get_softc(device_get_parent(dev));
+ sc = device_get_softc(dev);
+
+ mtx_lock(&sc->sc_mutex);
+
+ found = false;
+ for (irq = sc->sc_spi_start; irq < sc->sc_spi_end; irq++) {
+ /* Start on an aligned interrupt */
+ if ((irq & (maxcount - 1)) != 0)
+ continue;
+
+ /* Assume we found a valid range until shown otherwise */
+ found = true;
+
+ /* Check this range is valid */
+ for (end_irq = irq; end_irq != irq + count; end_irq++) {
+ /* No free interrupts */
+ if (end_irq == sc->sc_spi_end) {
+ found = false;
+ break;
+ }
+
+ KASSERT((psc->gic_irqs[end_irq].gi_flags & GI_FLAG_MSI)!= 0,
+ ("%s: Non-MSI interrupt found", __func__));
+
+ /* This is already used */
+ if ((psc->gic_irqs[end_irq].gi_flags & GI_FLAG_MSI_USED) ==
+ GI_FLAG_MSI_USED) {
+ found = false;
+ break;
+ }
+ }
+ if (found)
+ break;
+ }
+
+ /* Not enough interrupts were found */
+ if (!found || irq == sc->sc_spi_end) {
+ mtx_unlock(&sc->sc_mutex);
+ return (ENXIO);
+ }
+
+ for (i = 0; i < count; i++) {
+ /* Mark the interrupt as used */
+ psc->gic_irqs[irq + i].gi_flags |= GI_FLAG_MSI_USED;
+ }
+ mtx_unlock(&sc->sc_mutex);
+
+ for (i = 0; i < count; i++)
+ srcs[i] = (struct intr_irqsrc *)&psc->gic_irqs[irq + i];
+ *pic = device_get_parent(dev);
+
+ return (0);
+}
+
+static int
+arm_gicv2m_release_msi(device_t dev, device_t child, int count,
+ struct intr_irqsrc **isrc)
+{
+ struct arm_gicv2m_softc *sc;
+ struct gic_irqsrc *gi;
+ int i;
+
+ sc = device_get_softc(dev);
+
+ mtx_lock(&sc->sc_mutex);
+ for (i = 0; i < count; i++) {
+ gi = (struct gic_irqsrc *)isrc[i];
+
+ KASSERT((gi->gi_flags & GI_FLAG_MSI_USED) == GI_FLAG_MSI_USED,
+ ("%s: Trying to release an unused MSI-X interrupt",
+ __func__));
+
+ gi->gi_flags &= ~GI_FLAG_MSI_USED;
+ }
+ mtx_unlock(&sc->sc_mutex);
+
+ return (0);
+}
+
+static int
+arm_gicv2m_alloc_msix(device_t dev, device_t child, device_t *pic,
+ struct intr_irqsrc **isrcp)
+{
+ struct arm_gicv2m_softc *sc;
+ struct arm_gic_softc *psc;
+ int irq;
+
+ psc = device_get_softc(device_get_parent(dev));
+ sc = device_get_softc(dev);
+
+ mtx_lock(&sc->sc_mutex);
+ /* Find an unused interrupt */
+ for (irq = sc->sc_spi_start; irq < sc->sc_spi_end; irq++) {
+ KASSERT((psc->gic_irqs[irq].gi_flags & GI_FLAG_MSI) != 0,
+ ("%s: Non-MSI interrupt found", __func__));
+ if ((psc->gic_irqs[irq].gi_flags & GI_FLAG_MSI_USED) == 0)
+ break;
+ }
+ /* No free interrupt was found */
+ if (irq == sc->sc_spi_end) {
+ mtx_unlock(&sc->sc_mutex);
+ return (ENXIO);
+ }
+
+ /* Mark the interrupt as used */
+ psc->gic_irqs[irq].gi_flags |= GI_FLAG_MSI_USED;
+ mtx_unlock(&sc->sc_mutex);
+
+ *isrcp = (struct intr_irqsrc *)&psc->gic_irqs[irq];
+ *pic = device_get_parent(dev);
+
+ return (0);
+}
+
+static int
+arm_gicv2m_release_msix(device_t dev, device_t child, struct intr_irqsrc *isrc)
+{
+ struct arm_gicv2m_softc *sc;
+ struct gic_irqsrc *gi;
+
+ sc = device_get_softc(dev);
+ gi = (struct gic_irqsrc *)isrc;
+
+ KASSERT((gi->gi_flags & GI_FLAG_MSI_USED) == GI_FLAG_MSI_USED,
+ ("%s: Trying to release an unused MSI-X interrupt", __func__));
+
+ mtx_lock(&sc->sc_mutex);
+ gi->gi_flags &= ~GI_FLAG_MSI_USED;
+ mtx_unlock(&sc->sc_mutex);
+
+ return (0);
+}
+
+static int
+arm_gicv2m_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc,
+ uint64_t *addr, uint32_t *data)
+{
+ struct arm_gicv2m_softc *sc = device_get_softc(dev);
+ struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc;
+
+ *addr = vtophys(rman_get_virtual(sc->sc_mem)) + GICv2M_MSI_SETSPI_NS;
+ *data = gi->gi_irq;
+
+ return (0);
+}
+
+static device_method_t arm_gicv2m_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_attach, arm_gicv2m_attach),
+
+ /* MSI/MSI-X */
+ DEVMETHOD(msi_alloc_msi, arm_gicv2m_alloc_msi),
+ DEVMETHOD(msi_release_msi, arm_gicv2m_release_msi),
+ DEVMETHOD(msi_alloc_msix, arm_gicv2m_alloc_msix),
+ DEVMETHOD(msi_release_msix, arm_gicv2m_release_msix),
+ DEVMETHOD(msi_map_msi, arm_gicv2m_map_msi),
+
+ /* End */
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_0(gicv2m, arm_gicv2m_driver, arm_gicv2m_methods,
+ sizeof(struct arm_gicv2m_softc));
diff --git a/sys/arm/arm/gic.h b/sys/arm/arm/gic.h
new file mode 100644
index 000000000000..74cfbbee9d5a
--- /dev/null
+++ b/sys/arm/arm/gic.h
@@ -0,0 +1,86 @@
+/*-
+ * Copyright (c) 2011,2016 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Andrew Turner under
+ * sponsorship from the FreeBSD Foundation.
+ *
+ * Developed by Damjan Marion <damjan.marion@gmail.com>
+ *
+ * Based on OMAP4 GIC code by Ben Gray
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _ARM_GIC_H_
+#define _ARM_GIC_H_
+
+struct arm_gic_range {
+ uint64_t bus;
+ uint64_t host;
+ uint64_t size;
+};
+
+struct arm_gic_softc {
+ device_t gic_dev;
+ void * gic_intrhand;
+ struct gic_irqsrc * gic_irqs;
+#define GIC_RES_DIST 0
+#define GIC_RES_CPU 1
+ struct resource * gic_res[3];
+ uint8_t ver;
+ struct mtx mutex;
+ uint32_t nirqs;
+ uint32_t typer;
+ uint32_t last_irq[MAXCPU];
+
+ uint32_t gic_iidr;
+ u_int gic_bus;
+
+ int nranges;
+ struct arm_gic_range * ranges;
+};
+
+DECLARE_CLASS(arm_gic_driver);
+
+struct arm_gicv2m_softc {
+ struct resource *sc_mem;
+ struct mtx sc_mutex;
+ uintptr_t sc_xref;
+ u_int sc_spi_start;
+ u_int sc_spi_end;
+ u_int sc_spi_count;
+};
+
+DECLARE_CLASS(arm_gicv2m_driver);
+
+int arm_gic_attach(device_t);
+int arm_gic_detach(device_t);
+int arm_gicv2m_attach(device_t);
+int arm_gic_intr(void *);
+
+#endif /* _ARM_GIC_H_ */
diff --git a/sys/arm/arm/gic_acpi.c b/sys/arm/arm/gic_acpi.c
new file mode 100644
index 000000000000..97d8db75fe41
--- /dev/null
+++ b/sys/arm/arm/gic_acpi.c
@@ -0,0 +1,360 @@
+/*-
+ * Copyright (c) 2011,2016 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Andrew Turner under
+ * sponsorship from the FreeBSD Foundation.
+ *
+ * Developed by Damjan Marion <damjan.marion@gmail.com>
+ *
+ * Based on OMAP4 GIC code by Ben Gray
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "opt_acpi.h"
+#include "opt_platform.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+
+#include <machine/intr.h>
+
+#include <contrib/dev/acpica/include/acpi.h>
+#include <dev/acpica/acpivar.h>
+
+#include <arm/arm/gic.h>
+#include <arm/arm/gic_common.h>
+
+struct gic_acpi_devinfo {
+ struct resource_list rl;
+};
+
+static device_identify_t gic_acpi_identify;
+static device_probe_t gic_acpi_probe;
+static device_attach_t gic_acpi_attach;
+static bus_get_resource_list_t gic_acpi_get_resource_list;
+static bool arm_gic_add_children(device_t);
+
+static device_method_t gic_acpi_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_identify, gic_acpi_identify),
+ DEVMETHOD(device_probe, gic_acpi_probe),
+ DEVMETHOD(device_attach, gic_acpi_attach),
+
+ /* Bus interface */
+ DEVMETHOD(bus_get_resource_list, gic_acpi_get_resource_list),
+
+ DEVMETHOD_END,
+};
+
+DEFINE_CLASS_1(gic, gic_acpi_driver, gic_acpi_methods,
+ sizeof(struct arm_gic_softc), arm_gic_driver);
+
+static devclass_t gic_acpi_devclass;
+
+EARLY_DRIVER_MODULE(gic, acpi, gic_acpi_driver, gic_acpi_devclass, 0, 0,
+ BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
+
+struct madt_table_data {
+ device_t parent;
+ ACPI_MADT_GENERIC_DISTRIBUTOR *dist;
+ ACPI_MADT_GENERIC_INTERRUPT *intr[MAXCPU];
+};
+
+static void
+madt_handler(ACPI_SUBTABLE_HEADER *entry, void *arg)
+{
+ struct madt_table_data *madt_data;
+ ACPI_MADT_GENERIC_INTERRUPT *intr;
+
+ madt_data = (struct madt_table_data *)arg;
+
+ switch(entry->Type) {
+ case ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR:
+ if (madt_data->dist != NULL) {
+ if (bootverbose)
+ device_printf(madt_data->parent,
+ "gic: Already have a distributor table");
+ } else
+ madt_data->dist =
+ (ACPI_MADT_GENERIC_DISTRIBUTOR *)entry;
+ break;
+ case ACPI_MADT_TYPE_GENERIC_INTERRUPT:
+ intr = (ACPI_MADT_GENERIC_INTERRUPT *)entry;
+ if (intr->CpuInterfaceNumber < MAXCPU)
+ madt_data->intr[intr->CpuInterfaceNumber] = intr;
+ break;
+ }
+}
+
+static void
+gic_acpi_identify(driver_t *driver, device_t parent)
+{
+ struct madt_table_data madt_data;
+ ACPI_MADT_GENERIC_INTERRUPT *intr;
+ ACPI_TABLE_MADT *madt;
+ vm_paddr_t physaddr;
+ device_t dev;
+ int i;
+
+ physaddr = acpi_find_table(ACPI_SIG_MADT);
+ if (physaddr == 0)
+ return;
+
+ madt = acpi_map_table(physaddr, ACPI_SIG_MADT);
+ if (madt == NULL) {
+ device_printf(parent, "gic: Unable to map the MADT\n");
+ return;
+ }
+
+ bzero(&madt_data, sizeof(madt_data));
+ madt_data.parent = parent;
+ madt_data.dist = NULL;
+
+ acpi_walk_subtables(madt + 1, (char *)madt + madt->Header.Length,
+ madt_handler, &madt_data);
+
+ /* Check the version of the GIC we have */
+ switch (madt_data.dist->Version) {
+ case ACPI_MADT_GIC_VERSION_NONE:
+ case ACPI_MADT_GIC_VERSION_V1:
+ case ACPI_MADT_GIC_VERSION_V2:
+ break;
+ default:
+ goto out;
+ }
+
+ intr = NULL;
+ for (i = 0; i < MAXCPU; i++) {
+ if (madt_data.intr[i] != NULL) {
+ if (intr == NULL) {
+ intr = madt_data.intr[i];
+ } else if (intr->BaseAddress !=
+ madt_data.intr[i]->BaseAddress) {
+ device_printf(parent,
+"gic: Not all CPU interfaces at the same address, this may fail\n");
+ }
+ }
+ }
+ if (intr == NULL) {
+ device_printf(parent, "gic: No CPU interfaces found\n");
+ goto out;
+ }
+
+ dev = BUS_ADD_CHILD(parent, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE,
+ "gic", -1);
+ if (dev == NULL) {
+ device_printf(parent, "add gic child failed\n");
+ goto out;
+ }
+
+ BUS_SET_RESOURCE(parent, dev, SYS_RES_MEMORY, 0,
+ madt_data.dist->BaseAddress, 4 * 1024);
+ BUS_SET_RESOURCE(parent, dev, SYS_RES_MEMORY, 1,
+ intr->BaseAddress, 4 * 1024);
+
+ acpi_set_private(dev, (void *)(uintptr_t)madt_data.dist->Version);
+out:
+ acpi_unmap_table(madt);
+}
+
+static int
+gic_acpi_probe(device_t dev)
+{
+
+ switch((uintptr_t)acpi_get_private(dev)) {
+ case ACPI_MADT_GIC_VERSION_NONE:
+ case ACPI_MADT_GIC_VERSION_V1:
+ case ACPI_MADT_GIC_VERSION_V2:
+ break;
+ default:
+ return (ENXIO);
+ }
+
+ device_set_desc(dev, "ARM Generic Interrupt Controller");
+ return (BUS_PROBE_NOWILDCARD);
+}
+
+static int
+gic_acpi_attach(device_t dev)
+{
+ struct arm_gic_softc *sc = device_get_softc(dev);
+ intptr_t xref;
+ int err;
+
+ sc->gic_bus = GIC_BUS_ACPI;
+
+ err = arm_gic_attach(dev);
+ if (err != 0)
+ return (err);
+
+ xref = ACPI_INTR_XREF;
+
+ /*
+ * Now, when everything is initialized, it's right time to
+ * register interrupt controller to interrupt framefork.
+ */
+ if (intr_pic_register(dev, xref) == NULL) {
+ device_printf(dev, "could not register PIC\n");
+ goto cleanup;
+ }
+
+ /*
+ * Controller is root:
+ */
+ if (intr_pic_claim_root(dev, xref, arm_gic_intr, sc,
+ GIC_LAST_SGI - GIC_FIRST_SGI + 1) != 0) {
+ device_printf(dev, "could not set PIC as a root\n");
+ intr_pic_deregister(dev, xref);
+ goto cleanup;
+ }
+ /* If we have children probe and attach them */
+ if (arm_gic_add_children(dev)) {
+ bus_generic_probe(dev);
+ return (bus_generic_attach(dev));
+ }
+
+ return (0);
+
+cleanup:
+ arm_gic_detach(dev);
+ return(ENXIO);
+}
+
+static struct resource_list *
+gic_acpi_get_resource_list(device_t bus, device_t child)
+{
+ struct gic_acpi_devinfo *di;
+
+ di = device_get_ivars(child);
+ KASSERT(di != NULL, ("gic_acpi_get_resource_list: No devinfo"));
+
+ return (&di->rl);
+}
+
+static void
+madt_gicv2m_handler(ACPI_SUBTABLE_HEADER *entry, void *arg)
+{
+ struct arm_gic_softc *sc;
+ ACPI_MADT_GENERIC_MSI_FRAME *msi;
+ struct gic_acpi_devinfo *dinfo;
+ device_t dev, cdev;
+
+ if (entry->Type == ACPI_MADT_TYPE_GENERIC_MSI_FRAME) {
+ sc = arg;
+ dev = sc->gic_dev;
+ msi = (ACPI_MADT_GENERIC_MSI_FRAME *)entry;
+
+ device_printf(dev, "frame: %x %lx %x %u %u\n", msi->MsiFrameId,
+ msi->BaseAddress, msi->Flags, msi->SpiCount, msi->SpiBase);
+
+ cdev = device_add_child(dev, NULL, -1);
+ if (cdev == NULL)
+ return;
+
+ dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_WAITOK | M_ZERO);
+ resource_list_init(&dinfo->rl);
+ resource_list_add(&dinfo->rl, SYS_RES_MEMORY, 0,
+ msi->BaseAddress, msi->BaseAddress + PAGE_SIZE - 1,
+ PAGE_SIZE);
+ device_set_ivars(cdev, dinfo);
+ }
+}
+
+static bool
+arm_gic_add_children(device_t dev)
+{
+ struct arm_gic_softc *sc = device_get_softc(dev);
+ ACPI_TABLE_MADT *madt;
+ vm_paddr_t physaddr;
+
+ /* This should return a valid address as it did in gic_acpi_identify */
+ physaddr = acpi_find_table(ACPI_SIG_MADT);
+ if (physaddr == 0)
+ return (false);
+
+ madt = acpi_map_table(physaddr, ACPI_SIG_MADT);
+ if (madt == NULL) {
+ device_printf(dev, "gic: Unable to map the MADT\n");
+ return (false);
+ }
+
+ acpi_walk_subtables(madt + 1, (char *)madt + madt->Header.Length,
+ madt_gicv2m_handler, sc);
+
+ acpi_unmap_table(madt);
+
+ return (true);
+}
+
+static int
+arm_gicv2m_acpi_probe(device_t dev)
+{
+
+ if (gic_get_bus(dev) != GIC_BUS_ACPI)
+ return (EINVAL);
+
+ if (gic_get_hw_rev(dev) > 2)
+ return (EINVAL);
+
+ device_set_desc(dev, "ARM Generic Interrupt Controller MSI/MSIX");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+arm_gicv2m_acpi_attach(device_t dev)
+{
+ struct arm_gicv2m_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->sc_xref = ACPI_MSI_XREF;
+
+ return (arm_gicv2m_attach(dev));
+}
+
+static device_method_t arm_gicv2m_acpi_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, arm_gicv2m_acpi_probe),
+ DEVMETHOD(device_attach, arm_gicv2m_acpi_attach),
+
+ /* End */
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(gicv2m, arm_gicv2m_acpi_driver, arm_gicv2m_acpi_methods,
+ sizeof(struct arm_gicv2m_softc), arm_gicv2m_driver);
+
+static devclass_t arm_gicv2m_acpi_devclass;
+
+EARLY_DRIVER_MODULE(gicv2m_acpi, gic, arm_gicv2m_acpi_driver,
+ arm_gicv2m_acpi_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
diff --git a/sys/arm/arm/gic_common.h b/sys/arm/arm/gic_common.h
new file mode 100644
index 000000000000..6643496afc38
--- /dev/null
+++ b/sys/arm/arm/gic_common.h
@@ -0,0 +1,102 @@
+/*-
+ * Copyright (c) 2016 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Andrew Turner under
+ * the sponsorship of the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _GIC_COMMON_H_
+#define _GIC_COMMON_H_
+
+#define GIC_IVAR_HW_REV 500
+#define GIC_IVAR_BUS 501
+
+/* GIC_IVAR_BUS values */
+#define GIC_BUS_UNKNOWN 0
+#define GIC_BUS_FDT 1
+#define GIC_BUS_ACPI 2
+#define GIC_BUS_MAX 2
+
+__BUS_ACCESSOR(gic, hw_rev, GIC, HW_REV, u_int);
+__BUS_ACCESSOR(gic, bus, GIC, BUS, u_int);
+
+/* Software Generated Interrupts */
+#define GIC_FIRST_SGI 0 /* Irqs 0-15 are SGIs/IPIs. */
+#define GIC_LAST_SGI 15
+/* Private Peripheral Interrupts */
+#define GIC_FIRST_PPI 16 /* Irqs 16-31 are private (per */
+#define GIC_LAST_PPI 31 /* core) peripheral interrupts. */
+/* Shared Peripheral Interrupts */
+#define GIC_FIRST_SPI 32 /* Irqs 32+ are shared peripherals. */
+
+/* Common register values */
+#define GICD_CTLR 0x0000 /* v1 ICDDCR */
+#define GICD_TYPER 0x0004 /* v1 ICDICTR */
+#define GICD_TYPER_I_NUM(n) ((((n) & 0x1F) + 1) * 32)
+#define GICD_IIDR 0x0008 /* v1 ICDIIDR */
+#define GICD_IIDR_PROD_SHIFT 24
+#define GICD_IIDR_PROD_MASK 0xff000000
+#define GICD_IIDR_PROD(x) \
+ (((x) & GICD_IIDR_PROD_MASK) >> GICD_IIDR_PROD_SHIFT)
+#define GICD_IIDR_VAR_SHIFT 16
+#define GICD_IIDR_VAR_MASK 0x000f0000
+#define GICD_IIDR_VAR(x) \
+ (((x) & GICD_IIDR_VAR_MASK) >> GICD_IIDR_VAR_SHIFT)
+#define GICD_IIDR_REV_SHIFT 12
+#define GICD_IIDR_REV_MASK 0x0000f000
+#define GICD_IIDR_REV(x) \
+ (((x) & GICD_IIDR_REV_MASK) >> GICD_IIDR_REV_SHIFT)
+#define GICD_IIDR_IMPL_SHIFT 0
+#define GICD_IIDR_IMPL_MASK 0x00000fff
+#define GICD_IIDR_IMPL(x) \
+ (((x) & GICD_IIDR_IMPL_MASK) >> GICD_IIDR_IMPL_SHIFT)
+#define GICD_IGROUPR(n) (0x0080 + (((n) >> 5) * 4)) /* v1 ICDISER */
+#define GICD_I_PER_IGROUPRn 32
+#define GICD_ISENABLER(n) (0x0100 + (((n) >> 5) * 4)) /* v1 ICDISER */
+#define GICD_I_MASK(n) (1ul << ((n) & 0x1f))
+#define GICD_I_PER_ISENABLERn 32
+#define GICD_ICENABLER(n) (0x0180 + (((n) >> 5) * 4)) /* v1 ICDICER */
+#define GICD_ISPENDR(n) (0x0200 + (((n) >> 5) * 4)) /* v1 ICDISPR */
+#define GICD_ICPENDR(n) (0x0280 + (((n) >> 5) * 4)) /* v1 ICDICPR */
+#define GICD_ICACTIVER(n) (0x0380 + (((n) >> 5) * 4)) /* v1 ICDABR */
+#define GICD_IPRIORITYR(n) (0x0400 + (((n) >> 2) * 4)) /* v1 ICDIPR */
+#define GICD_I_PER_IPRIORITYn 4
+#define GICD_ITARGETSR(n) (0x0800 + (((n) >> 2) * 4)) /* v1 ICDIPTR */
+#define GICD_ICFGR(n) (0x0C00 + (((n) >> 4) * 4)) /* v1 ICDICFR */
+#define GICD_I_PER_ICFGRn 16
+/* First bit is a polarity bit (0 - low, 1 - high) */
+#define GICD_ICFGR_POL_LOW (0 << 0)
+#define GICD_ICFGR_POL_HIGH (1 << 0)
+#define GICD_ICFGR_POL_MASK 0x1
+/* Second bit is a trigger bit (0 - level, 1 - edge) */
+#define GICD_ICFGR_TRIG_LVL (0 << 1)
+#define GICD_ICFGR_TRIG_EDGE (1 << 1)
+#define GICD_ICFGR_TRIG_MASK 0x2
+#define GICD_SGIR 0x0F00 /* v1 ICDSGIR */
+#define GICD_SGI_TARGET_SHIFT 16
+
+#endif /* _GIC_COMMON_H_ */
diff --git a/sys/arm/arm/gic_fdt.c b/sys/arm/arm/gic_fdt.c
new file mode 100644
index 000000000000..bf43339d65bd
--- /dev/null
+++ b/sys/arm/arm/gic_fdt.c
@@ -0,0 +1,363 @@
+/*-
+ * Copyright (c) 2011,2016 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Andrew Turner under
+ * sponsorship from the FreeBSD Foundation.
+ *
+ * Developed by Damjan Marion <damjan.marion@gmail.com>
+ *
+ * Based on OMAP4 GIC code by Ben Gray
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "opt_platform.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+
+#include <machine/intr.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/arm/gic.h>
+#include <arm/arm/gic_common.h>
+
+struct arm_gic_devinfo {
+ struct ofw_bus_devinfo obdinfo;
+ struct resource_list rl;
+};
+
+struct arm_gic_fdt_softc {
+ struct arm_gic_softc base;
+ pcell_t addr_cells;
+ pcell_t size_cells;
+};
+
+static device_probe_t gic_fdt_probe;
+static device_attach_t gic_fdt_attach;
+static ofw_bus_get_devinfo_t gic_ofw_get_devinfo;
+static bus_get_resource_list_t gic_fdt_get_resource_list;
+static bool arm_gic_add_children(device_t);
+
+static struct ofw_compat_data compat_data[] = {
+ {"arm,gic", true}, /* Non-standard, used in FreeBSD dts. */
+ {"arm,gic-400", true},
+ {"arm,cortex-a15-gic", true},
+ {"arm,cortex-a9-gic", true},
+ {"arm,cortex-a7-gic", true},
+ {"arm,arm11mp-gic", true},
+ {"brcm,brahma-b15-gic", true},
+ {"qcom,msm-qgic2", true},
+ {NULL, false}
+};
+
+static device_method_t gic_fdt_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, gic_fdt_probe),
+ DEVMETHOD(device_attach, gic_fdt_attach),
+
+ /* Bus interface */
+ DEVMETHOD(bus_get_resource_list,gic_fdt_get_resource_list),
+
+ /* ofw_bus interface */
+ DEVMETHOD(ofw_bus_get_devinfo, gic_ofw_get_devinfo),
+ DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat),
+ DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model),
+ DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name),
+ DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node),
+ DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type),
+
+ DEVMETHOD_END,
+};
+
+DEFINE_CLASS_1(gic, gic_fdt_driver, gic_fdt_methods,
+ sizeof(struct arm_gic_fdt_softc), arm_gic_driver);
+
+static devclass_t gic_fdt_devclass;
+
+EARLY_DRIVER_MODULE(gic, simplebus, gic_fdt_driver, gic_fdt_devclass, 0, 0,
+ BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
+EARLY_DRIVER_MODULE(gic, ofwbus, gic_fdt_driver, gic_fdt_devclass, 0, 0,
+ BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
+
+static int
+gic_fdt_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+ return (ENXIO);
+ device_set_desc(dev, "ARM Generic Interrupt Controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+gic_fdt_attach(device_t dev)
+{
+ struct arm_gic_fdt_softc *sc = device_get_softc(dev);
+ phandle_t pxref;
+ intptr_t xref;
+ int err;
+
+ sc->base.gic_bus = GIC_BUS_FDT;
+
+ err = arm_gic_attach(dev);
+ if (err != 0)
+ return (err);
+
+ xref = OF_xref_from_node(ofw_bus_get_node(dev));
+
+ /*
+ * Now, when everything is initialized, it's right time to
+ * register interrupt controller to interrupt framefork.
+ */
+ if (intr_pic_register(dev, xref) == NULL) {
+ device_printf(dev, "could not register PIC\n");
+ goto cleanup;
+ }
+
+ /*
+ * Controller is root if:
+ * - doesn't have interrupt parent
+ * - his interrupt parent is this controller
+ */
+ pxref = ofw_bus_find_iparent(ofw_bus_get_node(dev));
+ if (pxref == 0 || xref == pxref) {
+ if (intr_pic_claim_root(dev, xref, arm_gic_intr, sc,
+ GIC_LAST_SGI - GIC_FIRST_SGI + 1) != 0) {
+ device_printf(dev, "could not set PIC as a root\n");
+ intr_pic_deregister(dev, xref);
+ goto cleanup;
+ }
+ } else {
+ if (sc->base.gic_res[2] == NULL) {
+ device_printf(dev,
+ "not root PIC must have defined interrupt\n");
+ intr_pic_deregister(dev, xref);
+ goto cleanup;
+ }
+ if (bus_setup_intr(dev, sc->base.gic_res[2], INTR_TYPE_CLK,
+ arm_gic_intr, NULL, sc, &sc->base.gic_intrhand)) {
+ device_printf(dev, "could not setup irq handler\n");
+ intr_pic_deregister(dev, xref);
+ goto cleanup;
+ }
+ }
+
+ OF_device_register_xref(xref, dev);
+
+ /* If we have children probe and attach them */
+ if (arm_gic_add_children(dev)) {
+ bus_generic_probe(dev);
+ return (bus_generic_attach(dev));
+ }
+
+ return (0);
+
+cleanup:
+ arm_gic_detach(dev);
+ return(ENXIO);
+}
+
+static struct resource_list *
+gic_fdt_get_resource_list(device_t bus, device_t child)
+{
+ struct arm_gic_devinfo *di;
+
+ di = device_get_ivars(child);
+ KASSERT(di != NULL, ("gic_fdt_get_resource_list: No devinfo"));
+
+ return (&di->rl);
+}
+
+static int
+arm_gic_fill_ranges(phandle_t node, struct arm_gic_fdt_softc *sc)
+{
+ pcell_t host_cells;
+ cell_t *base_ranges;
+ ssize_t nbase_ranges;
+ int i, j, k;
+
+ host_cells = 1;
+ OF_getencprop(OF_parent(node), "#address-cells", &host_cells,
+ sizeof(host_cells));
+ sc->addr_cells = 2;
+ OF_getencprop(node, "#address-cells", &sc->addr_cells,
+ sizeof(sc->addr_cells));
+ sc->size_cells = 2;
+ OF_getencprop(node, "#size-cells", &sc->size_cells,
+ sizeof(sc->size_cells));
+
+ nbase_ranges = OF_getproplen(node, "ranges");
+ if (nbase_ranges < 0)
+ return (-1);
+ sc->base.nranges = nbase_ranges / sizeof(cell_t) /
+ (sc->addr_cells + host_cells + sc->size_cells);
+ if (sc->base.nranges == 0)
+ return (0);
+
+ sc->base.ranges = malloc(sc->base.nranges * sizeof(sc->base.ranges[0]),
+ M_DEVBUF, M_WAITOK);
+ base_ranges = malloc(nbase_ranges, M_DEVBUF, M_WAITOK);
+ OF_getencprop(node, "ranges", base_ranges, nbase_ranges);
+
+ for (i = 0, j = 0; i < sc->base.nranges; i++) {
+ sc->base.ranges[i].bus = 0;
+ for (k = 0; k < sc->addr_cells; k++) {
+ sc->base.ranges[i].bus <<= 32;
+ sc->base.ranges[i].bus |= base_ranges[j++];
+ }
+ sc->base.ranges[i].host = 0;
+ for (k = 0; k < host_cells; k++) {
+ sc->base.ranges[i].host <<= 32;
+ sc->base.ranges[i].host |= base_ranges[j++];
+ }
+ sc->base.ranges[i].size = 0;
+ for (k = 0; k < sc->size_cells; k++) {
+ sc->base.ranges[i].size <<= 32;
+ sc->base.ranges[i].size |= base_ranges[j++];
+ }
+ }
+
+ free(base_ranges, M_DEVBUF);
+ return (sc->base.nranges);
+}
+
+static bool
+arm_gic_add_children(device_t dev)
+{
+ struct arm_gic_fdt_softc *sc;
+ struct arm_gic_devinfo *dinfo;
+ phandle_t child, node;
+ device_t cdev;
+
+ sc = device_get_softc(dev);
+ node = ofw_bus_get_node(dev);
+
+ /* If we have no children don't probe for them */
+ child = OF_child(node);
+ if (child == 0)
+ return (false);
+
+ if (arm_gic_fill_ranges(node, sc) < 0) {
+ device_printf(dev, "Have a child, but no ranges\n");
+ return (false);
+ }
+
+ for (; child != 0; child = OF_peer(child)) {
+ dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_WAITOK | M_ZERO);
+
+ if (ofw_bus_gen_setup_devinfo(&dinfo->obdinfo, child) != 0) {
+ free(dinfo, M_DEVBUF);
+ continue;
+ }
+
+ resource_list_init(&dinfo->rl);
+ ofw_bus_reg_to_rl(dev, child, sc->addr_cells,
+ sc->size_cells, &dinfo->rl);
+
+ cdev = device_add_child(dev, NULL, -1);
+ if (cdev == NULL) {
+ device_printf(dev, "<%s>: device_add_child failed\n",
+ dinfo->obdinfo.obd_name);
+ resource_list_free(&dinfo->rl);
+ ofw_bus_gen_destroy_devinfo(&dinfo->obdinfo);
+ free(dinfo, M_DEVBUF);
+ continue;
+ }
+ device_set_ivars(cdev, dinfo);
+ }
+
+ return (true);
+}
+
+static const struct ofw_bus_devinfo *
+gic_ofw_get_devinfo(device_t bus __unused, device_t child)
+{
+ struct arm_gic_devinfo *di;
+
+ di = device_get_ivars(child);
+
+ return (&di->obdinfo);
+}
+
+static struct ofw_compat_data gicv2m_compat_data[] = {
+ {"arm,gic-v2m-frame", true},
+ {NULL, false}
+};
+
+static int
+arm_gicv2m_fdt_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_search_compatible(dev, gicv2m_compat_data)->ocd_data)
+ return (ENXIO);
+
+ device_set_desc(dev, "ARM Generic Interrupt Controller MSI/MSIX");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+arm_gicv2m_fdt_attach(device_t dev)
+{
+ struct arm_gicv2m_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->sc_xref = OF_xref_from_node(ofw_bus_get_node(dev));
+
+ return (arm_gicv2m_attach(dev));
+}
+
+static device_method_t arm_gicv2m_fdt_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, arm_gicv2m_fdt_probe),
+ DEVMETHOD(device_attach, arm_gicv2m_fdt_attach),
+
+ /* End */
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(gicv2m, arm_gicv2m_fdt_driver, arm_gicv2m_fdt_methods,
+ sizeof(struct arm_gicv2m_softc), arm_gicv2m_driver);
+
+static devclass_t arm_gicv2m_fdt_devclass;
+
+EARLY_DRIVER_MODULE(gicv2m, gic, arm_gicv2m_fdt_driver,
+ arm_gicv2m_fdt_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
diff --git a/sys/arm/arm/hypervisor-stub.S b/sys/arm/arm/hypervisor-stub.S
new file mode 100644
index 000000000000..e7268e6465c4
--- /dev/null
+++ b/sys/arm/arm/hypervisor-stub.S
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2015 Mihai Carabas <mihai.carabas@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "assym.inc"
+#include <sys/syscall.h>
+#include <machine/asm.h>
+#include <machine/asmacros.h>
+#include <machine/armreg.h>
+
+__FBSDID("$FreeBSD$");
+
+#if __ARM_ARCH >= 7
+#if defined(__ARM_ARCH_7VE__) || defined(__clang__)
+.arch_extension virt
+#endif
+
+ASENTRY_NP(hypervisor_stub_vect_install)
+
+ /* Install hypervisor stub vectors. */
+ adr r0, hypervisor_stub_vect
+ mcr CP15_HVBAR(r0)
+
+ /* Disable all the traps in the hypervisor. */
+ mov r0, #0
+ mcr CP15_HCR(r0)
+ mcr CP15_HCPTR(r0)
+ mcr CP15_HSTR(r0)
+ mcr CP15_HSCTLR(r0)
+
+ /* Don't disable access to perf-mon from PL0,1 and preserve HPMN. */
+ mrc CP15_HDCR(r0)
+ and r0, #(ARM_CP15_HDCR_HPMN)
+ /* Caller implicit instruction barrier in the ERET. */
+ mcr CP15_HDCR(r0)
+
+ RET
+
+END(hypervisor_stub_vect_install)
+
+ASENTRY_NP(hypervisor_stub_trap)
+ /*
+ * If the first parameter is -1 than return the
+ * exception vector (HVBAR), otherwise set it to
+ * the value of it.
+ */
+ cmp r0, #-1
+ mrceq CP15_HVBAR(r0)
+ mcrne CP15_HVBAR(r0)
+ ERET
+END(hypervisor_stub_trap)
+
+ .globl hypervisor_stub_vect
+ .align 5
+_C_LABEL(hypervisor_stub_vect):
+ .word 0 /* Reset */
+ .word 0 /* undev */
+ .word 0 /* SMC */
+ .word 0 /* PABT */
+ .word 0 /* DABT */
+ b hypervisor_stub_trap /* HYP-Mode */
+ .word 0 /* FIQ */
+ .word 0 /* IRQ */
+#endif /* __ARM_ARCH >= 7 */
+
diff --git a/sys/arm/arm/identcpu-v6.c b/sys/arm/arm/identcpu-v6.c
new file mode 100644
index 000000000000..ad23d65b6b6f
--- /dev/null
+++ b/sys/arm/arm/identcpu-v6.c
@@ -0,0 +1,381 @@
+/* $NetBSD: cpu.c,v 1.55 2004/02/13 11:36:10 wiz Exp $ */
+
+/*-
+ * Copyright (c) 1995 Mark Brinicombe.
+ * Copyright (c) 1995 Brini.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Brini.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * RiscBSD kernel project
+ *
+ * cpu.c
+ *
+ * Probing and configuration for the master CPU
+ *
+ * Created : 10/10/95
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
+#include <machine/cpu.h>
+#include <machine/md_var.h>
+
+char machine[] = "arm";
+
+SYSCTL_STRING(_hw, HW_MACHINE, machine, CTLFLAG_RD,
+ machine, 0, "Machine class");
+
+static char cpu_model[64];
+SYSCTL_STRING(_hw, HW_MODEL, model, CTLFLAG_RD,
+ cpu_model, sizeof(cpu_model), "Machine model");
+
+static char hw_buf[81];
+static int hw_buf_idx;
+static bool hw_buf_newline;
+
+enum cpu_class cpu_class = CPU_CLASS_NONE;
+
+static struct {
+ int implementer;
+ int part_number;
+ char *impl_name;
+ char *core_name;
+ enum cpu_class cpu_class;
+} cpu_names[] = {
+ {CPU_IMPLEMENTER_ARM, CPU_ARCH_ARM1176, "ARM", "ARM1176",
+ CPU_CLASS_ARM11J},
+ {CPU_IMPLEMENTER_ARM, CPU_ARCH_CORTEX_A5 , "ARM", "Cortex-A5",
+ CPU_CLASS_CORTEXA},
+ {CPU_IMPLEMENTER_ARM, CPU_ARCH_CORTEX_A7 , "ARM", "Cortex-A7",
+ CPU_CLASS_CORTEXA},
+ {CPU_IMPLEMENTER_ARM, CPU_ARCH_CORTEX_A8 , "ARM", "Cortex-A8",
+ CPU_CLASS_CORTEXA},
+ {CPU_IMPLEMENTER_ARM, CPU_ARCH_CORTEX_A9 , "ARM", "Cortex-A9",
+ CPU_CLASS_CORTEXA},
+ {CPU_IMPLEMENTER_ARM, CPU_ARCH_CORTEX_A12, "ARM", "Cortex-A12",
+ CPU_CLASS_CORTEXA},
+ {CPU_IMPLEMENTER_ARM, CPU_ARCH_CORTEX_A15, "ARM", "Cortex-A15",
+ CPU_CLASS_CORTEXA},
+ {CPU_IMPLEMENTER_ARM, CPU_ARCH_CORTEX_A17, "ARM", "Cortex-A17",
+ CPU_CLASS_CORTEXA},
+ {CPU_IMPLEMENTER_ARM, CPU_ARCH_CORTEX_A53, "ARM", "Cortex-A53",
+ CPU_CLASS_CORTEXA},
+ {CPU_IMPLEMENTER_ARM, CPU_ARCH_CORTEX_A57, "ARM", "Cortex-A57",
+ CPU_CLASS_CORTEXA},
+ {CPU_IMPLEMENTER_ARM, CPU_ARCH_CORTEX_A72, "ARM", "Cortex-A72",
+ CPU_CLASS_CORTEXA},
+ {CPU_IMPLEMENTER_ARM, CPU_ARCH_CORTEX_A73, "ARM", "Cortex-A73",
+ CPU_CLASS_CORTEXA},
+
+ {CPU_IMPLEMENTER_MRVL, CPU_ARCH_SHEEVA_581, "Marvell", "PJ4 v7",
+ CPU_CLASS_MARVELL},
+ {CPU_IMPLEMENTER_MRVL, CPU_ARCH_SHEEVA_584, "Marvell", "PJ4MP v7",
+ CPU_CLASS_MARVELL},
+
+ {CPU_IMPLEMENTER_QCOM, CPU_ARCH_KRAIT_300, "Qualcomm", "Krait 300",
+ CPU_CLASS_KRAIT},
+};
+
+static void
+print_v5_cache(void)
+{
+ uint32_t isize, dsize;
+ uint32_t multiplier;
+ int pcache_type;
+ int pcache_unified;
+ int picache_size;
+ int picache_line_size;
+ int picache_ways;
+ int pdcache_size;
+ int pdcache_line_size;
+ int pdcache_ways;
+
+ pcache_unified = 0;
+ picache_size = 0 ;
+ picache_line_size = 0 ;
+ picache_ways = 0 ;
+ pdcache_size = 0;
+ pdcache_line_size = 0;
+ pdcache_ways = 0;
+
+ if ((cpuinfo.ctr & CPU_CT_S) == 0)
+ pcache_unified = 1;
+
+ /*
+ * If you want to know how this code works, go read the ARM ARM.
+ */
+ pcache_type = CPU_CT_CTYPE(cpuinfo.ctr);
+
+ if (pcache_unified == 0) {
+ isize = CPU_CT_ISIZE(cpuinfo.ctr);
+ multiplier = (isize & CPU_CT_xSIZE_M) ? 3 : 2;
+ picache_line_size = 1U << (CPU_CT_xSIZE_LEN(isize) + 3);
+ if (CPU_CT_xSIZE_ASSOC(isize) == 0) {
+ if (isize & CPU_CT_xSIZE_M)
+ picache_line_size = 0; /* not present */
+ else
+ picache_ways = 1;
+ } else {
+ picache_ways = multiplier <<
+ (CPU_CT_xSIZE_ASSOC(isize) - 1);
+ }
+ picache_size = multiplier << (CPU_CT_xSIZE_SIZE(isize) + 8);
+ }
+
+ dsize = CPU_CT_DSIZE(cpuinfo.ctr);
+ multiplier = (dsize & CPU_CT_xSIZE_M) ? 3 : 2;
+ pdcache_line_size = 1U << (CPU_CT_xSIZE_LEN(dsize) + 3);
+ if (CPU_CT_xSIZE_ASSOC(dsize) == 0) {
+ if (dsize & CPU_CT_xSIZE_M)
+ pdcache_line_size = 0; /* not present */
+ else
+ pdcache_ways = 1;
+ } else {
+ pdcache_ways = multiplier <<
+ (CPU_CT_xSIZE_ASSOC(dsize) - 1);
+ }
+ pdcache_size = multiplier << (CPU_CT_xSIZE_SIZE(dsize) + 8);
+
+ /* Print cache info. */
+ if (picache_line_size == 0 && pdcache_line_size == 0)
+ return;
+
+ if (pcache_unified) {
+ printf(" %dKB/%dB %d-way %s unified cache\n",
+ pdcache_size / 1024,
+ pdcache_line_size, pdcache_ways,
+ pcache_type == 0 ? "WT" : "WB");
+ } else {
+ printf(" %dKB/%dB %d-way instruction cache\n",
+ picache_size / 1024,
+ picache_line_size, picache_ways);
+ printf(" %dKB/%dB %d-way %s data cache\n",
+ pdcache_size / 1024,
+ pdcache_line_size, pdcache_ways,
+ pcache_type == 0 ? "WT" : "WB");
+ }
+}
+
+static void
+print_v7_cache(void )
+{
+ uint32_t type, val, size, sets, ways, linesize;
+ int i;
+
+ printf("LoUU:%d LoC:%d LoUIS:%d \n",
+ CPU_CLIDR_LOUU(cpuinfo.clidr) + 1,
+ CPU_CLIDR_LOC(cpuinfo.clidr) + 1,
+ CPU_CLIDR_LOUIS(cpuinfo.clidr) + 1);
+
+ for (i = 0; i < 7; i++) {
+ type = CPU_CLIDR_CTYPE(cpuinfo.clidr, i);
+ if (type == 0)
+ break;
+ printf("Cache level %d:\n", i + 1);
+ if (type == CACHE_DCACHE || type == CACHE_UNI_CACHE ||
+ type == CACHE_SEP_CACHE) {
+ cp15_csselr_set(i << 1);
+ val = cp15_ccsidr_get();
+ ways = CPUV7_CT_xSIZE_ASSOC(val) + 1;
+ sets = CPUV7_CT_xSIZE_SET(val) + 1;
+ linesize = 1 << (CPUV7_CT_xSIZE_LEN(val) + 4);
+ size = (ways * sets * linesize) / 1024;
+
+ if (type == CACHE_UNI_CACHE)
+ printf(" %dKB/%dB %d-way unified cache",
+ size, linesize,ways);
+ else
+ printf(" %dKB/%dB %d-way data cache",
+ size, linesize, ways);
+ if (val & CPUV7_CT_CTYPE_WT)
+ printf(" WT");
+ if (val & CPUV7_CT_CTYPE_WB)
+ printf(" WB");
+ if (val & CPUV7_CT_CTYPE_RA)
+ printf(" Read-Alloc");
+ if (val & CPUV7_CT_CTYPE_WA)
+ printf(" Write-Alloc");
+ printf("\n");
+ }
+
+ if (type == CACHE_ICACHE || type == CACHE_SEP_CACHE) {
+ cp15_csselr_set(i << 1 | 1);
+ val = cp15_ccsidr_get();
+ ways = CPUV7_CT_xSIZE_ASSOC(val) + 1;
+ sets = CPUV7_CT_xSIZE_SET(val) + 1;
+ linesize = 1 << (CPUV7_CT_xSIZE_LEN(val) + 4);
+ size = (ways * sets * linesize) / 1024;
+ printf(" %dKB/%dB %d-way instruction cache",
+ size, linesize, ways);
+ if (val & CPUV7_CT_CTYPE_WT)
+ printf(" WT");
+ if (val & CPUV7_CT_CTYPE_WB)
+ printf(" WB");
+ if (val & CPUV7_CT_CTYPE_RA)
+ printf(" Read-Alloc");
+ if (val & CPUV7_CT_CTYPE_WA)
+ printf(" Write-Alloc");
+ printf("\n");
+ }
+ }
+ cp15_csselr_set(0);
+}
+
+static void
+add_cap(char *cap)
+{
+ int len;
+
+ len = strlen(cap);
+
+ if ((hw_buf_idx + len + 2) >= 79) {
+ printf("%s,\n", hw_buf);
+ hw_buf_idx = 0;
+ hw_buf_newline = true;
+ }
+ if (hw_buf_newline)
+ hw_buf_idx += sprintf(hw_buf + hw_buf_idx, " ");
+ else
+ hw_buf_idx += sprintf(hw_buf + hw_buf_idx, ", ");
+ hw_buf_newline = false;
+
+ hw_buf_idx += sprintf(hw_buf + hw_buf_idx, "%s", cap);
+}
+
+void
+identify_arm_cpu(void)
+{
+ int i;
+ u_int val;
+
+ /*
+ * CPU
+ */
+ for(i = 0; i < nitems(cpu_names); i++) {
+ if (cpu_names[i].implementer == cpuinfo.implementer &&
+ cpu_names[i].part_number == cpuinfo.part_number) {
+ cpu_class = cpu_names[i].cpu_class;
+ snprintf(cpu_model, sizeof(cpu_model),
+ "%s %s r%dp%d (ECO: 0x%08X)",
+ cpu_names[i].impl_name, cpu_names[i].core_name,
+ cpuinfo.revision, cpuinfo.patch,
+ cpuinfo.midr != cpuinfo.revidr ?
+ cpuinfo.revidr : 0);
+ printf("CPU: %s\n", cpu_model);
+ break;
+ }
+ }
+ if (i >= nitems(cpu_names))
+ printf("unknown CPU (ID = 0x%x)\n", cpuinfo.midr);
+
+ printf("CPU Features: \n");
+ hw_buf_idx = 0;
+ hw_buf_newline = true;
+
+ val = (cpuinfo.mpidr >> 4)& 0xF;
+ if (cpuinfo.mpidr & (1 << 31U))
+ add_cap("Multiprocessing");
+ val = (cpuinfo.id_pfr0 >> 4)& 0xF;
+ if (val == 1)
+ add_cap("Thumb");
+ else if (val == 3)
+ add_cap("Thumb2");
+
+ val = (cpuinfo.id_pfr1 >> 4)& 0xF;
+ if (val == 1 || val == 2)
+ add_cap("Security");
+
+ val = (cpuinfo.id_pfr1 >> 12)& 0xF;
+ if (val == 1)
+ add_cap("Virtualization");
+
+ val = (cpuinfo.id_pfr1 >> 16)& 0xF;
+ if (val == 1)
+ add_cap("Generic Timer");
+
+ val = (cpuinfo.id_mmfr0 >> 0)& 0xF;
+ if (val == 2) {
+ add_cap("VMSAv6");
+ } else if (val >= 3) {
+ add_cap("VMSAv7");
+ if (val >= 4)
+ add_cap("PXN");
+ if (val >= 5)
+ add_cap("LPAE");
+ }
+
+ val = (cpuinfo.id_mmfr3 >> 20)& 0xF;
+ if (val == 1)
+ add_cap("Coherent Walk");
+
+ if (hw_buf_idx != 0)
+ printf("%s\n", hw_buf);
+
+ printf("Optional instructions: \n");
+ hw_buf_idx = 0;
+ hw_buf_newline = true;
+ val = (cpuinfo.id_isar0 >> 24)& 0xF;
+ if (val == 1)
+ add_cap("SDIV/UDIV (Thumb)");
+ else if (val == 2)
+ add_cap("SDIV/UDIV");
+
+ val = (cpuinfo.id_isar2 >> 20)& 0xF;
+ if (val == 1 || val == 2)
+ add_cap("UMULL");
+
+ val = (cpuinfo.id_isar2 >> 16)& 0xF;
+ if (val == 1 || val == 2 || val == 3)
+ add_cap("SMULL");
+
+ val = (cpuinfo.id_isar2 >> 12)& 0xF;
+ if (val == 1)
+ add_cap("MLA");
+
+ val = (cpuinfo.id_isar3 >> 4)& 0xF;
+ if (val == 1)
+ add_cap("SIMD");
+ else if (val == 3)
+ add_cap("SIMD(ext)");
+ if (hw_buf_idx != 0)
+ printf("%s\n", hw_buf);
+
+ /*
+ * Cache
+ */
+ if (CPU_CT_FORMAT(cpuinfo.ctr) == CPU_CT_ARMV7)
+ print_v7_cache();
+ else
+ print_v5_cache();
+}
diff --git a/sys/arm/arm/in_cksum.c b/sys/arm/arm/in_cksum.c
new file mode 100644
index 000000000000..d37c68720d2a
--- /dev/null
+++ b/sys/arm/arm/in_cksum.c
@@ -0,0 +1,154 @@
+/* $NetBSD: in_cksum.c,v 1.7 1997/09/02 13:18:15 thorpej Exp $ */
+
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 1988, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1996
+ * Matt Thomas <matt@3am-software.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)in_cksum.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/cdefs.h> /* RCS ID & Copyright macro defns */
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/systm.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <machine/in_cksum.h>
+
+/*
+ * Checksum routine for Internet Protocol family headers
+ * (Portable Alpha version).
+ *
+ * This routine is very heavily used in the network
+ * code and should be modified for each CPU to be as fast as possible.
+ */
+
+#define ADDCARRY(x) (x > 65535 ? x -= 65535 : x)
+#define REDUCE32 \
+ { \
+ q_util.q = sum; \
+ sum = q_util.s[0] + q_util.s[1] + q_util.s[2] + q_util.s[3]; \
+ }
+#define REDUCE16 \
+ { \
+ q_util.q = sum; \
+ l_util.l = q_util.s[0] + q_util.s[1] + q_util.s[2] + q_util.s[3]; \
+ sum = l_util.s[0] + l_util.s[1]; \
+ ADDCARRY(sum); \
+ }
+
+union l_util {
+ u_int16_t s[2];
+ u_int32_t l;
+};
+union q_util {
+ u_int16_t s[4];
+ u_int32_t l[2];
+ u_int64_t q;
+};
+
+u_short
+in_addword(u_short a, u_short b)
+{
+ u_int64_t sum = a + b;
+
+ ADDCARRY(sum);
+ return (sum);
+}
+
+static
+uint64_t _do_cksum(void *addr, int len)
+{
+ uint64_t sum;
+ union q_util q_util;
+
+ sum = do_cksum(addr, len);
+ REDUCE32;
+ return (sum);
+}
+
+u_short
+in_cksum_skip(struct mbuf *m, int len, int skip)
+{
+ u_int64_t sum = 0;
+ int mlen = 0;
+ int clen = 0;
+ caddr_t addr;
+ union q_util q_util;
+ union l_util l_util;
+
+ len -= skip;
+ for (; skip && m; m = m->m_next) {
+ if (m->m_len > skip) {
+ mlen = m->m_len - skip;
+ addr = mtod(m, caddr_t) + skip;
+ goto skip_start;
+ } else {
+ skip -= m->m_len;
+ }
+ }
+
+ for (; m && len; m = m->m_next) {
+ if (m->m_len == 0)
+ continue;
+ mlen = m->m_len;
+ addr = mtod(m, caddr_t);
+skip_start:
+ if (len < mlen)
+ mlen = len;
+
+ if ((clen ^ (int) addr) & 1)
+ sum += _do_cksum(addr, mlen) << 8;
+ else
+ sum += _do_cksum(addr, mlen);
+
+ clen += mlen;
+ len -= mlen;
+ }
+ REDUCE16;
+ return (~sum & 0xffff);
+}
+
+u_int in_cksum_hdr(const struct ip *ip)
+{
+ u_int64_t sum = do_cksum(ip, sizeof(struct ip));
+ union q_util q_util;
+ union l_util l_util;
+ REDUCE16;
+ return (~sum & 0xffff);
+}
diff --git a/sys/arm/arm/in_cksum_arm.S b/sys/arm/arm/in_cksum_arm.S
new file mode 100644
index 000000000000..4f4dd7c437c1
--- /dev/null
+++ b/sys/arm/arm/in_cksum_arm.S
@@ -0,0 +1,282 @@
+/* $NetBSD: in_cksum_arm.S,v 1.2 2003/09/23 10:01:36 scw Exp $ */
+
+/*-
+ * Copyright 2003 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Steve C. Woodford for Wasabi Systems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the NetBSD Project by
+ * Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/*
+ * Hand-optimised in_cksum() and in4_cksum() implementations for ARM/armv5e
+ */
+
+#include "opt_inet.h"
+
+#include <machine/asm.h>
+#include "assym.inc"
+__FBSDID("$FreeBSD$");
+
+ .syntax unified
+/*
+ * int in_cksum(struct mbuf *m, int len)
+ *
+ * Entry:
+ * r0 m
+ * r1 len
+ *
+ * NOTE: Assumes 'm' is *never* NULL.
+ */
+/* LINTSTUB: Func: int in_cksum(struct mbuf *, int) */
+ENTRY(in_cksum)
+ stmfd sp!, {r4-r11,lr}
+ mov r8, #0x00
+ mov r9, r1
+ mov r10, #0x00
+ mov ip, r0
+
+.Lin_cksum_loop:
+ ldr r1, [ip, #(M_LEN)]
+ ldr r0, [ip, #(M_DATA)]
+ ldr ip, [ip, #(M_NEXT)]
+.Lin_cksum_entry4:
+ cmp r9, r1
+ movlt r1, r9
+ sub r9, r9, r1
+ eor r11, r10, r0
+ add r10, r10, r1
+ adds r2, r1, #0x00
+ blne _ASM_LABEL(L_cksumdata)
+ tst r11, #0x01
+ movne r2, r2, ror #8
+ adds r8, r8, r2
+ adc r8, r8, #0x00
+ cmp ip, #0x00
+ bne .Lin_cksum_loop
+
+ mov r1, #0xff
+ orr r1, r1, #0xff00
+ and r0, r8, r1
+ add r0, r0, r8, lsr #16
+ add r0, r0, r0, lsr #16
+ and r0, r0, r1
+ eor r0, r0, r1
+ ldmfd sp!, {r4-r11,pc}
+END(in_cksum)
+
+ENTRY(do_cksum)
+ stmfd sp!, {r4-r7, lr}
+ bl L_cksumdata
+ mov r0, r2
+ ldmfd sp!, {r4-r7, pc}
+END(do_cksum)
+
+/*
+ * The main in*_cksum() workhorse...
+ *
+ * Entry parameters:
+ * r0 Pointer to buffer
+ * r1 Buffer length
+ * lr Return address
+ *
+ * Returns:
+ * r2 Accumulated 32-bit sum
+ *
+ * Clobbers:
+ * r0-r7
+ */
+/* LINTSTUB: Ignore */
+ASENTRY_NP(L_cksumdata)
+ pld [r0] /* Pre-fetch the start of the buffer */
+ mov r2, #0
+
+ /* We first have to word-align the buffer. */
+ ands r7, r0, #0x03
+ beq .Lcksumdata_wordaligned
+ rsb r7, r7, #0x04
+ cmp r1, r7 /* Enough bytes left to make it? */
+ blt .Lcksumdata_endgame
+ cmp r7, #0x02
+ ldrb r4, [r0], #0x01 /* Fetch 1st byte */
+ ldrbge r5, [r0], #0x01 /* Fetch 2nd byte */
+ movlt r5, #0x00
+ ldrbgt r6, [r0], #0x01 /* Fetch 3rd byte */
+ movle r6, #0x00
+
+ /* Combine the three bytes depending on endianness and alignment */
+ orreq r2, r4, r5, lsl #8
+ orreq r2, r2, r6, lsl #16
+ orrne r2, r5, r4, lsl #8
+ orrne r2, r2, r6, lsl #24
+ subs r1, r1, r7 /* Update length */
+ RETeq /* All done? */
+
+ /* Buffer is now word aligned */
+.Lcksumdata_wordaligned:
+ cmp r1, #0x04 /* Less than 4 bytes left? */
+ blt .Lcksumdata_endgame /* Yup */
+
+ /* Now quad-align, if necessary */
+ ands r7, r0, #0x04
+ ldrne r7, [r0], #0x04
+ subne r1, r1, #0x04
+ subs r1, r1, #0x40
+ blt .Lcksumdata_bigloop_end /* Note: C flag clear if branch taken */
+
+ /*
+ * Buffer is now quad aligned. Sum 64 bytes at a time.
+ * Note: First ldrd is hoisted above the loop, together with
+ * setting r6 to zero to avoid stalling for results in the
+ * loop. (r7 is live, from above).
+ */
+ ldrd r4, [r0], #0x08
+ mov r6, #0x00
+.Lcksumdata_bigloop:
+ pld [r0, #0x18]
+ adds r2, r2, r6
+ adcs r2, r2, r7
+ ldrd r6, [r0], #0x08
+ adcs r2, r2, r4
+ adcs r2, r2, r5
+ ldrd r4, [r0], #0x08
+ adcs r2, r2, r6
+ adcs r2, r2, r7
+ ldrd r6, [r0], #0x08
+ adcs r2, r2, r4
+ adcs r2, r2, r5
+ ldrd r4, [r0], #0x08
+ adcs r2, r2, r6
+ adcs r2, r2, r7
+ pld [r0, #0x18]
+ ldrd r6, [r0], #0x08
+ adcs r2, r2, r4
+ adcs r2, r2, r5
+ ldrd r4, [r0], #0x08
+ adcs r2, r2, r6
+ adcs r2, r2, r7
+ ldrd r6, [r0], #0x08
+ adcs r2, r2, r4
+ adcs r2, r2, r5
+ adc r2, r2, #0x00
+ subs r1, r1, #0x40
+ ldrdge r4, [r0], #0x08
+ bge .Lcksumdata_bigloop
+
+ adds r2, r2, r6 /* r6/r7 still need summing */
+.Lcksumdata_bigloop_end:
+ adcs r2, r2, r7
+ adc r2, r2, #0x00
+
+ adds r1, r1, #0x40
+ RETeq
+ cmp r1, #0x20
+
+ ldrdge r4, [r0], #0x08 /* Avoid stalling pld and result */
+ blt .Lcksumdata_less_than_32
+ pld [r0, #0x18]
+ ldrd r6, [r0], #0x08
+ adds r2, r2, r4
+ adcs r2, r2, r5
+ ldrd r4, [r0], #0x08
+ adcs r2, r2, r6
+ adcs r2, r2, r7
+ ldrd r6, [r0], #0x08
+ adcs r2, r2, r4
+ adcs r2, r2, r5
+ adcs r2, r2, r6 /* XXX: Unavoidable result stall */
+ adcs r2, r2, r7
+ adc r2, r2, #0x00
+ subs r1, r1, #0x20
+ RETeq
+
+.Lcksumdata_less_than_32:
+ /* There are less than 32 bytes left */
+ and r3, r1, #0x18
+ rsb r4, r3, #0x18
+ sub r1, r1, r3
+ adds r4, r4, r4, lsr #1 /* Side effect: Clear carry flag */
+ addne pc, pc, r4
+ nop
+
+/*
+ * Note: We use ldm here, even on armv5e, since the combined issue/result
+ * latencies for ldm and ldrd are the same. Using ldm avoids needless #ifdefs.
+ */
+ /* At least 24 bytes remaining... */
+ ldmia r0!, {r4, r5}
+ adcs r2, r2, r4
+ adcs r2, r2, r5
+
+ /* At least 16 bytes remaining... */
+ ldmia r0!, {r4, r5}
+ adcs r2, r2, r4
+ adcs r2, r2, r5
+
+ /* At least 8 bytes remaining... */
+ ldmia r0!, {r4, r5}
+ adcs r2, r2, r4
+ adcs r2, r2, r5
+
+ /* Less than 8 bytes remaining... */
+ adc r2, r2, #0x00
+ subs r1, r1, #0x04
+ blt .Lcksumdata_lessthan4
+
+ ldr r4, [r0], #0x04
+ sub r1, r1, #0x04
+ adds r2, r2, r4
+ adc r2, r2, #0x00
+
+ /* Deal with < 4 bytes remaining */
+.Lcksumdata_lessthan4:
+ adds r1, r1, #0x04
+ RETeq
+
+ /* Deal with 1 to 3 remaining bytes, possibly misaligned */
+.Lcksumdata_endgame:
+ ldrb r3, [r0] /* Fetch first byte */
+ cmp r1, #0x02
+ ldrbge r4, [r0, #0x01] /* Fetch 2nd and 3rd as necessary */
+ movlt r4, #0x00
+ ldrbgt r5, [r0, #0x02]
+ movle r5, #0x00
+ /* Combine the three bytes depending on endianness and alignment */
+ tst r0, #0x01
+ orreq r3, r3, r4, lsl #8
+ orreq r3, r3, r5, lsl #16
+ orrne r3, r4, r3, lsl #8
+ orrne r3, r3, r5, lsl #24
+ adds r2, r2, r3
+ adc r2, r2, #0x00
+ RET
+END(L_cksumdata)
+
diff --git a/sys/arm/arm/locore-v6.S b/sys/arm/arm/locore-v6.S
new file mode 100644
index 000000000000..d796e6267cdc
--- /dev/null
+++ b/sys/arm/arm/locore-v6.S
@@ -0,0 +1,598 @@
+/*-
+ * Copyright 2004-2014 Olivier Houchard <cognet@FreeBSD.org>
+ * Copyright 2012-2014 Ian Lepore <ian@FreeBSD.org>
+ * Copyright 2013-2014 Andrew Turner <andrew@FreeBSD.org>
+ * Copyright 2014 Svatopluk Kraus <onwahe@gmail.com>
+ * Copyright 2014 Michal Meloun <meloun@miracle.cz>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "assym.inc"
+#include <sys/syscall.h>
+#include <machine/asm.h>
+#include <machine/asmacros.h>
+#include <machine/armreg.h>
+#include <machine/sysreg.h>
+#include <machine/pte-v6.h>
+
+__FBSDID("$FreeBSD$");
+
+/* We map 64MB of kernel unless overridden in assym.inc by the kernel option. */
+#ifndef LOCORE_MAP_MB
+#define LOCORE_MAP_MB 64
+#endif
+
+#if __ARM_ARCH >= 7
+#if defined(__ARM_ARCH_7VE__) || defined(__clang__)
+/*
+ * HYP support is in bintuils >= 2.21 and gcc >= 4.9 defines __ARM_ARCH_7VE__
+ * when enabled. llvm >= 3.6 supports it too.
+ */
+.arch_extension virt
+#endif
+#endif /* __ARM_ARCH >= 7 */
+
+/* A small statically-allocated stack used only during initarm() and AP startup. */
+#define INIT_ARM_STACK_SIZE 2048
+
+ .text
+ .align 2
+
+ .globl kernbase
+ .set kernbase,KERNVIRTADDR
+
+#if __ARM_ARCH >= 7
+#define HANDLE_HYP \
+ /* Leave HYP mode */ ;\
+ mrs r0, cpsr ;\
+ and r0, r0, #(PSR_MODE) /* Mode is in the low 5 bits of CPSR */ ;\
+ teq r0, #(PSR_HYP32_MODE) /* Hyp Mode? */ ;\
+ bne 1f ;\
+ /* Install Hypervisor Stub Exception Vector */ ;\
+ bl hypervisor_stub_vect_install ;\
+ mov r0, 0 ;\
+ adr r1, hypmode_enabled ;\
+ str r0, [r1] ;\
+ /* Ensure that IRQ, FIQ and Aborts will be disabled after eret */ ;\
+ mrs r0, cpsr ;\
+ bic r0, r0, #(PSR_MODE) ;\
+ orr r0, r0, #(PSR_SVC32_MODE) ;\
+ orr r0, r0, #(PSR_I | PSR_F | PSR_A) ;\
+ msr spsr_cxsf, r0 ;\
+ /* Exit hypervisor mode */ ;\
+ adr lr, 2f ;\
+ MSR_ELR_HYP(14) ;\
+ ERET ;\
+1: ;\
+ mov r0, -1 ;\
+ adr r1, hypmode_enabled ;\
+ str r0, [r1] ;\
+2:
+#else
+#define HANDLE_HYP
+#endif /* __ARM_ARCH >= 7 */
+
+/*
+ * On entry for FreeBSD boot ABI:
+ * r0 - metadata pointer or 0 (boothowto on AT91's boot2)
+ * r1 - if (r0 == 0) then metadata pointer
+ * On entry for Linux boot ABI:
+ * r0 - 0
+ * r1 - machine type (passed as arg2 to initarm)
+ * r2 - Pointer to a tagged list or dtb image (phys addr) (passed as arg1 initarm)
+ *
+ * For both types of boot we gather up the args, put them in a struct arm_boot_params
+ * structure and pass that to initarm.
+ */
+ .globl btext
+btext:
+ASENTRY_NP(_start)
+ STOP_UNWINDING /* Can't unwind into the bootloader! */
+
+ /* Make sure interrupts are disabled. */
+ cpsid ifa
+
+ mov r8, r0 /* 0 or boot mode from boot2 */
+ mov r9, r1 /* Save Machine type */
+ mov r10, r2 /* Save meta data */
+ mov r11, r3 /* Future expansion */
+
+ # If HYP-MODE is active, install an exception vector stub
+ HANDLE_HYP
+
+ /*
+ * Check whether data cache is enabled. If it is, then we know
+ * current tags are valid (not power-on garbage values) and there
+ * might be dirty lines that need cleaning. Disable cache to prevent
+ * new lines being allocated, then call wbinv_poc_all to clean it.
+ */
+ mrc CP15_SCTLR(r7)
+ tst r7, #CPU_CONTROL_DC_ENABLE
+ blne dcache_wbinv_poc_all
+
+ /* ! Do not write to memory between wbinv and disabling cache ! */
+
+ /*
+ * Now there are no dirty lines, but there may still be lines marked
+ * valid. Disable all caches and the MMU, and invalidate everything
+ * before setting up new page tables and re-enabling the mmu.
+ */
+1:
+ bic r7, #CPU_CONTROL_DC_ENABLE
+ bic r7, #CPU_CONTROL_AFLT_ENABLE
+ bic r7, #CPU_CONTROL_MMU_ENABLE
+ bic r7, #CPU_CONTROL_IC_ENABLE
+ bic r7, #CPU_CONTROL_BPRD_ENABLE
+ bic r7, #CPU_CONTROL_SW_ENABLE
+ orr r7, #CPU_CONTROL_UNAL_ENABLE
+ orr r7, #CPU_CONTROL_VECRELOC
+ mcr CP15_SCTLR(r7)
+ DSB
+ ISB
+ bl dcache_inv_poc_all
+ mcr CP15_ICIALLU
+ DSB
+ ISB
+
+ /*
+ * Build page table from scratch.
+ */
+
+ /*
+ * Figure out the physical address we're loaded at by assuming this
+ * entry point code is in the first L1 section and so if we clear the
+ * offset bits of the pc that will give us the section-aligned load
+ * address, which remains in r5 throughout all the following code.
+ */
+ ldr r2, =(L1_S_OFFSET)
+ bic r5, pc, r2
+
+ /* Find the delta between VA and PA, result stays in r0 throughout. */
+ adr r0, Lpagetable
+ bl translate_va_to_pa
+
+ /*
+ * First map the entire 4GB address space as VA=PA. It's mapped as
+ * normal (cached) memory because it's for things like accessing the
+ * parameters passed in from the bootloader, which might be at any
+ * physical address, different for every platform.
+ */
+ mov r1, #0
+ mov r2, #0
+ mov r3, #4096
+ bl build_pagetables
+
+ /*
+ * Next we map the kernel starting at the physical load address, mapped
+ * to the VA the kernel is linked for. The default size we map is 64MiB
+ * but it can be overridden with a kernel option.
+ */
+ mov r1, r5
+ ldr r2, =(KERNVIRTADDR)
+ ldr r3, =(LOCORE_MAP_MB)
+ bl build_pagetables
+
+ /* Create a device mapping for early_printf if specified. */
+#if defined(SOCDEV_PA) && defined(SOCDEV_VA)
+ ldr r1, =SOCDEV_PA
+ ldr r2, =SOCDEV_VA
+ mov r3, #1
+ bl build_device_pagetables
+#endif
+ bl init_mmu
+
+ /* Transition the PC from physical to virtual addressing. */
+ ldr pc, =1f
+1:
+
+ /* Setup stack, clear BSS */
+ ldr r1, =.Lstart
+ ldmia r1, {r1, r2, sp} /* Set initial stack and */
+ add sp, sp, #INIT_ARM_STACK_SIZE
+ sub r2, r2, r1 /* get zero init data */
+ mov r3, #0
+2:
+ str r3, [r1], #0x0004 /* get zero init data */
+ subs r2, r2, #4
+ bgt 2b
+
+ mov r1, #28 /* loader info size is 28 bytes also second arg */
+ subs sp, sp, r1 /* allocate arm_boot_params struct on stack */
+ mov r0, sp /* loader info pointer is first arg */
+ bic sp, sp, #7 /* align stack to 8 bytes */
+ str r1, [r0] /* Store length of loader info */
+ str r8, [r0, #4] /* Store r0 from boot loader */
+ str r9, [r0, #8] /* Store r1 from boot loader */
+ str r10, [r0, #12] /* store r2 from boot loader */
+ str r11, [r0, #16] /* store r3 from boot loader */
+ str r5, [r0, #20] /* store the physical address */
+ adr r4, Lpagetable /* load the pagetable address */
+ ldr r5, [r4, #4]
+ str r5, [r0, #24] /* store the pagetable address */
+ mov fp, #0 /* trace back starts here */
+ bl _C_LABEL(initarm) /* Off we go */
+
+ /* init arm will return the new stack pointer. */
+ mov sp, r0
+
+ bl _C_LABEL(mi_startup) /* call mi_startup()! */
+
+ ldr r0, =.Lmainreturned
+ b _C_LABEL(panic)
+ /* NOTREACHED */
+END(_start)
+
+#define VA_TO_PA_POINTER(name, table) \
+name: ;\
+ .word . ;\
+ .word table
+
+/*
+ * Returns the physical address of a magic va to pa pointer.
+ * r0 - The pagetable data pointer. This must be built using the
+ * VA_TO_PA_POINTER macro.
+ * e.g.
+ * VA_TO_PA_POINTER(Lpagetable, pagetable)
+ * ...
+ * adr r0, Lpagetable
+ * bl translate_va_to_pa
+ * r0 will now contain the physical address of pagetable
+ * r1, r2 - Trashed
+ */
+translate_va_to_pa:
+ ldr r1, [r0]
+ sub r2, r1, r0
+ /* At this point: r2 = VA - PA */
+
+ /*
+ * Find the physical address of the table. After these two
+ * instructions:
+ * r1 = va(pagetable)
+ *
+ * r0 = va(pagetable) - (VA - PA)
+ * = va(pagetable) - VA + PA
+ * = pa(pagetable)
+ */
+ ldr r1, [r0, #4]
+ sub r0, r1, r2
+ mov pc, lr
+
+/*
+ * Init MMU
+ * r0 - the table base address
+ */
+
+ASENTRY_NP(init_mmu)
+
+ /* Setup TLB and MMU registers */
+ mcr CP15_TTBR0(r0) /* Set TTB */
+ mov r0, #0
+ mcr CP15_CONTEXTIDR(r0) /* Set ASID to 0 */
+
+ /* Set the Domain Access register */
+ mov r0, #DOMAIN_CLIENT /* Only domain #0 is used */
+ mcr CP15_DACR(r0)
+
+ /*
+ * Set TEX remap registers
+ * - All is set to uncacheable memory
+ */
+ ldr r0, =0xAAAAA
+ mcr CP15_PRRR(r0)
+ mov r0, #0
+ mcr CP15_NMRR(r0)
+ mcr CP15_TLBIALL /* Flush TLB */
+ DSB
+ ISB
+
+ /* Enable MMU */
+ mrc CP15_SCTLR(r0)
+ orr r0, r0, #CPU_CONTROL_MMU_ENABLE
+ orr r0, r0, #CPU_CONTROL_V6_EXTPAGE
+ orr r0, r0, #CPU_CONTROL_TR_ENABLE
+ orr r0, r0, #CPU_CONTROL_AF_ENABLE
+ mcr CP15_SCTLR(r0)
+ DSB
+ ISB
+ mcr CP15_TLBIALL /* Flush TLB */
+ mcr CP15_BPIALL /* Flush Branch predictor */
+ DSB
+ ISB
+
+ mov pc, lr
+END(init_mmu)
+
+
+/*
+ * Init SMP coherent mode, enable caching and switch to final MMU table.
+ * Called with disabled caches
+ * r0 - The table base address
+ * r1 - clear bits for aux register
+ * r2 - set bits for aux register
+ */
+ASENTRY_NP(reinit_mmu)
+ push {r4-r11, lr}
+ mov r4, r0
+ mov r5, r1
+ mov r6, r2
+
+ /* !! Be very paranoid here !! */
+ /* !! We cannot write single bit here !! */
+
+#if 0 /* XXX writeback shouldn't be necessary */
+ /* Write back and invalidate all integrated caches */
+ bl dcache_wbinv_poc_all
+#else
+ bl dcache_inv_pou_all
+#endif
+ mcr CP15_ICIALLU
+ DSB
+ ISB
+
+ /* Set auxiliary register */
+ mrc CP15_ACTLR(r7)
+ bic r8, r7, r5 /* Mask bits */
+ eor r8, r8, r6 /* Set bits */
+ teq r7, r8
+ mcrne CP15_ACTLR(r8)
+ DSB
+ ISB
+
+ /* Enable caches. */
+ mrc CP15_SCTLR(r7)
+ orr r7, #CPU_CONTROL_DC_ENABLE
+ orr r7, #CPU_CONTROL_IC_ENABLE
+ orr r7, #CPU_CONTROL_BPRD_ENABLE
+ mcr CP15_SCTLR(r7)
+ DSB
+
+ mcr CP15_TTBR0(r4) /* Set new TTB */
+ DSB
+ ISB
+
+ mcr CP15_TLBIALL /* Flush TLB */
+ mcr CP15_BPIALL /* Flush Branch predictor */
+ DSB
+ ISB
+
+#if 0 /* XXX writeback shouldn't be necessary */
+ /* Write back and invalidate all integrated caches */
+ bl dcache_wbinv_poc_all
+#else
+ bl dcache_inv_pou_all
+#endif
+ mcr CP15_ICIALLU
+ DSB
+ ISB
+
+ pop {r4-r11, pc}
+END(reinit_mmu)
+
+
+/*
+ * Builds the page table
+ * r0 - The table base address
+ * r1 - The physical address (trashed)
+ * r2 - The virtual address (trashed)
+ * r3 - The number of 1MiB sections
+ * r4 - Trashed
+ *
+ * Addresses must be 1MiB aligned
+ */
+build_device_pagetables:
+ ldr r4, =PTE1_V|PTE1_A|PTE1_AP_KRW|TEX1_CLASS_0
+ b 1f
+build_pagetables:
+ /* Set the required page attributed */
+ ldr r4, =PTE1_V|PTE1_A|PTE1_AP_KRW|TEX1_CLASS_0
+1:
+ orr r1, r4
+
+ /* Move the virtual address to the correct bit location */
+ lsr r2, #(PTE1_SHIFT - 2)
+
+ mov r4, r3
+2:
+ str r1, [r0, r2]
+ add r2, r2, #4
+ add r1, r1, #(PTE1_SIZE)
+ adds r4, r4, #-1
+ bhi 2b
+
+ mov pc, lr
+
+VA_TO_PA_POINTER(Lpagetable, boot_pt1)
+
+ .global _C_LABEL(hypmode_enabled)
+_C_LABEL(hypmode_enabled):
+ .word 0
+
+.Lstart:
+ .word _edata /* Note that these three items are */
+ .word _ebss /* loaded with a single ldmia and */
+ .word svcstk /* must remain in order together. */
+
+.Lmainreturned:
+ .asciz "main() returned"
+ .align 2
+
+ .bss
+svcstk:
+ .space INIT_ARM_STACK_SIZE * MAXCPU
+
+/*
+ * Memory for the initial pagetable. We are unable to place this in
+ * the bss as this will be cleared after the table is loaded.
+ */
+ .section ".init_pagetable", "aw", %nobits
+ .align 14 /* 16KiB aligned */
+ .globl boot_pt1
+boot_pt1:
+ .space L1_TABLE_SIZE
+
+ .text
+ .align 2
+
+#if defined(SMP)
+
+ASENTRY_NP(mpentry)
+ /* Make sure interrupts are disabled. */
+ cpsid ifa
+
+ HANDLE_HYP
+
+ /* Setup core, disable all caches. */
+ mrc CP15_SCTLR(r0)
+ bic r0, #CPU_CONTROL_MMU_ENABLE
+ bic r0, #CPU_CONTROL_AFLT_ENABLE
+ bic r0, #CPU_CONTROL_DC_ENABLE
+ bic r0, #CPU_CONTROL_IC_ENABLE
+ bic r0, #CPU_CONTROL_BPRD_ENABLE
+ bic r0, #CPU_CONTROL_SW_ENABLE
+ orr r0, #CPU_CONTROL_UNAL_ENABLE
+ orr r0, #CPU_CONTROL_VECRELOC
+ mcr CP15_SCTLR(r0)
+ DSB
+ ISB
+
+ /* Invalidate L1 cache I+D cache */
+ bl dcache_inv_pou_all
+ mcr CP15_ICIALLU
+ DSB
+ ISB
+
+ /* Find the delta between VA and PA */
+ adr r0, Lpagetable
+ bl translate_va_to_pa
+
+ bl init_mmu
+
+ adr r1, .Lstart+8 /* Get initstack pointer from */
+ ldr sp, [r1] /* startup data. */
+ mrc CP15_MPIDR(r0) /* Get processor id number. */
+ and r0, r0, #0x0f
+ mov r1, #INIT_ARM_STACK_SIZE
+ mul r2, r1, r0 /* Point sp to initstack */
+ add sp, sp, r2 /* area for this processor. */
+
+ /* Switch to virtual addresses. */
+ ldr pc, =1f
+1:
+ mov fp, #0 /* trace back starts here */
+ bl _C_LABEL(init_secondary)/* Off we go, cpu id in r0. */
+
+ adr r0, .Lmpreturned
+ b _C_LABEL(panic)
+ /* NOTREACHED */
+END(mpentry)
+
+.Lmpreturned:
+ .asciz "init_secondary() returned"
+ .align 2
+#endif
+
+ENTRY_NP(cpu_halt)
+
+ /* XXX re-implement !!! */
+ cpsid ifa
+ bl dcache_wbinv_poc_all
+
+ ldr r4, .Lcpu_reset_address
+ ldr r4, [r4]
+ teq r4, #0
+ movne pc, r4
+1:
+ WFI
+ b 1b
+
+ /*
+ * _cpu_reset_address contains the address to branch to, to complete
+ * the cpu reset after turning the MMU off
+ * This variable is provided by the hardware specific code
+ */
+.Lcpu_reset_address:
+ .word _C_LABEL(cpu_reset_address)
+END(cpu_halt)
+
+
+/*
+ * setjump + longjmp
+ */
+ENTRY(setjmp)
+ stmia r0, {r4-r14}
+ mov r0, #0x00000000
+ RET
+END(setjmp)
+
+ENTRY(longjmp)
+ ldmia r0, {r4-r14}
+ mov r0, #0x00000001
+ RET
+END(longjmp)
+
+ .data
+ .global _C_LABEL(esym)
+_C_LABEL(esym): .word _C_LABEL(end)
+
+ENTRY_NP(abort)
+ b _C_LABEL(abort)
+END(abort)
+
+ENTRY_NP(sigcode)
+ mov r0, sp
+ add r0, r0, #SIGF_UC
+
+ /*
+ * Call the sigreturn system call.
+ *
+ * We have to load r7 manually rather than using
+ * "ldr r7, =SYS_sigreturn" to ensure the value of szsigcode is
+ * correct. Using the alternative places esigcode at the address
+ * of the data rather than the address one past the data.
+ */
+
+ ldr r7, [pc, #12] /* Load SYS_sigreturn */
+ swi SYS_sigreturn
+
+ /* Well if that failed we better exit quick ! */
+
+ ldr r7, [pc, #8] /* Load SYS_exit */
+ swi SYS_exit
+
+ /* Branch back to retry SYS_sigreturn */
+ b . - 16
+END(sigcode)
+ .word SYS_sigreturn
+ .word SYS_exit
+
+ .align 2
+ .global _C_LABEL(esigcode)
+ _C_LABEL(esigcode):
+
+ .data
+ .global szsigcode
+szsigcode:
+ .long esigcode-sigcode
+
+/* End of locore.S */
diff --git a/sys/arm/arm/locore.S b/sys/arm/arm/locore.S
new file mode 100644
index 000000000000..eba95f62e310
--- /dev/null
+++ b/sys/arm/arm/locore.S
@@ -0,0 +1,37 @@
+/*-
+ * Copyright (c) 2014 Ian Lepore <ian@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * The kernel build machinery wants the file containing the entry point to be
+ * named locore.S, but we want separate files for v4 and v6 builds, so just
+ * include the arch-appropriate file from this properly-named file.
+ */
+
+#include <machine/acle-compat.h>
+
+#include "locore-v6.S"
diff --git a/sys/arm/arm/machdep.c b/sys/arm/arm/machdep.c
new file mode 100644
index 000000000000..3d966527599d
--- /dev/null
+++ b/sys/arm/arm/machdep.c
@@ -0,0 +1,958 @@
+/* $NetBSD: arm32_machdep.c,v 1.44 2004/03/24 15:34:47 atatat Exp $ */
+
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 2004 Olivier Houchard
+ * Copyright (c) 1994-1998 Mark Brinicombe.
+ * Copyright (c) 1994 Brini.
+ * All rights reserved.
+ *
+ * This code is derived from software written for Brini by Mark Brinicombe
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Mark Brinicombe
+ * for the NetBSD Project.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Machine dependent functions for kernel setup
+ *
+ * Created : 17/09/94
+ * Updated : 18/04/01 updated for new wscons
+ */
+
+#include "opt_ddb.h"
+#include "opt_kstack_pages.h"
+#include "opt_platform.h"
+#include "opt_sched.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/buf.h>
+#include <sys/bus.h>
+#include <sys/cons.h>
+#include <sys/cpu.h>
+#include <sys/devmap.h>
+#include <sys/efi.h>
+#include <sys/imgact.h>
+#include <sys/kdb.h>
+#include <sys/kernel.h>
+#include <sys/ktr.h>
+#include <sys/linker.h>
+#include <sys/msgbuf.h>
+#include <sys/physmem.h>
+#include <sys/reboot.h>
+#include <sys/rwlock.h>
+#include <sys/sched.h>
+#include <sys/syscallsubr.h>
+#include <sys/sysent.h>
+#include <sys/sysproto.h>
+#include <sys/vmmeter.h>
+
+#include <vm/vm_object.h>
+#include <vm/vm_page.h>
+#include <vm/vm_pager.h>
+
+#include <machine/asm.h>
+#include <machine/debug_monitor.h>
+#include <machine/machdep.h>
+#include <machine/metadata.h>
+#include <machine/pcb.h>
+#include <machine/platform.h>
+#include <machine/sysarch.h>
+#include <machine/undefined.h>
+#include <machine/vfp.h>
+#include <machine/vmparam.h>
+
+#ifdef FDT
+#include <dev/fdt/fdt_common.h>
+#include <machine/ofw_machdep.h>
+#endif
+
+#ifdef DEBUG
+#define debugf(fmt, args...) printf(fmt, ##args)
+#else
+#define debugf(fmt, args...)
+#endif
+
+#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
+ defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7) || \
+ defined(COMPAT_FREEBSD9)
+#error FreeBSD/arm doesn't provide compatibility with releases prior to 10
+#endif
+
+
+#if __ARM_ARCH < 6
+#error FreeBSD requires ARMv6 or later
+#endif
+
+struct pcpu __pcpu[MAXCPU];
+struct pcpu *pcpup = &__pcpu[0];
+
+static struct trapframe proc0_tf;
+uint32_t cpu_reset_address = 0;
+int cold = 1;
+vm_offset_t vector_page;
+
+/* The address at which the kernel was loaded. Set early in initarm(). */
+vm_paddr_t arm_physmem_kernaddr;
+
+int (*_arm_memcpy)(void *, void *, int, int) = NULL;
+int (*_arm_bzero)(void *, int, int) = NULL;
+int _min_memcpy_size = 0;
+int _min_bzero_size = 0;
+
+extern int *end;
+
+#ifdef FDT
+vm_paddr_t pmap_pa;
+vm_offset_t systempage;
+vm_offset_t irqstack;
+vm_offset_t undstack;
+vm_offset_t abtstack;
+#endif /* FDT */
+
+#ifdef PLATFORM
+static delay_func *delay_impl;
+static void *delay_arg;
+#endif
+
+struct kva_md_info kmi;
+/*
+ * arm32_vector_init:
+ *
+ * Initialize the vector page, and select whether or not to
+ * relocate the vectors.
+ *
+ * NOTE: We expect the vector page to be mapped at its expected
+ * destination.
+ */
+
+extern unsigned int page0[], page0_data[];
+void
+arm_vector_init(vm_offset_t va, int which)
+{
+ unsigned int *vectors = (int *) va;
+ unsigned int *vectors_data = vectors + (page0_data - page0);
+ int vec;
+
+ /*
+ * Loop through the vectors we're taking over, and copy the
+ * vector's insn and data word.
+ */
+ for (vec = 0; vec < ARM_NVEC; vec++) {
+ if ((which & (1 << vec)) == 0) {
+ /* Don't want to take over this vector. */
+ continue;
+ }
+ vectors[vec] = page0[vec];
+ vectors_data[vec] = page0_data[vec];
+ }
+
+ /* Now sync the vectors. */
+ icache_sync(va, (ARM_NVEC * 2) * sizeof(u_int));
+
+ vector_page = va;
+}
+
+static void
+cpu_startup(void *dummy)
+{
+ struct pcb *pcb = thread0.td_pcb;
+ const unsigned int mbyte = 1024 * 1024;
+
+ identify_arm_cpu();
+
+ vm_ksubmap_init(&kmi);
+
+ /*
+ * Display the RAM layout.
+ */
+ printf("real memory = %ju (%ju MB)\n",
+ (uintmax_t)arm32_ptob(realmem),
+ (uintmax_t)arm32_ptob(realmem) / mbyte);
+ printf("avail memory = %ju (%ju MB)\n",
+ (uintmax_t)arm32_ptob(vm_free_count()),
+ (uintmax_t)arm32_ptob(vm_free_count()) / mbyte);
+ if (bootverbose) {
+ physmem_print_tables();
+ devmap_print_table();
+ }
+
+ bufinit();
+ vm_pager_bufferinit();
+ pcb->pcb_regs.sf_sp = (u_int)thread0.td_kstack +
+ USPACE_SVC_STACK_TOP;
+ pmap_set_pcb_pagedir(kernel_pmap, pcb);
+}
+
+SYSINIT(cpu, SI_SUB_CPU, SI_ORDER_FIRST, cpu_startup, NULL);
+
+/*
+ * Flush the D-cache for non-DMA I/O so that the I-cache can
+ * be made coherent later.
+ */
+void
+cpu_flush_dcache(void *ptr, size_t len)
+{
+
+ dcache_wb_poc((vm_offset_t)ptr, (vm_paddr_t)vtophys(ptr), len);
+}
+
+/* Get current clock frequency for the given cpu id. */
+int
+cpu_est_clockrate(int cpu_id, uint64_t *rate)
+{
+ struct pcpu *pc;
+
+ pc = pcpu_find(cpu_id);
+ if (pc == NULL || rate == NULL)
+ return (EINVAL);
+
+ if (pc->pc_clock == 0)
+ return (EOPNOTSUPP);
+
+ *rate = pc->pc_clock;
+
+ return (0);
+}
+
+void
+cpu_idle(int busy)
+{
+
+ CTR2(KTR_SPARE2, "cpu_idle(%d) at %d", busy, curcpu);
+ spinlock_enter();
+ if (!busy)
+ cpu_idleclock();
+ if (!sched_runnable())
+ cpu_sleep(0);
+ if (!busy)
+ cpu_activeclock();
+ spinlock_exit();
+ CTR2(KTR_SPARE2, "cpu_idle(%d) at %d done", busy, curcpu);
+}
+
+int
+cpu_idle_wakeup(int cpu)
+{
+
+ return (0);
+}
+
+void
+cpu_initclocks(void)
+{
+
+#ifdef SMP
+ if (PCPU_GET(cpuid) == 0)
+ cpu_initclocks_bsp();
+ else
+ cpu_initclocks_ap();
+#else
+ cpu_initclocks_bsp();
+#endif
+}
+
+#ifdef PLATFORM
+void
+arm_set_delay(delay_func *impl, void *arg)
+{
+
+ KASSERT(impl != NULL, ("No DELAY implementation"));
+ delay_impl = impl;
+ delay_arg = arg;
+}
+
+void
+DELAY(int usec)
+{
+
+ TSENTER();
+ delay_impl(usec, delay_arg);
+ TSEXIT();
+}
+#endif
+
+void
+cpu_pcpu_init(struct pcpu *pcpu, int cpuid, size_t size)
+{
+
+ pcpu->pc_mpidr = 0xffffffff;
+}
+
+void
+spinlock_enter(void)
+{
+ struct thread *td;
+ register_t cspr;
+
+ td = curthread;
+ if (td->td_md.md_spinlock_count == 0) {
+ cspr = disable_interrupts(PSR_I | PSR_F);
+ td->td_md.md_spinlock_count = 1;
+ td->td_md.md_saved_cspr = cspr;
+ critical_enter();
+ } else
+ td->td_md.md_spinlock_count++;
+}
+
+void
+spinlock_exit(void)
+{
+ struct thread *td;
+ register_t cspr;
+
+ td = curthread;
+ cspr = td->td_md.md_saved_cspr;
+ td->td_md.md_spinlock_count--;
+ if (td->td_md.md_spinlock_count == 0) {
+ critical_exit();
+ restore_interrupts(cspr);
+ }
+}
+
+/*
+ * Clear registers on exec
+ */
+void
+exec_setregs(struct thread *td, struct image_params *imgp, uintptr_t stack)
+{
+ struct trapframe *tf = td->td_frame;
+
+ memset(tf, 0, sizeof(*tf));
+ tf->tf_usr_sp = stack;
+ tf->tf_usr_lr = imgp->entry_addr;
+ tf->tf_svc_lr = 0x77777777;
+ tf->tf_pc = imgp->entry_addr;
+ tf->tf_spsr = PSR_USR32_MODE;
+}
+
+#ifdef VFP
+/*
+ * Get machine VFP context.
+ */
+void
+get_vfpcontext(struct thread *td, mcontext_vfp_t *vfp)
+{
+ struct pcb *pcb;
+
+ pcb = td->td_pcb;
+ if (td == curthread) {
+ critical_enter();
+ vfp_store(&pcb->pcb_vfpstate, false);
+ critical_exit();
+ } else
+ MPASS(TD_IS_SUSPENDED(td));
+ memcpy(vfp->mcv_reg, pcb->pcb_vfpstate.reg,
+ sizeof(vfp->mcv_reg));
+ vfp->mcv_fpscr = pcb->pcb_vfpstate.fpscr;
+}
+
+/*
+ * Set machine VFP context.
+ */
+void
+set_vfpcontext(struct thread *td, mcontext_vfp_t *vfp)
+{
+ struct pcb *pcb;
+
+ pcb = td->td_pcb;
+ if (td == curthread) {
+ critical_enter();
+ vfp_discard(td);
+ critical_exit();
+ } else
+ MPASS(TD_IS_SUSPENDED(td));
+ memcpy(pcb->pcb_vfpstate.reg, vfp->mcv_reg,
+ sizeof(pcb->pcb_vfpstate.reg));
+ pcb->pcb_vfpstate.fpscr = vfp->mcv_fpscr;
+}
+#endif
+
+int
+arm_get_vfpstate(struct thread *td, void *args)
+{
+ int rv;
+ struct arm_get_vfpstate_args ua;
+ mcontext_vfp_t mcontext_vfp;
+
+ rv = copyin(args, &ua, sizeof(ua));
+ if (rv != 0)
+ return (rv);
+ if (ua.mc_vfp_size != sizeof(mcontext_vfp_t))
+ return (EINVAL);
+#ifdef VFP
+ get_vfpcontext(td, &mcontext_vfp);
+#else
+ bzero(&mcontext_vfp, sizeof(mcontext_vfp));
+#endif
+
+ rv = copyout(&mcontext_vfp, ua.mc_vfp, sizeof(mcontext_vfp));
+ if (rv != 0)
+ return (rv);
+ return (0);
+}
+
+/*
+ * Get machine context.
+ */
+int
+get_mcontext(struct thread *td, mcontext_t *mcp, int clear_ret)
+{
+ struct trapframe *tf = td->td_frame;
+ __greg_t *gr = mcp->__gregs;
+
+ if (clear_ret & GET_MC_CLEAR_RET) {
+ gr[_REG_R0] = 0;
+ gr[_REG_CPSR] = tf->tf_spsr & ~PSR_C;
+ } else {
+ gr[_REG_R0] = tf->tf_r0;
+ gr[_REG_CPSR] = tf->tf_spsr;
+ }
+ gr[_REG_R1] = tf->tf_r1;
+ gr[_REG_R2] = tf->tf_r2;
+ gr[_REG_R3] = tf->tf_r3;
+ gr[_REG_R4] = tf->tf_r4;
+ gr[_REG_R5] = tf->tf_r5;
+ gr[_REG_R6] = tf->tf_r6;
+ gr[_REG_R7] = tf->tf_r7;
+ gr[_REG_R8] = tf->tf_r8;
+ gr[_REG_R9] = tf->tf_r9;
+ gr[_REG_R10] = tf->tf_r10;
+ gr[_REG_R11] = tf->tf_r11;
+ gr[_REG_R12] = tf->tf_r12;
+ gr[_REG_SP] = tf->tf_usr_sp;
+ gr[_REG_LR] = tf->tf_usr_lr;
+ gr[_REG_PC] = tf->tf_pc;
+
+ mcp->mc_vfp_size = 0;
+ mcp->mc_vfp_ptr = NULL;
+ memset(&mcp->mc_spare, 0, sizeof(mcp->mc_spare));
+
+ return (0);
+}
+
+/*
+ * Set machine context.
+ *
+ * However, we don't set any but the user modifiable flags, and we won't
+ * touch the cs selector.
+ */
+int
+set_mcontext(struct thread *td, mcontext_t *mcp)
+{
+ mcontext_vfp_t mc_vfp, *vfp;
+ struct trapframe *tf = td->td_frame;
+ const __greg_t *gr = mcp->__gregs;
+ int spsr;
+
+ /*
+ * Make sure the processor mode has not been tampered with and
+ * interrupts have not been disabled.
+ */
+ spsr = gr[_REG_CPSR];
+ if ((spsr & PSR_MODE) != PSR_USR32_MODE ||
+ (spsr & (PSR_I | PSR_F)) != 0)
+ return (EINVAL);
+
+#ifdef WITNESS
+ if (mcp->mc_vfp_size != 0 && mcp->mc_vfp_size != sizeof(mc_vfp)) {
+ printf("%s: %s: Malformed mc_vfp_size: %d (0x%08X)\n",
+ td->td_proc->p_comm, __func__,
+ mcp->mc_vfp_size, mcp->mc_vfp_size);
+ } else if (mcp->mc_vfp_size != 0 && mcp->mc_vfp_ptr == NULL) {
+ printf("%s: %s: c_vfp_size != 0 but mc_vfp_ptr == NULL\n",
+ td->td_proc->p_comm, __func__);
+ }
+#endif
+
+ if (mcp->mc_vfp_size == sizeof(mc_vfp) && mcp->mc_vfp_ptr != NULL) {
+ if (copyin(mcp->mc_vfp_ptr, &mc_vfp, sizeof(mc_vfp)) != 0)
+ return (EFAULT);
+ vfp = &mc_vfp;
+ } else {
+ vfp = NULL;
+ }
+
+ tf->tf_r0 = gr[_REG_R0];
+ tf->tf_r1 = gr[_REG_R1];
+ tf->tf_r2 = gr[_REG_R2];
+ tf->tf_r3 = gr[_REG_R3];
+ tf->tf_r4 = gr[_REG_R4];
+ tf->tf_r5 = gr[_REG_R5];
+ tf->tf_r6 = gr[_REG_R6];
+ tf->tf_r7 = gr[_REG_R7];
+ tf->tf_r8 = gr[_REG_R8];
+ tf->tf_r9 = gr[_REG_R9];
+ tf->tf_r10 = gr[_REG_R10];
+ tf->tf_r11 = gr[_REG_R11];
+ tf->tf_r12 = gr[_REG_R12];
+ tf->tf_usr_sp = gr[_REG_SP];
+ tf->tf_usr_lr = gr[_REG_LR];
+ tf->tf_pc = gr[_REG_PC];
+ tf->tf_spsr = gr[_REG_CPSR];
+#ifdef VFP
+ if (vfp != NULL)
+ set_vfpcontext(td, vfp);
+#endif
+ return (0);
+}
+
+void
+sendsig(catcher, ksi, mask)
+ sig_t catcher;
+ ksiginfo_t *ksi;
+ sigset_t *mask;
+{
+ struct thread *td;
+ struct proc *p;
+ struct trapframe *tf;
+ struct sigframe *fp, frame;
+ struct sigacts *psp;
+ struct sysentvec *sysent;
+ int onstack;
+ int sig;
+ int code;
+
+ td = curthread;
+ p = td->td_proc;
+ PROC_LOCK_ASSERT(p, MA_OWNED);
+ sig = ksi->ksi_signo;
+ code = ksi->ksi_code;
+ psp = p->p_sigacts;
+ mtx_assert(&psp->ps_mtx, MA_OWNED);
+ tf = td->td_frame;
+ onstack = sigonstack(tf->tf_usr_sp);
+
+ CTR4(KTR_SIG, "sendsig: td=%p (%s) catcher=%p sig=%d", td, p->p_comm,
+ catcher, sig);
+
+ /* Allocate and validate space for the signal handler context. */
+ if ((td->td_pflags & TDP_ALTSTACK) != 0 && !(onstack) &&
+ SIGISMEMBER(psp->ps_sigonstack, sig)) {
+ fp = (struct sigframe *)((uintptr_t)td->td_sigstk.ss_sp +
+ td->td_sigstk.ss_size);
+#if defined(COMPAT_43)
+ td->td_sigstk.ss_flags |= SS_ONSTACK;
+#endif
+ } else
+ fp = (struct sigframe *)td->td_frame->tf_usr_sp;
+
+ /* make room on the stack */
+ fp--;
+
+ /* make the stack aligned */
+ fp = (struct sigframe *)STACKALIGN(fp);
+ /* Populate the siginfo frame. */
+ bzero(&frame, sizeof(frame));
+ get_mcontext(td, &frame.sf_uc.uc_mcontext, 0);
+#ifdef VFP
+ get_vfpcontext(td, &frame.sf_vfp);
+ frame.sf_uc.uc_mcontext.mc_vfp_size = sizeof(fp->sf_vfp);
+ frame.sf_uc.uc_mcontext.mc_vfp_ptr = &fp->sf_vfp;
+#else
+ frame.sf_uc.uc_mcontext.mc_vfp_size = 0;
+ frame.sf_uc.uc_mcontext.mc_vfp_ptr = NULL;
+#endif
+ frame.sf_si = ksi->ksi_info;
+ frame.sf_uc.uc_sigmask = *mask;
+ frame.sf_uc.uc_stack = td->td_sigstk;
+ frame.sf_uc.uc_stack.ss_flags = (td->td_pflags & TDP_ALTSTACK) != 0 ?
+ (onstack ? SS_ONSTACK : 0) : SS_DISABLE;
+ mtx_unlock(&psp->ps_mtx);
+ PROC_UNLOCK(td->td_proc);
+
+ /* Copy the sigframe out to the user's stack. */
+ if (copyout(&frame, fp, sizeof(*fp)) != 0) {
+ /* Process has trashed its stack. Kill it. */
+ CTR2(KTR_SIG, "sendsig: sigexit td=%p fp=%p", td, fp);
+ PROC_LOCK(p);
+ sigexit(td, SIGILL);
+ }
+
+ /*
+ * Build context to run handler in. We invoke the handler
+ * directly, only returning via the trampoline. Note the
+ * trampoline version numbers are coordinated with machine-
+ * dependent code in libc.
+ */
+
+ tf->tf_r0 = sig;
+ tf->tf_r1 = (register_t)&fp->sf_si;
+ tf->tf_r2 = (register_t)&fp->sf_uc;
+
+ /* the trampoline uses r5 as the uc address */
+ tf->tf_r5 = (register_t)&fp->sf_uc;
+ tf->tf_pc = (register_t)catcher;
+ tf->tf_usr_sp = (register_t)fp;
+ sysent = p->p_sysent;
+ if (sysent->sv_sigcode_base != 0)
+ tf->tf_usr_lr = (register_t)sysent->sv_sigcode_base;
+ else
+ tf->tf_usr_lr = (register_t)(sysent->sv_psstrings -
+ *(sysent->sv_szsigcode));
+ /* Set the mode to enter in the signal handler */
+#if __ARM_ARCH >= 7
+ if ((register_t)catcher & 1)
+ tf->tf_spsr |= PSR_T;
+ else
+ tf->tf_spsr &= ~PSR_T;
+#endif
+
+ CTR3(KTR_SIG, "sendsig: return td=%p pc=%#x sp=%#x", td, tf->tf_usr_lr,
+ tf->tf_usr_sp);
+
+ PROC_LOCK(p);
+ mtx_lock(&psp->ps_mtx);
+}
+
+int
+sys_sigreturn(td, uap)
+ struct thread *td;
+ struct sigreturn_args /* {
+ const struct __ucontext *sigcntxp;
+ } */ *uap;
+{
+ ucontext_t uc;
+ int error;
+
+ if (uap == NULL)
+ return (EFAULT);
+ if (copyin(uap->sigcntxp, &uc, sizeof(uc)))
+ return (EFAULT);
+ /* Restore register context. */
+ error = set_mcontext(td, &uc.uc_mcontext);
+ if (error != 0)
+ return (error);
+
+ /* Restore signal mask. */
+ kern_sigprocmask(td, SIG_SETMASK, &uc.uc_sigmask, NULL, 0);
+
+ return (EJUSTRETURN);
+}
+
+/*
+ * Construct a PCB from a trapframe. This is called from kdb_trap() where
+ * we want to start a backtrace from the function that caused us to enter
+ * the debugger. We have the context in the trapframe, but base the trace
+ * on the PCB. The PCB doesn't have to be perfect, as long as it contains
+ * enough for a backtrace.
+ */
+void
+makectx(struct trapframe *tf, struct pcb *pcb)
+{
+ pcb->pcb_regs.sf_r4 = tf->tf_r4;
+ pcb->pcb_regs.sf_r5 = tf->tf_r5;
+ pcb->pcb_regs.sf_r6 = tf->tf_r6;
+ pcb->pcb_regs.sf_r7 = tf->tf_r7;
+ pcb->pcb_regs.sf_r8 = tf->tf_r8;
+ pcb->pcb_regs.sf_r9 = tf->tf_r9;
+ pcb->pcb_regs.sf_r10 = tf->tf_r10;
+ pcb->pcb_regs.sf_r11 = tf->tf_r11;
+ pcb->pcb_regs.sf_r12 = tf->tf_r12;
+ pcb->pcb_regs.sf_pc = tf->tf_pc;
+ pcb->pcb_regs.sf_lr = tf->tf_usr_lr;
+ pcb->pcb_regs.sf_sp = tf->tf_usr_sp;
+}
+
+void
+pcpu0_init(void)
+{
+ set_curthread(&thread0);
+ pcpu_init(pcpup, 0, sizeof(struct pcpu));
+ pcpup->pc_mpidr = cp15_mpidr_get() & 0xFFFFFF;
+ PCPU_SET(curthread, &thread0);
+}
+
+/*
+ * Initialize proc0
+ */
+void
+init_proc0(vm_offset_t kstack)
+{
+ proc_linkup0(&proc0, &thread0);
+ thread0.td_kstack = kstack;
+ thread0.td_kstack_pages = kstack_pages;
+ thread0.td_pcb = (struct pcb *)(thread0.td_kstack +
+ thread0.td_kstack_pages * PAGE_SIZE) - 1;
+ thread0.td_pcb->pcb_flags = 0;
+ thread0.td_pcb->pcb_vfpcpu = -1;
+ thread0.td_pcb->pcb_vfpstate.fpscr = VFPSCR_DN;
+ thread0.td_frame = &proc0_tf;
+ pcpup->pc_curpcb = thread0.td_pcb;
+}
+
+void
+set_stackptrs(int cpu)
+{
+
+ set_stackptr(PSR_IRQ32_MODE,
+ irqstack + ((IRQ_STACK_SIZE * PAGE_SIZE) * (cpu + 1)));
+ set_stackptr(PSR_ABT32_MODE,
+ abtstack + ((ABT_STACK_SIZE * PAGE_SIZE) * (cpu + 1)));
+ set_stackptr(PSR_UND32_MODE,
+ undstack + ((UND_STACK_SIZE * PAGE_SIZE) * (cpu + 1)));
+}
+
+static void
+arm_kdb_init(void)
+{
+
+ kdb_init();
+#ifdef KDB
+ if (boothowto & RB_KDB)
+ kdb_enter(KDB_WHY_BOOTFLAGS, "Boot flags requested debugger");
+#endif
+}
+
+#ifdef FDT
+void *
+initarm(struct arm_boot_params *abp)
+{
+ struct mem_region mem_regions[FDT_MEM_REGIONS];
+ vm_paddr_t lastaddr;
+ vm_offset_t dtbp, kernelstack, dpcpu;
+ char *env;
+ void *kmdp;
+ int err_devmap, mem_regions_sz;
+ phandle_t root;
+ char dts_version[255];
+#ifdef EFI
+ struct efi_map_header *efihdr;
+#endif
+
+ /* get last allocated physical address */
+ arm_physmem_kernaddr = abp->abp_physaddr;
+ lastaddr = parse_boot_param(abp) - KERNVIRTADDR + arm_physmem_kernaddr;
+
+ set_cpufuncs();
+ cpuinfo_init();
+
+ /*
+ * Find the dtb passed in by the boot loader.
+ */
+ kmdp = preload_search_by_type("elf kernel");
+ dtbp = MD_FETCH(kmdp, MODINFOMD_DTBP, vm_offset_t);
+#if defined(FDT_DTB_STATIC)
+ /*
+ * In case the device tree blob was not retrieved (from metadata) try
+ * to use the statically embedded one.
+ */
+ if (dtbp == (vm_offset_t)NULL)
+ dtbp = (vm_offset_t)&fdt_static_dtb;
+#endif
+
+ if (OF_install(OFW_FDT, 0) == FALSE)
+ panic("Cannot install FDT");
+
+ if (OF_init((void *)dtbp) != 0)
+ panic("OF_init failed with the found device tree");
+
+#if defined(LINUX_BOOT_ABI)
+ arm_parse_fdt_bootargs();
+#endif
+
+#ifdef EFI
+ efihdr = (struct efi_map_header *)preload_search_info(kmdp,
+ MODINFO_METADATA | MODINFOMD_EFI_MAP);
+ if (efihdr != NULL) {
+ arm_add_efi_map_entries(efihdr, mem_regions, &mem_regions_sz);
+ } else
+#endif
+ {
+ /* Grab physical memory regions information from device tree. */
+ if (fdt_get_mem_regions(mem_regions, &mem_regions_sz,NULL) != 0)
+ panic("Cannot get physical memory regions");
+ }
+ physmem_hardware_regions(mem_regions, mem_regions_sz);
+
+ /* Grab reserved memory regions information from device tree. */
+ if (fdt_get_reserved_regions(mem_regions, &mem_regions_sz) == 0)
+ physmem_exclude_regions(mem_regions, mem_regions_sz,
+ EXFLAG_NODUMP | EXFLAG_NOALLOC);
+
+ /*
+ * Set TEX remapping registers.
+ * Setup kernel page tables and switch to kernel L1 page table.
+ */
+ pmap_set_tex();
+ pmap_bootstrap_prepare(lastaddr);
+
+ /*
+ * If EARLY_PRINTF support is enabled, we need to re-establish the
+ * mapping after pmap_bootstrap_prepare() switches to new page tables.
+ * Note that we can only do the remapping if the VA is outside the
+ * kernel, now that we have real virtual (not VA=PA) mappings in effect.
+ * Early printf does not work between the time pmap_set_tex() does
+ * cp15_prrr_set() and this code remaps the VA.
+ */
+#if defined(EARLY_PRINTF) && defined(SOCDEV_PA) && defined(SOCDEV_VA) && SOCDEV_VA < KERNBASE
+ pmap_preboot_map_attr(SOCDEV_PA, SOCDEV_VA, 1024 * 1024,
+ VM_PROT_READ | VM_PROT_WRITE, VM_MEMATTR_DEVICE);
+#endif
+
+ /*
+ * Now that proper page tables are installed, call cpu_setup() to enable
+ * instruction and data caches and other chip-specific features.
+ */
+ cpu_setup();
+
+ /* Platform-specific initialisation */
+ platform_probe_and_attach();
+ pcpu0_init();
+
+ /* Do basic tuning, hz etc */
+ init_param1();
+
+ /*
+ * Allocate a page for the system page mapped to 0xffff0000
+ * This page will just contain the system vectors and can be
+ * shared by all processes.
+ */
+ systempage = pmap_preboot_get_pages(1);
+
+ /* Map the vector page. */
+ pmap_preboot_map_pages(systempage, ARM_VECTORS_HIGH, 1);
+ if (virtual_end >= ARM_VECTORS_HIGH)
+ virtual_end = ARM_VECTORS_HIGH - 1;
+
+ /* Allocate dynamic per-cpu area. */
+ dpcpu = pmap_preboot_get_vpages(DPCPU_SIZE / PAGE_SIZE);
+ dpcpu_init((void *)dpcpu, 0);
+
+ /* Allocate stacks for all modes */
+ irqstack = pmap_preboot_get_vpages(IRQ_STACK_SIZE * MAXCPU);
+ abtstack = pmap_preboot_get_vpages(ABT_STACK_SIZE * MAXCPU);
+ undstack = pmap_preboot_get_vpages(UND_STACK_SIZE * MAXCPU );
+ kernelstack = pmap_preboot_get_vpages(kstack_pages);
+
+ /* Allocate message buffer. */
+ msgbufp = (void *)pmap_preboot_get_vpages(
+ round_page(msgbufsize) / PAGE_SIZE);
+
+ /*
+ * Pages were allocated during the secondary bootstrap for the
+ * stacks for different CPU modes.
+ * We must now set the r13 registers in the different CPU modes to
+ * point to these stacks.
+ * Since the ARM stacks use STMFD etc. we must set r13 to the top end
+ * of the stack memory.
+ */
+ set_stackptrs(0);
+ mutex_init();
+
+ /* Establish static device mappings. */
+ err_devmap = platform_devmap_init();
+ devmap_bootstrap(0, NULL);
+ vm_max_kernel_address = platform_lastaddr();
+
+ /*
+ * Only after the SOC registers block is mapped we can perform device
+ * tree fixups, as they may attempt to read parameters from hardware.
+ */
+ OF_interpret("perform-fixup", 0);
+ platform_gpio_init();
+ cninit();
+
+ /*
+ * If we made a mapping for EARLY_PRINTF after pmap_bootstrap_prepare(),
+ * undo it now that the normal console printf works.
+ */
+#if defined(EARLY_PRINTF) && defined(SOCDEV_PA) && defined(SOCDEV_VA) && SOCDEV_VA < KERNBASE
+ pmap_kremove(SOCDEV_VA);
+#endif
+
+ debugf("initarm: console initialized\n");
+ debugf(" arg1 kmdp = 0x%08x\n", (uint32_t)kmdp);
+ debugf(" boothowto = 0x%08x\n", boothowto);
+ debugf(" dtbp = 0x%08x\n", (uint32_t)dtbp);
+ debugf(" lastaddr1: 0x%08x\n", lastaddr);
+ arm_print_kenv();
+
+ env = kern_getenv("kernelname");
+ if (env != NULL)
+ strlcpy(kernelname, env, sizeof(kernelname));
+
+ if (err_devmap != 0)
+ printf("WARNING: could not fully configure devmap, error=%d\n",
+ err_devmap);
+
+ platform_late_init();
+
+ root = OF_finddevice("/");
+ if (OF_getprop(root, "freebsd,dts-version", dts_version, sizeof(dts_version)) > 0) {
+ if (strcmp(LINUX_DTS_VERSION, dts_version) != 0)
+ printf("WARNING: DTB version is %s while kernel expects %s, "
+ "please update the DTB in the ESP\n",
+ dts_version,
+ LINUX_DTS_VERSION);
+ } else {
+ printf("WARNING: Cannot find freebsd,dts-version property, "
+ "cannot check DTB compliance\n");
+ }
+
+ /*
+ * We must now clean the cache again....
+ * Cleaning may be done by reading new data to displace any
+ * dirty data in the cache. This will have happened in cpu_setttb()
+ * but since we are boot strapping the addresses used for the read
+ * may have just been remapped and thus the cache could be out
+ * of sync. A re-clean after the switch will cure this.
+ * After booting there are no gross relocations of the kernel thus
+ * this problem will not occur after initarm().
+ */
+ /* Set stack for exception handlers */
+ undefined_init();
+ init_proc0(kernelstack);
+ arm_vector_init(ARM_VECTORS_HIGH, ARM_VEC_ALL);
+ enable_interrupts(PSR_A);
+ pmap_bootstrap(0);
+
+ /* Exclude the kernel (and all the things we allocated which immediately
+ * follow the kernel) from the VM allocation pool but not from crash
+ * dumps. virtual_avail is a global variable which tracks the kva we've
+ * "allocated" while setting up pmaps.
+ *
+ * Prepare the list of physical memory available to the vm subsystem.
+ */
+ physmem_exclude_region(abp->abp_physaddr,
+ pmap_preboot_get_pages(0) - abp->abp_physaddr, EXFLAG_NOALLOC);
+ physmem_init_kernel_globals();
+
+ init_param2(physmem);
+ /* Init message buffer. */
+ msgbufinit(msgbufp, msgbufsize);
+ dbg_monitor_init();
+ arm_kdb_init();
+ /* Apply possible BP hardening. */
+ cpuinfo_init_bp_hardening();
+ return ((void *)STACKALIGN(thread0.td_pcb));
+
+}
+#endif /* FDT */
diff --git a/sys/arm/arm/machdep_boot.c b/sys/arm/arm/machdep_boot.c
new file mode 100644
index 000000000000..b13cf2ef23ea
--- /dev/null
+++ b/sys/arm/arm/machdep_boot.c
@@ -0,0 +1,499 @@
+/*-
+ * Copyright (c) 2004 Olivier Houchard
+ * Copyright (c) 1994-1998 Mark Brinicombe.
+ * Copyright (c) 1994 Brini.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "opt_platform.h"
+#include "opt_ddb.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/ctype.h>
+#include <sys/linker.h>
+#include <sys/physmem.h>
+#include <sys/reboot.h>
+#include <sys/sysctl.h>
+#if defined(LINUX_BOOT_ABI)
+#include <sys/boot.h>
+#endif
+
+#include <machine/atags.h>
+#include <machine/cpu.h>
+#include <machine/machdep.h>
+#include <machine/metadata.h>
+#include <machine/vmparam.h> /* For KERNVIRTADDR */
+
+#ifdef FDT
+#include <contrib/libfdt/libfdt.h>
+#include <dev/fdt/fdt_common.h>
+#endif
+
+#ifdef EFI
+#include <sys/efi.h>
+#endif
+
+#ifdef DDB
+#include <ddb/ddb.h>
+#endif
+
+#ifdef DEBUG
+#define debugf(fmt, args...) printf(fmt, ##args)
+#else
+#define debugf(fmt, args...)
+#endif
+
+#ifdef LINUX_BOOT_ABI
+static char static_kenv[4096];
+#endif
+
+extern int *end;
+
+static uint32_t board_revision;
+/* hex representation of uint64_t */
+static char board_serial[32];
+static char *loader_envp;
+
+#if defined(LINUX_BOOT_ABI)
+#define LBABI_MAX_BANKS 10
+#define CMDLINE_GUARD "FreeBSD:"
+static uint32_t board_id;
+static struct arm_lbabi_tag *atag_list;
+static char linux_command_line[LBABI_MAX_COMMAND_LINE + 1];
+static char atags[LBABI_MAX_COMMAND_LINE * 2];
+#endif /* defined(LINUX_BOOT_ABI) */
+
+SYSCTL_NODE(_hw, OID_AUTO, board, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
+ "Board attributes");
+SYSCTL_UINT(_hw_board, OID_AUTO, revision, CTLFLAG_RD,
+ &board_revision, 0, "Board revision");
+SYSCTL_STRING(_hw_board, OID_AUTO, serial, CTLFLAG_RD,
+ board_serial, 0, "Board serial");
+
+int vfp_exists;
+SYSCTL_INT(_hw, HW_FLOATINGPT, floatingpoint, CTLFLAG_RD,
+ &vfp_exists, 0, "Floating point support enabled");
+
+void
+board_set_serial(uint64_t serial)
+{
+
+ snprintf(board_serial, sizeof(board_serial)-1,
+ "%016jx", serial);
+}
+
+void
+board_set_revision(uint32_t revision)
+{
+
+ board_revision = revision;
+}
+
+static char *
+kenv_next(char *cp)
+{
+
+ if (cp != NULL) {
+ while (*cp != 0)
+ cp++;
+ cp++;
+ if (*cp == 0)
+ cp = NULL;
+ }
+ return (cp);
+}
+
+void
+arm_print_kenv(void)
+{
+ char *cp;
+
+ debugf("loader passed (static) kenv:\n");
+ if (loader_envp == NULL) {
+ debugf(" no env, null ptr\n");
+ return;
+ }
+ debugf(" loader_envp = 0x%08x\n", (uint32_t)loader_envp);
+
+ for (cp = loader_envp; cp != NULL; cp = kenv_next(cp))
+ debugf(" %x %s\n", (uint32_t)cp, cp);
+}
+
+#if defined(LINUX_BOOT_ABI)
+
+/* Convert the U-Boot command line into FreeBSD kenv and boot options. */
+static void
+cmdline_set_env(char *cmdline, const char *guard)
+{
+ size_t guard_len;
+
+ /* Skip leading spaces. */
+ while (isspace(*cmdline))
+ cmdline++;
+
+ /* Test and remove guard. */
+ if (guard != NULL && guard[0] != '\0') {
+ guard_len = strlen(guard);
+ if (strncasecmp(cmdline, guard, guard_len) != 0)
+ return;
+ cmdline += guard_len;
+ }
+
+ boothowto |= boot_parse_cmdline(cmdline);
+}
+
+/*
+ * Called for armv6 and newer.
+ */
+void arm_parse_fdt_bootargs(void)
+{
+
+#ifdef FDT
+ if (loader_envp == NULL && fdt_get_chosen_bootargs(linux_command_line,
+ LBABI_MAX_COMMAND_LINE) == 0) {
+ init_static_kenv(static_kenv, sizeof(static_kenv));
+ cmdline_set_env(linux_command_line, CMDLINE_GUARD);
+ }
+#endif
+}
+
+/*
+ * Called for armv[45].
+ */
+static vm_offset_t
+linux_parse_boot_param(struct arm_boot_params *abp)
+{
+ struct arm_lbabi_tag *walker;
+ uint32_t revision;
+ uint64_t serial;
+ int size;
+ vm_offset_t lastaddr;
+#ifdef FDT
+ struct fdt_header *dtb_ptr;
+ uint32_t dtb_size;
+#endif
+
+ /*
+ * Linux boot ABI: r0 = 0, r1 is the board type (!= 0) and r2
+ * is atags or dtb pointer. If all of these aren't satisfied,
+ * then punt. Unfortunately, it looks like DT enabled kernels
+ * doesn't uses board type and U-Boot delivers 0 in r1 for them.
+ */
+ if (abp->abp_r0 != 0 || abp->abp_r2 == 0)
+ return (0);
+#ifdef FDT
+ /* Test if r2 point to valid DTB. */
+ dtb_ptr = (struct fdt_header *)abp->abp_r2;
+ if (fdt_check_header(dtb_ptr) == 0) {
+ dtb_size = fdt_totalsize(dtb_ptr);
+ return (fake_preload_metadata(abp, dtb_ptr, dtb_size));
+ }
+#endif
+
+ board_id = abp->abp_r1;
+ walker = (struct arm_lbabi_tag *)abp->abp_r2;
+
+ if (ATAG_TAG(walker) != ATAG_CORE)
+ return 0;
+
+ atag_list = walker;
+ while (ATAG_TAG(walker) != ATAG_NONE) {
+ switch (ATAG_TAG(walker)) {
+ case ATAG_CORE:
+ break;
+ case ATAG_MEM:
+ physmem_hardware_region(walker->u.tag_mem.start,
+ walker->u.tag_mem.size);
+ break;
+ case ATAG_INITRD2:
+ break;
+ case ATAG_SERIAL:
+ serial = walker->u.tag_sn.high;
+ serial <<= 32;
+ serial |= walker->u.tag_sn.low;
+ board_set_serial(serial);
+ break;
+ case ATAG_REVISION:
+ revision = walker->u.tag_rev.rev;
+ board_set_revision(revision);
+ break;
+ case ATAG_CMDLINE:
+ size = ATAG_SIZE(walker) -
+ sizeof(struct arm_lbabi_header);
+ size = min(size, LBABI_MAX_COMMAND_LINE);
+ strncpy(linux_command_line, walker->u.tag_cmd.command,
+ size);
+ linux_command_line[size] = '\0';
+ break;
+ default:
+ break;
+ }
+ walker = ATAG_NEXT(walker);
+ }
+
+ /* Save a copy for later */
+ bcopy(atag_list, atags,
+ (char *)walker - (char *)atag_list + ATAG_SIZE(walker));
+
+ lastaddr = fake_preload_metadata(abp, NULL, 0);
+ init_static_kenv(static_kenv, sizeof(static_kenv));
+ cmdline_set_env(linux_command_line, CMDLINE_GUARD);
+ return lastaddr;
+}
+#endif
+
+#if defined(FREEBSD_BOOT_LOADER)
+static vm_offset_t
+freebsd_parse_boot_param(struct arm_boot_params *abp)
+{
+ vm_offset_t lastaddr = 0;
+ void *mdp;
+ void *kmdp;
+#ifdef DDB
+ vm_offset_t ksym_start;
+ vm_offset_t ksym_end;
+#endif
+
+ /*
+ * Mask metadata pointer: it is supposed to be on page boundary. If
+ * the first argument (mdp) doesn't point to a valid address the
+ * bootloader must have passed us something else than the metadata
+ * ptr, so we give up. Also give up if we cannot find metadta section
+ * the loader creates that we get all this data out of.
+ */
+
+ if ((mdp = (void *)(abp->abp_r0 & ~PAGE_MASK)) == NULL)
+ return 0;
+ preload_metadata = mdp;
+ kmdp = preload_search_by_type("elf kernel");
+ if (kmdp == NULL)
+ return 0;
+
+ boothowto = MD_FETCH(kmdp, MODINFOMD_HOWTO, int);
+ loader_envp = MD_FETCH(kmdp, MODINFOMD_ENVP, char *);
+ init_static_kenv(loader_envp, 0);
+ lastaddr = MD_FETCH(kmdp, MODINFOMD_KERNEND, vm_offset_t);
+#ifdef DDB
+ ksym_start = MD_FETCH(kmdp, MODINFOMD_SSYM, uintptr_t);
+ ksym_end = MD_FETCH(kmdp, MODINFOMD_ESYM, uintptr_t);
+ db_fetch_ksymtab(ksym_start, ksym_end, 0);
+#endif
+ return lastaddr;
+}
+#endif
+
+vm_offset_t
+default_parse_boot_param(struct arm_boot_params *abp)
+{
+ vm_offset_t lastaddr;
+
+#if defined(LINUX_BOOT_ABI)
+ if ((lastaddr = linux_parse_boot_param(abp)) != 0)
+ return lastaddr;
+#endif
+#if defined(FREEBSD_BOOT_LOADER)
+ if ((lastaddr = freebsd_parse_boot_param(abp)) != 0)
+ return lastaddr;
+#endif
+ /* Fall back to hardcoded metadata. */
+ lastaddr = fake_preload_metadata(abp, NULL, 0);
+
+ return lastaddr;
+}
+
+/*
+ * Stub version of the boot parameter parsing routine. We are
+ * called early in initarm, before even VM has been initialized.
+ * This routine needs to preserve any data that the boot loader
+ * has passed in before the kernel starts to grow past the end
+ * of the BSS, traditionally the place boot-loaders put this data.
+ *
+ * Since this is called so early, things that depend on the vm system
+ * being setup (including access to some SoC's serial ports), about
+ * all that can be done in this routine is to copy the arguments.
+ *
+ * This is the default boot parameter parsing routine. Individual
+ * kernels/boards can override this weak function with one of their
+ * own. We just fake metadata...
+ */
+__weak_reference(default_parse_boot_param, parse_boot_param);
+
+/*
+ * Fake up a boot descriptor table
+ */
+vm_offset_t
+fake_preload_metadata(struct arm_boot_params *abp __unused, void *dtb_ptr,
+ size_t dtb_size)
+{
+ vm_offset_t lastaddr;
+ int i = 0;
+ static uint32_t fake_preload[35];
+
+ lastaddr = (vm_offset_t)&end;
+
+ fake_preload[i++] = MODINFO_NAME;
+ fake_preload[i++] = strlen("kernel") + 1;
+ strcpy((char*)&fake_preload[i++], "kernel");
+ i += 1;
+ fake_preload[i++] = MODINFO_TYPE;
+ fake_preload[i++] = strlen("elf kernel") + 1;
+ strcpy((char*)&fake_preload[i++], "elf kernel");
+ i += 2;
+ fake_preload[i++] = MODINFO_ADDR;
+ fake_preload[i++] = sizeof(vm_offset_t);
+ fake_preload[i++] = KERNVIRTADDR;
+ fake_preload[i++] = MODINFO_SIZE;
+ fake_preload[i++] = sizeof(uint32_t);
+ fake_preload[i++] = (uint32_t)&end - KERNVIRTADDR;
+ if (dtb_ptr != NULL) {
+ /* Copy DTB to KVA space and insert it into module chain. */
+ lastaddr = roundup(lastaddr, sizeof(int));
+ fake_preload[i++] = MODINFO_METADATA | MODINFOMD_DTBP;
+ fake_preload[i++] = sizeof(uint32_t);
+ fake_preload[i++] = (uint32_t)lastaddr;
+ memmove((void *)lastaddr, dtb_ptr, dtb_size);
+ lastaddr += dtb_size;
+ lastaddr = roundup(lastaddr, sizeof(int));
+ }
+ fake_preload[i++] = 0;
+ fake_preload[i] = 0;
+ preload_metadata = (void *)fake_preload;
+
+ init_static_kenv(NULL, 0);
+
+ return (lastaddr);
+}
+
+#ifdef EFI
+void
+arm_add_efi_map_entries(struct efi_map_header *efihdr, struct mem_region *mr,
+ int *mrcnt)
+{
+ struct efi_md *map, *p;
+ const char *type;
+ size_t efisz, memory_size;
+ int ndesc, i, j;
+
+ static const char *types[] = {
+ "Reserved",
+ "LoaderCode",
+ "LoaderData",
+ "BootServicesCode",
+ "BootServicesData",
+ "RuntimeServicesCode",
+ "RuntimeServicesData",
+ "ConventionalMemory",
+ "UnusableMemory",
+ "ACPIReclaimMemory",
+ "ACPIMemoryNVS",
+ "MemoryMappedIO",
+ "MemoryMappedIOPortSpace",
+ "PalCode",
+ "PersistentMemory"
+ };
+
+ *mrcnt = 0;
+
+ /*
+ * Memory map data provided by UEFI via the GetMemoryMap
+ * Boot Services API.
+ */
+ efisz = roundup2(sizeof(struct efi_map_header), 0x10);
+ map = (struct efi_md *)((uint8_t *)efihdr + efisz);
+
+ if (efihdr->descriptor_size == 0)
+ return;
+ ndesc = efihdr->memory_size / efihdr->descriptor_size;
+
+ if (boothowto & RB_VERBOSE)
+ printf("%23s %12s %12s %8s %4s\n",
+ "Type", "Physical", "Virtual", "#Pages", "Attr");
+
+ memory_size = 0;
+ for (i = 0, j = 0, p = map; i < ndesc; i++,
+ p = efi_next_descriptor(p, efihdr->descriptor_size)) {
+ if (boothowto & RB_VERBOSE) {
+ if (p->md_type < nitems(types))
+ type = types[p->md_type];
+ else
+ type = "<INVALID>";
+ printf("%23s %012llx %12p %08llx ", type, p->md_phys,
+ p->md_virt, p->md_pages);
+ if (p->md_attr & EFI_MD_ATTR_UC)
+ printf("UC ");
+ if (p->md_attr & EFI_MD_ATTR_WC)
+ printf("WC ");
+ if (p->md_attr & EFI_MD_ATTR_WT)
+ printf("WT ");
+ if (p->md_attr & EFI_MD_ATTR_WB)
+ printf("WB ");
+ if (p->md_attr & EFI_MD_ATTR_UCE)
+ printf("UCE ");
+ if (p->md_attr & EFI_MD_ATTR_WP)
+ printf("WP ");
+ if (p->md_attr & EFI_MD_ATTR_RP)
+ printf("RP ");
+ if (p->md_attr & EFI_MD_ATTR_XP)
+ printf("XP ");
+ if (p->md_attr & EFI_MD_ATTR_NV)
+ printf("NV ");
+ if (p->md_attr & EFI_MD_ATTR_MORE_RELIABLE)
+ printf("MORE_RELIABLE ");
+ if (p->md_attr & EFI_MD_ATTR_RO)
+ printf("RO ");
+ if (p->md_attr & EFI_MD_ATTR_RT)
+ printf("RUNTIME");
+ printf("\n");
+ }
+
+ switch (p->md_type) {
+ case EFI_MD_TYPE_CODE:
+ case EFI_MD_TYPE_DATA:
+ case EFI_MD_TYPE_BS_CODE:
+ case EFI_MD_TYPE_BS_DATA:
+ case EFI_MD_TYPE_FREE:
+ /*
+ * We're allowed to use any entry with these types.
+ */
+ break;
+ default:
+ continue;
+ }
+
+ j++;
+ if (j >= FDT_MEM_REGIONS)
+ break;
+
+ mr[j].mr_start = p->md_phys;
+ mr[j].mr_size = p->md_pages * PAGE_SIZE;
+ memory_size += mr[j].mr_size;
+ }
+
+ *mrcnt = j;
+}
+#endif /* EFI */
diff --git a/sys/arm/arm/machdep_intr.c b/sys/arm/arm/machdep_intr.c
new file mode 100644
index 000000000000..e2fb34ce93c8
--- /dev/null
+++ b/sys/arm/arm/machdep_intr.c
@@ -0,0 +1,228 @@
+/*-
+ * Copyright (c) 2015-2016 Svatopluk Kraus
+ * Copyright (c) 2015-2016 Michal Meloun
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "opt_platform.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/syslog.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/bus.h>
+#include <sys/interrupt.h>
+#include <sys/conf.h>
+#include <sys/pmc.h>
+#include <sys/pmckern.h>
+#include <sys/smp.h>
+
+#include <machine/atomic.h>
+#include <machine/bus.h>
+#include <machine/intr.h>
+#include <machine/cpu.h>
+#include <machine/smp.h>
+
+#include "pic_if.h"
+
+#ifdef SMP
+#define INTR_IPI_NAMELEN (MAXCOMLEN + 1)
+
+struct intr_ipi {
+ intr_ipi_handler_t * ii_handler;
+ void * ii_handler_arg;
+ intr_ipi_send_t * ii_send;
+ void * ii_send_arg;
+ char ii_name[INTR_IPI_NAMELEN];
+ u_long * ii_count;
+};
+
+static struct intr_ipi ipi_sources[INTR_IPI_COUNT];
+#endif
+
+/*
+ * arm_irq_memory_barrier()
+ *
+ * Ensure all writes to device memory have reached devices before proceeding.
+ *
+ * This is intended to be called from the post-filter and post-thread routines
+ * of an interrupt controller implementation. A peripheral device driver should
+ * use bus_space_barrier() if it needs to ensure a write has reached the
+ * hardware for some reason other than clearing interrupt conditions.
+ *
+ * The need for this function arises from the ARM weak memory ordering model.
+ * Writes to locations mapped with the Device attribute bypass any caches, but
+ * are buffered. Multiple writes to the same device will be observed by that
+ * device in the order issued by the cpu. Writes to different devices may
+ * appear at those devices in a different order than issued by the cpu. That
+ * is, if the cpu writes to device A then device B, the write to device B could
+ * complete before the write to device A.
+ *
+ * Consider a typical device interrupt handler which services the interrupt and
+ * writes to a device status-acknowledge register to clear the interrupt before
+ * returning. That write is posted to the L2 controller which "immediately"
+ * places it in a store buffer and automatically drains that buffer. This can
+ * be less immediate than you'd think... There may be no free slots in the store
+ * buffers, so an existing buffer has to be drained first to make room. The
+ * target bus may be busy with other traffic (such as DMA for various devices),
+ * delaying the drain of the store buffer for some indeterminate time. While
+ * all this delay is happening, execution proceeds on the CPU, unwinding its way
+ * out of the interrupt call stack to the point where the interrupt driver code
+ * is ready to EOI and unmask the interrupt. The interrupt controller may be
+ * accessed via a faster bus than the hardware whose handler just ran; the write
+ * to unmask and EOI the interrupt may complete quickly while the device write
+ * to ack and clear the interrupt source is still lingering in a store buffer
+ * waiting for access to a slower bus. With the interrupt unmasked at the
+ * interrupt controller but still active at the device, as soon as interrupts
+ * are enabled on the core the device re-interrupts immediately: now you've got
+ * a spurious interrupt on your hands.
+ *
+ * The right way to fix this problem is for every device driver to use the
+ * proper bus_space_barrier() calls in its interrupt handler. For ARM a single
+ * barrier call at the end of the handler would work. This would have to be
+ * done to every driver in the system, not just arm-specific drivers.
+ *
+ * Another potential fix is to map all device memory as Strongly-Ordered rather
+ * than Device memory, which takes the store buffers out of the picture. This
+ * has a pretty big impact on overall system performance, because each strongly
+ * ordered memory access causes all L2 store buffers to be drained.
+ *
+ * A compromise solution is to have the interrupt controller implementation call
+ * this function to establish a barrier between writes to the interrupt-source
+ * device and writes to the interrupt controller device.
+ *
+ * This takes the interrupt number as an argument, and currently doesn't use it.
+ * The plan is that maybe some day there is a way to flag certain interrupts as
+ * "memory barrier safe" and we can avoid this overhead with them.
+ */
+void
+arm_irq_memory_barrier(uintptr_t irq)
+{
+
+ dsb();
+ cpu_l2cache_drain_writebuf();
+}
+
+#ifdef SMP
+static inline struct intr_ipi *
+intr_ipi_lookup(u_int ipi)
+{
+
+ if (ipi >= INTR_IPI_COUNT)
+ panic("%s: no such IPI %u", __func__, ipi);
+
+ return (&ipi_sources[ipi]);
+}
+
+void
+intr_ipi_dispatch(u_int ipi, struct trapframe *tf)
+{
+ void *arg;
+ struct intr_ipi *ii;
+
+ ii = intr_ipi_lookup(ipi);
+ if (ii->ii_count == NULL)
+ panic("%s: not setup IPI %u", __func__, ipi);
+
+ intr_ipi_increment_count(ii->ii_count, PCPU_GET(cpuid));
+
+ /*
+ * Supply ipi filter with trapframe argument
+ * if none is registered.
+ */
+ arg = ii->ii_handler_arg != NULL ? ii->ii_handler_arg : tf;
+ ii->ii_handler(arg);
+}
+
+void
+intr_ipi_send(cpuset_t cpus, u_int ipi)
+{
+ struct intr_ipi *ii;
+
+ ii = intr_ipi_lookup(ipi);
+ if (ii->ii_count == NULL)
+ panic("%s: not setup IPI %u", __func__, ipi);
+
+ ii->ii_send(ii->ii_send_arg, cpus, ipi);
+}
+
+void
+intr_ipi_setup(u_int ipi, const char *name, intr_ipi_handler_t *hand,
+ void *h_arg, intr_ipi_send_t *send, void *s_arg)
+{
+ struct intr_ipi *ii;
+
+ ii = intr_ipi_lookup(ipi);
+
+ KASSERT(hand != NULL, ("%s: ipi %u no handler", __func__, ipi));
+ KASSERT(send != NULL, ("%s: ipi %u no sender", __func__, ipi));
+ KASSERT(ii->ii_count == NULL, ("%s: ipi %u reused", __func__, ipi));
+
+ ii->ii_handler = hand;
+ ii->ii_handler_arg = h_arg;
+ ii->ii_send = send;
+ ii->ii_send_arg = s_arg;
+ strlcpy(ii->ii_name, name, INTR_IPI_NAMELEN);
+ ii->ii_count = intr_ipi_setup_counters(name);
+}
+
+/*
+ * Send IPI thru interrupt controller.
+ */
+static void
+pic_ipi_send(void *arg, cpuset_t cpus, u_int ipi)
+{
+
+ KASSERT(intr_irq_root_dev != NULL, ("%s: no root attached", __func__));
+ PIC_IPI_SEND(intr_irq_root_dev, arg, cpus, ipi);
+}
+
+/*
+ * Setup IPI handler on interrupt controller.
+ *
+ * Not SMP coherent.
+ */
+int
+intr_pic_ipi_setup(u_int ipi, const char *name, intr_ipi_handler_t *hand,
+ void *arg)
+{
+ int error;
+ struct intr_irqsrc *isrc;
+
+ KASSERT(intr_irq_root_dev != NULL, ("%s: no root attached", __func__));
+
+ error = PIC_IPI_SETUP(intr_irq_root_dev, ipi, &isrc);
+ if (error != 0)
+ return (error);
+
+ isrc->isrc_handlers++;
+ intr_ipi_setup(ipi, name, hand, arg, pic_ipi_send, isrc);
+ return (0);
+}
+#endif
diff --git a/sys/arm/arm/machdep_kdb.c b/sys/arm/arm/machdep_kdb.c
new file mode 100644
index 000000000000..4dedb72edb22
--- /dev/null
+++ b/sys/arm/arm/machdep_kdb.c
@@ -0,0 +1,144 @@
+/* $NetBSD: arm32_machdep.c,v 1.44 2004/03/24 15:34:47 atatat Exp $ */
+
+/*-
+ * Copyright (c) 2004 Olivier Houchard
+ * Copyright (c) 1994-1998 Mark Brinicombe.
+ * Copyright (c) 1994 Brini.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "opt_ddb.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+
+#include <machine/cpu.h>
+#include <machine/reg.h>
+
+#ifdef DDB
+#include <ddb/ddb.h>
+
+DB_SHOW_COMMAND(cp15, db_show_cp15)
+{
+ u_int reg;
+
+ reg = cp15_midr_get();
+ db_printf("Cpu ID: 0x%08x\n", reg);
+ reg = cp15_ctr_get();
+ db_printf("Current Cache Lvl ID: 0x%08x\n",reg);
+
+ reg = cp15_sctlr_get();
+ db_printf("Ctrl: 0x%08x\n",reg);
+ reg = cp15_actlr_get();
+ db_printf("Aux Ctrl: 0x%08x\n",reg);
+
+ reg = cp15_id_pfr0_get();
+ db_printf("Processor Feat 0: 0x%08x\n", reg);
+ reg = cp15_id_pfr1_get();
+ db_printf("Processor Feat 1: 0x%08x\n", reg);
+ reg = cp15_id_dfr0_get();
+ db_printf("Debug Feat 0: 0x%08x\n", reg);
+ reg = cp15_id_afr0_get();
+ db_printf("Auxiliary Feat 0: 0x%08x\n", reg);
+ reg = cp15_id_mmfr0_get();
+ db_printf("Memory Model Feat 0: 0x%08x\n", reg);
+ reg = cp15_id_mmfr1_get();
+ db_printf("Memory Model Feat 1: 0x%08x\n", reg);
+ reg = cp15_id_mmfr2_get();
+ db_printf("Memory Model Feat 2: 0x%08x\n", reg);
+ reg = cp15_id_mmfr3_get();
+ db_printf("Memory Model Feat 3: 0x%08x\n", reg);
+ reg = cp15_ttbr_get();
+ db_printf("TTB0: 0x%08x\n", reg);
+}
+
+DB_SHOW_COMMAND(vtop, db_show_vtop)
+{
+ u_int reg;
+
+ if (have_addr) {
+ cp15_ats1cpr_set(addr);
+ reg = cp15_par_get();
+ db_printf("Physical address reg: 0x%08x\n",reg);
+ } else
+ db_printf("show vtop <virt_addr>\n");
+}
+#endif /* DDB */
+
+int
+fill_regs(struct thread *td, struct reg *regs)
+{
+ struct trapframe *tf = td->td_frame;
+ bcopy(&tf->tf_r0, regs->r, sizeof(regs->r));
+ regs->r_sp = tf->tf_usr_sp;
+ regs->r_lr = tf->tf_usr_lr;
+ regs->r_pc = tf->tf_pc;
+ regs->r_cpsr = tf->tf_spsr;
+ return (0);
+}
+
+int
+fill_fpregs(struct thread *td, struct fpreg *regs)
+{
+ bzero(regs, sizeof(*regs));
+ return (0);
+}
+
+int
+set_regs(struct thread *td, struct reg *regs)
+{
+ struct trapframe *tf = td->td_frame;
+
+ bcopy(regs->r, &tf->tf_r0, sizeof(regs->r));
+ tf->tf_usr_sp = regs->r_sp;
+ tf->tf_usr_lr = regs->r_lr;
+ tf->tf_pc = regs->r_pc;
+ tf->tf_spsr &= ~PSR_FLAGS;
+ tf->tf_spsr |= regs->r_cpsr & PSR_FLAGS;
+ return (0);
+}
+
+int
+set_fpregs(struct thread *td, struct fpreg *regs)
+{
+ return (0);
+}
+
+int
+fill_dbregs(struct thread *td, struct dbreg *regs)
+{
+
+ bzero(regs, sizeof(*regs));
+ return (0);
+}
+
+int
+set_dbregs(struct thread *td, struct dbreg *regs)
+{
+ return (0);
+}
diff --git a/sys/arm/arm/machdep_ptrace.c b/sys/arm/arm/machdep_ptrace.c
new file mode 100644
index 000000000000..af0cf6ba2997
--- /dev/null
+++ b/sys/arm/arm/machdep_ptrace.c
@@ -0,0 +1,323 @@
+/*-
+ * Copyright (c) 2004 Olivier Houchard
+ * Copyright (c) 1994-1998 Mark Brinicombe.
+ * Copyright (c) 1994 Brini.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/ptrace.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <machine/machdep.h>
+#include <machine/db_machdep.h>
+
+static int
+ptrace_read_int(struct thread *td, vm_offset_t addr, uint32_t *v)
+{
+
+ if (proc_readmem(td, td->td_proc, addr, v, sizeof(*v)) != sizeof(*v))
+ return (ENOMEM);
+ return (0);
+}
+
+static int
+ptrace_write_int(struct thread *td, vm_offset_t addr, uint32_t v)
+{
+
+ if (proc_writemem(td, td->td_proc, addr, &v, sizeof(v)) != sizeof(v))
+ return (ENOMEM);
+ return (0);
+}
+
+static u_int
+ptrace_get_usr_reg(void *cookie, int reg)
+{
+ int ret;
+ struct thread *td = cookie;
+
+ KASSERT(((reg >= 0) && (reg <= ARM_REG_NUM_PC)),
+ ("reg is outside range"));
+
+ switch(reg) {
+ case ARM_REG_NUM_PC:
+ ret = td->td_frame->tf_pc;
+ break;
+ case ARM_REG_NUM_LR:
+ ret = td->td_frame->tf_usr_lr;
+ break;
+ case ARM_REG_NUM_SP:
+ ret = td->td_frame->tf_usr_sp;
+ break;
+ default:
+ ret = *((register_t*)&td->td_frame->tf_r0 + reg);
+ break;
+ }
+
+ return (ret);
+}
+
+static u_int
+ptrace_get_usr_int(void* cookie, vm_offset_t offset, u_int* val)
+{
+ struct thread *td = cookie;
+ u_int error;
+
+ error = ptrace_read_int(td, offset, val);
+
+ return (error);
+}
+
+/**
+ * This function parses current instruction opcode and decodes
+ * any possible jump (change in PC) which might occur after
+ * the instruction is executed.
+ *
+ * @param td Thread structure of analysed task
+ * @param cur_instr Currently executed instruction
+ * @param alt_next_address Pointer to the variable where
+ * the destination address of the
+ * jump instruction shall be stored.
+ *
+ * @return <0> when jump is possible
+ * <EINVAL> otherwise
+ */
+static int
+ptrace_get_alternative_next(struct thread *td, uint32_t cur_instr,
+ uint32_t *alt_next_address)
+{
+ int error;
+
+ if (inst_branch(cur_instr) || inst_call(cur_instr) ||
+ inst_return(cur_instr)) {
+ error = arm_predict_branch(td, cur_instr, td->td_frame->tf_pc,
+ alt_next_address, ptrace_get_usr_reg, ptrace_get_usr_int);
+
+ return (error);
+ }
+
+ return (EINVAL);
+}
+
+int
+ptrace_single_step(struct thread *td)
+{
+ struct proc *p;
+ int error, error_alt;
+ uint32_t cur_instr, alt_next = 0;
+
+ /* TODO: This needs to be updated for Thumb-2 */
+ if ((td->td_frame->tf_spsr & PSR_T) != 0)
+ return (EINVAL);
+
+ KASSERT(td->td_md.md_ptrace_instr == 0,
+ ("Didn't clear single step"));
+ KASSERT(td->td_md.md_ptrace_instr_alt == 0,
+ ("Didn't clear alternative single step"));
+ p = td->td_proc;
+ PROC_UNLOCK(p);
+
+ error = ptrace_read_int(td, td->td_frame->tf_pc,
+ &cur_instr);
+ if (error)
+ goto out;
+
+ error = ptrace_read_int(td, td->td_frame->tf_pc + INSN_SIZE,
+ &td->td_md.md_ptrace_instr);
+ if (error == 0) {
+ error = ptrace_write_int(td, td->td_frame->tf_pc + INSN_SIZE,
+ PTRACE_BREAKPOINT);
+ if (error) {
+ td->td_md.md_ptrace_instr = 0;
+ } else {
+ td->td_md.md_ptrace_addr = td->td_frame->tf_pc +
+ INSN_SIZE;
+ }
+ }
+
+ error_alt = ptrace_get_alternative_next(td, cur_instr, &alt_next);
+ if (error_alt == 0) {
+ error_alt = ptrace_read_int(td, alt_next,
+ &td->td_md.md_ptrace_instr_alt);
+ if (error_alt) {
+ td->td_md.md_ptrace_instr_alt = 0;
+ } else {
+ error_alt = ptrace_write_int(td, alt_next,
+ PTRACE_BREAKPOINT);
+ if (error_alt)
+ td->td_md.md_ptrace_instr_alt = 0;
+ else
+ td->td_md.md_ptrace_addr_alt = alt_next;
+ }
+ }
+
+out:
+ PROC_LOCK(p);
+ return ((error != 0) && (error_alt != 0));
+}
+
+int
+ptrace_clear_single_step(struct thread *td)
+{
+ struct proc *p;
+
+ /* TODO: This needs to be updated for Thumb-2 */
+ if ((td->td_frame->tf_spsr & PSR_T) != 0)
+ return (EINVAL);
+
+ if (td->td_md.md_ptrace_instr != 0) {
+ p = td->td_proc;
+ PROC_UNLOCK(p);
+ ptrace_write_int(td, td->td_md.md_ptrace_addr,
+ td->td_md.md_ptrace_instr);
+ PROC_LOCK(p);
+ td->td_md.md_ptrace_instr = 0;
+ }
+
+ if (td->td_md.md_ptrace_instr_alt != 0) {
+ p = td->td_proc;
+ PROC_UNLOCK(p);
+ ptrace_write_int(td, td->td_md.md_ptrace_addr_alt,
+ td->td_md.md_ptrace_instr_alt);
+ PROC_LOCK(p);
+ td->td_md.md_ptrace_instr_alt = 0;
+ }
+
+ return (0);
+}
+
+int
+ptrace_set_pc(struct thread *td, unsigned long addr)
+{
+ td->td_frame->tf_pc = addr;
+ return (0);
+}
+
+int
+arm_predict_branch(void *cookie, u_int insn, register_t pc, register_t *new_pc,
+ u_int (*fetch_reg)(void*, int),
+ u_int (*read_int)(void*, vm_offset_t, u_int*))
+{
+ u_int addr, nregs, offset = 0;
+ int error = 0;
+
+ switch ((insn >> 24) & 0xf) {
+ case 0x2: /* add pc, reg1, #value */
+ case 0x0: /* add pc, reg1, reg2, lsl #offset */
+ addr = fetch_reg(cookie, (insn >> 16) & 0xf);
+ if (((insn >> 16) & 0xf) == 15)
+ addr += 8;
+ if (insn & 0x0200000) {
+ offset = (insn >> 7) & 0x1e;
+ offset = (insn & 0xff) << (32 - offset) |
+ (insn & 0xff) >> offset;
+ } else {
+ offset = fetch_reg(cookie, insn & 0x0f);
+ if ((insn & 0x0000ff0) != 0x00000000) {
+ if (insn & 0x10)
+ nregs = fetch_reg(cookie,
+ (insn >> 8) & 0xf);
+ else
+ nregs = (insn >> 7) & 0x1f;
+ switch ((insn >> 5) & 3) {
+ case 0:
+ /* lsl */
+ offset = offset << nregs;
+ break;
+ case 1:
+ /* lsr */
+ offset = offset >> nregs;
+ break;
+ default:
+ break; /* XXX */
+ }
+ }
+ *new_pc = addr + offset;
+ return (0);
+ }
+
+ case 0xa: /* b ... */
+ case 0xb: /* bl ... */
+ addr = ((insn << 2) & 0x03ffffff);
+ if (addr & 0x02000000)
+ addr |= 0xfc000000;
+ *new_pc = (pc + 8 + addr);
+ return (0);
+ case 0x7: /* ldr pc, [pc, reg, lsl #2] */
+ addr = fetch_reg(cookie, insn & 0xf);
+ addr = pc + 8 + (addr << 2);
+ error = read_int(cookie, addr, &addr);
+ *new_pc = addr;
+ return (error);
+ case 0x1: /* mov pc, reg */
+ *new_pc = fetch_reg(cookie, insn & 0xf);
+ return (0);
+ case 0x4:
+ case 0x5: /* ldr pc, [reg] */
+ addr = fetch_reg(cookie, (insn >> 16) & 0xf);
+ /* ldr pc, [reg, #offset] */
+ if (insn & (1 << 24))
+ offset = insn & 0xfff;
+ if (insn & 0x00800000)
+ addr += offset;
+ else
+ addr -= offset;
+ error = read_int(cookie, addr, &addr);
+ *new_pc = addr;
+
+ return (error);
+ case 0x8: /* ldmxx reg, {..., pc} */
+ case 0x9:
+ addr = fetch_reg(cookie, (insn >> 16) & 0xf);
+ nregs = (insn & 0x5555) + ((insn >> 1) & 0x5555);
+ nregs = (nregs & 0x3333) + ((nregs >> 2) & 0x3333);
+ nregs = (nregs + (nregs >> 4)) & 0x0f0f;
+ nregs = (nregs + (nregs >> 8)) & 0x001f;
+ switch ((insn >> 23) & 0x3) {
+ case 0x0: /* ldmda */
+ addr = addr - 0;
+ break;
+ case 0x1: /* ldmia */
+ addr = addr + 0 + ((nregs - 1) << 2);
+ break;
+ case 0x2: /* ldmdb */
+ addr = addr - 4;
+ break;
+ case 0x3: /* ldmib */
+ addr = addr + 4 + ((nregs - 1) << 2);
+ break;
+ }
+ error = read_int(cookie, addr, &addr);
+ *new_pc = addr;
+
+ return (error);
+ default:
+ return (EINVAL);
+ }
+}
diff --git a/sys/arm/arm/mem.c b/sys/arm/arm/mem.c
new file mode 100644
index 000000000000..b1571ee12287
--- /dev/null
+++ b/sys/arm/arm/mem.c
@@ -0,0 +1,179 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1988 University of Utah.
+ * Copyright (c) 1982, 1986, 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Systems Programming Group of the University of Utah Computer
+ * Science Department, and code derived from software contributed to
+ * Berkeley by William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: Utah $Hdr: mem.c 1.13 89/10/08$
+ * from: @(#)mem.c 7.2 (Berkeley) 5/9/91
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Memory special file
+ */
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+#include <sys/ioccom.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/memrange.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/signalvar.h>
+#include <sys/sx.h>
+#include <sys/systm.h>
+#include <sys/uio.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/vm_page.h>
+#include <vm/vm_phys.h>
+#include <vm/vm_dumpset.h>
+#include <vm/pmap.h>
+#include <vm/vm_extern.h>
+
+#include <machine/memdev.h>
+
+/*
+ * Used in /dev/mem drivers and elsewhere
+ */
+MALLOC_DEFINE(M_MEMDESC, "memdesc", "memory range descriptors");
+
+struct mem_range_softc mem_range_softc;
+
+static struct sx tmppt_lock;
+SX_SYSINIT(tmppt, &tmppt_lock, "mem4map");
+
+/* ARGSUSED */
+int
+memrw(struct cdev *dev, struct uio *uio, int flags)
+{
+ int o;
+ u_int c = 0, v;
+ struct iovec *iov;
+ int error = 0;
+ vm_offset_t addr, eaddr;
+
+ while (uio->uio_resid > 0 && error == 0) {
+ iov = uio->uio_iov;
+ if (iov->iov_len == 0) {
+ uio->uio_iov++;
+ uio->uio_iovcnt--;
+ if (uio->uio_iovcnt < 0)
+ panic("memrw");
+ continue;
+ }
+ if (dev2unit(dev) == CDEV_MINOR_MEM) {
+ int i;
+ int address_valid = 0;
+
+ v = uio->uio_offset;
+ v &= ~PAGE_MASK;
+ for (i = 0; dump_avail[i] || dump_avail[i + 1];
+ i += 2) {
+ if (v >= dump_avail[i] &&
+ v < dump_avail[i + 1]) {
+ address_valid = 1;
+ break;
+ }
+ }
+ if (!address_valid)
+ return (EINVAL);
+ sx_xlock(&tmppt_lock);
+ pmap_kenter((vm_offset_t)_tmppt, v);
+ pmap_tlb_flush(kernel_pmap, (vm_offset_t)_tmppt);
+ o = (int)uio->uio_offset & PAGE_MASK;
+ c = (u_int)(PAGE_SIZE - ((int)iov->iov_base & PAGE_MASK));
+ c = min(c, (u_int)(PAGE_SIZE - o));
+ c = min(c, (u_int)iov->iov_len);
+ error = uiomove((caddr_t)&_tmppt[o], (int)c, uio);
+ pmap_qremove((vm_offset_t)_tmppt, 1);
+ sx_xunlock(&tmppt_lock);
+ continue;
+ }
+ else if (dev2unit(dev) == CDEV_MINOR_KMEM) {
+ c = iov->iov_len;
+
+ /*
+ * Make sure that all of the pages are currently
+ * resident so that we don't create any zero-fill
+ * pages.
+ */
+ addr = trunc_page(uio->uio_offset);
+ eaddr = round_page(uio->uio_offset + c);
+
+ for (; addr < eaddr; addr += PAGE_SIZE)
+ if (pmap_extract(kernel_pmap, addr) == 0)
+ return (EFAULT);
+ if (!kernacc((caddr_t)(int)uio->uio_offset, c,
+ uio->uio_rw == UIO_READ ?
+ VM_PROT_READ : VM_PROT_WRITE))
+ return (EFAULT);
+ error = uiomove((caddr_t)(int)uio->uio_offset, (int)c, uio);
+ continue;
+ }
+ /* else panic! */
+ }
+ return (error);
+}
+
+/*
+ * allow user processes to MMAP some memory sections
+ * instead of going through read/write
+ */
+/* ARGSUSED */
+
+int
+memmmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr,
+ int prot __unused, vm_memattr_t *memattr __unused)
+{
+ if (dev2unit(dev) == CDEV_MINOR_MEM) {
+ *paddr = offset;
+ return (0);
+ }
+ return (-1);
+}
+
+int
+memioctl_md(struct cdev *dev __unused, u_long cmd __unused,
+ caddr_t data __unused, int flags __unused, struct thread *td __unused)
+{
+ return (ENOTTY);
+}
diff --git a/sys/arm/arm/minidump_machdep.c b/sys/arm/arm/minidump_machdep.c
new file mode 100644
index 000000000000..c5f9cb58302e
--- /dev/null
+++ b/sys/arm/arm/minidump_machdep.c
@@ -0,0 +1,347 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2006 Peter Wemm
+ * Copyright (c) 2008 Semihalf, Grzegorz Bernacki
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * from: FreeBSD: src/sys/i386/i386/minidump_machdep.c,v 1.6 2008/08/17 23:27:27
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_watchdog.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/cons.h>
+#include <sys/kernel.h>
+#include <sys/kerneldump.h>
+#include <sys/msgbuf.h>
+#ifdef SW_WATCHDOG
+#include <sys/watchdog.h>
+#endif
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/vm_page.h>
+#include <vm/vm_phys.h>
+#include <vm/vm_dumpset.h>
+#include <vm/pmap.h>
+#include <machine/atomic.h>
+#include <machine/cpu.h>
+#include <machine/elf.h>
+#include <machine/md_var.h>
+#include <machine/minidump.h>
+
+CTASSERT(sizeof(struct kerneldumpheader) == 512);
+
+static struct kerneldumpheader kdh;
+
+/* Handle chunked writes. */
+static size_t fragsz;
+static void *dump_va;
+static uint64_t counter, progress;
+
+static int
+is_dumpable(vm_paddr_t pa)
+{
+ vm_page_t m;
+ int i;
+
+ if ((m = vm_phys_paddr_to_vm_page(pa)) != NULL)
+ return ((m->flags & PG_NODUMP) == 0);
+ for (i = 0; dump_avail[i] != 0 || dump_avail[i + 1] != 0; i += 2) {
+ if (pa >= dump_avail[i] && pa < dump_avail[i + 1])
+ return (1);
+ }
+ return (0);
+}
+
+#define PG2MB(pgs) (((pgs) + (1 << 8) - 1) >> 8)
+
+static int
+blk_flush(struct dumperinfo *di)
+{
+ int error;
+
+ if (fragsz == 0)
+ return (0);
+
+ error = dump_append(di, dump_va, 0, fragsz);
+ fragsz = 0;
+ return (error);
+}
+
+static int
+blk_write(struct dumperinfo *di, char *ptr, vm_paddr_t pa, size_t sz)
+{
+ size_t len;
+ int error, i, c;
+ u_int maxdumpsz;
+
+ maxdumpsz = min(di->maxiosize, MAXDUMPPGS * PAGE_SIZE);
+ if (maxdumpsz == 0) /* seatbelt */
+ maxdumpsz = PAGE_SIZE;
+ error = 0;
+ if (ptr != NULL && pa != 0) {
+ printf("cant have both va and pa!\n");
+ return (EINVAL);
+ }
+ if (pa != 0) {
+ if ((sz % PAGE_SIZE) != 0) {
+ printf("size not page aligned\n");
+ return (EINVAL);
+ }
+ if ((pa & PAGE_MASK) != 0) {
+ printf("address not page aligned\n");
+ return (EINVAL);
+ }
+ }
+ if (ptr != NULL) {
+ /* Flush any pre-existing pa pages before a virtual dump. */
+ error = blk_flush(di);
+ if (error)
+ return (error);
+ }
+ while (sz) {
+ len = maxdumpsz - fragsz;
+ if (len > sz)
+ len = sz;
+ counter += len;
+ progress -= len;
+ if (counter >> 22) {
+ printf(" %lld", PG2MB(progress >> PAGE_SHIFT));
+ counter &= (1<<22) - 1;
+ }
+
+#ifdef SW_WATCHDOG
+ wdog_kern_pat(WD_LASTVAL);
+#endif
+ if (ptr) {
+ error = dump_append(di, ptr, 0, len);
+ if (error)
+ return (error);
+ ptr += len;
+ sz -= len;
+ } else {
+ for (i = 0; i < len; i += PAGE_SIZE)
+ dump_va = pmap_kenter_temporary(pa + i,
+ (i + fragsz) >> PAGE_SHIFT);
+ fragsz += len;
+ pa += len;
+ sz -= len;
+ if (fragsz == maxdumpsz) {
+ error = blk_flush(di);
+ if (error)
+ return (error);
+ }
+ }
+
+ /* Check for user abort. */
+ c = cncheckc();
+ if (c == 0x03)
+ return (ECANCELED);
+ if (c != -1)
+ printf(" (CTRL-C to abort) ");
+ }
+
+ return (0);
+}
+
+/* A buffer for general use. Its size must be one page at least. */
+static char dumpbuf[PAGE_SIZE] __aligned(sizeof(uint64_t));
+CTASSERT(sizeof(dumpbuf) % sizeof(pt2_entry_t) == 0);
+
+int
+minidumpsys(struct dumperinfo *di)
+{
+ struct minidumphdr mdhdr;
+ uint64_t dumpsize, *dump_avail_buf;
+ uint32_t ptesize;
+ uint32_t pa, prev_pa = 0, count = 0;
+ vm_offset_t va;
+ int error, i;
+ char *addr;
+
+ /*
+ * Flush caches. Note that in the SMP case this operates only on the
+ * current CPU's L1 cache. Before we reach this point, code in either
+ * the system shutdown or kernel debugger has called stop_cpus() to stop
+ * all cores other than this one. Part of the ARM handling of
+ * stop_cpus() is to call wbinv_all() on that core's local L1 cache. So
+ * by time we get to here, all that remains is to flush the L1 for the
+ * current CPU, then the L2.
+ */
+ dcache_wbinv_poc_all();
+
+ counter = 0;
+ /* Walk page table pages, set bits in vm_page_dump */
+ ptesize = 0;
+ for (va = KERNBASE; va < kernel_vm_end; va += PAGE_SIZE) {
+ pa = pmap_dump_kextract(va, NULL);
+ if (pa != 0 && is_dumpable(pa))
+ dump_add_page(pa);
+ ptesize += sizeof(pt2_entry_t);
+ }
+
+ /* Calculate dump size. */
+ dumpsize = ptesize;
+ dumpsize += round_page(msgbufp->msg_size);
+ dumpsize += round_page(nitems(dump_avail) * sizeof(uint64_t));
+ dumpsize += round_page(BITSET_SIZE(vm_page_dump_pages));
+ VM_PAGE_DUMP_FOREACH(pa) {
+ /* Clear out undumpable pages now if needed */
+ if (is_dumpable(pa))
+ dumpsize += PAGE_SIZE;
+ else
+ dump_drop_page(pa);
+ }
+ dumpsize += PAGE_SIZE;
+
+ progress = dumpsize;
+
+ /* Initialize mdhdr */
+ bzero(&mdhdr, sizeof(mdhdr));
+ strcpy(mdhdr.magic, MINIDUMP_MAGIC);
+ mdhdr.version = MINIDUMP_VERSION;
+ mdhdr.msgbufsize = msgbufp->msg_size;
+ mdhdr.bitmapsize = round_page(BITSET_SIZE(vm_page_dump_pages));
+ mdhdr.ptesize = ptesize;
+ mdhdr.kernbase = KERNBASE;
+ mdhdr.arch = __ARM_ARCH;
+ mdhdr.mmuformat = MINIDUMP_MMU_FORMAT_V6;
+ mdhdr.dumpavailsize = round_page(nitems(dump_avail) * sizeof(uint64_t));
+
+ dump_init_header(di, &kdh, KERNELDUMPMAGIC, KERNELDUMP_ARM_VERSION,
+ dumpsize);
+
+ error = dump_start(di, &kdh);
+ if (error != 0)
+ goto fail;
+
+ printf("Physical memory: %u MB\n", ptoa((uintmax_t)physmem) / 1048576);
+ printf("Dumping %llu MB:", (long long)dumpsize >> 20);
+
+ /* Dump my header */
+ bzero(dumpbuf, sizeof(dumpbuf));
+ bcopy(&mdhdr, dumpbuf, sizeof(mdhdr));
+ error = blk_write(di, dumpbuf, 0, PAGE_SIZE);
+ if (error)
+ goto fail;
+
+ /* Dump msgbuf up front */
+ error = blk_write(di, (char *)msgbufp->msg_ptr, 0,
+ round_page(msgbufp->msg_size));
+ if (error)
+ goto fail;
+
+ /* Dump dump_avail. Make a copy using 64-bit physical addresses. */
+ _Static_assert(nitems(dump_avail) * sizeof(uint64_t) <= sizeof(dumpbuf),
+ "Large dump_avail not handled");
+ bzero(dumpbuf, sizeof(dumpbuf));
+ dump_avail_buf = (uint64_t *)dumpbuf;
+ for (i = 0; dump_avail[i] != 0 || dump_avail[i + 1] != 0; i += 2) {
+ dump_avail_buf[i] = dump_avail[i];
+ dump_avail_buf[i + 1] = dump_avail[i + 1];
+ }
+ error = blk_write(di, dumpbuf, 0, PAGE_SIZE);
+ if (error)
+ goto fail;
+
+ /* Dump bitmap */
+ error = blk_write(di, (char *)vm_page_dump, 0,
+ round_page(BITSET_SIZE(vm_page_dump_pages)));
+ if (error)
+ goto fail;
+
+ /* Dump kernel page table pages */
+ addr = dumpbuf;
+ for (va = KERNBASE; va < kernel_vm_end; va += PAGE_SIZE) {
+ pmap_dump_kextract(va, (pt2_entry_t *)addr);
+ addr += sizeof(pt2_entry_t);
+ if (addr == dumpbuf + sizeof(dumpbuf)) {
+ error = blk_write(di, dumpbuf, 0, sizeof(dumpbuf));
+ if (error != 0)
+ goto fail;
+ addr = dumpbuf;
+ }
+ }
+ if (addr != dumpbuf) {
+ error = blk_write(di, dumpbuf, 0, addr - dumpbuf);
+ if (error != 0)
+ goto fail;
+ }
+
+ /* Dump memory chunks */
+ VM_PAGE_DUMP_FOREACH(pa) {
+ if (!count) {
+ prev_pa = pa;
+ count++;
+ } else {
+ if (pa == (prev_pa + count * PAGE_SIZE))
+ count++;
+ else {
+ error = blk_write(di, NULL, prev_pa,
+ count * PAGE_SIZE);
+ if (error)
+ goto fail;
+ count = 1;
+ prev_pa = pa;
+ }
+ }
+ }
+ if (count) {
+ error = blk_write(di, NULL, prev_pa, count * PAGE_SIZE);
+ if (error)
+ goto fail;
+ count = 0;
+ prev_pa = 0;
+ }
+
+ error = blk_flush(di);
+ if (error)
+ goto fail;
+
+ error = dump_finish(di, &kdh);
+ if (error != 0)
+ goto fail;
+
+ printf("\nDump complete\n");
+ return (0);
+
+fail:
+ if (error < 0)
+ error = -error;
+
+ if (error == ECANCELED)
+ printf("\nDump aborted\n");
+ else if (error == E2BIG || error == ENOSPC) {
+ printf("\nDump failed. Partition too small (about %lluMB were "
+ "needed this time).\n", (long long)dumpsize >> 20);
+ } else
+ printf("\n** DUMP FAILED (ERROR %d) **\n", error);
+ return (error);
+}
diff --git a/sys/arm/arm/mp_machdep.c b/sys/arm/arm/mp_machdep.c
new file mode 100644
index 000000000000..6368f7b72da9
--- /dev/null
+++ b/sys/arm/arm/mp_machdep.c
@@ -0,0 +1,396 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2011 Semihalf.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "opt_ddb.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/pcpu.h>
+#include <sys/sched.h>
+#include <sys/smp.h>
+#include <sys/ktr.h>
+#include <sys/malloc.h>
+
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_kern.h>
+#include <vm/pmap.h>
+
+#include <machine/armreg.h>
+#include <machine/cpu.h>
+#include <machine/cpufunc.h>
+#include <machine/debug_monitor.h>
+#include <machine/smp.h>
+#include <machine/pcb.h>
+#include <machine/intr.h>
+#include <machine/vmparam.h>
+#ifdef VFP
+#include <machine/vfp.h>
+#endif
+#ifdef CPU_MV_PJ4B
+#include <arm/mv/mvwin.h>
+#endif
+
+/* used to hold the AP's until we are ready to release them */
+struct mtx ap_boot_mtx;
+struct pcb stoppcbs[MAXCPU];
+
+/* # of Applications processors */
+volatile int mp_naps;
+
+/* Set to 1 once we're ready to let the APs out of the pen. */
+volatile int aps_ready = 0;
+
+void set_stackptrs(int cpu);
+
+/* Temporary variables for init_secondary() */
+void *dpcpu[MAXCPU - 1];
+
+/* Determine if we running MP machine */
+int
+cpu_mp_probe(void)
+{
+
+ KASSERT(mp_ncpus != 0, ("cpu_mp_probe: mp_ncpus is unset"));
+
+ CPU_SETOF(0, &all_cpus);
+
+ return (mp_ncpus > 1);
+}
+
+/* Start Application Processor via platform specific function */
+static int
+check_ap(void)
+{
+ uint32_t ms;
+
+ for (ms = 0; ms < 2000; ++ms) {
+ if ((mp_naps + 1) == mp_ncpus)
+ return (0); /* success */
+ else
+ DELAY(1000);
+ }
+
+ return (-2);
+}
+
+/* Initialize and fire up non-boot processors */
+void
+cpu_mp_start(void)
+{
+ int error, i;
+
+ mtx_init(&ap_boot_mtx, "ap boot", NULL, MTX_SPIN);
+
+ /* Reserve memory for application processors */
+ for(i = 0; i < (mp_ncpus - 1); i++)
+ dpcpu[i] = (void *)kmem_malloc(DPCPU_SIZE, M_WAITOK | M_ZERO);
+
+ dcache_wbinv_poc_all();
+
+ /* Initialize boot code and start up processors */
+ platform_mp_start_ap();
+
+ /* Check if ap's started properly */
+ error = check_ap();
+ if (error)
+ printf("WARNING: Some AP's failed to start\n");
+ else
+ for (i = 1; i < mp_ncpus; i++)
+ CPU_SET(i, &all_cpus);
+}
+
+/* Introduce rest of cores to the world */
+void
+cpu_mp_announce(void)
+{
+
+}
+
+void
+init_secondary(int cpu)
+{
+ struct pcpu *pc;
+ uint32_t loop_counter;
+
+ pmap_set_tex();
+ cpuinfo_reinit_mmu(pmap_kern_ttb);
+ cpu_setup();
+
+ /* Provide stack pointers for other processor modes. */
+ set_stackptrs(cpu);
+
+ enable_interrupts(PSR_A);
+ pc = &__pcpu[cpu];
+
+ /*
+ * pcpu_init() updates queue, so it should not be executed in parallel
+ * on several cores
+ */
+ while(mp_naps < (cpu - 1))
+ ;
+
+ pcpu_init(pc, cpu, sizeof(struct pcpu));
+ pc->pc_mpidr = cp15_mpidr_get() & 0xFFFFFF;
+ dpcpu_init(dpcpu[cpu - 1], cpu);
+#if defined(DDB)
+ dbg_monitor_init_secondary();
+#endif
+ /* Signal our startup to BSP */
+ atomic_add_rel_32(&mp_naps, 1);
+
+ /* Spin until the BSP releases the APs */
+ while (!atomic_load_acq_int(&aps_ready)) {
+#if __ARM_ARCH >= 7
+ __asm __volatile("wfe");
+#endif
+ }
+
+ /* Initialize curthread */
+ KASSERT(PCPU_GET(idlethread) != NULL, ("no idle thread"));
+ pc->pc_curthread = pc->pc_idlethread;
+ pc->pc_curpcb = pc->pc_idlethread->td_pcb;
+ set_curthread(pc->pc_idlethread);
+#ifdef VFP
+ vfp_init();
+#endif
+
+ /* Configure the interrupt controller */
+ intr_pic_init_secondary();
+
+ /* Apply possible BP hardening */
+ cpuinfo_init_bp_hardening();
+
+ mtx_lock_spin(&ap_boot_mtx);
+
+ atomic_add_rel_32(&smp_cpus, 1);
+
+ if (smp_cpus == mp_ncpus) {
+ /* enable IPI's, tlb shootdown, freezes etc */
+ atomic_store_rel_int(&smp_started, 1);
+ }
+
+ mtx_unlock_spin(&ap_boot_mtx);
+
+ loop_counter = 0;
+ while (smp_started == 0) {
+ DELAY(100);
+ loop_counter++;
+ if (loop_counter == 1000)
+ CTR0(KTR_SMP, "AP still wait for smp_started");
+ }
+ /* Start per-CPU event timers. */
+ cpu_initclocks_ap();
+
+ CTR0(KTR_SMP, "go into scheduler");
+
+ /* Enter the scheduler */
+ sched_throw(NULL);
+
+ panic("scheduler returned us to %s", __func__);
+ /* NOTREACHED */
+}
+
+static void
+ipi_rendezvous(void *dummy __unused)
+{
+
+ CTR0(KTR_SMP, "IPI_RENDEZVOUS");
+ smp_rendezvous_action();
+}
+
+static void
+ipi_ast(void *dummy __unused)
+{
+
+ CTR0(KTR_SMP, "IPI_AST");
+}
+
+static void
+ipi_stop(void *dummy __unused)
+{
+ u_int cpu;
+
+ /*
+ * IPI_STOP_HARD is mapped to IPI_STOP.
+ */
+ CTR0(KTR_SMP, "IPI_STOP or IPI_STOP_HARD");
+
+ cpu = PCPU_GET(cpuid);
+ savectx(&stoppcbs[cpu]);
+
+ /*
+ * CPUs are stopped when entering the debugger and at
+ * system shutdown, both events which can precede a
+ * panic dump. For the dump to be correct, all caches
+ * must be flushed and invalidated, but on ARM there's
+ * no way to broadcast a wbinv_all to other cores.
+ * Instead, we have each core do the local wbinv_all as
+ * part of stopping the core. The core requesting the
+ * stop will do the l2 cache flush after all other cores
+ * have done their l1 flushes and stopped.
+ */
+ dcache_wbinv_poc_all();
+
+ /* Indicate we are stopped */
+ CPU_SET_ATOMIC(cpu, &stopped_cpus);
+
+ /* Wait for restart */
+ while (!CPU_ISSET(cpu, &started_cpus))
+ cpu_spinwait();
+
+ CPU_CLR_ATOMIC(cpu, &started_cpus);
+ CPU_CLR_ATOMIC(cpu, &stopped_cpus);
+#ifdef DDB
+ dbg_resume_dbreg();
+#endif
+ CTR0(KTR_SMP, "IPI_STOP (restart)");
+}
+
+static void
+ipi_preempt(void *arg)
+{
+ struct trapframe *oldframe;
+ struct thread *td;
+
+ critical_enter();
+ td = curthread;
+ td->td_intr_nesting_level++;
+ oldframe = td->td_intr_frame;
+ td->td_intr_frame = (struct trapframe *)arg;
+
+ CTR1(KTR_SMP, "%s: IPI_PREEMPT", __func__);
+ sched_preempt(td);
+
+ td->td_intr_frame = oldframe;
+ td->td_intr_nesting_level--;
+ critical_exit();
+}
+
+static void
+ipi_hardclock(void *arg)
+{
+ struct trapframe *oldframe;
+ struct thread *td;
+
+ critical_enter();
+ td = curthread;
+ td->td_intr_nesting_level++;
+ oldframe = td->td_intr_frame;
+ td->td_intr_frame = (struct trapframe *)arg;
+
+ CTR1(KTR_SMP, "%s: IPI_HARDCLOCK", __func__);
+ hardclockintr();
+
+ td->td_intr_frame = oldframe;
+ td->td_intr_nesting_level--;
+ critical_exit();
+}
+
+static void
+release_aps(void *dummy __unused)
+{
+ uint32_t loop_counter;
+
+ if (mp_ncpus == 1)
+ return;
+
+ intr_pic_ipi_setup(IPI_RENDEZVOUS, "rendezvous", ipi_rendezvous, NULL);
+ intr_pic_ipi_setup(IPI_AST, "ast", ipi_ast, NULL);
+ intr_pic_ipi_setup(IPI_STOP, "stop", ipi_stop, NULL);
+ intr_pic_ipi_setup(IPI_PREEMPT, "preempt", ipi_preempt, NULL);
+ intr_pic_ipi_setup(IPI_HARDCLOCK, "hardclock", ipi_hardclock, NULL);
+
+ atomic_store_rel_int(&aps_ready, 1);
+ /* Wake the other threads up */
+ dsb();
+ sev();
+
+ printf("Release APs\n");
+
+ for (loop_counter = 0; loop_counter < 2000; loop_counter++) {
+ if (smp_started)
+ return;
+ DELAY(1000);
+ }
+ printf("AP's not started\n");
+}
+
+SYSINIT(start_aps, SI_SUB_SMP, SI_ORDER_FIRST, release_aps, NULL);
+
+struct cpu_group *
+cpu_topo(void)
+{
+
+ return (smp_topo_1level(CG_SHARE_L2, mp_ncpus, 0));
+}
+
+void
+cpu_mp_setmaxid(void)
+{
+
+ platform_mp_setmaxid();
+}
+
+/* Sending IPI */
+void
+ipi_all_but_self(u_int ipi)
+{
+ cpuset_t other_cpus;
+
+ other_cpus = all_cpus;
+ CPU_CLR(PCPU_GET(cpuid), &other_cpus);
+ CTR2(KTR_SMP, "%s: ipi: %x", __func__, ipi);
+ intr_ipi_send(other_cpus, ipi);
+}
+
+void
+ipi_cpu(int cpu, u_int ipi)
+{
+ cpuset_t cpus;
+
+ CPU_ZERO(&cpus);
+ CPU_SET(cpu, &cpus);
+
+ CTR3(KTR_SMP, "%s: cpu: %d, ipi: %x", __func__, cpu, ipi);
+ intr_ipi_send(cpus, ipi);
+}
+
+void
+ipi_selected(cpuset_t cpus, u_int ipi)
+{
+
+ CTR2(KTR_SMP, "%s: ipi: %x", __func__, ipi);
+ intr_ipi_send(cpus, ipi);
+}
diff --git a/sys/arm/arm/mpcore_timer.c b/sys/arm/arm/mpcore_timer.c
new file mode 100644
index 000000000000..de8dce5dac83
--- /dev/null
+++ b/sys/arm/arm/mpcore_timer.c
@@ -0,0 +1,561 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 2011 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * Developed by Ben Gray <ben.r.gray@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/**
+ * The ARM Cortex-A9 core can support a global timer plus a private and
+ * watchdog timer per core. This driver reserves memory and interrupt
+ * resources for accessing both timer register sets, these resources are
+ * stored globally and used to setup the timecount and eventtimer.
+ *
+ * The timecount timer uses the global 64-bit counter, whereas the
+ * per-CPU eventtimer uses the private 32-bit counters.
+ *
+ *
+ * REF: ARM Cortex-A9 MPCore, Technical Reference Manual (rev. r2p2)
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <machine/machdep.h> /* For arm_set_delay */
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+
+#include <arm/arm/mpcore_timervar.h>
+
+/* Private (per-CPU) timer register map */
+#define PRV_TIMER_LOAD 0x0000
+#define PRV_TIMER_COUNT 0x0004
+#define PRV_TIMER_CTRL 0x0008
+#define PRV_TIMER_INTR 0x000C
+
+#define PRV_TIMER_CTR_PRESCALER_SHIFT 8
+#define PRV_TIMER_CTRL_IRQ_ENABLE (1UL << 2)
+#define PRV_TIMER_CTRL_AUTO_RELOAD (1UL << 1)
+#define PRV_TIMER_CTRL_TIMER_ENABLE (1UL << 0)
+
+#define PRV_TIMER_INTR_EVENT (1UL << 0)
+
+/* Global timer register map */
+#define GBL_TIMER_COUNT_LOW 0x0000
+#define GBL_TIMER_COUNT_HIGH 0x0004
+#define GBL_TIMER_CTRL 0x0008
+#define GBL_TIMER_INTR 0x000C
+
+#define GBL_TIMER_CTR_PRESCALER_SHIFT 8
+#define GBL_TIMER_CTRL_AUTO_INC (1UL << 3)
+#define GBL_TIMER_CTRL_IRQ_ENABLE (1UL << 2)
+#define GBL_TIMER_CTRL_COMP_ENABLE (1UL << 1)
+#define GBL_TIMER_CTRL_TIMER_ENABLE (1UL << 0)
+
+#define GBL_TIMER_INTR_EVENT (1UL << 0)
+
+struct arm_tmr_softc {
+ device_t dev;
+ int irqrid;
+ int memrid;
+ struct resource * gbl_mem;
+ struct resource * prv_mem;
+ struct resource * prv_irq;
+ uint64_t clkfreq;
+ struct eventtimer et;
+};
+
+static struct eventtimer *arm_tmr_et;
+static struct timecounter *arm_tmr_tc;
+static uint64_t arm_tmr_freq;
+static boolean_t arm_tmr_freq_varies;
+
+#define tmr_prv_read_4(sc, reg) bus_read_4((sc)->prv_mem, reg)
+#define tmr_prv_write_4(sc, reg, val) bus_write_4((sc)->prv_mem, reg, val)
+#define tmr_gbl_read_4(sc, reg) bus_read_4((sc)->gbl_mem, reg)
+#define tmr_gbl_write_4(sc, reg, val) bus_write_4((sc)->gbl_mem, reg, val)
+
+static void arm_tmr_delay(int, void *);
+
+static timecounter_get_t arm_tmr_get_timecount;
+
+static struct timecounter arm_tmr_timecount = {
+ .tc_name = "MPCore",
+ .tc_get_timecount = arm_tmr_get_timecount,
+ .tc_poll_pps = NULL,
+ .tc_counter_mask = ~0u,
+ .tc_frequency = 0,
+ .tc_quality = 800,
+};
+
+#define TMR_GBL 0x01
+#define TMR_PRV 0x02
+#define TMR_BOTH (TMR_GBL | TMR_PRV)
+#define TMR_NONE 0
+
+static struct ofw_compat_data compat_data[] = {
+ {"arm,mpcore-timers", TMR_BOTH}, /* Non-standard, FreeBSD. */
+ {"arm,cortex-a9-global-timer", TMR_GBL},
+ {"arm,cortex-a5-global-timer", TMR_GBL},
+ {"arm,cortex-a9-twd-timer", TMR_PRV},
+ {"arm,cortex-a5-twd-timer", TMR_PRV},
+ {"arm,arm11mp-twd-timer", TMR_PRV},
+ {NULL, TMR_NONE}
+};
+
+/**
+ * arm_tmr_get_timecount - reads the timecount (global) timer
+ * @tc: pointer to arm_tmr_timecount struct
+ *
+ * We only read the lower 32-bits, the timecount stuff only uses 32-bits
+ * so (for now?) ignore the upper 32-bits.
+ *
+ * RETURNS
+ * The lower 32-bits of the counter.
+ */
+static unsigned
+arm_tmr_get_timecount(struct timecounter *tc)
+{
+ struct arm_tmr_softc *sc;
+
+ sc = tc->tc_priv;
+ return (tmr_gbl_read_4(sc, GBL_TIMER_COUNT_LOW));
+}
+
+/**
+ * arm_tmr_start - starts the eventtimer (private) timer
+ * @et: pointer to eventtimer struct
+ * @first: the number of seconds and fractional sections to trigger in
+ * @period: the period (in seconds and fractional sections) to set
+ *
+ * If the eventtimer is required to be in oneshot mode, period will be
+ * NULL and first will point to the time to trigger. If in periodic mode
+ * period will contain the time period and first may optionally contain
+ * the time for the first period.
+ *
+ * RETURNS
+ * Always returns 0
+ */
+static int
+arm_tmr_start(struct eventtimer *et, sbintime_t first, sbintime_t period)
+{
+ struct arm_tmr_softc *sc;
+ uint32_t load, count;
+ uint32_t ctrl;
+
+ sc = et->et_priv;
+ tmr_prv_write_4(sc, PRV_TIMER_CTRL, 0);
+ tmr_prv_write_4(sc, PRV_TIMER_INTR, PRV_TIMER_INTR_EVENT);
+
+ ctrl = PRV_TIMER_CTRL_IRQ_ENABLE | PRV_TIMER_CTRL_TIMER_ENABLE;
+
+ if (period != 0) {
+ load = ((uint32_t)et->et_frequency * period) >> 32;
+ ctrl |= PRV_TIMER_CTRL_AUTO_RELOAD;
+ } else
+ load = 0;
+
+ if (first != 0)
+ count = (uint32_t)((et->et_frequency * first) >> 32);
+ else
+ count = load;
+
+ tmr_prv_write_4(sc, PRV_TIMER_LOAD, load);
+ tmr_prv_write_4(sc, PRV_TIMER_COUNT, count);
+ tmr_prv_write_4(sc, PRV_TIMER_CTRL, ctrl);
+
+ return (0);
+}
+
+/**
+ * arm_tmr_stop - stops the eventtimer (private) timer
+ * @et: pointer to eventtimer struct
+ *
+ * Simply stops the private timer by clearing all bits in the ctrl register.
+ *
+ * RETURNS
+ * Always returns 0
+ */
+static int
+arm_tmr_stop(struct eventtimer *et)
+{
+ struct arm_tmr_softc *sc;
+
+ sc = et->et_priv;
+ tmr_prv_write_4(sc, PRV_TIMER_CTRL, 0);
+ tmr_prv_write_4(sc, PRV_TIMER_INTR, PRV_TIMER_INTR_EVENT);
+ return (0);
+}
+
+/**
+ * arm_tmr_intr - ISR for the eventtimer (private) timer
+ * @arg: pointer to arm_tmr_softc struct
+ *
+ * Clears the event register and then calls the eventtimer callback.
+ *
+ * RETURNS
+ * Always returns FILTER_HANDLED
+ */
+static int
+arm_tmr_intr(void *arg)
+{
+ struct arm_tmr_softc *sc;
+
+ sc = arg;
+ tmr_prv_write_4(sc, PRV_TIMER_INTR, PRV_TIMER_INTR_EVENT);
+ if (sc->et.et_active)
+ sc->et.et_event_cb(&sc->et, sc->et.et_arg);
+ return (FILTER_HANDLED);
+}
+
+/**
+ * arm_tmr_probe - timer probe routine
+ * @dev: new device
+ *
+ * The probe function returns success when probed with the fdt compatible
+ * string set to "arm,mpcore-timers".
+ *
+ * RETURNS
+ * BUS_PROBE_DEFAULT if the fdt device is compatible, otherwise ENXIO.
+ */
+static int
+arm_tmr_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == TMR_NONE)
+ return (ENXIO);
+
+ device_set_desc(dev, "ARM MPCore Timers");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+attach_tc(struct arm_tmr_softc *sc)
+{
+ int rid;
+
+ if (arm_tmr_tc != NULL)
+ return (EBUSY);
+
+ rid = sc->memrid;
+ sc->gbl_mem = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->gbl_mem == NULL) {
+ device_printf(sc->dev, "could not allocate gbl mem resources\n");
+ return (ENXIO);
+ }
+ tmr_gbl_write_4(sc, GBL_TIMER_CTRL, 0x00000000);
+
+ arm_tmr_timecount.tc_frequency = sc->clkfreq;
+ arm_tmr_timecount.tc_priv = sc;
+ tc_init(&arm_tmr_timecount);
+ arm_tmr_tc = &arm_tmr_timecount;
+
+ tmr_gbl_write_4(sc, GBL_TIMER_CTRL, GBL_TIMER_CTRL_TIMER_ENABLE);
+
+ return (0);
+}
+
+static int
+attach_et(struct arm_tmr_softc *sc)
+{
+ void *ihl;
+ int irid, mrid;
+
+ if (arm_tmr_et != NULL)
+ return (EBUSY);
+
+ mrid = sc->memrid;
+ sc->prv_mem = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY, &mrid,
+ RF_ACTIVE);
+ if (sc->prv_mem == NULL) {
+ device_printf(sc->dev, "could not allocate prv mem resources\n");
+ return (ENXIO);
+ }
+ tmr_prv_write_4(sc, PRV_TIMER_CTRL, 0x00000000);
+
+ irid = sc->irqrid;
+ sc->prv_irq = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &irid, RF_ACTIVE);
+ if (sc->prv_irq == NULL) {
+ bus_release_resource(sc->dev, SYS_RES_MEMORY, mrid, sc->prv_mem);
+ device_printf(sc->dev, "could not allocate prv irq resources\n");
+ return (ENXIO);
+ }
+
+ if (bus_setup_intr(sc->dev, sc->prv_irq, INTR_TYPE_CLK, arm_tmr_intr,
+ NULL, sc, &ihl) != 0) {
+ bus_release_resource(sc->dev, SYS_RES_MEMORY, mrid, sc->prv_mem);
+ bus_release_resource(sc->dev, SYS_RES_IRQ, irid, sc->prv_irq);
+ device_printf(sc->dev, "unable to setup the et irq handler.\n");
+ return (ENXIO);
+ }
+
+ /*
+ * Setup and register the eventtimer. Most event timers set their min
+ * and max period values to some value calculated from the clock
+ * frequency. We might not know yet what our runtime clock frequency
+ * will be, so we just use some safe values. A max of 2 seconds ensures
+ * that even if our base clock frequency is 2GHz (meaning a 4GHz CPU),
+ * we won't overflow our 32-bit timer count register. A min of 20
+ * nanoseconds is pretty much completely arbitrary.
+ */
+ sc->et.et_name = "MPCore";
+ sc->et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT | ET_FLAGS_PERCPU;
+ sc->et.et_quality = 1000;
+ sc->et.et_frequency = sc->clkfreq;
+ sc->et.et_min_period = nstosbt(20);
+ sc->et.et_max_period = 2 * SBT_1S;
+ sc->et.et_start = arm_tmr_start;
+ sc->et.et_stop = arm_tmr_stop;
+ sc->et.et_priv = sc;
+ et_register(&sc->et);
+ arm_tmr_et = &sc->et;
+
+ return (0);
+}
+
+/**
+ * arm_tmr_attach - attaches the timer to the simplebus
+ * @dev: new device
+ *
+ * Reserves memory and interrupt resources, stores the softc structure
+ * globally and registers both the timecount and eventtimer objects.
+ *
+ * RETURNS
+ * Zero on success or ENXIO if an error occuried.
+ */
+static int
+arm_tmr_attach(device_t dev)
+{
+ struct arm_tmr_softc *sc;
+ phandle_t node;
+ pcell_t clock;
+ int et_err, tc_err, tmrtype;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ if (arm_tmr_freq_varies) {
+ sc->clkfreq = arm_tmr_freq;
+ } else {
+ if (arm_tmr_freq != 0) {
+ sc->clkfreq = arm_tmr_freq;
+ } else {
+ /* Get the base clock frequency */
+ node = ofw_bus_get_node(dev);
+ if ((OF_getencprop(node, "clock-frequency", &clock,
+ sizeof(clock))) <= 0) {
+ device_printf(dev, "missing clock-frequency "
+ "attribute in FDT\n");
+ return (ENXIO);
+ }
+ sc->clkfreq = clock;
+ }
+ }
+
+ tmrtype = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+ tc_err = ENXIO;
+ et_err = ENXIO;
+
+ /*
+ * If we're handling the global timer and it is fixed-frequency, set it
+ * up to use as a timecounter. If it's variable frequency it won't work
+ * as a timecounter. We also can't use it for DELAY(), so hopefully the
+ * platform provides its own implementation. If it doesn't, ours will
+ * get used, but since the frequency isn't set, it will only use the
+ * bogus loop counter.
+ */
+ if (tmrtype & TMR_GBL) {
+ if (!arm_tmr_freq_varies)
+ tc_err = attach_tc(sc);
+ else if (bootverbose)
+ device_printf(sc->dev,
+ "not using variable-frequency device as timecounter\n");
+ sc->memrid++;
+ sc->irqrid++;
+ }
+
+ /* If we are handling the private timer, set it up as an eventtimer. */
+ if (tmrtype & TMR_PRV) {
+ et_err = attach_et(sc);
+ }
+
+ /*
+ * If we didn't successfully set up a timecounter or eventtimer then we
+ * didn't actually attach at all, return error.
+ */
+ if (tc_err != 0 && et_err != 0) {
+ return (ENXIO);
+ }
+
+#ifdef PLATFORM
+ /*
+ * We can register as the DELAY() implementation only if we successfully
+ * set up the global timer.
+ */
+ if (tc_err == 0)
+ arm_set_delay(arm_tmr_delay, sc);
+#endif
+
+ return (0);
+}
+
+static device_method_t arm_tmr_methods[] = {
+ DEVMETHOD(device_probe, arm_tmr_probe),
+ DEVMETHOD(device_attach, arm_tmr_attach),
+ { 0, 0 }
+};
+
+static driver_t arm_tmr_driver = {
+ "mp_tmr",
+ arm_tmr_methods,
+ sizeof(struct arm_tmr_softc),
+};
+
+static devclass_t arm_tmr_devclass;
+
+EARLY_DRIVER_MODULE(mp_tmr, simplebus, arm_tmr_driver, arm_tmr_devclass, 0, 0,
+ BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE);
+EARLY_DRIVER_MODULE(mp_tmr, ofwbus, arm_tmr_driver, arm_tmr_devclass, 0, 0,
+ BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE);
+
+/*
+ * Handle a change in clock frequency. The mpcore timer runs at half the CPU
+ * frequency. When the CPU frequency changes due to power-saving or thermal
+ * management, the platform-specific code that causes the frequency change calls
+ * this routine to inform the clock driver, and we in turn inform the event
+ * timer system, which actually updates the value in et->frequency for us and
+ * reschedules the current event(s) in a way that's atomic with respect to
+ * start/stop/intr code that may be running on various CPUs at the time of the
+ * call.
+ *
+ * This routine can also be called by a platform's early init code. If the
+ * value passed is ARM_TMR_FREQUENCY_VARIES, that will cause the attach() code
+ * to register as an eventtimer, but not a timecounter. If the value passed in
+ * is any other non-zero value it is used as the fixed frequency for the timer.
+ */
+void
+arm_tmr_change_frequency(uint64_t newfreq)
+{
+
+ if (newfreq == ARM_TMR_FREQUENCY_VARIES) {
+ arm_tmr_freq_varies = true;
+ return;
+ }
+
+ arm_tmr_freq = newfreq;
+ if (arm_tmr_et != NULL)
+ et_change_frequency(arm_tmr_et, newfreq);
+}
+
+static void
+arm_tmr_delay(int usec, void *arg)
+{
+ struct arm_tmr_softc *sc = arg;
+ int32_t counts_per_usec;
+ int32_t counts;
+ uint32_t first, last;
+
+ /* Get the number of times to count */
+ counts_per_usec = ((arm_tmr_timecount.tc_frequency / 1000000) + 1);
+
+ /*
+ * Clamp the timeout at a maximum value (about 32 seconds with
+ * a 66MHz clock). *Nobody* should be delay()ing for anywhere
+ * near that length of time and if they are, they should be hung
+ * out to dry.
+ */
+ if (usec >= (0x80000000U / counts_per_usec))
+ counts = (0x80000000U / counts_per_usec) - 1;
+ else
+ counts = usec * counts_per_usec;
+
+ first = tmr_gbl_read_4(sc, GBL_TIMER_COUNT_LOW);
+
+ while (counts > 0) {
+ last = tmr_gbl_read_4(sc, GBL_TIMER_COUNT_LOW);
+ counts -= (int32_t)(last - first);
+ first = last;
+ }
+}
+
+#ifndef PLATFORM
+/**
+ * DELAY - Delay for at least usec microseconds.
+ * @usec: number of microseconds to delay by
+ *
+ * This function is called all over the kernel and is suppose to provide a
+ * consistent delay. This function may also be called before the console
+ * is setup so no printf's can be called here.
+ *
+ * RETURNS:
+ * nothing
+ */
+void
+DELAY(int usec)
+{
+ struct arm_tmr_softc *sc;
+ int32_t counts;
+
+ TSENTER();
+ /* Check the timers are setup, if not just use a for loop for the meantime */
+ if (arm_tmr_tc == NULL || arm_tmr_timecount.tc_frequency == 0) {
+ for (; usec > 0; usec--)
+ for (counts = 200; counts > 0; counts--)
+ cpufunc_nullop(); /* Prevent gcc from optimizing
+ * out the loop
+ */
+ } else {
+ sc = arm_tmr_tc->tc_priv;
+ arm_tmr_delay(usec, sc);
+ }
+ TSEXIT();
+}
+#endif
diff --git a/sys/arm/arm/mpcore_timervar.h b/sys/arm/arm/mpcore_timervar.h
new file mode 100644
index 000000000000..0a90a11798cd
--- /dev/null
+++ b/sys/arm/arm/mpcore_timervar.h
@@ -0,0 +1,47 @@
+/*-
+ * Copyright (c) 2014 Ian Lepore <ian@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _ARM_MPCORE_TIMERVAR_H_
+#define _ARM_MPCORE_TIMERVAR_H_
+
+/*
+ * This value, passed to arm_tmr_change_frequency() any time before the mpcore
+ * timer device attaches, informs the driver that the mpcore clock frequency can
+ * change on the fly, and thus can't be used as a timecounter. The hardware can
+ * still be used as an eventtimer, as long as each frequency change is
+ * communicated to it with calls to arm_tmr_change_frequency().
+ */
+#define ARM_TMR_FREQUENCY_VARIES -1ULL
+
+/*
+ * Inform the mpcore timer driver of a new clock frequency. This can be called
+ * both before and after the mpcore timer driver attaches.
+ */
+void arm_tmr_change_frequency(uint64_t newfreq);
+
+#endif
diff --git a/sys/arm/arm/nexus.c b/sys/arm/arm/nexus.c
new file mode 100644
index 000000000000..f9e9fec7ad25
--- /dev/null
+++ b/sys/arm/arm/nexus.c
@@ -0,0 +1,434 @@
+/*-
+ * Copyright 1998 Massachusetts Institute of Technology
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that both the above copyright notice and this
+ * permission notice appear in all copies, that both the above
+ * copyright notice and this permission notice appear in all
+ * supporting documentation, and that the name of M.I.T. not be used
+ * in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission. M.I.T. makes
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
+ * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/*
+ * This code implements a `root nexus' for Arm Architecture
+ * machines. The function of the root nexus is to serve as an
+ * attachment point for both processors and buses, and to manage
+ * resources which are common to all of them. In particular,
+ * this code implements the core resource managers for interrupt
+ * requests, DMA requests (which rightfully should be a part of the
+ * ISA code but it's easier to do it here for now), I/O port addresses,
+ * and I/O memory address space.
+ */
+
+#include "opt_platform.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <sys/interrupt.h>
+
+#include <machine/vmparam.h>
+#include <machine/pcb.h>
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/resource.h>
+#include <machine/intr.h>
+
+#include <arm/arm/nexusvar.h>
+
+#ifdef FDT
+#include <machine/fdt.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include "ofw_bus_if.h"
+#endif
+
+static MALLOC_DEFINE(M_NEXUSDEV, "nexusdev", "Nexus device");
+
+struct nexus_device {
+ struct resource_list nx_resources;
+};
+
+#define DEVTONX(dev) ((struct nexus_device *)device_get_ivars(dev))
+
+static struct rman mem_rman;
+
+static int nexus_probe(device_t);
+static int nexus_attach(device_t);
+static int nexus_print_child(device_t, device_t);
+static device_t nexus_add_child(device_t, u_int, const char *, int);
+static struct resource *nexus_alloc_resource(device_t, device_t, int, int *,
+ rman_res_t, rman_res_t, rman_res_t, u_int);
+static int nexus_activate_resource(device_t, device_t, int, int,
+ struct resource *);
+static bus_space_tag_t nexus_get_bus_tag(device_t, device_t);
+static bus_dma_tag_t nexus_get_dma_tag(device_t dev, device_t child);
+#ifdef SMP
+static int nexus_bind_intr(device_t, device_t, struct resource *, int);
+#endif
+static int nexus_config_intr(device_t dev, int irq, enum intr_trigger trig,
+ enum intr_polarity pol);
+static int nexus_describe_intr(device_t dev, device_t child,
+ struct resource *irq, void *cookie, const char *descr);
+static int nexus_deactivate_resource(device_t, device_t, int, int,
+ struct resource *);
+static int nexus_release_resource(device_t, device_t, int, int,
+ struct resource *);
+
+static int nexus_setup_intr(device_t dev, device_t child, struct resource *res,
+ int flags, driver_filter_t *filt, driver_intr_t *intr, void *arg, void **cookiep);
+static int nexus_teardown_intr(device_t, device_t, struct resource *, void *);
+
+#ifdef FDT
+static int nexus_ofw_map_intr(device_t dev, device_t child, phandle_t iparent,
+ int icells, pcell_t *intr);
+#endif
+
+/*
+ * Normally NULL (which results in defaults which are handled in
+ * busdma_machdep), platform init code can use nexus_set_dma_tag() to set this
+ * to a tag that will be inherited by all busses and devices on the platform.
+ */
+static bus_dma_tag_t nexus_dma_tag;
+
+static device_method_t nexus_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, nexus_probe),
+ DEVMETHOD(device_attach, nexus_attach),
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, nexus_print_child),
+ DEVMETHOD(bus_add_child, nexus_add_child),
+ DEVMETHOD(bus_alloc_resource, nexus_alloc_resource),
+ DEVMETHOD(bus_activate_resource, nexus_activate_resource),
+ DEVMETHOD(bus_config_intr, nexus_config_intr),
+ DEVMETHOD(bus_deactivate_resource, nexus_deactivate_resource),
+ DEVMETHOD(bus_release_resource, nexus_release_resource),
+ DEVMETHOD(bus_setup_intr, nexus_setup_intr),
+ DEVMETHOD(bus_teardown_intr, nexus_teardown_intr),
+ DEVMETHOD(bus_get_bus_tag, nexus_get_bus_tag),
+ DEVMETHOD(bus_get_dma_tag, nexus_get_dma_tag),
+ DEVMETHOD(bus_describe_intr, nexus_describe_intr),
+#ifdef SMP
+ DEVMETHOD(bus_bind_intr, nexus_bind_intr),
+#endif
+#ifdef FDT
+ DEVMETHOD(ofw_bus_map_intr, nexus_ofw_map_intr),
+#endif
+ { 0, 0 }
+};
+
+static devclass_t nexus_devclass;
+static driver_t nexus_driver = {
+ "nexus",
+ nexus_methods,
+ 1 /* no softc */
+};
+EARLY_DRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0,
+ BUS_PASS_BUS + BUS_PASS_ORDER_EARLY);
+
+static int
+nexus_probe(device_t dev)
+{
+
+ device_quiet(dev); /* suppress attach message for neatness */
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+nexus_attach(device_t dev)
+{
+
+ mem_rman.rm_start = 0;
+ mem_rman.rm_end = BUS_SPACE_MAXADDR;
+ mem_rman.rm_type = RMAN_ARRAY;
+ mem_rman.rm_descr = "I/O memory addresses";
+ if (rman_init(&mem_rman) ||
+ rman_manage_region(&mem_rman, 0, BUS_SPACE_MAXADDR))
+ panic("nexus_probe mem_rman");
+
+ /*
+ * First, deal with the children we know about already
+ */
+ bus_generic_probe(dev);
+ bus_generic_attach(dev);
+
+ return (0);
+}
+
+static int
+nexus_print_child(device_t bus, device_t child)
+{
+ int retval = 0;
+
+ retval += bus_print_child_header(bus, child);
+ retval += printf("\n");
+
+ return (retval);
+}
+
+static device_t
+nexus_add_child(device_t bus, u_int order, const char *name, int unit)
+{
+ device_t child;
+ struct nexus_device *ndev;
+
+ ndev = malloc(sizeof(struct nexus_device), M_NEXUSDEV, M_NOWAIT|M_ZERO);
+ if (!ndev)
+ return (0);
+ resource_list_init(&ndev->nx_resources);
+
+ child = device_add_child_ordered(bus, order, name, unit);
+
+ /* should we free this in nexus_child_detached? */
+ device_set_ivars(child, ndev);
+
+ return (child);
+}
+
+/*
+ * Allocate a resource on behalf of child. NB: child is usually going to be a
+ * child of one of our descendants, not a direct child of nexus0.
+ * (Exceptions include footbridge.)
+ */
+static struct resource *
+nexus_alloc_resource(device_t bus, device_t child, int type, int *rid,
+ rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
+{
+ struct resource *rv;
+ struct rman *rm;
+ int needactivate = flags & RF_ACTIVE;
+
+ flags &= ~RF_ACTIVE;
+
+ switch (type) {
+ case SYS_RES_MEMORY:
+ case SYS_RES_IOPORT:
+ rm = &mem_rman;
+ break;
+
+ default:
+ return (NULL);
+ }
+
+ rv = rman_reserve_resource(rm, start, end, count, flags, child);
+ if (rv == NULL)
+ return (NULL);
+
+ rman_set_rid(rv, *rid);
+
+ if (needactivate) {
+ if (bus_activate_resource(child, type, *rid, rv)) {
+ rman_release_resource(rv);
+ return (0);
+ }
+ }
+
+ return (rv);
+}
+
+static int
+nexus_release_resource(device_t bus, device_t child, int type, int rid,
+ struct resource *res)
+{
+ int error;
+
+ if (rman_get_flags(res) & RF_ACTIVE) {
+ error = bus_deactivate_resource(child, type, rid, res);
+ if (error)
+ return (error);
+ }
+ return (rman_release_resource(res));
+}
+
+static bus_space_tag_t
+nexus_get_bus_tag(device_t bus __unused, device_t child __unused)
+{
+
+#ifdef FDT
+ return(fdtbus_bs_tag);
+#else
+ return((void *)1);
+#endif
+}
+
+static bus_dma_tag_t
+nexus_get_dma_tag(device_t dev, device_t child)
+{
+
+ return nexus_dma_tag;
+}
+
+void
+nexus_set_dma_tag(bus_dma_tag_t tag)
+{
+
+ nexus_dma_tag = tag;
+}
+
+static int
+nexus_config_intr(device_t dev, int irq, enum intr_trigger trig,
+ enum intr_polarity pol)
+{
+ int ret = ENODEV;
+
+ device_printf(dev, "bus_config_intr is obsolete and not supported!\n");
+ ret = EOPNOTSUPP;
+ return (ret);
+}
+
+static int
+nexus_setup_intr(device_t dev, device_t child, struct resource *res, int flags,
+ driver_filter_t *filt, driver_intr_t *intr, void *arg, void **cookiep)
+{
+
+ if ((rman_get_flags(res) & RF_SHAREABLE) == 0)
+ flags |= INTR_EXCL;
+
+ return(intr_setup_irq(child, res, filt, intr, arg, flags, cookiep));
+}
+
+static int
+nexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih)
+{
+
+ return (intr_teardown_irq(child, r, ih));
+}
+
+static int
+nexus_describe_intr(device_t dev, device_t child, struct resource *irq,
+ void *cookie, const char *descr)
+{
+
+ return (intr_describe_irq(child, irq, cookie, descr));
+}
+
+#ifdef SMP
+static int
+nexus_bind_intr(device_t dev, device_t child, struct resource *irq, int cpu)
+{
+
+ return (intr_bind_irq(child, irq, cpu));
+}
+#endif
+
+static int
+nexus_activate_resource(device_t bus, device_t child, int type, int rid,
+ struct resource *r)
+{
+ int err;
+ bus_addr_t paddr;
+ bus_size_t psize;
+ bus_space_handle_t vaddr;
+
+ if ((err = rman_activate_resource(r)) != 0)
+ return (err);
+
+ /*
+ * If this is a memory resource, map it into the kernel.
+ */
+ if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) {
+ paddr = (bus_addr_t)rman_get_start(r);
+ psize = (bus_size_t)rman_get_size(r);
+#ifdef FDT
+ err = bus_space_map(fdtbus_bs_tag, paddr, psize, 0, &vaddr);
+ if (err != 0) {
+ rman_deactivate_resource(r);
+ return (err);
+ }
+ rman_set_bustag(r, fdtbus_bs_tag);
+#else
+ vaddr = (bus_space_handle_t)pmap_mapdev((vm_offset_t)paddr,
+ (vm_size_t)psize);
+ if (vaddr == 0) {
+ rman_deactivate_resource(r);
+ return (ENOMEM);
+ }
+ rman_set_bustag(r, (void *)1);
+#endif
+ rman_set_virtual(r, (void *)vaddr);
+ rman_set_bushandle(r, vaddr);
+ return (0);
+ } else if (type == SYS_RES_IRQ) {
+ err = intr_activate_irq(child, r);
+ if (err != 0) {
+ rman_deactivate_resource(r);
+ return (err);
+ }
+ }
+ return (0);
+}
+
+static int
+nexus_deactivate_resource(device_t bus, device_t child, int type, int rid,
+ struct resource *r)
+{
+ bus_size_t psize;
+ bus_space_handle_t vaddr;
+
+ if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) {
+ psize = (bus_size_t)rman_get_size(r);
+ vaddr = rman_get_bushandle(r);
+
+ if (vaddr != 0) {
+#ifdef FDT
+ bus_space_unmap(fdtbus_bs_tag, vaddr, psize);
+#else
+ pmap_unmapdev((vm_offset_t)vaddr, (vm_size_t)psize);
+#endif
+ rman_set_virtual(r, NULL);
+ rman_set_bushandle(r, 0);
+ }
+ } else if (type == SYS_RES_IRQ) {
+ intr_deactivate_irq(child, r);
+ }
+
+ return (rman_deactivate_resource(r));
+}
+
+#ifdef FDT
+static int
+nexus_ofw_map_intr(device_t dev, device_t child, phandle_t iparent, int icells,
+ pcell_t *intr)
+{
+ u_int irq;
+ struct intr_map_data_fdt *fdt_data;
+ size_t len;
+
+ len = sizeof(*fdt_data) + icells * sizeof(pcell_t);
+ fdt_data = (struct intr_map_data_fdt *)intr_alloc_map_data(
+ INTR_MAP_DATA_FDT, len, M_WAITOK | M_ZERO);
+ fdt_data->iparent = iparent;
+ fdt_data->ncells = icells;
+ memcpy(fdt_data->cells, intr, icells * sizeof(pcell_t));
+ irq = intr_map_irq(NULL, iparent, (struct intr_map_data *)fdt_data);
+ return (irq);
+}
+#endif /* FDT */
diff --git a/sys/arm/arm/nexusvar.h b/sys/arm/arm/nexusvar.h
new file mode 100644
index 000000000000..9e1bd3027687
--- /dev/null
+++ b/sys/arm/arm/nexusvar.h
@@ -0,0 +1,35 @@
+/*-
+ * Copyright (c) 2017 Ian Lepore <ian@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _ARM_ARM_NEXUSVAR_H_
+#define _ARM_ARM_NEXUSVAR_H_
+
+/* Set a platform busdma tag to be inherited by all busses and devices. */
+void nexus_set_dma_tag(bus_dma_tag_t _tag);
+
+#endif
diff --git a/sys/arm/arm/ofw_machdep.c b/sys/arm/arm/ofw_machdep.c
new file mode 100644
index 000000000000..0c53a77feda8
--- /dev/null
+++ b/sys/arm/arm/ofw_machdep.c
@@ -0,0 +1,74 @@
+/*-
+ * Copyright (c) 2015 Ian Lepore <ian@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_subr.h>
+
+int
+OF_decode_addr(phandle_t dev, int regno, bus_space_tag_t *tag,
+ bus_space_handle_t *handle, bus_size_t *sz)
+{
+ bus_addr_t addr;
+ bus_size_t size;
+ pcell_t pci_hi;
+ int flags, res;
+
+ res = ofw_reg_to_paddr(dev, regno, &addr, &size, &pci_hi);
+ if (res < 0)
+ return (res);
+
+ /*
+ * Nothing special to do for PCI buses right now.
+ * This may need to be handled per-platform when it does come up.
+ */
+#ifdef notyet
+ if (pci_hi == OFW_PADDR_NOT_PCI) {
+ *tag = fdtbus_bs_tag;
+ flags = 0;
+ } else {
+ *tag = fdtbus_bs_tag;
+ flags = (pci_hi & OFW_PCI_PHYS_HI_PREFETCHABLE) ?
+ BUS_SPACE_MAP_PREFETCHABLE: 0;
+ }
+#else
+ *tag = fdtbus_bs_tag;
+ flags = 0;
+#endif
+
+ if (sz != NULL)
+ *sz = size;
+
+ return (bus_space_map(*tag, addr, size, flags, handle));
+}
diff --git a/sys/arm/arm/pl190.c b/sys/arm/arm/pl190.c
new file mode 100644
index 000000000000..a8ca18effb8c
--- /dev/null
+++ b/sys/arm/arm/pl190.c
@@ -0,0 +1,311 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012-2017 Oleksandr Tymoshenko <gonzo@bluezbox.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/ktr.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/rman.h>
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "pic_if.h"
+
+#ifdef DEBUG
+#define dprintf(fmt, args...) printf(fmt, ##args)
+#else
+#define dprintf(fmt, args...)
+#endif
+
+#define VICIRQSTATUS 0x000
+#define VICFIQSTATUS 0x004
+#define VICRAWINTR 0x008
+#define VICINTSELECT 0x00C
+#define VICINTENABLE 0x010
+#define VICINTENCLEAR 0x014
+#define VICSOFTINT 0x018
+#define VICSOFTINTCLEAR 0x01C
+#define VICPROTECTION 0x020
+#define VICPERIPHID 0xFE0
+#define VICPRIMECELLID 0xFF0
+
+#define VIC_NIRQS 32
+
+struct pl190_intc_irqsrc {
+ struct intr_irqsrc isrc;
+ u_int irq;
+};
+
+struct pl190_intc_softc {
+ device_t dev;
+ struct mtx mtx;
+ struct resource * intc_res;
+ struct pl190_intc_irqsrc isrcs[VIC_NIRQS];
+};
+
+#define INTC_VIC_READ_4(sc, reg) \
+ bus_read_4(sc->intc_res, (reg))
+#define INTC_VIC_WRITE_4(sc, reg, val) \
+ bus_write_4(sc->intc_res, (reg), (val))
+
+#define VIC_LOCK(_sc) mtx_lock_spin(&(_sc)->mtx)
+#define VIC_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->mtx)
+
+static inline void
+pl190_intc_irq_dispatch(struct pl190_intc_softc *sc, u_int irq,
+ struct trapframe *tf)
+{
+ struct pl190_intc_irqsrc *src;
+
+ src = &sc->isrcs[irq];
+ if (intr_isrc_dispatch(&src->isrc, tf) != 0)
+ device_printf(sc->dev, "Stray irq %u detected\n", irq);
+}
+
+static int
+pl190_intc_intr(void *arg)
+{
+ struct pl190_intc_softc *sc;
+ u_int cpu;
+ uint32_t num, pending;
+ struct trapframe *tf;
+
+ sc = arg;
+ cpu = PCPU_GET(cpuid);
+ tf = curthread->td_intr_frame;
+
+ VIC_LOCK(sc);
+ pending = INTC_VIC_READ_4(sc, VICIRQSTATUS);
+ VIC_UNLOCK(sc);
+ for (num = 0 ; num < VIC_NIRQS; num++) {
+ if (pending & (1 << num))
+ pl190_intc_irq_dispatch(sc, num, tf);
+ }
+
+ return (FILTER_HANDLED);
+}
+
+static void
+pl190_intc_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct pl190_intc_softc *sc;
+ struct pl190_intc_irqsrc *src;
+
+ sc = device_get_softc(dev);
+ src = (struct pl190_intc_irqsrc *)isrc;
+
+ VIC_LOCK(sc);
+ INTC_VIC_WRITE_4(sc, VICINTENCLEAR, (1 << src->irq));
+ VIC_UNLOCK(sc);
+}
+
+static void
+pl190_intc_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct pl190_intc_softc *sc;
+ struct pl190_intc_irqsrc *src;
+
+ sc = device_get_softc(dev);
+ src = (struct pl190_intc_irqsrc *)isrc;
+
+ VIC_LOCK(sc);
+ INTC_VIC_WRITE_4(sc, VICINTENABLE, (1 << src->irq));
+ VIC_UNLOCK(sc);
+}
+
+static int
+pl190_intc_map_intr(device_t dev, struct intr_map_data *data,
+ struct intr_irqsrc **isrcp)
+{
+ struct intr_map_data_fdt *daf;
+ struct pl190_intc_softc *sc;
+
+ if (data->type != INTR_MAP_DATA_FDT)
+ return (ENOTSUP);
+
+ daf = (struct intr_map_data_fdt *)data;
+ if (daf->ncells != 1 || daf->cells[0] >= VIC_NIRQS)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+ *isrcp = &sc->isrcs[daf->cells[0]].isrc;
+ return (0);
+}
+
+static void
+pl190_intc_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+ pl190_intc_disable_intr(dev, isrc);
+}
+
+static void
+pl190_intc_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct pl190_intc_irqsrc *src;
+
+ src = (struct pl190_intc_irqsrc *)isrc;
+ pl190_intc_enable_intr(dev, isrc);
+ arm_irq_memory_barrier(src->irq);
+}
+
+static void
+pl190_intc_post_filter(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct pl190_intc_irqsrc *src;
+
+ src = (struct pl190_intc_irqsrc *)isrc;
+ arm_irq_memory_barrier(src->irq);
+}
+
+static int
+pl190_intc_setup_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+
+ return (0);
+}
+
+static int
+pl190_intc_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "arm,versatile-vic"))
+ return (ENXIO);
+ device_set_desc(dev, "ARM PL190 VIC");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+pl190_intc_attach(device_t dev)
+{
+ struct pl190_intc_softc *sc;
+ uint32_t id;
+ int i, rid;
+ struct pl190_intc_irqsrc *isrcs;
+ struct intr_pic *pic;
+ int error;
+ uint32_t irq;
+ const char *name;
+ phandle_t xref;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ mtx_init(&sc->mtx, device_get_nameunit(dev), "pl190",
+ MTX_SPIN);
+
+ /* Request memory resources */
+ rid = 0;
+ sc->intc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->intc_res == NULL) {
+ device_printf(dev, "Error: could not allocate memory resources\n");
+ return (ENXIO);
+ }
+
+ /*
+ * All interrupts should use IRQ line
+ */
+ INTC_VIC_WRITE_4(sc, VICINTSELECT, 0x00000000);
+ /* Disable all interrupts */
+ INTC_VIC_WRITE_4(sc, VICINTENCLEAR, 0xffffffff);
+
+ id = 0;
+ for (i = 3; i >= 0; i--) {
+ id = (id << 8) |
+ (INTC_VIC_READ_4(sc, VICPERIPHID + i*4) & 0xff);
+ }
+
+ device_printf(dev, "Peripheral ID: %08x\n", id);
+
+ id = 0;
+ for (i = 3; i >= 0; i--) {
+ id = (id << 8) |
+ (INTC_VIC_READ_4(sc, VICPRIMECELLID + i*4) & 0xff);
+ }
+
+ device_printf(dev, "PrimeCell ID: %08x\n", id);
+
+ /* PIC attachment */
+ isrcs = sc->isrcs;
+ name = device_get_nameunit(sc->dev);
+ for (irq = 0; irq < VIC_NIRQS; irq++) {
+ isrcs[irq].irq = irq;
+ error = intr_isrc_register(&isrcs[irq].isrc, sc->dev,
+ 0, "%s,%u", name, irq);
+ if (error != 0)
+ return (error);
+ }
+
+ xref = OF_xref_from_node(ofw_bus_get_node(sc->dev));
+ pic = intr_pic_register(sc->dev, xref);
+ if (pic == NULL)
+ return (ENXIO);
+
+ return (intr_pic_claim_root(sc->dev, xref, pl190_intc_intr, sc, 0));
+}
+
+static device_method_t pl190_intc_methods[] = {
+ DEVMETHOD(device_probe, pl190_intc_probe),
+ DEVMETHOD(device_attach, pl190_intc_attach),
+
+ DEVMETHOD(pic_disable_intr, pl190_intc_disable_intr),
+ DEVMETHOD(pic_enable_intr, pl190_intc_enable_intr),
+ DEVMETHOD(pic_map_intr, pl190_intc_map_intr),
+ DEVMETHOD(pic_post_filter, pl190_intc_post_filter),
+ DEVMETHOD(pic_post_ithread, pl190_intc_post_ithread),
+ DEVMETHOD(pic_pre_ithread, pl190_intc_pre_ithread),
+ DEVMETHOD(pic_setup_intr, pl190_intc_setup_intr),
+
+ DEVMETHOD_END
+};
+
+static driver_t pl190_intc_driver = {
+ "intc",
+ pl190_intc_methods,
+ sizeof(struct pl190_intc_softc),
+};
+
+static devclass_t pl190_intc_devclass;
+
+EARLY_DRIVER_MODULE(intc, simplebus, pl190_intc_driver, pl190_intc_devclass,
+ 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
diff --git a/sys/arm/arm/pl310.c b/sys/arm/arm/pl310.c
new file mode 100644
index 000000000000..86e5fb84c7db
--- /dev/null
+++ b/sys/arm/arm/pl310.c
@@ -0,0 +1,593 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 2012 Olivier Houchard <cognet@FreeBSD.org>
+ * Copyright (c) 2011
+ * Ben Gray <ben.r.gray@gmail.com>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BEN GRAY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/rman.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <machine/intr.h>
+
+#include <machine/bus.h>
+#include <machine/pl310.h>
+#ifdef PLATFORM
+#include <machine/platformvar.h>
+#endif
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#ifdef PLATFORM
+#include "platform_pl310_if.h"
+#endif
+
+/*
+ * Define this if you need to disable PL310 for debugging purpose
+ * Spec:
+ * http://infocenter.arm.com/help/topic/com.arm.doc.ddi0246e/DDI0246E_l2c310_r3p1_trm.pdf
+ */
+
+/*
+ * Hardcode errata for now
+ * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0246b/pr01s02s02.html
+ */
+#define PL310_ERRATA_588369
+#define PL310_ERRATA_753970
+#define PL310_ERRATA_727915
+
+#define PL310_LOCK(sc) do { \
+ mtx_lock_spin(&(sc)->sc_mtx); \
+} while(0);
+
+#define PL310_UNLOCK(sc) do { \
+ mtx_unlock_spin(&(sc)->sc_mtx); \
+} while(0);
+
+static int pl310_enabled = 1;
+TUNABLE_INT("hw.pl310.enabled", &pl310_enabled);
+
+static uint32_t g_l2cache_way_mask;
+
+static const uint32_t g_l2cache_line_size = 32;
+static const uint32_t g_l2cache_align_mask = (32 - 1);
+
+static uint32_t g_l2cache_size;
+static uint32_t g_way_size;
+static uint32_t g_ways_assoc;
+
+static struct pl310_softc *pl310_softc;
+
+static struct ofw_compat_data compat_data[] = {
+ {"arm,pl310", true}, /* Non-standard, FreeBSD. */
+ {"arm,pl310-cache", true},
+ {NULL, false}
+};
+
+#ifdef PLATFORM
+static void
+platform_pl310_init(struct pl310_softc *sc)
+{
+
+ PLATFORM_PL310_INIT(platform_obj(), sc);
+}
+
+static void
+platform_pl310_write_ctrl(struct pl310_softc *sc, uint32_t val)
+{
+
+ PLATFORM_PL310_WRITE_CTRL(platform_obj(), sc, val);
+}
+
+static void
+platform_pl310_write_debug(struct pl310_softc *sc, uint32_t val)
+{
+
+ PLATFORM_PL310_WRITE_DEBUG(platform_obj(), sc, val);
+}
+#endif
+
+static void
+pl310_print_config(struct pl310_softc *sc)
+{
+ uint32_t aux, prefetch;
+ const char *dis = "disabled";
+ const char *ena = "enabled";
+
+ aux = pl310_read4(sc, PL310_AUX_CTRL);
+ prefetch = pl310_read4(sc, PL310_PREFETCH_CTRL);
+
+ device_printf(sc->sc_dev, "Early BRESP response: %s\n",
+ (aux & AUX_CTRL_EARLY_BRESP) ? ena : dis);
+ device_printf(sc->sc_dev, "Instruction prefetch: %s\n",
+ (aux & AUX_CTRL_INSTR_PREFETCH) ? ena : dis);
+ device_printf(sc->sc_dev, "Data prefetch: %s\n",
+ (aux & AUX_CTRL_DATA_PREFETCH) ? ena : dis);
+ device_printf(sc->sc_dev, "Non-secure interrupt control: %s\n",
+ (aux & AUX_CTRL_NS_INT_CTRL) ? ena : dis);
+ device_printf(sc->sc_dev, "Non-secure lockdown: %s\n",
+ (aux & AUX_CTRL_NS_LOCKDOWN) ? ena : dis);
+ device_printf(sc->sc_dev, "Share override: %s\n",
+ (aux & AUX_CTRL_SHARE_OVERRIDE) ? ena : dis);
+
+ device_printf(sc->sc_dev, "Double linefill: %s\n",
+ (prefetch & PREFETCH_CTRL_DL) ? ena : dis);
+ device_printf(sc->sc_dev, "Instruction prefetch: %s\n",
+ (prefetch & PREFETCH_CTRL_INSTR_PREFETCH) ? ena : dis);
+ device_printf(sc->sc_dev, "Data prefetch: %s\n",
+ (prefetch & PREFETCH_CTRL_DATA_PREFETCH) ? ena : dis);
+ device_printf(sc->sc_dev, "Double linefill on WRAP request: %s\n",
+ (prefetch & PREFETCH_CTRL_DL_ON_WRAP) ? ena : dis);
+ device_printf(sc->sc_dev, "Prefetch drop: %s\n",
+ (prefetch & PREFETCH_CTRL_PREFETCH_DROP) ? ena : dis);
+ device_printf(sc->sc_dev, "Incr double Linefill: %s\n",
+ (prefetch & PREFETCH_CTRL_INCR_DL) ? ena : dis);
+ device_printf(sc->sc_dev, "Not same ID on exclusive sequence: %s\n",
+ (prefetch & PREFETCH_CTRL_NOTSAMEID) ? ena : dis);
+ device_printf(sc->sc_dev, "Prefetch offset: %d\n",
+ (prefetch & PREFETCH_CTRL_OFFSET_MASK));
+}
+
+void
+pl310_set_ram_latency(struct pl310_softc *sc, uint32_t which_reg,
+ uint32_t read, uint32_t write, uint32_t setup)
+{
+ uint32_t v;
+
+ KASSERT(which_reg == PL310_TAG_RAM_CTRL ||
+ which_reg == PL310_DATA_RAM_CTRL,
+ ("bad pl310 ram latency register address"));
+
+ v = pl310_read4(sc, which_reg);
+ if (setup != 0) {
+ KASSERT(setup <= 8, ("bad pl310 setup latency: %d", setup));
+ v &= ~RAM_CTRL_SETUP_MASK;
+ v |= (setup - 1) << RAM_CTRL_SETUP_SHIFT;
+ }
+ if (read != 0) {
+ KASSERT(read <= 8, ("bad pl310 read latency: %d", read));
+ v &= ~RAM_CTRL_READ_MASK;
+ v |= (read - 1) << RAM_CTRL_READ_SHIFT;
+ }
+ if (write != 0) {
+ KASSERT(write <= 8, ("bad pl310 write latency: %d", write));
+ v &= ~RAM_CTRL_WRITE_MASK;
+ v |= (write - 1) << RAM_CTRL_WRITE_SHIFT;
+ }
+ pl310_write4(sc, which_reg, v);
+}
+
+static int
+pl310_filter(void *arg)
+{
+ struct pl310_softc *sc = arg;
+ uint32_t intr;
+
+ intr = pl310_read4(sc, PL310_INTR_MASK);
+
+ if (!sc->sc_enabled && (intr & INTR_MASK_ECNTR)) {
+ /*
+ * This is for debug purpose, so be blunt about it
+ * We disable PL310 only when something fishy is going
+ * on and we need to make sure L2 cache is 100% disabled
+ */
+ panic("pl310: caches disabled but cache event detected\n");
+ }
+
+ return (FILTER_HANDLED);
+}
+
+static __inline void
+pl310_wait_background_op(uint32_t off, uint32_t mask)
+{
+
+ while (pl310_read4(pl310_softc, off) & mask)
+ continue;
+}
+
+/**
+ * pl310_cache_sync - performs a cache sync operation
+ *
+ * According to the TRM:
+ *
+ * "Before writing to any other register you must perform an explicit
+ * Cache Sync operation. This is particularly important when the cache is
+ * enabled and changes to how the cache allocates new lines are to be made."
+ *
+ *
+ */
+static __inline void
+pl310_cache_sync(void)
+{
+
+ if ((pl310_softc == NULL) || !pl310_softc->sc_enabled)
+ return;
+
+ /* Do not sync outer cache on IO coherent platform */
+ if (pl310_softc->sc_io_coherent)
+ return;
+
+#ifdef PL310_ERRATA_753970
+ if (pl310_softc->sc_rtl_revision == CACHE_ID_RELEASE_r3p0)
+ /* Write uncached PL310 register */
+ pl310_write4(pl310_softc, 0x740, 0xffffffff);
+ else
+#endif
+ pl310_write4(pl310_softc, PL310_CACHE_SYNC, 0xffffffff);
+}
+
+static void
+pl310_wbinv_all(void)
+{
+
+ if ((pl310_softc == NULL) || !pl310_softc->sc_enabled)
+ return;
+
+ PL310_LOCK(pl310_softc);
+#ifdef PL310_ERRATA_727915
+ if (pl310_softc->sc_rtl_revision == CACHE_ID_RELEASE_r2p0) {
+ int i, j;
+
+ for (i = 0; i < g_ways_assoc; i++) {
+ for (j = 0; j < g_way_size / g_l2cache_line_size; j++) {
+ pl310_write4(pl310_softc,
+ PL310_CLEAN_INV_LINE_IDX,
+ (i << 28 | j << 5));
+ }
+ }
+ pl310_cache_sync();
+ PL310_UNLOCK(pl310_softc);
+ return;
+ }
+ if (pl310_softc->sc_rtl_revision == CACHE_ID_RELEASE_r3p0)
+ platform_pl310_write_debug(pl310_softc, 3);
+#endif
+ pl310_write4(pl310_softc, PL310_CLEAN_INV_WAY, g_l2cache_way_mask);
+ pl310_wait_background_op(PL310_CLEAN_INV_WAY, g_l2cache_way_mask);
+ pl310_cache_sync();
+#ifdef PL310_ERRATA_727915
+ if (pl310_softc->sc_rtl_revision == CACHE_ID_RELEASE_r3p0)
+ platform_pl310_write_debug(pl310_softc, 0);
+#endif
+ PL310_UNLOCK(pl310_softc);
+}
+
+static void
+pl310_wbinv_range(vm_paddr_t start, vm_size_t size)
+{
+
+ if ((pl310_softc == NULL) || !pl310_softc->sc_enabled)
+ return;
+
+ PL310_LOCK(pl310_softc);
+ if (start & g_l2cache_align_mask) {
+ size += start & g_l2cache_align_mask;
+ start &= ~g_l2cache_align_mask;
+ }
+ if (size & g_l2cache_align_mask) {
+ size &= ~g_l2cache_align_mask;
+ size += g_l2cache_line_size;
+ }
+
+#ifdef PL310_ERRATA_727915
+ if (pl310_softc->sc_rtl_revision >= CACHE_ID_RELEASE_r2p0 &&
+ pl310_softc->sc_rtl_revision < CACHE_ID_RELEASE_r3p1)
+ platform_pl310_write_debug(pl310_softc, 3);
+#endif
+ while (size > 0) {
+#ifdef PL310_ERRATA_588369
+ if (pl310_softc->sc_rtl_revision <= CACHE_ID_RELEASE_r1p0) {
+ /*
+ * Errata 588369 says that clean + inv may keep the
+ * cache line if it was clean, the recommanded
+ * workaround is to clean then invalidate the cache
+ * line, with write-back and cache linefill disabled.
+ */
+ pl310_write4(pl310_softc, PL310_CLEAN_LINE_PA, start);
+ pl310_write4(pl310_softc, PL310_INV_LINE_PA, start);
+ } else
+#endif
+ pl310_write4(pl310_softc, PL310_CLEAN_INV_LINE_PA,
+ start);
+ start += g_l2cache_line_size;
+ size -= g_l2cache_line_size;
+ }
+#ifdef PL310_ERRATA_727915
+ if (pl310_softc->sc_rtl_revision >= CACHE_ID_RELEASE_r2p0 &&
+ pl310_softc->sc_rtl_revision < CACHE_ID_RELEASE_r3p1)
+ platform_pl310_write_debug(pl310_softc, 0);
+#endif
+
+ pl310_cache_sync();
+ PL310_UNLOCK(pl310_softc);
+}
+
+static void
+pl310_wb_range(vm_paddr_t start, vm_size_t size)
+{
+
+ if ((pl310_softc == NULL) || !pl310_softc->sc_enabled)
+ return;
+
+ PL310_LOCK(pl310_softc);
+ if (start & g_l2cache_align_mask) {
+ size += start & g_l2cache_align_mask;
+ start &= ~g_l2cache_align_mask;
+ }
+
+ if (size & g_l2cache_align_mask) {
+ size &= ~g_l2cache_align_mask;
+ size += g_l2cache_line_size;
+ }
+
+ while (size > 0) {
+ pl310_write4(pl310_softc, PL310_CLEAN_LINE_PA, start);
+ start += g_l2cache_line_size;
+ size -= g_l2cache_line_size;
+ }
+
+ pl310_cache_sync();
+ PL310_UNLOCK(pl310_softc);
+}
+
+static void
+pl310_inv_range(vm_paddr_t start, vm_size_t size)
+{
+
+ if ((pl310_softc == NULL) || !pl310_softc->sc_enabled)
+ return;
+
+ PL310_LOCK(pl310_softc);
+ if (start & g_l2cache_align_mask) {
+ size += start & g_l2cache_align_mask;
+ start &= ~g_l2cache_align_mask;
+ }
+ if (size & g_l2cache_align_mask) {
+ size &= ~g_l2cache_align_mask;
+ size += g_l2cache_line_size;
+ }
+ while (size > 0) {
+ pl310_write4(pl310_softc, PL310_INV_LINE_PA, start);
+ start += g_l2cache_line_size;
+ size -= g_l2cache_line_size;
+ }
+
+ pl310_cache_sync();
+ PL310_UNLOCK(pl310_softc);
+}
+
+static void
+pl310_drain_writebuf(void)
+{
+
+ if ((pl310_softc == NULL) || !pl310_softc->sc_enabled)
+ return;
+
+ PL310_LOCK(pl310_softc);
+ pl310_cache_sync();
+ PL310_UNLOCK(pl310_softc);
+}
+
+static void
+pl310_set_way_sizes(struct pl310_softc *sc)
+{
+ uint32_t aux_value;
+
+ aux_value = pl310_read4(sc, PL310_AUX_CTRL);
+ g_way_size = (aux_value & AUX_CTRL_WAY_SIZE_MASK) >>
+ AUX_CTRL_WAY_SIZE_SHIFT;
+ g_way_size = 1 << (g_way_size + 13);
+ if (aux_value & (1 << AUX_CTRL_ASSOCIATIVITY_SHIFT))
+ g_ways_assoc = 16;
+ else
+ g_ways_assoc = 8;
+ g_l2cache_way_mask = (1 << g_ways_assoc) - 1;
+ g_l2cache_size = g_way_size * g_ways_assoc;
+}
+
+/*
+ * Setup interrupt handling. This is done only if the cache controller is
+ * disabled, for debugging. We set counters so when a cache event happens we'll
+ * get interrupted and be warned that something is wrong, because no cache
+ * events should happen if we're disabled.
+ */
+static void
+pl310_config_intr(void *arg)
+{
+ struct pl310_softc * sc;
+
+ sc = arg;
+
+ /* activate the interrupt */
+ bus_setup_intr(sc->sc_dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+ pl310_filter, NULL, sc, &sc->sc_irq_h);
+
+ /* Cache Line Eviction for Counter 0 */
+ pl310_write4(sc, PL310_EVENT_COUNTER0_CONF,
+ EVENT_COUNTER_CONF_INCR | EVENT_COUNTER_CONF_CO);
+ /* Data Read Request for Counter 1 */
+ pl310_write4(sc, PL310_EVENT_COUNTER1_CONF,
+ EVENT_COUNTER_CONF_INCR | EVENT_COUNTER_CONF_DRREQ);
+
+ /* Enable and clear pending interrupts */
+ pl310_write4(sc, PL310_INTR_CLEAR, INTR_MASK_ECNTR);
+ pl310_write4(sc, PL310_INTR_MASK, INTR_MASK_ALL);
+
+ /* Enable counters and reset C0 and C1 */
+ pl310_write4(sc, PL310_EVENT_COUNTER_CTRL,
+ EVENT_COUNTER_CTRL_ENABLED |
+ EVENT_COUNTER_CTRL_C0_RESET |
+ EVENT_COUNTER_CTRL_C1_RESET);
+
+ config_intrhook_disestablish(sc->sc_ich);
+ free(sc->sc_ich, M_DEVBUF);
+ sc->sc_ich = NULL;
+}
+
+static int
+pl310_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+ if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+ return (ENXIO);
+ device_set_desc(dev, "PL310 L2 cache controller");
+ return (0);
+}
+
+static int
+pl310_attach(device_t dev)
+{
+ struct pl310_softc *sc = device_get_softc(dev);
+ int rid;
+ uint32_t cache_id, debug_ctrl;
+ phandle_t node;
+
+ sc->sc_dev = dev;
+ rid = 0;
+ sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->sc_mem_res == NULL)
+ panic("%s: Cannot map registers", device_get_name(dev));
+
+ /* Allocate an IRQ resource */
+ rid = 0;
+ sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE | RF_SHAREABLE);
+ if (sc->sc_irq_res == NULL) {
+ device_printf(dev, "cannot allocate IRQ, not using interrupt\n");
+ }
+
+ pl310_softc = sc;
+ mtx_init(&sc->sc_mtx, "pl310lock", NULL, MTX_SPIN);
+
+ cache_id = pl310_read4(sc, PL310_CACHE_ID);
+ sc->sc_rtl_revision = (cache_id >> CACHE_ID_RELEASE_SHIFT) &
+ CACHE_ID_RELEASE_MASK;
+ device_printf(dev, "Part number: 0x%x, release: 0x%x\n",
+ (cache_id >> CACHE_ID_PARTNUM_SHIFT) & CACHE_ID_PARTNUM_MASK,
+ (cache_id >> CACHE_ID_RELEASE_SHIFT) & CACHE_ID_RELEASE_MASK);
+
+ /*
+ * Test for "arm,io-coherent" property and disable sync operation if
+ * platform is I/O coherent. Outer sync operations are not needed
+ * on coherent platform and may be harmful in certain situations.
+ */
+ node = ofw_bus_get_node(dev);
+ if (OF_hasprop(node, "arm,io-coherent"))
+ sc->sc_io_coherent = true;
+
+ /*
+ * If L2 cache is already enabled then something has violated the rules,
+ * because caches are supposed to be off at kernel entry. The cache
+ * must be disabled to write the configuration registers without
+ * triggering an access error (SLVERR), but there's no documented safe
+ * procedure for disabling the L2 cache in the manual. So we'll try to
+ * invent one:
+ * - Use the debug register to force write-through mode and prevent
+ * linefills (allocation of new lines on read); now anything we do
+ * will not cause new data to come into the L2 cache.
+ * - Writeback and invalidate the current contents.
+ * - Disable the controller.
+ * - Restore the original debug settings.
+ */
+ if (pl310_read4(sc, PL310_CTRL) & CTRL_ENABLED) {
+ device_printf(dev, "Warning: L2 Cache should not already be "
+ "active; trying to de-activate and re-initialize...\n");
+ sc->sc_enabled = 1;
+ debug_ctrl = pl310_read4(sc, PL310_DEBUG_CTRL);
+ platform_pl310_write_debug(sc, debug_ctrl |
+ DEBUG_CTRL_DISABLE_WRITEBACK | DEBUG_CTRL_DISABLE_LINEFILL);
+ pl310_set_way_sizes(sc);
+ pl310_wbinv_all();
+ platform_pl310_write_ctrl(sc, CTRL_DISABLED);
+ platform_pl310_write_debug(sc, debug_ctrl);
+ }
+ sc->sc_enabled = pl310_enabled;
+
+ if (sc->sc_enabled) {
+ platform_pl310_init(sc);
+ pl310_set_way_sizes(sc); /* platform init might change these */
+ pl310_write4(pl310_softc, PL310_INV_WAY, 0xffff);
+ pl310_wait_background_op(PL310_INV_WAY, 0xffff);
+ platform_pl310_write_ctrl(sc, CTRL_ENABLED);
+ device_printf(dev, "L2 Cache enabled: %uKB/%dB %d ways\n",
+ (g_l2cache_size / 1024), g_l2cache_line_size, g_ways_assoc);
+ if (bootverbose)
+ pl310_print_config(sc);
+ } else {
+ if (sc->sc_irq_res != NULL) {
+ sc->sc_ich = malloc(sizeof(*sc->sc_ich), M_DEVBUF, M_WAITOK);
+ sc->sc_ich->ich_func = pl310_config_intr;
+ sc->sc_ich->ich_arg = sc;
+ if (config_intrhook_establish(sc->sc_ich) != 0) {
+ device_printf(dev,
+ "config_intrhook_establish failed\n");
+ free(sc->sc_ich, M_DEVBUF);
+ return(ENXIO);
+ }
+ }
+
+ device_printf(dev, "L2 Cache disabled\n");
+ }
+
+ /* Set the l2 functions in the set of cpufuncs */
+ cpufuncs.cf_l2cache_wbinv_all = pl310_wbinv_all;
+ cpufuncs.cf_l2cache_wbinv_range = pl310_wbinv_range;
+ cpufuncs.cf_l2cache_inv_range = pl310_inv_range;
+ cpufuncs.cf_l2cache_wb_range = pl310_wb_range;
+ cpufuncs.cf_l2cache_drain_writebuf = pl310_drain_writebuf;
+
+ return (0);
+}
+
+static device_method_t pl310_methods[] = {
+ DEVMETHOD(device_probe, pl310_probe),
+ DEVMETHOD(device_attach, pl310_attach),
+ DEVMETHOD_END
+};
+
+static driver_t pl310_driver = {
+ "l2cache",
+ pl310_methods,
+ sizeof(struct pl310_softc),
+};
+static devclass_t pl310_devclass;
+
+EARLY_DRIVER_MODULE(pl310, simplebus, pl310_driver, pl310_devclass, 0, 0,
+ BUS_PASS_CPU + BUS_PASS_ORDER_MIDDLE);
diff --git a/sys/arm/arm/platform.c b/sys/arm/arm/platform.c
new file mode 100644
index 000000000000..465fd90b87f7
--- /dev/null
+++ b/sys/arm/arm/platform.c
@@ -0,0 +1,244 @@
+/*-
+ * Copyright (c) 2005 Peter Grehan
+ * Copyright (c) 2009 Nathan Whitehorn
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Dispatch platform calls to the appropriate platform implementation
+ * through a previously registered kernel object.
+ */
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/ktr.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/systm.h>
+#include <sys/smp.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+
+#include <vm/vm.h>
+#include <vm/vm_page.h>
+
+#include <machine/bus_dma.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+#include <machine/machdep.h>
+#include <machine/md_var.h>
+#include <machine/platform.h>
+#include <machine/platformvar.h>
+#include <machine/smp.h>
+
+#include "platform_if.h"
+
+static platform_def_t *plat_def_impl;
+static platform_t plat_obj;
+static struct kobj_ops plat_kernel_kops;
+static struct platform_kobj plat_kernel_obj;
+
+static char plat_name[64];
+SYSCTL_STRING(_hw, OID_AUTO, platform, CTLFLAG_RDTUN | CTLFLAG_NOFETCH, plat_name, 0,
+ "Platform currently in use");
+
+/*
+ * Platform install routines. Highest priority wins, using the same
+ * algorithm as bus attachment.
+ */
+SET_DECLARE(platform_set, platform_def_t);
+
+static delay_func platform_delay;
+
+platform_t
+platform_obj(void)
+{
+
+ return (plat_obj);
+}
+
+void
+platform_probe_and_attach(void)
+{
+ platform_def_t **platpp, *platp;
+ int prio, best_prio;
+
+ plat_obj = &plat_kernel_obj;
+ best_prio = 0;
+
+ /*
+ * We are unable to use TUNABLE_STR as the read will happen
+ * well after this function has returned.
+ */
+ TUNABLE_STR_FETCH("hw.platform", plat_name, sizeof(plat_name));
+
+ /*
+ * Try to locate the best platform kobj
+ */
+ SET_FOREACH(platpp, platform_set) {
+ platp = *platpp;
+
+ /*
+ * Take care of compiling the selected class, and
+ * then statically initialise the MMU object
+ */
+ kobj_class_compile_static((kobj_class_t)platp,
+ &plat_kernel_kops);
+ kobj_init_static((kobj_t)plat_obj, (kobj_class_t)platp);
+
+ plat_obj->cls = platp;
+
+ prio = PLATFORM_PROBE(plat_obj);
+
+ /* Check for errors */
+ if (prio > 0)
+ continue;
+
+ /*
+ * Check if this module was specifically requested through
+ * the loader tunable we provide.
+ */
+ if (strcmp(platp->name,plat_name) == 0) {
+ plat_def_impl = platp;
+ break;
+ }
+
+ /* Otherwise, see if it is better than our current best */
+ if (plat_def_impl == NULL || prio > best_prio) {
+ best_prio = prio;
+ plat_def_impl = platp;
+ }
+
+ /*
+ * We can't free the KOBJ, since it is static. Reset the ops
+ * member of this class so that we can come back later.
+ */
+ platp->ops = NULL;
+ }
+
+ if (plat_def_impl == NULL)
+ panic("No platform module found!");
+
+ /*
+ * Recompile to make sure we ended with the
+ * correct one, and then attach.
+ */
+
+ kobj_class_compile_static((kobj_class_t)plat_def_impl,
+ &plat_kernel_kops);
+ kobj_init_static((kobj_t)plat_obj, (kobj_class_t)plat_def_impl);
+
+ strlcpy(plat_name, plat_def_impl->name, sizeof(plat_name));
+
+ /* Set a default delay function */
+ arm_set_delay(platform_delay, NULL);
+
+ PLATFORM_ATTACH(plat_obj);
+}
+
+int
+platform_devmap_init(void)
+{
+
+ return PLATFORM_DEVMAP_INIT(plat_obj);
+}
+
+vm_offset_t
+platform_lastaddr(void)
+{
+
+ return PLATFORM_LASTADDR(plat_obj);
+}
+
+void
+platform_gpio_init(void)
+{
+
+ PLATFORM_GPIO_INIT(plat_obj);
+}
+
+void
+platform_late_init(void)
+{
+
+ PLATFORM_LATE_INIT(plat_obj);
+}
+
+void
+cpu_reset(void)
+{
+
+ PLATFORM_CPU_RESET(plat_obj);
+
+ printf("cpu_reset failed");
+
+ intr_disable();
+ while(1) {
+ cpu_sleep(0);
+ }
+}
+
+static void
+platform_delay(int usec, void *arg __unused)
+{
+ int counts;
+
+ for (; usec > 0; usec--)
+ for (counts = plat_obj->cls->delay_count; counts > 0; counts--)
+ /*
+ * Prevent the compiler from optimizing
+ * out the loop
+ */
+ cpufunc_nullop();
+}
+
+#if defined(SMP)
+void
+platform_mp_setmaxid(void)
+{
+ int ncpu;
+
+ PLATFORM_MP_SETMAXID(plat_obj);
+
+ if (TUNABLE_INT_FETCH("hw.ncpu", &ncpu)) {
+ if (ncpu >= 1 && ncpu <= mp_ncpus) {
+ mp_ncpus = ncpu;
+ mp_maxid = ncpu - 1;
+ }
+ }
+}
+
+void
+platform_mp_start_ap(void)
+{
+
+ PLATFORM_MP_START_AP(plat_obj);
+}
+#endif
diff --git a/sys/arm/arm/platform_if.m b/sys/arm/arm/platform_if.m
new file mode 100644
index 000000000000..1539e771c4e5
--- /dev/null
+++ b/sys/arm/arm/platform_if.m
@@ -0,0 +1,148 @@
+#-
+# Copyright (c) 2009 Nathan Whitehorn
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/devmap.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/smp.h>
+
+#include <machine/machdep.h>
+#include <machine/platform.h>
+#include <machine/platformvar.h>
+#include <machine/smp.h>
+#include <machine/vmparam.h>
+
+/**
+ * @defgroup PLATFORM platform - KObj methods for ARM platform
+ * implementations
+ * @brief A set of methods required by all platform implementations.
+ * These are used to bring up secondary CPUs, supply the physical memory
+ * map, etc.
+ *@{
+ */
+
+INTERFACE platform;
+
+#
+# Default implementations
+#
+CODE {
+ static void platform_null_attach(platform_t plat)
+ {
+ return;
+ }
+
+ static vm_offset_t platform_default_lastaddr(platform_t plat)
+ {
+ return (devmap_lastaddr());
+ }
+
+ static void platform_default_mp_setmaxid(platform_t plat)
+ {
+ mp_ncpus = 1;
+ mp_maxid = 0;
+ }
+};
+
+/**
+ * @brief Probe for whether we are on this platform, returning the standard
+ * newbus probe codes. If we have Open Firmware or a flattened device tree,
+ * it is guaranteed to be available at this point.
+ */
+METHOD int probe {
+ platform_t _plat;
+};
+
+/**
+ * @brief Attach this platform module. This happens before the MMU is online,
+ * so the platform module can install its own high-priority MMU module at
+ * this point.
+ */
+METHOD int attach {
+ platform_t _plat;
+} DEFAULT platform_null_attach;
+
+/**
+ * @brief Called as one of the last steps of early virtual memory
+ * initialization, shortly before the new page tables are installed.
+ */
+METHOD int devmap_init {
+ platform_t _plat;
+};
+
+/**
+ * @brief Called after devmap_init(), and must return the address of the
+ * first byte of unusable KVA space. This allows a platform to carve out
+ * of the top of the KVA space whatever reserves it needs for things like
+ * static device mapping, and this is called to get the value before
+ * calling pmap_bootstrap() which uses the value to size the available KVA.
+ */
+METHOD vm_offset_t lastaddr {
+ platform_t _plat;
+} DEFAULT platform_default_lastaddr;
+
+/**
+ * @brief Called after the static device mappings are established and just
+ * before cninit(). The intention is that the routine can do any hardware
+ * setup (such as gpio or pinmux) necessary to make the console functional.
+ */
+METHOD void gpio_init {
+ platform_t _plat;
+};
+
+/**
+ * @brief Called just after cninit(). This is the first of the init
+ * routines that can use printf() and expect the output to appear on
+ * a standard console.
+ */
+METHOD void late_init {
+ platform_t _plat;
+};
+
+/**
+ * @brief Called by cpu_mp_setmaxid() to set mp_maxid and mp_ncpus.
+ */
+METHOD void mp_setmaxid {
+ platform_t _plat;
+} DEFAULT platform_default_mp_setmaxid;
+
+/**
+ * @brief Called by cpu_mp_start to start the secondary processors.
+ */
+METHOD void mp_start_ap {
+ platform_t _plat;
+};
+
+/**
+ * @brief Called by cpu_reset to reboot.
+ */
+METHOD void cpu_reset {
+ platform_t _plat;
+};
diff --git a/sys/arm/arm/platform_pl310_if.m b/sys/arm/arm/platform_pl310_if.m
new file mode 100644
index 000000000000..e032bebbf8aa
--- /dev/null
+++ b/sys/arm/arm/platform_pl310_if.m
@@ -0,0 +1,84 @@
+#-
+# Copyright (c) 2017 Andrew Turner
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+
+#include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+#include <machine/pl310.h>
+#include <machine/platformvar.h>
+
+INTERFACE platform_pl310;
+
+HEADER {
+ struct pl310_softc;
+};
+
+CODE {
+ static void platform_pl310_default_write_ctrl(platform_t plat,
+ struct pl310_softc *sc, uint32_t val)
+ {
+ pl310_write4(sc, PL310_CTRL, val);
+ }
+
+ static void platform_pl310_default_write_debug(platform_t plat,
+ struct pl310_softc *sc, uint32_t val)
+ {
+ pl310_write4(sc, PL310_DEBUG_CTRL, val);
+ }
+};
+
+/**
+ * Initialize the pl310, e.g. to configure the prefetch control. The following
+ * write functions may have already been called so they must not rely on
+ * this function.
+ */
+METHOD void init {
+ platform_t _plat;
+ struct pl310_softc *sc;
+};
+
+/**
+ * Write to the Control Register.
+ */
+METHOD void write_ctrl {
+ platform_t _plat;
+ struct pl310_softc *sc;
+ uint32_t val;
+} DEFAULT platform_pl310_default_write_ctrl;
+
+/**
+ * Write to the Debug Control Register.
+ */
+METHOD void write_debug {
+ platform_t _plat;
+ struct pl310_softc *sc;
+ uint32_t val;
+} DEFAULT platform_pl310_default_write_debug;
diff --git a/sys/arm/arm/pmap-v6.c b/sys/arm/arm/pmap-v6.c
new file mode 100644
index 000000000000..c9b939f6ce34
--- /dev/null
+++ b/sys/arm/arm/pmap-v6.c
@@ -0,0 +1,6956 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause AND BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 1991 Regents of the University of California.
+ * Copyright (c) 1994 John S. Dyson
+ * Copyright (c) 1994 David Greenman
+ * Copyright (c) 2005-2010 Alan L. Cox <alc@cs.rice.edu>
+ * Copyright (c) 2014-2016 Svatopluk Kraus <skra@FreeBSD.org>
+ * Copyright (c) 2014-2016 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Systems Programming Group of the University of Utah Computer
+ * Science Department and William Jolitz of UUNET Technologies Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)pmap.c 7.7 (Berkeley) 5/12/91
+ */
+/*-
+ * Copyright (c) 2003 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * This software was developed for the FreeBSD Project by Jake Burkholder,
+ * Safeport Network Services, and Network Associates Laboratories, the
+ * Security Research Division of Network Associates, Inc. under
+ * DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA
+ * CHATS research program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Manages physical address maps.
+ *
+ * Since the information managed by this module is
+ * also stored by the logical address mapping module,
+ * this module may throw away valid virtual-to-physical
+ * mappings at almost any time. However, invalidations
+ * of virtual-to-physical mappings must be done as
+ * requested.
+ *
+ * In order to cope with hardware architectures which
+ * make virtual-to-physical map invalidates expensive,
+ * this module may delay invalidate or reduced protection
+ * operations until such time as they are actually
+ * necessary. This module is given full information as
+ * to which processors are currently using which maps,
+ * and to when physical maps must be made correct.
+ */
+
+#include "opt_vm.h"
+#include "opt_pmap.h"
+#include "opt_ddb.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/ktr.h>
+#include <sys/lock.h>
+#include <sys/proc.h>
+#include <sys/rwlock.h>
+#include <sys/malloc.h>
+#include <sys/vmmeter.h>
+#include <sys/malloc.h>
+#include <sys/mman.h>
+#include <sys/sf_buf.h>
+#include <sys/smp.h>
+#include <sys/sched.h>
+#include <sys/sysctl.h>
+
+#ifdef DDB
+#include <ddb/ddb.h>
+#endif
+
+#include <vm/vm.h>
+#include <vm/uma.h>
+#include <vm/pmap.h>
+#include <vm/vm_param.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_object.h>
+#include <vm/vm_map.h>
+#include <vm/vm_page.h>
+#include <vm/vm_pageout.h>
+#include <vm/vm_phys.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_reserv.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <machine/md_var.h>
+#include <machine/pmap_var.h>
+#include <machine/cpu.h>
+#include <machine/pcb.h>
+#include <machine/sf_buf.h>
+#ifdef SMP
+#include <machine/smp.h>
+#endif
+#ifndef PMAP_SHPGPERPROC
+#define PMAP_SHPGPERPROC 200
+#endif
+
+#ifndef DIAGNOSTIC
+#define PMAP_INLINE __inline
+#else
+#define PMAP_INLINE
+#endif
+
+#ifdef PMAP_DEBUG
+static void pmap_zero_page_check(vm_page_t m);
+void pmap_debug(int level);
+int pmap_pid_dump(int pid);
+
+#define PDEBUG(_lev_,_stat_) \
+ if (pmap_debug_level >= (_lev_)) \
+ ((_stat_))
+#define dprintf printf
+int pmap_debug_level = 1;
+#else /* PMAP_DEBUG */
+#define PDEBUG(_lev_,_stat_) /* Nothing */
+#define dprintf(x, arg...)
+#endif /* PMAP_DEBUG */
+
+/*
+ * Level 2 page tables map definion ('max' is excluded).
+ */
+
+#define PT2V_MIN_ADDRESS ((vm_offset_t)PT2MAP)
+#define PT2V_MAX_ADDRESS ((vm_offset_t)PT2MAP + PT2MAP_SIZE)
+
+#define UPT2V_MIN_ADDRESS ((vm_offset_t)PT2MAP)
+#define UPT2V_MAX_ADDRESS \
+ ((vm_offset_t)(PT2MAP + (KERNBASE >> PT2MAP_SHIFT)))
+
+/*
+ * Promotion to a 1MB (PTE1) page mapping requires that the corresponding
+ * 4KB (PTE2) page mappings have identical settings for the following fields:
+ */
+#define PTE2_PROMOTE (PTE2_V | PTE2_A | PTE2_NM | PTE2_S | PTE2_NG | \
+ PTE2_NX | PTE2_RO | PTE2_U | PTE2_W | \
+ PTE2_ATTR_MASK)
+
+#define PTE1_PROMOTE (PTE1_V | PTE1_A | PTE1_NM | PTE1_S | PTE1_NG | \
+ PTE1_NX | PTE1_RO | PTE1_U | PTE1_W | \
+ PTE1_ATTR_MASK)
+
+#define ATTR_TO_L1(l2_attr) ((((l2_attr) & L2_TEX0) ? L1_S_TEX0 : 0) | \
+ (((l2_attr) & L2_C) ? L1_S_C : 0) | \
+ (((l2_attr) & L2_B) ? L1_S_B : 0) | \
+ (((l2_attr) & PTE2_A) ? PTE1_A : 0) | \
+ (((l2_attr) & PTE2_NM) ? PTE1_NM : 0) | \
+ (((l2_attr) & PTE2_S) ? PTE1_S : 0) | \
+ (((l2_attr) & PTE2_NG) ? PTE1_NG : 0) | \
+ (((l2_attr) & PTE2_NX) ? PTE1_NX : 0) | \
+ (((l2_attr) & PTE2_RO) ? PTE1_RO : 0) | \
+ (((l2_attr) & PTE2_U) ? PTE1_U : 0) | \
+ (((l2_attr) & PTE2_W) ? PTE1_W : 0))
+
+#define ATTR_TO_L2(l1_attr) ((((l1_attr) & L1_S_TEX0) ? L2_TEX0 : 0) | \
+ (((l1_attr) & L1_S_C) ? L2_C : 0) | \
+ (((l1_attr) & L1_S_B) ? L2_B : 0) | \
+ (((l1_attr) & PTE1_A) ? PTE2_A : 0) | \
+ (((l1_attr) & PTE1_NM) ? PTE2_NM : 0) | \
+ (((l1_attr) & PTE1_S) ? PTE2_S : 0) | \
+ (((l1_attr) & PTE1_NG) ? PTE2_NG : 0) | \
+ (((l1_attr) & PTE1_NX) ? PTE2_NX : 0) | \
+ (((l1_attr) & PTE1_RO) ? PTE2_RO : 0) | \
+ (((l1_attr) & PTE1_U) ? PTE2_U : 0) | \
+ (((l1_attr) & PTE1_W) ? PTE2_W : 0))
+
+/*
+ * PTE2 descriptors creation macros.
+ */
+#define PTE2_ATTR_DEFAULT vm_memattr_to_pte2(VM_MEMATTR_DEFAULT)
+#define PTE2_ATTR_PT vm_memattr_to_pte2(pt_memattr)
+
+#define PTE2_KPT(pa) PTE2_KERN(pa, PTE2_AP_KRW, PTE2_ATTR_PT)
+#define PTE2_KPT_NG(pa) PTE2_KERN_NG(pa, PTE2_AP_KRW, PTE2_ATTR_PT)
+
+#define PTE2_KRW(pa) PTE2_KERN(pa, PTE2_AP_KRW, PTE2_ATTR_DEFAULT)
+#define PTE2_KRO(pa) PTE2_KERN(pa, PTE2_AP_KR, PTE2_ATTR_DEFAULT)
+
+#define PV_STATS
+#ifdef PV_STATS
+#define PV_STAT(x) do { x ; } while (0)
+#else
+#define PV_STAT(x) do { } while (0)
+#endif
+
+/*
+ * The boot_pt1 is used temporary in very early boot stage as L1 page table.
+ * We can init many things with no memory allocation thanks to its static
+ * allocation and this brings two main advantages:
+ * (1) other cores can be started very simply,
+ * (2) various boot loaders can be supported as its arguments can be processed
+ * in virtual address space and can be moved to safe location before
+ * first allocation happened.
+ * Only disadvantage is that boot_pt1 is used only in very early boot stage.
+ * However, the table is uninitialized and so lays in bss. Therefore kernel
+ * image size is not influenced.
+ *
+ * QQQ: In the future, maybe, boot_pt1 can be used for soft reset and
+ * CPU suspend/resume game.
+ */
+extern pt1_entry_t boot_pt1[];
+
+vm_paddr_t base_pt1;
+pt1_entry_t *kern_pt1;
+pt2_entry_t *kern_pt2tab;
+pt2_entry_t *PT2MAP;
+
+static uint32_t ttb_flags;
+static vm_memattr_t pt_memattr;
+ttb_entry_t pmap_kern_ttb;
+
+struct pmap kernel_pmap_store;
+LIST_HEAD(pmaplist, pmap);
+static struct pmaplist allpmaps;
+static struct mtx allpmaps_lock;
+
+vm_offset_t virtual_avail; /* VA of first avail page (after kernel bss) */
+vm_offset_t virtual_end; /* VA of last avail page (end of kernel AS) */
+
+static vm_offset_t kernel_vm_end_new;
+vm_offset_t kernel_vm_end = KERNBASE + NKPT2PG * NPT2_IN_PG * PTE1_SIZE;
+vm_offset_t vm_max_kernel_address;
+vm_paddr_t kernel_l1pa;
+
+static struct rwlock __aligned(CACHE_LINE_SIZE) pvh_global_lock;
+
+/*
+ * Data for the pv entry allocation mechanism
+ */
+static TAILQ_HEAD(pch, pv_chunk) pv_chunks = TAILQ_HEAD_INITIALIZER(pv_chunks);
+static int pv_entry_count = 0, pv_entry_max = 0, pv_entry_high_water = 0;
+static struct md_page *pv_table; /* XXX: Is it used only the list in md_page? */
+static int shpgperproc = PMAP_SHPGPERPROC;
+
+struct pv_chunk *pv_chunkbase; /* KVA block for pv_chunks */
+int pv_maxchunks; /* How many chunks we have KVA for */
+vm_offset_t pv_vafree; /* freelist stored in the PTE */
+
+vm_paddr_t first_managed_pa;
+#define pa_to_pvh(pa) (&pv_table[pte1_index(pa - first_managed_pa)])
+
+/*
+ * All those kernel PT submaps that BSD is so fond of
+ */
+caddr_t _tmppt = 0;
+
+/*
+ * Crashdump maps.
+ */
+static caddr_t crashdumpmap;
+
+static pt2_entry_t *PMAP1 = NULL, *PMAP2;
+static pt2_entry_t *PADDR1 = NULL, *PADDR2;
+#ifdef DDB
+static pt2_entry_t *PMAP3;
+static pt2_entry_t *PADDR3;
+static int PMAP3cpu __unused; /* for SMP only */
+#endif
+#ifdef SMP
+static int PMAP1cpu;
+static int PMAP1changedcpu;
+SYSCTL_INT(_debug, OID_AUTO, PMAP1changedcpu, CTLFLAG_RD,
+ &PMAP1changedcpu, 0,
+ "Number of times pmap_pte2_quick changed CPU with same PMAP1");
+#endif
+static int PMAP1changed;
+SYSCTL_INT(_debug, OID_AUTO, PMAP1changed, CTLFLAG_RD,
+ &PMAP1changed, 0,
+ "Number of times pmap_pte2_quick changed PMAP1");
+static int PMAP1unchanged;
+SYSCTL_INT(_debug, OID_AUTO, PMAP1unchanged, CTLFLAG_RD,
+ &PMAP1unchanged, 0,
+ "Number of times pmap_pte2_quick didn't change PMAP1");
+static struct mtx PMAP2mutex;
+
+/*
+ * Internal flags for pmap_enter()'s helper functions.
+ */
+#define PMAP_ENTER_NORECLAIM 0x1000000 /* Don't reclaim PV entries. */
+#define PMAP_ENTER_NOREPLACE 0x2000000 /* Don't replace mappings. */
+
+static __inline void pt2_wirecount_init(vm_page_t m);
+static boolean_t pmap_demote_pte1(pmap_t pmap, pt1_entry_t *pte1p,
+ vm_offset_t va);
+static int pmap_enter_pte1(pmap_t pmap, vm_offset_t va, pt1_entry_t pte1,
+ u_int flags, vm_page_t m);
+void cache_icache_sync_fresh(vm_offset_t va, vm_paddr_t pa, vm_size_t size);
+
+/*
+ * Function to set the debug level of the pmap code.
+ */
+#ifdef PMAP_DEBUG
+void
+pmap_debug(int level)
+{
+
+ pmap_debug_level = level;
+ dprintf("pmap_debug: level=%d\n", pmap_debug_level);
+}
+#endif /* PMAP_DEBUG */
+
+/*
+ * This table must corespond with memory attribute configuration in vm.h.
+ * First entry is used for normal system mapping.
+ *
+ * Device memory is always marked as shared.
+ * Normal memory is shared only in SMP .
+ * Not outer shareable bits are not used yet.
+ * Class 6 cannot be used on ARM11.
+ */
+#define TEXDEF_TYPE_SHIFT 0
+#define TEXDEF_TYPE_MASK 0x3
+#define TEXDEF_INNER_SHIFT 2
+#define TEXDEF_INNER_MASK 0x3
+#define TEXDEF_OUTER_SHIFT 4
+#define TEXDEF_OUTER_MASK 0x3
+#define TEXDEF_NOS_SHIFT 6
+#define TEXDEF_NOS_MASK 0x1
+
+#define TEX(t, i, o, s) \
+ ((t) << TEXDEF_TYPE_SHIFT) | \
+ ((i) << TEXDEF_INNER_SHIFT) | \
+ ((o) << TEXDEF_OUTER_SHIFT | \
+ ((s) << TEXDEF_NOS_SHIFT))
+
+static uint32_t tex_class[8] = {
+/* type inner cache outer cache */
+ TEX(PRRR_MEM, NMRR_WB_WA, NMRR_WB_WA, 0), /* 0 - ATTR_WB_WA */
+ TEX(PRRR_MEM, NMRR_NC, NMRR_NC, 0), /* 1 - ATTR_NOCACHE */
+ TEX(PRRR_DEV, NMRR_NC, NMRR_NC, 0), /* 2 - ATTR_DEVICE */
+ TEX(PRRR_SO, NMRR_NC, NMRR_NC, 0), /* 3 - ATTR_SO */
+ TEX(PRRR_MEM, NMRR_WT, NMRR_WT, 0), /* 4 - ATTR_WT */
+ TEX(PRRR_MEM, NMRR_NC, NMRR_NC, 0), /* 5 - NOT USED YET */
+ TEX(PRRR_MEM, NMRR_NC, NMRR_NC, 0), /* 6 - NOT USED YET */
+ TEX(PRRR_MEM, NMRR_NC, NMRR_NC, 0), /* 7 - NOT USED YET */
+};
+#undef TEX
+
+static uint32_t pte2_attr_tab[8] = {
+ PTE2_ATTR_WB_WA, /* 0 - VM_MEMATTR_WB_WA */
+ PTE2_ATTR_NOCACHE, /* 1 - VM_MEMATTR_NOCACHE */
+ PTE2_ATTR_DEVICE, /* 2 - VM_MEMATTR_DEVICE */
+ PTE2_ATTR_SO, /* 3 - VM_MEMATTR_SO */
+ PTE2_ATTR_WT, /* 4 - VM_MEMATTR_WRITE_THROUGH */
+ 0, /* 5 - NOT USED YET */
+ 0, /* 6 - NOT USED YET */
+ 0 /* 7 - NOT USED YET */
+};
+CTASSERT(VM_MEMATTR_WB_WA == 0);
+CTASSERT(VM_MEMATTR_NOCACHE == 1);
+CTASSERT(VM_MEMATTR_DEVICE == 2);
+CTASSERT(VM_MEMATTR_SO == 3);
+CTASSERT(VM_MEMATTR_WRITE_THROUGH == 4);
+#define VM_MEMATTR_END (VM_MEMATTR_WRITE_THROUGH + 1)
+
+boolean_t
+pmap_is_valid_memattr(pmap_t pmap __unused, vm_memattr_t mode)
+{
+
+ return (mode >= 0 && mode < VM_MEMATTR_END);
+}
+
+static inline uint32_t
+vm_memattr_to_pte2(vm_memattr_t ma)
+{
+
+ KASSERT((u_int)ma < VM_MEMATTR_END,
+ ("%s: bad vm_memattr_t %d", __func__, ma));
+ return (pte2_attr_tab[(u_int)ma]);
+}
+
+static inline uint32_t
+vm_page_pte2_attr(vm_page_t m)
+{
+
+ return (vm_memattr_to_pte2(m->md.pat_mode));
+}
+
+/*
+ * Convert TEX definition entry to TTB flags.
+ */
+static uint32_t
+encode_ttb_flags(int idx)
+{
+ uint32_t inner, outer, nos, reg;
+
+ inner = (tex_class[idx] >> TEXDEF_INNER_SHIFT) &
+ TEXDEF_INNER_MASK;
+ outer = (tex_class[idx] >> TEXDEF_OUTER_SHIFT) &
+ TEXDEF_OUTER_MASK;
+ nos = (tex_class[idx] >> TEXDEF_NOS_SHIFT) &
+ TEXDEF_NOS_MASK;
+
+ reg = nos << 5;
+ reg |= outer << 3;
+ if (cpuinfo.coherent_walk)
+ reg |= (inner & 0x1) << 6;
+ reg |= (inner & 0x2) >> 1;
+#ifdef SMP
+ ARM_SMP_UP(
+ reg |= 1 << 1,
+ );
+#endif
+ return reg;
+}
+
+/*
+ * Set TEX remapping registers in current CPU.
+ */
+void
+pmap_set_tex(void)
+{
+ uint32_t prrr, nmrr;
+ uint32_t type, inner, outer, nos;
+ int i;
+
+#ifdef PMAP_PTE_NOCACHE
+ /* XXX fixme */
+ if (cpuinfo.coherent_walk) {
+ pt_memattr = VM_MEMATTR_WB_WA;
+ ttb_flags = encode_ttb_flags(0);
+ }
+ else {
+ pt_memattr = VM_MEMATTR_NOCACHE;
+ ttb_flags = encode_ttb_flags(1);
+ }
+#else
+ pt_memattr = VM_MEMATTR_WB_WA;
+ ttb_flags = encode_ttb_flags(0);
+#endif
+
+ prrr = 0;
+ nmrr = 0;
+
+ /* Build remapping register from TEX classes. */
+ for (i = 0; i < 8; i++) {
+ type = (tex_class[i] >> TEXDEF_TYPE_SHIFT) &
+ TEXDEF_TYPE_MASK;
+ inner = (tex_class[i] >> TEXDEF_INNER_SHIFT) &
+ TEXDEF_INNER_MASK;
+ outer = (tex_class[i] >> TEXDEF_OUTER_SHIFT) &
+ TEXDEF_OUTER_MASK;
+ nos = (tex_class[i] >> TEXDEF_NOS_SHIFT) &
+ TEXDEF_NOS_MASK;
+
+ prrr |= type << (i * 2);
+ prrr |= nos << (i + 24);
+ nmrr |= inner << (i * 2);
+ nmrr |= outer << (i * 2 + 16);
+ }
+ /* Add shareable bits for device memory. */
+ prrr |= PRRR_DS0 | PRRR_DS1;
+
+ /* Add shareable bits for normal memory in SMP case. */
+#ifdef SMP
+ ARM_SMP_UP(
+ prrr |= PRRR_NS1,
+ );
+#endif
+ cp15_prrr_set(prrr);
+ cp15_nmrr_set(nmrr);
+
+ /* Caches are disabled, so full TLB flush should be enough. */
+ tlb_flush_all_local();
+}
+
+/*
+ * Remap one vm_meattr class to another one. This can be useful as
+ * workaround for SOC errata, e.g. if devices must be accessed using
+ * SO memory class.
+ *
+ * !!! Please note that this function is absolutely last resort thing.
+ * It should not be used under normal circumstances. !!!
+ *
+ * Usage rules:
+ * - it shall be called after pmap_bootstrap_prepare() and before
+ * cpu_mp_start() (thus only on boot CPU). In practice, it's expected
+ * to be called from platform_attach() or platform_late_init().
+ *
+ * - if remapping doesn't change caching mode, or until uncached class
+ * is remapped to any kind of cached one, then no other restriction exists.
+ *
+ * - if pmap_remap_vm_attr() changes caching mode, but both (original and
+ * remapped) remain cached, then caller is resposible for calling
+ * of dcache_wbinv_poc_all().
+ *
+ * - remapping of any kind of cached class to uncached is not permitted.
+ */
+void
+pmap_remap_vm_attr(vm_memattr_t old_attr, vm_memattr_t new_attr)
+{
+ int old_idx, new_idx;
+
+ /* Map VM memattrs to indexes to tex_class table. */
+ old_idx = PTE2_ATTR2IDX(pte2_attr_tab[(int)old_attr]);
+ new_idx = PTE2_ATTR2IDX(pte2_attr_tab[(int)new_attr]);
+
+ /* Replace TEX attribute and apply it. */
+ tex_class[old_idx] = tex_class[new_idx];
+ pmap_set_tex();
+}
+
+/*
+ * KERNBASE must be multiple of NPT2_IN_PG * PTE1_SIZE. In other words,
+ * KERNBASE is mapped by first L2 page table in L2 page table page. It
+ * meets same constrain due to PT2MAP being placed just under KERNBASE.
+ */
+CTASSERT((KERNBASE & (NPT2_IN_PG * PTE1_SIZE - 1)) == 0);
+CTASSERT((KERNBASE - VM_MAXUSER_ADDRESS) >= PT2MAP_SIZE);
+
+/*
+ * In crazy dreams, PAGE_SIZE could be a multiple of PTE2_SIZE in general.
+ * For now, anyhow, the following check must be fulfilled.
+ */
+CTASSERT(PAGE_SIZE == PTE2_SIZE);
+/*
+ * We don't want to mess up MI code with all MMU and PMAP definitions,
+ * so some things, which depend on other ones, are defined independently.
+ * Now, it is time to check that we don't screw up something.
+ */
+CTASSERT(PDRSHIFT == PTE1_SHIFT);
+/*
+ * Check L1 and L2 page table entries definitions consistency.
+ */
+CTASSERT(NB_IN_PT1 == (sizeof(pt1_entry_t) * NPTE1_IN_PT1));
+CTASSERT(NB_IN_PT2 == (sizeof(pt2_entry_t) * NPTE2_IN_PT2));
+/*
+ * Check L2 page tables page consistency.
+ */
+CTASSERT(PAGE_SIZE == (NPT2_IN_PG * NB_IN_PT2));
+CTASSERT((1 << PT2PG_SHIFT) == NPT2_IN_PG);
+/*
+ * Check PT2TAB consistency.
+ * PT2TAB_ENTRIES is defined as a division of NPTE1_IN_PT1 by NPT2_IN_PG.
+ * This should be done without remainder.
+ */
+CTASSERT(NPTE1_IN_PT1 == (PT2TAB_ENTRIES * NPT2_IN_PG));
+
+/*
+ * A PT2MAP magic.
+ *
+ * All level 2 page tables (PT2s) are mapped continuously and accordingly
+ * into PT2MAP address space. As PT2 size is less than PAGE_SIZE, this can
+ * be done only if PAGE_SIZE is a multiple of PT2 size. All PT2s in one page
+ * must be used together, but not necessary at once. The first PT2 in a page
+ * must map things on correctly aligned address and the others must follow
+ * in right order.
+ */
+#define NB_IN_PT2TAB (PT2TAB_ENTRIES * sizeof(pt2_entry_t))
+#define NPT2_IN_PT2TAB (NB_IN_PT2TAB / NB_IN_PT2)
+#define NPG_IN_PT2TAB (NB_IN_PT2TAB / PAGE_SIZE)
+
+/*
+ * Check PT2TAB consistency.
+ * NPT2_IN_PT2TAB is defined as a division of NB_IN_PT2TAB by NB_IN_PT2.
+ * NPG_IN_PT2TAB is defined as a division of NB_IN_PT2TAB by PAGE_SIZE.
+ * The both should be done without remainder.
+ */
+CTASSERT(NB_IN_PT2TAB == (NPT2_IN_PT2TAB * NB_IN_PT2));
+CTASSERT(NB_IN_PT2TAB == (NPG_IN_PT2TAB * PAGE_SIZE));
+/*
+ * The implementation was made general, however, with the assumption
+ * bellow in mind. In case of another value of NPG_IN_PT2TAB,
+ * the code should be once more rechecked.
+ */
+CTASSERT(NPG_IN_PT2TAB == 1);
+
+/*
+ * Get offset of PT2 in a page
+ * associated with given PT1 index.
+ */
+static __inline u_int
+page_pt2off(u_int pt1_idx)
+{
+
+ return ((pt1_idx & PT2PG_MASK) * NB_IN_PT2);
+}
+
+/*
+ * Get physical address of PT2
+ * associated with given PT2s page and PT1 index.
+ */
+static __inline vm_paddr_t
+page_pt2pa(vm_paddr_t pgpa, u_int pt1_idx)
+{
+
+ return (pgpa + page_pt2off(pt1_idx));
+}
+
+/*
+ * Get first entry of PT2
+ * associated with given PT2s page and PT1 index.
+ */
+static __inline pt2_entry_t *
+page_pt2(vm_offset_t pgva, u_int pt1_idx)
+{
+
+ return ((pt2_entry_t *)(pgva + page_pt2off(pt1_idx)));
+}
+
+/*
+ * Get virtual address of PT2s page (mapped in PT2MAP)
+ * which holds PT2 which holds entry which maps given virtual address.
+ */
+static __inline vm_offset_t
+pt2map_pt2pg(vm_offset_t va)
+{
+
+ va &= ~(NPT2_IN_PG * PTE1_SIZE - 1);
+ return ((vm_offset_t)pt2map_entry(va));
+}
+
+/*****************************************************************************
+ *
+ * THREE pmap initialization milestones exist:
+ *
+ * locore.S
+ * -> fundamental init (including MMU) in ASM
+ *
+ * initarm()
+ * -> fundamental init continues in C
+ * -> first available physical address is known
+ *
+ * pmap_bootstrap_prepare() -> FIRST PMAP MILESTONE (first epoch begins)
+ * -> basic (safe) interface for physical address allocation is made
+ * -> basic (safe) interface for virtual mapping is made
+ * -> limited not SMP coherent work is possible
+ *
+ * -> more fundamental init continues in C
+ * -> locks and some more things are available
+ * -> all fundamental allocations and mappings are done
+ *
+ * pmap_bootstrap() -> SECOND PMAP MILESTONE (second epoch begins)
+ * -> phys_avail[] and virtual_avail is set
+ * -> control is passed to vm subsystem
+ * -> physical and virtual address allocation are off limit
+ * -> low level mapping functions, some SMP coherent,
+ * are available, which cannot be used before vm subsystem
+ * is being inited
+ *
+ * mi_startup()
+ * -> vm subsystem is being inited
+ *
+ * pmap_init() -> THIRD PMAP MILESTONE (third epoch begins)
+ * -> pmap is fully inited
+ *
+ *****************************************************************************/
+
+/*****************************************************************************
+ *
+ * PMAP first stage initialization and utility functions
+ * for pre-bootstrap epoch.
+ *
+ * After pmap_bootstrap_prepare() is called, the following functions
+ * can be used:
+ *
+ * (1) strictly only for this stage functions for physical page allocations,
+ * virtual space allocations, and mappings:
+ *
+ * vm_paddr_t pmap_preboot_get_pages(u_int num);
+ * void pmap_preboot_map_pages(vm_paddr_t pa, vm_offset_t va, u_int num);
+ * vm_offset_t pmap_preboot_reserve_pages(u_int num);
+ * vm_offset_t pmap_preboot_get_vpages(u_int num);
+ * void pmap_preboot_map_attr(vm_paddr_t pa, vm_offset_t va, vm_size_t size,
+ * vm_prot_t prot, vm_memattr_t attr);
+ *
+ * (2) for all stages:
+ *
+ * vm_paddr_t pmap_kextract(vm_offset_t va);
+ *
+ * NOTE: This is not SMP coherent stage.
+ *
+ *****************************************************************************/
+
+#define KERNEL_P2V(pa) \
+ ((vm_offset_t)((pa) - arm_physmem_kernaddr + KERNVIRTADDR))
+#define KERNEL_V2P(va) \
+ ((vm_paddr_t)((va) - KERNVIRTADDR + arm_physmem_kernaddr))
+
+static vm_paddr_t last_paddr;
+
+/*
+ * Pre-bootstrap epoch page allocator.
+ */
+vm_paddr_t
+pmap_preboot_get_pages(u_int num)
+{
+ vm_paddr_t ret;
+
+ ret = last_paddr;
+ last_paddr += num * PAGE_SIZE;
+
+ return (ret);
+}
+
+/*
+ * The fundamental initialization of PMAP stuff.
+ *
+ * Some things already happened in locore.S and some things could happen
+ * before pmap_bootstrap_prepare() is called, so let's recall what is done:
+ * 1. Caches are disabled.
+ * 2. We are running on virtual addresses already with 'boot_pt1'
+ * as L1 page table.
+ * 3. So far, all virtual addresses can be converted to physical ones and
+ * vice versa by the following macros:
+ * KERNEL_P2V(pa) .... physical to virtual ones,
+ * KERNEL_V2P(va) .... virtual to physical ones.
+ *
+ * What is done herein:
+ * 1. The 'boot_pt1' is replaced by real kernel L1 page table 'kern_pt1'.
+ * 2. PT2MAP magic is brought to live.
+ * 3. Basic preboot functions for page allocations and mappings can be used.
+ * 4. Everything is prepared for L1 cache enabling.
+ *
+ * Variations:
+ * 1. To use second TTB register, so kernel and users page tables will be
+ * separated. This way process forking - pmap_pinit() - could be faster,
+ * it saves physical pages and KVA per a process, and it's simple change.
+ * However, it will lead, due to hardware matter, to the following:
+ * (a) 2G space for kernel and 2G space for users.
+ * (b) 1G space for kernel in low addresses and 3G for users above it.
+ * A question is: Is the case (b) really an option? Note that case (b)
+ * does save neither physical memory and KVA.
+ */
+void
+pmap_bootstrap_prepare(vm_paddr_t last)
+{
+ vm_paddr_t pt2pg_pa, pt2tab_pa, pa, size;
+ vm_offset_t pt2pg_va;
+ pt1_entry_t *pte1p;
+ pt2_entry_t *pte2p;
+ u_int i;
+ uint32_t l1_attr;
+
+ /*
+ * Now, we are going to make real kernel mapping. Note that we are
+ * already running on some mapping made in locore.S and we expect
+ * that it's large enough to ensure nofault access to physical memory
+ * allocated herein before switch.
+ *
+ * As kernel image and everything needed before are and will be mapped
+ * by section mappings, we align last physical address to PTE1_SIZE.
+ */
+ last_paddr = pte1_roundup(last);
+
+ /*
+ * Allocate and zero page(s) for kernel L1 page table.
+ *
+ * Note that it's first allocation on space which was PTE1_SIZE
+ * aligned and as such base_pt1 is aligned to NB_IN_PT1 too.
+ */
+ base_pt1 = pmap_preboot_get_pages(NPG_IN_PT1);
+ kern_pt1 = (pt1_entry_t *)KERNEL_P2V(base_pt1);
+ bzero((void*)kern_pt1, NB_IN_PT1);
+ pte1_sync_range(kern_pt1, NB_IN_PT1);
+
+ /* Allocate and zero page(s) for kernel PT2TAB. */
+ pt2tab_pa = pmap_preboot_get_pages(NPG_IN_PT2TAB);
+ kern_pt2tab = (pt2_entry_t *)KERNEL_P2V(pt2tab_pa);
+ bzero(kern_pt2tab, NB_IN_PT2TAB);
+ pte2_sync_range(kern_pt2tab, NB_IN_PT2TAB);
+
+ /* Allocate and zero page(s) for kernel L2 page tables. */
+ pt2pg_pa = pmap_preboot_get_pages(NKPT2PG);
+ pt2pg_va = KERNEL_P2V(pt2pg_pa);
+ size = NKPT2PG * PAGE_SIZE;
+ bzero((void*)pt2pg_va, size);
+ pte2_sync_range((pt2_entry_t *)pt2pg_va, size);
+
+ /*
+ * Add a physical memory segment (vm_phys_seg) corresponding to the
+ * preallocated pages for kernel L2 page tables so that vm_page
+ * structures representing these pages will be created. The vm_page
+ * structures are required for promotion of the corresponding kernel
+ * virtual addresses to section mappings.
+ */
+ vm_phys_add_seg(pt2tab_pa, pmap_preboot_get_pages(0));
+
+ /*
+ * Insert allocated L2 page table pages to PT2TAB and make
+ * link to all PT2s in L1 page table. See how kernel_vm_end
+ * is initialized.
+ *
+ * We play simple and safe. So every KVA will have underlaying
+ * L2 page table, even kernel image mapped by sections.
+ */
+ pte2p = kern_pt2tab_entry(KERNBASE);
+ for (pa = pt2pg_pa; pa < pt2pg_pa + size; pa += PTE2_SIZE)
+ pt2tab_store(pte2p++, PTE2_KPT(pa));
+
+ pte1p = kern_pte1(KERNBASE);
+ for (pa = pt2pg_pa; pa < pt2pg_pa + size; pa += NB_IN_PT2)
+ pte1_store(pte1p++, PTE1_LINK(pa));
+
+ /* Make section mappings for kernel. */
+ l1_attr = ATTR_TO_L1(PTE2_ATTR_DEFAULT);
+ pte1p = kern_pte1(KERNBASE);
+ for (pa = KERNEL_V2P(KERNBASE); pa < last; pa += PTE1_SIZE)
+ pte1_store(pte1p++, PTE1_KERN(pa, PTE1_AP_KRW, l1_attr));
+
+ /*
+ * Get free and aligned space for PT2MAP and make L1 page table links
+ * to L2 page tables held in PT2TAB.
+ *
+ * Note that pages holding PT2s are stored in PT2TAB as pt2_entry_t
+ * descriptors and PT2TAB page(s) itself is(are) used as PT2s. Thus
+ * each entry in PT2TAB maps all PT2s in a page. This implies that
+ * virtual address of PT2MAP must be aligned to NPT2_IN_PG * PTE1_SIZE.
+ */
+ PT2MAP = (pt2_entry_t *)(KERNBASE - PT2MAP_SIZE);
+ pte1p = kern_pte1((vm_offset_t)PT2MAP);
+ for (pa = pt2tab_pa, i = 0; i < NPT2_IN_PT2TAB; i++, pa += NB_IN_PT2) {
+ pte1_store(pte1p++, PTE1_LINK(pa));
+ }
+
+ /*
+ * Store PT2TAB in PT2TAB itself, i.e. self reference mapping.
+ * Each pmap will hold own PT2TAB, so the mapping should be not global.
+ */
+ pte2p = kern_pt2tab_entry((vm_offset_t)PT2MAP);
+ for (pa = pt2tab_pa, i = 0; i < NPG_IN_PT2TAB; i++, pa += PTE2_SIZE) {
+ pt2tab_store(pte2p++, PTE2_KPT_NG(pa));
+ }
+
+ /*
+ * Choose correct L2 page table and make mappings for allocations
+ * made herein which replaces temporary locore.S mappings after a while.
+ * Note that PT2MAP cannot be used until we switch to kern_pt1.
+ *
+ * Note, that these allocations started aligned on 1M section and
+ * kernel PT1 was allocated first. Making of mappings must follow
+ * order of physical allocations as we've used KERNEL_P2V() macro
+ * for virtual addresses resolution.
+ */
+ pte2p = kern_pt2tab_entry((vm_offset_t)kern_pt1);
+ pt2pg_va = KERNEL_P2V(pte2_pa(pte2_load(pte2p)));
+
+ pte2p = page_pt2(pt2pg_va, pte1_index((vm_offset_t)kern_pt1));
+
+ /* Make mapping for kernel L1 page table. */
+ for (pa = base_pt1, i = 0; i < NPG_IN_PT1; i++, pa += PTE2_SIZE)
+ pte2_store(pte2p++, PTE2_KPT(pa));
+
+ /* Make mapping for kernel PT2TAB. */
+ for (pa = pt2tab_pa, i = 0; i < NPG_IN_PT2TAB; i++, pa += PTE2_SIZE)
+ pte2_store(pte2p++, PTE2_KPT(pa));
+
+ /* Finally, switch from 'boot_pt1' to 'kern_pt1'. */
+ pmap_kern_ttb = base_pt1 | ttb_flags;
+ cpuinfo_reinit_mmu(pmap_kern_ttb);
+ /*
+ * Initialize the first available KVA. As kernel image is mapped by
+ * sections, we are leaving some gap behind.
+ */
+ virtual_avail = (vm_offset_t)kern_pt2tab + NPG_IN_PT2TAB * PAGE_SIZE;
+}
+
+/*
+ * Setup L2 page table page for given KVA.
+ * Used in pre-bootstrap epoch.
+ *
+ * Note that we have allocated NKPT2PG pages for L2 page tables in advance
+ * and used them for mapping KVA starting from KERNBASE. However, this is not
+ * enough. Vectors and devices need L2 page tables too. Note that they are
+ * even above VM_MAX_KERNEL_ADDRESS.
+ */
+static __inline vm_paddr_t
+pmap_preboot_pt2pg_setup(vm_offset_t va)
+{
+ pt2_entry_t *pte2p, pte2;
+ vm_paddr_t pt2pg_pa;
+
+ /* Get associated entry in PT2TAB. */
+ pte2p = kern_pt2tab_entry(va);
+
+ /* Just return, if PT2s page exists already. */
+ pte2 = pt2tab_load(pte2p);
+ if (pte2_is_valid(pte2))
+ return (pte2_pa(pte2));
+
+ KASSERT(va >= VM_MAX_KERNEL_ADDRESS,
+ ("%s: NKPT2PG too small", __func__));
+
+ /*
+ * Allocate page for PT2s and insert it to PT2TAB.
+ * In other words, map it into PT2MAP space.
+ */
+ pt2pg_pa = pmap_preboot_get_pages(1);
+ pt2tab_store(pte2p, PTE2_KPT(pt2pg_pa));
+
+ /* Zero all PT2s in allocated page. */
+ bzero((void*)pt2map_pt2pg(va), PAGE_SIZE);
+ pte2_sync_range((pt2_entry_t *)pt2map_pt2pg(va), PAGE_SIZE);
+
+ return (pt2pg_pa);
+}
+
+/*
+ * Setup L2 page table for given KVA.
+ * Used in pre-bootstrap epoch.
+ */
+static void
+pmap_preboot_pt2_setup(vm_offset_t va)
+{
+ pt1_entry_t *pte1p;
+ vm_paddr_t pt2pg_pa, pt2_pa;
+
+ /* Setup PT2's page. */
+ pt2pg_pa = pmap_preboot_pt2pg_setup(va);
+ pt2_pa = page_pt2pa(pt2pg_pa, pte1_index(va));
+
+ /* Insert PT2 to PT1. */
+ pte1p = kern_pte1(va);
+ pte1_store(pte1p, PTE1_LINK(pt2_pa));
+}
+
+/*
+ * Get L2 page entry associated with given KVA.
+ * Used in pre-bootstrap epoch.
+ */
+static __inline pt2_entry_t*
+pmap_preboot_vtopte2(vm_offset_t va)
+{
+ pt1_entry_t *pte1p;
+
+ /* Setup PT2 if needed. */
+ pte1p = kern_pte1(va);
+ if (!pte1_is_valid(pte1_load(pte1p))) /* XXX - sections ?! */
+ pmap_preboot_pt2_setup(va);
+
+ return (pt2map_entry(va));
+}
+
+/*
+ * Pre-bootstrap epoch page(s) mapping(s).
+ */
+void
+pmap_preboot_map_pages(vm_paddr_t pa, vm_offset_t va, u_int num)
+{
+ u_int i;
+ pt2_entry_t *pte2p;
+
+ /* Map all the pages. */
+ for (i = 0; i < num; i++) {
+ pte2p = pmap_preboot_vtopte2(va);
+ pte2_store(pte2p, PTE2_KRW(pa));
+ va += PAGE_SIZE;
+ pa += PAGE_SIZE;
+ }
+}
+
+/*
+ * Pre-bootstrap epoch virtual space alocator.
+ */
+vm_offset_t
+pmap_preboot_reserve_pages(u_int num)
+{
+ u_int i;
+ vm_offset_t start, va;
+ pt2_entry_t *pte2p;
+
+ /* Allocate virtual space. */
+ start = va = virtual_avail;
+ virtual_avail += num * PAGE_SIZE;
+
+ /* Zero the mapping. */
+ for (i = 0; i < num; i++) {
+ pte2p = pmap_preboot_vtopte2(va);
+ pte2_store(pte2p, 0);
+ va += PAGE_SIZE;
+ }
+
+ return (start);
+}
+
+/*
+ * Pre-bootstrap epoch page(s) allocation and mapping(s).
+ */
+vm_offset_t
+pmap_preboot_get_vpages(u_int num)
+{
+ vm_paddr_t pa;
+ vm_offset_t va;
+
+ /* Allocate physical page(s). */
+ pa = pmap_preboot_get_pages(num);
+
+ /* Allocate virtual space. */
+ va = virtual_avail;
+ virtual_avail += num * PAGE_SIZE;
+
+ /* Map and zero all. */
+ pmap_preboot_map_pages(pa, va, num);
+ bzero((void *)va, num * PAGE_SIZE);
+
+ return (va);
+}
+
+/*
+ * Pre-bootstrap epoch page mapping(s) with attributes.
+ */
+void
+pmap_preboot_map_attr(vm_paddr_t pa, vm_offset_t va, vm_size_t size,
+ vm_prot_t prot, vm_memattr_t attr)
+{
+ u_int num;
+ u_int l1_attr, l1_prot, l2_prot, l2_attr;
+ pt1_entry_t *pte1p;
+ pt2_entry_t *pte2p;
+
+ l2_prot = prot & VM_PROT_WRITE ? PTE2_AP_KRW : PTE2_AP_KR;
+ l2_prot |= (prot & VM_PROT_EXECUTE) ? PTE2_X : PTE2_NX;
+ l2_attr = vm_memattr_to_pte2(attr);
+ l1_prot = ATTR_TO_L1(l2_prot);
+ l1_attr = ATTR_TO_L1(l2_attr);
+
+ /* Map all the pages. */
+ num = round_page(size);
+ while (num > 0) {
+ if ((((va | pa) & PTE1_OFFSET) == 0) && (num >= PTE1_SIZE)) {
+ pte1p = kern_pte1(va);
+ pte1_store(pte1p, PTE1_KERN(pa, l1_prot, l1_attr));
+ va += PTE1_SIZE;
+ pa += PTE1_SIZE;
+ num -= PTE1_SIZE;
+ } else {
+ pte2p = pmap_preboot_vtopte2(va);
+ pte2_store(pte2p, PTE2_KERN(pa, l2_prot, l2_attr));
+ va += PAGE_SIZE;
+ pa += PAGE_SIZE;
+ num -= PAGE_SIZE;
+ }
+ }
+}
+
+/*
+ * Extract from the kernel page table the physical address
+ * that is mapped by the given virtual address "va".
+ */
+vm_paddr_t
+pmap_kextract(vm_offset_t va)
+{
+ vm_paddr_t pa;
+ pt1_entry_t pte1;
+ pt2_entry_t pte2;
+
+ pte1 = pte1_load(kern_pte1(va));
+ if (pte1_is_section(pte1)) {
+ pa = pte1_pa(pte1) | (va & PTE1_OFFSET);
+ } else if (pte1_is_link(pte1)) {
+ /*
+ * We should beware of concurrent promotion that changes
+ * pte1 at this point. However, it's not a problem as PT2
+ * page is preserved by promotion in PT2TAB. So even if
+ * it happens, using of PT2MAP is still safe.
+ *
+ * QQQ: However, concurrent removing is a problem which
+ * ends in abort on PT2MAP space. Locking must be used
+ * to deal with this.
+ */
+ pte2 = pte2_load(pt2map_entry(va));
+ pa = pte2_pa(pte2) | (va & PTE2_OFFSET);
+ }
+ else {
+ panic("%s: va %#x pte1 %#x", __func__, va, pte1);
+ }
+ return (pa);
+}
+
+/*
+ * Extract from the kernel page table the physical address
+ * that is mapped by the given virtual address "va". Also
+ * return L2 page table entry which maps the address.
+ *
+ * This is only intended to be used for panic dumps.
+ */
+vm_paddr_t
+pmap_dump_kextract(vm_offset_t va, pt2_entry_t *pte2p)
+{
+ vm_paddr_t pa;
+ pt1_entry_t pte1;
+ pt2_entry_t pte2;
+
+ pte1 = pte1_load(kern_pte1(va));
+ if (pte1_is_section(pte1)) {
+ pa = pte1_pa(pte1) | (va & PTE1_OFFSET);
+ pte2 = pa | ATTR_TO_L2(pte1) | PTE2_V;
+ } else if (pte1_is_link(pte1)) {
+ pte2 = pte2_load(pt2map_entry(va));
+ pa = pte2_pa(pte2);
+ } else {
+ pte2 = 0;
+ pa = 0;
+ }
+ if (pte2p != NULL)
+ *pte2p = pte2;
+ return (pa);
+}
+
+/*****************************************************************************
+ *
+ * PMAP second stage initialization and utility functions
+ * for bootstrap epoch.
+ *
+ * After pmap_bootstrap() is called, the following functions for
+ * mappings can be used:
+ *
+ * void pmap_kenter(vm_offset_t va, vm_paddr_t pa);
+ * void pmap_kremove(vm_offset_t va);
+ * vm_offset_t pmap_map(vm_offset_t *virt, vm_paddr_t start, vm_paddr_t end,
+ * int prot);
+ *
+ * NOTE: This is not SMP coherent stage. And physical page allocation is not
+ * allowed during this stage.
+ *
+ *****************************************************************************/
+
+/*
+ * Initialize kernel PMAP locks and lists, kernel_pmap itself, and
+ * reserve various virtual spaces for temporary mappings.
+ */
+void
+pmap_bootstrap(vm_offset_t firstaddr)
+{
+ pt2_entry_t *unused __unused;
+ struct pcpu *pc;
+
+ /*
+ * Initialize the kernel pmap (which is statically allocated).
+ */
+ PMAP_LOCK_INIT(kernel_pmap);
+ kernel_l1pa = (vm_paddr_t)kern_pt1; /* for libkvm */
+ kernel_pmap->pm_pt1 = kern_pt1;
+ kernel_pmap->pm_pt2tab = kern_pt2tab;
+ CPU_FILL(&kernel_pmap->pm_active); /* don't allow deactivation */
+ TAILQ_INIT(&kernel_pmap->pm_pvchunk);
+
+ /*
+ * Initialize the global pv list lock.
+ */
+ rw_init(&pvh_global_lock, "pmap pv global");
+
+ LIST_INIT(&allpmaps);
+
+ /*
+ * Request a spin mutex so that changes to allpmaps cannot be
+ * preempted by smp_rendezvous_cpus().
+ */
+ mtx_init(&allpmaps_lock, "allpmaps", NULL, MTX_SPIN);
+ mtx_lock_spin(&allpmaps_lock);
+ LIST_INSERT_HEAD(&allpmaps, kernel_pmap, pm_list);
+ mtx_unlock_spin(&allpmaps_lock);
+
+ /*
+ * Reserve some special page table entries/VA space for temporary
+ * mapping of pages.
+ */
+#define SYSMAP(c, p, v, n) do { \
+ v = (c)pmap_preboot_reserve_pages(n); \
+ p = pt2map_entry((vm_offset_t)v); \
+ } while (0)
+
+ /*
+ * Local CMAP1/CMAP2 are used for zeroing and copying pages.
+ * Local CMAP2 is also used for data cache cleaning.
+ */
+ pc = get_pcpu();
+ mtx_init(&pc->pc_cmap_lock, "SYSMAPS", NULL, MTX_DEF);
+ SYSMAP(caddr_t, pc->pc_cmap1_pte2p, pc->pc_cmap1_addr, 1);
+ SYSMAP(caddr_t, pc->pc_cmap2_pte2p, pc->pc_cmap2_addr, 1);
+ SYSMAP(vm_offset_t, pc->pc_qmap_pte2p, pc->pc_qmap_addr, 1);
+
+ /*
+ * Crashdump maps.
+ */
+ SYSMAP(caddr_t, unused, crashdumpmap, MAXDUMPPGS);
+
+ /*
+ * _tmppt is used for reading arbitrary physical pages via /dev/mem.
+ */
+ SYSMAP(caddr_t, unused, _tmppt, 1);
+
+ /*
+ * PADDR1 and PADDR2 are used by pmap_pte2_quick() and pmap_pte2(),
+ * respectively. PADDR3 is used by pmap_pte2_ddb().
+ */
+ SYSMAP(pt2_entry_t *, PMAP1, PADDR1, 1);
+ SYSMAP(pt2_entry_t *, PMAP2, PADDR2, 1);
+#ifdef DDB
+ SYSMAP(pt2_entry_t *, PMAP3, PADDR3, 1);
+#endif
+ mtx_init(&PMAP2mutex, "PMAP2", NULL, MTX_DEF);
+
+ /*
+ * Note that in very short time in initarm(), we are going to
+ * initialize phys_avail[] array and no further page allocation
+ * can happen after that until vm subsystem will be initialized.
+ */
+ kernel_vm_end_new = kernel_vm_end;
+ virtual_end = vm_max_kernel_address;
+}
+
+static void
+pmap_init_reserved_pages(void)
+{
+ struct pcpu *pc;
+ vm_offset_t pages;
+ int i;
+
+ CPU_FOREACH(i) {
+ pc = pcpu_find(i);
+ /*
+ * Skip if the mapping has already been initialized,
+ * i.e. this is the BSP.
+ */
+ if (pc->pc_cmap1_addr != 0)
+ continue;
+ mtx_init(&pc->pc_cmap_lock, "SYSMAPS", NULL, MTX_DEF);
+ pages = kva_alloc(PAGE_SIZE * 3);
+ if (pages == 0)
+ panic("%s: unable to allocate KVA", __func__);
+ pc->pc_cmap1_pte2p = pt2map_entry(pages);
+ pc->pc_cmap2_pte2p = pt2map_entry(pages + PAGE_SIZE);
+ pc->pc_qmap_pte2p = pt2map_entry(pages + (PAGE_SIZE * 2));
+ pc->pc_cmap1_addr = (caddr_t)pages;
+ pc->pc_cmap2_addr = (caddr_t)(pages + PAGE_SIZE);
+ pc->pc_qmap_addr = pages + (PAGE_SIZE * 2);
+ }
+}
+SYSINIT(rpages_init, SI_SUB_CPU, SI_ORDER_ANY, pmap_init_reserved_pages, NULL);
+
+/*
+ * The function can already be use in second initialization stage.
+ * As such, the function DOES NOT call pmap_growkernel() where PT2
+ * allocation can happen. So if used, be sure that PT2 for given
+ * virtual address is allocated already!
+ *
+ * Add a wired page to the kva.
+ * Note: not SMP coherent.
+ */
+static __inline void
+pmap_kenter_prot_attr(vm_offset_t va, vm_paddr_t pa, uint32_t prot,
+ uint32_t attr)
+{
+ pt1_entry_t *pte1p;
+ pt2_entry_t *pte2p;
+
+ pte1p = kern_pte1(va);
+ if (!pte1_is_valid(pte1_load(pte1p))) { /* XXX - sections ?! */
+ /*
+ * This is a very low level function, so PT2 and particularly
+ * PT2PG associated with given virtual address must be already
+ * allocated. It's a pain mainly during pmap initialization
+ * stage. However, called after pmap initialization with
+ * virtual address not under kernel_vm_end will lead to
+ * the same misery.
+ */
+ if (!pte2_is_valid(pte2_load(kern_pt2tab_entry(va))))
+ panic("%s: kernel PT2 not allocated!", __func__);
+ }
+
+ pte2p = pt2map_entry(va);
+ pte2_store(pte2p, PTE2_KERN(pa, prot, attr));
+}
+
+PMAP_INLINE void
+pmap_kenter(vm_offset_t va, vm_paddr_t pa)
+{
+
+ pmap_kenter_prot_attr(va, pa, PTE2_AP_KRW, PTE2_ATTR_DEFAULT);
+}
+
+/*
+ * Remove a page from the kernel pagetables.
+ * Note: not SMP coherent.
+ */
+PMAP_INLINE void
+pmap_kremove(vm_offset_t va)
+{
+ pt1_entry_t *pte1p;
+ pt2_entry_t *pte2p;
+
+ pte1p = kern_pte1(va);
+ if (pte1_is_section(pte1_load(pte1p))) {
+ pte1_clear(pte1p);
+ } else {
+ pte2p = pt2map_entry(va);
+ pte2_clear(pte2p);
+ }
+}
+
+/*
+ * Share new kernel PT2PG with all pmaps.
+ * The caller is responsible for maintaining TLB consistency.
+ */
+static void
+pmap_kenter_pt2tab(vm_offset_t va, pt2_entry_t npte2)
+{
+ pmap_t pmap;
+ pt2_entry_t *pte2p;
+
+ mtx_lock_spin(&allpmaps_lock);
+ LIST_FOREACH(pmap, &allpmaps, pm_list) {
+ pte2p = pmap_pt2tab_entry(pmap, va);
+ pt2tab_store(pte2p, npte2);
+ }
+ mtx_unlock_spin(&allpmaps_lock);
+}
+
+/*
+ * Share new kernel PTE1 with all pmaps.
+ * The caller is responsible for maintaining TLB consistency.
+ */
+static void
+pmap_kenter_pte1(vm_offset_t va, pt1_entry_t npte1)
+{
+ pmap_t pmap;
+ pt1_entry_t *pte1p;
+
+ mtx_lock_spin(&allpmaps_lock);
+ LIST_FOREACH(pmap, &allpmaps, pm_list) {
+ pte1p = pmap_pte1(pmap, va);
+ pte1_store(pte1p, npte1);
+ }
+ mtx_unlock_spin(&allpmaps_lock);
+}
+
+/*
+ * Used to map a range of physical addresses into kernel
+ * virtual address space.
+ *
+ * The value passed in '*virt' is a suggested virtual address for
+ * the mapping. Architectures which can support a direct-mapped
+ * physical to virtual region can return the appropriate address
+ * within that region, leaving '*virt' unchanged. Other
+ * architectures should map the pages starting at '*virt' and
+ * update '*virt' with the first usable address after the mapped
+ * region.
+ *
+ * NOTE: Read the comments above pmap_kenter_prot_attr() as
+ * the function is used herein!
+ */
+vm_offset_t
+pmap_map(vm_offset_t *virt, vm_paddr_t start, vm_paddr_t end, int prot)
+{
+ vm_offset_t va, sva;
+ vm_paddr_t pte1_offset;
+ pt1_entry_t npte1;
+ uint32_t l1prot, l2prot;
+ uint32_t l1attr, l2attr;
+
+ PDEBUG(1, printf("%s: virt = %#x, start = %#x, end = %#x (size = %#x),"
+ " prot = %d\n", __func__, *virt, start, end, end - start, prot));
+
+ l2prot = (prot & VM_PROT_WRITE) ? PTE2_AP_KRW : PTE2_AP_KR;
+ l2prot |= (prot & VM_PROT_EXECUTE) ? PTE2_X : PTE2_NX;
+ l1prot = ATTR_TO_L1(l2prot);
+
+ l2attr = PTE2_ATTR_DEFAULT;
+ l1attr = ATTR_TO_L1(l2attr);
+
+ va = *virt;
+ /*
+ * Does the physical address range's size and alignment permit at
+ * least one section mapping to be created?
+ */
+ pte1_offset = start & PTE1_OFFSET;
+ if ((end - start) - ((PTE1_SIZE - pte1_offset) & PTE1_OFFSET) >=
+ PTE1_SIZE) {
+ /*
+ * Increase the starting virtual address so that its alignment
+ * does not preclude the use of section mappings.
+ */
+ if ((va & PTE1_OFFSET) < pte1_offset)
+ va = pte1_trunc(va) + pte1_offset;
+ else if ((va & PTE1_OFFSET) > pte1_offset)
+ va = pte1_roundup(va) + pte1_offset;
+ }
+ sva = va;
+ while (start < end) {
+ if ((start & PTE1_OFFSET) == 0 && end - start >= PTE1_SIZE) {
+ KASSERT((va & PTE1_OFFSET) == 0,
+ ("%s: misaligned va %#x", __func__, va));
+ npte1 = PTE1_KERN(start, l1prot, l1attr);
+ pmap_kenter_pte1(va, npte1);
+ va += PTE1_SIZE;
+ start += PTE1_SIZE;
+ } else {
+ pmap_kenter_prot_attr(va, start, l2prot, l2attr);
+ va += PAGE_SIZE;
+ start += PAGE_SIZE;
+ }
+ }
+ tlb_flush_range(sva, va - sva);
+ *virt = va;
+ return (sva);
+}
+
+/*
+ * Make a temporary mapping for a physical address.
+ * This is only intended to be used for panic dumps.
+ */
+void *
+pmap_kenter_temporary(vm_paddr_t pa, int i)
+{
+ vm_offset_t va;
+
+ /* QQQ: 'i' should be less or equal to MAXDUMPPGS. */
+
+ va = (vm_offset_t)crashdumpmap + (i * PAGE_SIZE);
+ pmap_kenter(va, pa);
+ tlb_flush_local(va);
+ return ((void *)crashdumpmap);
+}
+
+/*************************************
+ *
+ * TLB & cache maintenance routines.
+ *
+ *************************************/
+
+/*
+ * We inline these within pmap.c for speed.
+ */
+PMAP_INLINE void
+pmap_tlb_flush(pmap_t pmap, vm_offset_t va)
+{
+
+ if (pmap == kernel_pmap || !CPU_EMPTY(&pmap->pm_active))
+ tlb_flush(va);
+}
+
+PMAP_INLINE void
+pmap_tlb_flush_range(pmap_t pmap, vm_offset_t sva, vm_size_t size)
+{
+
+ if (pmap == kernel_pmap || !CPU_EMPTY(&pmap->pm_active))
+ tlb_flush_range(sva, size);
+}
+
+/*
+ * Abuse the pte2 nodes for unmapped kva to thread a kva freelist through.
+ * Requirements:
+ * - Must deal with pages in order to ensure that none of the PTE2_* bits
+ * are ever set, PTE2_V in particular.
+ * - Assumes we can write to pte2s without pte2_store() atomic ops.
+ * - Assumes nothing will ever test these addresses for 0 to indicate
+ * no mapping instead of correctly checking PTE2_V.
+ * - Assumes a vm_offset_t will fit in a pte2 (true for arm).
+ * Because PTE2_V is never set, there can be no mappings to invalidate.
+ */
+static vm_offset_t
+pmap_pte2list_alloc(vm_offset_t *head)
+{
+ pt2_entry_t *pte2p;
+ vm_offset_t va;
+
+ va = *head;
+ if (va == 0)
+ panic("pmap_ptelist_alloc: exhausted ptelist KVA");
+ pte2p = pt2map_entry(va);
+ *head = *pte2p;
+ if (*head & PTE2_V)
+ panic("%s: va with PTE2_V set!", __func__);
+ *pte2p = 0;
+ return (va);
+}
+
+static void
+pmap_pte2list_free(vm_offset_t *head, vm_offset_t va)
+{
+ pt2_entry_t *pte2p;
+
+ if (va & PTE2_V)
+ panic("%s: freeing va with PTE2_V set!", __func__);
+ pte2p = pt2map_entry(va);
+ *pte2p = *head; /* virtual! PTE2_V is 0 though */
+ *head = va;
+}
+
+static void
+pmap_pte2list_init(vm_offset_t *head, void *base, int npages)
+{
+ int i;
+ vm_offset_t va;
+
+ *head = 0;
+ for (i = npages - 1; i >= 0; i--) {
+ va = (vm_offset_t)base + i * PAGE_SIZE;
+ pmap_pte2list_free(head, va);
+ }
+}
+
+/*****************************************************************************
+ *
+ * PMAP third and final stage initialization.
+ *
+ * After pmap_init() is called, PMAP subsystem is fully initialized.
+ *
+ *****************************************************************************/
+
+SYSCTL_NODE(_vm, OID_AUTO, pmap, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
+ "VM/pmap parameters");
+
+SYSCTL_INT(_vm_pmap, OID_AUTO, pv_entry_max, CTLFLAG_RD, &pv_entry_max, 0,
+ "Max number of PV entries");
+SYSCTL_INT(_vm_pmap, OID_AUTO, shpgperproc, CTLFLAG_RD, &shpgperproc, 0,
+ "Page share factor per proc");
+
+static u_long nkpt2pg = NKPT2PG;
+SYSCTL_ULONG(_vm_pmap, OID_AUTO, nkpt2pg, CTLFLAG_RD,
+ &nkpt2pg, 0, "Pre-allocated pages for kernel PT2s");
+
+static int sp_enabled = 1;
+SYSCTL_INT(_vm_pmap, OID_AUTO, sp_enabled, CTLFLAG_RDTUN | CTLFLAG_NOFETCH,
+ &sp_enabled, 0, "Are large page mappings enabled?");
+
+bool
+pmap_ps_enabled(pmap_t pmap __unused)
+{
+
+ return (sp_enabled != 0);
+}
+
+static SYSCTL_NODE(_vm_pmap, OID_AUTO, pte1, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
+ "1MB page mapping counters");
+
+static u_long pmap_pte1_demotions;
+SYSCTL_ULONG(_vm_pmap_pte1, OID_AUTO, demotions, CTLFLAG_RD,
+ &pmap_pte1_demotions, 0, "1MB page demotions");
+
+static u_long pmap_pte1_mappings;
+SYSCTL_ULONG(_vm_pmap_pte1, OID_AUTO, mappings, CTLFLAG_RD,
+ &pmap_pte1_mappings, 0, "1MB page mappings");
+
+static u_long pmap_pte1_p_failures;
+SYSCTL_ULONG(_vm_pmap_pte1, OID_AUTO, p_failures, CTLFLAG_RD,
+ &pmap_pte1_p_failures, 0, "1MB page promotion failures");
+
+static u_long pmap_pte1_promotions;
+SYSCTL_ULONG(_vm_pmap_pte1, OID_AUTO, promotions, CTLFLAG_RD,
+ &pmap_pte1_promotions, 0, "1MB page promotions");
+
+static u_long pmap_pte1_kern_demotions;
+SYSCTL_ULONG(_vm_pmap_pte1, OID_AUTO, kern_demotions, CTLFLAG_RD,
+ &pmap_pte1_kern_demotions, 0, "1MB page kernel demotions");
+
+static u_long pmap_pte1_kern_promotions;
+SYSCTL_ULONG(_vm_pmap_pte1, OID_AUTO, kern_promotions, CTLFLAG_RD,
+ &pmap_pte1_kern_promotions, 0, "1MB page kernel promotions");
+
+static __inline ttb_entry_t
+pmap_ttb_get(pmap_t pmap)
+{
+
+ return (vtophys(pmap->pm_pt1) | ttb_flags);
+}
+
+/*
+ * Initialize a vm_page's machine-dependent fields.
+ *
+ * Variations:
+ * 1. Pages for L2 page tables are always not managed. So, pv_list and
+ * pt2_wirecount can share same physical space. However, proper
+ * initialization on a page alloc for page tables and reinitialization
+ * on the page free must be ensured.
+ */
+void
+pmap_page_init(vm_page_t m)
+{
+
+ TAILQ_INIT(&m->md.pv_list);
+ pt2_wirecount_init(m);
+ m->md.pat_mode = VM_MEMATTR_DEFAULT;
+}
+
+/*
+ * Virtualization for faster way how to zero whole page.
+ */
+static __inline void
+pagezero(void *page)
+{
+
+ bzero(page, PAGE_SIZE);
+}
+
+/*
+ * Zero L2 page table page.
+ * Use same KVA as in pmap_zero_page().
+ */
+static __inline vm_paddr_t
+pmap_pt2pg_zero(vm_page_t m)
+{
+ pt2_entry_t *cmap2_pte2p;
+ vm_paddr_t pa;
+ struct pcpu *pc;
+
+ pa = VM_PAGE_TO_PHYS(m);
+
+ /*
+ * XXX: For now, we map whole page even if it's already zero,
+ * to sync it even if the sync is only DSB.
+ */
+ sched_pin();
+ pc = get_pcpu();
+ cmap2_pte2p = pc->pc_cmap2_pte2p;
+ mtx_lock(&pc->pc_cmap_lock);
+ if (pte2_load(cmap2_pte2p) != 0)
+ panic("%s: CMAP2 busy", __func__);
+ pte2_store(cmap2_pte2p, PTE2_KERN_NG(pa, PTE2_AP_KRW,
+ vm_page_pte2_attr(m)));
+ /* Even VM_ALLOC_ZERO request is only advisory. */
+ if ((m->flags & PG_ZERO) == 0)
+ pagezero(pc->pc_cmap2_addr);
+ pte2_sync_range((pt2_entry_t *)pc->pc_cmap2_addr, PAGE_SIZE);
+ pte2_clear(cmap2_pte2p);
+ tlb_flush((vm_offset_t)pc->pc_cmap2_addr);
+
+ /*
+ * Unpin the thread before releasing the lock. Otherwise the thread
+ * could be rescheduled while still bound to the current CPU, only
+ * to unpin itself immediately upon resuming execution.
+ */
+ sched_unpin();
+ mtx_unlock(&pc->pc_cmap_lock);
+
+ return (pa);
+}
+
+/*
+ * Init just allocated page as L2 page table(s) holder
+ * and return its physical address.
+ */
+static __inline vm_paddr_t
+pmap_pt2pg_init(pmap_t pmap, vm_offset_t va, vm_page_t m)
+{
+ vm_paddr_t pa;
+ pt2_entry_t *pte2p;
+
+ /* Check page attributes. */
+ if (m->md.pat_mode != pt_memattr)
+ pmap_page_set_memattr(m, pt_memattr);
+
+ /* Zero page and init wire counts. */
+ pa = pmap_pt2pg_zero(m);
+ pt2_wirecount_init(m);
+
+ /*
+ * Map page to PT2MAP address space for given pmap.
+ * Note that PT2MAP space is shared with all pmaps.
+ */
+ if (pmap == kernel_pmap)
+ pmap_kenter_pt2tab(va, PTE2_KPT(pa));
+ else {
+ pte2p = pmap_pt2tab_entry(pmap, va);
+ pt2tab_store(pte2p, PTE2_KPT_NG(pa));
+ }
+
+ return (pa);
+}
+
+/*
+ * Initialize the pmap module.
+ * Called by vm_init, to initialize any structures that the pmap
+ * system needs to map virtual memory.
+ */
+void
+pmap_init(void)
+{
+ vm_size_t s;
+ pt2_entry_t *pte2p, pte2;
+ u_int i, pte1_idx, pv_npg;
+
+ PDEBUG(1, printf("%s: phys_start = %#x\n", __func__, PHYSADDR));
+
+ /*
+ * Initialize the vm page array entries for kernel pmap's
+ * L2 page table pages allocated in advance.
+ */
+ pte1_idx = pte1_index(KERNBASE - PT2MAP_SIZE);
+ pte2p = kern_pt2tab_entry(KERNBASE - PT2MAP_SIZE);
+ for (i = 0; i < nkpt2pg + NPG_IN_PT2TAB; i++, pte2p++) {
+ vm_paddr_t pa;
+ vm_page_t m;
+
+ pte2 = pte2_load(pte2p);
+ KASSERT(pte2_is_valid(pte2), ("%s: no valid entry", __func__));
+
+ pa = pte2_pa(pte2);
+ m = PHYS_TO_VM_PAGE(pa);
+ KASSERT(m >= vm_page_array &&
+ m < &vm_page_array[vm_page_array_size],
+ ("%s: L2 page table page is out of range", __func__));
+
+ m->pindex = pte1_idx;
+ m->phys_addr = pa;
+ pte1_idx += NPT2_IN_PG;
+ }
+
+ /*
+ * Initialize the address space (zone) for the pv entries. Set a
+ * high water mark so that the system can recover from excessive
+ * numbers of pv entries.
+ */
+ TUNABLE_INT_FETCH("vm.pmap.shpgperproc", &shpgperproc);
+ pv_entry_max = shpgperproc * maxproc + vm_cnt.v_page_count;
+ TUNABLE_INT_FETCH("vm.pmap.pv_entries", &pv_entry_max);
+ pv_entry_max = roundup(pv_entry_max, _NPCPV);
+ pv_entry_high_water = 9 * (pv_entry_max / 10);
+
+ /*
+ * Are large page mappings enabled?
+ */
+ TUNABLE_INT_FETCH("vm.pmap.sp_enabled", &sp_enabled);
+ if (sp_enabled) {
+ KASSERT(MAXPAGESIZES > 1 && pagesizes[1] == 0,
+ ("%s: can't assign to pagesizes[1]", __func__));
+ pagesizes[1] = PTE1_SIZE;
+ }
+
+ /*
+ * Calculate the size of the pv head table for sections.
+ * Handle the possibility that "vm_phys_segs[...].end" is zero.
+ * Note that the table is only for sections which could be promoted.
+ */
+ first_managed_pa = pte1_trunc(vm_phys_segs[0].start);
+ pv_npg = (pte1_trunc(vm_phys_segs[vm_phys_nsegs - 1].end - PAGE_SIZE)
+ - first_managed_pa) / PTE1_SIZE + 1;
+
+ /*
+ * Allocate memory for the pv head table for sections.
+ */
+ s = (vm_size_t)(pv_npg * sizeof(struct md_page));
+ s = round_page(s);
+ pv_table = (struct md_page *)kmem_malloc(s, M_WAITOK | M_ZERO);
+ for (i = 0; i < pv_npg; i++)
+ TAILQ_INIT(&pv_table[i].pv_list);
+
+ pv_maxchunks = MAX(pv_entry_max / _NPCPV, maxproc);
+ pv_chunkbase = (struct pv_chunk *)kva_alloc(PAGE_SIZE * pv_maxchunks);
+ if (pv_chunkbase == NULL)
+ panic("%s: not enough kvm for pv chunks", __func__);
+ pmap_pte2list_init(&pv_vafree, pv_chunkbase, pv_maxchunks);
+}
+
+/*
+ * Add a list of wired pages to the kva
+ * this routine is only used for temporary
+ * kernel mappings that do not need to have
+ * page modification or references recorded.
+ * Note that old mappings are simply written
+ * over. The page *must* be wired.
+ * Note: SMP coherent. Uses a ranged shootdown IPI.
+ */
+void
+pmap_qenter(vm_offset_t sva, vm_page_t *ma, int count)
+{
+ u_int anychanged;
+ pt2_entry_t *epte2p, *pte2p, pte2;
+ vm_page_t m;
+ vm_paddr_t pa;
+
+ anychanged = 0;
+ pte2p = pt2map_entry(sva);
+ epte2p = pte2p + count;
+ while (pte2p < epte2p) {
+ m = *ma++;
+ pa = VM_PAGE_TO_PHYS(m);
+ pte2 = pte2_load(pte2p);
+ if ((pte2_pa(pte2) != pa) ||
+ (pte2_attr(pte2) != vm_page_pte2_attr(m))) {
+ anychanged++;
+ pte2_store(pte2p, PTE2_KERN(pa, PTE2_AP_KRW,
+ vm_page_pte2_attr(m)));
+ }
+ pte2p++;
+ }
+ if (__predict_false(anychanged))
+ tlb_flush_range(sva, count * PAGE_SIZE);
+}
+
+/*
+ * This routine tears out page mappings from the
+ * kernel -- it is meant only for temporary mappings.
+ * Note: SMP coherent. Uses a ranged shootdown IPI.
+ */
+void
+pmap_qremove(vm_offset_t sva, int count)
+{
+ vm_offset_t va;
+
+ va = sva;
+ while (count-- > 0) {
+ pmap_kremove(va);
+ va += PAGE_SIZE;
+ }
+ tlb_flush_range(sva, va - sva);
+}
+
+/*
+ * Are we current address space or kernel?
+ */
+static __inline int
+pmap_is_current(pmap_t pmap)
+{
+
+ return (pmap == kernel_pmap ||
+ (pmap == vmspace_pmap(curthread->td_proc->p_vmspace)));
+}
+
+/*
+ * If the given pmap is not the current or kernel pmap, the returned
+ * pte2 must be released by passing it to pmap_pte2_release().
+ */
+static pt2_entry_t *
+pmap_pte2(pmap_t pmap, vm_offset_t va)
+{
+ pt1_entry_t pte1;
+ vm_paddr_t pt2pg_pa;
+
+ pte1 = pte1_load(pmap_pte1(pmap, va));
+ if (pte1_is_section(pte1))
+ panic("%s: attempt to map PTE1", __func__);
+ if (pte1_is_link(pte1)) {
+ /* Are we current address space or kernel? */
+ if (pmap_is_current(pmap))
+ return (pt2map_entry(va));
+ /* Note that L2 page table size is not equal to PAGE_SIZE. */
+ pt2pg_pa = trunc_page(pte1_link_pa(pte1));
+ mtx_lock(&PMAP2mutex);
+ if (pte2_pa(pte2_load(PMAP2)) != pt2pg_pa) {
+ pte2_store(PMAP2, PTE2_KPT(pt2pg_pa));
+ tlb_flush((vm_offset_t)PADDR2);
+ }
+ return (PADDR2 + (arm32_btop(va) & (NPTE2_IN_PG - 1)));
+ }
+ return (NULL);
+}
+
+/*
+ * Releases a pte2 that was obtained from pmap_pte2().
+ * Be prepared for the pte2p being NULL.
+ */
+static __inline void
+pmap_pte2_release(pt2_entry_t *pte2p)
+{
+
+ if ((pt2_entry_t *)(trunc_page((vm_offset_t)pte2p)) == PADDR2) {
+ mtx_unlock(&PMAP2mutex);
+ }
+}
+
+/*
+ * Super fast pmap_pte2 routine best used when scanning
+ * the pv lists. This eliminates many coarse-grained
+ * invltlb calls. Note that many of the pv list
+ * scans are across different pmaps. It is very wasteful
+ * to do an entire tlb flush for checking a single mapping.
+ *
+ * If the given pmap is not the current pmap, pvh_global_lock
+ * must be held and curthread pinned to a CPU.
+ */
+static pt2_entry_t *
+pmap_pte2_quick(pmap_t pmap, vm_offset_t va)
+{
+ pt1_entry_t pte1;
+ vm_paddr_t pt2pg_pa;
+
+ pte1 = pte1_load(pmap_pte1(pmap, va));
+ if (pte1_is_section(pte1))
+ panic("%s: attempt to map PTE1", __func__);
+ if (pte1_is_link(pte1)) {
+ /* Are we current address space or kernel? */
+ if (pmap_is_current(pmap))
+ return (pt2map_entry(va));
+ rw_assert(&pvh_global_lock, RA_WLOCKED);
+ KASSERT(curthread->td_pinned > 0,
+ ("%s: curthread not pinned", __func__));
+ /* Note that L2 page table size is not equal to PAGE_SIZE. */
+ pt2pg_pa = trunc_page(pte1_link_pa(pte1));
+ if (pte2_pa(pte2_load(PMAP1)) != pt2pg_pa) {
+ pte2_store(PMAP1, PTE2_KPT(pt2pg_pa));
+#ifdef SMP
+ PMAP1cpu = PCPU_GET(cpuid);
+#endif
+ tlb_flush_local((vm_offset_t)PADDR1);
+ PMAP1changed++;
+ } else
+#ifdef SMP
+ if (PMAP1cpu != PCPU_GET(cpuid)) {
+ PMAP1cpu = PCPU_GET(cpuid);
+ tlb_flush_local((vm_offset_t)PADDR1);
+ PMAP1changedcpu++;
+ } else
+#endif
+ PMAP1unchanged++;
+ return (PADDR1 + (arm32_btop(va) & (NPTE2_IN_PG - 1)));
+ }
+ return (NULL);
+}
+
+/*
+ * Routine: pmap_extract
+ * Function:
+ * Extract the physical page address associated
+ * with the given map/virtual_address pair.
+ */
+vm_paddr_t
+pmap_extract(pmap_t pmap, vm_offset_t va)
+{
+ vm_paddr_t pa;
+ pt1_entry_t pte1;
+ pt2_entry_t *pte2p;
+
+ PMAP_LOCK(pmap);
+ pte1 = pte1_load(pmap_pte1(pmap, va));
+ if (pte1_is_section(pte1))
+ pa = pte1_pa(pte1) | (va & PTE1_OFFSET);
+ else if (pte1_is_link(pte1)) {
+ pte2p = pmap_pte2(pmap, va);
+ pa = pte2_pa(pte2_load(pte2p)) | (va & PTE2_OFFSET);
+ pmap_pte2_release(pte2p);
+ } else
+ pa = 0;
+ PMAP_UNLOCK(pmap);
+ return (pa);
+}
+
+/*
+ * Routine: pmap_extract_and_hold
+ * Function:
+ * Atomically extract and hold the physical page
+ * with the given pmap and virtual address pair
+ * if that mapping permits the given protection.
+ */
+vm_page_t
+pmap_extract_and_hold(pmap_t pmap, vm_offset_t va, vm_prot_t prot)
+{
+ vm_paddr_t pa;
+ pt1_entry_t pte1;
+ pt2_entry_t pte2, *pte2p;
+ vm_page_t m;
+
+ m = NULL;
+ PMAP_LOCK(pmap);
+ pte1 = pte1_load(pmap_pte1(pmap, va));
+ if (pte1_is_section(pte1)) {
+ if (!(pte1 & PTE1_RO) || !(prot & VM_PROT_WRITE)) {
+ pa = pte1_pa(pte1) | (va & PTE1_OFFSET);
+ m = PHYS_TO_VM_PAGE(pa);
+ if (!vm_page_wire_mapped(m))
+ m = NULL;
+ }
+ } else if (pte1_is_link(pte1)) {
+ pte2p = pmap_pte2(pmap, va);
+ pte2 = pte2_load(pte2p);
+ pmap_pte2_release(pte2p);
+ if (pte2_is_valid(pte2) &&
+ (!(pte2 & PTE2_RO) || !(prot & VM_PROT_WRITE))) {
+ pa = pte2_pa(pte2);
+ m = PHYS_TO_VM_PAGE(pa);
+ if (!vm_page_wire_mapped(m))
+ m = NULL;
+ }
+ }
+ PMAP_UNLOCK(pmap);
+ return (m);
+}
+
+/*
+ * Grow the number of kernel L2 page table entries, if needed.
+ */
+void
+pmap_growkernel(vm_offset_t addr)
+{
+ vm_page_t m;
+ vm_paddr_t pt2pg_pa, pt2_pa;
+ pt1_entry_t pte1;
+ pt2_entry_t pte2;
+
+ PDEBUG(1, printf("%s: addr = %#x\n", __func__, addr));
+ /*
+ * All the time kernel_vm_end is first KVA for which underlying
+ * L2 page table is either not allocated or linked from L1 page table
+ * (not considering sections). Except for two possible cases:
+ *
+ * (1) in the very beginning as long as pmap_growkernel() was
+ * not called, it could be first unused KVA (which is not
+ * rounded up to PTE1_SIZE),
+ *
+ * (2) when all KVA space is mapped and vm_map_max(kernel_map)
+ * address is not rounded up to PTE1_SIZE. (For example,
+ * it could be 0xFFFFFFFF.)
+ */
+ kernel_vm_end = pte1_roundup(kernel_vm_end);
+ mtx_assert(&kernel_map->system_mtx, MA_OWNED);
+ addr = roundup2(addr, PTE1_SIZE);
+ if (addr - 1 >= vm_map_max(kernel_map))
+ addr = vm_map_max(kernel_map);
+ while (kernel_vm_end < addr) {
+ pte1 = pte1_load(kern_pte1(kernel_vm_end));
+ if (pte1_is_valid(pte1)) {
+ kernel_vm_end += PTE1_SIZE;
+ if (kernel_vm_end - 1 >= vm_map_max(kernel_map)) {
+ kernel_vm_end = vm_map_max(kernel_map);
+ break;
+ }
+ continue;
+ }
+
+ /*
+ * kernel_vm_end_new is used in pmap_pinit() when kernel
+ * mappings are entered to new pmap all at once to avoid race
+ * between pmap_kenter_pte1() and kernel_vm_end increase.
+ * The same aplies to pmap_kenter_pt2tab().
+ */
+ kernel_vm_end_new = kernel_vm_end + PTE1_SIZE;
+
+ pte2 = pt2tab_load(kern_pt2tab_entry(kernel_vm_end));
+ if (!pte2_is_valid(pte2)) {
+ /*
+ * Install new PT2s page into kernel PT2TAB.
+ */
+ m = vm_page_alloc(NULL,
+ pte1_index(kernel_vm_end) & ~PT2PG_MASK,
+ VM_ALLOC_INTERRUPT | VM_ALLOC_NOOBJ |
+ VM_ALLOC_WIRED | VM_ALLOC_ZERO);
+ if (m == NULL)
+ panic("%s: no memory to grow kernel", __func__);
+ /*
+ * QQQ: To link all new L2 page tables from L1 page
+ * table now and so pmap_kenter_pte1() them
+ * at once together with pmap_kenter_pt2tab()
+ * could be nice speed up. However,
+ * pmap_growkernel() does not happen so often...
+ * QQQ: The other TTBR is another option.
+ */
+ pt2pg_pa = pmap_pt2pg_init(kernel_pmap, kernel_vm_end,
+ m);
+ } else
+ pt2pg_pa = pte2_pa(pte2);
+
+ pt2_pa = page_pt2pa(pt2pg_pa, pte1_index(kernel_vm_end));
+ pmap_kenter_pte1(kernel_vm_end, PTE1_LINK(pt2_pa));
+
+ kernel_vm_end = kernel_vm_end_new;
+ if (kernel_vm_end - 1 >= vm_map_max(kernel_map)) {
+ kernel_vm_end = vm_map_max(kernel_map);
+ break;
+ }
+ }
+}
+
+static int
+kvm_size(SYSCTL_HANDLER_ARGS)
+{
+ unsigned long ksize = vm_max_kernel_address - KERNBASE;
+
+ return (sysctl_handle_long(oidp, &ksize, 0, req));
+}
+SYSCTL_PROC(_vm, OID_AUTO, kvm_size,
+ CTLTYPE_LONG | CTLFLAG_RD | CTLFLAG_NEEDGIANT, 0, 0, kvm_size, "IU",
+ "Size of KVM");
+
+static int
+kvm_free(SYSCTL_HANDLER_ARGS)
+{
+ unsigned long kfree = vm_max_kernel_address - kernel_vm_end;
+
+ return (sysctl_handle_long(oidp, &kfree, 0, req));
+}
+SYSCTL_PROC(_vm, OID_AUTO, kvm_free,
+ CTLTYPE_LONG | CTLFLAG_RD | CTLFLAG_NEEDGIANT, 0, 0, kvm_free, "IU",
+ "Amount of KVM free");
+
+/***********************************************
+ *
+ * Pmap allocation/deallocation routines.
+ *
+ ***********************************************/
+
+/*
+ * Initialize the pmap for the swapper process.
+ */
+void
+pmap_pinit0(pmap_t pmap)
+{
+ PDEBUG(1, printf("%s: pmap = %p\n", __func__, pmap));
+
+ PMAP_LOCK_INIT(pmap);
+
+ /*
+ * Kernel page table directory and pmap stuff around is already
+ * initialized, we are using it right now and here. So, finish
+ * only PMAP structures initialization for process0 ...
+ *
+ * Since the L1 page table and PT2TAB is shared with the kernel pmap,
+ * which is already included in the list "allpmaps", this pmap does
+ * not need to be inserted into that list.
+ */
+ pmap->pm_pt1 = kern_pt1;
+ pmap->pm_pt2tab = kern_pt2tab;
+ CPU_ZERO(&pmap->pm_active);
+ PCPU_SET(curpmap, pmap);
+ TAILQ_INIT(&pmap->pm_pvchunk);
+ bzero(&pmap->pm_stats, sizeof pmap->pm_stats);
+ CPU_SET(0, &pmap->pm_active);
+}
+
+static __inline void
+pte1_copy_nosync(pt1_entry_t *spte1p, pt1_entry_t *dpte1p, vm_offset_t sva,
+ vm_offset_t eva)
+{
+ u_int idx, count;
+
+ idx = pte1_index(sva);
+ count = (pte1_index(eva) - idx + 1) * sizeof(pt1_entry_t);
+ bcopy(spte1p + idx, dpte1p + idx, count);
+}
+
+static __inline void
+pt2tab_copy_nosync(pt2_entry_t *spte2p, pt2_entry_t *dpte2p, vm_offset_t sva,
+ vm_offset_t eva)
+{
+ u_int idx, count;
+
+ idx = pt2tab_index(sva);
+ count = (pt2tab_index(eva) - idx + 1) * sizeof(pt2_entry_t);
+ bcopy(spte2p + idx, dpte2p + idx, count);
+}
+
+/*
+ * Initialize a preallocated and zeroed pmap structure,
+ * such as one in a vmspace structure.
+ */
+int
+pmap_pinit(pmap_t pmap)
+{
+ pt1_entry_t *pte1p;
+ pt2_entry_t *pte2p;
+ vm_paddr_t pa, pt2tab_pa;
+ u_int i;
+
+ PDEBUG(6, printf("%s: pmap = %p, pm_pt1 = %p\n", __func__, pmap,
+ pmap->pm_pt1));
+
+ /*
+ * No need to allocate L2 page table space yet but we do need
+ * a valid L1 page table and PT2TAB table.
+ *
+ * Install shared kernel mappings to these tables. It's a little
+ * tricky as some parts of KVA are reserved for vectors, devices,
+ * and whatever else. These parts are supposed to be above
+ * vm_max_kernel_address. Thus two regions should be installed:
+ *
+ * (1) <KERNBASE, kernel_vm_end),
+ * (2) <vm_max_kernel_address, 0xFFFFFFFF>.
+ *
+ * QQQ: The second region should be stable enough to be installed
+ * only once in time when the tables are allocated.
+ * QQQ: Maybe copy of both regions at once could be faster ...
+ * QQQ: Maybe the other TTBR is an option.
+ *
+ * Finally, install own PT2TAB table to these tables.
+ */
+
+ if (pmap->pm_pt1 == NULL) {
+ pmap->pm_pt1 = (pt1_entry_t *)kmem_alloc_contig(NB_IN_PT1,
+ M_NOWAIT | M_ZERO, 0, -1UL, NB_IN_PT1, 0, pt_memattr);
+ if (pmap->pm_pt1 == NULL)
+ return (0);
+ }
+ if (pmap->pm_pt2tab == NULL) {
+ /*
+ * QQQ: (1) PT2TAB must be contiguous. If PT2TAB is one page
+ * only, what should be the only size for 32 bit systems,
+ * then we could allocate it with vm_page_alloc() and all
+ * the stuff needed as other L2 page table pages.
+ * (2) Note that a process PT2TAB is special L2 page table
+ * page. Its mapping in kernel_arena is permanent and can
+ * be used no matter which process is current. Its mapping
+ * in PT2MAP can be used only for current process.
+ */
+ pmap->pm_pt2tab = (pt2_entry_t *)kmem_alloc_attr(NB_IN_PT2TAB,
+ M_NOWAIT | M_ZERO, 0, -1UL, pt_memattr);
+ if (pmap->pm_pt2tab == NULL) {
+ /*
+ * QQQ: As struct pmap is allocated from UMA with
+ * UMA_ZONE_NOFREE flag, it's important to leave
+ * no allocation in pmap if initialization failed.
+ */
+ kmem_free((vm_offset_t)pmap->pm_pt1, NB_IN_PT1);
+ pmap->pm_pt1 = NULL;
+ return (0);
+ }
+ /*
+ * QQQ: Each L2 page table page vm_page_t has pindex set to
+ * pte1 index of virtual address mapped by this page.
+ * It's not valid for non kernel PT2TABs themselves.
+ * The pindex of these pages can not be altered because
+ * of the way how they are allocated now. However, it
+ * should not be a problem.
+ */
+ }
+
+ mtx_lock_spin(&allpmaps_lock);
+ /*
+ * To avoid race with pmap_kenter_pte1() and pmap_kenter_pt2tab(),
+ * kernel_vm_end_new is used here instead of kernel_vm_end.
+ */
+ pte1_copy_nosync(kern_pt1, pmap->pm_pt1, KERNBASE,
+ kernel_vm_end_new - 1);
+ pte1_copy_nosync(kern_pt1, pmap->pm_pt1, vm_max_kernel_address,
+ 0xFFFFFFFF);
+ pt2tab_copy_nosync(kern_pt2tab, pmap->pm_pt2tab, KERNBASE,
+ kernel_vm_end_new - 1);
+ pt2tab_copy_nosync(kern_pt2tab, pmap->pm_pt2tab, vm_max_kernel_address,
+ 0xFFFFFFFF);
+ LIST_INSERT_HEAD(&allpmaps, pmap, pm_list);
+ mtx_unlock_spin(&allpmaps_lock);
+
+ /*
+ * Store PT2MAP PT2 pages (a.k.a. PT2TAB) in PT2TAB itself.
+ * I.e. self reference mapping. The PT2TAB is private, however mapped
+ * into shared PT2MAP space, so the mapping should be not global.
+ */
+ pt2tab_pa = vtophys(pmap->pm_pt2tab);
+ pte2p = pmap_pt2tab_entry(pmap, (vm_offset_t)PT2MAP);
+ for (pa = pt2tab_pa, i = 0; i < NPG_IN_PT2TAB; i++, pa += PTE2_SIZE) {
+ pt2tab_store(pte2p++, PTE2_KPT_NG(pa));
+ }
+
+ /* Insert PT2MAP PT2s into pmap PT1. */
+ pte1p = pmap_pte1(pmap, (vm_offset_t)PT2MAP);
+ for (pa = pt2tab_pa, i = 0; i < NPT2_IN_PT2TAB; i++, pa += NB_IN_PT2) {
+ pte1_store(pte1p++, PTE1_LINK(pa));
+ }
+
+ /*
+ * Now synchronize new mapping which was made above.
+ */
+ pte1_sync_range(pmap->pm_pt1, NB_IN_PT1);
+ pte2_sync_range(pmap->pm_pt2tab, NB_IN_PT2TAB);
+
+ CPU_ZERO(&pmap->pm_active);
+ TAILQ_INIT(&pmap->pm_pvchunk);
+ bzero(&pmap->pm_stats, sizeof pmap->pm_stats);
+
+ return (1);
+}
+
+#ifdef INVARIANTS
+static boolean_t
+pt2tab_user_is_empty(pt2_entry_t *tab)
+{
+ u_int i, end;
+
+ end = pt2tab_index(VM_MAXUSER_ADDRESS);
+ for (i = 0; i < end; i++)
+ if (tab[i] != 0) return (FALSE);
+ return (TRUE);
+}
+#endif
+/*
+ * Release any resources held by the given physical map.
+ * Called when a pmap initialized by pmap_pinit is being released.
+ * Should only be called if the map contains no valid mappings.
+ */
+void
+pmap_release(pmap_t pmap)
+{
+#ifdef INVARIANTS
+ vm_offset_t start, end;
+#endif
+ KASSERT(pmap->pm_stats.resident_count == 0,
+ ("%s: pmap resident count %ld != 0", __func__,
+ pmap->pm_stats.resident_count));
+ KASSERT(pt2tab_user_is_empty(pmap->pm_pt2tab),
+ ("%s: has allocated user PT2(s)", __func__));
+ KASSERT(CPU_EMPTY(&pmap->pm_active),
+ ("%s: pmap %p is active on some CPU(s)", __func__, pmap));
+
+ mtx_lock_spin(&allpmaps_lock);
+ LIST_REMOVE(pmap, pm_list);
+ mtx_unlock_spin(&allpmaps_lock);
+
+#ifdef INVARIANTS
+ start = pte1_index(KERNBASE) * sizeof(pt1_entry_t);
+ end = (pte1_index(0xFFFFFFFF) + 1) * sizeof(pt1_entry_t);
+ bzero((char *)pmap->pm_pt1 + start, end - start);
+
+ start = pt2tab_index(KERNBASE) * sizeof(pt2_entry_t);
+ end = (pt2tab_index(0xFFFFFFFF) + 1) * sizeof(pt2_entry_t);
+ bzero((char *)pmap->pm_pt2tab + start, end - start);
+#endif
+ /*
+ * We are leaving PT1 and PT2TAB allocated on released pmap,
+ * so hopefully UMA vmspace_zone will always be inited with
+ * UMA_ZONE_NOFREE flag.
+ */
+}
+
+/*********************************************************
+ *
+ * L2 table pages and their pages management routines.
+ *
+ *********************************************************/
+
+/*
+ * Virtual interface for L2 page table wire counting.
+ *
+ * Each L2 page table in a page has own counter which counts a number of
+ * valid mappings in a table. Global page counter counts mappings in all
+ * tables in a page plus a single itself mapping in PT2TAB.
+ *
+ * During a promotion we leave the associated L2 page table counter
+ * untouched, so the table (strictly speaking a page which holds it)
+ * is never freed if promoted.
+ *
+ * If a page m->ref_count == 1 then no valid mappings exist in any L2 page
+ * table in the page and the page itself is only mapped in PT2TAB.
+ */
+
+static __inline void
+pt2_wirecount_init(vm_page_t m)
+{
+ u_int i;
+
+ /*
+ * Note: A page m is allocated with VM_ALLOC_WIRED flag and
+ * m->ref_count should be already set correctly.
+ * So, there is no need to set it again herein.
+ */
+ for (i = 0; i < NPT2_IN_PG; i++)
+ m->md.pt2_wirecount[i] = 0;
+}
+
+static __inline void
+pt2_wirecount_inc(vm_page_t m, uint32_t pte1_idx)
+{
+
+ /*
+ * Note: A just modificated pte2 (i.e. already allocated)
+ * is acquiring one extra reference which must be
+ * explicitly cleared. It influences the KASSERTs herein.
+ * All L2 page tables in a page always belong to the same
+ * pmap, so we allow only one extra reference for the page.
+ */
+ KASSERT(m->md.pt2_wirecount[pte1_idx & PT2PG_MASK] < (NPTE2_IN_PT2 + 1),
+ ("%s: PT2 is overflowing ...", __func__));
+ KASSERT(m->ref_count <= (NPTE2_IN_PG + 1),
+ ("%s: PT2PG is overflowing ...", __func__));
+
+ m->ref_count++;
+ m->md.pt2_wirecount[pte1_idx & PT2PG_MASK]++;
+}
+
+static __inline void
+pt2_wirecount_dec(vm_page_t m, uint32_t pte1_idx)
+{
+
+ KASSERT(m->md.pt2_wirecount[pte1_idx & PT2PG_MASK] != 0,
+ ("%s: PT2 is underflowing ...", __func__));
+ KASSERT(m->ref_count > 1,
+ ("%s: PT2PG is underflowing ...", __func__));
+
+ m->ref_count--;
+ m->md.pt2_wirecount[pte1_idx & PT2PG_MASK]--;
+}
+
+static __inline void
+pt2_wirecount_set(vm_page_t m, uint32_t pte1_idx, uint16_t count)
+{
+
+ KASSERT(count <= NPTE2_IN_PT2,
+ ("%s: invalid count %u", __func__, count));
+ KASSERT(m->ref_count > m->md.pt2_wirecount[pte1_idx & PT2PG_MASK],
+ ("%s: PT2PG corrupting (%u, %u) ...", __func__, m->ref_count,
+ m->md.pt2_wirecount[pte1_idx & PT2PG_MASK]));
+
+ m->ref_count -= m->md.pt2_wirecount[pte1_idx & PT2PG_MASK];
+ m->ref_count += count;
+ m->md.pt2_wirecount[pte1_idx & PT2PG_MASK] = count;
+
+ KASSERT(m->ref_count <= (NPTE2_IN_PG + 1),
+ ("%s: PT2PG is overflowed (%u) ...", __func__, m->ref_count));
+}
+
+static __inline uint32_t
+pt2_wirecount_get(vm_page_t m, uint32_t pte1_idx)
+{
+
+ return (m->md.pt2_wirecount[pte1_idx & PT2PG_MASK]);
+}
+
+static __inline boolean_t
+pt2_is_empty(vm_page_t m, vm_offset_t va)
+{
+
+ return (m->md.pt2_wirecount[pte1_index(va) & PT2PG_MASK] == 0);
+}
+
+static __inline boolean_t
+pt2_is_full(vm_page_t m, vm_offset_t va)
+{
+
+ return (m->md.pt2_wirecount[pte1_index(va) & PT2PG_MASK] ==
+ NPTE2_IN_PT2);
+}
+
+static __inline boolean_t
+pt2pg_is_empty(vm_page_t m)
+{
+
+ return (m->ref_count == 1);
+}
+
+/*
+ * This routine is called if the L2 page table
+ * is not mapped correctly.
+ */
+static vm_page_t
+_pmap_allocpte2(pmap_t pmap, vm_offset_t va, u_int flags)
+{
+ uint32_t pte1_idx;
+ pt1_entry_t *pte1p;
+ pt2_entry_t pte2;
+ vm_page_t m;
+ vm_paddr_t pt2pg_pa, pt2_pa;
+
+ pte1_idx = pte1_index(va);
+ pte1p = pmap->pm_pt1 + pte1_idx;
+
+ KASSERT(pte1_load(pte1p) == 0,
+ ("%s: pm_pt1[%#x] is not zero: %#x", __func__, pte1_idx,
+ pte1_load(pte1p)));
+
+ pte2 = pt2tab_load(pmap_pt2tab_entry(pmap, va));
+ if (!pte2_is_valid(pte2)) {
+ /*
+ * Install new PT2s page into pmap PT2TAB.
+ */
+ m = vm_page_alloc(NULL, pte1_idx & ~PT2PG_MASK,
+ VM_ALLOC_NOOBJ | VM_ALLOC_WIRED | VM_ALLOC_ZERO);
+ if (m == NULL) {
+ if ((flags & PMAP_ENTER_NOSLEEP) == 0) {
+ PMAP_UNLOCK(pmap);
+ rw_wunlock(&pvh_global_lock);
+ vm_wait(NULL);
+ rw_wlock(&pvh_global_lock);
+ PMAP_LOCK(pmap);
+ }
+
+ /*
+ * Indicate the need to retry. While waiting,
+ * the L2 page table page may have been allocated.
+ */
+ return (NULL);
+ }
+ pmap->pm_stats.resident_count++;
+ pt2pg_pa = pmap_pt2pg_init(pmap, va, m);
+ } else {
+ pt2pg_pa = pte2_pa(pte2);
+ m = PHYS_TO_VM_PAGE(pt2pg_pa);
+ }
+
+ pt2_wirecount_inc(m, pte1_idx);
+ pt2_pa = page_pt2pa(pt2pg_pa, pte1_idx);
+ pte1_store(pte1p, PTE1_LINK(pt2_pa));
+
+ return (m);
+}
+
+static vm_page_t
+pmap_allocpte2(pmap_t pmap, vm_offset_t va, u_int flags)
+{
+ u_int pte1_idx;
+ pt1_entry_t *pte1p, pte1;
+ vm_page_t m;
+
+ pte1_idx = pte1_index(va);
+retry:
+ pte1p = pmap->pm_pt1 + pte1_idx;
+ pte1 = pte1_load(pte1p);
+
+ /*
+ * This supports switching from a 1MB page to a
+ * normal 4K page.
+ */
+ if (pte1_is_section(pte1)) {
+ (void)pmap_demote_pte1(pmap, pte1p, va);
+ /*
+ * Reload pte1 after demotion.
+ *
+ * Note: Demotion can even fail as either PT2 is not find for
+ * the virtual address or PT2PG can not be allocated.
+ */
+ pte1 = pte1_load(pte1p);
+ }
+
+ /*
+ * If the L2 page table page is mapped, we just increment the
+ * hold count, and activate it.
+ */
+ if (pte1_is_link(pte1)) {
+ m = PHYS_TO_VM_PAGE(pte1_link_pa(pte1));
+ pt2_wirecount_inc(m, pte1_idx);
+ } else {
+ /*
+ * Here if the PT2 isn't mapped, or if it has
+ * been deallocated.
+ */
+ m = _pmap_allocpte2(pmap, va, flags);
+ if (m == NULL && (flags & PMAP_ENTER_NOSLEEP) == 0)
+ goto retry;
+ }
+
+ return (m);
+}
+
+/*
+ * Schedule the specified unused L2 page table page to be freed. Specifically,
+ * add the page to the specified list of pages that will be released to the
+ * physical memory manager after the TLB has been updated.
+ */
+static __inline void
+pmap_add_delayed_free_list(vm_page_t m, struct spglist *free)
+{
+
+ /*
+ * Put page on a list so that it is released after
+ * *ALL* TLB shootdown is done
+ */
+#ifdef PMAP_DEBUG
+ pmap_zero_page_check(m);
+#endif
+ m->flags |= PG_ZERO;
+ SLIST_INSERT_HEAD(free, m, plinks.s.ss);
+}
+
+/*
+ * Unwire L2 page tables page.
+ */
+static void
+pmap_unwire_pt2pg(pmap_t pmap, vm_offset_t va, vm_page_t m)
+{
+ pt1_entry_t *pte1p, opte1 __unused;
+ pt2_entry_t *pte2p;
+ uint32_t i;
+
+ KASSERT(pt2pg_is_empty(m),
+ ("%s: pmap %p PT2PG %p wired", __func__, pmap, m));
+
+ /*
+ * Unmap all L2 page tables in the page from L1 page table.
+ *
+ * QQQ: Individual L2 page tables (except the last one) can be unmapped
+ * earlier. However, we are doing that this way.
+ */
+ KASSERT(m->pindex == (pte1_index(va) & ~PT2PG_MASK),
+ ("%s: pmap %p va %#x PT2PG %p bad index", __func__, pmap, va, m));
+ pte1p = pmap->pm_pt1 + m->pindex;
+ for (i = 0; i < NPT2_IN_PG; i++, pte1p++) {
+ KASSERT(m->md.pt2_wirecount[i] == 0,
+ ("%s: pmap %p PT2 %u (PG %p) wired", __func__, pmap, i, m));
+ opte1 = pte1_load(pte1p);
+ if (pte1_is_link(opte1)) {
+ pte1_clear(pte1p);
+ /*
+ * Flush intermediate TLB cache.
+ */
+ pmap_tlb_flush(pmap, (m->pindex + i) << PTE1_SHIFT);
+ }
+#ifdef INVARIANTS
+ else
+ KASSERT((opte1 == 0) || pte1_is_section(opte1),
+ ("%s: pmap %p va %#x bad pte1 %x at %u", __func__,
+ pmap, va, opte1, i));
+#endif
+ }
+
+ /*
+ * Unmap the page from PT2TAB.
+ */
+ pte2p = pmap_pt2tab_entry(pmap, va);
+ (void)pt2tab_load_clear(pte2p);
+ pmap_tlb_flush(pmap, pt2map_pt2pg(va));
+
+ m->ref_count = 0;
+ pmap->pm_stats.resident_count--;
+
+ /*
+ * This barrier is so that the ordinary store unmapping
+ * the L2 page table page is globally performed before TLB shoot-
+ * down is begun.
+ */
+ wmb();
+ vm_wire_sub(1);
+}
+
+/*
+ * Decrements a L2 page table page's wire count, which is used to record the
+ * number of valid page table entries within the page. If the wire count
+ * drops to zero, then the page table page is unmapped. Returns TRUE if the
+ * page table page was unmapped and FALSE otherwise.
+ */
+static __inline boolean_t
+pmap_unwire_pt2(pmap_t pmap, vm_offset_t va, vm_page_t m, struct spglist *free)
+{
+ pt2_wirecount_dec(m, pte1_index(va));
+ if (pt2pg_is_empty(m)) {
+ /*
+ * QQQ: Wire count is zero, so whole page should be zero and
+ * we can set PG_ZERO flag to it.
+ * Note that when promotion is enabled, it takes some
+ * more efforts. See pmap_unwire_pt2_all() below.
+ */
+ pmap_unwire_pt2pg(pmap, va, m);
+ pmap_add_delayed_free_list(m, free);
+ return (TRUE);
+ } else
+ return (FALSE);
+}
+
+/*
+ * Drop a L2 page table page's wire count at once, which is used to record
+ * the number of valid L2 page table entries within the page. If the wire
+ * count drops to zero, then the L2 page table page is unmapped.
+ */
+static __inline void
+pmap_unwire_pt2_all(pmap_t pmap, vm_offset_t va, vm_page_t m,
+ struct spglist *free)
+{
+ u_int pte1_idx = pte1_index(va);
+
+ KASSERT(m->pindex == (pte1_idx & ~PT2PG_MASK),
+ ("%s: PT2 page's pindex is wrong", __func__));
+ KASSERT(m->ref_count > pt2_wirecount_get(m, pte1_idx),
+ ("%s: bad pt2 wire count %u > %u", __func__, m->ref_count,
+ pt2_wirecount_get(m, pte1_idx)));
+
+ /*
+ * It's possible that the L2 page table was never used.
+ * It happened in case that a section was created without promotion.
+ */
+ if (pt2_is_full(m, va)) {
+ pt2_wirecount_set(m, pte1_idx, 0);
+
+ /*
+ * QQQ: We clear L2 page table now, so when L2 page table page
+ * is going to be freed, we can set it PG_ZERO flag ...
+ * This function is called only on section mappings, so
+ * hopefully it's not to big overload.
+ *
+ * XXX: If pmap is current, existing PT2MAP mapping could be
+ * used for zeroing.
+ */
+ pmap_zero_page_area(m, page_pt2off(pte1_idx), NB_IN_PT2);
+ }
+#ifdef INVARIANTS
+ else
+ KASSERT(pt2_is_empty(m, va), ("%s: PT2 is not empty (%u)",
+ __func__, pt2_wirecount_get(m, pte1_idx)));
+#endif
+ if (pt2pg_is_empty(m)) {
+ pmap_unwire_pt2pg(pmap, va, m);
+ pmap_add_delayed_free_list(m, free);
+ }
+}
+
+/*
+ * After removing a L2 page table entry, this routine is used to
+ * conditionally free the page, and manage the hold/wire counts.
+ */
+static boolean_t
+pmap_unuse_pt2(pmap_t pmap, vm_offset_t va, struct spglist *free)
+{
+ pt1_entry_t pte1;
+ vm_page_t mpte;
+
+ if (va >= VM_MAXUSER_ADDRESS)
+ return (FALSE);
+ pte1 = pte1_load(pmap_pte1(pmap, va));
+ mpte = PHYS_TO_VM_PAGE(pte1_link_pa(pte1));
+ return (pmap_unwire_pt2(pmap, va, mpte, free));
+}
+
+/*************************************
+ *
+ * Page management routines.
+ *
+ *************************************/
+
+CTASSERT(sizeof(struct pv_chunk) == PAGE_SIZE);
+CTASSERT(_NPCM == 11);
+CTASSERT(_NPCPV == 336);
+
+static __inline struct pv_chunk *
+pv_to_chunk(pv_entry_t pv)
+{
+
+ return ((struct pv_chunk *)((uintptr_t)pv & ~(uintptr_t)PAGE_MASK));
+}
+
+#define PV_PMAP(pv) (pv_to_chunk(pv)->pc_pmap)
+
+#define PC_FREE0_9 0xfffffffful /* Free values for index 0 through 9 */
+#define PC_FREE10 0x0000fffful /* Free values for index 10 */
+
+static const uint32_t pc_freemask[_NPCM] = {
+ PC_FREE0_9, PC_FREE0_9, PC_FREE0_9,
+ PC_FREE0_9, PC_FREE0_9, PC_FREE0_9,
+ PC_FREE0_9, PC_FREE0_9, PC_FREE0_9,
+ PC_FREE0_9, PC_FREE10
+};
+
+SYSCTL_INT(_vm_pmap, OID_AUTO, pv_entry_count, CTLFLAG_RD, &pv_entry_count, 0,
+ "Current number of pv entries");
+
+#ifdef PV_STATS
+static int pc_chunk_count, pc_chunk_allocs, pc_chunk_frees, pc_chunk_tryfail;
+
+SYSCTL_INT(_vm_pmap, OID_AUTO, pc_chunk_count, CTLFLAG_RD, &pc_chunk_count, 0,
+ "Current number of pv entry chunks");
+SYSCTL_INT(_vm_pmap, OID_AUTO, pc_chunk_allocs, CTLFLAG_RD, &pc_chunk_allocs, 0,
+ "Current number of pv entry chunks allocated");
+SYSCTL_INT(_vm_pmap, OID_AUTO, pc_chunk_frees, CTLFLAG_RD, &pc_chunk_frees, 0,
+ "Current number of pv entry chunks frees");
+SYSCTL_INT(_vm_pmap, OID_AUTO, pc_chunk_tryfail, CTLFLAG_RD, &pc_chunk_tryfail,
+ 0, "Number of times tried to get a chunk page but failed.");
+
+static long pv_entry_frees, pv_entry_allocs;
+static int pv_entry_spare;
+
+SYSCTL_LONG(_vm_pmap, OID_AUTO, pv_entry_frees, CTLFLAG_RD, &pv_entry_frees, 0,
+ "Current number of pv entry frees");
+SYSCTL_LONG(_vm_pmap, OID_AUTO, pv_entry_allocs, CTLFLAG_RD, &pv_entry_allocs,
+ 0, "Current number of pv entry allocs");
+SYSCTL_INT(_vm_pmap, OID_AUTO, pv_entry_spare, CTLFLAG_RD, &pv_entry_spare, 0,
+ "Current number of spare pv entries");
+#endif
+
+/*
+ * Is given page managed?
+ */
+static __inline bool
+is_managed(vm_paddr_t pa)
+{
+ vm_page_t m;
+
+ m = PHYS_TO_VM_PAGE(pa);
+ if (m == NULL)
+ return (false);
+ return ((m->oflags & VPO_UNMANAGED) == 0);
+}
+
+static __inline bool
+pte1_is_managed(pt1_entry_t pte1)
+{
+
+ return (is_managed(pte1_pa(pte1)));
+}
+
+static __inline bool
+pte2_is_managed(pt2_entry_t pte2)
+{
+
+ return (is_managed(pte2_pa(pte2)));
+}
+
+/*
+ * We are in a serious low memory condition. Resort to
+ * drastic measures to free some pages so we can allocate
+ * another pv entry chunk.
+ */
+static vm_page_t
+pmap_pv_reclaim(pmap_t locked_pmap)
+{
+ struct pch newtail;
+ struct pv_chunk *pc;
+ struct md_page *pvh;
+ pt1_entry_t *pte1p;
+ pmap_t pmap;
+ pt2_entry_t *pte2p, tpte2;
+ pv_entry_t pv;
+ vm_offset_t va;
+ vm_page_t m, m_pc;
+ struct spglist free;
+ uint32_t inuse;
+ int bit, field, freed;
+
+ PMAP_LOCK_ASSERT(locked_pmap, MA_OWNED);
+ pmap = NULL;
+ m_pc = NULL;
+ SLIST_INIT(&free);
+ TAILQ_INIT(&newtail);
+ while ((pc = TAILQ_FIRST(&pv_chunks)) != NULL && (pv_vafree == 0 ||
+ SLIST_EMPTY(&free))) {
+ TAILQ_REMOVE(&pv_chunks, pc, pc_lru);
+ if (pmap != pc->pc_pmap) {
+ if (pmap != NULL) {
+ if (pmap != locked_pmap)
+ PMAP_UNLOCK(pmap);
+ }
+ pmap = pc->pc_pmap;
+ /* Avoid deadlock and lock recursion. */
+ if (pmap > locked_pmap)
+ PMAP_LOCK(pmap);
+ else if (pmap != locked_pmap && !PMAP_TRYLOCK(pmap)) {
+ pmap = NULL;
+ TAILQ_INSERT_TAIL(&newtail, pc, pc_lru);
+ continue;
+ }
+ }
+
+ /*
+ * Destroy every non-wired, 4 KB page mapping in the chunk.
+ */
+ freed = 0;
+ for (field = 0; field < _NPCM; field++) {
+ for (inuse = ~pc->pc_map[field] & pc_freemask[field];
+ inuse != 0; inuse &= ~(1UL << bit)) {
+ bit = ffs(inuse) - 1;
+ pv = &pc->pc_pventry[field * 32 + bit];
+ va = pv->pv_va;
+ pte1p = pmap_pte1(pmap, va);
+ if (pte1_is_section(pte1_load(pte1p)))
+ continue;
+ pte2p = pmap_pte2(pmap, va);
+ tpte2 = pte2_load(pte2p);
+ if ((tpte2 & PTE2_W) == 0)
+ tpte2 = pte2_load_clear(pte2p);
+ pmap_pte2_release(pte2p);
+ if ((tpte2 & PTE2_W) != 0)
+ continue;
+ KASSERT(tpte2 != 0,
+ ("pmap_pv_reclaim: pmap %p va %#x zero pte",
+ pmap, va));
+ pmap_tlb_flush(pmap, va);
+ m = PHYS_TO_VM_PAGE(pte2_pa(tpte2));
+ if (pte2_is_dirty(tpte2))
+ vm_page_dirty(m);
+ if ((tpte2 & PTE2_A) != 0)
+ vm_page_aflag_set(m, PGA_REFERENCED);
+ TAILQ_REMOVE(&m->md.pv_list, pv, pv_next);
+ if (TAILQ_EMPTY(&m->md.pv_list) &&
+ (m->flags & PG_FICTITIOUS) == 0) {
+ pvh = pa_to_pvh(VM_PAGE_TO_PHYS(m));
+ if (TAILQ_EMPTY(&pvh->pv_list)) {
+ vm_page_aflag_clear(m,
+ PGA_WRITEABLE);
+ }
+ }
+ pc->pc_map[field] |= 1UL << bit;
+ pmap_unuse_pt2(pmap, va, &free);
+ freed++;
+ }
+ }
+ if (freed == 0) {
+ TAILQ_INSERT_TAIL(&newtail, pc, pc_lru);
+ continue;
+ }
+ /* Every freed mapping is for a 4 KB page. */
+ pmap->pm_stats.resident_count -= freed;
+ PV_STAT(pv_entry_frees += freed);
+ PV_STAT(pv_entry_spare += freed);
+ pv_entry_count -= freed;
+ TAILQ_REMOVE(&pmap->pm_pvchunk, pc, pc_list);
+ for (field = 0; field < _NPCM; field++)
+ if (pc->pc_map[field] != pc_freemask[field]) {
+ TAILQ_INSERT_HEAD(&pmap->pm_pvchunk, pc,
+ pc_list);
+ TAILQ_INSERT_TAIL(&newtail, pc, pc_lru);
+
+ /*
+ * One freed pv entry in locked_pmap is
+ * sufficient.
+ */
+ if (pmap == locked_pmap)
+ goto out;
+ break;
+ }
+ if (field == _NPCM) {
+ PV_STAT(pv_entry_spare -= _NPCPV);
+ PV_STAT(pc_chunk_count--);
+ PV_STAT(pc_chunk_frees++);
+ /* Entire chunk is free; return it. */
+ m_pc = PHYS_TO_VM_PAGE(pmap_kextract((vm_offset_t)pc));
+ pmap_qremove((vm_offset_t)pc, 1);
+ pmap_pte2list_free(&pv_vafree, (vm_offset_t)pc);
+ break;
+ }
+ }
+out:
+ TAILQ_CONCAT(&pv_chunks, &newtail, pc_lru);
+ if (pmap != NULL) {
+ if (pmap != locked_pmap)
+ PMAP_UNLOCK(pmap);
+ }
+ if (m_pc == NULL && pv_vafree != 0 && SLIST_EMPTY(&free)) {
+ m_pc = SLIST_FIRST(&free);
+ SLIST_REMOVE_HEAD(&free, plinks.s.ss);
+ /* Recycle a freed page table page. */
+ m_pc->ref_count = 1;
+ vm_wire_add(1);
+ }
+ vm_page_free_pages_toq(&free, false);
+ return (m_pc);
+}
+
+static void
+free_pv_chunk(struct pv_chunk *pc)
+{
+ vm_page_t m;
+
+ TAILQ_REMOVE(&pv_chunks, pc, pc_lru);
+ PV_STAT(pv_entry_spare -= _NPCPV);
+ PV_STAT(pc_chunk_count--);
+ PV_STAT(pc_chunk_frees++);
+ /* entire chunk is free, return it */
+ m = PHYS_TO_VM_PAGE(pmap_kextract((vm_offset_t)pc));
+ pmap_qremove((vm_offset_t)pc, 1);
+ vm_page_unwire_noq(m);
+ vm_page_free(m);
+ pmap_pte2list_free(&pv_vafree, (vm_offset_t)pc);
+}
+
+/*
+ * Free the pv_entry back to the free list.
+ */
+static void
+free_pv_entry(pmap_t pmap, pv_entry_t pv)
+{
+ struct pv_chunk *pc;
+ int idx, field, bit;
+
+ rw_assert(&pvh_global_lock, RA_WLOCKED);
+ PMAP_LOCK_ASSERT(pmap, MA_OWNED);
+ PV_STAT(pv_entry_frees++);
+ PV_STAT(pv_entry_spare++);
+ pv_entry_count--;
+ pc = pv_to_chunk(pv);
+ idx = pv - &pc->pc_pventry[0];
+ field = idx / 32;
+ bit = idx % 32;
+ pc->pc_map[field] |= 1ul << bit;
+ for (idx = 0; idx < _NPCM; idx++)
+ if (pc->pc_map[idx] != pc_freemask[idx]) {
+ /*
+ * 98% of the time, pc is already at the head of the
+ * list. If it isn't already, move it to the head.
+ */
+ if (__predict_false(TAILQ_FIRST(&pmap->pm_pvchunk) !=
+ pc)) {
+ TAILQ_REMOVE(&pmap->pm_pvchunk, pc, pc_list);
+ TAILQ_INSERT_HEAD(&pmap->pm_pvchunk, pc,
+ pc_list);
+ }
+ return;
+ }
+ TAILQ_REMOVE(&pmap->pm_pvchunk, pc, pc_list);
+ free_pv_chunk(pc);
+}
+
+/*
+ * Get a new pv_entry, allocating a block from the system
+ * when needed.
+ */
+static pv_entry_t
+get_pv_entry(pmap_t pmap, boolean_t try)
+{
+ static const struct timeval printinterval = { 60, 0 };
+ static struct timeval lastprint;
+ int bit, field;
+ pv_entry_t pv;
+ struct pv_chunk *pc;
+ vm_page_t m;
+
+ rw_assert(&pvh_global_lock, RA_WLOCKED);
+ PMAP_LOCK_ASSERT(pmap, MA_OWNED);
+ PV_STAT(pv_entry_allocs++);
+ pv_entry_count++;
+ if (pv_entry_count > pv_entry_high_water)
+ if (ratecheck(&lastprint, &printinterval))
+ printf("Approaching the limit on PV entries, consider "
+ "increasing either the vm.pmap.shpgperproc or the "
+ "vm.pmap.pv_entries tunable.\n");
+retry:
+ pc = TAILQ_FIRST(&pmap->pm_pvchunk);
+ if (pc != NULL) {
+ for (field = 0; field < _NPCM; field++) {
+ if (pc->pc_map[field]) {
+ bit = ffs(pc->pc_map[field]) - 1;
+ break;
+ }
+ }
+ if (field < _NPCM) {
+ pv = &pc->pc_pventry[field * 32 + bit];
+ pc->pc_map[field] &= ~(1ul << bit);
+ /* If this was the last item, move it to tail */
+ for (field = 0; field < _NPCM; field++)
+ if (pc->pc_map[field] != 0) {
+ PV_STAT(pv_entry_spare--);
+ return (pv); /* not full, return */
+ }
+ TAILQ_REMOVE(&pmap->pm_pvchunk, pc, pc_list);
+ TAILQ_INSERT_TAIL(&pmap->pm_pvchunk, pc, pc_list);
+ PV_STAT(pv_entry_spare--);
+ return (pv);
+ }
+ }
+ /*
+ * Access to the pte2list "pv_vafree" is synchronized by the pvh
+ * global lock. If "pv_vafree" is currently non-empty, it will
+ * remain non-empty until pmap_pte2list_alloc() completes.
+ */
+ if (pv_vafree == 0 || (m = vm_page_alloc(NULL, 0, VM_ALLOC_NORMAL |
+ VM_ALLOC_NOOBJ | VM_ALLOC_WIRED)) == NULL) {
+ if (try) {
+ pv_entry_count--;
+ PV_STAT(pc_chunk_tryfail++);
+ return (NULL);
+ }
+ m = pmap_pv_reclaim(pmap);
+ if (m == NULL)
+ goto retry;
+ }
+ PV_STAT(pc_chunk_count++);
+ PV_STAT(pc_chunk_allocs++);
+ pc = (struct pv_chunk *)pmap_pte2list_alloc(&pv_vafree);
+ pmap_qenter((vm_offset_t)pc, &m, 1);
+ pc->pc_pmap = pmap;
+ pc->pc_map[0] = pc_freemask[0] & ~1ul; /* preallocated bit 0 */
+ for (field = 1; field < _NPCM; field++)
+ pc->pc_map[field] = pc_freemask[field];
+ TAILQ_INSERT_TAIL(&pv_chunks, pc, pc_lru);
+ pv = &pc->pc_pventry[0];
+ TAILQ_INSERT_HEAD(&pmap->pm_pvchunk, pc, pc_list);
+ PV_STAT(pv_entry_spare += _NPCPV - 1);
+ return (pv);
+}
+
+/*
+ * Create a pv entry for page at pa for
+ * (pmap, va).
+ */
+static void
+pmap_insert_entry(pmap_t pmap, vm_offset_t va, vm_page_t m)
+{
+ pv_entry_t pv;
+
+ rw_assert(&pvh_global_lock, RA_WLOCKED);
+ PMAP_LOCK_ASSERT(pmap, MA_OWNED);
+ pv = get_pv_entry(pmap, FALSE);
+ pv->pv_va = va;
+ TAILQ_INSERT_TAIL(&m->md.pv_list, pv, pv_next);
+}
+
+static __inline pv_entry_t
+pmap_pvh_remove(struct md_page *pvh, pmap_t pmap, vm_offset_t va)
+{
+ pv_entry_t pv;
+
+ rw_assert(&pvh_global_lock, RA_WLOCKED);
+ TAILQ_FOREACH(pv, &pvh->pv_list, pv_next) {
+ if (pmap == PV_PMAP(pv) && va == pv->pv_va) {
+ TAILQ_REMOVE(&pvh->pv_list, pv, pv_next);
+ break;
+ }
+ }
+ return (pv);
+}
+
+static void
+pmap_pvh_free(struct md_page *pvh, pmap_t pmap, vm_offset_t va)
+{
+ pv_entry_t pv;
+
+ pv = pmap_pvh_remove(pvh, pmap, va);
+ KASSERT(pv != NULL, ("pmap_pvh_free: pv not found"));
+ free_pv_entry(pmap, pv);
+}
+
+static void
+pmap_remove_entry(pmap_t pmap, vm_page_t m, vm_offset_t va)
+{
+ struct md_page *pvh;
+
+ rw_assert(&pvh_global_lock, RA_WLOCKED);
+ pmap_pvh_free(&m->md, pmap, va);
+ if (TAILQ_EMPTY(&m->md.pv_list) && (m->flags & PG_FICTITIOUS) == 0) {
+ pvh = pa_to_pvh(VM_PAGE_TO_PHYS(m));
+ if (TAILQ_EMPTY(&pvh->pv_list))
+ vm_page_aflag_clear(m, PGA_WRITEABLE);
+ }
+}
+
+static void
+pmap_pv_demote_pte1(pmap_t pmap, vm_offset_t va, vm_paddr_t pa)
+{
+ struct md_page *pvh;
+ pv_entry_t pv;
+ vm_offset_t va_last;
+ vm_page_t m;
+
+ rw_assert(&pvh_global_lock, RA_WLOCKED);
+ KASSERT((pa & PTE1_OFFSET) == 0,
+ ("pmap_pv_demote_pte1: pa is not 1mpage aligned"));
+
+ /*
+ * Transfer the 1mpage's pv entry for this mapping to the first
+ * page's pv list.
+ */
+ pvh = pa_to_pvh(pa);
+ va = pte1_trunc(va);
+ pv = pmap_pvh_remove(pvh, pmap, va);
+ KASSERT(pv != NULL, ("pmap_pv_demote_pte1: pv not found"));
+ m = PHYS_TO_VM_PAGE(pa);
+ TAILQ_INSERT_TAIL(&m->md.pv_list, pv, pv_next);
+ /* Instantiate the remaining NPTE2_IN_PT2 - 1 pv entries. */
+ va_last = va + PTE1_SIZE - PAGE_SIZE;
+ do {
+ m++;
+ KASSERT((m->oflags & VPO_UNMANAGED) == 0,
+ ("pmap_pv_demote_pte1: page %p is not managed", m));
+ va += PAGE_SIZE;
+ pmap_insert_entry(pmap, va, m);
+ } while (va < va_last);
+}
+
+#if VM_NRESERVLEVEL > 0
+static void
+pmap_pv_promote_pte1(pmap_t pmap, vm_offset_t va, vm_paddr_t pa)
+{
+ struct md_page *pvh;
+ pv_entry_t pv;
+ vm_offset_t va_last;
+ vm_page_t m;
+
+ rw_assert(&pvh_global_lock, RA_WLOCKED);
+ KASSERT((pa & PTE1_OFFSET) == 0,
+ ("pmap_pv_promote_pte1: pa is not 1mpage aligned"));
+
+ /*
+ * Transfer the first page's pv entry for this mapping to the
+ * 1mpage's pv list. Aside from avoiding the cost of a call
+ * to get_pv_entry(), a transfer avoids the possibility that
+ * get_pv_entry() calls pmap_pv_reclaim() and that pmap_pv_reclaim()
+ * removes one of the mappings that is being promoted.
+ */
+ m = PHYS_TO_VM_PAGE(pa);
+ va = pte1_trunc(va);
+ pv = pmap_pvh_remove(&m->md, pmap, va);
+ KASSERT(pv != NULL, ("pmap_pv_promote_pte1: pv not found"));
+ pvh = pa_to_pvh(pa);
+ TAILQ_INSERT_TAIL(&pvh->pv_list, pv, pv_next);
+ /* Free the remaining NPTE2_IN_PT2 - 1 pv entries. */
+ va_last = va + PTE1_SIZE - PAGE_SIZE;
+ do {
+ m++;
+ va += PAGE_SIZE;
+ pmap_pvh_free(&m->md, pmap, va);
+ } while (va < va_last);
+}
+#endif
+
+/*
+ * Conditionally create a pv entry.
+ */
+static boolean_t
+pmap_try_insert_pv_entry(pmap_t pmap, vm_offset_t va, vm_page_t m)
+{
+ pv_entry_t pv;
+
+ rw_assert(&pvh_global_lock, RA_WLOCKED);
+ PMAP_LOCK_ASSERT(pmap, MA_OWNED);
+ if (pv_entry_count < pv_entry_high_water &&
+ (pv = get_pv_entry(pmap, TRUE)) != NULL) {
+ pv->pv_va = va;
+ TAILQ_INSERT_TAIL(&m->md.pv_list, pv, pv_next);
+ return (TRUE);
+ } else
+ return (FALSE);
+}
+
+/*
+ * Create the pv entries for each of the pages within a section.
+ */
+static bool
+pmap_pv_insert_pte1(pmap_t pmap, vm_offset_t va, pt1_entry_t pte1, u_int flags)
+{
+ struct md_page *pvh;
+ pv_entry_t pv;
+ bool noreclaim;
+
+ rw_assert(&pvh_global_lock, RA_WLOCKED);
+ noreclaim = (flags & PMAP_ENTER_NORECLAIM) != 0;
+ if ((noreclaim && pv_entry_count >= pv_entry_high_water) ||
+ (pv = get_pv_entry(pmap, noreclaim)) == NULL)
+ return (false);
+ pv->pv_va = va;
+ pvh = pa_to_pvh(pte1_pa(pte1));
+ TAILQ_INSERT_TAIL(&pvh->pv_list, pv, pv_next);
+ return (true);
+}
+
+static inline void
+pmap_tlb_flush_pte1(pmap_t pmap, vm_offset_t va, pt1_entry_t npte1)
+{
+
+ /* Kill all the small mappings or the big one only. */
+ if (pte1_is_section(npte1))
+ pmap_tlb_flush_range(pmap, pte1_trunc(va), PTE1_SIZE);
+ else
+ pmap_tlb_flush(pmap, pte1_trunc(va));
+}
+
+/*
+ * Update kernel pte1 on all pmaps.
+ *
+ * The following function is called only on one cpu with disabled interrupts.
+ * In SMP case, smp_rendezvous_cpus() is used to stop other cpus. This way
+ * nobody can invoke explicit hardware table walk during the update of pte1.
+ * Unsolicited hardware table walk can still happen, invoked by speculative
+ * data or instruction prefetch or even by speculative hardware table walk.
+ *
+ * The break-before-make approach should be implemented here. However, it's
+ * not so easy to do that for kernel mappings as it would be unhappy to unmap
+ * itself unexpectedly but voluntarily.
+ */
+static void
+pmap_update_pte1_kernel(vm_offset_t va, pt1_entry_t npte1)
+{
+ pmap_t pmap;
+ pt1_entry_t *pte1p;
+
+ /*
+ * Get current pmap. Interrupts should be disabled here
+ * so PCPU_GET() is done atomically.
+ */
+ pmap = PCPU_GET(curpmap);
+ if (pmap == NULL)
+ pmap = kernel_pmap;
+
+ /*
+ * (1) Change pte1 on current pmap.
+ * (2) Flush all obsolete TLB entries on current CPU.
+ * (3) Change pte1 on all pmaps.
+ * (4) Flush all obsolete TLB entries on all CPUs in SMP case.
+ */
+
+ pte1p = pmap_pte1(pmap, va);
+ pte1_store(pte1p, npte1);
+
+ /* Kill all the small mappings or the big one only. */
+ if (pte1_is_section(npte1)) {
+ pmap_pte1_kern_promotions++;
+ tlb_flush_range_local(pte1_trunc(va), PTE1_SIZE);
+ } else {
+ pmap_pte1_kern_demotions++;
+ tlb_flush_local(pte1_trunc(va));
+ }
+
+ /*
+ * In SMP case, this function is called when all cpus are at smp
+ * rendezvous, so there is no need to use 'allpmaps_lock' lock here.
+ * In UP case, the function is called with this lock locked.
+ */
+ LIST_FOREACH(pmap, &allpmaps, pm_list) {
+ pte1p = pmap_pte1(pmap, va);
+ pte1_store(pte1p, npte1);
+ }
+
+#ifdef SMP
+ /* Kill all the small mappings or the big one only. */
+ if (pte1_is_section(npte1))
+ tlb_flush_range(pte1_trunc(va), PTE1_SIZE);
+ else
+ tlb_flush(pte1_trunc(va));
+#endif
+}
+
+#ifdef SMP
+struct pte1_action {
+ vm_offset_t va;
+ pt1_entry_t npte1;
+ u_int update; /* CPU that updates the PTE1 */
+};
+
+static void
+pmap_update_pte1_action(void *arg)
+{
+ struct pte1_action *act = arg;
+
+ if (act->update == PCPU_GET(cpuid))
+ pmap_update_pte1_kernel(act->va, act->npte1);
+}
+
+/*
+ * Change pte1 on current pmap.
+ * Note that kernel pte1 must be changed on all pmaps.
+ *
+ * According to the architecture reference manual published by ARM,
+ * the behaviour is UNPREDICTABLE when two or more TLB entries map the same VA.
+ * According to this manual, UNPREDICTABLE behaviours must never happen in
+ * a viable system. In contrast, on x86 processors, it is not specified which
+ * TLB entry mapping the virtual address will be used, but the MMU doesn't
+ * generate a bogus translation the way it does on Cortex-A8 rev 2 (Beaglebone
+ * Black).
+ *
+ * It's a problem when either promotion or demotion is being done. The pte1
+ * update and appropriate TLB flush must be done atomically in general.
+ */
+static void
+pmap_change_pte1(pmap_t pmap, pt1_entry_t *pte1p, vm_offset_t va,
+ pt1_entry_t npte1)
+{
+
+ if (pmap == kernel_pmap) {
+ struct pte1_action act;
+
+ sched_pin();
+ act.va = va;
+ act.npte1 = npte1;
+ act.update = PCPU_GET(cpuid);
+ smp_rendezvous_cpus(all_cpus, smp_no_rendezvous_barrier,
+ pmap_update_pte1_action, NULL, &act);
+ sched_unpin();
+ } else {
+ register_t cspr;
+
+ /*
+ * Use break-before-make approach for changing userland
+ * mappings. It can cause L1 translation aborts on other
+ * cores in SMP case. So, special treatment is implemented
+ * in pmap_fault(). To reduce the likelihood that another core
+ * will be affected by the broken mapping, disable interrupts
+ * until the mapping change is completed.
+ */
+ cspr = disable_interrupts(PSR_I | PSR_F);
+ pte1_clear(pte1p);
+ pmap_tlb_flush_pte1(pmap, va, npte1);
+ pte1_store(pte1p, npte1);
+ restore_interrupts(cspr);
+ }
+}
+#else
+static void
+pmap_change_pte1(pmap_t pmap, pt1_entry_t *pte1p, vm_offset_t va,
+ pt1_entry_t npte1)
+{
+
+ if (pmap == kernel_pmap) {
+ mtx_lock_spin(&allpmaps_lock);
+ pmap_update_pte1_kernel(va, npte1);
+ mtx_unlock_spin(&allpmaps_lock);
+ } else {
+ register_t cspr;
+
+ /*
+ * Use break-before-make approach for changing userland
+ * mappings. It's absolutely safe in UP case when interrupts
+ * are disabled.
+ */
+ cspr = disable_interrupts(PSR_I | PSR_F);
+ pte1_clear(pte1p);
+ pmap_tlb_flush_pte1(pmap, va, npte1);
+ pte1_store(pte1p, npte1);
+ restore_interrupts(cspr);
+ }
+}
+#endif
+
+#if VM_NRESERVLEVEL > 0
+/*
+ * Tries to promote the NPTE2_IN_PT2, contiguous 4KB page mappings that are
+ * within a single page table page (PT2) to a single 1MB page mapping.
+ * For promotion to occur, two conditions must be met: (1) the 4KB page
+ * mappings must map aligned, contiguous physical memory and (2) the 4KB page
+ * mappings must have identical characteristics.
+ *
+ * Managed (PG_MANAGED) mappings within the kernel address space are not
+ * promoted. The reason is that kernel PTE1s are replicated in each pmap but
+ * pmap_remove_write(), pmap_clear_modify(), and pmap_clear_reference() only
+ * read the PTE1 from the kernel pmap.
+ */
+static void
+pmap_promote_pte1(pmap_t pmap, pt1_entry_t *pte1p, vm_offset_t va)
+{
+ pt1_entry_t npte1;
+ pt2_entry_t *fpte2p, fpte2, fpte2_fav;
+ pt2_entry_t *pte2p, pte2;
+ vm_offset_t pteva __unused;
+ vm_page_t m __unused;
+
+ PDEBUG(6, printf("%s(%p): try for va %#x pte1 %#x at %p\n", __func__,
+ pmap, va, pte1_load(pte1p), pte1p));
+
+ PMAP_LOCK_ASSERT(pmap, MA_OWNED);
+
+ /*
+ * Examine the first PTE2 in the specified PT2. Abort if this PTE2 is
+ * either invalid, unused, or does not map the first 4KB physical page
+ * within a 1MB page.
+ */
+ fpte2p = pmap_pte2_quick(pmap, pte1_trunc(va));
+ fpte2 = pte2_load(fpte2p);
+ if ((fpte2 & ((PTE2_FRAME & PTE1_OFFSET) | PTE2_A | PTE2_V)) !=
+ (PTE2_A | PTE2_V)) {
+ pmap_pte1_p_failures++;
+ CTR3(KTR_PMAP, "%s: failure(1) for va %#x in pmap %p",
+ __func__, va, pmap);
+ return;
+ }
+ if (pte2_is_managed(fpte2) && pmap == kernel_pmap) {
+ pmap_pte1_p_failures++;
+ CTR3(KTR_PMAP, "%s: failure(2) for va %#x in pmap %p",
+ __func__, va, pmap);
+ return;
+ }
+ if ((fpte2 & (PTE2_NM | PTE2_RO)) == PTE2_NM) {
+ /*
+ * When page is not modified, PTE2_RO can be set without
+ * a TLB invalidation.
+ */
+ fpte2 |= PTE2_RO;
+ pte2_store(fpte2p, fpte2);
+ }
+
+ /*
+ * Examine each of the other PTE2s in the specified PT2. Abort if this
+ * PTE2 maps an unexpected 4KB physical page or does not have identical
+ * characteristics to the first PTE2.
+ */
+ fpte2_fav = (fpte2 & (PTE2_FRAME | PTE2_A | PTE2_V));
+ fpte2_fav += PTE1_SIZE - PTE2_SIZE; /* examine from the end */
+ for (pte2p = fpte2p + NPTE2_IN_PT2 - 1; pte2p > fpte2p; pte2p--) {
+ pte2 = pte2_load(pte2p);
+ if ((pte2 & (PTE2_FRAME | PTE2_A | PTE2_V)) != fpte2_fav) {
+ pmap_pte1_p_failures++;
+ CTR3(KTR_PMAP, "%s: failure(3) for va %#x in pmap %p",
+ __func__, va, pmap);
+ return;
+ }
+ if ((pte2 & (PTE2_NM | PTE2_RO)) == PTE2_NM) {
+ /*
+ * When page is not modified, PTE2_RO can be set
+ * without a TLB invalidation. See note above.
+ */
+ pte2 |= PTE2_RO;
+ pte2_store(pte2p, pte2);
+ pteva = pte1_trunc(va) | (pte2 & PTE1_OFFSET &
+ PTE2_FRAME);
+ CTR3(KTR_PMAP, "%s: protect for va %#x in pmap %p",
+ __func__, pteva, pmap);
+ }
+ if ((pte2 & PTE2_PROMOTE) != (fpte2 & PTE2_PROMOTE)) {
+ pmap_pte1_p_failures++;
+ CTR3(KTR_PMAP, "%s: failure(4) for va %#x in pmap %p",
+ __func__, va, pmap);
+ return;
+ }
+
+ fpte2_fav -= PTE2_SIZE;
+ }
+ /*
+ * The page table page in its current state will stay in PT2TAB
+ * until the PTE1 mapping the section is demoted by pmap_demote_pte1()
+ * or destroyed by pmap_remove_pte1().
+ *
+ * Note that L2 page table size is not equal to PAGE_SIZE.
+ */
+ m = PHYS_TO_VM_PAGE(trunc_page(pte1_link_pa(pte1_load(pte1p))));
+ KASSERT(m >= vm_page_array && m < &vm_page_array[vm_page_array_size],
+ ("%s: PT2 page is out of range", __func__));
+ KASSERT(m->pindex == (pte1_index(va) & ~PT2PG_MASK),
+ ("%s: PT2 page's pindex is wrong", __func__));
+
+ /*
+ * Get pte1 from pte2 format.
+ */
+ npte1 = (fpte2 & PTE1_FRAME) | ATTR_TO_L1(fpte2) | PTE1_V;
+
+ /*
+ * Promote the pv entries.
+ */
+ if (pte2_is_managed(fpte2))
+ pmap_pv_promote_pte1(pmap, va, pte1_pa(npte1));
+
+ /*
+ * Promote the mappings.
+ */
+ pmap_change_pte1(pmap, pte1p, va, npte1);
+
+ pmap_pte1_promotions++;
+ CTR3(KTR_PMAP, "%s: success for va %#x in pmap %p",
+ __func__, va, pmap);
+
+ PDEBUG(6, printf("%s(%p): success for va %#x pte1 %#x(%#x) at %p\n",
+ __func__, pmap, va, npte1, pte1_load(pte1p), pte1p));
+}
+#endif /* VM_NRESERVLEVEL > 0 */
+
+/*
+ * Zero L2 page table page.
+ */
+static __inline void
+pmap_clear_pt2(pt2_entry_t *fpte2p)
+{
+ pt2_entry_t *pte2p;
+
+ for (pte2p = fpte2p; pte2p < fpte2p + NPTE2_IN_PT2; pte2p++)
+ pte2_clear(pte2p);
+
+}
+
+/*
+ * Removes a 1MB page mapping from the kernel pmap.
+ */
+static void
+pmap_remove_kernel_pte1(pmap_t pmap, pt1_entry_t *pte1p, vm_offset_t va)
+{
+ vm_page_t m;
+ uint32_t pte1_idx;
+ pt2_entry_t *fpte2p;
+ vm_paddr_t pt2_pa;
+
+ PMAP_LOCK_ASSERT(pmap, MA_OWNED);
+ m = pmap_pt2_page(pmap, va);
+ if (m == NULL)
+ /*
+ * QQQ: Is this function called only on promoted pte1?
+ * We certainly do section mappings directly
+ * (without promotion) in kernel !!!
+ */
+ panic("%s: missing pt2 page", __func__);
+
+ pte1_idx = pte1_index(va);
+
+ /*
+ * Initialize the L2 page table.
+ */
+ fpte2p = page_pt2(pt2map_pt2pg(va), pte1_idx);
+ pmap_clear_pt2(fpte2p);
+
+ /*
+ * Remove the mapping.
+ */
+ pt2_pa = page_pt2pa(VM_PAGE_TO_PHYS(m), pte1_idx);
+ pmap_kenter_pte1(va, PTE1_LINK(pt2_pa));
+
+ /*
+ * QQQ: We do not need to invalidate PT2MAP mapping
+ * as we did not change it. I.e. the L2 page table page
+ * was and still is mapped the same way.
+ */
+}
+
+/*
+ * Do the things to unmap a section in a process
+ */
+static void
+pmap_remove_pte1(pmap_t pmap, pt1_entry_t *pte1p, vm_offset_t sva,
+ struct spglist *free)
+{
+ pt1_entry_t opte1;
+ struct md_page *pvh;
+ vm_offset_t eva, va;
+ vm_page_t m;
+
+ PDEBUG(6, printf("%s(%p): va %#x pte1 %#x at %p\n", __func__, pmap, sva,
+ pte1_load(pte1p), pte1p));
+
+ PMAP_LOCK_ASSERT(pmap, MA_OWNED);
+ KASSERT((sva & PTE1_OFFSET) == 0,
+ ("%s: sva is not 1mpage aligned", __func__));
+
+ /*
+ * Clear and invalidate the mapping. It should occupy one and only TLB
+ * entry. So, pmap_tlb_flush() called with aligned address should be
+ * sufficient.
+ */
+ opte1 = pte1_load_clear(pte1p);
+ pmap_tlb_flush(pmap, sva);
+
+ if (pte1_is_wired(opte1))
+ pmap->pm_stats.wired_count -= PTE1_SIZE / PAGE_SIZE;
+ pmap->pm_stats.resident_count -= PTE1_SIZE / PAGE_SIZE;
+ if (pte1_is_managed(opte1)) {
+ pvh = pa_to_pvh(pte1_pa(opte1));
+ pmap_pvh_free(pvh, pmap, sva);
+ eva = sva + PTE1_SIZE;
+ for (va = sva, m = PHYS_TO_VM_PAGE(pte1_pa(opte1));
+ va < eva; va += PAGE_SIZE, m++) {
+ if (pte1_is_dirty(opte1))
+ vm_page_dirty(m);
+ if (opte1 & PTE1_A)
+ vm_page_aflag_set(m, PGA_REFERENCED);
+ if (TAILQ_EMPTY(&m->md.pv_list) &&
+ TAILQ_EMPTY(&pvh->pv_list))
+ vm_page_aflag_clear(m, PGA_WRITEABLE);
+ }
+ }
+ if (pmap == kernel_pmap) {
+ /*
+ * L2 page table(s) can't be removed from kernel map as
+ * kernel counts on it (stuff around pmap_growkernel()).
+ */
+ pmap_remove_kernel_pte1(pmap, pte1p, sva);
+ } else {
+ /*
+ * Get associated L2 page table page.
+ * It's possible that the page was never allocated.
+ */
+ m = pmap_pt2_page(pmap, sva);
+ if (m != NULL)
+ pmap_unwire_pt2_all(pmap, sva, m, free);
+ }
+}
+
+/*
+ * Fills L2 page table page with mappings to consecutive physical pages.
+ */
+static __inline void
+pmap_fill_pt2(pt2_entry_t *fpte2p, pt2_entry_t npte2)
+{
+ pt2_entry_t *pte2p;
+
+ for (pte2p = fpte2p; pte2p < fpte2p + NPTE2_IN_PT2; pte2p++) {
+ pte2_store(pte2p, npte2);
+ npte2 += PTE2_SIZE;
+ }
+}
+
+/*
+ * Tries to demote a 1MB page mapping. If demotion fails, the
+ * 1MB page mapping is invalidated.
+ */
+static boolean_t
+pmap_demote_pte1(pmap_t pmap, pt1_entry_t *pte1p, vm_offset_t va)
+{
+ pt1_entry_t opte1, npte1;
+ pt2_entry_t *fpte2p, npte2;
+ vm_paddr_t pt2pg_pa, pt2_pa;
+ vm_page_t m;
+ struct spglist free;
+ uint32_t pte1_idx, isnew = 0;
+
+ PDEBUG(6, printf("%s(%p): try for va %#x pte1 %#x at %p\n", __func__,
+ pmap, va, pte1_load(pte1p), pte1p));
+
+ PMAP_LOCK_ASSERT(pmap, MA_OWNED);
+
+ opte1 = pte1_load(pte1p);
+ KASSERT(pte1_is_section(opte1), ("%s: opte1 not a section", __func__));
+
+ if ((opte1 & PTE1_A) == 0 || (m = pmap_pt2_page(pmap, va)) == NULL) {
+ KASSERT(!pte1_is_wired(opte1),
+ ("%s: PT2 page for a wired mapping is missing", __func__));
+
+ /*
+ * Invalidate the 1MB page mapping and return
+ * "failure" if the mapping was never accessed or the
+ * allocation of the new page table page fails.
+ */
+ if ((opte1 & PTE1_A) == 0 || (m = vm_page_alloc(NULL,
+ pte1_index(va) & ~PT2PG_MASK, VM_ALLOC_NOOBJ |
+ VM_ALLOC_NORMAL | VM_ALLOC_WIRED)) == NULL) {
+ SLIST_INIT(&free);
+ pmap_remove_pte1(pmap, pte1p, pte1_trunc(va), &free);
+ vm_page_free_pages_toq(&free, false);
+ CTR3(KTR_PMAP, "%s: failure for va %#x in pmap %p",
+ __func__, va, pmap);
+ return (FALSE);
+ }
+ if (va < VM_MAXUSER_ADDRESS)
+ pmap->pm_stats.resident_count++;
+
+ isnew = 1;
+
+ /*
+ * We init all L2 page tables in the page even if
+ * we are going to change everything for one L2 page
+ * table in a while.
+ */
+ pt2pg_pa = pmap_pt2pg_init(pmap, va, m);
+ } else {
+ if (va < VM_MAXUSER_ADDRESS) {
+ if (pt2_is_empty(m, va))
+ isnew = 1; /* Demoting section w/o promotion. */
+#ifdef INVARIANTS
+ else
+ KASSERT(pt2_is_full(m, va), ("%s: bad PT2 wire"
+ " count %u", __func__,
+ pt2_wirecount_get(m, pte1_index(va))));
+#endif
+ }
+ }
+
+ pt2pg_pa = VM_PAGE_TO_PHYS(m);
+ pte1_idx = pte1_index(va);
+ /*
+ * If the pmap is current, then the PT2MAP can provide access to
+ * the page table page (promoted L2 page tables are not unmapped).
+ * Otherwise, temporarily map the L2 page table page (m) into
+ * the kernel's address space at either PADDR1 or PADDR2.
+ *
+ * Note that L2 page table size is not equal to PAGE_SIZE.
+ */
+ if (pmap_is_current(pmap))
+ fpte2p = page_pt2(pt2map_pt2pg(va), pte1_idx);
+ else if (curthread->td_pinned > 0 && rw_wowned(&pvh_global_lock)) {
+ if (pte2_pa(pte2_load(PMAP1)) != pt2pg_pa) {
+ pte2_store(PMAP1, PTE2_KPT(pt2pg_pa));
+#ifdef SMP
+ PMAP1cpu = PCPU_GET(cpuid);
+#endif
+ tlb_flush_local((vm_offset_t)PADDR1);
+ PMAP1changed++;
+ } else
+#ifdef SMP
+ if (PMAP1cpu != PCPU_GET(cpuid)) {
+ PMAP1cpu = PCPU_GET(cpuid);
+ tlb_flush_local((vm_offset_t)PADDR1);
+ PMAP1changedcpu++;
+ } else
+#endif
+ PMAP1unchanged++;
+ fpte2p = page_pt2((vm_offset_t)PADDR1, pte1_idx);
+ } else {
+ mtx_lock(&PMAP2mutex);
+ if (pte2_pa(pte2_load(PMAP2)) != pt2pg_pa) {
+ pte2_store(PMAP2, PTE2_KPT(pt2pg_pa));
+ tlb_flush((vm_offset_t)PADDR2);
+ }
+ fpte2p = page_pt2((vm_offset_t)PADDR2, pte1_idx);
+ }
+ pt2_pa = page_pt2pa(pt2pg_pa, pte1_idx);
+ npte1 = PTE1_LINK(pt2_pa);
+
+ KASSERT((opte1 & PTE1_A) != 0,
+ ("%s: opte1 is missing PTE1_A", __func__));
+ KASSERT((opte1 & (PTE1_NM | PTE1_RO)) != PTE1_NM,
+ ("%s: opte1 has PTE1_NM", __func__));
+
+ /*
+ * Get pte2 from pte1 format.
+ */
+ npte2 = pte1_pa(opte1) | ATTR_TO_L2(opte1) | PTE2_V;
+
+ /*
+ * If the L2 page table page is new, initialize it. If the mapping
+ * has changed attributes, update the page table entries.
+ */
+ if (isnew != 0) {
+ pt2_wirecount_set(m, pte1_idx, NPTE2_IN_PT2);
+ pmap_fill_pt2(fpte2p, npte2);
+ } else if ((pte2_load(fpte2p) & PTE2_PROMOTE) !=
+ (npte2 & PTE2_PROMOTE))
+ pmap_fill_pt2(fpte2p, npte2);
+
+ KASSERT(pte2_pa(pte2_load(fpte2p)) == pte2_pa(npte2),
+ ("%s: fpte2p and npte2 map different physical addresses",
+ __func__));
+
+ if (fpte2p == PADDR2)
+ mtx_unlock(&PMAP2mutex);
+
+ /*
+ * Demote the mapping. This pmap is locked. The old PTE1 has
+ * PTE1_A set. If the old PTE1 has not PTE1_RO set, it also
+ * has not PTE1_NM set. Thus, there is no danger of a race with
+ * another processor changing the setting of PTE1_A and/or PTE1_NM
+ * between the read above and the store below.
+ */
+ pmap_change_pte1(pmap, pte1p, va, npte1);
+
+ /*
+ * Demote the pv entry. This depends on the earlier demotion
+ * of the mapping. Specifically, the (re)creation of a per-
+ * page pv entry might trigger the execution of pmap_pv_reclaim(),
+ * which might reclaim a newly (re)created per-page pv entry
+ * and destroy the associated mapping. In order to destroy
+ * the mapping, the PTE1 must have already changed from mapping
+ * the 1mpage to referencing the page table page.
+ */
+ if (pte1_is_managed(opte1))
+ pmap_pv_demote_pte1(pmap, va, pte1_pa(opte1));
+
+ pmap_pte1_demotions++;
+ CTR3(KTR_PMAP, "%s: success for va %#x in pmap %p",
+ __func__, va, pmap);
+
+ PDEBUG(6, printf("%s(%p): success for va %#x pte1 %#x(%#x) at %p\n",
+ __func__, pmap, va, npte1, pte1_load(pte1p), pte1p));
+ return (TRUE);
+}
+
+/*
+ * Insert the given physical page (p) at
+ * the specified virtual address (v) in the
+ * target physical map with the protection requested.
+ *
+ * If specified, the page will be wired down, meaning
+ * that the related pte can not be reclaimed.
+ *
+ * NB: This is the only routine which MAY NOT lazy-evaluate
+ * or lose information. That is, this routine must actually
+ * insert this page into the given map NOW.
+ */
+int
+pmap_enter(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot,
+ u_int flags, int8_t psind)
+{
+ pt1_entry_t *pte1p;
+ pt2_entry_t *pte2p;
+ pt2_entry_t npte2, opte2;
+ pv_entry_t pv;
+ vm_paddr_t opa, pa;
+ vm_page_t mpte2, om;
+ int rv;
+
+ va = trunc_page(va);
+ KASSERT(va <= vm_max_kernel_address, ("%s: toobig", __func__));
+ KASSERT(va < UPT2V_MIN_ADDRESS || va >= UPT2V_MAX_ADDRESS,
+ ("%s: invalid to pmap_enter page table pages (va: 0x%x)", __func__,
+ va));
+ KASSERT((m->oflags & VPO_UNMANAGED) != 0 || va < kmi.clean_sva ||
+ va >= kmi.clean_eva,
+ ("%s: managed mapping within the clean submap", __func__));
+ if ((m->oflags & VPO_UNMANAGED) == 0)
+ VM_PAGE_OBJECT_BUSY_ASSERT(m);
+ KASSERT((flags & PMAP_ENTER_RESERVED) == 0,
+ ("%s: flags %u has reserved bits set", __func__, flags));
+ pa = VM_PAGE_TO_PHYS(m);
+ npte2 = PTE2(pa, PTE2_A, vm_page_pte2_attr(m));
+ if ((flags & VM_PROT_WRITE) == 0)
+ npte2 |= PTE2_NM;
+ if ((prot & VM_PROT_WRITE) == 0)
+ npte2 |= PTE2_RO;
+ KASSERT((npte2 & (PTE2_NM | PTE2_RO)) != PTE2_RO,
+ ("%s: flags includes VM_PROT_WRITE but prot doesn't", __func__));
+ if ((prot & VM_PROT_EXECUTE) == 0)
+ npte2 |= PTE2_NX;
+ if ((flags & PMAP_ENTER_WIRED) != 0)
+ npte2 |= PTE2_W;
+ if (va < VM_MAXUSER_ADDRESS)
+ npte2 |= PTE2_U;
+ if (pmap != kernel_pmap)
+ npte2 |= PTE2_NG;
+
+ rw_wlock(&pvh_global_lock);
+ PMAP_LOCK(pmap);
+ sched_pin();
+ if (psind == 1) {
+ /* Assert the required virtual and physical alignment. */
+ KASSERT((va & PTE1_OFFSET) == 0,
+ ("%s: va unaligned", __func__));
+ KASSERT(m->psind > 0, ("%s: m->psind < psind", __func__));
+ rv = pmap_enter_pte1(pmap, va, PTE1_PA(pa) | ATTR_TO_L1(npte2) |
+ PTE1_V, flags, m);
+ goto out;
+ }
+
+ /*
+ * In the case that a page table page is not
+ * resident, we are creating it here.
+ */
+ if (va < VM_MAXUSER_ADDRESS) {
+ mpte2 = pmap_allocpte2(pmap, va, flags);
+ if (mpte2 == NULL) {
+ KASSERT((flags & PMAP_ENTER_NOSLEEP) != 0,
+ ("pmap_allocpte2 failed with sleep allowed"));
+ rv = KERN_RESOURCE_SHORTAGE;
+ goto out;
+ }
+ } else
+ mpte2 = NULL;
+ pte1p = pmap_pte1(pmap, va);
+ if (pte1_is_section(pte1_load(pte1p)))
+ panic("%s: attempted on 1MB page", __func__);
+ pte2p = pmap_pte2_quick(pmap, va);
+ if (pte2p == NULL)
+ panic("%s: invalid L1 page table entry va=%#x", __func__, va);
+
+ om = NULL;
+ opte2 = pte2_load(pte2p);
+ opa = pte2_pa(opte2);
+ /*
+ * Mapping has not changed, must be protection or wiring change.
+ */
+ if (pte2_is_valid(opte2) && (opa == pa)) {
+ /*
+ * Wiring change, just update stats. We don't worry about
+ * wiring PT2 pages as they remain resident as long as there
+ * are valid mappings in them. Hence, if a user page is wired,
+ * the PT2 page will be also.
+ */
+ if (pte2_is_wired(npte2) && !pte2_is_wired(opte2))
+ pmap->pm_stats.wired_count++;
+ else if (!pte2_is_wired(npte2) && pte2_is_wired(opte2))
+ pmap->pm_stats.wired_count--;
+
+ /*
+ * Remove extra pte2 reference
+ */
+ if (mpte2)
+ pt2_wirecount_dec(mpte2, pte1_index(va));
+ if ((m->oflags & VPO_UNMANAGED) == 0)
+ om = m;
+ goto validate;
+ }
+
+ /*
+ * QQQ: We think that changing physical address on writeable mapping
+ * is not safe. Well, maybe on kernel address space with correct
+ * locking, it can make a sense. However, we have no idea why
+ * anyone should do that on user address space. Are we wrong?
+ */
+ KASSERT((opa == 0) || (opa == pa) ||
+ !pte2_is_valid(opte2) || ((opte2 & PTE2_RO) != 0),
+ ("%s: pmap %p va %#x(%#x) opa %#x pa %#x - gotcha %#x %#x!",
+ __func__, pmap, va, opte2, opa, pa, flags, prot));
+
+ pv = NULL;
+
+ /*
+ * Mapping has changed, invalidate old range and fall through to
+ * handle validating new mapping.
+ */
+ if (opa) {
+ if (pte2_is_wired(opte2))
+ pmap->pm_stats.wired_count--;
+ om = PHYS_TO_VM_PAGE(opa);
+ if (om != NULL && (om->oflags & VPO_UNMANAGED) != 0)
+ om = NULL;
+ if (om != NULL)
+ pv = pmap_pvh_remove(&om->md, pmap, va);
+
+ /*
+ * Remove extra pte2 reference
+ */
+ if (mpte2 != NULL)
+ pt2_wirecount_dec(mpte2, va >> PTE1_SHIFT);
+ } else
+ pmap->pm_stats.resident_count++;
+
+ /*
+ * Enter on the PV list if part of our managed memory.
+ */
+ if ((m->oflags & VPO_UNMANAGED) == 0) {
+ if (pv == NULL) {
+ pv = get_pv_entry(pmap, FALSE);
+ pv->pv_va = va;
+ }
+ TAILQ_INSERT_TAIL(&m->md.pv_list, pv, pv_next);
+ } else if (pv != NULL)
+ free_pv_entry(pmap, pv);
+
+ /*
+ * Increment counters
+ */
+ if (pte2_is_wired(npte2))
+ pmap->pm_stats.wired_count++;
+
+validate:
+ /*
+ * Now validate mapping with desired protection/wiring.
+ */
+ if (prot & VM_PROT_WRITE) {
+ if ((m->oflags & VPO_UNMANAGED) == 0)
+ vm_page_aflag_set(m, PGA_WRITEABLE);
+ }
+
+ /*
+ * If the mapping or permission bits are different, we need
+ * to update the pte2.
+ *
+ * QQQ: Think again and again what to do
+ * if the mapping is going to be changed!
+ */
+ if ((opte2 & ~(PTE2_NM | PTE2_A)) != (npte2 & ~(PTE2_NM | PTE2_A))) {
+ /*
+ * Sync icache if exec permission and attribute VM_MEMATTR_WB_WA
+ * is set. Do it now, before the mapping is stored and made
+ * valid for hardware table walk. If done later, there is a race
+ * for other threads of current process in lazy loading case.
+ * Don't do it for kernel memory which is mapped with exec
+ * permission even if the memory isn't going to hold executable
+ * code. The only time when icache sync is needed is after
+ * kernel module is loaded and the relocation info is processed.
+ * And it's done in elf_cpu_load_file().
+ *
+ * QQQ: (1) Does it exist any better way where
+ * or how to sync icache?
+ * (2) Now, we do it on a page basis.
+ */
+ if ((prot & VM_PROT_EXECUTE) && pmap != kernel_pmap &&
+ m->md.pat_mode == VM_MEMATTR_WB_WA &&
+ (opa != pa || (opte2 & PTE2_NX)))
+ cache_icache_sync_fresh(va, pa, PAGE_SIZE);
+
+ if (opte2 & PTE2_V) {
+ /* Change mapping with break-before-make approach. */
+ opte2 = pte2_load_clear(pte2p);
+ pmap_tlb_flush(pmap, va);
+ pte2_store(pte2p, npte2);
+ if (om != NULL) {
+ KASSERT((om->oflags & VPO_UNMANAGED) == 0,
+ ("%s: om %p unmanaged", __func__, om));
+ if ((opte2 & PTE2_A) != 0)
+ vm_page_aflag_set(om, PGA_REFERENCED);
+ if (pte2_is_dirty(opte2))
+ vm_page_dirty(om);
+ if (TAILQ_EMPTY(&om->md.pv_list) &&
+ ((om->flags & PG_FICTITIOUS) != 0 ||
+ TAILQ_EMPTY(&pa_to_pvh(opa)->pv_list)))
+ vm_page_aflag_clear(om, PGA_WRITEABLE);
+ }
+ } else
+ pte2_store(pte2p, npte2);
+ }
+#if 0
+ else {
+ /*
+ * QQQ: In time when both access and not mofified bits are
+ * emulated by software, this should not happen. Some
+ * analysis is need, if this really happen. Missing
+ * tlb flush somewhere could be the reason.
+ */
+ panic("%s: pmap %p va %#x opte2 %x npte2 %x !!", __func__, pmap,
+ va, opte2, npte2);
+ }
+#endif
+
+#if VM_NRESERVLEVEL > 0
+ /*
+ * If both the L2 page table page and the reservation are fully
+ * populated, then attempt promotion.
+ */
+ if ((mpte2 == NULL || pt2_is_full(mpte2, va)) &&
+ sp_enabled && (m->flags & PG_FICTITIOUS) == 0 &&
+ vm_reserv_level_iffullpop(m) == 0)
+ pmap_promote_pte1(pmap, pte1p, va);
+#endif
+
+ rv = KERN_SUCCESS;
+out:
+ sched_unpin();
+ rw_wunlock(&pvh_global_lock);
+ PMAP_UNLOCK(pmap);
+ return (rv);
+}
+
+/*
+ * Do the things to unmap a page in a process.
+ */
+static int
+pmap_remove_pte2(pmap_t pmap, pt2_entry_t *pte2p, vm_offset_t va,
+ struct spglist *free)
+{
+ pt2_entry_t opte2;
+ vm_page_t m;
+
+ rw_assert(&pvh_global_lock, RA_WLOCKED);
+ PMAP_LOCK_ASSERT(pmap, MA_OWNED);
+
+ /* Clear and invalidate the mapping. */
+ opte2 = pte2_load_clear(pte2p);
+ pmap_tlb_flush(pmap, va);
+
+ KASSERT(pte2_is_valid(opte2), ("%s: pmap %p va %#x not link pte2 %#x",
+ __func__, pmap, va, opte2));
+
+ if (opte2 & PTE2_W)
+ pmap->pm_stats.wired_count -= 1;
+ pmap->pm_stats.resident_count -= 1;
+ if (pte2_is_managed(opte2)) {
+ m = PHYS_TO_VM_PAGE(pte2_pa(opte2));
+ if (pte2_is_dirty(opte2))
+ vm_page_dirty(m);
+ if (opte2 & PTE2_A)
+ vm_page_aflag_set(m, PGA_REFERENCED);
+ pmap_remove_entry(pmap, m, va);
+ }
+ return (pmap_unuse_pt2(pmap, va, free));
+}
+
+/*
+ * Remove a single page from a process address space.
+ */
+static void
+pmap_remove_page(pmap_t pmap, vm_offset_t va, struct spglist *free)
+{
+ pt2_entry_t *pte2p;
+
+ rw_assert(&pvh_global_lock, RA_WLOCKED);
+ KASSERT(curthread->td_pinned > 0,
+ ("%s: curthread not pinned", __func__));
+ PMAP_LOCK_ASSERT(pmap, MA_OWNED);
+ if ((pte2p = pmap_pte2_quick(pmap, va)) == NULL ||
+ !pte2_is_valid(pte2_load(pte2p)))
+ return;
+ pmap_remove_pte2(pmap, pte2p, va, free);
+}
+
+/*
+ * Remove the given range of addresses from the specified map.
+ *
+ * It is assumed that the start and end are properly
+ * rounded to the page size.
+ */
+void
+pmap_remove(pmap_t pmap, vm_offset_t sva, vm_offset_t eva)
+{
+ vm_offset_t nextva;
+ pt1_entry_t *pte1p, pte1;
+ pt2_entry_t *pte2p, pte2;
+ struct spglist free;
+
+ /*
+ * Perform an unsynchronized read. This is, however, safe.
+ */
+ if (pmap->pm_stats.resident_count == 0)
+ return;
+
+ SLIST_INIT(&free);
+
+ rw_wlock(&pvh_global_lock);
+ sched_pin();
+ PMAP_LOCK(pmap);
+
+ /*
+ * Special handling of removing one page. A very common
+ * operation and easy to short circuit some code.
+ */
+ if (sva + PAGE_SIZE == eva) {
+ pte1 = pte1_load(pmap_pte1(pmap, sva));
+ if (pte1_is_link(pte1)) {
+ pmap_remove_page(pmap, sva, &free);
+ goto out;
+ }
+ }
+
+ for (; sva < eva; sva = nextva) {
+ /*
+ * Calculate address for next L2 page table.
+ */
+ nextva = pte1_trunc(sva + PTE1_SIZE);
+ if (nextva < sva)
+ nextva = eva;
+ if (pmap->pm_stats.resident_count == 0)
+ break;
+
+ pte1p = pmap_pte1(pmap, sva);
+ pte1 = pte1_load(pte1p);
+
+ /*
+ * Weed out invalid mappings. Note: we assume that the L1 page
+ * table is always allocated, and in kernel virtual.
+ */
+ if (pte1 == 0)
+ continue;
+
+ if (pte1_is_section(pte1)) {
+ /*
+ * Are we removing the entire large page? If not,
+ * demote the mapping and fall through.
+ */
+ if (sva + PTE1_SIZE == nextva && eva >= nextva) {
+ pmap_remove_pte1(pmap, pte1p, sva, &free);
+ continue;
+ } else if (!pmap_demote_pte1(pmap, pte1p, sva)) {
+ /* The large page mapping was destroyed. */
+ continue;
+ }
+#ifdef INVARIANTS
+ else {
+ /* Update pte1 after demotion. */
+ pte1 = pte1_load(pte1p);
+ }
+#endif
+ }
+
+ KASSERT(pte1_is_link(pte1), ("%s: pmap %p va %#x pte1 %#x at %p"
+ " is not link", __func__, pmap, sva, pte1, pte1p));
+
+ /*
+ * Limit our scan to either the end of the va represented
+ * by the current L2 page table page, or to the end of the
+ * range being removed.
+ */
+ if (nextva > eva)
+ nextva = eva;
+
+ for (pte2p = pmap_pte2_quick(pmap, sva); sva != nextva;
+ pte2p++, sva += PAGE_SIZE) {
+ pte2 = pte2_load(pte2p);
+ if (!pte2_is_valid(pte2))
+ continue;
+ if (pmap_remove_pte2(pmap, pte2p, sva, &free))
+ break;
+ }
+ }
+out:
+ sched_unpin();
+ rw_wunlock(&pvh_global_lock);
+ PMAP_UNLOCK(pmap);
+ vm_page_free_pages_toq(&free, false);
+}
+
+/*
+ * Routine: pmap_remove_all
+ * Function:
+ * Removes this physical page from
+ * all physical maps in which it resides.
+ * Reflects back modify bits to the pager.
+ *
+ * Notes:
+ * Original versions of this routine were very
+ * inefficient because they iteratively called
+ * pmap_remove (slow...)
+ */
+
+void
+pmap_remove_all(vm_page_t m)
+{
+ struct md_page *pvh;
+ pv_entry_t pv;
+ pmap_t pmap;
+ pt2_entry_t *pte2p, opte2;
+ pt1_entry_t *pte1p;
+ vm_offset_t va;
+ struct spglist free;
+
+ KASSERT((m->oflags & VPO_UNMANAGED) == 0,
+ ("%s: page %p is not managed", __func__, m));
+ SLIST_INIT(&free);
+ rw_wlock(&pvh_global_lock);
+ sched_pin();
+ if ((m->flags & PG_FICTITIOUS) != 0)
+ goto small_mappings;
+ pvh = pa_to_pvh(VM_PAGE_TO_PHYS(m));
+ while ((pv = TAILQ_FIRST(&pvh->pv_list)) != NULL) {
+ va = pv->pv_va;
+ pmap = PV_PMAP(pv);
+ PMAP_LOCK(pmap);
+ pte1p = pmap_pte1(pmap, va);
+ (void)pmap_demote_pte1(pmap, pte1p, va);
+ PMAP_UNLOCK(pmap);
+ }
+small_mappings:
+ while ((pv = TAILQ_FIRST(&m->md.pv_list)) != NULL) {
+ pmap = PV_PMAP(pv);
+ PMAP_LOCK(pmap);
+ pmap->pm_stats.resident_count--;
+ pte1p = pmap_pte1(pmap, pv->pv_va);
+ KASSERT(!pte1_is_section(pte1_load(pte1p)), ("%s: found "
+ "a 1mpage in page %p's pv list", __func__, m));
+ pte2p = pmap_pte2_quick(pmap, pv->pv_va);
+ opte2 = pte2_load_clear(pte2p);
+ pmap_tlb_flush(pmap, pv->pv_va);
+ KASSERT(pte2_is_valid(opte2), ("%s: pmap %p va %x zero pte2",
+ __func__, pmap, pv->pv_va));
+ if (pte2_is_wired(opte2))
+ pmap->pm_stats.wired_count--;
+ if (opte2 & PTE2_A)
+ vm_page_aflag_set(m, PGA_REFERENCED);
+
+ /*
+ * Update the vm_page_t clean and reference bits.
+ */
+ if (pte2_is_dirty(opte2))
+ vm_page_dirty(m);
+ pmap_unuse_pt2(pmap, pv->pv_va, &free);
+ TAILQ_REMOVE(&m->md.pv_list, pv, pv_next);
+ free_pv_entry(pmap, pv);
+ PMAP_UNLOCK(pmap);
+ }
+ vm_page_aflag_clear(m, PGA_WRITEABLE);
+ sched_unpin();
+ rw_wunlock(&pvh_global_lock);
+ vm_page_free_pages_toq(&free, false);
+}
+
+/*
+ * Just subroutine for pmap_remove_pages() to reasonably satisfy
+ * good coding style, a.k.a. 80 character line width limit hell.
+ */
+static __inline void
+pmap_remove_pte1_quick(pmap_t pmap, pt1_entry_t pte1, pv_entry_t pv,
+ struct spglist *free)
+{
+ vm_paddr_t pa;
+ vm_page_t m, mt, mpt2pg;
+ struct md_page *pvh;
+
+ pa = pte1_pa(pte1);
+ m = PHYS_TO_VM_PAGE(pa);
+
+ KASSERT(m->phys_addr == pa, ("%s: vm_page_t %p addr mismatch %#x %#x",
+ __func__, m, m->phys_addr, pa));
+ KASSERT((m->flags & PG_FICTITIOUS) != 0 ||
+ m < &vm_page_array[vm_page_array_size],
+ ("%s: bad pte1 %#x", __func__, pte1));
+
+ if (pte1_is_dirty(pte1)) {
+ for (mt = m; mt < &m[PTE1_SIZE / PAGE_SIZE]; mt++)
+ vm_page_dirty(mt);
+ }
+
+ pmap->pm_stats.resident_count -= PTE1_SIZE / PAGE_SIZE;
+ pvh = pa_to_pvh(pa);
+ TAILQ_REMOVE(&pvh->pv_list, pv, pv_next);
+ if (TAILQ_EMPTY(&pvh->pv_list)) {
+ for (mt = m; mt < &m[PTE1_SIZE / PAGE_SIZE]; mt++)
+ if (TAILQ_EMPTY(&mt->md.pv_list))
+ vm_page_aflag_clear(mt, PGA_WRITEABLE);
+ }
+ mpt2pg = pmap_pt2_page(pmap, pv->pv_va);
+ if (mpt2pg != NULL)
+ pmap_unwire_pt2_all(pmap, pv->pv_va, mpt2pg, free);
+}
+
+/*
+ * Just subroutine for pmap_remove_pages() to reasonably satisfy
+ * good coding style, a.k.a. 80 character line width limit hell.
+ */
+static __inline void
+pmap_remove_pte2_quick(pmap_t pmap, pt2_entry_t pte2, pv_entry_t pv,
+ struct spglist *free)
+{
+ vm_paddr_t pa;
+ vm_page_t m;
+ struct md_page *pvh;
+
+ pa = pte2_pa(pte2);
+ m = PHYS_TO_VM_PAGE(pa);
+
+ KASSERT(m->phys_addr == pa, ("%s: vm_page_t %p addr mismatch %#x %#x",
+ __func__, m, m->phys_addr, pa));
+ KASSERT((m->flags & PG_FICTITIOUS) != 0 ||
+ m < &vm_page_array[vm_page_array_size],
+ ("%s: bad pte2 %#x", __func__, pte2));
+
+ if (pte2_is_dirty(pte2))
+ vm_page_dirty(m);
+
+ pmap->pm_stats.resident_count--;
+ TAILQ_REMOVE(&m->md.pv_list, pv, pv_next);
+ if (TAILQ_EMPTY(&m->md.pv_list) && (m->flags & PG_FICTITIOUS) == 0) {
+ pvh = pa_to_pvh(pa);
+ if (TAILQ_EMPTY(&pvh->pv_list))
+ vm_page_aflag_clear(m, PGA_WRITEABLE);
+ }
+ pmap_unuse_pt2(pmap, pv->pv_va, free);
+}
+
+/*
+ * Remove all pages from specified address space this aids process
+ * exit speeds. Also, this code is special cased for current process
+ * only, but can have the more generic (and slightly slower) mode enabled.
+ * This is much faster than pmap_remove in the case of running down
+ * an entire address space.
+ */
+void
+pmap_remove_pages(pmap_t pmap)
+{
+ pt1_entry_t *pte1p, pte1;
+ pt2_entry_t *pte2p, pte2;
+ pv_entry_t pv;
+ struct pv_chunk *pc, *npc;
+ struct spglist free;
+ int field, idx;
+ int32_t bit;
+ uint32_t inuse, bitmask;
+ boolean_t allfree;
+
+ /*
+ * Assert that the given pmap is only active on the current
+ * CPU. Unfortunately, we cannot block another CPU from
+ * activating the pmap while this function is executing.
+ */
+ KASSERT(pmap == vmspace_pmap(curthread->td_proc->p_vmspace),
+ ("%s: non-current pmap %p", __func__, pmap));
+#if defined(SMP) && defined(INVARIANTS)
+ {
+ cpuset_t other_cpus;
+
+ sched_pin();
+ other_cpus = pmap->pm_active;
+ CPU_CLR(PCPU_GET(cpuid), &other_cpus);
+ sched_unpin();
+ KASSERT(CPU_EMPTY(&other_cpus),
+ ("%s: pmap %p active on other cpus", __func__, pmap));
+ }
+#endif
+ SLIST_INIT(&free);
+ rw_wlock(&pvh_global_lock);
+ PMAP_LOCK(pmap);
+ sched_pin();
+ TAILQ_FOREACH_SAFE(pc, &pmap->pm_pvchunk, pc_list, npc) {
+ KASSERT(pc->pc_pmap == pmap, ("%s: wrong pmap %p %p",
+ __func__, pmap, pc->pc_pmap));
+ allfree = TRUE;
+ for (field = 0; field < _NPCM; field++) {
+ inuse = (~(pc->pc_map[field])) & pc_freemask[field];
+ while (inuse != 0) {
+ bit = ffs(inuse) - 1;
+ bitmask = 1UL << bit;
+ idx = field * 32 + bit;
+ pv = &pc->pc_pventry[idx];
+ inuse &= ~bitmask;
+
+ /*
+ * Note that we cannot remove wired pages
+ * from a process' mapping at this time
+ */
+ pte1p = pmap_pte1(pmap, pv->pv_va);
+ pte1 = pte1_load(pte1p);
+ if (pte1_is_section(pte1)) {
+ if (pte1_is_wired(pte1)) {
+ allfree = FALSE;
+ continue;
+ }
+ pte1_clear(pte1p);
+ pmap_remove_pte1_quick(pmap, pte1, pv,
+ &free);
+ }
+ else if (pte1_is_link(pte1)) {
+ pte2p = pt2map_entry(pv->pv_va);
+ pte2 = pte2_load(pte2p);
+
+ if (!pte2_is_valid(pte2)) {
+ printf("%s: pmap %p va %#x "
+ "pte2 %#x\n", __func__,
+ pmap, pv->pv_va, pte2);
+ panic("bad pte2");
+ }
+
+ if (pte2_is_wired(pte2)) {
+ allfree = FALSE;
+ continue;
+ }
+ pte2_clear(pte2p);
+ pmap_remove_pte2_quick(pmap, pte2, pv,
+ &free);
+ } else {
+ printf("%s: pmap %p va %#x pte1 %#x\n",
+ __func__, pmap, pv->pv_va, pte1);
+ panic("bad pte1");
+ }
+
+ /* Mark free */
+ PV_STAT(pv_entry_frees++);
+ PV_STAT(pv_entry_spare++);
+ pv_entry_count--;
+ pc->pc_map[field] |= bitmask;
+ }
+ }
+ if (allfree) {
+ TAILQ_REMOVE(&pmap->pm_pvchunk, pc, pc_list);
+ free_pv_chunk(pc);
+ }
+ }
+ tlb_flush_all_ng_local();
+ sched_unpin();
+ rw_wunlock(&pvh_global_lock);
+ PMAP_UNLOCK(pmap);
+ vm_page_free_pages_toq(&free, false);
+}
+
+/*
+ * This code makes some *MAJOR* assumptions:
+ * 1. Current pmap & pmap exists.
+ * 2. Not wired.
+ * 3. Read access.
+ * 4. No L2 page table pages.
+ * but is *MUCH* faster than pmap_enter...
+ */
+static vm_page_t
+pmap_enter_quick_locked(pmap_t pmap, vm_offset_t va, vm_page_t m,
+ vm_prot_t prot, vm_page_t mpt2pg)
+{
+ pt2_entry_t *pte2p, pte2;
+ vm_paddr_t pa;
+ struct spglist free;
+ uint32_t l2prot;
+
+ KASSERT(va < kmi.clean_sva || va >= kmi.clean_eva ||
+ (m->oflags & VPO_UNMANAGED) != 0,
+ ("%s: managed mapping within the clean submap", __func__));
+ rw_assert(&pvh_global_lock, RA_WLOCKED);
+ PMAP_LOCK_ASSERT(pmap, MA_OWNED);
+
+ /*
+ * In the case that a L2 page table page is not
+ * resident, we are creating it here.
+ */
+ if (va < VM_MAXUSER_ADDRESS) {
+ u_int pte1_idx;
+ pt1_entry_t pte1, *pte1p;
+ vm_paddr_t pt2_pa;
+
+ /*
+ * Get L1 page table things.
+ */
+ pte1_idx = pte1_index(va);
+ pte1p = pmap_pte1(pmap, va);
+ pte1 = pte1_load(pte1p);
+
+ if (mpt2pg && (mpt2pg->pindex == (pte1_idx & ~PT2PG_MASK))) {
+ /*
+ * Each of NPT2_IN_PG L2 page tables on the page can
+ * come here. Make sure that associated L1 page table
+ * link is established.
+ *
+ * QQQ: It comes that we don't establish all links to
+ * L2 page tables for newly allocated L2 page
+ * tables page.
+ */
+ KASSERT(!pte1_is_section(pte1),
+ ("%s: pte1 %#x is section", __func__, pte1));
+ if (!pte1_is_link(pte1)) {
+ pt2_pa = page_pt2pa(VM_PAGE_TO_PHYS(mpt2pg),
+ pte1_idx);
+ pte1_store(pte1p, PTE1_LINK(pt2_pa));
+ }
+ pt2_wirecount_inc(mpt2pg, pte1_idx);
+ } else {
+ /*
+ * If the L2 page table page is mapped, we just
+ * increment the hold count, and activate it.
+ */
+ if (pte1_is_section(pte1)) {
+ return (NULL);
+ } else if (pte1_is_link(pte1)) {
+ mpt2pg = PHYS_TO_VM_PAGE(pte1_link_pa(pte1));
+ pt2_wirecount_inc(mpt2pg, pte1_idx);
+ } else {
+ mpt2pg = _pmap_allocpte2(pmap, va,
+ PMAP_ENTER_NOSLEEP);
+ if (mpt2pg == NULL)
+ return (NULL);
+ }
+ }
+ } else {
+ mpt2pg = NULL;
+ }
+
+ /*
+ * This call to pt2map_entry() makes the assumption that we are
+ * entering the page into the current pmap. In order to support
+ * quick entry into any pmap, one would likely use pmap_pte2_quick().
+ * But that isn't as quick as pt2map_entry().
+ */
+ pte2p = pt2map_entry(va);
+ pte2 = pte2_load(pte2p);
+ if (pte2_is_valid(pte2)) {
+ if (mpt2pg != NULL) {
+ /*
+ * Remove extra pte2 reference
+ */
+ pt2_wirecount_dec(mpt2pg, pte1_index(va));
+ mpt2pg = NULL;
+ }
+ return (NULL);
+ }
+
+ /*
+ * Enter on the PV list if part of our managed memory.
+ */
+ if ((m->oflags & VPO_UNMANAGED) == 0 &&
+ !pmap_try_insert_pv_entry(pmap, va, m)) {
+ if (mpt2pg != NULL) {
+ SLIST_INIT(&free);
+ if (pmap_unwire_pt2(pmap, va, mpt2pg, &free)) {
+ pmap_tlb_flush(pmap, va);
+ vm_page_free_pages_toq(&free, false);
+ }
+
+ mpt2pg = NULL;
+ }
+ return (NULL);
+ }
+
+ /*
+ * Increment counters
+ */
+ pmap->pm_stats.resident_count++;
+
+ /*
+ * Now validate mapping with RO protection
+ */
+ pa = VM_PAGE_TO_PHYS(m);
+ l2prot = PTE2_RO | PTE2_NM;
+ if (va < VM_MAXUSER_ADDRESS)
+ l2prot |= PTE2_U | PTE2_NG;
+ if ((prot & VM_PROT_EXECUTE) == 0)
+ l2prot |= PTE2_NX;
+ else if (m->md.pat_mode == VM_MEMATTR_WB_WA && pmap != kernel_pmap) {
+ /*
+ * Sync icache if exec permission and attribute VM_MEMATTR_WB_WA
+ * is set. QQQ: For more info, see comments in pmap_enter().
+ */
+ cache_icache_sync_fresh(va, pa, PAGE_SIZE);
+ }
+ pte2_store(pte2p, PTE2(pa, l2prot, vm_page_pte2_attr(m)));
+
+ return (mpt2pg);
+}
+
+void
+pmap_enter_quick(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot)
+{
+
+ rw_wlock(&pvh_global_lock);
+ PMAP_LOCK(pmap);
+ (void)pmap_enter_quick_locked(pmap, va, m, prot, NULL);
+ rw_wunlock(&pvh_global_lock);
+ PMAP_UNLOCK(pmap);
+}
+
+/*
+ * Tries to create a read- and/or execute-only 1 MB page mapping. Returns
+ * true if successful. Returns false if (1) a mapping already exists at the
+ * specified virtual address or (2) a PV entry cannot be allocated without
+ * reclaiming another PV entry.
+ */
+static bool
+pmap_enter_1mpage(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot)
+{
+ pt1_entry_t pte1;
+ vm_paddr_t pa;
+
+ PMAP_LOCK_ASSERT(pmap, MA_OWNED);
+ pa = VM_PAGE_TO_PHYS(m);
+ pte1 = PTE1(pa, PTE1_NM | PTE1_RO, ATTR_TO_L1(vm_page_pte2_attr(m)));
+ if ((prot & VM_PROT_EXECUTE) == 0)
+ pte1 |= PTE1_NX;
+ if (va < VM_MAXUSER_ADDRESS)
+ pte1 |= PTE1_U;
+ if (pmap != kernel_pmap)
+ pte1 |= PTE1_NG;
+ return (pmap_enter_pte1(pmap, va, pte1, PMAP_ENTER_NOSLEEP |
+ PMAP_ENTER_NOREPLACE | PMAP_ENTER_NORECLAIM, m) == KERN_SUCCESS);
+}
+
+/*
+ * Tries to create the specified 1 MB page mapping. Returns KERN_SUCCESS if
+ * the mapping was created, and either KERN_FAILURE or KERN_RESOURCE_SHORTAGE
+ * otherwise. Returns KERN_FAILURE if PMAP_ENTER_NOREPLACE was specified and
+ * a mapping already exists at the specified virtual address. Returns
+ * KERN_RESOURCE_SHORTAGE if PMAP_ENTER_NORECLAIM was specified and PV entry
+ * allocation failed.
+ */
+static int
+pmap_enter_pte1(pmap_t pmap, vm_offset_t va, pt1_entry_t pte1, u_int flags,
+ vm_page_t m)
+{
+ struct spglist free;
+ pt1_entry_t opte1, *pte1p;
+ pt2_entry_t pte2, *pte2p;
+ vm_offset_t cur, end;
+ vm_page_t mt;
+
+ rw_assert(&pvh_global_lock, RA_WLOCKED);
+ KASSERT((pte1 & (PTE1_NM | PTE1_RO)) == 0 ||
+ (pte1 & (PTE1_NM | PTE1_RO)) == (PTE1_NM | PTE1_RO),
+ ("%s: pte1 has inconsistent NM and RO attributes", __func__));
+ PMAP_LOCK_ASSERT(pmap, MA_OWNED);
+ pte1p = pmap_pte1(pmap, va);
+ opte1 = pte1_load(pte1p);
+ if (pte1_is_valid(opte1)) {
+ if ((flags & PMAP_ENTER_NOREPLACE) != 0) {
+ CTR3(KTR_PMAP, "%s: failure for va %#lx in pmap %p",
+ __func__, va, pmap);
+ return (KERN_FAILURE);
+ }
+ /* Break the existing mapping(s). */
+ SLIST_INIT(&free);
+ if (pte1_is_section(opte1)) {
+ /*
+ * If the section resulted from a promotion, then a
+ * reserved PT page could be freed.
+ */
+ pmap_remove_pte1(pmap, pte1p, va, &free);
+ } else {
+ sched_pin();
+ end = va + PTE1_SIZE;
+ for (cur = va, pte2p = pmap_pte2_quick(pmap, va);
+ cur != end; cur += PAGE_SIZE, pte2p++) {
+ pte2 = pte2_load(pte2p);
+ if (!pte2_is_valid(pte2))
+ continue;
+ if (pmap_remove_pte2(pmap, pte2p, cur, &free))
+ break;
+ }
+ sched_unpin();
+ }
+ vm_page_free_pages_toq(&free, false);
+ }
+ if ((m->oflags & VPO_UNMANAGED) == 0) {
+ /*
+ * Abort this mapping if its PV entry could not be created.
+ */
+ if (!pmap_pv_insert_pte1(pmap, va, pte1, flags)) {
+ CTR3(KTR_PMAP, "%s: failure for va %#lx in pmap %p",
+ __func__, va, pmap);
+ return (KERN_RESOURCE_SHORTAGE);
+ }
+ if ((pte1 & PTE1_RO) == 0) {
+ for (mt = m; mt < &m[PTE1_SIZE / PAGE_SIZE]; mt++)
+ vm_page_aflag_set(mt, PGA_WRITEABLE);
+ }
+ }
+
+ /*
+ * Increment counters.
+ */
+ if (pte1_is_wired(pte1))
+ pmap->pm_stats.wired_count += PTE1_SIZE / PAGE_SIZE;
+ pmap->pm_stats.resident_count += PTE1_SIZE / PAGE_SIZE;
+
+ /*
+ * Sync icache if exec permission and attribute VM_MEMATTR_WB_WA
+ * is set. QQQ: For more info, see comments in pmap_enter().
+ */
+ if ((pte1 & PTE1_NX) == 0 && m->md.pat_mode == VM_MEMATTR_WB_WA &&
+ pmap != kernel_pmap && (!pte1_is_section(opte1) ||
+ pte1_pa(opte1) != VM_PAGE_TO_PHYS(m) || (opte1 & PTE2_NX) != 0))
+ cache_icache_sync_fresh(va, VM_PAGE_TO_PHYS(m), PTE1_SIZE);
+
+ /*
+ * Map the section.
+ */
+ pte1_store(pte1p, pte1);
+
+ pmap_pte1_mappings++;
+ CTR3(KTR_PMAP, "%s: success for va %#lx in pmap %p", __func__, va,
+ pmap);
+ return (KERN_SUCCESS);
+}
+
+/*
+ * Maps a sequence of resident pages belonging to the same object.
+ * The sequence begins with the given page m_start. This page is
+ * mapped at the given virtual address start. Each subsequent page is
+ * mapped at a virtual address that is offset from start by the same
+ * amount as the page is offset from m_start within the object. The
+ * last page in the sequence is the page with the largest offset from
+ * m_start that can be mapped at a virtual address less than the given
+ * virtual address end. Not every virtual page between start and end
+ * is mapped; only those for which a resident page exists with the
+ * corresponding offset from m_start are mapped.
+ */
+void
+pmap_enter_object(pmap_t pmap, vm_offset_t start, vm_offset_t end,
+ vm_page_t m_start, vm_prot_t prot)
+{
+ vm_offset_t va;
+ vm_page_t m, mpt2pg;
+ vm_pindex_t diff, psize;
+
+ PDEBUG(6, printf("%s: pmap %p start %#x end %#x m %p prot %#x\n",
+ __func__, pmap, start, end, m_start, prot));
+
+ VM_OBJECT_ASSERT_LOCKED(m_start->object);
+ psize = atop(end - start);
+ mpt2pg = NULL;
+ m = m_start;
+ rw_wlock(&pvh_global_lock);
+ PMAP_LOCK(pmap);
+ while (m != NULL && (diff = m->pindex - m_start->pindex) < psize) {
+ va = start + ptoa(diff);
+ if ((va & PTE1_OFFSET) == 0 && va + PTE1_SIZE <= end &&
+ m->psind == 1 && sp_enabled &&
+ pmap_enter_1mpage(pmap, va, m, prot))
+ m = &m[PTE1_SIZE / PAGE_SIZE - 1];
+ else
+ mpt2pg = pmap_enter_quick_locked(pmap, va, m, prot,
+ mpt2pg);
+ m = TAILQ_NEXT(m, listq);
+ }
+ rw_wunlock(&pvh_global_lock);
+ PMAP_UNLOCK(pmap);
+}
+
+/*
+ * This code maps large physical mmap regions into the
+ * processor address space. Note that some shortcuts
+ * are taken, but the code works.
+ */
+void
+pmap_object_init_pt(pmap_t pmap, vm_offset_t addr, vm_object_t object,
+ vm_pindex_t pindex, vm_size_t size)
+{
+ pt1_entry_t *pte1p;
+ vm_paddr_t pa, pte2_pa;
+ vm_page_t p;
+ vm_memattr_t pat_mode;
+ u_int l1attr, l1prot;
+
+ VM_OBJECT_ASSERT_WLOCKED(object);
+ KASSERT(object->type == OBJT_DEVICE || object->type == OBJT_SG,
+ ("%s: non-device object", __func__));
+ if ((addr & PTE1_OFFSET) == 0 && (size & PTE1_OFFSET) == 0) {
+ if (!vm_object_populate(object, pindex, pindex + atop(size)))
+ return;
+ p = vm_page_lookup(object, pindex);
+ KASSERT(p->valid == VM_PAGE_BITS_ALL,
+ ("%s: invalid page %p", __func__, p));
+ pat_mode = p->md.pat_mode;
+
+ /*
+ * Abort the mapping if the first page is not physically
+ * aligned to a 1MB page boundary.
+ */
+ pte2_pa = VM_PAGE_TO_PHYS(p);
+ if (pte2_pa & PTE1_OFFSET)
+ return;
+
+ /*
+ * Skip the first page. Abort the mapping if the rest of
+ * the pages are not physically contiguous or have differing
+ * memory attributes.
+ */
+ p = TAILQ_NEXT(p, listq);
+ for (pa = pte2_pa + PAGE_SIZE; pa < pte2_pa + size;
+ pa += PAGE_SIZE) {
+ KASSERT(p->valid == VM_PAGE_BITS_ALL,
+ ("%s: invalid page %p", __func__, p));
+ if (pa != VM_PAGE_TO_PHYS(p) ||
+ pat_mode != p->md.pat_mode)
+ return;
+ p = TAILQ_NEXT(p, listq);
+ }
+
+ /*
+ * Map using 1MB pages.
+ *
+ * QQQ: Well, we are mapping a section, so same condition must
+ * be hold like during promotion. It looks that only RW mapping
+ * is done here, so readonly mapping must be done elsewhere.
+ */
+ l1prot = PTE1_U | PTE1_NG | PTE1_RW | PTE1_M | PTE1_A;
+ l1attr = ATTR_TO_L1(vm_memattr_to_pte2(pat_mode));
+ PMAP_LOCK(pmap);
+ for (pa = pte2_pa; pa < pte2_pa + size; pa += PTE1_SIZE) {
+ pte1p = pmap_pte1(pmap, addr);
+ if (!pte1_is_valid(pte1_load(pte1p))) {
+ pte1_store(pte1p, PTE1(pa, l1prot, l1attr));
+ pmap->pm_stats.resident_count += PTE1_SIZE /
+ PAGE_SIZE;
+ pmap_pte1_mappings++;
+ }
+ /* Else continue on if the PTE1 is already valid. */
+ addr += PTE1_SIZE;
+ }
+ PMAP_UNLOCK(pmap);
+ }
+}
+
+/*
+ * Do the things to protect a 1mpage in a process.
+ */
+static void
+pmap_protect_pte1(pmap_t pmap, pt1_entry_t *pte1p, vm_offset_t sva,
+ vm_prot_t prot)
+{
+ pt1_entry_t npte1, opte1;
+ vm_offset_t eva, va;
+ vm_page_t m;
+
+ PMAP_LOCK_ASSERT(pmap, MA_OWNED);
+ KASSERT((sva & PTE1_OFFSET) == 0,
+ ("%s: sva is not 1mpage aligned", __func__));
+
+ opte1 = npte1 = pte1_load(pte1p);
+ if (pte1_is_managed(opte1) && pte1_is_dirty(opte1)) {
+ eva = sva + PTE1_SIZE;
+ for (va = sva, m = PHYS_TO_VM_PAGE(pte1_pa(opte1));
+ va < eva; va += PAGE_SIZE, m++)
+ vm_page_dirty(m);
+ }
+ if ((prot & VM_PROT_WRITE) == 0)
+ npte1 |= PTE1_RO | PTE1_NM;
+ if ((prot & VM_PROT_EXECUTE) == 0)
+ npte1 |= PTE1_NX;
+
+ /*
+ * QQQ: Herein, execute permission is never set.
+ * It only can be cleared. So, no icache
+ * syncing is needed.
+ */
+
+ if (npte1 != opte1) {
+ pte1_store(pte1p, npte1);
+ pmap_tlb_flush(pmap, sva);
+ }
+}
+
+/*
+ * Set the physical protection on the
+ * specified range of this map as requested.
+ */
+void
+pmap_protect(pmap_t pmap, vm_offset_t sva, vm_offset_t eva, vm_prot_t prot)
+{
+ boolean_t pv_lists_locked;
+ vm_offset_t nextva;
+ pt1_entry_t *pte1p, pte1;
+ pt2_entry_t *pte2p, opte2, npte2;
+
+ KASSERT((prot & ~VM_PROT_ALL) == 0, ("invalid prot %x", prot));
+ if (prot == VM_PROT_NONE) {
+ pmap_remove(pmap, sva, eva);
+ return;
+ }
+
+ if ((prot & (VM_PROT_WRITE | VM_PROT_EXECUTE)) ==
+ (VM_PROT_WRITE | VM_PROT_EXECUTE))
+ return;
+
+ if (pmap_is_current(pmap))
+ pv_lists_locked = FALSE;
+ else {
+ pv_lists_locked = TRUE;
+resume:
+ rw_wlock(&pvh_global_lock);
+ sched_pin();
+ }
+
+ PMAP_LOCK(pmap);
+ for (; sva < eva; sva = nextva) {
+ /*
+ * Calculate address for next L2 page table.
+ */
+ nextva = pte1_trunc(sva + PTE1_SIZE);
+ if (nextva < sva)
+ nextva = eva;
+
+ pte1p = pmap_pte1(pmap, sva);
+ pte1 = pte1_load(pte1p);
+
+ /*
+ * Weed out invalid mappings. Note: we assume that L1 page
+ * page table is always allocated, and in kernel virtual.
+ */
+ if (pte1 == 0)
+ continue;
+
+ if (pte1_is_section(pte1)) {
+ /*
+ * Are we protecting the entire large page? If not,
+ * demote the mapping and fall through.
+ */
+ if (sva + PTE1_SIZE == nextva && eva >= nextva) {
+ pmap_protect_pte1(pmap, pte1p, sva, prot);
+ continue;
+ } else {
+ if (!pv_lists_locked) {
+ pv_lists_locked = TRUE;
+ if (!rw_try_wlock(&pvh_global_lock)) {
+ PMAP_UNLOCK(pmap);
+ goto resume;
+ }
+ sched_pin();
+ }
+ if (!pmap_demote_pte1(pmap, pte1p, sva)) {
+ /*
+ * The large page mapping
+ * was destroyed.
+ */
+ continue;
+ }
+#ifdef INVARIANTS
+ else {
+ /* Update pte1 after demotion */
+ pte1 = pte1_load(pte1p);
+ }
+#endif
+ }
+ }
+
+ KASSERT(pte1_is_link(pte1), ("%s: pmap %p va %#x pte1 %#x at %p"
+ " is not link", __func__, pmap, sva, pte1, pte1p));
+
+ /*
+ * Limit our scan to either the end of the va represented
+ * by the current L2 page table page, or to the end of the
+ * range being protected.
+ */
+ if (nextva > eva)
+ nextva = eva;
+
+ for (pte2p = pmap_pte2_quick(pmap, sva); sva != nextva; pte2p++,
+ sva += PAGE_SIZE) {
+ vm_page_t m;
+
+ opte2 = npte2 = pte2_load(pte2p);
+ if (!pte2_is_valid(opte2))
+ continue;
+
+ if ((prot & VM_PROT_WRITE) == 0) {
+ if (pte2_is_managed(opte2) &&
+ pte2_is_dirty(opte2)) {
+ m = PHYS_TO_VM_PAGE(pte2_pa(opte2));
+ vm_page_dirty(m);
+ }
+ npte2 |= PTE2_RO | PTE2_NM;
+ }
+
+ if ((prot & VM_PROT_EXECUTE) == 0)
+ npte2 |= PTE2_NX;
+
+ /*
+ * QQQ: Herein, execute permission is never set.
+ * It only can be cleared. So, no icache
+ * syncing is needed.
+ */
+
+ if (npte2 != opte2) {
+ pte2_store(pte2p, npte2);
+ pmap_tlb_flush(pmap, sva);
+ }
+ }
+ }
+ if (pv_lists_locked) {
+ sched_unpin();
+ rw_wunlock(&pvh_global_lock);
+ }
+ PMAP_UNLOCK(pmap);
+}
+
+/*
+ * pmap_pvh_wired_mappings:
+ *
+ * Return the updated number "count" of managed mappings that are wired.
+ */
+static int
+pmap_pvh_wired_mappings(struct md_page *pvh, int count)
+{
+ pmap_t pmap;
+ pt1_entry_t pte1;
+ pt2_entry_t pte2;
+ pv_entry_t pv;
+
+ rw_assert(&pvh_global_lock, RA_WLOCKED);
+ sched_pin();
+ TAILQ_FOREACH(pv, &pvh->pv_list, pv_next) {
+ pmap = PV_PMAP(pv);
+ PMAP_LOCK(pmap);
+ pte1 = pte1_load(pmap_pte1(pmap, pv->pv_va));
+ if (pte1_is_section(pte1)) {
+ if (pte1_is_wired(pte1))
+ count++;
+ } else {
+ KASSERT(pte1_is_link(pte1),
+ ("%s: pte1 %#x is not link", __func__, pte1));
+ pte2 = pte2_load(pmap_pte2_quick(pmap, pv->pv_va));
+ if (pte2_is_wired(pte2))
+ count++;
+ }
+ PMAP_UNLOCK(pmap);
+ }
+ sched_unpin();
+ return (count);
+}
+
+/*
+ * pmap_page_wired_mappings:
+ *
+ * Return the number of managed mappings to the given physical page
+ * that are wired.
+ */
+int
+pmap_page_wired_mappings(vm_page_t m)
+{
+ int count;
+
+ count = 0;
+ if ((m->oflags & VPO_UNMANAGED) != 0)
+ return (count);
+ rw_wlock(&pvh_global_lock);
+ count = pmap_pvh_wired_mappings(&m->md, count);
+ if ((m->flags & PG_FICTITIOUS) == 0) {
+ count = pmap_pvh_wired_mappings(pa_to_pvh(VM_PAGE_TO_PHYS(m)),
+ count);
+ }
+ rw_wunlock(&pvh_global_lock);
+ return (count);
+}
+
+/*
+ * Returns TRUE if any of the given mappings were used to modify
+ * physical memory. Otherwise, returns FALSE. Both page and 1mpage
+ * mappings are supported.
+ */
+static boolean_t
+pmap_is_modified_pvh(struct md_page *pvh)
+{
+ pv_entry_t pv;
+ pt1_entry_t pte1;
+ pt2_entry_t pte2;
+ pmap_t pmap;
+ boolean_t rv;
+
+ rw_assert(&pvh_global_lock, RA_WLOCKED);
+ rv = FALSE;
+ sched_pin();
+ TAILQ_FOREACH(pv, &pvh->pv_list, pv_next) {
+ pmap = PV_PMAP(pv);
+ PMAP_LOCK(pmap);
+ pte1 = pte1_load(pmap_pte1(pmap, pv->pv_va));
+ if (pte1_is_section(pte1)) {
+ rv = pte1_is_dirty(pte1);
+ } else {
+ KASSERT(pte1_is_link(pte1),
+ ("%s: pte1 %#x is not link", __func__, pte1));
+ pte2 = pte2_load(pmap_pte2_quick(pmap, pv->pv_va));
+ rv = pte2_is_dirty(pte2);
+ }
+ PMAP_UNLOCK(pmap);
+ if (rv)
+ break;
+ }
+ sched_unpin();
+ return (rv);
+}
+
+/*
+ * pmap_is_modified:
+ *
+ * Return whether or not the specified physical page was modified
+ * in any physical maps.
+ */
+boolean_t
+pmap_is_modified(vm_page_t m)
+{
+ boolean_t rv;
+
+ KASSERT((m->oflags & VPO_UNMANAGED) == 0,
+ ("%s: page %p is not managed", __func__, m));
+
+ /*
+ * If the page is not busied then this check is racy.
+ */
+ if (!pmap_page_is_write_mapped(m))
+ return (FALSE);
+ rw_wlock(&pvh_global_lock);
+ rv = pmap_is_modified_pvh(&m->md) ||
+ ((m->flags & PG_FICTITIOUS) == 0 &&
+ pmap_is_modified_pvh(pa_to_pvh(VM_PAGE_TO_PHYS(m))));
+ rw_wunlock(&pvh_global_lock);
+ return (rv);
+}
+
+/*
+ * pmap_is_prefaultable:
+ *
+ * Return whether or not the specified virtual address is eligible
+ * for prefault.
+ */
+boolean_t
+pmap_is_prefaultable(pmap_t pmap, vm_offset_t addr)
+{
+ pt1_entry_t pte1;
+ pt2_entry_t pte2;
+ boolean_t rv;
+
+ rv = FALSE;
+ PMAP_LOCK(pmap);
+ pte1 = pte1_load(pmap_pte1(pmap, addr));
+ if (pte1_is_link(pte1)) {
+ pte2 = pte2_load(pt2map_entry(addr));
+ rv = !pte2_is_valid(pte2) ;
+ }
+ PMAP_UNLOCK(pmap);
+ return (rv);
+}
+
+/*
+ * Returns TRUE if any of the given mappings were referenced and FALSE
+ * otherwise. Both page and 1mpage mappings are supported.
+ */
+static boolean_t
+pmap_is_referenced_pvh(struct md_page *pvh)
+{
+
+ pv_entry_t pv;
+ pt1_entry_t pte1;
+ pt2_entry_t pte2;
+ pmap_t pmap;
+ boolean_t rv;
+
+ rw_assert(&pvh_global_lock, RA_WLOCKED);
+ rv = FALSE;
+ sched_pin();
+ TAILQ_FOREACH(pv, &pvh->pv_list, pv_next) {
+ pmap = PV_PMAP(pv);
+ PMAP_LOCK(pmap);
+ pte1 = pte1_load(pmap_pte1(pmap, pv->pv_va));
+ if (pte1_is_section(pte1)) {
+ rv = (pte1 & (PTE1_A | PTE1_V)) == (PTE1_A | PTE1_V);
+ } else {
+ pte2 = pte2_load(pmap_pte2_quick(pmap, pv->pv_va));
+ rv = (pte2 & (PTE2_A | PTE2_V)) == (PTE2_A | PTE2_V);
+ }
+ PMAP_UNLOCK(pmap);
+ if (rv)
+ break;
+ }
+ sched_unpin();
+ return (rv);
+}
+
+/*
+ * pmap_is_referenced:
+ *
+ * Return whether or not the specified physical page was referenced
+ * in any physical maps.
+ */
+boolean_t
+pmap_is_referenced(vm_page_t m)
+{
+ boolean_t rv;
+
+ KASSERT((m->oflags & VPO_UNMANAGED) == 0,
+ ("%s: page %p is not managed", __func__, m));
+ rw_wlock(&pvh_global_lock);
+ rv = pmap_is_referenced_pvh(&m->md) ||
+ ((m->flags & PG_FICTITIOUS) == 0 &&
+ pmap_is_referenced_pvh(pa_to_pvh(VM_PAGE_TO_PHYS(m))));
+ rw_wunlock(&pvh_global_lock);
+ return (rv);
+}
+
+/*
+ * pmap_ts_referenced:
+ *
+ * Return a count of reference bits for a page, clearing those bits.
+ * It is not necessary for every reference bit to be cleared, but it
+ * is necessary that 0 only be returned when there are truly no
+ * reference bits set.
+ *
+ * As an optimization, update the page's dirty field if a modified bit is
+ * found while counting reference bits. This opportunistic update can be
+ * performed at low cost and can eliminate the need for some future calls
+ * to pmap_is_modified(). However, since this function stops after
+ * finding PMAP_TS_REFERENCED_MAX reference bits, it may not detect some
+ * dirty pages. Those dirty pages will only be detected by a future call
+ * to pmap_is_modified().
+ */
+int
+pmap_ts_referenced(vm_page_t m)
+{
+ struct md_page *pvh;
+ pv_entry_t pv, pvf;
+ pmap_t pmap;
+ pt1_entry_t *pte1p, opte1;
+ pt2_entry_t *pte2p, opte2;
+ vm_paddr_t pa;
+ int rtval = 0;
+
+ KASSERT((m->oflags & VPO_UNMANAGED) == 0,
+ ("%s: page %p is not managed", __func__, m));
+ pa = VM_PAGE_TO_PHYS(m);
+ pvh = pa_to_pvh(pa);
+ rw_wlock(&pvh_global_lock);
+ sched_pin();
+ if ((m->flags & PG_FICTITIOUS) != 0 ||
+ (pvf = TAILQ_FIRST(&pvh->pv_list)) == NULL)
+ goto small_mappings;
+ pv = pvf;
+ do {
+ pmap = PV_PMAP(pv);
+ PMAP_LOCK(pmap);
+ pte1p = pmap_pte1(pmap, pv->pv_va);
+ opte1 = pte1_load(pte1p);
+ if (pte1_is_dirty(opte1)) {
+ /*
+ * Although "opte1" is mapping a 1MB page, because
+ * this function is called at a 4KB page granularity,
+ * we only update the 4KB page under test.
+ */
+ vm_page_dirty(m);
+ }
+ if ((opte1 & PTE1_A) != 0) {
+ /*
+ * Since this reference bit is shared by 256 4KB pages,
+ * it should not be cleared every time it is tested.
+ * Apply a simple "hash" function on the physical page
+ * number, the virtual section number, and the pmap
+ * address to select one 4KB page out of the 256
+ * on which testing the reference bit will result
+ * in clearing that bit. This function is designed
+ * to avoid the selection of the same 4KB page
+ * for every 1MB page mapping.
+ *
+ * On demotion, a mapping that hasn't been referenced
+ * is simply destroyed. To avoid the possibility of a
+ * subsequent page fault on a demoted wired mapping,
+ * always leave its reference bit set. Moreover,
+ * since the section is wired, the current state of
+ * its reference bit won't affect page replacement.
+ */
+ if ((((pa >> PAGE_SHIFT) ^ (pv->pv_va >> PTE1_SHIFT) ^
+ (uintptr_t)pmap) & (NPTE2_IN_PG - 1)) == 0 &&
+ !pte1_is_wired(opte1)) {
+ pte1_clear_bit(pte1p, PTE1_A);
+ pmap_tlb_flush(pmap, pv->pv_va);
+ }
+ rtval++;
+ }
+ PMAP_UNLOCK(pmap);
+ /* Rotate the PV list if it has more than one entry. */
+ if (TAILQ_NEXT(pv, pv_next) != NULL) {
+ TAILQ_REMOVE(&pvh->pv_list, pv, pv_next);
+ TAILQ_INSERT_TAIL(&pvh->pv_list, pv, pv_next);
+ }
+ if (rtval >= PMAP_TS_REFERENCED_MAX)
+ goto out;
+ } while ((pv = TAILQ_FIRST(&pvh->pv_list)) != pvf);
+small_mappings:
+ if ((pvf = TAILQ_FIRST(&m->md.pv_list)) == NULL)
+ goto out;
+ pv = pvf;
+ do {
+ pmap = PV_PMAP(pv);
+ PMAP_LOCK(pmap);
+ pte1p = pmap_pte1(pmap, pv->pv_va);
+ KASSERT(pte1_is_link(pte1_load(pte1p)),
+ ("%s: not found a link in page %p's pv list", __func__, m));
+
+ pte2p = pmap_pte2_quick(pmap, pv->pv_va);
+ opte2 = pte2_load(pte2p);
+ if (pte2_is_dirty(opte2))
+ vm_page_dirty(m);
+ if ((opte2 & PTE2_A) != 0) {
+ pte2_clear_bit(pte2p, PTE2_A);
+ pmap_tlb_flush(pmap, pv->pv_va);
+ rtval++;
+ }
+ PMAP_UNLOCK(pmap);
+ /* Rotate the PV list if it has more than one entry. */
+ if (TAILQ_NEXT(pv, pv_next) != NULL) {
+ TAILQ_REMOVE(&m->md.pv_list, pv, pv_next);
+ TAILQ_INSERT_TAIL(&m->md.pv_list, pv, pv_next);
+ }
+ } while ((pv = TAILQ_FIRST(&m->md.pv_list)) != pvf && rtval <
+ PMAP_TS_REFERENCED_MAX);
+out:
+ sched_unpin();
+ rw_wunlock(&pvh_global_lock);
+ return (rtval);
+}
+
+/*
+ * Clear the wired attribute from the mappings for the specified range of
+ * addresses in the given pmap. Every valid mapping within that range
+ * must have the wired attribute set. In contrast, invalid mappings
+ * cannot have the wired attribute set, so they are ignored.
+ *
+ * The wired attribute of the page table entry is not a hardware feature,
+ * so there is no need to invalidate any TLB entries.
+ */
+void
+pmap_unwire(pmap_t pmap, vm_offset_t sva, vm_offset_t eva)
+{
+ vm_offset_t nextva;
+ pt1_entry_t *pte1p, pte1;
+ pt2_entry_t *pte2p, pte2;
+ boolean_t pv_lists_locked;
+
+ if (pmap_is_current(pmap))
+ pv_lists_locked = FALSE;
+ else {
+ pv_lists_locked = TRUE;
+resume:
+ rw_wlock(&pvh_global_lock);
+ sched_pin();
+ }
+ PMAP_LOCK(pmap);
+ for (; sva < eva; sva = nextva) {
+ nextva = pte1_trunc(sva + PTE1_SIZE);
+ if (nextva < sva)
+ nextva = eva;
+
+ pte1p = pmap_pte1(pmap, sva);
+ pte1 = pte1_load(pte1p);
+
+ /*
+ * Weed out invalid mappings. Note: we assume that L1 page
+ * page table is always allocated, and in kernel virtual.
+ */
+ if (pte1 == 0)
+ continue;
+
+ if (pte1_is_section(pte1)) {
+ if (!pte1_is_wired(pte1))
+ panic("%s: pte1 %#x not wired", __func__, pte1);
+
+ /*
+ * Are we unwiring the entire large page? If not,
+ * demote the mapping and fall through.
+ */
+ if (sva + PTE1_SIZE == nextva && eva >= nextva) {
+ pte1_clear_bit(pte1p, PTE1_W);
+ pmap->pm_stats.wired_count -= PTE1_SIZE /
+ PAGE_SIZE;
+ continue;
+ } else {
+ if (!pv_lists_locked) {
+ pv_lists_locked = TRUE;
+ if (!rw_try_wlock(&pvh_global_lock)) {
+ PMAP_UNLOCK(pmap);
+ /* Repeat sva. */
+ goto resume;
+ }
+ sched_pin();
+ }
+ if (!pmap_demote_pte1(pmap, pte1p, sva))
+ panic("%s: demotion failed", __func__);
+#ifdef INVARIANTS
+ else {
+ /* Update pte1 after demotion */
+ pte1 = pte1_load(pte1p);
+ }
+#endif
+ }
+ }
+
+ KASSERT(pte1_is_link(pte1), ("%s: pmap %p va %#x pte1 %#x at %p"
+ " is not link", __func__, pmap, sva, pte1, pte1p));
+
+ /*
+ * Limit our scan to either the end of the va represented
+ * by the current L2 page table page, or to the end of the
+ * range being protected.
+ */
+ if (nextva > eva)
+ nextva = eva;
+
+ for (pte2p = pmap_pte2_quick(pmap, sva); sva != nextva; pte2p++,
+ sva += PAGE_SIZE) {
+ pte2 = pte2_load(pte2p);
+ if (!pte2_is_valid(pte2))
+ continue;
+ if (!pte2_is_wired(pte2))
+ panic("%s: pte2 %#x is missing PTE2_W",
+ __func__, pte2);
+
+ /*
+ * PTE2_W must be cleared atomically. Although the pmap
+ * lock synchronizes access to PTE2_W, another processor
+ * could be changing PTE2_NM and/or PTE2_A concurrently.
+ */
+ pte2_clear_bit(pte2p, PTE2_W);
+ pmap->pm_stats.wired_count--;
+ }
+ }
+ if (pv_lists_locked) {
+ sched_unpin();
+ rw_wunlock(&pvh_global_lock);
+ }
+ PMAP_UNLOCK(pmap);
+}
+
+/*
+ * Clear the write and modified bits in each of the given page's mappings.
+ */
+void
+pmap_remove_write(vm_page_t m)
+{
+ struct md_page *pvh;
+ pv_entry_t next_pv, pv;
+ pmap_t pmap;
+ pt1_entry_t *pte1p;
+ pt2_entry_t *pte2p, opte2;
+ vm_offset_t va;
+
+ KASSERT((m->oflags & VPO_UNMANAGED) == 0,
+ ("%s: page %p is not managed", __func__, m));
+ vm_page_assert_busied(m);
+
+ if (!pmap_page_is_write_mapped(m))
+ return;
+ rw_wlock(&pvh_global_lock);
+ sched_pin();
+ if ((m->flags & PG_FICTITIOUS) != 0)
+ goto small_mappings;
+ pvh = pa_to_pvh(VM_PAGE_TO_PHYS(m));
+ TAILQ_FOREACH_SAFE(pv, &pvh->pv_list, pv_next, next_pv) {
+ va = pv->pv_va;
+ pmap = PV_PMAP(pv);
+ PMAP_LOCK(pmap);
+ pte1p = pmap_pte1(pmap, va);
+ if (!(pte1_load(pte1p) & PTE1_RO))
+ (void)pmap_demote_pte1(pmap, pte1p, va);
+ PMAP_UNLOCK(pmap);
+ }
+small_mappings:
+ TAILQ_FOREACH(pv, &m->md.pv_list, pv_next) {
+ pmap = PV_PMAP(pv);
+ PMAP_LOCK(pmap);
+ pte1p = pmap_pte1(pmap, pv->pv_va);
+ KASSERT(!pte1_is_section(pte1_load(pte1p)), ("%s: found"
+ " a section in page %p's pv list", __func__, m));
+ pte2p = pmap_pte2_quick(pmap, pv->pv_va);
+ opte2 = pte2_load(pte2p);
+ if (!(opte2 & PTE2_RO)) {
+ pte2_store(pte2p, opte2 | PTE2_RO | PTE2_NM);
+ if (pte2_is_dirty(opte2))
+ vm_page_dirty(m);
+ pmap_tlb_flush(pmap, pv->pv_va);
+ }
+ PMAP_UNLOCK(pmap);
+ }
+ vm_page_aflag_clear(m, PGA_WRITEABLE);
+ sched_unpin();
+ rw_wunlock(&pvh_global_lock);
+}
+
+/*
+ * Apply the given advice to the specified range of addresses within the
+ * given pmap. Depending on the advice, clear the referenced and/or
+ * modified flags in each mapping and set the mapped page's dirty field.
+ */
+void
+pmap_advise(pmap_t pmap, vm_offset_t sva, vm_offset_t eva, int advice)
+{
+ pt1_entry_t *pte1p, opte1;
+ pt2_entry_t *pte2p, pte2;
+ vm_offset_t pdnxt;
+ vm_page_t m;
+ boolean_t pv_lists_locked;
+
+ if (advice != MADV_DONTNEED && advice != MADV_FREE)
+ return;
+ if (pmap_is_current(pmap))
+ pv_lists_locked = FALSE;
+ else {
+ pv_lists_locked = TRUE;
+resume:
+ rw_wlock(&pvh_global_lock);
+ sched_pin();
+ }
+ PMAP_LOCK(pmap);
+ for (; sva < eva; sva = pdnxt) {
+ pdnxt = pte1_trunc(sva + PTE1_SIZE);
+ if (pdnxt < sva)
+ pdnxt = eva;
+ pte1p = pmap_pte1(pmap, sva);
+ opte1 = pte1_load(pte1p);
+ if (!pte1_is_valid(opte1)) /* XXX */
+ continue;
+ else if (pte1_is_section(opte1)) {
+ if (!pte1_is_managed(opte1))
+ continue;
+ if (!pv_lists_locked) {
+ pv_lists_locked = TRUE;
+ if (!rw_try_wlock(&pvh_global_lock)) {
+ PMAP_UNLOCK(pmap);
+ goto resume;
+ }
+ sched_pin();
+ }
+ if (!pmap_demote_pte1(pmap, pte1p, sva)) {
+ /*
+ * The large page mapping was destroyed.
+ */
+ continue;
+ }
+
+ /*
+ * Unless the page mappings are wired, remove the
+ * mapping to a single page so that a subsequent
+ * access may repromote. Since the underlying L2 page
+ * table is fully populated, this removal never
+ * frees a L2 page table page.
+ */
+ if (!pte1_is_wired(opte1)) {
+ pte2p = pmap_pte2_quick(pmap, sva);
+ KASSERT(pte2_is_valid(pte2_load(pte2p)),
+ ("%s: invalid PTE2", __func__));
+ pmap_remove_pte2(pmap, pte2p, sva, NULL);
+ }
+ }
+ if (pdnxt > eva)
+ pdnxt = eva;
+ for (pte2p = pmap_pte2_quick(pmap, sva); sva != pdnxt; pte2p++,
+ sva += PAGE_SIZE) {
+ pte2 = pte2_load(pte2p);
+ if (!pte2_is_valid(pte2) || !pte2_is_managed(pte2))
+ continue;
+ else if (pte2_is_dirty(pte2)) {
+ if (advice == MADV_DONTNEED) {
+ /*
+ * Future calls to pmap_is_modified()
+ * can be avoided by making the page
+ * dirty now.
+ */
+ m = PHYS_TO_VM_PAGE(pte2_pa(pte2));
+ vm_page_dirty(m);
+ }
+ pte2_set_bit(pte2p, PTE2_NM);
+ pte2_clear_bit(pte2p, PTE2_A);
+ } else if ((pte2 & PTE2_A) != 0)
+ pte2_clear_bit(pte2p, PTE2_A);
+ else
+ continue;
+ pmap_tlb_flush(pmap, sva);
+ }
+ }
+ if (pv_lists_locked) {
+ sched_unpin();
+ rw_wunlock(&pvh_global_lock);
+ }
+ PMAP_UNLOCK(pmap);
+}
+
+/*
+ * Clear the modify bits on the specified physical page.
+ */
+void
+pmap_clear_modify(vm_page_t m)
+{
+ struct md_page *pvh;
+ pv_entry_t next_pv, pv;
+ pmap_t pmap;
+ pt1_entry_t *pte1p, opte1;
+ pt2_entry_t *pte2p, opte2;
+ vm_offset_t va;
+
+ KASSERT((m->oflags & VPO_UNMANAGED) == 0,
+ ("%s: page %p is not managed", __func__, m));
+ vm_page_assert_busied(m);
+
+ if (!pmap_page_is_write_mapped(m))
+ return;
+ rw_wlock(&pvh_global_lock);
+ sched_pin();
+ if ((m->flags & PG_FICTITIOUS) != 0)
+ goto small_mappings;
+ pvh = pa_to_pvh(VM_PAGE_TO_PHYS(m));
+ TAILQ_FOREACH_SAFE(pv, &pvh->pv_list, pv_next, next_pv) {
+ va = pv->pv_va;
+ pmap = PV_PMAP(pv);
+ PMAP_LOCK(pmap);
+ pte1p = pmap_pte1(pmap, va);
+ opte1 = pte1_load(pte1p);
+ if (!(opte1 & PTE1_RO)) {
+ if (pmap_demote_pte1(pmap, pte1p, va) &&
+ !pte1_is_wired(opte1)) {
+ /*
+ * Write protect the mapping to a
+ * single page so that a subsequent
+ * write access may repromote.
+ */
+ va += VM_PAGE_TO_PHYS(m) - pte1_pa(opte1);
+ pte2p = pmap_pte2_quick(pmap, va);
+ opte2 = pte2_load(pte2p);
+ if ((opte2 & PTE2_V)) {
+ pte2_set_bit(pte2p, PTE2_NM | PTE2_RO);
+ vm_page_dirty(m);
+ pmap_tlb_flush(pmap, va);
+ }
+ }
+ }
+ PMAP_UNLOCK(pmap);
+ }
+small_mappings:
+ TAILQ_FOREACH(pv, &m->md.pv_list, pv_next) {
+ pmap = PV_PMAP(pv);
+ PMAP_LOCK(pmap);
+ pte1p = pmap_pte1(pmap, pv->pv_va);
+ KASSERT(!pte1_is_section(pte1_load(pte1p)), ("%s: found"
+ " a section in page %p's pv list", __func__, m));
+ pte2p = pmap_pte2_quick(pmap, pv->pv_va);
+ if (pte2_is_dirty(pte2_load(pte2p))) {
+ pte2_set_bit(pte2p, PTE2_NM);
+ pmap_tlb_flush(pmap, pv->pv_va);
+ }
+ PMAP_UNLOCK(pmap);
+ }
+ sched_unpin();
+ rw_wunlock(&pvh_global_lock);
+}
+
+/*
+ * Sets the memory attribute for the specified page.
+ */
+void
+pmap_page_set_memattr(vm_page_t m, vm_memattr_t ma)
+{
+ pt2_entry_t *cmap2_pte2p;
+ vm_memattr_t oma;
+ vm_paddr_t pa;
+ struct pcpu *pc;
+
+ oma = m->md.pat_mode;
+ m->md.pat_mode = ma;
+
+ CTR5(KTR_PMAP, "%s: page %p - 0x%08X oma: %d, ma: %d", __func__, m,
+ VM_PAGE_TO_PHYS(m), oma, ma);
+ if ((m->flags & PG_FICTITIOUS) != 0)
+ return;
+#if 0
+ /*
+ * If "m" is a normal page, flush it from the cache.
+ *
+ * First, try to find an existing mapping of the page by sf
+ * buffer. sf_buf_invalidate_cache() modifies mapping and
+ * flushes the cache.
+ */
+ if (sf_buf_invalidate_cache(m, oma))
+ return;
+#endif
+ /*
+ * If page is not mapped by sf buffer, map the page
+ * transient and do invalidation.
+ */
+ if (ma != oma) {
+ pa = VM_PAGE_TO_PHYS(m);
+ sched_pin();
+ pc = get_pcpu();
+ cmap2_pte2p = pc->pc_cmap2_pte2p;
+ mtx_lock(&pc->pc_cmap_lock);
+ if (pte2_load(cmap2_pte2p) != 0)
+ panic("%s: CMAP2 busy", __func__);
+ pte2_store(cmap2_pte2p, PTE2_KERN_NG(pa, PTE2_AP_KRW,
+ vm_memattr_to_pte2(ma)));
+ dcache_wbinv_poc((vm_offset_t)pc->pc_cmap2_addr, pa, PAGE_SIZE);
+ pte2_clear(cmap2_pte2p);
+ tlb_flush((vm_offset_t)pc->pc_cmap2_addr);
+ sched_unpin();
+ mtx_unlock(&pc->pc_cmap_lock);
+ }
+}
+
+/*
+ * Miscellaneous support routines follow
+ */
+
+/*
+ * Returns TRUE if the given page is mapped individually or as part of
+ * a 1mpage. Otherwise, returns FALSE.
+ */
+boolean_t
+pmap_page_is_mapped(vm_page_t m)
+{
+ boolean_t rv;
+
+ if ((m->oflags & VPO_UNMANAGED) != 0)
+ return (FALSE);
+ rw_wlock(&pvh_global_lock);
+ rv = !TAILQ_EMPTY(&m->md.pv_list) ||
+ ((m->flags & PG_FICTITIOUS) == 0 &&
+ !TAILQ_EMPTY(&pa_to_pvh(VM_PAGE_TO_PHYS(m))->pv_list));
+ rw_wunlock(&pvh_global_lock);
+ return (rv);
+}
+
+/*
+ * Returns true if the pmap's pv is one of the first
+ * 16 pvs linked to from this page. This count may
+ * be changed upwards or downwards in the future; it
+ * is only necessary that true be returned for a small
+ * subset of pmaps for proper page aging.
+ */
+boolean_t
+pmap_page_exists_quick(pmap_t pmap, vm_page_t m)
+{
+ struct md_page *pvh;
+ pv_entry_t pv;
+ int loops = 0;
+ boolean_t rv;
+
+ KASSERT((m->oflags & VPO_UNMANAGED) == 0,
+ ("%s: page %p is not managed", __func__, m));
+ rv = FALSE;
+ rw_wlock(&pvh_global_lock);
+ TAILQ_FOREACH(pv, &m->md.pv_list, pv_next) {
+ if (PV_PMAP(pv) == pmap) {
+ rv = TRUE;
+ break;
+ }
+ loops++;
+ if (loops >= 16)
+ break;
+ }
+ if (!rv && loops < 16 && (m->flags & PG_FICTITIOUS) == 0) {
+ pvh = pa_to_pvh(VM_PAGE_TO_PHYS(m));
+ TAILQ_FOREACH(pv, &pvh->pv_list, pv_next) {
+ if (PV_PMAP(pv) == pmap) {
+ rv = TRUE;
+ break;
+ }
+ loops++;
+ if (loops >= 16)
+ break;
+ }
+ }
+ rw_wunlock(&pvh_global_lock);
+ return (rv);
+}
+
+/*
+ * pmap_zero_page zeros the specified hardware page by mapping
+ * the page into KVM and using bzero to clear its contents.
+ */
+void
+pmap_zero_page(vm_page_t m)
+{
+ pt2_entry_t *cmap2_pte2p;
+ struct pcpu *pc;
+
+ sched_pin();
+ pc = get_pcpu();
+ cmap2_pte2p = pc->pc_cmap2_pte2p;
+ mtx_lock(&pc->pc_cmap_lock);
+ if (pte2_load(cmap2_pte2p) != 0)
+ panic("%s: CMAP2 busy", __func__);
+ pte2_store(cmap2_pte2p, PTE2_KERN_NG(VM_PAGE_TO_PHYS(m), PTE2_AP_KRW,
+ vm_page_pte2_attr(m)));
+ pagezero(pc->pc_cmap2_addr);
+ pte2_clear(cmap2_pte2p);
+ tlb_flush((vm_offset_t)pc->pc_cmap2_addr);
+ sched_unpin();
+ mtx_unlock(&pc->pc_cmap_lock);
+}
+
+/*
+ * pmap_zero_page_area zeros the specified hardware page by mapping
+ * the page into KVM and using bzero to clear its contents.
+ *
+ * off and size may not cover an area beyond a single hardware page.
+ */
+void
+pmap_zero_page_area(vm_page_t m, int off, int size)
+{
+ pt2_entry_t *cmap2_pte2p;
+ struct pcpu *pc;
+
+ sched_pin();
+ pc = get_pcpu();
+ cmap2_pte2p = pc->pc_cmap2_pte2p;
+ mtx_lock(&pc->pc_cmap_lock);
+ if (pte2_load(cmap2_pte2p) != 0)
+ panic("%s: CMAP2 busy", __func__);
+ pte2_store(cmap2_pte2p, PTE2_KERN_NG(VM_PAGE_TO_PHYS(m), PTE2_AP_KRW,
+ vm_page_pte2_attr(m)));
+ if (off == 0 && size == PAGE_SIZE)
+ pagezero(pc->pc_cmap2_addr);
+ else
+ bzero(pc->pc_cmap2_addr + off, size);
+ pte2_clear(cmap2_pte2p);
+ tlb_flush((vm_offset_t)pc->pc_cmap2_addr);
+ sched_unpin();
+ mtx_unlock(&pc->pc_cmap_lock);
+}
+
+/*
+ * pmap_copy_page copies the specified (machine independent)
+ * page by mapping the page into virtual memory and using
+ * bcopy to copy the page, one machine dependent page at a
+ * time.
+ */
+void
+pmap_copy_page(vm_page_t src, vm_page_t dst)
+{
+ pt2_entry_t *cmap1_pte2p, *cmap2_pte2p;
+ struct pcpu *pc;
+
+ sched_pin();
+ pc = get_pcpu();
+ cmap1_pte2p = pc->pc_cmap1_pte2p;
+ cmap2_pte2p = pc->pc_cmap2_pte2p;
+ mtx_lock(&pc->pc_cmap_lock);
+ if (pte2_load(cmap1_pte2p) != 0)
+ panic("%s: CMAP1 busy", __func__);
+ if (pte2_load(cmap2_pte2p) != 0)
+ panic("%s: CMAP2 busy", __func__);
+ pte2_store(cmap1_pte2p, PTE2_KERN_NG(VM_PAGE_TO_PHYS(src),
+ PTE2_AP_KR | PTE2_NM, vm_page_pte2_attr(src)));
+ pte2_store(cmap2_pte2p, PTE2_KERN_NG(VM_PAGE_TO_PHYS(dst),
+ PTE2_AP_KRW, vm_page_pte2_attr(dst)));
+ bcopy(pc->pc_cmap1_addr, pc->pc_cmap2_addr, PAGE_SIZE);
+ pte2_clear(cmap1_pte2p);
+ tlb_flush((vm_offset_t)pc->pc_cmap1_addr);
+ pte2_clear(cmap2_pte2p);
+ tlb_flush((vm_offset_t)pc->pc_cmap2_addr);
+ sched_unpin();
+ mtx_unlock(&pc->pc_cmap_lock);
+}
+
+int unmapped_buf_allowed = 1;
+
+void
+pmap_copy_pages(vm_page_t ma[], vm_offset_t a_offset, vm_page_t mb[],
+ vm_offset_t b_offset, int xfersize)
+{
+ pt2_entry_t *cmap1_pte2p, *cmap2_pte2p;
+ vm_page_t a_pg, b_pg;
+ char *a_cp, *b_cp;
+ vm_offset_t a_pg_offset, b_pg_offset;
+ struct pcpu *pc;
+ int cnt;
+
+ sched_pin();
+ pc = get_pcpu();
+ cmap1_pte2p = pc->pc_cmap1_pte2p;
+ cmap2_pte2p = pc->pc_cmap2_pte2p;
+ mtx_lock(&pc->pc_cmap_lock);
+ if (pte2_load(cmap1_pte2p) != 0)
+ panic("pmap_copy_pages: CMAP1 busy");
+ if (pte2_load(cmap2_pte2p) != 0)
+ panic("pmap_copy_pages: CMAP2 busy");
+ while (xfersize > 0) {
+ a_pg = ma[a_offset >> PAGE_SHIFT];
+ a_pg_offset = a_offset & PAGE_MASK;
+ cnt = min(xfersize, PAGE_SIZE - a_pg_offset);
+ b_pg = mb[b_offset >> PAGE_SHIFT];
+ b_pg_offset = b_offset & PAGE_MASK;
+ cnt = min(cnt, PAGE_SIZE - b_pg_offset);
+ pte2_store(cmap1_pte2p, PTE2_KERN_NG(VM_PAGE_TO_PHYS(a_pg),
+ PTE2_AP_KR | PTE2_NM, vm_page_pte2_attr(a_pg)));
+ tlb_flush_local((vm_offset_t)pc->pc_cmap1_addr);
+ pte2_store(cmap2_pte2p, PTE2_KERN_NG(VM_PAGE_TO_PHYS(b_pg),
+ PTE2_AP_KRW, vm_page_pte2_attr(b_pg)));
+ tlb_flush_local((vm_offset_t)pc->pc_cmap2_addr);
+ a_cp = pc->pc_cmap1_addr + a_pg_offset;
+ b_cp = pc->pc_cmap2_addr + b_pg_offset;
+ bcopy(a_cp, b_cp, cnt);
+ a_offset += cnt;
+ b_offset += cnt;
+ xfersize -= cnt;
+ }
+ pte2_clear(cmap1_pte2p);
+ tlb_flush((vm_offset_t)pc->pc_cmap1_addr);
+ pte2_clear(cmap2_pte2p);
+ tlb_flush((vm_offset_t)pc->pc_cmap2_addr);
+ sched_unpin();
+ mtx_unlock(&pc->pc_cmap_lock);
+}
+
+vm_offset_t
+pmap_quick_enter_page(vm_page_t m)
+{
+ struct pcpu *pc;
+ pt2_entry_t *pte2p;
+
+ critical_enter();
+ pc = get_pcpu();
+ pte2p = pc->pc_qmap_pte2p;
+
+ KASSERT(pte2_load(pte2p) == 0, ("%s: PTE2 busy", __func__));
+
+ pte2_store(pte2p, PTE2_KERN_NG(VM_PAGE_TO_PHYS(m), PTE2_AP_KRW,
+ vm_page_pte2_attr(m)));
+ return (pc->pc_qmap_addr);
+}
+
+void
+pmap_quick_remove_page(vm_offset_t addr)
+{
+ struct pcpu *pc;
+ pt2_entry_t *pte2p;
+
+ pc = get_pcpu();
+ pte2p = pc->pc_qmap_pte2p;
+
+ KASSERT(addr == pc->pc_qmap_addr, ("%s: invalid address", __func__));
+ KASSERT(pte2_load(pte2p) != 0, ("%s: PTE2 not in use", __func__));
+
+ pte2_clear(pte2p);
+ tlb_flush(pc->pc_qmap_addr);
+ critical_exit();
+}
+
+/*
+ * Copy the range specified by src_addr/len
+ * from the source map to the range dst_addr/len
+ * in the destination map.
+ *
+ * This routine is only advisory and need not do anything.
+ */
+void
+pmap_copy(pmap_t dst_pmap, pmap_t src_pmap, vm_offset_t dst_addr, vm_size_t len,
+ vm_offset_t src_addr)
+{
+ struct spglist free;
+ vm_offset_t addr;
+ vm_offset_t end_addr = src_addr + len;
+ vm_offset_t nextva;
+
+ if (dst_addr != src_addr)
+ return;
+
+ if (!pmap_is_current(src_pmap))
+ return;
+
+ rw_wlock(&pvh_global_lock);
+ if (dst_pmap < src_pmap) {
+ PMAP_LOCK(dst_pmap);
+ PMAP_LOCK(src_pmap);
+ } else {
+ PMAP_LOCK(src_pmap);
+ PMAP_LOCK(dst_pmap);
+ }
+ sched_pin();
+ for (addr = src_addr; addr < end_addr; addr = nextva) {
+ pt2_entry_t *src_pte2p, *dst_pte2p;
+ vm_page_t dst_mpt2pg, src_mpt2pg;
+ pt1_entry_t src_pte1;
+ u_int pte1_idx;
+
+ KASSERT(addr < VM_MAXUSER_ADDRESS,
+ ("%s: invalid to pmap_copy page tables", __func__));
+
+ nextva = pte1_trunc(addr + PTE1_SIZE);
+ if (nextva < addr)
+ nextva = end_addr;
+
+ pte1_idx = pte1_index(addr);
+ src_pte1 = src_pmap->pm_pt1[pte1_idx];
+ if (pte1_is_section(src_pte1)) {
+ if ((addr & PTE1_OFFSET) != 0 ||
+ (addr + PTE1_SIZE) > end_addr)
+ continue;
+ if (dst_pmap->pm_pt1[pte1_idx] == 0 &&
+ (!pte1_is_managed(src_pte1) ||
+ pmap_pv_insert_pte1(dst_pmap, addr, src_pte1,
+ PMAP_ENTER_NORECLAIM))) {
+ dst_pmap->pm_pt1[pte1_idx] = src_pte1 &
+ ~PTE1_W;
+ dst_pmap->pm_stats.resident_count +=
+ PTE1_SIZE / PAGE_SIZE;
+ pmap_pte1_mappings++;
+ }
+ continue;
+ } else if (!pte1_is_link(src_pte1))
+ continue;
+
+ src_mpt2pg = PHYS_TO_VM_PAGE(pte1_link_pa(src_pte1));
+
+ /*
+ * We leave PT2s to be linked from PT1 even if they are not
+ * referenced until all PT2s in a page are without reference.
+ *
+ * QQQ: It could be changed ...
+ */
+#if 0 /* single_pt2_link_is_cleared */
+ KASSERT(pt2_wirecount_get(src_mpt2pg, pte1_idx) > 0,
+ ("%s: source page table page is unused", __func__));
+#else
+ if (pt2_wirecount_get(src_mpt2pg, pte1_idx) == 0)
+ continue;
+#endif
+ if (nextva > end_addr)
+ nextva = end_addr;
+
+ src_pte2p = pt2map_entry(addr);
+ while (addr < nextva) {
+ pt2_entry_t temp_pte2;
+ temp_pte2 = pte2_load(src_pte2p);
+ /*
+ * we only virtual copy managed pages
+ */
+ if (pte2_is_managed(temp_pte2)) {
+ dst_mpt2pg = pmap_allocpte2(dst_pmap, addr,
+ PMAP_ENTER_NOSLEEP);
+ if (dst_mpt2pg == NULL)
+ goto out;
+ dst_pte2p = pmap_pte2_quick(dst_pmap, addr);
+ if (!pte2_is_valid(pte2_load(dst_pte2p)) &&
+ pmap_try_insert_pv_entry(dst_pmap, addr,
+ PHYS_TO_VM_PAGE(pte2_pa(temp_pte2)))) {
+ /*
+ * Clear the wired, modified, and
+ * accessed (referenced) bits
+ * during the copy.
+ */
+ temp_pte2 &= ~(PTE2_W | PTE2_A);
+ temp_pte2 |= PTE2_NM;
+ pte2_store(dst_pte2p, temp_pte2);
+ dst_pmap->pm_stats.resident_count++;
+ } else {
+ SLIST_INIT(&free);
+ if (pmap_unwire_pt2(dst_pmap, addr,
+ dst_mpt2pg, &free)) {
+ pmap_tlb_flush(dst_pmap, addr);
+ vm_page_free_pages_toq(&free,
+ false);
+ }
+ goto out;
+ }
+ if (pt2_wirecount_get(dst_mpt2pg, pte1_idx) >=
+ pt2_wirecount_get(src_mpt2pg, pte1_idx))
+ break;
+ }
+ addr += PAGE_SIZE;
+ src_pte2p++;
+ }
+ }
+out:
+ sched_unpin();
+ rw_wunlock(&pvh_global_lock);
+ PMAP_UNLOCK(src_pmap);
+ PMAP_UNLOCK(dst_pmap);
+}
+
+/*
+ * Increase the starting virtual address of the given mapping if a
+ * different alignment might result in more section mappings.
+ */
+void
+pmap_align_superpage(vm_object_t object, vm_ooffset_t offset,
+ vm_offset_t *addr, vm_size_t size)
+{
+ vm_offset_t pte1_offset;
+
+ if (size < PTE1_SIZE)
+ return;
+ if (object != NULL && (object->flags & OBJ_COLORED) != 0)
+ offset += ptoa(object->pg_color);
+ pte1_offset = offset & PTE1_OFFSET;
+ if (size - ((PTE1_SIZE - pte1_offset) & PTE1_OFFSET) < PTE1_SIZE ||
+ (*addr & PTE1_OFFSET) == pte1_offset)
+ return;
+ if ((*addr & PTE1_OFFSET) < pte1_offset)
+ *addr = pte1_trunc(*addr) + pte1_offset;
+ else
+ *addr = pte1_roundup(*addr) + pte1_offset;
+}
+
+void
+pmap_activate(struct thread *td)
+{
+ pmap_t pmap, oldpmap;
+ u_int cpuid, ttb;
+
+ PDEBUG(9, printf("%s: td = %08x\n", __func__, (uint32_t)td));
+
+ critical_enter();
+ pmap = vmspace_pmap(td->td_proc->p_vmspace);
+ oldpmap = PCPU_GET(curpmap);
+ cpuid = PCPU_GET(cpuid);
+
+#if defined(SMP)
+ CPU_CLR_ATOMIC(cpuid, &oldpmap->pm_active);
+ CPU_SET_ATOMIC(cpuid, &pmap->pm_active);
+#else
+ CPU_CLR(cpuid, &oldpmap->pm_active);
+ CPU_SET(cpuid, &pmap->pm_active);
+#endif
+
+ ttb = pmap_ttb_get(pmap);
+
+ /*
+ * pmap_activate is for the current thread on the current cpu
+ */
+ td->td_pcb->pcb_pagedir = ttb;
+ cp15_ttbr_set(ttb);
+ PCPU_SET(curpmap, pmap);
+ critical_exit();
+}
+
+/*
+ * Perform the pmap work for mincore(2). If the page is not both referenced and
+ * modified by this pmap, returns its physical address so that the caller can
+ * find other mappings.
+ */
+int
+pmap_mincore(pmap_t pmap, vm_offset_t addr, vm_paddr_t *pap)
+{
+ pt1_entry_t *pte1p, pte1;
+ pt2_entry_t *pte2p, pte2;
+ vm_paddr_t pa;
+ bool managed;
+ int val;
+
+ PMAP_LOCK(pmap);
+ pte1p = pmap_pte1(pmap, addr);
+ pte1 = pte1_load(pte1p);
+ if (pte1_is_section(pte1)) {
+ pa = trunc_page(pte1_pa(pte1) | (addr & PTE1_OFFSET));
+ managed = pte1_is_managed(pte1);
+ val = MINCORE_PSIND(1) | MINCORE_INCORE;
+ if (pte1_is_dirty(pte1))
+ val |= MINCORE_MODIFIED | MINCORE_MODIFIED_OTHER;
+ if (pte1 & PTE1_A)
+ val |= MINCORE_REFERENCED | MINCORE_REFERENCED_OTHER;
+ } else if (pte1_is_link(pte1)) {
+ pte2p = pmap_pte2(pmap, addr);
+ pte2 = pte2_load(pte2p);
+ pmap_pte2_release(pte2p);
+ pa = pte2_pa(pte2);
+ managed = pte2_is_managed(pte2);
+ val = MINCORE_INCORE;
+ if (pte2_is_dirty(pte2))
+ val |= MINCORE_MODIFIED | MINCORE_MODIFIED_OTHER;
+ if (pte2 & PTE2_A)
+ val |= MINCORE_REFERENCED | MINCORE_REFERENCED_OTHER;
+ } else {
+ managed = false;
+ val = 0;
+ }
+ if ((val & (MINCORE_MODIFIED_OTHER | MINCORE_REFERENCED_OTHER)) !=
+ (MINCORE_MODIFIED_OTHER | MINCORE_REFERENCED_OTHER) && managed) {
+ *pap = pa;
+ }
+ PMAP_UNLOCK(pmap);
+ return (val);
+}
+
+void
+pmap_kenter_device(vm_offset_t va, vm_size_t size, vm_paddr_t pa)
+{
+ vm_offset_t sva;
+ uint32_t l2attr;
+
+ KASSERT((size & PAGE_MASK) == 0,
+ ("%s: device mapping not page-sized", __func__));
+
+ sva = va;
+ l2attr = vm_memattr_to_pte2(VM_MEMATTR_DEVICE);
+ while (size != 0) {
+ pmap_kenter_prot_attr(va, pa, PTE2_AP_KRW, l2attr);
+ va += PAGE_SIZE;
+ pa += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+ tlb_flush_range(sva, va - sva);
+}
+
+void
+pmap_kremove_device(vm_offset_t va, vm_size_t size)
+{
+ vm_offset_t sva;
+
+ KASSERT((size & PAGE_MASK) == 0,
+ ("%s: device mapping not page-sized", __func__));
+
+ sva = va;
+ while (size != 0) {
+ pmap_kremove(va);
+ va += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+ tlb_flush_range(sva, va - sva);
+}
+
+void
+pmap_set_pcb_pagedir(pmap_t pmap, struct pcb *pcb)
+{
+
+ pcb->pcb_pagedir = pmap_ttb_get(pmap);
+}
+
+/*
+ * Clean L1 data cache range by physical address.
+ * The range must be within a single page.
+ */
+static void
+pmap_dcache_wb_pou(vm_paddr_t pa, vm_size_t size, uint32_t attr)
+{
+ pt2_entry_t *cmap2_pte2p;
+ struct pcpu *pc;
+
+ KASSERT(((pa & PAGE_MASK) + size) <= PAGE_SIZE,
+ ("%s: not on single page", __func__));
+
+ sched_pin();
+ pc = get_pcpu();
+ cmap2_pte2p = pc->pc_cmap2_pte2p;
+ mtx_lock(&pc->pc_cmap_lock);
+ if (pte2_load(cmap2_pte2p) != 0)
+ panic("%s: CMAP2 busy", __func__);
+ pte2_store(cmap2_pte2p, PTE2_KERN_NG(pa, PTE2_AP_KRW, attr));
+ dcache_wb_pou((vm_offset_t)pc->pc_cmap2_addr + (pa & PAGE_MASK), size);
+ pte2_clear(cmap2_pte2p);
+ tlb_flush((vm_offset_t)pc->pc_cmap2_addr);
+ sched_unpin();
+ mtx_unlock(&pc->pc_cmap_lock);
+}
+
+/*
+ * Sync instruction cache range which is not mapped yet.
+ */
+void
+cache_icache_sync_fresh(vm_offset_t va, vm_paddr_t pa, vm_size_t size)
+{
+ uint32_t len, offset;
+ vm_page_t m;
+
+ /* Write back d-cache on given address range. */
+ offset = pa & PAGE_MASK;
+ for ( ; size != 0; size -= len, pa += len, offset = 0) {
+ len = min(PAGE_SIZE - offset, size);
+ m = PHYS_TO_VM_PAGE(pa);
+ KASSERT(m != NULL, ("%s: vm_page_t is null for %#x",
+ __func__, pa));
+ pmap_dcache_wb_pou(pa, len, vm_page_pte2_attr(m));
+ }
+ /*
+ * I-cache is VIPT. Only way how to flush all virtual mappings
+ * on given physical address is to invalidate all i-cache.
+ */
+ icache_inv_all();
+}
+
+void
+pmap_sync_icache(pmap_t pmap, vm_offset_t va, vm_size_t size)
+{
+
+ /* Write back d-cache on given address range. */
+ if (va >= VM_MIN_KERNEL_ADDRESS) {
+ dcache_wb_pou(va, size);
+ } else {
+ uint32_t len, offset;
+ vm_paddr_t pa;
+ vm_page_t m;
+
+ offset = va & PAGE_MASK;
+ for ( ; size != 0; size -= len, va += len, offset = 0) {
+ pa = pmap_extract(pmap, va); /* offset is preserved */
+ len = min(PAGE_SIZE - offset, size);
+ m = PHYS_TO_VM_PAGE(pa);
+ KASSERT(m != NULL, ("%s: vm_page_t is null for %#x",
+ __func__, pa));
+ pmap_dcache_wb_pou(pa, len, vm_page_pte2_attr(m));
+ }
+ }
+ /*
+ * I-cache is VIPT. Only way how to flush all virtual mappings
+ * on given physical address is to invalidate all i-cache.
+ */
+ icache_inv_all();
+}
+
+/*
+ * The implementation of pmap_fault() uses IN_RANGE2() macro which
+ * depends on the fact that given range size is a power of 2.
+ */
+CTASSERT(powerof2(NB_IN_PT1));
+CTASSERT(powerof2(PT2MAP_SIZE));
+
+#define IN_RANGE2(addr, start, size) \
+ ((vm_offset_t)(start) == ((vm_offset_t)(addr) & ~((size) - 1)))
+
+/*
+ * Handle access and R/W emulation faults.
+ */
+int
+pmap_fault(pmap_t pmap, vm_offset_t far, uint32_t fsr, int idx, bool usermode)
+{
+ pt1_entry_t *pte1p, pte1;
+ pt2_entry_t *pte2p, pte2;
+
+ if (pmap == NULL)
+ pmap = kernel_pmap;
+
+ /*
+ * In kernel, we should never get abort with FAR which is in range of
+ * pmap->pm_pt1 or PT2MAP address spaces. If it happens, stop here
+ * and print out a useful abort message and even get to the debugger
+ * otherwise it likely ends with never ending loop of aborts.
+ */
+ if (__predict_false(IN_RANGE2(far, pmap->pm_pt1, NB_IN_PT1))) {
+ /*
+ * All L1 tables should always be mapped and present.
+ * However, we check only current one herein. For user mode,
+ * only permission abort from malicious user is not fatal.
+ * And alignment abort as it may have higher priority.
+ */
+ if (!usermode || (idx != FAULT_ALIGN && idx != FAULT_PERM_L2)) {
+ CTR4(KTR_PMAP, "%s: pmap %#x pm_pt1 %#x far %#x",
+ __func__, pmap, pmap->pm_pt1, far);
+ panic("%s: pm_pt1 abort", __func__);
+ }
+ return (KERN_INVALID_ADDRESS);
+ }
+ if (__predict_false(IN_RANGE2(far, PT2MAP, PT2MAP_SIZE))) {
+ /*
+ * PT2MAP should be always mapped and present in current
+ * L1 table. However, only existing L2 tables are mapped
+ * in PT2MAP. For user mode, only L2 translation abort and
+ * permission abort from malicious user is not fatal.
+ * And alignment abort as it may have higher priority.
+ */
+ if (!usermode || (idx != FAULT_ALIGN &&
+ idx != FAULT_TRAN_L2 && idx != FAULT_PERM_L2)) {
+ CTR4(KTR_PMAP, "%s: pmap %#x PT2MAP %#x far %#x",
+ __func__, pmap, PT2MAP, far);
+ panic("%s: PT2MAP abort", __func__);
+ }
+ return (KERN_INVALID_ADDRESS);
+ }
+
+ /*
+ * A pmap lock is used below for handling of access and R/W emulation
+ * aborts. They were handled by atomic operations before so some
+ * analysis of new situation is needed to answer the following question:
+ * Is it safe to use the lock even for these aborts?
+ *
+ * There may happen two cases in general:
+ *
+ * (1) Aborts while the pmap lock is locked already - this should not
+ * happen as pmap lock is not recursive. However, under pmap lock only
+ * internal kernel data should be accessed and such data should be
+ * mapped with A bit set and NM bit cleared. If double abort happens,
+ * then a mapping of data which has caused it must be fixed. Further,
+ * all new mappings are always made with A bit set and the bit can be
+ * cleared only on managed mappings.
+ *
+ * (2) Aborts while another lock(s) is/are locked - this already can
+ * happen. However, there is no difference here if it's either access or
+ * R/W emulation abort, or if it's some other abort.
+ */
+
+ PMAP_LOCK(pmap);
+#ifdef INVARIANTS
+ pte1 = pte1_load(pmap_pte1(pmap, far));
+ if (pte1_is_link(pte1)) {
+ /*
+ * Check in advance that associated L2 page table is mapped into
+ * PT2MAP space. Note that faulty access to not mapped L2 page
+ * table is caught in more general check above where "far" is
+ * checked that it does not lay in PT2MAP space. Note also that
+ * L1 page table and PT2TAB always exist and are mapped.
+ */
+ pte2 = pt2tab_load(pmap_pt2tab_entry(pmap, far));
+ if (!pte2_is_valid(pte2))
+ panic("%s: missing L2 page table (%p, %#x)",
+ __func__, pmap, far);
+ }
+#endif
+#ifdef SMP
+ /*
+ * Special treatment is due to break-before-make approach done when
+ * pte1 is updated for userland mapping during section promotion or
+ * demotion. If not caught here, pmap_enter() can find a section
+ * mapping on faulting address. That is not allowed.
+ */
+ if (idx == FAULT_TRAN_L1 && usermode && cp15_ats1cur_check(far) == 0) {
+ PMAP_UNLOCK(pmap);
+ return (KERN_SUCCESS);
+ }
+#endif
+ /*
+ * Accesss bits for page and section. Note that the entry
+ * is not in TLB yet, so TLB flush is not necessary.
+ *
+ * QQQ: This is hardware emulation, we do not call userret()
+ * for aborts from user mode.
+ */
+ if (idx == FAULT_ACCESS_L2) {
+ pte1 = pte1_load(pmap_pte1(pmap, far));
+ if (pte1_is_link(pte1)) {
+ /* L2 page table should exist and be mapped. */
+ pte2p = pt2map_entry(far);
+ pte2 = pte2_load(pte2p);
+ if (pte2_is_valid(pte2)) {
+ pte2_store(pte2p, pte2 | PTE2_A);
+ PMAP_UNLOCK(pmap);
+ return (KERN_SUCCESS);
+ }
+ } else {
+ /*
+ * We got L2 access fault but PTE1 is not a link.
+ * Probably some race happened, do nothing.
+ */
+ CTR3(KTR_PMAP, "%s: FAULT_ACCESS_L2 - pmap %#x far %#x",
+ __func__, pmap, far);
+ PMAP_UNLOCK(pmap);
+ return (KERN_SUCCESS);
+ }
+ }
+ if (idx == FAULT_ACCESS_L1) {
+ pte1p = pmap_pte1(pmap, far);
+ pte1 = pte1_load(pte1p);
+ if (pte1_is_section(pte1)) {
+ pte1_store(pte1p, pte1 | PTE1_A);
+ PMAP_UNLOCK(pmap);
+ return (KERN_SUCCESS);
+ } else {
+ /*
+ * We got L1 access fault but PTE1 is not section
+ * mapping. Probably some race happened, do nothing.
+ */
+ CTR3(KTR_PMAP, "%s: FAULT_ACCESS_L1 - pmap %#x far %#x",
+ __func__, pmap, far);
+ PMAP_UNLOCK(pmap);
+ return (KERN_SUCCESS);
+ }
+ }
+
+ /*
+ * Handle modify bits for page and section. Note that the modify
+ * bit is emulated by software. So PTEx_RO is software read only
+ * bit and PTEx_NM flag is real hardware read only bit.
+ *
+ * QQQ: This is hardware emulation, we do not call userret()
+ * for aborts from user mode.
+ */
+ if ((fsr & FSR_WNR) && (idx == FAULT_PERM_L2)) {
+ pte1 = pte1_load(pmap_pte1(pmap, far));
+ if (pte1_is_link(pte1)) {
+ /* L2 page table should exist and be mapped. */
+ pte2p = pt2map_entry(far);
+ pte2 = pte2_load(pte2p);
+ if (pte2_is_valid(pte2) && !(pte2 & PTE2_RO) &&
+ (pte2 & PTE2_NM)) {
+ pte2_store(pte2p, pte2 & ~PTE2_NM);
+ tlb_flush(trunc_page(far));
+ PMAP_UNLOCK(pmap);
+ return (KERN_SUCCESS);
+ }
+ } else {
+ /*
+ * We got L2 permission fault but PTE1 is not a link.
+ * Probably some race happened, do nothing.
+ */
+ CTR3(KTR_PMAP, "%s: FAULT_PERM_L2 - pmap %#x far %#x",
+ __func__, pmap, far);
+ PMAP_UNLOCK(pmap);
+ return (KERN_SUCCESS);
+ }
+ }
+ if ((fsr & FSR_WNR) && (idx == FAULT_PERM_L1)) {
+ pte1p = pmap_pte1(pmap, far);
+ pte1 = pte1_load(pte1p);
+ if (pte1_is_section(pte1)) {
+ if (!(pte1 & PTE1_RO) && (pte1 & PTE1_NM)) {
+ pte1_store(pte1p, pte1 & ~PTE1_NM);
+ tlb_flush(pte1_trunc(far));
+ PMAP_UNLOCK(pmap);
+ return (KERN_SUCCESS);
+ }
+ } else {
+ /*
+ * We got L1 permission fault but PTE1 is not section
+ * mapping. Probably some race happened, do nothing.
+ */
+ CTR3(KTR_PMAP, "%s: FAULT_PERM_L1 - pmap %#x far %#x",
+ __func__, pmap, far);
+ PMAP_UNLOCK(pmap);
+ return (KERN_SUCCESS);
+ }
+ }
+
+ /*
+ * QQQ: The previous code, mainly fast handling of access and
+ * modify bits aborts, could be moved to ASM. Now we are
+ * starting to deal with not fast aborts.
+ */
+ PMAP_UNLOCK(pmap);
+ return (KERN_FAILURE);
+}
+
+#if defined(PMAP_DEBUG)
+/*
+ * Reusing of KVA used in pmap_zero_page function !!!
+ */
+static void
+pmap_zero_page_check(vm_page_t m)
+{
+ pt2_entry_t *cmap2_pte2p;
+ uint32_t *p, *end;
+ struct pcpu *pc;
+
+ sched_pin();
+ pc = get_pcpu();
+ cmap2_pte2p = pc->pc_cmap2_pte2p;
+ mtx_lock(&pc->pc_cmap_lock);
+ if (pte2_load(cmap2_pte2p) != 0)
+ panic("%s: CMAP2 busy", __func__);
+ pte2_store(cmap2_pte2p, PTE2_KERN_NG(VM_PAGE_TO_PHYS(m), PTE2_AP_KRW,
+ vm_page_pte2_attr(m)));
+ end = (uint32_t*)(pc->pc_cmap2_addr + PAGE_SIZE);
+ for (p = (uint32_t*)pc->pc_cmap2_addr; p < end; p++)
+ if (*p != 0)
+ panic("%s: page %p not zero, va: %p", __func__, m,
+ pc->pc_cmap2_addr);
+ pte2_clear(cmap2_pte2p);
+ tlb_flush((vm_offset_t)pc->pc_cmap2_addr);
+ sched_unpin();
+ mtx_unlock(&pc->pc_cmap_lock);
+}
+
+int
+pmap_pid_dump(int pid)
+{
+ pmap_t pmap;
+ struct proc *p;
+ int npte2 = 0;
+ int i, j, index;
+
+ sx_slock(&allproc_lock);
+ FOREACH_PROC_IN_SYSTEM(p) {
+ if (p->p_pid != pid || p->p_vmspace == NULL)
+ continue;
+ index = 0;
+ pmap = vmspace_pmap(p->p_vmspace);
+ for (i = 0; i < NPTE1_IN_PT1; i++) {
+ pt1_entry_t pte1;
+ pt2_entry_t *pte2p, pte2;
+ vm_offset_t base, va;
+ vm_paddr_t pa;
+ vm_page_t m;
+
+ base = i << PTE1_SHIFT;
+ pte1 = pte1_load(&pmap->pm_pt1[i]);
+
+ if (pte1_is_section(pte1)) {
+ /*
+ * QQQ: Do something here!
+ */
+ } else if (pte1_is_link(pte1)) {
+ for (j = 0; j < NPTE2_IN_PT2; j++) {
+ va = base + (j << PAGE_SHIFT);
+ if (va >= VM_MIN_KERNEL_ADDRESS) {
+ if (index) {
+ index = 0;
+ printf("\n");
+ }
+ sx_sunlock(&allproc_lock);
+ return (npte2);
+ }
+ pte2p = pmap_pte2(pmap, va);
+ pte2 = pte2_load(pte2p);
+ pmap_pte2_release(pte2p);
+ if (!pte2_is_valid(pte2))
+ continue;
+
+ pa = pte2_pa(pte2);
+ m = PHYS_TO_VM_PAGE(pa);
+ printf("va: 0x%x, pa: 0x%x, w: %d, "
+ "f: 0x%x", va, pa,
+ m->ref_count, m->flags);
+ npte2++;
+ index++;
+ if (index >= 2) {
+ index = 0;
+ printf("\n");
+ } else {
+ printf(" ");
+ }
+ }
+ }
+ }
+ }
+ sx_sunlock(&allproc_lock);
+ return (npte2);
+}
+
+#endif
+
+#ifdef DDB
+static pt2_entry_t *
+pmap_pte2_ddb(pmap_t pmap, vm_offset_t va)
+{
+ pt1_entry_t pte1;
+ vm_paddr_t pt2pg_pa;
+
+ pte1 = pte1_load(pmap_pte1(pmap, va));
+ if (!pte1_is_link(pte1))
+ return (NULL);
+
+ if (pmap_is_current(pmap))
+ return (pt2map_entry(va));
+
+ /* Note that L2 page table size is not equal to PAGE_SIZE. */
+ pt2pg_pa = trunc_page(pte1_link_pa(pte1));
+ if (pte2_pa(pte2_load(PMAP3)) != pt2pg_pa) {
+ pte2_store(PMAP3, PTE2_KPT(pt2pg_pa));
+#ifdef SMP
+ PMAP3cpu = PCPU_GET(cpuid);
+#endif
+ tlb_flush_local((vm_offset_t)PADDR3);
+ }
+#ifdef SMP
+ else if (PMAP3cpu != PCPU_GET(cpuid)) {
+ PMAP3cpu = PCPU_GET(cpuid);
+ tlb_flush_local((vm_offset_t)PADDR3);
+ }
+#endif
+ return (PADDR3 + (arm32_btop(va) & (NPTE2_IN_PG - 1)));
+}
+
+static void
+dump_pmap(pmap_t pmap)
+{
+
+ printf("pmap %p\n", pmap);
+ printf(" pm_pt1: %p\n", pmap->pm_pt1);
+ printf(" pm_pt2tab: %p\n", pmap->pm_pt2tab);
+ printf(" pm_active: 0x%08lX\n", pmap->pm_active.__bits[0]);
+}
+
+DB_SHOW_COMMAND(pmaps, pmap_list_pmaps)
+{
+
+ pmap_t pmap;
+ LIST_FOREACH(pmap, &allpmaps, pm_list) {
+ dump_pmap(pmap);
+ }
+}
+
+static int
+pte2_class(pt2_entry_t pte2)
+{
+ int cls;
+
+ cls = (pte2 >> 2) & 0x03;
+ cls |= (pte2 >> 4) & 0x04;
+ return (cls);
+}
+
+static void
+dump_section(pmap_t pmap, uint32_t pte1_idx)
+{
+}
+
+static void
+dump_link(pmap_t pmap, uint32_t pte1_idx, boolean_t invalid_ok)
+{
+ uint32_t i;
+ vm_offset_t va;
+ pt2_entry_t *pte2p, pte2;
+ vm_page_t m;
+
+ va = pte1_idx << PTE1_SHIFT;
+ pte2p = pmap_pte2_ddb(pmap, va);
+ for (i = 0; i < NPTE2_IN_PT2; i++, pte2p++, va += PAGE_SIZE) {
+ pte2 = pte2_load(pte2p);
+ if (pte2 == 0)
+ continue;
+ if (!pte2_is_valid(pte2)) {
+ printf(" 0x%08X: 0x%08X", va, pte2);
+ if (!invalid_ok)
+ printf(" - not valid !!!");
+ printf("\n");
+ continue;
+ }
+ m = PHYS_TO_VM_PAGE(pte2_pa(pte2));
+ printf(" 0x%08X: 0x%08X, TEX%d, s:%d, g:%d, m:%p", va , pte2,
+ pte2_class(pte2), !!(pte2 & PTE2_S), !(pte2 & PTE2_NG), m);
+ if (m != NULL) {
+ printf(" v:%d w:%d f:0x%04X\n", m->valid,
+ m->ref_count, m->flags);
+ } else {
+ printf("\n");
+ }
+ }
+}
+
+static __inline boolean_t
+is_pv_chunk_space(vm_offset_t va)
+{
+
+ if ((((vm_offset_t)pv_chunkbase) <= va) &&
+ (va < ((vm_offset_t)pv_chunkbase + PAGE_SIZE * pv_maxchunks)))
+ return (TRUE);
+ return (FALSE);
+}
+
+DB_SHOW_COMMAND(pmap, pmap_pmap_print)
+{
+ /* XXX convert args. */
+ pmap_t pmap = (pmap_t)addr;
+ pt1_entry_t pte1;
+ pt2_entry_t pte2;
+ vm_offset_t va, eva;
+ vm_page_t m;
+ uint32_t i;
+ boolean_t invalid_ok, dump_link_ok, dump_pv_chunk;
+
+ if (have_addr) {
+ pmap_t pm;
+
+ LIST_FOREACH(pm, &allpmaps, pm_list)
+ if (pm == pmap) break;
+ if (pm == NULL) {
+ printf("given pmap %p is not in allpmaps list\n", pmap);
+ return;
+ }
+ } else
+ pmap = PCPU_GET(curpmap);
+
+ eva = (modif[0] == 'u') ? VM_MAXUSER_ADDRESS : 0xFFFFFFFF;
+ dump_pv_chunk = FALSE; /* XXX evaluate from modif[] */
+
+ printf("pmap: 0x%08X\n", (uint32_t)pmap);
+ printf("PT2MAP: 0x%08X\n", (uint32_t)PT2MAP);
+ printf("pt2tab: 0x%08X\n", (uint32_t)pmap->pm_pt2tab);
+
+ for(i = 0; i < NPTE1_IN_PT1; i++) {
+ pte1 = pte1_load(&pmap->pm_pt1[i]);
+ if (pte1 == 0)
+ continue;
+ va = i << PTE1_SHIFT;
+ if (va >= eva)
+ break;
+
+ if (pte1_is_section(pte1)) {
+ printf("0x%08X: Section 0x%08X, s:%d g:%d\n", va, pte1,
+ !!(pte1 & PTE1_S), !(pte1 & PTE1_NG));
+ dump_section(pmap, i);
+ } else if (pte1_is_link(pte1)) {
+ dump_link_ok = TRUE;
+ invalid_ok = FALSE;
+ pte2 = pte2_load(pmap_pt2tab_entry(pmap, va));
+ m = PHYS_TO_VM_PAGE(pte1_link_pa(pte1));
+ printf("0x%08X: Link 0x%08X, pt2tab: 0x%08X m: %p",
+ va, pte1, pte2, m);
+ if (is_pv_chunk_space(va)) {
+ printf(" - pv_chunk space");
+ if (dump_pv_chunk)
+ invalid_ok = TRUE;
+ else
+ dump_link_ok = FALSE;
+ }
+ else if (m != NULL)
+ printf(" w:%d w2:%u", m->ref_count,
+ pt2_wirecount_get(m, pte1_index(va)));
+ if (pte2 == 0)
+ printf(" !!! pt2tab entry is ZERO");
+ else if (pte2_pa(pte1) != pte2_pa(pte2))
+ printf(" !!! pt2tab entry is DIFFERENT - m: %p",
+ PHYS_TO_VM_PAGE(pte2_pa(pte2)));
+ printf("\n");
+ if (dump_link_ok)
+ dump_link(pmap, i, invalid_ok);
+ } else
+ printf("0x%08X: Invalid entry 0x%08X\n", va, pte1);
+ }
+}
+
+static void
+dump_pt2tab(pmap_t pmap)
+{
+ uint32_t i;
+ pt2_entry_t pte2;
+ vm_offset_t va;
+ vm_paddr_t pa;
+ vm_page_t m;
+
+ printf("PT2TAB:\n");
+ for (i = 0; i < PT2TAB_ENTRIES; i++) {
+ pte2 = pte2_load(&pmap->pm_pt2tab[i]);
+ if (!pte2_is_valid(pte2))
+ continue;
+ va = i << PT2TAB_SHIFT;
+ pa = pte2_pa(pte2);
+ m = PHYS_TO_VM_PAGE(pa);
+ printf(" 0x%08X: 0x%08X, TEX%d, s:%d, m:%p", va, pte2,
+ pte2_class(pte2), !!(pte2 & PTE2_S), m);
+ if (m != NULL)
+ printf(" , w: %d, f: 0x%04X pidx: %lld",
+ m->ref_count, m->flags, m->pindex);
+ printf("\n");
+ }
+}
+
+DB_SHOW_COMMAND(pmap_pt2tab, pmap_pt2tab_print)
+{
+ /* XXX convert args. */
+ pmap_t pmap = (pmap_t)addr;
+ pt1_entry_t pte1;
+ pt2_entry_t pte2;
+ vm_offset_t va;
+ uint32_t i, start;
+
+ if (have_addr) {
+ printf("supported only on current pmap\n");
+ return;
+ }
+
+ pmap = PCPU_GET(curpmap);
+ printf("curpmap: 0x%08X\n", (uint32_t)pmap);
+ printf("PT2MAP: 0x%08X\n", (uint32_t)PT2MAP);
+ printf("pt2tab: 0x%08X\n", (uint32_t)pmap->pm_pt2tab);
+
+ start = pte1_index((vm_offset_t)PT2MAP);
+ for (i = start; i < (start + NPT2_IN_PT2TAB); i++) {
+ pte1 = pte1_load(&pmap->pm_pt1[i]);
+ if (pte1 == 0)
+ continue;
+ va = i << PTE1_SHIFT;
+ if (pte1_is_section(pte1)) {
+ printf("0x%08X: Section 0x%08X, s:%d\n", va, pte1,
+ !!(pte1 & PTE1_S));
+ dump_section(pmap, i);
+ } else if (pte1_is_link(pte1)) {
+ pte2 = pte2_load(pmap_pt2tab_entry(pmap, va));
+ printf("0x%08X: Link 0x%08X, pt2tab: 0x%08X\n", va,
+ pte1, pte2);
+ if (pte2 == 0)
+ printf(" !!! pt2tab entry is ZERO\n");
+ } else
+ printf("0x%08X: Invalid entry 0x%08X\n", va, pte1);
+ }
+ dump_pt2tab(pmap);
+}
+#endif
diff --git a/sys/arm/arm/pmu.c b/sys/arm/arm/pmu.c
new file mode 100644
index 000000000000..544962e9ab28
--- /dev/null
+++ b/sys/arm/arm/pmu.c
@@ -0,0 +1,161 @@
+/*-
+ * Copyright (c) 2015 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Performance Monitoring Unit
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_hwpmc_hooks.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/pmc.h>
+#include <sys/pmckern.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include "pmu.h"
+
+/* CCNT */
+#if defined(__arm__) && (__ARM_ARCH > 6)
+int pmu_attched = 0;
+uint32_t ccnt_hi[MAXCPU];
+#endif
+
+#define PMU_OVSR_C 0x80000000 /* Cycle Counter */
+#define PMU_IESR_C 0x80000000 /* Cycle Counter */
+
+static int
+pmu_intr(void *arg)
+{
+#ifdef HWPMC_HOOKS
+ struct trapframe *tf;
+#endif
+ uint32_t r;
+#if defined(__arm__) && (__ARM_ARCH > 6)
+ u_int cpu;
+
+ cpu = PCPU_GET(cpuid);
+
+ r = cp15_pmovsr_get();
+ if (r & PMU_OVSR_C) {
+ atomic_add_32(&ccnt_hi[cpu], 1);
+ /* Clear the event. */
+ r &= ~PMU_OVSR_C;
+ cp15_pmovsr_set(PMU_OVSR_C);
+ }
+#else
+ r = 1;
+#endif
+
+#ifdef HWPMC_HOOKS
+ /* Only call into the HWPMC framework if we know there is work. */
+ if (r != 0 && pmc_intr) {
+ tf = arg;
+ (*pmc_intr)(tf);
+ }
+#endif
+
+ return (FILTER_HANDLED);
+}
+
+int
+pmu_attach(device_t dev)
+{
+ struct pmu_softc *sc;
+#if defined(__arm__) && (__ARM_ARCH > 6)
+ uint32_t iesr;
+#endif
+ int err, i;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ for (i = 0; i < MAX_RLEN; i++) {
+ if (sc->irq[i].res == NULL)
+ break;
+ err = bus_setup_intr(dev, sc->irq[i].res,
+ INTR_MPSAFE | INTR_TYPE_MISC, pmu_intr, NULL, NULL,
+ &sc->irq[i].ih);
+ if (err != 0) {
+ device_printf(dev,
+ "Unable to setup interrupt handler.\n");
+ goto fail;
+ }
+ if (sc->irq[i].cpuid != -1) {
+ err = bus_bind_intr(dev, sc->irq[i].res,
+ sc->irq[i].cpuid);
+ if (err != 0) {
+ device_printf(sc->dev,
+ "Unable to bind interrupt.\n");
+ goto fail;
+ }
+ }
+ }
+
+#if defined(__arm__) && (__ARM_ARCH > 6)
+ /* Initialize to 0. */
+ for (i = 0; i < MAXCPU; i++)
+ ccnt_hi[i] = 0;
+
+ /* Enable the interrupt to fire on overflow. */
+ iesr = cp15_pminten_get();
+ iesr |= PMU_IESR_C;
+ cp15_pminten_set(iesr);
+
+ /* Need this for getcyclecount() fast path. */
+ pmu_attched |= 1;
+#endif
+
+ return (0);
+
+fail:
+ for (i = 1; i < MAX_RLEN; i++) {
+ if (sc->irq[i].ih != NULL)
+ bus_teardown_intr(dev, sc->irq[i].res, sc->irq[i].ih);
+ if (sc->irq[i].res != NULL)
+ bus_release_resource(dev, SYS_RES_IRQ, i,
+ sc->irq[i].res);
+ }
+ return(err);
+}
+
diff --git a/sys/arm/arm/pmu.h b/sys/arm/arm/pmu.h
new file mode 100644
index 000000000000..bfad6d9da282
--- /dev/null
+++ b/sys/arm/arm/pmu.h
@@ -0,0 +1,52 @@
+/*-
+ * Copyright (c) 2015 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _ARM_PMU_H_
+#define _ARM_PMU_H_
+
+#define MAX_RLEN 8
+
+struct pmu_intr {
+ struct resource *res;
+ void *ih;
+ int cpuid;
+};
+
+struct pmu_softc {
+ device_t dev;
+ struct pmu_intr irq[MAX_RLEN];
+};
+
+
+int pmu_attach(device_t dev);
+
+#endif
diff --git a/sys/arm/arm/pmu_fdt.c b/sys/arm/arm/pmu_fdt.c
new file mode 100644
index 000000000000..2e03fc98bfe0
--- /dev/null
+++ b/sys/arm/arm/pmu_fdt.c
@@ -0,0 +1,239 @@
+/*-
+ * Copyright (c) 2015 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "pmu.h"
+
+static struct ofw_compat_data compat_data[] = {
+ {"arm,armv8-pmuv3", 1},
+ {"arm,cortex-a77-pmu", 1},
+ {"arm,cortex-a76-pmu", 1},
+ {"arm,cortex-a75-pmu", 1},
+ {"arm,cortex-a73-pmu", 1},
+ {"arm,cortex-a72-pmu", 1},
+ {"arm,cortex-a65-pmu", 1},
+ {"arm,cortex-a57-pmu", 1},
+ {"arm,cortex-a55-pmu", 1},
+ {"arm,cortex-a53-pmu", 1},
+ {"arm,cortex-a34-pmu", 1},
+
+ {"arm,cortex-a17-pmu", 1},
+ {"arm,cortex-a15-pmu", 1},
+ {"arm,cortex-a12-pmu", 1},
+ {"arm,cortex-a9-pmu", 1},
+ {"arm,cortex-a8-pmu", 1},
+ {"arm,cortex-a7-pmu", 1},
+ {"arm,cortex-a5-pmu", 1},
+ {"arm,arm11mpcore-pmu", 1},
+ {"arm,arm1176-pmu", 1},
+ {"arm,arm1136-pmu", 1},
+ {"qcom,krait-pmu", 1},
+ {NULL, 0}
+};
+
+static int
+pmu_fdt_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) {
+ device_set_desc(dev, "Performance Monitoring Unit");
+ return (BUS_PROBE_DEFAULT);
+ }
+
+ return (ENXIO);
+}
+
+static int
+pmu_parse_affinity(device_t dev, struct pmu_softc *sc, struct pmu_intr *irq,
+ phandle_t xref, uint32_t mpidr)
+{
+ struct pcpu *pcpu;
+ int i, err;
+
+
+ if (xref != 0) {
+ err = OF_getencprop(OF_node_from_xref(xref), "reg", &mpidr,
+ sizeof(mpidr));
+ if (err < 0) {
+ device_printf(dev, "missing 'reg' property\n");
+ return (ENXIO);
+ }
+ }
+
+ for (i = 0; i < MAXCPU; i++) {
+ pcpu = pcpu_find(i);
+ if (pcpu != NULL && pcpu->pc_mpidr == mpidr) {
+ irq->cpuid = i;
+ return (0);
+ }
+ }
+
+ device_printf(dev, "Cannot find CPU with MPIDR: 0x%08X\n", mpidr);
+ return (ENXIO);
+}
+
+static int
+pmu_parse_intr(device_t dev, struct pmu_softc *sc)
+{
+ bool has_affinity;
+ phandle_t node, *cpus;
+ int rid, err, ncpus, i;
+
+
+ node = ofw_bus_get_node(dev);
+ has_affinity = OF_hasprop(node, "interrupt-affinity");
+
+ for (i = 0; i < MAX_RLEN; i++)
+ sc->irq[i].cpuid = -1;
+
+ cpus = NULL;
+ if (has_affinity) {
+ ncpus = OF_getencprop_alloc_multi(node, "interrupt-affinity",
+ sizeof(*cpus), (void **)&cpus);
+ if (ncpus < 0) {
+ device_printf(dev,
+ "Cannot read interrupt affinity property\n");
+ return (ENXIO);
+ }
+ }
+
+ /* Process first interrupt */
+ rid = 0;
+ sc->irq[0].res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE | RF_SHAREABLE);
+
+ if (sc->irq[0].res == NULL) {
+ device_printf(dev, "Cannot get interrupt\n");
+ err = ENXIO;
+ goto done;
+ }
+
+ /* Check if PMU have one per-CPU interrupt */
+ if (intr_is_per_cpu(sc->irq[0].res)) {
+ if (has_affinity) {
+ device_printf(dev,
+ "Per CPU interupt have declared affinity\n");
+ err = ENXIO;
+ goto done;
+ }
+ return (0);
+ }
+
+ /*
+ * PMU with set of generic interrupts (one per core)
+ * Each one must be binded to exact core.
+ */
+ err = pmu_parse_affinity(dev, sc, sc->irq + 0,
+ has_affinity ? cpus[0] : 0, 0);
+ if (err != 0) {
+ device_printf(dev, "Cannot parse affinity for CPUid: 0\n");
+ goto done;
+ }
+
+ for (i = 1; i < MAX_RLEN; i++) {
+ rid = i;
+ sc->irq[i].res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
+ &rid, RF_ACTIVE | RF_SHAREABLE);
+ if (sc->irq[i].res == NULL)
+ break;
+
+ if (intr_is_per_cpu(sc->irq[i].res))
+ {
+ device_printf(dev, "Unexpected per CPU interupt\n");
+ err = ENXIO;
+ goto done;
+ }
+
+ if (has_affinity && i >= ncpus) {
+ device_printf(dev, "Missing value in interrupt "
+ "affinity property\n");
+ err = ENXIO;
+ goto done;
+ }
+
+ err = pmu_parse_affinity(dev, sc, sc->irq + i,
+ has_affinity ? cpus[i] : 0, i);
+ if (err != 0) {
+ device_printf(dev,
+ "Cannot parse affinity for CPUid: %d.\n", i);
+ goto done;
+ }
+ }
+ err = 0;
+done:
+ OF_prop_free(cpus);
+ return (err);
+}
+
+static int
+pmu_fdt_attach(device_t dev)
+{
+ struct pmu_softc *sc;
+ int err;
+
+ sc = device_get_softc(dev);
+ err = pmu_parse_intr(dev, sc);
+ if (err != 0)
+ return (err);
+
+ return (pmu_attach(dev));
+}
+
+static device_method_t pmu_fdt_methods[] = {
+ DEVMETHOD(device_probe, pmu_fdt_probe),
+ DEVMETHOD(device_attach, pmu_fdt_attach),
+ { 0, 0 }
+};
+
+static driver_t pmu_fdt_driver = {
+ "pmu",
+ pmu_fdt_methods,
+ sizeof(struct pmu_softc),
+};
+
+static devclass_t pmu_fdt_devclass;
+
+DRIVER_MODULE(pmu, simplebus, pmu_fdt_driver, pmu_fdt_devclass, 0, 0);
diff --git a/sys/arm/arm/ptrace_machdep.c b/sys/arm/arm/ptrace_machdep.c
new file mode 100644
index 000000000000..563f962dc473
--- /dev/null
+++ b/sys/arm/arm/ptrace_machdep.c
@@ -0,0 +1,62 @@
+/*-
+ * Copyright (c) 2017 John Baldwin <jhb@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/proc.h>
+#include <sys/ptrace.h>
+#ifdef VFP
+#include <machine/vfp.h>
+#endif
+
+int
+cpu_ptrace(struct thread *td, int req, void *addr, int data)
+{
+#ifdef VFP
+ mcontext_vfp_t vfp;
+#endif
+ int error;
+
+ switch (req) {
+#ifdef VFP
+ case PT_GETVFPREGS:
+ get_vfpcontext(td, &vfp);
+ error = copyout(&vfp, addr, sizeof(vfp));
+ break;
+ case PT_SETVFPREGS:
+ error = copyin(addr, &vfp, sizeof(vfp));
+ if (error == 0)
+ set_vfpcontext(td, &vfp);
+ break;
+#endif
+ default:
+ error = EINVAL;
+ }
+
+ return (error);
+}
diff --git a/sys/arm/arm/sc_machdep.c b/sys/arm/arm/sc_machdep.c
new file mode 100644
index 000000000000..4e2fd65bce14
--- /dev/null
+++ b/sys/arm/arm/sc_machdep.c
@@ -0,0 +1,88 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2003 Jake Burkholder.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/cons.h>
+#include <sys/kbio.h>
+#include <sys/consio.h>
+#include <sys/sysctl.h>
+
+#include <dev/syscons/syscons.h>
+
+static sc_softc_t sc_softcs[8];
+
+int
+sc_get_cons_priority(int *unit, int *flags)
+{
+
+ *unit = 0;
+ *flags = 0;
+ return (CN_INTERNAL);
+}
+
+int
+sc_max_unit(void)
+{
+ return (1);
+}
+
+sc_softc_t *
+sc_get_softc(int unit, int flags)
+{
+ sc_softc_t *sc;
+
+ if (unit < 0)
+ return (NULL);
+ sc = &sc_softcs[unit];
+ sc->unit = unit;
+ if ((sc->flags & SC_INIT_DONE) == 0) {
+ sc->adapter = -1;
+ sc->cursor_char = SC_CURSOR_CHAR;
+ sc->mouse_char = SC_MOUSE_CHAR;
+ }
+ return (sc);
+}
+
+void
+sc_get_bios_values(bios_values_t *values)
+{
+}
+
+int
+sc_tone(int hz)
+{
+ return (0);
+}
diff --git a/sys/arm/arm/setcpsr.S b/sys/arm/arm/setcpsr.S
new file mode 100644
index 000000000000..e71e5a20adaf
--- /dev/null
+++ b/sys/arm/arm/setcpsr.S
@@ -0,0 +1,82 @@
+/* $NetBSD: setcpsr.S,v 1.2 2002/08/15 01:37:02 briggs Exp $ */
+
+/*-
+ * Copyright (c) 1994 Mark Brinicombe.
+ * Copyright (c) 1994 Brini.
+ * All rights reserved.
+ *
+ * This code is derived from software written for Brini by Mark Brinicombe
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Brini.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * RiscBSD kernel project
+ *
+ * setcpsr.S
+ *
+ * Miscellaneous routines to play with the CPSR register
+ *
+ * Eventually this routine can be inline assembly.
+ *
+ * Created : 12/09/94
+ *
+ * Based of kate/display/setcpsr.s
+ *
+ */
+
+#include <machine/asm.h>
+__FBSDID("$FreeBSD$");
+
+/* Sets and clears bits in the CPSR register
+ *
+ * r0 - bic mask
+ * r1 - eor mask
+ */
+
+ENTRY_NP(SetCPSR)
+ mrs r3, cpsr /* Set the CPSR */
+ bic r2, r3, r0
+ eor r2, r2, r1
+ msr cpsr_fsxc, r2
+
+ mov r0, r3 /* Return the old CPSR */
+
+ RET
+END(SetCPSR)
+
+
+/* Gets the CPSR register
+ *
+ * Returns the CPSR in r0
+ */
+
+ENTRY_NP(GetCPSR)
+ mrs r0, cpsr /* Get the CPSR */
+
+ RET
+END(GetCPSR)
+
diff --git a/sys/arm/arm/setstack.s b/sys/arm/arm/setstack.s
new file mode 100644
index 000000000000..f66b93225649
--- /dev/null
+++ b/sys/arm/arm/setstack.s
@@ -0,0 +1,95 @@
+/* $NetBSD: setstack.S,v 1.1 2001/07/28 13:28:03 chris Exp $ */
+
+/*-
+ * Copyright (c) 1994 Mark Brinicombe.
+ * Copyright (c) 1994 Brini.
+ * All rights reserved.
+ *
+ * This code is derived from software written for Brini by Mark Brinicombe
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Brini.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * RiscBSD kernel project
+ *
+ * setstack.S
+ *
+ * Miscellaneous routine to play with the stack pointer in different CPU modes
+ *
+ * Eventually this routine can be inline assembly.
+ *
+ * Created : 17/09/94
+ *
+ * Based of kate/display/setstack.s
+ *
+ */
+
+#include <machine/asm.h>
+__FBSDID("$FreeBSD$");
+
+#include <machine/armreg.h>
+
+/* To set the stack pointer for a particular mode we must switch
+ * to that mode update the banked r13 and then switch back.
+ * This routine provides an easy way of doing this for any mode
+ *
+ * r0 = CPU mode
+ * r1 = stackptr
+ */
+
+ENTRY(set_stackptr)
+ mrs r3, cpsr /* Switch to the appropriate mode */
+ bic r2, r3, #(PSR_MODE)
+ orr r2, r2, r0
+ msr cpsr_fsxc, r2
+
+ mov sp, r1 /* Set the stack pointer */
+
+ msr cpsr_fsxc, r3 /* Restore the old mode */
+
+ mov pc, lr /* Exit */
+END(set_stackptr)
+/* To get the stack pointer for a particular mode we must switch
+ * to that mode copy the banked r13 and then switch back.
+ * This routine provides an easy way of doing this for any mode
+ *
+ * r0 = CPU mode
+ */
+
+ENTRY(get_stackptr)
+ mrs r3, cpsr /* Switch to the appropriate mode */
+ bic r2, r3, #(PSR_MODE)
+ orr r2, r2, r0
+ msr cpsr_fsxc, r2
+
+ mov r0, sp /* Set the stack pointer */
+
+ msr cpsr_fsxc, r3 /* Restore the old mode */
+
+ mov pc, lr /* Exit */
+END(get_stackptr)
+/* End of setstack.S */
diff --git a/sys/arm/arm/stack_machdep.c b/sys/arm/arm/stack_machdep.c
new file mode 100644
index 000000000000..89ea76a96fc5
--- /dev/null
+++ b/sys/arm/arm/stack_machdep.c
@@ -0,0 +1,88 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 Ian Lepore <ian@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/stack.h>
+
+#include <machine/pcb.h>
+#include <machine/stack.h>
+
+static void
+stack_capture(struct stack *st, struct unwind_state *state)
+{
+
+ stack_zero(st);
+ while (unwind_stack_one(state, 0) == 0) {
+ if (stack_put(st, state->registers[PC]) == -1)
+ break;
+ }
+}
+
+void
+stack_save(struct stack *st)
+{
+ struct unwind_state state;
+ uint32_t sp;
+
+ /* Read the stack pointer */
+ __asm __volatile("mov %0, sp" : "=&r" (sp));
+
+ state.registers[FP] = (uint32_t)__builtin_frame_address(0);
+ state.registers[SP] = sp;
+ state.registers[LR] = (uint32_t)__builtin_return_address(0);
+ state.registers[PC] = (uint32_t)stack_save;
+
+ stack_capture(st, &state);
+}
+
+int
+stack_save_td(struct stack *st, struct thread *td)
+{
+ struct unwind_state state;
+
+ THREAD_LOCK_ASSERT(td, MA_OWNED);
+ KASSERT(!TD_IS_SWAPPED(td),
+ ("stack_save_td: thread %p is swapped", td));
+
+ if (TD_IS_RUNNING(td))
+ return (EOPNOTSUPP);
+
+ state.registers[FP] = td->td_pcb->pcb_regs.sf_r11;
+ state.registers[SP] = td->td_pcb->pcb_regs.sf_sp;
+ state.registers[LR] = td->td_pcb->pcb_regs.sf_lr;
+ state.registers[PC] = td->td_pcb->pcb_regs.sf_pc;
+
+ stack_capture(st, &state);
+ return (0);
+}
diff --git a/sys/arm/arm/stdatomic.c b/sys/arm/arm/stdatomic.c
new file mode 100644
index 000000000000..bf7ed20c117d
--- /dev/null
+++ b/sys/arm/arm/stdatomic.c
@@ -0,0 +1,429 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Ed Schouten <ed@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/stdatomic.h>
+#include <sys/types.h>
+
+#include <machine/atomic.h>
+#include <machine/cpufunc.h>
+#include <machine/sysarch.h>
+
+/*
+ * Executing statements with interrupts disabled.
+ */
+
+#if defined(_KERNEL) && !defined(SMP)
+#define WITHOUT_INTERRUPTS(s) do { \
+ register_t regs; \
+ \
+ regs = intr_disable(); \
+ do s while (0); \
+ intr_restore(regs); \
+} while (0)
+#endif /* _KERNEL && !SMP */
+
+/*
+ * Memory barriers.
+ *
+ * It turns out __sync_synchronize() does not emit any code when used
+ * with GCC 4.2. Implement our own version that does work reliably.
+ *
+ * Although __sync_lock_test_and_set() should only perform an acquire
+ * barrier, make it do a full barrier like the other functions. This
+ * should make <stdatomic.h>'s atomic_exchange_explicit() work reliably.
+ */
+
+#if defined(_KERNEL) && !defined(SMP)
+static inline void
+do_sync(void)
+{
+
+ __asm volatile ("" : : : "memory");
+}
+#else
+static inline void
+do_sync(void)
+{
+
+ dmb();
+}
+#endif
+
+
+#if defined(__SYNC_ATOMICS) || defined(EMIT_SYNC_ATOMICS)
+
+#ifdef __clang__
+#pragma redefine_extname __sync_lock_test_and_set_1_c __sync_lock_test_and_set_1
+#pragma redefine_extname __sync_lock_test_and_set_2_c __sync_lock_test_and_set_2
+#pragma redefine_extname __sync_lock_test_and_set_4_c __sync_lock_test_and_set_4
+#pragma redefine_extname __sync_val_compare_and_swap_1_c __sync_val_compare_and_swap_1
+#pragma redefine_extname __sync_val_compare_and_swap_2_c __sync_val_compare_and_swap_2
+#pragma redefine_extname __sync_val_compare_and_swap_4_c __sync_val_compare_and_swap_4
+#pragma redefine_extname __sync_fetch_and_add_1_c __sync_fetch_and_add_1
+#pragma redefine_extname __sync_fetch_and_add_2_c __sync_fetch_and_add_2
+#pragma redefine_extname __sync_fetch_and_add_4_c __sync_fetch_and_add_4
+#pragma redefine_extname __sync_fetch_and_and_1_c __sync_fetch_and_and_1
+#pragma redefine_extname __sync_fetch_and_and_2_c __sync_fetch_and_and_2
+#pragma redefine_extname __sync_fetch_and_and_4_c __sync_fetch_and_and_4
+#pragma redefine_extname __sync_fetch_and_or_1_c __sync_fetch_and_or_1
+#pragma redefine_extname __sync_fetch_and_or_2_c __sync_fetch_and_or_2
+#pragma redefine_extname __sync_fetch_and_or_4_c __sync_fetch_and_or_4
+#pragma redefine_extname __sync_fetch_and_xor_1_c __sync_fetch_and_xor_1
+#pragma redefine_extname __sync_fetch_and_xor_2_c __sync_fetch_and_xor_2
+#pragma redefine_extname __sync_fetch_and_xor_4_c __sync_fetch_and_xor_4
+#pragma redefine_extname __sync_fetch_and_sub_1_c __sync_fetch_and_sub_1
+#pragma redefine_extname __sync_fetch_and_sub_2_c __sync_fetch_and_sub_2
+#pragma redefine_extname __sync_fetch_and_sub_4_c __sync_fetch_and_sub_4
+#endif
+
+/*
+ * Old __sync_* API.
+ */
+
+
+/* Implementations for old GCC versions, lacking support for atomics. */
+
+typedef union {
+ uint8_t v8[4];
+ uint32_t v32;
+} reg_t;
+
+/*
+ * Given a memory address pointing to an 8-bit or 16-bit integer, return
+ * the address of the 32-bit word containing it.
+ */
+
+static inline uint32_t *
+round_to_word(void *ptr)
+{
+
+ return ((uint32_t *)((intptr_t)ptr & ~3));
+}
+
+/*
+ * Utility functions for loading and storing 8-bit and 16-bit integers
+ * in 32-bit words at an offset corresponding with the location of the
+ * atomic variable.
+ */
+
+static inline void
+put_1(reg_t *r, const uint8_t *offset_ptr, uint8_t val)
+{
+ size_t offset;
+
+ offset = (intptr_t)offset_ptr & 3;
+ r->v8[offset] = val;
+}
+
+static inline uint8_t
+get_1(const reg_t *r, const uint8_t *offset_ptr)
+{
+ size_t offset;
+
+ offset = (intptr_t)offset_ptr & 3;
+ return (r->v8[offset]);
+}
+
+static inline void
+put_2(reg_t *r, const uint16_t *offset_ptr, uint16_t val)
+{
+ size_t offset;
+ union {
+ uint16_t in;
+ uint8_t out[2];
+ } bytes;
+
+ offset = (intptr_t)offset_ptr & 3;
+ bytes.in = val;
+ r->v8[offset] = bytes.out[0];
+ r->v8[offset + 1] = bytes.out[1];
+}
+
+static inline uint16_t
+get_2(const reg_t *r, const uint16_t *offset_ptr)
+{
+ size_t offset;
+ union {
+ uint8_t in[2];
+ uint16_t out;
+ } bytes;
+
+ offset = (intptr_t)offset_ptr & 3;
+ bytes.in[0] = r->v8[offset];
+ bytes.in[1] = r->v8[offset + 1];
+ return (bytes.out);
+}
+
+/*
+ * 8-bit and 16-bit routines.
+ *
+ * These operations are not natively supported by the CPU, so we use
+ * some shifting and bitmasking on top of the 32-bit instructions.
+ */
+
+#define EMIT_LOCK_TEST_AND_SET_N(N, uintN_t) \
+uintN_t \
+__sync_lock_test_and_set_##N##_c(uintN_t *mem, uintN_t val) \
+{ \
+ uint32_t *mem32; \
+ reg_t val32, negmask, old; \
+ uint32_t temp1, temp2; \
+ \
+ mem32 = round_to_word(mem); \
+ val32.v32 = 0x00000000; \
+ put_##N(&val32, mem, val); \
+ negmask.v32 = 0xffffffff; \
+ put_##N(&negmask, mem, 0); \
+ \
+ do_sync(); \
+ __asm volatile ( \
+ "1:" \
+ "\tldrex %0, %6\n" /* Load old value. */ \
+ "\tand %2, %5, %0\n" /* Remove the old value. */ \
+ "\torr %2, %2, %4\n" /* Put in the new value. */ \
+ "\tstrex %3, %2, %1\n" /* Attempt to store. */ \
+ "\tcmp %3, #0\n" /* Did it succeed? */ \
+ "\tbne 1b\n" /* Spin if failed. */ \
+ : "=&r" (old.v32), "=m" (*mem32), "=&r" (temp1), \
+ "=&r" (temp2) \
+ : "r" (val32.v32), "r" (negmask.v32), "m" (*mem32)); \
+ return (get_##N(&old, mem)); \
+}
+
+EMIT_LOCK_TEST_AND_SET_N(1, uint8_t)
+EMIT_LOCK_TEST_AND_SET_N(2, uint16_t)
+
+#define EMIT_VAL_COMPARE_AND_SWAP_N(N, uintN_t) \
+uintN_t \
+__sync_val_compare_and_swap_##N##_c(uintN_t *mem, uintN_t expected, \
+ uintN_t desired) \
+{ \
+ uint32_t *mem32; \
+ reg_t expected32, desired32, posmask, old; \
+ uint32_t negmask, temp1, temp2; \
+ \
+ mem32 = round_to_word(mem); \
+ expected32.v32 = 0x00000000; \
+ put_##N(&expected32, mem, expected); \
+ desired32.v32 = 0x00000000; \
+ put_##N(&desired32, mem, desired); \
+ posmask.v32 = 0x00000000; \
+ put_##N(&posmask, mem, ~0); \
+ negmask = ~posmask.v32; \
+ \
+ do_sync(); \
+ __asm volatile ( \
+ "1:" \
+ "\tldrex %0, %8\n" /* Load old value. */ \
+ "\tand %2, %6, %0\n" /* Isolate the old value. */ \
+ "\tcmp %2, %4\n" /* Compare to expected value. */\
+ "\tbne 2f\n" /* Values are unequal. */ \
+ "\tand %2, %7, %0\n" /* Remove the old value. */ \
+ "\torr %2, %5\n" /* Put in the new value. */ \
+ "\tstrex %3, %2, %1\n" /* Attempt to store. */ \
+ "\tcmp %3, #0\n" /* Did it succeed? */ \
+ "\tbne 1b\n" /* Spin if failed. */ \
+ "2:" \
+ : "=&r" (old), "=m" (*mem32), "=&r" (temp1), \
+ "=&r" (temp2) \
+ : "r" (expected32.v32), "r" (desired32.v32), \
+ "r" (posmask.v32), "r" (negmask), "m" (*mem32)); \
+ return (get_##N(&old, mem)); \
+}
+
+EMIT_VAL_COMPARE_AND_SWAP_N(1, uint8_t)
+EMIT_VAL_COMPARE_AND_SWAP_N(2, uint16_t)
+
+#define EMIT_ARITHMETIC_FETCH_AND_OP_N(N, uintN_t, name, op) \
+uintN_t \
+__sync_##name##_##N##_c(uintN_t *mem, uintN_t val) \
+{ \
+ uint32_t *mem32; \
+ reg_t val32, posmask, old; \
+ uint32_t negmask, temp1, temp2; \
+ \
+ mem32 = round_to_word(mem); \
+ val32.v32 = 0x00000000; \
+ put_##N(&val32, mem, val); \
+ posmask.v32 = 0x00000000; \
+ put_##N(&posmask, mem, ~0); \
+ negmask = ~posmask.v32; \
+ \
+ do_sync(); \
+ __asm volatile ( \
+ "1:" \
+ "\tldrex %0, %7\n" /* Load old value. */ \
+ "\t"op" %2, %0, %4\n" /* Calculate new value. */ \
+ "\tand %2, %5\n" /* Isolate the new value. */ \
+ "\tand %3, %6, %0\n" /* Remove the old value. */ \
+ "\torr %2, %2, %3\n" /* Put in the new value. */ \
+ "\tstrex %3, %2, %1\n" /* Attempt to store. */ \
+ "\tcmp %3, #0\n" /* Did it succeed? */ \
+ "\tbne 1b\n" /* Spin if failed. */ \
+ : "=&r" (old.v32), "=m" (*mem32), "=&r" (temp1), \
+ "=&r" (temp2) \
+ : "r" (val32.v32), "r" (posmask.v32), "r" (negmask), \
+ "m" (*mem32)); \
+ return (get_##N(&old, mem)); \
+}
+
+EMIT_ARITHMETIC_FETCH_AND_OP_N(1, uint8_t, fetch_and_add, "add")
+EMIT_ARITHMETIC_FETCH_AND_OP_N(1, uint8_t, fetch_and_sub, "sub")
+EMIT_ARITHMETIC_FETCH_AND_OP_N(2, uint16_t, fetch_and_add, "add")
+EMIT_ARITHMETIC_FETCH_AND_OP_N(2, uint16_t, fetch_and_sub, "sub")
+
+#define EMIT_BITWISE_FETCH_AND_OP_N(N, uintN_t, name, op, idempotence) \
+uintN_t \
+__sync_##name##_##N##_c(uintN_t *mem, uintN_t val) \
+{ \
+ uint32_t *mem32; \
+ reg_t val32, old; \
+ uint32_t temp1, temp2; \
+ \
+ mem32 = round_to_word(mem); \
+ val32.v32 = idempotence ? 0xffffffff : 0x00000000; \
+ put_##N(&val32, mem, val); \
+ \
+ do_sync(); \
+ __asm volatile ( \
+ "1:" \
+ "\tldrex %0, %5\n" /* Load old value. */ \
+ "\t"op" %2, %4, %0\n" /* Calculate new value. */ \
+ "\tstrex %3, %2, %1\n" /* Attempt to store. */ \
+ "\tcmp %3, #0\n" /* Did it succeed? */ \
+ "\tbne 1b\n" /* Spin if failed. */ \
+ : "=&r" (old.v32), "=m" (*mem32), "=&r" (temp1), \
+ "=&r" (temp2) \
+ : "r" (val32.v32), "m" (*mem32)); \
+ return (get_##N(&old, mem)); \
+}
+
+EMIT_BITWISE_FETCH_AND_OP_N(1, uint8_t, fetch_and_and, "and", 1)
+EMIT_BITWISE_FETCH_AND_OP_N(1, uint8_t, fetch_and_or, "orr", 0)
+EMIT_BITWISE_FETCH_AND_OP_N(1, uint8_t, fetch_and_xor, "eor", 0)
+EMIT_BITWISE_FETCH_AND_OP_N(2, uint16_t, fetch_and_and, "and", 1)
+EMIT_BITWISE_FETCH_AND_OP_N(2, uint16_t, fetch_and_or, "orr", 0)
+EMIT_BITWISE_FETCH_AND_OP_N(2, uint16_t, fetch_and_xor, "eor", 0)
+
+/*
+ * 32-bit routines.
+ */
+
+uint32_t
+__sync_lock_test_and_set_4_c(uint32_t *mem, uint32_t val)
+{
+ uint32_t old, temp;
+
+ do_sync();
+ __asm volatile (
+ "1:"
+ "\tldrex %0, %4\n" /* Load old value. */
+ "\tstrex %2, %3, %1\n" /* Attempt to store. */
+ "\tcmp %2, #0\n" /* Did it succeed? */
+ "\tbne 1b\n" /* Spin if failed. */
+ : "=&r" (old), "=m" (*mem), "=&r" (temp)
+ : "r" (val), "m" (*mem));
+ return (old);
+}
+
+uint32_t
+__sync_val_compare_and_swap_4_c(uint32_t *mem, uint32_t expected,
+ uint32_t desired)
+{
+ uint32_t old, temp;
+
+ do_sync();
+ __asm volatile (
+ "1:"
+ "\tldrex %0, %5\n" /* Load old value. */
+ "\tcmp %0, %3\n" /* Compare to expected value. */
+ "\tbne 2f\n" /* Values are unequal. */
+ "\tstrex %2, %4, %1\n" /* Attempt to store. */
+ "\tcmp %2, #0\n" /* Did it succeed? */
+ "\tbne 1b\n" /* Spin if failed. */
+ "2:"
+ : "=&r" (old), "=m" (*mem), "=&r" (temp)
+ : "r" (expected), "r" (desired), "m" (*mem));
+ return (old);
+}
+
+#define EMIT_FETCH_AND_OP_4(name, op) \
+uint32_t \
+__sync_##name##_4##_c(uint32_t *mem, uint32_t val) \
+{ \
+ uint32_t old, temp1, temp2; \
+ \
+ do_sync(); \
+ __asm volatile ( \
+ "1:" \
+ "\tldrex %0, %5\n" /* Load old value. */ \
+ "\t"op" %2, %0, %4\n" /* Calculate new value. */ \
+ "\tstrex %3, %2, %1\n" /* Attempt to store. */ \
+ "\tcmp %3, #0\n" /* Did it succeed? */ \
+ "\tbne 1b\n" /* Spin if failed. */ \
+ : "=&r" (old), "=m" (*mem), "=&r" (temp1), \
+ "=&r" (temp2) \
+ : "r" (val), "m" (*mem)); \
+ return (old); \
+}
+
+EMIT_FETCH_AND_OP_4(fetch_and_add, "add")
+EMIT_FETCH_AND_OP_4(fetch_and_and, "and")
+EMIT_FETCH_AND_OP_4(fetch_and_or, "orr")
+EMIT_FETCH_AND_OP_4(fetch_and_sub, "sub")
+EMIT_FETCH_AND_OP_4(fetch_and_xor, "eor")
+
+#ifndef __clang__
+__strong_reference(__sync_lock_test_and_set_1_c, __sync_lock_test_and_set_1);
+__strong_reference(__sync_lock_test_and_set_2_c, __sync_lock_test_and_set_2);
+__strong_reference(__sync_lock_test_and_set_4_c, __sync_lock_test_and_set_4);
+__strong_reference(__sync_val_compare_and_swap_1_c, __sync_val_compare_and_swap_1);
+__strong_reference(__sync_val_compare_and_swap_2_c, __sync_val_compare_and_swap_2);
+__strong_reference(__sync_val_compare_and_swap_4_c, __sync_val_compare_and_swap_4);
+__strong_reference(__sync_fetch_and_add_1_c, __sync_fetch_and_add_1);
+__strong_reference(__sync_fetch_and_add_2_c, __sync_fetch_and_add_2);
+__strong_reference(__sync_fetch_and_add_4_c, __sync_fetch_and_add_4);
+__strong_reference(__sync_fetch_and_and_1_c, __sync_fetch_and_and_1);
+__strong_reference(__sync_fetch_and_and_2_c, __sync_fetch_and_and_2);
+__strong_reference(__sync_fetch_and_and_4_c, __sync_fetch_and_and_4);
+__strong_reference(__sync_fetch_and_sub_1_c, __sync_fetch_and_sub_1);
+__strong_reference(__sync_fetch_and_sub_2_c, __sync_fetch_and_sub_2);
+__strong_reference(__sync_fetch_and_sub_4_c, __sync_fetch_and_sub_4);
+__strong_reference(__sync_fetch_and_or_1_c, __sync_fetch_and_or_1);
+__strong_reference(__sync_fetch_and_or_2_c, __sync_fetch_and_or_2);
+__strong_reference(__sync_fetch_and_or_4_c, __sync_fetch_and_or_4);
+__strong_reference(__sync_fetch_and_xor_1_c, __sync_fetch_and_xor_1);
+__strong_reference(__sync_fetch_and_xor_2_c, __sync_fetch_and_xor_2);
+__strong_reference(__sync_fetch_and_xor_4_c, __sync_fetch_and_xor_4);
+#endif
+
+#endif /* __SYNC_ATOMICS */
diff --git a/sys/arm/arm/support.S b/sys/arm/arm/support.S
new file mode 100644
index 000000000000..a3ee3c3b93ba
--- /dev/null
+++ b/sys/arm/arm/support.S
@@ -0,0 +1,2122 @@
+/*-
+ * Copyright (c) 2004 Olivier Houchard
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * Copyright 2003 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Steve C. Woodford for Wasabi Systems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the NetBSD Project by
+ * Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 1997 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Neil A. Carson and Mark Brinicombe
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <machine/asm.h>
+__FBSDID("$FreeBSD$");
+
+#include "assym.inc"
+
+ .syntax unified
+
+.L_arm_memcpy:
+ .word _C_LABEL(_arm_memcpy)
+.L_arm_bzero:
+ .word _C_LABEL(_arm_bzero)
+.L_min_memcpy_size:
+ .word _C_LABEL(_min_memcpy_size)
+.L_min_bzero_size:
+ .word _C_LABEL(_min_bzero_size)
+/*
+ * memset: Sets a block of memory to the specified value
+ *
+ * On entry:
+ * r0 - dest address
+ * r1 - byte to write
+ * r2 - number of bytes to write
+ *
+ * On exit:
+ * r0 - dest address
+ */
+/* LINTSTUB: Func: void bzero(void *, size_t) */
+ENTRY(bzero)
+ ldr r3, .L_arm_bzero
+ ldr r3, [r3]
+ cmp r3, #0
+ beq .Lnormal0
+ ldr r2, .L_min_bzero_size
+ ldr r2, [r2]
+ cmp r1, r2
+ blt .Lnormal0
+ stmfd sp!, {r0, r1, lr}
+ mov r2, #0
+ mov lr, pc
+ mov pc, r3
+ cmp r0, #0
+ ldmfd sp!, {r0, r1, lr}
+ RETeq
+.Lnormal0:
+ mov r3, #0x00
+ b do_memset
+END(bzero)
+/* LINTSTUB: Func: void *memset(void *, int, size_t) */
+ENTRY(memset)
+ and r3, r1, #0xff /* We deal with bytes */
+ mov r1, r2
+do_memset:
+ cmp r1, #0x04 /* Do we have less than 4 bytes */
+ mov ip, r0
+ blt .Lmemset_lessthanfour
+
+ /* Ok first we will word align the address */
+ ands r2, ip, #0x03 /* Get the bottom two bits */
+ bne .Lmemset_wordunaligned /* The address is not word aligned */
+
+ /* We are now word aligned */
+.Lmemset_wordaligned:
+ orr r3, r3, r3, lsl #8 /* Extend value to 16-bits */
+ tst ip, #0x04 /* Quad-align for armv5e */
+ orr r3, r3, r3, lsl #16 /* Extend value to 32-bits */
+ subne r1, r1, #0x04 /* Quad-align if necessary */
+ strne r3, [ip], #0x04
+ cmp r1, #0x10
+ blt .Lmemset_loop4 /* If less than 16 then use words */
+ mov r2, r3 /* Duplicate data */
+ cmp r1, #0x80 /* If < 128 then skip the big loop */
+ blt .Lmemset_loop32
+
+ /* Do 128 bytes at a time */
+.Lmemset_loop128:
+ subs r1, r1, #0x80
+ strdge r2, [ip], #0x08
+ strdge r2, [ip], #0x08
+ strdge r2, [ip], #0x08
+ strdge r2, [ip], #0x08
+ strdge r2, [ip], #0x08
+ strdge r2, [ip], #0x08
+ strdge r2, [ip], #0x08
+ strdge r2, [ip], #0x08
+ strdge r2, [ip], #0x08
+ strdge r2, [ip], #0x08
+ strdge r2, [ip], #0x08
+ strdge r2, [ip], #0x08
+ strdge r2, [ip], #0x08
+ strdge r2, [ip], #0x08
+ strdge r2, [ip], #0x08
+ strdge r2, [ip], #0x08
+ bgt .Lmemset_loop128
+ RETeq /* Zero length so just exit */
+
+ add r1, r1, #0x80 /* Adjust for extra sub */
+
+ /* Do 32 bytes at a time */
+.Lmemset_loop32:
+ subs r1, r1, #0x20
+ strdge r2, [ip], #0x08
+ strdge r2, [ip], #0x08
+ strdge r2, [ip], #0x08
+ strdge r2, [ip], #0x08
+ bgt .Lmemset_loop32
+ RETeq /* Zero length so just exit */
+
+ adds r1, r1, #0x10 /* Partially adjust for extra sub */
+
+ /* Deal with 16 bytes or more */
+ strdge r2, [ip], #0x08
+ strdge r2, [ip], #0x08
+ RETeq /* Zero length so just exit */
+
+ addlt r1, r1, #0x10 /* Possibly adjust for extra sub */
+
+ /* We have at least 4 bytes so copy as words */
+.Lmemset_loop4:
+ subs r1, r1, #0x04
+ strge r3, [ip], #0x04
+ bgt .Lmemset_loop4
+ RETeq /* Zero length so just exit */
+
+ /* Compensate for 64-bit alignment check */
+ adds r1, r1, #0x04
+ RETeq
+ cmp r1, #2
+
+ strb r3, [ip], #0x01 /* Set 1 byte */
+ strbge r3, [ip], #0x01 /* Set another byte */
+ strbgt r3, [ip] /* and a third */
+ RET /* Exit */
+
+.Lmemset_wordunaligned:
+ rsb r2, r2, #0x004
+ strb r3, [ip], #0x01 /* Set 1 byte */
+ cmp r2, #0x02
+ strbge r3, [ip], #0x01 /* Set another byte */
+ sub r1, r1, r2
+ strbgt r3, [ip], #0x01 /* and a third */
+ cmp r1, #0x04 /* More than 4 bytes left? */
+ bge .Lmemset_wordaligned /* Yup */
+
+.Lmemset_lessthanfour:
+ cmp r1, #0x00
+ RETeq /* Zero length so exit */
+ strb r3, [ip], #0x01 /* Set 1 byte */
+ cmp r1, #0x02
+ strbge r3, [ip], #0x01 /* Set another byte */
+ strbgt r3, [ip] /* and a third */
+ RET /* Exit */
+EEND(memset)
+END(bzero)
+
+ENTRY(bcmp)
+ mov ip, r0
+ cmp r2, #0x06
+ beq .Lmemcmp_6bytes
+ mov r0, #0x00
+
+ /* Are both addresses aligned the same way? */
+ cmp r2, #0x00
+ eorsne r3, ip, r1
+ RETeq /* len == 0, or same addresses! */
+ tst r3, #0x03
+ subne r2, r2, #0x01
+ bne .Lmemcmp_bytewise2 /* Badly aligned. Do it the slow way */
+
+ /* Word-align the addresses, if necessary */
+ sub r3, r1, #0x05
+ ands r3, r3, #0x03
+ add r3, r3, r3, lsl #1
+ addne pc, pc, r3, lsl #3
+ nop
+
+ /* Compare up to 3 bytes */
+ ldrb r0, [ip], #0x01
+ ldrb r3, [r1], #0x01
+ subs r0, r0, r3
+ RETne
+ subs r2, r2, #0x01
+ RETeq
+
+ /* Compare up to 2 bytes */
+ ldrb r0, [ip], #0x01
+ ldrb r3, [r1], #0x01
+ subs r0, r0, r3
+ RETne
+ subs r2, r2, #0x01
+ RETeq
+
+ /* Compare 1 byte */
+ ldrb r0, [ip], #0x01
+ ldrb r3, [r1], #0x01
+ subs r0, r0, r3
+ RETne
+ subs r2, r2, #0x01
+ RETeq
+
+ /* Compare 4 bytes at a time, if possible */
+ subs r2, r2, #0x04
+ bcc .Lmemcmp_bytewise
+.Lmemcmp_word_aligned:
+ ldr r0, [ip], #0x04
+ ldr r3, [r1], #0x04
+ subs r2, r2, #0x04
+ cmpcs r0, r3
+ beq .Lmemcmp_word_aligned
+ sub r0, r0, r3
+
+ /* Correct for extra subtraction, and check if done */
+ adds r2, r2, #0x04
+ cmpeq r0, #0x00 /* If done, did all bytes match? */
+ RETeq /* Yup. Just return */
+
+ /* Re-do the final word byte-wise */
+ sub ip, ip, #0x04
+ sub r1, r1, #0x04
+
+.Lmemcmp_bytewise:
+ add r2, r2, #0x03
+.Lmemcmp_bytewise2:
+ ldrb r0, [ip], #0x01
+ ldrb r3, [r1], #0x01
+ subs r2, r2, #0x01
+ cmpcs r0, r3
+ beq .Lmemcmp_bytewise2
+ sub r0, r0, r3
+ RET
+
+ /*
+ * 6 byte compares are very common, thanks to the network stack.
+ * This code is hand-scheduled to reduce the number of stalls for
+ * load results. Everything else being equal, this will be ~32%
+ * faster than a byte-wise memcmp.
+ */
+ .align 5
+.Lmemcmp_6bytes:
+ ldrb r3, [r1, #0x00] /* r3 = b2#0 */
+ ldrb r0, [ip, #0x00] /* r0 = b1#0 */
+ ldrb r2, [r1, #0x01] /* r2 = b2#1 */
+ subs r0, r0, r3 /* r0 = b1#0 - b2#0 */
+ ldrbeq r3, [ip, #0x01] /* r3 = b1#1 */
+ RETne /* Return if mismatch on #0 */
+ subs r0, r3, r2 /* r0 = b1#1 - b2#1 */
+ ldrbeq r3, [r1, #0x02] /* r3 = b2#2 */
+ ldrbeq r0, [ip, #0x02] /* r0 = b1#2 */
+ RETne /* Return if mismatch on #1 */
+ ldrb r2, [r1, #0x03] /* r2 = b2#3 */
+ subs r0, r0, r3 /* r0 = b1#2 - b2#2 */
+ ldrbeq r3, [ip, #0x03] /* r3 = b1#3 */
+ RETne /* Return if mismatch on #2 */
+ subs r0, r3, r2 /* r0 = b1#3 - b2#3 */
+ ldrbeq r3, [r1, #0x04] /* r3 = b2#4 */
+ ldrbeq r0, [ip, #0x04] /* r0 = b1#4 */
+ RETne /* Return if mismatch on #3 */
+ ldrb r2, [r1, #0x05] /* r2 = b2#5 */
+ subs r0, r0, r3 /* r0 = b1#4 - b2#4 */
+ ldrbeq r3, [ip, #0x05] /* r3 = b1#5 */
+ RETne /* Return if mismatch on #4 */
+ sub r0, r3, r2 /* r0 = b1#5 - b2#5 */
+ RET
+END(bcmp)
+
+ENTRY(bcopy)
+ /* switch the source and destination registers */
+ eor r0, r1, r0
+ eor r1, r0, r1
+ eor r0, r1, r0
+EENTRY(memmove)
+ /* Do the buffers overlap? */
+ cmp r0, r1
+ RETeq /* Bail now if src/dst are the same */
+ subcc r3, r0, r1 /* if (dst > src) r3 = dst - src */
+ subcs r3, r1, r0 /* if (src > dsr) r3 = src - dst */
+ cmp r3, r2 /* if (r3 < len) we have an overlap */
+ bcc PIC_SYM(_C_LABEL(memcpy), PLT)
+
+ /* Determine copy direction */
+ cmp r1, r0
+ bcc .Lmemmove_backwards
+
+ moveq r0, #0 /* Quick abort for len=0 */
+ RETeq
+
+ stmdb sp!, {r0, lr} /* memmove() returns dest addr */
+ subs r2, r2, #4
+ blt .Lmemmove_fl4 /* less than 4 bytes */
+ ands r12, r0, #3
+ bne .Lmemmove_fdestul /* oh unaligned destination addr */
+ ands r12, r1, #3
+ bne .Lmemmove_fsrcul /* oh unaligned source addr */
+
+.Lmemmove_ft8:
+ /* We have aligned source and destination */
+ subs r2, r2, #8
+ blt .Lmemmove_fl12 /* less than 12 bytes (4 from above) */
+ subs r2, r2, #0x14
+ blt .Lmemmove_fl32 /* less than 32 bytes (12 from above) */
+ stmdb sp!, {r4} /* borrow r4 */
+
+ /* blat 32 bytes at a time */
+ /* XXX for really big copies perhaps we should use more registers */
+.Lmemmove_floop32:
+ ldmia r1!, {r3, r4, r12, lr}
+ stmia r0!, {r3, r4, r12, lr}
+ ldmia r1!, {r3, r4, r12, lr}
+ stmia r0!, {r3, r4, r12, lr}
+ subs r2, r2, #0x20
+ bge .Lmemmove_floop32
+
+ cmn r2, #0x10
+ ldmiage r1!, {r3, r4, r12, lr} /* blat a remaining 16 bytes */
+ stmiage r0!, {r3, r4, r12, lr}
+ subge r2, r2, #0x10
+ ldmia sp!, {r4} /* return r4 */
+
+.Lmemmove_fl32:
+ adds r2, r2, #0x14
+
+ /* blat 12 bytes at a time */
+.Lmemmove_floop12:
+ ldmiage r1!, {r3, r12, lr}
+ stmiage r0!, {r3, r12, lr}
+ subsge r2, r2, #0x0c
+ bge .Lmemmove_floop12
+
+.Lmemmove_fl12:
+ adds r2, r2, #8
+ blt .Lmemmove_fl4
+
+ subs r2, r2, #4
+ ldrlt r3, [r1], #4
+ strlt r3, [r0], #4
+ ldmiage r1!, {r3, r12}
+ stmiage r0!, {r3, r12}
+ subge r2, r2, #4
+
+.Lmemmove_fl4:
+ /* less than 4 bytes to go */
+ adds r2, r2, #4
+ ldmiaeq sp!, {r0, pc} /* done */
+
+ /* copy the crud byte at a time */
+ cmp r2, #2
+ ldrb r3, [r1], #1
+ strb r3, [r0], #1
+ ldrbge r3, [r1], #1
+ strbge r3, [r0], #1
+ ldrbgt r3, [r1], #1
+ strbgt r3, [r0], #1
+ ldmia sp!, {r0, pc}
+
+ /* erg - unaligned destination */
+.Lmemmove_fdestul:
+ rsb r12, r12, #4
+ cmp r12, #2
+
+ /* align destination with byte copies */
+ ldrb r3, [r1], #1
+ strb r3, [r0], #1
+ ldrbge r3, [r1], #1
+ strbge r3, [r0], #1
+ ldrbgt r3, [r1], #1
+ strbgt r3, [r0], #1
+ subs r2, r2, r12
+ blt .Lmemmove_fl4 /* less the 4 bytes */
+
+ ands r12, r1, #3
+ beq .Lmemmove_ft8 /* we have an aligned source */
+
+ /* erg - unaligned source */
+ /* This is where it gets nasty ... */
+.Lmemmove_fsrcul:
+ bic r1, r1, #3
+ ldr lr, [r1], #4
+ cmp r12, #2
+ bgt .Lmemmove_fsrcul3
+ beq .Lmemmove_fsrcul2
+ cmp r2, #0x0c
+ blt .Lmemmove_fsrcul1loop4
+ sub r2, r2, #0x0c
+ stmdb sp!, {r4, r5}
+
+.Lmemmove_fsrcul1loop16:
+ mov r3, lr, lsr #8
+ ldmia r1!, {r4, r5, r12, lr}
+ orr r3, r3, r4, lsl #24
+ mov r4, r4, lsr #8
+ orr r4, r4, r5, lsl #24
+ mov r5, r5, lsr #8
+ orr r5, r5, r12, lsl #24
+ mov r12, r12, lsr #8
+ orr r12, r12, lr, lsl #24
+ stmia r0!, {r3-r5, r12}
+ subs r2, r2, #0x10
+ bge .Lmemmove_fsrcul1loop16
+ ldmia sp!, {r4, r5}
+ adds r2, r2, #0x0c
+ blt .Lmemmove_fsrcul1l4
+
+.Lmemmove_fsrcul1loop4:
+ mov r12, lr, lsr #8
+ ldr lr, [r1], #4
+ orr r12, r12, lr, lsl #24
+ str r12, [r0], #4
+ subs r2, r2, #4
+ bge .Lmemmove_fsrcul1loop4
+
+.Lmemmove_fsrcul1l4:
+ sub r1, r1, #3
+ b .Lmemmove_fl4
+
+.Lmemmove_fsrcul2:
+ cmp r2, #0x0c
+ blt .Lmemmove_fsrcul2loop4
+ sub r2, r2, #0x0c
+ stmdb sp!, {r4, r5}
+
+.Lmemmove_fsrcul2loop16:
+ mov r3, lr, lsr #16
+ ldmia r1!, {r4, r5, r12, lr}
+ orr r3, r3, r4, lsl #16
+ mov r4, r4, lsr #16
+ orr r4, r4, r5, lsl #16
+ mov r5, r5, lsr #16
+ orr r5, r5, r12, lsl #16
+ mov r12, r12, lsr #16
+ orr r12, r12, lr, lsl #16
+ stmia r0!, {r3-r5, r12}
+ subs r2, r2, #0x10
+ bge .Lmemmove_fsrcul2loop16
+ ldmia sp!, {r4, r5}
+ adds r2, r2, #0x0c
+ blt .Lmemmove_fsrcul2l4
+
+.Lmemmove_fsrcul2loop4:
+ mov r12, lr, lsr #16
+ ldr lr, [r1], #4
+ orr r12, r12, lr, lsl #16
+ str r12, [r0], #4
+ subs r2, r2, #4
+ bge .Lmemmove_fsrcul2loop4
+
+.Lmemmove_fsrcul2l4:
+ sub r1, r1, #2
+ b .Lmemmove_fl4
+
+.Lmemmove_fsrcul3:
+ cmp r2, #0x0c
+ blt .Lmemmove_fsrcul3loop4
+ sub r2, r2, #0x0c
+ stmdb sp!, {r4, r5}
+
+.Lmemmove_fsrcul3loop16:
+ mov r3, lr, lsr #24
+ ldmia r1!, {r4, r5, r12, lr}
+ orr r3, r3, r4, lsl #8
+ mov r4, r4, lsr #24
+ orr r4, r4, r5, lsl #8
+ mov r5, r5, lsr #24
+ orr r5, r5, r12, lsl #8
+ mov r12, r12, lsr #24
+ orr r12, r12, lr, lsl #8
+ stmia r0!, {r3-r5, r12}
+ subs r2, r2, #0x10
+ bge .Lmemmove_fsrcul3loop16
+ ldmia sp!, {r4, r5}
+ adds r2, r2, #0x0c
+ blt .Lmemmove_fsrcul3l4
+
+.Lmemmove_fsrcul3loop4:
+ mov r12, lr, lsr #24
+ ldr lr, [r1], #4
+ orr r12, r12, lr, lsl #8
+ str r12, [r0], #4
+ subs r2, r2, #4
+ bge .Lmemmove_fsrcul3loop4
+
+.Lmemmove_fsrcul3l4:
+ sub r1, r1, #1
+ b .Lmemmove_fl4
+
+.Lmemmove_backwards:
+ add r1, r1, r2
+ add r0, r0, r2
+ subs r2, r2, #4
+ blt .Lmemmove_bl4 /* less than 4 bytes */
+ ands r12, r0, #3
+ bne .Lmemmove_bdestul /* oh unaligned destination addr */
+ ands r12, r1, #3
+ bne .Lmemmove_bsrcul /* oh unaligned source addr */
+
+.Lmemmove_bt8:
+ /* We have aligned source and destination */
+ subs r2, r2, #8
+ blt .Lmemmove_bl12 /* less than 12 bytes (4 from above) */
+ stmdb sp!, {r4, lr}
+ subs r2, r2, #0x14 /* less than 32 bytes (12 from above) */
+ blt .Lmemmove_bl32
+
+ /* blat 32 bytes at a time */
+ /* XXX for really big copies perhaps we should use more registers */
+.Lmemmove_bloop32:
+ ldmdb r1!, {r3, r4, r12, lr}
+ stmdb r0!, {r3, r4, r12, lr}
+ ldmdb r1!, {r3, r4, r12, lr}
+ stmdb r0!, {r3, r4, r12, lr}
+ subs r2, r2, #0x20
+ bge .Lmemmove_bloop32
+
+.Lmemmove_bl32:
+ cmn r2, #0x10
+ ldmdbge r1!, {r3, r4, r12, lr} /* blat a remaining 16 bytes */
+ stmdbge r0!, {r3, r4, r12, lr}
+ subge r2, r2, #0x10
+ adds r2, r2, #0x14
+ ldmdbge r1!, {r3, r12, lr} /* blat a remaining 12 bytes */
+ stmdbge r0!, {r3, r12, lr}
+ subge r2, r2, #0x0c
+ ldmia sp!, {r4, lr}
+
+.Lmemmove_bl12:
+ adds r2, r2, #8
+ blt .Lmemmove_bl4
+ subs r2, r2, #4
+ ldrlt r3, [r1, #-4]!
+ strlt r3, [r0, #-4]!
+ ldmdbge r1!, {r3, r12}
+ stmdbge r0!, {r3, r12}
+ subge r2, r2, #4
+
+.Lmemmove_bl4:
+ /* less than 4 bytes to go */
+ adds r2, r2, #4
+ RETeq /* done */
+
+ /* copy the crud byte at a time */
+ cmp r2, #2
+ ldrb r3, [r1, #-1]!
+ strb r3, [r0, #-1]!
+ ldrbge r3, [r1, #-1]!
+ strbge r3, [r0, #-1]!
+ ldrbgt r3, [r1, #-1]!
+ strbgt r3, [r0, #-1]!
+ RET
+
+ /* erg - unaligned destination */
+.Lmemmove_bdestul:
+ cmp r12, #2
+
+ /* align destination with byte copies */
+ ldrb r3, [r1, #-1]!
+ strb r3, [r0, #-1]!
+ ldrbge r3, [r1, #-1]!
+ strbge r3, [r0, #-1]!
+ ldrbgt r3, [r1, #-1]!
+ strbgt r3, [r0, #-1]!
+ subs r2, r2, r12
+ blt .Lmemmove_bl4 /* less than 4 bytes to go */
+ ands r12, r1, #3
+ beq .Lmemmove_bt8 /* we have an aligned source */
+
+ /* erg - unaligned source */
+ /* This is where it gets nasty ... */
+.Lmemmove_bsrcul:
+ bic r1, r1, #3
+ ldr r3, [r1, #0]
+ cmp r12, #2
+ blt .Lmemmove_bsrcul1
+ beq .Lmemmove_bsrcul2
+ cmp r2, #0x0c
+ blt .Lmemmove_bsrcul3loop4
+ sub r2, r2, #0x0c
+ stmdb sp!, {r4, r5, lr}
+
+.Lmemmove_bsrcul3loop16:
+ mov lr, r3, lsl #8
+ ldmdb r1!, {r3-r5, r12}
+ orr lr, lr, r12, lsr #24
+ mov r12, r12, lsl #8
+ orr r12, r12, r5, lsr #24
+ mov r5, r5, lsl #8
+ orr r5, r5, r4, lsr #24
+ mov r4, r4, lsl #8
+ orr r4, r4, r3, lsr #24
+ stmdb r0!, {r4, r5, r12, lr}
+ subs r2, r2, #0x10
+ bge .Lmemmove_bsrcul3loop16
+ ldmia sp!, {r4, r5, lr}
+ adds r2, r2, #0x0c
+ blt .Lmemmove_bsrcul3l4
+
+.Lmemmove_bsrcul3loop4:
+ mov r12, r3, lsl #8
+ ldr r3, [r1, #-4]!
+ orr r12, r12, r3, lsr #24
+ str r12, [r0, #-4]!
+ subs r2, r2, #4
+ bge .Lmemmove_bsrcul3loop4
+
+.Lmemmove_bsrcul3l4:
+ add r1, r1, #3
+ b .Lmemmove_bl4
+
+.Lmemmove_bsrcul2:
+ cmp r2, #0x0c
+ blt .Lmemmove_bsrcul2loop4
+ sub r2, r2, #0x0c
+ stmdb sp!, {r4, r5, lr}
+
+.Lmemmove_bsrcul2loop16:
+ mov lr, r3, lsl #16
+ ldmdb r1!, {r3-r5, r12}
+ orr lr, lr, r12, lsr #16
+ mov r12, r12, lsl #16
+ orr r12, r12, r5, lsr #16
+ mov r5, r5, lsl #16
+ orr r5, r5, r4, lsr #16
+ mov r4, r4, lsl #16
+ orr r4, r4, r3, lsr #16
+ stmdb r0!, {r4, r5, r12, lr}
+ subs r2, r2, #0x10
+ bge .Lmemmove_bsrcul2loop16
+ ldmia sp!, {r4, r5, lr}
+ adds r2, r2, #0x0c
+ blt .Lmemmove_bsrcul2l4
+
+.Lmemmove_bsrcul2loop4:
+ mov r12, r3, lsl #16
+ ldr r3, [r1, #-4]!
+ orr r12, r12, r3, lsr #16
+ str r12, [r0, #-4]!
+ subs r2, r2, #4
+ bge .Lmemmove_bsrcul2loop4
+
+.Lmemmove_bsrcul2l4:
+ add r1, r1, #2
+ b .Lmemmove_bl4
+
+.Lmemmove_bsrcul1:
+ cmp r2, #0x0c
+ blt .Lmemmove_bsrcul1loop4
+ sub r2, r2, #0x0c
+ stmdb sp!, {r4, r5, lr}
+
+.Lmemmove_bsrcul1loop32:
+ mov lr, r3, lsl #24
+ ldmdb r1!, {r3-r5, r12}
+ orr lr, lr, r12, lsr #8
+ mov r12, r12, lsl #24
+ orr r12, r12, r5, lsr #8
+ mov r5, r5, lsl #24
+ orr r5, r5, r4, lsr #8
+ mov r4, r4, lsl #24
+ orr r4, r4, r3, lsr #8
+ stmdb r0!, {r4, r5, r12, lr}
+ subs r2, r2, #0x10
+ bge .Lmemmove_bsrcul1loop32
+ ldmia sp!, {r4, r5, lr}
+ adds r2, r2, #0x0c
+ blt .Lmemmove_bsrcul1l4
+
+.Lmemmove_bsrcul1loop4:
+ mov r12, r3, lsl #24
+ ldr r3, [r1, #-4]!
+ orr r12, r12, r3, lsr #8
+ str r12, [r0, #-4]!
+ subs r2, r2, #4
+ bge .Lmemmove_bsrcul1loop4
+
+.Lmemmove_bsrcul1l4:
+ add r1, r1, #1
+ b .Lmemmove_bl4
+EEND(memmove)
+END(bcopy)
+
+/* LINTSTUB: Func: void *memcpy(void *dst, const void *src, size_t len) */
+ENTRY(memcpy)
+ pld [r1]
+ cmp r2, #0x0c
+ ble .Lmemcpy_short /* <= 12 bytes */
+#ifdef FLASHADDR
+#if FLASHADDR > PHYSADDR
+ ldr r3, =FLASHADDR
+ cmp r3, pc
+ bls .Lnormal
+#else
+ ldr r3, =FLASHADDR
+ cmp r3, pc
+ bhi .Lnormal
+#endif
+#endif
+ ldr r3, .L_arm_memcpy
+ ldr r3, [r3]
+ cmp r3, #0
+ beq .Lnormal
+ ldr r3, .L_min_memcpy_size
+ ldr r3, [r3]
+ cmp r2, r3
+ blt .Lnormal
+ stmfd sp!, {r0-r2, r4, lr}
+ mov r3, #0
+ ldr r4, .L_arm_memcpy
+ mov lr, pc
+ ldr pc, [r4]
+ cmp r0, #0
+ ldmfd sp!, {r0-r2, r4, lr}
+ RETeq
+.Lnormal:
+ mov r3, r0 /* We must not clobber r0 */
+
+ /* Word-align the destination buffer */
+ ands ip, r3, #0x03 /* Already word aligned? */
+ beq .Lmemcpy_wordaligned /* Yup */
+ cmp ip, #0x02
+ ldrb ip, [r1], #0x01
+ sub r2, r2, #0x01
+ strb ip, [r3], #0x01
+ ldrble ip, [r1], #0x01
+ suble r2, r2, #0x01
+ strble ip, [r3], #0x01
+ ldrblt ip, [r1], #0x01
+ sublt r2, r2, #0x01
+ strblt ip, [r3], #0x01
+
+ /* Destination buffer is now word aligned */
+.Lmemcpy_wordaligned:
+ ands ip, r1, #0x03 /* Is src also word-aligned? */
+ bne .Lmemcpy_bad_align /* Nope. Things just got bad */
+
+ /* Quad-align the destination buffer */
+ tst r3, #0x07 /* Already quad aligned? */
+ ldrne ip, [r1], #0x04
+ stmfd sp!, {r4-r9} /* Free up some registers */
+ subne r2, r2, #0x04
+ strne ip, [r3], #0x04
+
+ /* Destination buffer quad aligned, source is at least word aligned */
+ subs r2, r2, #0x80
+ blt .Lmemcpy_w_lessthan128
+
+ /* Copy 128 bytes at a time */
+.Lmemcpy_w_loop128:
+ ldr r4, [r1], #0x04 /* LD:00-03 */
+ ldr r5, [r1], #0x04 /* LD:04-07 */
+ pld [r1, #0x18] /* Prefetch 0x20 */
+ ldr r6, [r1], #0x04 /* LD:08-0b */
+ ldr r7, [r1], #0x04 /* LD:0c-0f */
+ ldr r8, [r1], #0x04 /* LD:10-13 */
+ ldr r9, [r1], #0x04 /* LD:14-17 */
+ strd r4, [r3], #0x08 /* ST:00-07 */
+ ldr r4, [r1], #0x04 /* LD:18-1b */
+ ldr r5, [r1], #0x04 /* LD:1c-1f */
+ strd r6, [r3], #0x08 /* ST:08-0f */
+ ldr r6, [r1], #0x04 /* LD:20-23 */
+ ldr r7, [r1], #0x04 /* LD:24-27 */
+ pld [r1, #0x18] /* Prefetch 0x40 */
+ strd r8, [r3], #0x08 /* ST:10-17 */
+ ldr r8, [r1], #0x04 /* LD:28-2b */
+ ldr r9, [r1], #0x04 /* LD:2c-2f */
+ strd r4, [r3], #0x08 /* ST:18-1f */
+ ldr r4, [r1], #0x04 /* LD:30-33 */
+ ldr r5, [r1], #0x04 /* LD:34-37 */
+ strd r6, [r3], #0x08 /* ST:20-27 */
+ ldr r6, [r1], #0x04 /* LD:38-3b */
+ ldr r7, [r1], #0x04 /* LD:3c-3f */
+ strd r8, [r3], #0x08 /* ST:28-2f */
+ ldr r8, [r1], #0x04 /* LD:40-43 */
+ ldr r9, [r1], #0x04 /* LD:44-47 */
+ pld [r1, #0x18] /* Prefetch 0x60 */
+ strd r4, [r3], #0x08 /* ST:30-37 */
+ ldr r4, [r1], #0x04 /* LD:48-4b */
+ ldr r5, [r1], #0x04 /* LD:4c-4f */
+ strd r6, [r3], #0x08 /* ST:38-3f */
+ ldr r6, [r1], #0x04 /* LD:50-53 */
+ ldr r7, [r1], #0x04 /* LD:54-57 */
+ strd r8, [r3], #0x08 /* ST:40-47 */
+ ldr r8, [r1], #0x04 /* LD:58-5b */
+ ldr r9, [r1], #0x04 /* LD:5c-5f */
+ strd r4, [r3], #0x08 /* ST:48-4f */
+ ldr r4, [r1], #0x04 /* LD:60-63 */
+ ldr r5, [r1], #0x04 /* LD:64-67 */
+ pld [r1, #0x18] /* Prefetch 0x80 */
+ strd r6, [r3], #0x08 /* ST:50-57 */
+ ldr r6, [r1], #0x04 /* LD:68-6b */
+ ldr r7, [r1], #0x04 /* LD:6c-6f */
+ strd r8, [r3], #0x08 /* ST:58-5f */
+ ldr r8, [r1], #0x04 /* LD:70-73 */
+ ldr r9, [r1], #0x04 /* LD:74-77 */
+ strd r4, [r3], #0x08 /* ST:60-67 */
+ ldr r4, [r1], #0x04 /* LD:78-7b */
+ ldr r5, [r1], #0x04 /* LD:7c-7f */
+ strd r6, [r3], #0x08 /* ST:68-6f */
+ strd r8, [r3], #0x08 /* ST:70-77 */
+ subs r2, r2, #0x80
+ strd r4, [r3], #0x08 /* ST:78-7f */
+ bge .Lmemcpy_w_loop128
+
+.Lmemcpy_w_lessthan128:
+ adds r2, r2, #0x80 /* Adjust for extra sub */
+ ldmfdeq sp!, {r4-r9}
+ RETeq /* Return now if done */
+ subs r2, r2, #0x20
+ blt .Lmemcpy_w_lessthan32
+
+ /* Copy 32 bytes at a time */
+.Lmemcpy_w_loop32:
+ ldr r4, [r1], #0x04
+ ldr r5, [r1], #0x04
+ pld [r1, #0x18]
+ ldr r6, [r1], #0x04
+ ldr r7, [r1], #0x04
+ ldr r8, [r1], #0x04
+ ldr r9, [r1], #0x04
+ strd r4, [r3], #0x08
+ ldr r4, [r1], #0x04
+ ldr r5, [r1], #0x04
+ strd r6, [r3], #0x08
+ strd r8, [r3], #0x08
+ subs r2, r2, #0x20
+ strd r4, [r3], #0x08
+ bge .Lmemcpy_w_loop32
+
+.Lmemcpy_w_lessthan32:
+ adds r2, r2, #0x20 /* Adjust for extra sub */
+ ldmfdeq sp!, {r4-r9}
+ RETeq /* Return now if done */
+
+ and r4, r2, #0x18
+ rsbs r4, r4, #0x18
+ addne pc, pc, r4, lsl #1
+ nop
+
+ /* At least 24 bytes remaining */
+ ldr r4, [r1], #0x04
+ ldr r5, [r1], #0x04
+ sub r2, r2, #0x08
+ strd r4, [r3], #0x08
+
+ /* At least 16 bytes remaining */
+ ldr r4, [r1], #0x04
+ ldr r5, [r1], #0x04
+ sub r2, r2, #0x08
+ strd r4, [r3], #0x08
+
+ /* At least 8 bytes remaining */
+ ldr r4, [r1], #0x04
+ ldr r5, [r1], #0x04
+ subs r2, r2, #0x08
+ strd r4, [r3], #0x08
+
+ /* Less than 8 bytes remaining */
+ ldmfd sp!, {r4-r9}
+ RETeq /* Return now if done */
+ subs r2, r2, #0x04
+ ldrge ip, [r1], #0x04
+ strge ip, [r3], #0x04
+ RETeq /* Return now if done */
+ addlt r2, r2, #0x04
+ ldrb ip, [r1], #0x01
+ cmp r2, #0x02
+ ldrbge r2, [r1], #0x01
+ strb ip, [r3], #0x01
+ ldrbgt ip, [r1]
+ strbge r2, [r3], #0x01
+ strbgt ip, [r3]
+ RET
+/* Place a literal pool here for the above ldr instructions to use */
+.ltorg
+
+
+/*
+ * At this point, it has not been possible to word align both buffers.
+ * The destination buffer is word aligned, but the source buffer is not.
+ */
+.Lmemcpy_bad_align:
+ stmfd sp!, {r4-r7}
+ bic r1, r1, #0x03
+ cmp ip, #2
+ ldr ip, [r1], #0x04
+ bgt .Lmemcpy_bad3
+ beq .Lmemcpy_bad2
+ b .Lmemcpy_bad1
+
+.Lmemcpy_bad1_loop16:
+ mov r4, ip, lsr #8
+ ldr r5, [r1], #0x04
+ pld [r1, #0x018]
+ ldr r6, [r1], #0x04
+ ldr r7, [r1], #0x04
+ ldr ip, [r1], #0x04
+ orr r4, r4, r5, lsl #24
+ mov r5, r5, lsr #8
+ orr r5, r5, r6, lsl #24
+ mov r6, r6, lsr #8
+ orr r6, r6, r7, lsl #24
+ mov r7, r7, lsr #8
+ orr r7, r7, ip, lsl #24
+ str r4, [r3], #0x04
+ str r5, [r3], #0x04
+ str r6, [r3], #0x04
+ str r7, [r3], #0x04
+.Lmemcpy_bad1:
+ subs r2, r2, #0x10
+ bge .Lmemcpy_bad1_loop16
+
+ adds r2, r2, #0x10
+ ldmfdeq sp!, {r4-r7}
+ RETeq /* Return now if done */
+ subs r2, r2, #0x04
+ sublt r1, r1, #0x03
+ blt .Lmemcpy_bad_done
+
+.Lmemcpy_bad1_loop4:
+ mov r4, ip, lsr #8
+ ldr ip, [r1], #0x04
+ subs r2, r2, #0x04
+ orr r4, r4, ip, lsl #24
+ str r4, [r3], #0x04
+ bge .Lmemcpy_bad1_loop4
+ sub r1, r1, #0x03
+ b .Lmemcpy_bad_done
+
+.Lmemcpy_bad2_loop16:
+ mov r4, ip, lsr #16
+ ldr r5, [r1], #0x04
+ pld [r1, #0x018]
+ ldr r6, [r1], #0x04
+ ldr r7, [r1], #0x04
+ ldr ip, [r1], #0x04
+ orr r4, r4, r5, lsl #16
+ mov r5, r5, lsr #16
+ orr r5, r5, r6, lsl #16
+ mov r6, r6, lsr #16
+ orr r6, r6, r7, lsl #16
+ mov r7, r7, lsr #16
+ orr r7, r7, ip, lsl #16
+ str r4, [r3], #0x04
+ str r5, [r3], #0x04
+ str r6, [r3], #0x04
+ str r7, [r3], #0x04
+.Lmemcpy_bad2:
+ subs r2, r2, #0x10
+ bge .Lmemcpy_bad2_loop16
+
+ adds r2, r2, #0x10
+ ldmfdeq sp!, {r4-r7}
+ RETeq /* Return now if done */
+ subs r2, r2, #0x04
+ sublt r1, r1, #0x02
+ blt .Lmemcpy_bad_done
+
+.Lmemcpy_bad2_loop4:
+ mov r4, ip, lsr #16
+ ldr ip, [r1], #0x04
+ subs r2, r2, #0x04
+ orr r4, r4, ip, lsl #16
+ str r4, [r3], #0x04
+ bge .Lmemcpy_bad2_loop4
+ sub r1, r1, #0x02
+ b .Lmemcpy_bad_done
+
+.Lmemcpy_bad3_loop16:
+ mov r4, ip, lsr #24
+ ldr r5, [r1], #0x04
+ pld [r1, #0x018]
+ ldr r6, [r1], #0x04
+ ldr r7, [r1], #0x04
+ ldr ip, [r1], #0x04
+ orr r4, r4, r5, lsl #8
+ mov r5, r5, lsr #24
+ orr r5, r5, r6, lsl #8
+ mov r6, r6, lsr #24
+ orr r6, r6, r7, lsl #8
+ mov r7, r7, lsr #24
+ orr r7, r7, ip, lsl #8
+ str r4, [r3], #0x04
+ str r5, [r3], #0x04
+ str r6, [r3], #0x04
+ str r7, [r3], #0x04
+.Lmemcpy_bad3:
+ subs r2, r2, #0x10
+ bge .Lmemcpy_bad3_loop16
+
+ adds r2, r2, #0x10
+ ldmfdeq sp!, {r4-r7}
+ RETeq /* Return now if done */
+ subs r2, r2, #0x04
+ sublt r1, r1, #0x01
+ blt .Lmemcpy_bad_done
+
+.Lmemcpy_bad3_loop4:
+ mov r4, ip, lsr #24
+ ldr ip, [r1], #0x04
+ subs r2, r2, #0x04
+ orr r4, r4, ip, lsl #8
+ str r4, [r3], #0x04
+ bge .Lmemcpy_bad3_loop4
+ sub r1, r1, #0x01
+
+.Lmemcpy_bad_done:
+ ldmfd sp!, {r4-r7}
+ adds r2, r2, #0x04
+ RETeq
+ ldrb ip, [r1], #0x01
+ cmp r2, #0x02
+ ldrbge r2, [r1], #0x01
+ strb ip, [r3], #0x01
+ ldrbgt ip, [r1]
+ strbge r2, [r3], #0x01
+ strbgt ip, [r3]
+ RET
+
+
+/*
+ * Handle short copies (less than 16 bytes), possibly misaligned.
+ * Some of these are *very* common, thanks to the network stack,
+ * and so are handled specially.
+ */
+.Lmemcpy_short:
+ add pc, pc, r2, lsl #2
+ nop
+ RET /* 0x00 */
+ b .Lmemcpy_bytewise /* 0x01 */
+ b .Lmemcpy_bytewise /* 0x02 */
+ b .Lmemcpy_bytewise /* 0x03 */
+ b .Lmemcpy_4 /* 0x04 */
+ b .Lmemcpy_bytewise /* 0x05 */
+ b .Lmemcpy_6 /* 0x06 */
+ b .Lmemcpy_bytewise /* 0x07 */
+ b .Lmemcpy_8 /* 0x08 */
+ b .Lmemcpy_bytewise /* 0x09 */
+ b .Lmemcpy_bytewise /* 0x0a */
+ b .Lmemcpy_bytewise /* 0x0b */
+ b .Lmemcpy_c /* 0x0c */
+.Lmemcpy_bytewise:
+ mov r3, r0 /* We must not clobber r0 */
+ ldrb ip, [r1], #0x01
+1: subs r2, r2, #0x01
+ strb ip, [r3], #0x01
+ ldrbne ip, [r1], #0x01
+ bne 1b
+ RET
+
+/******************************************************************************
+ * Special case for 4 byte copies
+ */
+#define LMEMCPY_4_LOG2 6 /* 64 bytes */
+#define LMEMCPY_4_PAD .align LMEMCPY_4_LOG2
+ LMEMCPY_4_PAD
+.Lmemcpy_4:
+ and r2, r1, #0x03
+ orr r2, r2, r0, lsl #2
+ ands r2, r2, #0x0f
+ sub r3, pc, #0x14
+ addne pc, r3, r2, lsl #LMEMCPY_4_LOG2
+
+/*
+ * 0000: dst is 32-bit aligned, src is 32-bit aligned
+ */
+ ldr r2, [r1]
+ str r2, [r0]
+ RET
+ LMEMCPY_4_PAD
+
+/*
+ * 0001: dst is 32-bit aligned, src is 8-bit aligned
+ */
+ ldr r3, [r1, #-1] /* BE:r3 = x012 LE:r3 = 210x */
+ ldr r2, [r1, #3] /* BE:r2 = 3xxx LE:r2 = xxx3 */
+ mov r3, r3, lsr #8 /* r3 = .210 */
+ orr r3, r3, r2, lsl #24 /* r3 = 3210 */
+ str r3, [r0]
+ RET
+ LMEMCPY_4_PAD
+
+/*
+ * 0010: dst is 32-bit aligned, src is 16-bit aligned
+ */
+ ldrh r3, [r1, #0x02]
+ ldrh r2, [r1]
+ orr r3, r2, r3, lsl #16
+ str r3, [r0]
+ RET
+ LMEMCPY_4_PAD
+
+/*
+ * 0011: dst is 32-bit aligned, src is 8-bit aligned
+ */
+ ldr r3, [r1, #-3] /* BE:r3 = xxx0 LE:r3 = 0xxx */
+ ldr r2, [r1, #1] /* BE:r2 = 123x LE:r2 = x321 */
+ mov r3, r3, lsr #24 /* r3 = ...0 */
+ orr r3, r3, r2, lsl #8 /* r3 = 3210 */
+ str r3, [r0]
+ RET
+ LMEMCPY_4_PAD
+
+/*
+ * 0100: dst is 8-bit aligned, src is 32-bit aligned
+ */
+ ldr r2, [r1]
+ strb r2, [r0]
+ mov r3, r2, lsr #8
+ mov r1, r2, lsr #24
+ strb r1, [r0, #0x03]
+ strh r3, [r0, #0x01]
+ RET
+ LMEMCPY_4_PAD
+
+/*
+ * 0101: dst is 8-bit aligned, src is 8-bit aligned
+ */
+ ldrb r2, [r1]
+ ldrh r3, [r1, #0x01]
+ ldrb r1, [r1, #0x03]
+ strb r2, [r0]
+ strh r3, [r0, #0x01]
+ strb r1, [r0, #0x03]
+ RET
+ LMEMCPY_4_PAD
+
+/*
+ * 0110: dst is 8-bit aligned, src is 16-bit aligned
+ */
+ ldrh r2, [r1] /* BE:r2 = ..01 LE:r2 = ..10 */
+ ldrh r3, [r1, #0x02] /* LE:r3 = ..23 LE:r3 = ..32 */
+ strb r2, [r0]
+ mov r2, r2, lsr #8 /* r2 = ...1 */
+ orr r2, r2, r3, lsl #8 /* r2 = .321 */
+ mov r3, r3, lsr #8 /* r3 = ...3 */
+ strh r2, [r0, #0x01]
+ strb r3, [r0, #0x03]
+ RET
+ LMEMCPY_4_PAD
+
+/*
+ * 0111: dst is 8-bit aligned, src is 8-bit aligned
+ */
+ ldrb r2, [r1]
+ ldrh r3, [r1, #0x01]
+ ldrb r1, [r1, #0x03]
+ strb r2, [r0]
+ strh r3, [r0, #0x01]
+ strb r1, [r0, #0x03]
+ RET
+ LMEMCPY_4_PAD
+
+/*
+ * 1000: dst is 16-bit aligned, src is 32-bit aligned
+ */
+ ldr r2, [r1]
+ strh r2, [r0]
+ mov r3, r2, lsr #16
+ strh r3, [r0, #0x02]
+ RET
+ LMEMCPY_4_PAD
+
+/*
+ * 1001: dst is 16-bit aligned, src is 8-bit aligned
+ */
+ ldr r2, [r1, #-1] /* BE:r2 = x012 LE:r2 = 210x */
+ ldr r3, [r1, #3] /* BE:r3 = 3xxx LE:r3 = xxx3 */
+ mov r1, r2, lsr #8 /* BE:r1 = .x01 LE:r1 = .210 */
+ strh r1, [r0]
+ mov r2, r2, lsr #24 /* r2 = ...2 */
+ orr r2, r2, r3, lsl #8 /* r2 = xx32 */
+ strh r2, [r0, #0x02]
+ RET
+ LMEMCPY_4_PAD
+
+/*
+ * 1010: dst is 16-bit aligned, src is 16-bit aligned
+ */
+ ldrh r2, [r1]
+ ldrh r3, [r1, #0x02]
+ strh r2, [r0]
+ strh r3, [r0, #0x02]
+ RET
+ LMEMCPY_4_PAD
+
+/*
+ * 1011: dst is 16-bit aligned, src is 8-bit aligned
+ */
+ ldr r3, [r1, #1] /* BE:r3 = 123x LE:r3 = x321 */
+ ldr r2, [r1, #-3] /* BE:r2 = xxx0 LE:r2 = 0xxx */
+ mov r1, r3, lsr #8 /* BE:r1 = .123 LE:r1 = .x32 */
+ strh r1, [r0, #0x02]
+ mov r3, r3, lsl #8 /* r3 = 321. */
+ orr r3, r3, r2, lsr #24 /* r3 = 3210 */
+ strh r3, [r0]
+ RET
+ LMEMCPY_4_PAD
+
+/*
+ * 1100: dst is 8-bit aligned, src is 32-bit aligned
+ */
+ ldr r2, [r1] /* BE:r2 = 0123 LE:r2 = 3210 */
+ strb r2, [r0]
+ mov r3, r2, lsr #8
+ mov r1, r2, lsr #24
+ strh r3, [r0, #0x01]
+ strb r1, [r0, #0x03]
+ RET
+ LMEMCPY_4_PAD
+
+/*
+ * 1101: dst is 8-bit aligned, src is 8-bit aligned
+ */
+ ldrb r2, [r1]
+ ldrh r3, [r1, #0x01]
+ ldrb r1, [r1, #0x03]
+ strb r2, [r0]
+ strh r3, [r0, #0x01]
+ strb r1, [r0, #0x03]
+ RET
+ LMEMCPY_4_PAD
+
+/*
+ * 1110: dst is 8-bit aligned, src is 16-bit aligned
+ */
+ ldrh r2, [r1] /* BE:r2 = ..01 LE:r2 = ..10 */
+ ldrh r3, [r1, #0x02] /* BE:r3 = ..23 LE:r3 = ..32 */
+ strb r2, [r0]
+ mov r2, r2, lsr #8 /* r2 = ...1 */
+ orr r2, r2, r3, lsl #8 /* r2 = .321 */
+ strh r2, [r0, #0x01]
+ mov r3, r3, lsr #8 /* r3 = ...3 */
+ strb r3, [r0, #0x03]
+ RET
+ LMEMCPY_4_PAD
+
+/*
+ * 1111: dst is 8-bit aligned, src is 8-bit aligned
+ */
+ ldrb r2, [r1]
+ ldrh r3, [r1, #0x01]
+ ldrb r1, [r1, #0x03]
+ strb r2, [r0]
+ strh r3, [r0, #0x01]
+ strb r1, [r0, #0x03]
+ RET
+ LMEMCPY_4_PAD
+
+
+/******************************************************************************
+ * Special case for 6 byte copies
+ */
+#define LMEMCPY_6_LOG2 6 /* 64 bytes */
+#define LMEMCPY_6_PAD .align LMEMCPY_6_LOG2
+ LMEMCPY_6_PAD
+.Lmemcpy_6:
+ and r2, r1, #0x03
+ orr r2, r2, r0, lsl #2
+ ands r2, r2, #0x0f
+ sub r3, pc, #0x14
+ addne pc, r3, r2, lsl #LMEMCPY_6_LOG2
+
+/*
+ * 0000: dst is 32-bit aligned, src is 32-bit aligned
+ */
+ ldr r2, [r1]
+ ldrh r3, [r1, #0x04]
+ str r2, [r0]
+ strh r3, [r0, #0x04]
+ RET
+ LMEMCPY_6_PAD
+
+/*
+ * 0001: dst is 32-bit aligned, src is 8-bit aligned
+ */
+ ldr r2, [r1, #-1] /* BE:r2 = x012 LE:r2 = 210x */
+ ldr r3, [r1, #0x03] /* BE:r3 = 345x LE:r3 = x543 */
+ mov r2, r2, lsr #8 /* r2 = .210 */
+ orr r2, r2, r3, lsl #24 /* r2 = 3210 */
+ mov r3, r3, lsr #8 /* BE:r3 = .345 LE:r3 = .x54 */
+ str r2, [r0]
+ strh r3, [r0, #0x04]
+ RET
+ LMEMCPY_6_PAD
+
+/*
+ * 0010: dst is 32-bit aligned, src is 16-bit aligned
+ */
+ ldr r3, [r1, #0x02] /* BE:r3 = 2345 LE:r3 = 5432 */
+ ldrh r2, [r1] /* BE:r2 = ..01 LE:r2 = ..10 */
+ mov r1, r3, lsr #16 /* r1 = ..54 */
+ orr r2, r2, r3, lsl #16 /* r2 = 3210 */
+ str r2, [r0]
+ strh r1, [r0, #0x04]
+ RET
+ LMEMCPY_6_PAD
+
+/*
+ * 0011: dst is 32-bit aligned, src is 8-bit aligned
+ */
+ ldr r2, [r1, #-3] /* BE:r2 = xxx0 LE:r2 = 0xxx */
+ ldr r3, [r1, #1] /* BE:r3 = 1234 LE:r3 = 4321 */
+ ldr r1, [r1, #5] /* BE:r1 = 5xxx LE:r3 = xxx5 */
+ mov r2, r2, lsr #24 /* r2 = ...0 */
+ orr r2, r2, r3, lsl #8 /* r2 = 3210 */
+ mov r1, r1, lsl #8 /* r1 = xx5. */
+ orr r1, r1, r3, lsr #24 /* r1 = xx54 */
+ str r2, [r0]
+ strh r1, [r0, #0x04]
+ RET
+ LMEMCPY_6_PAD
+
+/*
+ * 0100: dst is 8-bit aligned, src is 32-bit aligned
+ */
+ ldr r3, [r1] /* BE:r3 = 0123 LE:r3 = 3210 */
+ ldrh r2, [r1, #0x04] /* BE:r2 = ..45 LE:r2 = ..54 */
+ mov r1, r3, lsr #8 /* BE:r1 = .012 LE:r1 = .321 */
+ strh r1, [r0, #0x01]
+ strb r3, [r0]
+ mov r3, r3, lsr #24 /* r3 = ...3 */
+ orr r3, r3, r2, lsl #8 /* r3 = .543 */
+ mov r2, r2, lsr #8 /* r2 = ...5 */
+ strh r3, [r0, #0x03]
+ strb r2, [r0, #0x05]
+ RET
+ LMEMCPY_6_PAD
+
+/*
+ * 0101: dst is 8-bit aligned, src is 8-bit aligned
+ */
+ ldrb r2, [r1]
+ ldrh r3, [r1, #0x01]
+ ldrh ip, [r1, #0x03]
+ ldrb r1, [r1, #0x05]
+ strb r2, [r0]
+ strh r3, [r0, #0x01]
+ strh ip, [r0, #0x03]
+ strb r1, [r0, #0x05]
+ RET
+ LMEMCPY_6_PAD
+
+/*
+ * 0110: dst is 8-bit aligned, src is 16-bit aligned
+ */
+ ldrh r2, [r1] /* BE:r2 = ..01 LE:r2 = ..10 */
+ ldr r1, [r1, #0x02] /* BE:r1 = 2345 LE:r1 = 5432 */
+ strb r2, [r0]
+ mov r3, r1, lsr #24
+ strb r3, [r0, #0x05]
+ mov r3, r1, lsr #8 /* r3 = .543 */
+ strh r3, [r0, #0x03]
+ mov r3, r2, lsr #8 /* r3 = ...1 */
+ orr r3, r3, r1, lsl #8 /* r3 = 4321 */
+ strh r3, [r0, #0x01]
+ RET
+ LMEMCPY_6_PAD
+
+/*
+ * 0111: dst is 8-bit aligned, src is 8-bit aligned
+ */
+ ldrb r2, [r1]
+ ldrh r3, [r1, #0x01]
+ ldrh ip, [r1, #0x03]
+ ldrb r1, [r1, #0x05]
+ strb r2, [r0]
+ strh r3, [r0, #0x01]
+ strh ip, [r0, #0x03]
+ strb r1, [r0, #0x05]
+ RET
+ LMEMCPY_6_PAD
+
+/*
+ * 1000: dst is 16-bit aligned, src is 32-bit aligned
+ */
+ ldrh r2, [r1, #0x04] /* r2 = ..54 */
+ ldr r3, [r1] /* r3 = 3210 */
+ mov r2, r2, lsl #16 /* r2 = 54.. */
+ orr r2, r2, r3, lsr #16 /* r2 = 5432 */
+ strh r3, [r0]
+ str r2, [r0, #0x02]
+ RET
+ LMEMCPY_6_PAD
+
+/*
+ * 1001: dst is 16-bit aligned, src is 8-bit aligned
+ */
+ ldr r3, [r1, #-1] /* BE:r3 = x012 LE:r3 = 210x */
+ ldr r2, [r1, #3] /* BE:r2 = 345x LE:r2 = x543 */
+ mov r1, r3, lsr #8 /* BE:r1 = .x01 LE:r1 = .210 */
+ mov r2, r2, lsl #8 /* r2 = 543. */
+ orr r2, r2, r3, lsr #24 /* r2 = 5432 */
+ strh r1, [r0]
+ str r2, [r0, #0x02]
+ RET
+ LMEMCPY_6_PAD
+
+/*
+ * 1010: dst is 16-bit aligned, src is 16-bit aligned
+ */
+ ldrh r2, [r1]
+ ldr r3, [r1, #0x02]
+ strh r2, [r0]
+ str r3, [r0, #0x02]
+ RET
+ LMEMCPY_6_PAD
+
+/*
+ * 1011: dst is 16-bit aligned, src is 8-bit aligned
+ */
+ ldrb r3, [r1] /* r3 = ...0 */
+ ldr r2, [r1, #0x01] /* BE:r2 = 1234 LE:r2 = 4321 */
+ ldrb r1, [r1, #0x05] /* r1 = ...5 */
+ orr r3, r3, r2, lsl #8 /* r3 = 3210 */
+ mov r1, r1, lsl #24 /* r1 = 5... */
+ orr r1, r1, r2, lsr #8 /* r1 = 5432 */
+ strh r3, [r0]
+ str r1, [r0, #0x02]
+ RET
+ LMEMCPY_6_PAD
+
+/*
+ * 1100: dst is 8-bit aligned, src is 32-bit aligned
+ */
+ ldr r2, [r1] /* BE:r2 = 0123 LE:r2 = 3210 */
+ ldrh r1, [r1, #0x04] /* BE:r1 = ..45 LE:r1 = ..54 */
+ strb r2, [r0]
+ mov r2, r2, lsr #8 /* r2 = .321 */
+ orr r2, r2, r1, lsl #24 /* r2 = 4321 */
+ mov r1, r1, lsr #8 /* r1 = ...5 */
+ str r2, [r0, #0x01]
+ strb r1, [r0, #0x05]
+ RET
+ LMEMCPY_6_PAD
+
+/*
+ * 1101: dst is 8-bit aligned, src is 8-bit aligned
+ */
+ ldrb r2, [r1]
+ ldrh r3, [r1, #0x01]
+ ldrh ip, [r1, #0x03]
+ ldrb r1, [r1, #0x05]
+ strb r2, [r0]
+ strh r3, [r0, #0x01]
+ strh ip, [r0, #0x03]
+ strb r1, [r0, #0x05]
+ RET
+ LMEMCPY_6_PAD
+
+/*
+ * 1110: dst is 8-bit aligned, src is 16-bit aligned
+ */
+ ldrh r2, [r1] /* BE:r2 = ..01 LE:r2 = ..10 */
+ ldr r1, [r1, #0x02] /* BE:r1 = 2345 LE:r1 = 5432 */
+ strb r2, [r0]
+ mov r2, r2, lsr #8 /* r2 = ...1 */
+ orr r2, r2, r1, lsl #8 /* r2 = 4321 */
+ mov r1, r1, lsr #24 /* r1 = ...5 */
+ str r2, [r0, #0x01]
+ strb r1, [r0, #0x05]
+ RET
+ LMEMCPY_6_PAD
+
+/*
+ * 1111: dst is 8-bit aligned, src is 8-bit aligned
+ */
+ ldrb r2, [r1]
+ ldr r3, [r1, #0x01]
+ ldrb r1, [r1, #0x05]
+ strb r2, [r0]
+ str r3, [r0, #0x01]
+ strb r1, [r0, #0x05]
+ RET
+ LMEMCPY_6_PAD
+
+
+/******************************************************************************
+ * Special case for 8 byte copies
+ */
+#define LMEMCPY_8_LOG2 6 /* 64 bytes */
+#define LMEMCPY_8_PAD .align LMEMCPY_8_LOG2
+ LMEMCPY_8_PAD
+.Lmemcpy_8:
+ and r2, r1, #0x03
+ orr r2, r2, r0, lsl #2
+ ands r2, r2, #0x0f
+ sub r3, pc, #0x14
+ addne pc, r3, r2, lsl #LMEMCPY_8_LOG2
+
+/*
+ * 0000: dst is 32-bit aligned, src is 32-bit aligned
+ */
+ ldr r2, [r1]
+ ldr r3, [r1, #0x04]
+ str r2, [r0]
+ str r3, [r0, #0x04]
+ RET
+ LMEMCPY_8_PAD
+
+/*
+ * 0001: dst is 32-bit aligned, src is 8-bit aligned
+ */
+ ldr r3, [r1, #-1] /* BE:r3 = x012 LE:r3 = 210x */
+ ldr r2, [r1, #0x03] /* BE:r2 = 3456 LE:r2 = 6543 */
+ ldrb r1, [r1, #0x07] /* r1 = ...7 */
+ mov r3, r3, lsr #8 /* r3 = .210 */
+ orr r3, r3, r2, lsl #24 /* r3 = 3210 */
+ mov r1, r1, lsl #24 /* r1 = 7... */
+ orr r2, r1, r2, lsr #8 /* r2 = 7654 */
+ str r3, [r0]
+ str r2, [r0, #0x04]
+ RET
+ LMEMCPY_8_PAD
+
+/*
+ * 0010: dst is 32-bit aligned, src is 16-bit aligned
+ */
+ ldrh r2, [r1] /* BE:r2 = ..01 LE:r2 = ..10 */
+ ldr r3, [r1, #0x02] /* BE:r3 = 2345 LE:r3 = 5432 */
+ ldrh r1, [r1, #0x06] /* BE:r1 = ..67 LE:r1 = ..76 */
+ orr r2, r2, r3, lsl #16 /* r2 = 3210 */
+ mov r3, r3, lsr #16 /* r3 = ..54 */
+ orr r3, r3, r1, lsl #16 /* r3 = 7654 */
+ str r2, [r0]
+ str r3, [r0, #0x04]
+ RET
+ LMEMCPY_8_PAD
+
+/*
+ * 0011: dst is 32-bit aligned, src is 8-bit aligned
+ */
+ ldrb r3, [r1] /* r3 = ...0 */
+ ldr r2, [r1, #0x01] /* BE:r2 = 1234 LE:r2 = 4321 */
+ ldr r1, [r1, #0x05] /* BE:r1 = 567x LE:r1 = x765 */
+ orr r3, r3, r2, lsl #8 /* r3 = 3210 */
+ mov r2, r2, lsr #24 /* r2 = ...4 */
+ orr r2, r2, r1, lsl #8 /* r2 = 7654 */
+ str r3, [r0]
+ str r2, [r0, #0x04]
+ RET
+ LMEMCPY_8_PAD
+
+/*
+ * 0100: dst is 8-bit aligned, src is 32-bit aligned
+ */
+ ldr r3, [r1] /* BE:r3 = 0123 LE:r3 = 3210 */
+ ldr r2, [r1, #0x04] /* BE:r2 = 4567 LE:r2 = 7654 */
+ strb r3, [r0]
+ mov r1, r2, lsr #24 /* r1 = ...7 */
+ strb r1, [r0, #0x07]
+ mov r1, r3, lsr #8 /* r1 = .321 */
+ mov r3, r3, lsr #24 /* r3 = ...3 */
+ orr r3, r3, r2, lsl #8 /* r3 = 6543 */
+ strh r1, [r0, #0x01]
+ str r3, [r0, #0x03]
+ RET
+ LMEMCPY_8_PAD
+
+/*
+ * 0101: dst is 8-bit aligned, src is 8-bit aligned
+ */
+ ldrb r2, [r1]
+ ldrh r3, [r1, #0x01]
+ ldr ip, [r1, #0x03]
+ ldrb r1, [r1, #0x07]
+ strb r2, [r0]
+ strh r3, [r0, #0x01]
+ str ip, [r0, #0x03]
+ strb r1, [r0, #0x07]
+ RET
+ LMEMCPY_8_PAD
+
+/*
+ * 0110: dst is 8-bit aligned, src is 16-bit aligned
+ */
+ ldrh r2, [r1] /* BE:r2 = ..01 LE:r2 = ..10 */
+ ldr r3, [r1, #0x02] /* BE:r3 = 2345 LE:r3 = 5432 */
+ ldrh r1, [r1, #0x06] /* BE:r1 = ..67 LE:r1 = ..76 */
+ strb r2, [r0] /* 0 */
+ mov ip, r1, lsr #8 /* ip = ...7 */
+ strb ip, [r0, #0x07] /* 7 */
+ mov ip, r2, lsr #8 /* ip = ...1 */
+ orr ip, ip, r3, lsl #8 /* ip = 4321 */
+ mov r3, r3, lsr #8 /* r3 = .543 */
+ orr r3, r3, r1, lsl #24 /* r3 = 6543 */
+ strh ip, [r0, #0x01]
+ str r3, [r0, #0x03]
+ RET
+ LMEMCPY_8_PAD
+
+/*
+ * 0111: dst is 8-bit aligned, src is 8-bit aligned
+ */
+ ldrb r3, [r1] /* r3 = ...0 */
+ ldr ip, [r1, #0x01] /* BE:ip = 1234 LE:ip = 4321 */
+ ldrh r2, [r1, #0x05] /* BE:r2 = ..56 LE:r2 = ..65 */
+ ldrb r1, [r1, #0x07] /* r1 = ...7 */
+ strb r3, [r0]
+ mov r3, ip, lsr #16 /* BE:r3 = ..12 LE:r3 = ..43 */
+ strh ip, [r0, #0x01]
+ orr r2, r3, r2, lsl #16 /* r2 = 6543 */
+ str r2, [r0, #0x03]
+ strb r1, [r0, #0x07]
+ RET
+ LMEMCPY_8_PAD
+
+/*
+ * 1000: dst is 16-bit aligned, src is 32-bit aligned
+ */
+ ldr r2, [r1] /* BE:r2 = 0123 LE:r2 = 3210 */
+ ldr r3, [r1, #0x04] /* BE:r3 = 4567 LE:r3 = 7654 */
+ mov r1, r2, lsr #16 /* BE:r1 = ..01 LE:r1 = ..32 */
+ strh r2, [r0]
+ orr r2, r1, r3, lsl #16 /* r2 = 5432 */
+ mov r3, r3, lsr #16 /* r3 = ..76 */
+ str r2, [r0, #0x02]
+ strh r3, [r0, #0x06]
+ RET
+ LMEMCPY_8_PAD
+
+/*
+ * 1001: dst is 16-bit aligned, src is 8-bit aligned
+ */
+ ldr r2, [r1, #-1] /* BE:r2 = x012 LE:r2 = 210x */
+ ldr r3, [r1, #0x03] /* BE:r3 = 3456 LE:r3 = 6543 */
+ ldrb ip, [r1, #0x07] /* ip = ...7 */
+ mov r1, r2, lsr #8 /* BE:r1 = .x01 LE:r1 = .210 */
+ strh r1, [r0]
+ mov r1, r2, lsr #24 /* r1 = ...2 */
+ orr r1, r1, r3, lsl #8 /* r1 = 5432 */
+ mov r3, r3, lsr #24 /* r3 = ...6 */
+ orr r3, r3, ip, lsl #8 /* r3 = ..76 */
+ str r1, [r0, #0x02]
+ strh r3, [r0, #0x06]
+ RET
+ LMEMCPY_8_PAD
+
+/*
+ * 1010: dst is 16-bit aligned, src is 16-bit aligned
+ */
+ ldrh r2, [r1]
+ ldr ip, [r1, #0x02]
+ ldrh r3, [r1, #0x06]
+ strh r2, [r0]
+ str ip, [r0, #0x02]
+ strh r3, [r0, #0x06]
+ RET
+ LMEMCPY_8_PAD
+
+/*
+ * 1011: dst is 16-bit aligned, src is 8-bit aligned
+ */
+ ldr r3, [r1, #0x05] /* BE:r3 = 567x LE:r3 = x765 */
+ ldr r2, [r1, #0x01] /* BE:r2 = 1234 LE:r2 = 4321 */
+ ldrb ip, [r1] /* ip = ...0 */
+ mov r1, r3, lsr #8 /* BE:r1 = .567 LE:r1 = .x76 */
+ strh r1, [r0, #0x06]
+ mov r3, r3, lsl #24 /* r3 = 5... */
+ orr r3, r3, r2, lsr #8 /* r3 = 5432 */
+ orr r2, ip, r2, lsl #8 /* r2 = 3210 */
+ str r3, [r0, #0x02]
+ strh r2, [r0]
+ RET
+ LMEMCPY_8_PAD
+
+/*
+ * 1100: dst is 8-bit aligned, src is 32-bit aligned
+ */
+ ldr r3, [r1, #0x04] /* BE:r3 = 4567 LE:r3 = 7654 */
+ ldr r2, [r1] /* BE:r2 = 0123 LE:r2 = 3210 */
+ mov r1, r3, lsr #8 /* BE:r1 = .456 LE:r1 = .765 */
+ strh r1, [r0, #0x05]
+ strb r2, [r0]
+ mov r1, r3, lsr #24 /* r1 = ...7 */
+ strb r1, [r0, #0x07]
+ mov r2, r2, lsr #8 /* r2 = .321 */
+ orr r2, r2, r3, lsl #24 /* r2 = 4321 */
+ str r2, [r0, #0x01]
+ RET
+ LMEMCPY_8_PAD
+
+/*
+ * 1101: dst is 8-bit aligned, src is 8-bit aligned
+ */
+ ldrb r3, [r1] /* r3 = ...0 */
+ ldrh r2, [r1, #0x01] /* BE:r2 = ..12 LE:r2 = ..21 */
+ ldr ip, [r1, #0x03] /* BE:ip = 3456 LE:ip = 6543 */
+ ldrb r1, [r1, #0x07] /* r1 = ...7 */
+ strb r3, [r0]
+ mov r3, ip, lsr #16 /* BE:r3 = ..34 LE:r3 = ..65 */
+ strh r3, [r0, #0x05]
+ orr r2, r2, ip, lsl #16 /* r2 = 4321 */
+ str r2, [r0, #0x01]
+ strb r1, [r0, #0x07]
+ RET
+ LMEMCPY_8_PAD
+
+/*
+ * 1110: dst is 8-bit aligned, src is 16-bit aligned
+ */
+ ldrh r2, [r1] /* BE:r2 = ..01 LE:r2 = ..10 */
+ ldr r3, [r1, #0x02] /* BE:r3 = 2345 LE:r3 = 5432 */
+ ldrh r1, [r1, #0x06] /* BE:r1 = ..67 LE:r1 = ..76 */
+ strb r2, [r0]
+ mov ip, r2, lsr #8 /* ip = ...1 */
+ orr ip, ip, r3, lsl #8 /* ip = 4321 */
+ mov r2, r1, lsr #8 /* r2 = ...7 */
+ strb r2, [r0, #0x07]
+ mov r1, r1, lsl #8 /* r1 = .76. */
+ orr r1, r1, r3, lsr #24 /* r1 = .765 */
+ str ip, [r0, #0x01]
+ strh r1, [r0, #0x05]
+ RET
+ LMEMCPY_8_PAD
+
+/*
+ * 1111: dst is 8-bit aligned, src is 8-bit aligned
+ */
+ ldrb r2, [r1]
+ ldr ip, [r1, #0x01]
+ ldrh r3, [r1, #0x05]
+ ldrb r1, [r1, #0x07]
+ strb r2, [r0]
+ str ip, [r0, #0x01]
+ strh r3, [r0, #0x05]
+ strb r1, [r0, #0x07]
+ RET
+ LMEMCPY_8_PAD
+
+/******************************************************************************
+ * Special case for 12 byte copies
+ */
+#define LMEMCPY_C_LOG2 7 /* 128 bytes */
+#define LMEMCPY_C_PAD .align LMEMCPY_C_LOG2
+ LMEMCPY_C_PAD
+.Lmemcpy_c:
+ and r2, r1, #0x03
+ orr r2, r2, r0, lsl #2
+ ands r2, r2, #0x0f
+ sub r3, pc, #0x14
+ addne pc, r3, r2, lsl #LMEMCPY_C_LOG2
+
+/*
+ * 0000: dst is 32-bit aligned, src is 32-bit aligned
+ */
+ ldr r2, [r1]
+ ldr r3, [r1, #0x04]
+ ldr r1, [r1, #0x08]
+ str r2, [r0]
+ str r3, [r0, #0x04]
+ str r1, [r0, #0x08]
+ RET
+ LMEMCPY_C_PAD
+
+/*
+ * 0001: dst is 32-bit aligned, src is 8-bit aligned
+ */
+ ldrb r2, [r1, #0xb] /* r2 = ...B */
+ ldr ip, [r1, #0x07] /* BE:ip = 789A LE:ip = A987 */
+ ldr r3, [r1, #0x03] /* BE:r3 = 3456 LE:r3 = 6543 */
+ ldr r1, [r1, #-1] /* BE:r1 = x012 LE:r1 = 210x */
+ mov r2, r2, lsl #24 /* r2 = B... */
+ orr r2, r2, ip, lsr #8 /* r2 = BA98 */
+ str r2, [r0, #0x08]
+ mov r2, ip, lsl #24 /* r2 = 7... */
+ orr r2, r2, r3, lsr #8 /* r2 = 7654 */
+ mov r1, r1, lsr #8 /* r1 = .210 */
+ orr r1, r1, r3, lsl #24 /* r1 = 3210 */
+ str r2, [r0, #0x04]
+ str r1, [r0]
+ RET
+ LMEMCPY_C_PAD
+
+/*
+ * 0010: dst is 32-bit aligned, src is 16-bit aligned
+ */
+ ldrh r2, [r1] /* BE:r2 = ..01 LE:r2 = ..10 */
+ ldr r3, [r1, #0x02] /* BE:r3 = 2345 LE:r3 = 5432 */
+ ldr ip, [r1, #0x06] /* BE:ip = 6789 LE:ip = 9876 */
+ ldrh r1, [r1, #0x0a] /* BE:r1 = ..AB LE:r1 = ..BA */
+ orr r2, r2, r3, lsl #16 /* r2 = 3210 */
+ str r2, [r0]
+ mov r3, r3, lsr #16 /* r3 = ..54 */
+ orr r3, r3, ip, lsl #16 /* r3 = 7654 */
+ mov r1, r1, lsl #16 /* r1 = BA.. */
+ orr r1, r1, ip, lsr #16 /* r1 = BA98 */
+ str r3, [r0, #0x04]
+ str r1, [r0, #0x08]
+ RET
+ LMEMCPY_C_PAD
+
+/*
+ * 0011: dst is 32-bit aligned, src is 8-bit aligned
+ */
+ ldrb r2, [r1] /* r2 = ...0 */
+ ldr r3, [r1, #0x01] /* BE:r3 = 1234 LE:r3 = 4321 */
+ ldr ip, [r1, #0x05] /* BE:ip = 5678 LE:ip = 8765 */
+ ldr r1, [r1, #0x09] /* BE:r1 = 9ABx LE:r1 = xBA9 */
+ orr r2, r2, r3, lsl #8 /* r2 = 3210 */
+ str r2, [r0]
+ mov r3, r3, lsr #24 /* r3 = ...4 */
+ orr r3, r3, ip, lsl #8 /* r3 = 7654 */
+ mov r1, r1, lsl #8 /* r1 = BA9. */
+ orr r1, r1, ip, lsr #24 /* r1 = BA98 */
+ str r3, [r0, #0x04]
+ str r1, [r0, #0x08]
+ RET
+ LMEMCPY_C_PAD
+
+/*
+ * 0100: dst is 8-bit aligned (byte 1), src is 32-bit aligned
+ */
+ ldr r2, [r1] /* BE:r2 = 0123 LE:r2 = 3210 */
+ ldr r3, [r1, #0x04] /* BE:r3 = 4567 LE:r3 = 7654 */
+ ldr ip, [r1, #0x08] /* BE:ip = 89AB LE:ip = BA98 */
+ mov r1, r2, lsr #8 /* BE:r1 = .012 LE:r1 = .321 */
+ strh r1, [r0, #0x01]
+ strb r2, [r0]
+ mov r1, r2, lsr #24 /* r1 = ...3 */
+ orr r2, r1, r3, lsl #8 /* r1 = 6543 */
+ mov r1, r3, lsr #24 /* r1 = ...7 */
+ orr r1, r1, ip, lsl #8 /* r1 = A987 */
+ mov ip, ip, lsr #24 /* ip = ...B */
+ str r2, [r0, #0x03]
+ str r1, [r0, #0x07]
+ strb ip, [r0, #0x0b]
+ RET
+ LMEMCPY_C_PAD
+
+/*
+ * 0101: dst is 8-bit aligned (byte 1), src is 8-bit aligned (byte 1)
+ */
+ ldrb r2, [r1]
+ ldrh r3, [r1, #0x01]
+ ldr ip, [r1, #0x03]
+ strb r2, [r0]
+ ldr r2, [r1, #0x07]
+ ldrb r1, [r1, #0x0b]
+ strh r3, [r0, #0x01]
+ str ip, [r0, #0x03]
+ str r2, [r0, #0x07]
+ strb r1, [r0, #0x0b]
+ RET
+ LMEMCPY_C_PAD
+
+/*
+ * 0110: dst is 8-bit aligned (byte 1), src is 16-bit aligned
+ */
+ ldrh r2, [r1] /* BE:r2 = ..01 LE:r2 = ..10 */
+ ldr r3, [r1, #0x02] /* BE:r3 = 2345 LE:r3 = 5432 */
+ ldr ip, [r1, #0x06] /* BE:ip = 6789 LE:ip = 9876 */
+ ldrh r1, [r1, #0x0a] /* BE:r1 = ..AB LE:r1 = ..BA */
+ strb r2, [r0]
+ mov r2, r2, lsr #8 /* r2 = ...1 */
+ orr r2, r2, r3, lsl #8 /* r2 = 4321 */
+ strh r2, [r0, #0x01]
+ mov r2, r3, lsr #8 /* r2 = .543 */
+ orr r3, r2, ip, lsl #24 /* r3 = 6543 */
+ mov r2, ip, lsr #8 /* r2 = .987 */
+ orr r2, r2, r1, lsl #24 /* r2 = A987 */
+ mov r1, r1, lsr #8 /* r1 = ...B */
+ str r3, [r0, #0x03]
+ str r2, [r0, #0x07]
+ strb r1, [r0, #0x0b]
+ RET
+ LMEMCPY_C_PAD
+
+/*
+ * 0111: dst is 8-bit aligned (byte 1), src is 8-bit aligned (byte 3)
+ */
+ ldrb r2, [r1]
+ ldr r3, [r1, #0x01] /* BE:r3 = 1234 LE:r3 = 4321 */
+ ldr ip, [r1, #0x05] /* BE:ip = 5678 LE:ip = 8765 */
+ ldr r1, [r1, #0x09] /* BE:r1 = 9ABx LE:r1 = xBA9 */
+ strb r2, [r0]
+ strh r3, [r0, #0x01]
+ mov r3, r3, lsr #16 /* r3 = ..43 */
+ orr r3, r3, ip, lsl #16 /* r3 = 6543 */
+ mov ip, ip, lsr #16 /* ip = ..87 */
+ orr ip, ip, r1, lsl #16 /* ip = A987 */
+ mov r1, r1, lsr #16 /* r1 = ..xB */
+ str r3, [r0, #0x03]
+ str ip, [r0, #0x07]
+ strb r1, [r0, #0x0b]
+ RET
+ LMEMCPY_C_PAD
+
+/*
+ * 1000: dst is 16-bit aligned, src is 32-bit aligned
+ */
+ ldr ip, [r1] /* BE:ip = 0123 LE:ip = 3210 */
+ ldr r3, [r1, #0x04] /* BE:r3 = 4567 LE:r3 = 7654 */
+ ldr r2, [r1, #0x08] /* BE:r2 = 89AB LE:r2 = BA98 */
+ mov r1, ip, lsr #16 /* BE:r1 = ..01 LE:r1 = ..32 */
+ strh ip, [r0]
+ orr r1, r1, r3, lsl #16 /* r1 = 5432 */
+ mov r3, r3, lsr #16 /* r3 = ..76 */
+ orr r3, r3, r2, lsl #16 /* r3 = 9876 */
+ mov r2, r2, lsr #16 /* r2 = ..BA */
+ str r1, [r0, #0x02]
+ str r3, [r0, #0x06]
+ strh r2, [r0, #0x0a]
+ RET
+ LMEMCPY_C_PAD
+
+/*
+ * 1001: dst is 16-bit aligned, src is 8-bit aligned (byte 1)
+ */
+ ldr r2, [r1, #-1] /* BE:r2 = x012 LE:r2 = 210x */
+ ldr r3, [r1, #0x03] /* BE:r3 = 3456 LE:r3 = 6543 */
+ mov ip, r2, lsr #8 /* BE:ip = .x01 LE:ip = .210 */
+ strh ip, [r0]
+ ldr ip, [r1, #0x07] /* BE:ip = 789A LE:ip = A987 */
+ ldrb r1, [r1, #0x0b] /* r1 = ...B */
+ mov r2, r2, lsr #24 /* r2 = ...2 */
+ orr r2, r2, r3, lsl #8 /* r2 = 5432 */
+ mov r3, r3, lsr #24 /* r3 = ...6 */
+ orr r3, r3, ip, lsl #8 /* r3 = 9876 */
+ mov r1, r1, lsl #8 /* r1 = ..B. */
+ orr r1, r1, ip, lsr #24 /* r1 = ..BA */
+ str r2, [r0, #0x02]
+ str r3, [r0, #0x06]
+ strh r1, [r0, #0x0a]
+ RET
+ LMEMCPY_C_PAD
+
+/*
+ * 1010: dst is 16-bit aligned, src is 16-bit aligned
+ */
+ ldrh r2, [r1]
+ ldr r3, [r1, #0x02]
+ ldr ip, [r1, #0x06]
+ ldrh r1, [r1, #0x0a]
+ strh r2, [r0]
+ str r3, [r0, #0x02]
+ str ip, [r0, #0x06]
+ strh r1, [r0, #0x0a]
+ RET
+ LMEMCPY_C_PAD
+
+/*
+ * 1011: dst is 16-bit aligned, src is 8-bit aligned (byte 3)
+ */
+ ldr r2, [r1, #0x09] /* BE:r2 = 9ABx LE:r2 = xBA9 */
+ ldr r3, [r1, #0x05] /* BE:r3 = 5678 LE:r3 = 8765 */
+ mov ip, r2, lsr #8 /* BE:ip = .9AB LE:ip = .xBA */
+ strh ip, [r0, #0x0a]
+ ldr ip, [r1, #0x01] /* BE:ip = 1234 LE:ip = 4321 */
+ ldrb r1, [r1] /* r1 = ...0 */
+ mov r2, r2, lsl #24 /* r2 = 9... */
+ orr r2, r2, r3, lsr #8 /* r2 = 9876 */
+ mov r3, r3, lsl #24 /* r3 = 5... */
+ orr r3, r3, ip, lsr #8 /* r3 = 5432 */
+ orr r1, r1, ip, lsl #8 /* r1 = 3210 */
+ str r2, [r0, #0x06]
+ str r3, [r0, #0x02]
+ strh r1, [r0]
+ RET
+ LMEMCPY_C_PAD
+
+/*
+ * 1100: dst is 8-bit aligned (byte 3), src is 32-bit aligned
+ */
+ ldr r2, [r1] /* BE:r2 = 0123 LE:r2 = 3210 */
+ ldr ip, [r1, #0x04] /* BE:ip = 4567 LE:ip = 7654 */
+ ldr r1, [r1, #0x08] /* BE:r1 = 89AB LE:r1 = BA98 */
+ strb r2, [r0]
+ mov r3, r2, lsr #8 /* r3 = .321 */
+ orr r3, r3, ip, lsl #24 /* r3 = 4321 */
+ str r3, [r0, #0x01]
+ mov r3, ip, lsr #8 /* r3 = .765 */
+ orr r3, r3, r1, lsl #24 /* r3 = 8765 */
+ str r3, [r0, #0x05]
+ mov r1, r1, lsr #8 /* r1 = .BA9 */
+ strh r1, [r0, #0x09]
+ mov r1, r1, lsr #16 /* r1 = ...B */
+ strb r1, [r0, #0x0b]
+ RET
+ LMEMCPY_C_PAD
+
+/*
+ * 1101: dst is 8-bit aligned (byte 3), src is 8-bit aligned (byte 1)
+ */
+ ldrb r2, [r1, #0x0b] /* r2 = ...B */
+ ldr r3, [r1, #0x07] /* BE:r3 = 789A LE:r3 = A987 */
+ ldr ip, [r1, #0x03] /* BE:ip = 3456 LE:ip = 6543 */
+ ldr r1, [r1, #-1] /* BE:r1 = x012 LE:r1 = 210x */
+ strb r2, [r0, #0x0b]
+ mov r2, r3, lsr #16 /* r2 = ..A9 */
+ strh r2, [r0, #0x09]
+ mov r3, r3, lsl #16 /* r3 = 87.. */
+ orr r3, r3, ip, lsr #16 /* r3 = 8765 */
+ mov ip, ip, lsl #16 /* ip = 43.. */
+ orr ip, ip, r1, lsr #16 /* ip = 4321 */
+ mov r1, r1, lsr #8 /* r1 = .210 */
+ str r3, [r0, #0x05]
+ str ip, [r0, #0x01]
+ strb r1, [r0]
+ RET
+ LMEMCPY_C_PAD
+
+/*
+ * 1110: dst is 8-bit aligned (byte 3), src is 16-bit aligned
+ */
+ ldrh r2, [r1] /* r2 = ..10 */
+ ldr r3, [r1, #0x02] /* r3 = 5432 */
+ ldr ip, [r1, #0x06] /* ip = 9876 */
+ ldrh r1, [r1, #0x0a] /* r1 = ..BA */
+ strb r2, [r0]
+ mov r2, r2, lsr #8 /* r2 = ...1 */
+ orr r2, r2, r3, lsl #8 /* r2 = 4321 */
+ mov r3, r3, lsr #24 /* r3 = ...5 */
+ orr r3, r3, ip, lsl #8 /* r3 = 8765 */
+ mov ip, ip, lsr #24 /* ip = ...9 */
+ orr ip, ip, r1, lsl #8 /* ip = .BA9 */
+ mov r1, r1, lsr #8 /* r1 = ...B */
+ str r2, [r0, #0x01]
+ str r3, [r0, #0x05]
+ strh ip, [r0, #0x09]
+ strb r1, [r0, #0x0b]
+ RET
+ LMEMCPY_C_PAD
+
+/*
+ * 1111: dst is 8-bit aligned (byte 3), src is 8-bit aligned (byte 3)
+ */
+ ldrb r2, [r1]
+ ldr r3, [r1, #0x01]
+ ldr ip, [r1, #0x05]
+ strb r2, [r0]
+ ldrh r2, [r1, #0x09]
+ ldrb r1, [r1, #0x0b]
+ str r3, [r0, #0x01]
+ str ip, [r0, #0x05]
+ strh r2, [r0, #0x09]
+ strb r1, [r0, #0x0b]
+ RET
+END(memcpy)
diff --git a/sys/arm/arm/swtch-v6.S b/sys/arm/arm/swtch-v6.S
new file mode 100644
index 000000000000..8bbd88bc8670
--- /dev/null
+++ b/sys/arm/arm/swtch-v6.S
@@ -0,0 +1,508 @@
+/* $NetBSD: cpuswitch.S,v 1.41 2003/11/15 08:44:18 scw Exp $ */
+
+/*-
+ * Copyright 2003 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Steve C. Woodford for Wasabi Systems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the NetBSD Project by
+ * Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*-
+ * Copyright (c) 1994-1998 Mark Brinicombe.
+ * Copyright (c) 1994 Brini.
+ * All rights reserved.
+ *
+ * This code is derived from software written for Brini by Mark Brinicombe
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Brini.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * RiscBSD kernel project
+ *
+ * cpuswitch.S
+ *
+ * cpu switching functions
+ *
+ * Created : 15/10/94
+ *
+ */
+
+#include "assym.inc"
+#include "opt_sched.h"
+
+#include <machine/asm.h>
+#include <machine/asmacros.h>
+#include <machine/armreg.h>
+#include <machine/sysreg.h>
+#include <machine/vfp.h>
+
+__FBSDID("$FreeBSD$");
+
+#if defined(SMP)
+#define GET_PCPU(tmp, tmp2) \
+ mrc CP15_MPIDR(tmp); \
+ and tmp, tmp, #0xf; \
+ ldr tmp2, .Lcurpcpu+4; \
+ mul tmp, tmp, tmp2; \
+ ldr tmp2, .Lcurpcpu; \
+ add tmp, tmp, tmp2;
+#else
+
+#define GET_PCPU(tmp, tmp2) \
+ ldr tmp, .Lcurpcpu
+#endif
+
+#ifdef VFP
+ .fpu vfp /* allow VFP instructions */
+#endif
+
+.Lcurpcpu:
+ .word _C_LABEL(__pcpu)
+ .word PCPU_SIZE
+.Lblocked_lock:
+ .word _C_LABEL(blocked_lock)
+
+ENTRY(cpu_context_switch)
+ DSB
+ /*
+ * We can directly switch between translation tables only when the
+ * size of the mapping for any given virtual address is the same
+ * in the old and new translation tables.
+ * Thus, we must switch to kernel pmap translation table as
+ * intermediate mapping because all sizes of these mappings are same
+ * (or unmapped). The same is true for switch from kernel pmap
+ * translation table to new pmap one.
+ */
+ mov r2, #(CPU_ASID_KERNEL)
+ ldr r1, =(_C_LABEL(pmap_kern_ttb))
+ ldr r1, [r1]
+ mcr CP15_TTBR0(r1) /* switch to kernel TTB */
+ ISB
+ mcr CP15_TLBIASID(r2) /* flush not global TLBs */
+ DSB
+ mcr CP15_TTBR0(r0) /* switch to new TTB */
+ ISB
+ /*
+ * We must flush not global TLBs again because PT2MAP mapping
+ * is different.
+ */
+ mcr CP15_TLBIASID(r2) /* flush not global TLBs */
+ /*
+ * Flush entire Branch Target Cache because of the branch predictor
+ * is not architecturally invisible. See ARM Architecture Reference
+ * Manual ARMv7-A and ARMv7-R edition, page B2-1264(65), Branch
+ * predictors and Requirements for branch predictor maintenance
+ * operations sections.
+ */
+ /*
+ * Additionally, to mitigate mistrained branch predictor attack
+ * we must invalidate it on affected CPUs. Unfortunately, BPIALL
+ * is effectively NOP on Cortex-A15 so it needs special treatment.
+ */
+ ldr r0, [r8, #PC_BP_HARDEN_KIND]
+ cmp r0, #PCPU_BP_HARDEN_KIND_ICIALLU
+ mcrne CP15_BPIALL /* Flush entire Branch Target Cache */
+ mcreq CP15_ICIALLU /* This is the only way how to flush */
+ /* Branch Target Cache on Cortex-A15. */
+ DSB
+ mov pc, lr
+END(cpu_context_switch)
+
+/*
+ * cpu_throw(oldtd, newtd)
+ *
+ * Remove current thread state, then select the next thread to run
+ * and load its state.
+ * r0 = oldtd
+ * r1 = newtd
+ */
+ENTRY(cpu_throw)
+ mov r10, r0 /* r10 = oldtd */
+ mov r11, r1 /* r11 = newtd */
+
+#ifdef VFP /* This thread is dying, disable */
+ bl _C_LABEL(vfp_discard) /* VFP without preserving state. */
+#endif
+ GET_PCPU(r8, r9) /* r8 = current pcpu */
+ ldr r4, [r8, #PC_CPUID] /* r4 = current cpu id */
+
+ cmp r10, #0 /* old thread? */
+ beq 2f /* no, skip */
+
+ /* Remove this CPU from the active list. */
+ ldr r5, [r8, #PC_CURPMAP]
+ mov r0, #(PM_ACTIVE)
+ add r5, r0 /* r5 = old pm_active */
+
+ /* Compute position and mask. */
+#if _NCPUWORDS > 1
+ lsr r0, r4, #3
+ bic r0, #3
+ add r5, r0 /* r5 = position in old pm_active */
+ mov r2, #1
+ and r0, r4, #31
+ lsl r2, r0 /* r2 = mask */
+#else
+ mov r2, #1
+ lsl r2, r4 /* r2 = mask */
+#endif
+ /* Clear cpu from old active list. */
+#ifdef SMP
+1: ldrex r0, [r5]
+ bic r0, r2
+ strex r1, r0, [r5]
+ teq r1, #0
+ bne 1b
+#else
+ ldr r0, [r5]
+ bic r0, r2
+ str r0, [r5]
+#endif
+
+2:
+#ifdef INVARIANTS
+ cmp r11, #0 /* new thread? */
+ beq badsw1 /* no, panic */
+#endif
+ ldr r7, [r11, #(TD_PCB)] /* r7 = new PCB */
+
+ /*
+ * Registers at this point
+ * r4 = current cpu id
+ * r7 = new PCB
+ * r8 = current pcpu
+ * r11 = newtd
+ */
+
+ /* MMU switch to new thread. */
+ ldr r0, [r7, #(PCB_PAGEDIR)]
+#ifdef INVARIANTS
+ cmp r0, #0 /* new thread? */
+ beq badsw4 /* no, panic */
+#endif
+ bl _C_LABEL(cpu_context_switch)
+
+ /*
+ * Set new PMAP as current one.
+ * Insert cpu to new active list.
+ */
+
+ ldr r6, [r11, #(TD_PROC)] /* newtd->proc */
+ ldr r6, [r6, #(P_VMSPACE)] /* newtd->proc->vmspace */
+ add r6, #VM_PMAP /* newtd->proc->vmspace->pmap */
+ str r6, [r8, #PC_CURPMAP] /* store to curpmap */
+
+ mov r0, #PM_ACTIVE
+ add r6, r0 /* r6 = new pm_active */
+
+ /* compute position and mask */
+#if _NCPUWORDS > 1
+ lsr r0, r4, #3
+ bic r0, #3
+ add r6, r0 /* r6 = position in new pm_active */
+ mov r2, #1
+ and r0, r4, #31
+ lsl r2, r0 /* r2 = mask */
+#else
+ mov r2, #1
+ lsl r2, r4 /* r2 = mask */
+#endif
+ /* Set cpu to new active list. */
+#ifdef SMP
+1: ldrex r0, [r6]
+ orr r0, r2
+ strex r1, r0, [r6]
+ teq r1, #0
+ bne 1b
+#else
+ ldr r0, [r6]
+ orr r0, r2
+ str r0, [r6]
+#endif
+ /*
+ * Registers at this point.
+ * r7 = new PCB
+ * r8 = current pcpu
+ * r11 = newtd
+ * They must match the ones in sw1 position !!!
+ */
+ DMB
+ b sw1 /* share new thread init with cpu_switch() */
+END(cpu_throw)
+
+/*
+ * cpu_switch(oldtd, newtd, lock)
+ *
+ * Save the current thread state, then select the next thread to run
+ * and load its state.
+ * r0 = oldtd
+ * r1 = newtd
+ * r2 = lock (new lock for old thread)
+ */
+ENTRY(cpu_switch)
+ /* Interrupts are disabled. */
+#ifdef INVARIANTS
+ cmp r0, #0 /* old thread? */
+ beq badsw2 /* no, panic */
+#endif
+ /* Save all the registers in the old thread's pcb. */
+ ldr r3, [r0, #(TD_PCB)]
+ add r3, #(PCB_R4)
+ stmia r3, {r4-r12, sp, lr, pc}
+ mrc CP15_TPIDRURW(r4)
+ str r4, [r3, #(PCB_TPIDRURW - PCB_R4)]
+
+#ifdef INVARIANTS
+ cmp r1, #0 /* new thread? */
+ beq badsw3 /* no, panic */
+#endif
+ /*
+ * Save arguments. Note that we can now use r0-r14 until
+ * it is time to restore them for the new thread. However,
+ * some registers are not safe over function call.
+ */
+ mov r9, r2 /* r9 = lock */
+ mov r10, r0 /* r10 = oldtd */
+ mov r11, r1 /* r11 = newtd */
+
+ GET_PCPU(r8, r3) /* r8 = current PCPU */
+ ldr r7, [r11, #(TD_PCB)] /* r7 = newtd->td_pcb */
+
+
+
+#ifdef VFP
+ ldr r3, [r10, #(TD_PCB)]
+ fmrx r0, fpexc /* If the VFP is enabled */
+ tst r0, #(VFPEXC_EN) /* the current thread has */
+ movne r1, #1 /* used it, so go save */
+ addne r0, r3, #(PCB_VFPSTATE) /* the state into the PCB */
+ blne _C_LABEL(vfp_store) /* and disable the VFP. */
+#endif
+
+ /*
+ * MMU switch. If we're switching to a thread with the same
+ * address space as the outgoing one, we can skip the MMU switch.
+ */
+ mrc CP15_TTBR0(r1) /* r1 = old TTB */
+ ldr r0, [r7, #(PCB_PAGEDIR)] /* r0 = new TTB */
+ cmp r0, r1 /* Switching to the TTB? */
+ beq sw0 /* same TTB, skip */
+
+#ifdef INVARIANTS
+ cmp r0, #0 /* new thread? */
+ beq badsw4 /* no, panic */
+#endif
+
+ bl cpu_context_switch /* new TTB as argument */
+
+ /*
+ * Registers at this point
+ * r7 = new PCB
+ * r8 = current pcpu
+ * r9 = lock
+ * r10 = oldtd
+ * r11 = newtd
+ */
+
+ /*
+ * Set new PMAP as current one.
+ * Update active list on PMAPs.
+ */
+ ldr r6, [r11, #TD_PROC] /* newtd->proc */
+ ldr r6, [r6, #P_VMSPACE] /* newtd->proc->vmspace */
+ add r6, #VM_PMAP /* newtd->proc->vmspace->pmap */
+
+ ldr r5, [r8, #PC_CURPMAP] /* get old curpmap */
+ str r6, [r8, #PC_CURPMAP] /* and save new one */
+
+ mov r0, #PM_ACTIVE
+ add r5, r0 /* r5 = old pm_active */
+ add r6, r0 /* r6 = new pm_active */
+
+ /* Compute position and mask. */
+ ldr r4, [r8, #PC_CPUID]
+#if _NCPUWORDS > 1
+ lsr r0, r4, #3
+ bic r0, #3
+ add r5, r0 /* r5 = position in old pm_active */
+ add r6, r0 /* r6 = position in new pm_active */
+ mov r2, #1
+ and r0, r4, #31
+ lsl r2, r0 /* r2 = mask */
+#else
+ mov r2, #1
+ lsl r2, r4 /* r2 = mask */
+#endif
+ /* Clear cpu from old active list. */
+#ifdef SMP
+1: ldrex r0, [r5]
+ bic r0, r2
+ strex r1, r0, [r5]
+ teq r1, #0
+ bne 1b
+#else
+ ldr r0, [r5]
+ bic r0, r2
+ str r0, [r5]
+#endif
+ /* Set cpu to new active list. */
+#ifdef SMP
+1: ldrex r0, [r6]
+ orr r0, r2
+ strex r1, r0, [r6]
+ teq r1, #0
+ bne 1b
+#else
+ ldr r0, [r6]
+ orr r0, r2
+ str r0, [r6]
+#endif
+
+sw0:
+ /*
+ * Registers at this point
+ * r7 = new PCB
+ * r8 = current pcpu
+ * r9 = lock
+ * r10 = oldtd
+ * r11 = newtd
+ */
+
+ /* Change the old thread lock. */
+ add r5, r10, #TD_LOCK
+ DMB
+1: ldrex r0, [r5]
+ strex r1, r9, [r5]
+ teq r1, #0
+ bne 1b
+ DMB
+
+sw1:
+ clrex
+ /*
+ * Registers at this point
+ * r7 = new PCB
+ * r8 = current pcpu
+ * r11 = newtd
+ */
+
+#if defined(SMP) && defined(SCHED_ULE)
+ /*
+ * 386 and amd64 do the blocked lock test only for SMP and SCHED_ULE
+ * QQQ: What does it mean in reality and why is it done?
+ */
+ ldr r6, =blocked_lock
+1:
+ ldr r3, [r11, #TD_LOCK] /* atomic write regular read */
+ cmp r3, r6
+ beq 1b
+#endif
+
+ /* We have a new curthread now so make a note it */
+ str r11, [r8, #PC_CURTHREAD]
+ mcr CP15_TPIDRPRW(r11)
+
+ /* store pcb in per cpu structure */
+ str r7, [r8, #PC_CURPCB]
+
+ /*
+ * Restore all saved registers and return. Note that some saved
+ * registers can be changed when either cpu_fork(), cpu_copy_thread(),
+ * cpu_fork_kthread_handler(), or makectx() was called.
+ *
+ * The value of TPIDRURW is also written into TPIDRURO, as
+ * userspace still uses TPIDRURO, modifying it through
+ * sysarch(ARM_SET_TP, addr).
+ */
+ ldr r3, [r7, #PCB_TPIDRURW]
+ mcr CP15_TPIDRURW(r3) /* write tls thread reg 2 */
+ mcr CP15_TPIDRURO(r3) /* write tls thread reg 3 */
+ add r3, r7, #PCB_R4
+ ldmia r3, {r4-r12, sp, pc}
+
+#ifdef INVARIANTS
+badsw1:
+ ldr r0, =sw1_panic_str
+ bl _C_LABEL(panic)
+1: nop
+ b 1b
+
+badsw2:
+ ldr r0, =sw2_panic_str
+ bl _C_LABEL(panic)
+1: nop
+ b 1b
+
+badsw3:
+ ldr r0, =sw3_panic_str
+ bl _C_LABEL(panic)
+1: nop
+ b 1b
+
+badsw4:
+ ldr r0, =sw4_panic_str
+ bl _C_LABEL(panic)
+1: nop
+ b 1b
+
+sw1_panic_str:
+ .asciz "cpu_throw: no newthread supplied.\n"
+sw2_panic_str:
+ .asciz "cpu_switch: no curthread supplied.\n"
+sw3_panic_str:
+ .asciz "cpu_switch: no newthread supplied.\n"
+sw4_panic_str:
+ .asciz "cpu_switch: new pagedir is NULL.\n"
+#endif
+END(cpu_switch)
diff --git a/sys/arm/arm/swtch.S b/sys/arm/arm/swtch.S
new file mode 100644
index 000000000000..b1180b06fc07
--- /dev/null
+++ b/sys/arm/arm/swtch.S
@@ -0,0 +1,121 @@
+/* $NetBSD: cpuswitch.S,v 1.41 2003/11/15 08:44:18 scw Exp $ */
+
+/*-
+ * Copyright 2003 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Steve C. Woodford for Wasabi Systems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the NetBSD Project by
+ * Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*-
+ * Copyright (c) 1994-1998 Mark Brinicombe.
+ * Copyright (c) 1994 Brini.
+ * All rights reserved.
+ *
+ * This code is derived from software written for Brini by Mark Brinicombe
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Brini.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * RiscBSD kernel project
+ *
+ * cpuswitch.S
+ *
+ * cpu switching functions
+ *
+ * Created : 15/10/94
+ *
+ */
+
+#include "assym.inc"
+
+#include <machine/asm.h>
+#include <machine/asmacros.h>
+#include <machine/armreg.h>
+#include <machine/vfp.h>
+
+__FBSDID("$FreeBSD$");
+
+#ifdef VFP
+ .fpu vfp /* allow VFP instructions */
+#endif
+
+ENTRY(savectx)
+ stmfd sp!, {lr}
+ sub sp, sp, #4
+
+ /* Store all the registers in the thread's pcb */
+ add r3, r0, #(PCB_R4)
+ stmia r3, {r4-r12, sp, lr, pc}
+#ifdef VFP
+ fmrx r2, fpexc /* If the VFP is enabled */
+ tst r2, #(VFPEXC_EN) /* the current thread has */
+ movne r1, #1 /* used it, so go save */
+ addne r0, r0, #(PCB_VFPSTATE) /* the state into the PCB */
+ blne _C_LABEL(vfp_store) /* and disable the VFP. */
+#endif
+ add sp, sp, #4;
+ ldmfd sp!, {pc}
+END(savectx)
+
+ENTRY(fork_trampoline)
+ STOP_UNWINDING /* EABI: Don't unwind beyond the thread enty point. */
+ mov fp, #0 /* OABI: Stack traceback via fp stops here. */
+ mov r2, sp
+ mov r1, r5
+ mov r0, r4
+ ldr lr, =swi_exit /* Go finish forking, then return */
+ b _C_LABEL(fork_exit) /* to userland via swi_exit code. */
+END(fork_trampoline)
+
diff --git a/sys/arm/arm/sys_machdep.c b/sys/arm/arm/sys_machdep.c
new file mode 100644
index 000000000000..fc424d0fad39
--- /dev/null
+++ b/sys/arm/arm/sys_machdep.c
@@ -0,0 +1,220 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)sys_machdep.c 5.5 (Berkeley) 1/19/91
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_capsicum.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/capsicum.h>
+#include <sys/proc.h>
+#include <sys/sysproto.h>
+#include <sys/syscall.h>
+#include <sys/sysent.h>
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+
+#include <machine/cpu.h>
+#include <machine/sysarch.h>
+#include <machine/machdep.h>
+#include <machine/vmparam.h>
+
+#ifndef _SYS_SYSPROTO_H_
+struct sysarch_args {
+ int op;
+ char *parms;
+};
+#endif
+
+/* Prototypes */
+static int arm32_sync_icache (struct thread *, void *);
+static int arm32_drain_writebuf(struct thread *, void *);
+
+static int
+sync_icache(uintptr_t addr, size_t len)
+{
+ size_t size;
+ vm_offset_t rv;
+
+ /* Align starting address to cacheline size */
+ len += addr & cpuinfo.dcache_line_mask;
+ addr &= ~cpuinfo.dcache_line_mask;
+
+ /* Break whole range to pages. */
+ do {
+ size = PAGE_SIZE - (addr & PAGE_MASK);
+ size = min(size, len);
+ rv = dcache_wb_pou_checked(addr, size);
+ if (rv == 1) /* see dcache_wb_pou_checked() */
+ rv = icache_inv_pou_checked(addr, size);
+ if (rv != 1) {
+ if (!useracc((void *)addr, size, VM_PROT_READ)) {
+ /* Invalid access */
+ return (rv);
+ }
+ /* Valid but unmapped page - skip it. */
+ }
+ len -= size;
+ addr += size;
+ } while (len > 0);
+
+ /* Invalidate branch predictor buffer. */
+ bpb_inv_all();
+ return (1);
+}
+
+static int
+arm32_sync_icache(struct thread *td, void *args)
+{
+ struct arm_sync_icache_args ua;
+ int error;
+ ksiginfo_t ksi;
+ vm_offset_t rv;
+
+ if ((error = copyin(args, &ua, sizeof(ua))) != 0)
+ return (error);
+
+ if (ua.len == 0) {
+ td->td_retval[0] = 0;
+ return (0);
+ }
+
+ /*
+ * Validate arguments. Address and length are unsigned,
+ * so we can use wrapped overflow check.
+ */
+ if (((ua.addr + ua.len) < ua.addr) ||
+ ((ua.addr + ua.len) > VM_MAXUSER_ADDRESS)) {
+ ksiginfo_init_trap(&ksi);
+ ksi.ksi_signo = SIGSEGV;
+ ksi.ksi_code = SEGV_ACCERR;
+ ksi.ksi_addr = (void *)max(ua.addr, VM_MAXUSER_ADDRESS);
+ trapsignal(td, &ksi);
+ return (EINVAL);
+ }
+
+ rv = sync_icache(ua.addr, ua.len);
+ if (rv != 1) {
+ ksiginfo_init_trap(&ksi);
+ ksi.ksi_signo = SIGSEGV;
+ ksi.ksi_code = SEGV_MAPERR;
+ ksi.ksi_addr = (void *)rv;
+ trapsignal(td, &ksi);
+ return (EINVAL);
+ }
+
+ td->td_retval[0] = 0;
+ return (0);
+}
+
+static int
+arm32_drain_writebuf(struct thread *td, void *args)
+{
+ /* No args. */
+
+ dsb();
+ cpu_l2cache_drain_writebuf();
+ td->td_retval[0] = 0;
+ return (0);
+}
+
+static int
+arm32_set_tp(struct thread *td, void *args)
+{
+
+ set_tls(args);
+ return (0);
+}
+
+static int
+arm32_get_tp(struct thread *td, void *args)
+{
+
+ td->td_retval[0] = (register_t)get_tls();
+ return (0);
+}
+
+int
+sysarch(struct thread *td, struct sysarch_args *uap)
+{
+ int error;
+
+#ifdef CAPABILITY_MODE
+ /*
+ * When adding new operations, add a new case statement here to
+ * explicitly indicate whether or not the operation is safe to
+ * perform in capability mode.
+ */
+ if (IN_CAPABILITY_MODE(td)) {
+ switch (uap->op) {
+ case ARM_SYNC_ICACHE:
+ case ARM_DRAIN_WRITEBUF:
+ case ARM_SET_TP:
+ case ARM_GET_TP:
+ case ARM_GET_VFPSTATE:
+ break;
+
+ default:
+#ifdef KTRACE
+ if (KTRPOINT(td, KTR_CAPFAIL))
+ ktrcapfail(CAPFAIL_SYSCALL, NULL, NULL);
+#endif
+ return (ECAPMODE);
+ }
+ }
+#endif
+
+ switch (uap->op) {
+ case ARM_SYNC_ICACHE:
+ error = arm32_sync_icache(td, uap->parms);
+ break;
+ case ARM_DRAIN_WRITEBUF:
+ error = arm32_drain_writebuf(td, uap->parms);
+ break;
+ case ARM_SET_TP:
+ error = arm32_set_tp(td, uap->parms);
+ break;
+ case ARM_GET_TP:
+ error = arm32_get_tp(td, uap->parms);
+ break;
+ case ARM_GET_VFPSTATE:
+ error = arm_get_vfpstate(td, uap->parms);
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ return (error);
+}
diff --git a/sys/arm/arm/syscall.c b/sys/arm/arm/syscall.c
new file mode 100644
index 000000000000..a851db6e4556
--- /dev/null
+++ b/sys/arm/arm/syscall.c
@@ -0,0 +1,170 @@
+/* $NetBSD: fault.c,v 1.45 2003/11/20 14:44:36 scw Exp $ */
+
+/*-
+ * Copyright 2004 Olivier Houchard
+ * Copyright 2003 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Steve C. Woodford for Wasabi Systems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the NetBSD Project by
+ * Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*-
+ * Copyright (c) 1994-1997 Mark Brinicombe.
+ * Copyright (c) 1994 Brini.
+ * All rights reserved.
+ *
+ * This code is derived from software written for Brini by Mark Brinicombe
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Brini.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * RiscBSD kernel project
+ *
+ * fault.c
+ *
+ * Fault handlers
+ *
+ * Created : 28/11/94
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/syscall.h>
+#include <sys/sysent.h>
+#include <sys/signalvar.h>
+#include <sys/ptrace.h>
+
+#include <machine/frame.h>
+
+void swi_handler(struct trapframe *);
+
+int
+cpu_fetch_syscall_args(struct thread *td)
+{
+ struct proc *p;
+ register_t *ap;
+ struct syscall_args *sa;
+ u_int nap;
+ int error;
+
+ nap = 4;
+ sa = &td->td_sa;
+ sa->code = td->td_frame->tf_r7;
+ ap = &td->td_frame->tf_r0;
+ if (sa->code == SYS_syscall) {
+ sa->code = *ap++;
+ nap--;
+ } else if (sa->code == SYS___syscall) {
+ sa->code = ap[_QUAD_LOWWORD];
+ nap -= 2;
+ ap += 2;
+ }
+ p = td->td_proc;
+ if (sa->code >= p->p_sysent->sv_size)
+ sa->callp = &p->p_sysent->sv_table[0];
+ else
+ sa->callp = &p->p_sysent->sv_table[sa->code];
+ error = 0;
+ memcpy(sa->args, ap, nap * sizeof(register_t));
+ if (sa->callp->sy_narg > nap) {
+ error = copyin((void *)td->td_frame->tf_usr_sp, sa->args +
+ nap, (sa->callp->sy_narg - nap) * sizeof(register_t));
+ }
+ if (error == 0) {
+ td->td_retval[0] = 0;
+ td->td_retval[1] = 0;
+ }
+ return (error);
+}
+
+#include "../../kern/subr_syscall.c"
+
+static void
+syscall(struct thread *td, struct trapframe *frame)
+{
+
+ syscallenter(td);
+ syscallret(td);
+}
+
+void
+swi_handler(struct trapframe *frame)
+{
+ struct thread *td = curthread;
+
+ td->td_frame = frame;
+
+ td->td_pticks = 0;
+
+ /*
+ * Enable interrupts if they were enabled before the exception.
+ * Since all syscalls *should* come from user mode it will always
+ * be safe to enable them, but check anyway.
+ */
+ if (td->td_md.md_spinlock_count == 0) {
+ if (__predict_true(frame->tf_spsr & PSR_I) == 0)
+ enable_interrupts(PSR_I);
+ if (__predict_true(frame->tf_spsr & PSR_F) == 0)
+ enable_interrupts(PSR_F);
+ }
+
+ syscall(td, frame);
+}
diff --git a/sys/arm/arm/trap-v6.c b/sys/arm/arm/trap-v6.c
new file mode 100644
index 000000000000..16f166e83114
--- /dev/null
+++ b/sys/arm/arm/trap-v6.c
@@ -0,0 +1,678 @@
+/*-
+ * Copyright 2014 Olivier Houchard <cognet@FreeBSD.org>
+ * Copyright 2014 Svatopluk Kraus <onwahe@gmail.com>
+ * Copyright 2014 Michal Meloun <meloun@miracle.cz>
+ * Copyright 2014 Andrew Turner <andrew@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "opt_ktrace.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/signalvar.h>
+#include <sys/ktr.h>
+#include <sys/vmmeter.h>
+#ifdef KTRACE
+#include <sys/uio.h>
+#include <sys/ktrace.h>
+#endif
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_map.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_param.h>
+
+#include <machine/cpu.h>
+#include <machine/frame.h>
+#include <machine/machdep.h>
+#include <machine/pcb.h>
+
+#ifdef KDB
+#include <sys/kdb.h>
+#include <machine/db_machdep.h>
+#endif
+
+#ifdef KDTRACE_HOOKS
+#include <sys/dtrace_bsd.h>
+#endif
+
+extern char cachebailout[];
+
+#ifdef DEBUG
+int last_fault_code; /* For the benefit of pmap_fault_fixup() */
+#endif
+
+struct ksig {
+ int sig;
+ u_long code;
+ vm_offset_t addr;
+};
+
+typedef int abort_func_t(struct trapframe *, u_int, u_int, u_int, u_int,
+ struct thread *, struct ksig *);
+
+static abort_func_t abort_fatal;
+static abort_func_t abort_align;
+static abort_func_t abort_icache;
+
+struct abort {
+ abort_func_t *func;
+ const char *desc;
+};
+
+/*
+ * How are the aborts handled?
+ *
+ * Undefined Code:
+ * - Always fatal as we do not know what does it mean.
+ * Imprecise External Abort:
+ * - Always fatal, but can be handled somehow in the future.
+ * Now, due to PCIe buggy hardware, ignored.
+ * Precise External Abort:
+ * - Always fatal, but who knows in the future???
+ * Debug Event:
+ * - Special handling.
+ * External Translation Abort (L1 & L2)
+ * - Always fatal as something is screwed up in page tables or hardware.
+ * Domain Fault (L1 & L2):
+ * - Always fatal as we do not play game with domains.
+ * Alignment Fault:
+ * - Everything should be aligned in kernel with exception of user to kernel
+ * and vice versa data copying, so if pcb_onfault is not set, it's fatal.
+ * We generate signal in case of abort from user mode.
+ * Instruction cache maintenance:
+ * - According to manual, this is translation fault during cache maintenance
+ * operation. So, it could be really complex in SMP case and fuzzy too
+ * for cache operations working on virtual addresses. For now, we will
+ * consider this abort as fatal. In fact, no cache maintenance on
+ * not mapped virtual addresses should be called. As cache maintenance
+ * operation (except DMB, DSB, and Flush Prefetch Buffer) are priviledged,
+ * the abort is fatal for user mode as well for now. (This is good place to
+ * note that cache maintenance on virtual address fill TLB.)
+ * Acces Bit (L1 & L2):
+ * - Fast hardware emulation for kernel and user mode.
+ * Translation Fault (L1 & L2):
+ * - Standard fault mechanism is held including vm_fault().
+ * Permission Fault (L1 & L2):
+ * - Fast hardware emulation of modify bits and in other cases, standard
+ * fault mechanism is held including vm_fault().
+ */
+
+static const struct abort aborts[] = {
+ {abort_fatal, "Undefined Code (0x000)"},
+ {abort_align, "Alignment Fault"},
+ {abort_fatal, "Debug Event"},
+ {NULL, "Access Bit (L1)"},
+ {NULL, "Instruction cache maintenance"},
+ {NULL, "Translation Fault (L1)"},
+ {NULL, "Access Bit (L2)"},
+ {NULL, "Translation Fault (L2)"},
+
+ {abort_fatal, "External Abort"},
+ {abort_fatal, "Domain Fault (L1)"},
+ {abort_fatal, "Undefined Code (0x00A)"},
+ {abort_fatal, "Domain Fault (L2)"},
+ {abort_fatal, "External Translation Abort (L1)"},
+ {NULL, "Permission Fault (L1)"},
+ {abort_fatal, "External Translation Abort (L2)"},
+ {NULL, "Permission Fault (L2)"},
+
+ {abort_fatal, "TLB Conflict Abort"},
+ {abort_fatal, "Undefined Code (0x401)"},
+ {abort_fatal, "Undefined Code (0x402)"},
+ {abort_fatal, "Undefined Code (0x403)"},
+ {abort_fatal, "Undefined Code (0x404)"},
+ {abort_fatal, "Undefined Code (0x405)"},
+ {abort_fatal, "Asynchronous External Abort"},
+ {abort_fatal, "Undefined Code (0x407)"},
+
+ {abort_fatal, "Asynchronous Parity Error on Memory Access"},
+ {abort_fatal, "Parity Error on Memory Access"},
+ {abort_fatal, "Undefined Code (0x40A)"},
+ {abort_fatal, "Undefined Code (0x40B)"},
+ {abort_fatal, "Parity Error on Translation (L1)"},
+ {abort_fatal, "Undefined Code (0x40D)"},
+ {abort_fatal, "Parity Error on Translation (L2)"},
+ {abort_fatal, "Undefined Code (0x40F)"}
+};
+
+static __inline void
+call_trapsignal(struct thread *td, int sig, int code, vm_offset_t addr,
+ int trapno)
+{
+ ksiginfo_t ksi;
+
+ CTR4(KTR_TRAP, "%s: addr: %#x, sig: %d, code: %d",
+ __func__, addr, sig, code);
+
+ /*
+ * TODO: some info would be nice to know
+ * if we are serving data or prefetch abort.
+ */
+
+ ksiginfo_init_trap(&ksi);
+ ksi.ksi_signo = sig;
+ ksi.ksi_code = code;
+ ksi.ksi_addr = (void *)addr;
+ ksi.ksi_trapno = trapno;
+ trapsignal(td, &ksi);
+}
+
+/*
+ * abort_imprecise() handles the following abort:
+ *
+ * FAULT_EA_IMPREC - Imprecise External Abort
+ *
+ * The imprecise means that we don't know where the abort happened,
+ * thus FAR is undefined. The abort should not never fire, but hot
+ * plugging or accidental hardware failure can be the cause of it.
+ * If the abort happens, it can even be on different (thread) context.
+ * Without any additional support, the abort is fatal, as we do not
+ * know what really happened.
+ *
+ * QQQ: Some additional functionality, like pcb_onfault but global,
+ * can be implemented. Imprecise handlers could be registered
+ * which tell us if the abort is caused by something they know
+ * about. They should return one of three codes like:
+ * FAULT_IS_MINE,
+ * FAULT_CAN_BE_MINE,
+ * FAULT_IS_NOT_MINE.
+ * The handlers should be called until some of them returns
+ * FAULT_IS_MINE value or all was called. If all handlers return
+ * FAULT_IS_NOT_MINE value, then the abort is fatal.
+ */
+static __inline void
+abort_imprecise(struct trapframe *tf, u_int fsr, u_int prefetch, bool usermode)
+{
+
+ /*
+ * XXX - We can got imprecise abort as result of access
+ * to not-present PCI/PCIe configuration space.
+ */
+#if 0
+ goto out;
+#endif
+ abort_fatal(tf, FAULT_EA_IMPREC, fsr, 0, prefetch, curthread, NULL);
+
+ /*
+ * Returning from this function means that we ignore
+ * the abort for good reason. Note that imprecise abort
+ * could fire any time even in user mode.
+ */
+
+#if 0
+out:
+ if (usermode)
+ userret(curthread, tf);
+#endif
+}
+
+/*
+ * abort_debug() handles the following abort:
+ *
+ * FAULT_DEBUG - Debug Event
+ *
+ */
+static __inline void
+abort_debug(struct trapframe *tf, u_int fsr, u_int prefetch, bool usermode,
+ u_int far)
+{
+
+ if (usermode) {
+ struct thread *td;
+
+ td = curthread;
+ call_trapsignal(td, SIGTRAP, TRAP_BRKPT, far, FAULT_DEBUG);
+ userret(td, tf);
+ } else {
+#ifdef KDB
+ kdb_trap((prefetch) ? T_BREAKPOINT : T_WATCHPOINT, 0, tf);
+#else
+ printf("No debugger in kernel.\n");
+#endif
+ }
+}
+
+/*
+ * Abort handler.
+ *
+ * FAR, FSR, and everything what can be lost after enabling
+ * interrupts must be grabbed before the interrupts will be
+ * enabled. Note that when interrupts will be enabled, we
+ * could even migrate to another CPU ...
+ *
+ * TODO: move quick cases to ASM
+ */
+void
+abort_handler(struct trapframe *tf, int prefetch)
+{
+ struct thread *td;
+ vm_offset_t far, va;
+ int idx, rv;
+ uint32_t fsr;
+ struct ksig ksig;
+ struct proc *p;
+ struct pcb *pcb;
+ struct vm_map *map;
+ struct vmspace *vm;
+ vm_prot_t ftype;
+ bool usermode;
+ int bp_harden, ucode;
+#ifdef INVARIANTS
+ void *onfault;
+#endif
+
+ VM_CNT_INC(v_trap);
+ td = curthread;
+
+ fsr = (prefetch) ? cp15_ifsr_get(): cp15_dfsr_get();
+#if __ARM_ARCH >= 7
+ far = (prefetch) ? cp15_ifar_get() : cp15_dfar_get();
+#else
+ far = (prefetch) ? TRAPF_PC(tf) : cp15_dfar_get();
+#endif
+
+ idx = FSR_TO_FAULT(fsr);
+ usermode = TRAPF_USERMODE(tf); /* Abort came from user mode? */
+
+ /*
+ * Apply BP hardening by flushing the branch prediction cache
+ * for prefaults on kernel addresses.
+ */
+ if (__predict_false(prefetch && far > VM_MAXUSER_ADDRESS &&
+ (idx == FAULT_TRAN_L2 || idx == FAULT_PERM_L2))) {
+ bp_harden = PCPU_GET(bp_harden_kind);
+ if (bp_harden == PCPU_BP_HARDEN_KIND_BPIALL)
+ _CP15_BPIALL();
+ else if (bp_harden == PCPU_BP_HARDEN_KIND_ICIALLU)
+ _CP15_ICIALLU();
+ }
+
+ if (usermode)
+ td->td_frame = tf;
+
+ CTR6(KTR_TRAP, "%s: fsr %#x (idx %u) far %#x prefetch %u usermode %d",
+ __func__, fsr, idx, far, prefetch, usermode);
+
+ /*
+ * Firstly, handle aborts that are not directly related to mapping.
+ */
+ if (__predict_false(idx == FAULT_EA_IMPREC)) {
+ abort_imprecise(tf, fsr, prefetch, usermode);
+ return;
+ }
+
+ if (__predict_false(idx == FAULT_DEBUG)) {
+ abort_debug(tf, fsr, prefetch, usermode, far);
+ return;
+ }
+
+ /*
+ * ARM has a set of unprivileged load and store instructions
+ * (LDRT/LDRBT/STRT/STRBT ...) which are supposed to be used in other
+ * than user mode and OS should recognize their aborts and behave
+ * appropriately. However, there is no way how to do that reasonably
+ * in general unless we restrict the handling somehow.
+ *
+ * For now, these instructions are used only in copyin()/copyout()
+ * like functions where usermode buffers are checked in advance that
+ * they are not from KVA space. Thus, no action is needed here.
+ */
+
+ /*
+ * (1) Handle access and R/W hardware emulation aborts.
+ * (2) Check that abort is not on pmap essential address ranges.
+ * There is no way how to fix it, so we don't even try.
+ */
+ rv = pmap_fault(PCPU_GET(curpmap), far, fsr, idx, usermode);
+ if (rv == KERN_SUCCESS)
+ return;
+#ifdef KDB
+ if (kdb_active) {
+ kdb_reenter();
+ goto out;
+ }
+#endif
+ if (rv == KERN_INVALID_ADDRESS)
+ goto nogo;
+
+ if (__predict_false((td->td_pflags & TDP_NOFAULTING) != 0)) {
+ /*
+ * Due to both processor errata and lazy TLB invalidation when
+ * access restrictions are removed from virtual pages, memory
+ * accesses that are allowed by the physical mapping layer may
+ * nonetheless cause one spurious page fault per virtual page.
+ * When the thread is executing a "no faulting" section that
+ * is bracketed by vm_fault_{disable,enable}_pagefaults(),
+ * every page fault is treated as a spurious page fault,
+ * unless it accesses the same virtual address as the most
+ * recent page fault within the same "no faulting" section.
+ */
+ if (td->td_md.md_spurflt_addr != far ||
+ (td->td_pflags & TDP_RESETSPUR) != 0) {
+ td->td_md.md_spurflt_addr = far;
+ td->td_pflags &= ~TDP_RESETSPUR;
+
+ tlb_flush_local(far & ~PAGE_MASK);
+ return;
+ }
+ } else {
+ /*
+ * If we get a page fault while in a critical section, then
+ * it is most likely a fatal kernel page fault. The kernel
+ * is already going to panic trying to get a sleep lock to
+ * do the VM lookup, so just consider it a fatal trap so the
+ * kernel can print out a useful trap message and even get
+ * to the debugger.
+ *
+ * If we get a page fault while holding a non-sleepable
+ * lock, then it is most likely a fatal kernel page fault.
+ * If WITNESS is enabled, then it's going to whine about
+ * bogus LORs with various VM locks, so just skip to the
+ * fatal trap handling directly.
+ */
+ if (td->td_critnest != 0 ||
+ WITNESS_CHECK(WARN_SLEEPOK | WARN_GIANTOK, NULL,
+ "Kernel page fault") != 0) {
+ abort_fatal(tf, idx, fsr, far, prefetch, td, &ksig);
+ return;
+ }
+ }
+
+ /* Re-enable interrupts if they were enabled previously. */
+ if (td->td_md.md_spinlock_count == 0) {
+ if (__predict_true(tf->tf_spsr & PSR_I) == 0)
+ enable_interrupts(PSR_I);
+ if (__predict_true(tf->tf_spsr & PSR_F) == 0)
+ enable_interrupts(PSR_F);
+ }
+
+ p = td->td_proc;
+ if (usermode) {
+ td->td_pticks = 0;
+ if (td->td_cowgen != p->p_cowgen)
+ thread_cow_update(td);
+ }
+
+ /* Invoke the appropriate handler, if necessary. */
+ if (__predict_false(aborts[idx].func != NULL)) {
+ if ((aborts[idx].func)(tf, idx, fsr, far, prefetch, td, &ksig))
+ goto do_trapsignal;
+ goto out;
+ }
+
+ /*
+ * At this point, we're dealing with one of the following aborts:
+ *
+ * FAULT_ICACHE - I-cache maintenance
+ * FAULT_TRAN_xx - Translation
+ * FAULT_PERM_xx - Permission
+ */
+
+ /*
+ * Don't pass faulting cache operation to vm_fault(). We don't want
+ * to handle all vm stuff at this moment.
+ */
+ pcb = td->td_pcb;
+ if (__predict_false(pcb->pcb_onfault == cachebailout)) {
+ tf->tf_r0 = far; /* return failing address */
+ tf->tf_pc = (register_t)pcb->pcb_onfault;
+ return;
+ }
+
+ /* Handle remaining I-cache aborts. */
+ if (idx == FAULT_ICACHE) {
+ if (abort_icache(tf, idx, fsr, far, prefetch, td, &ksig))
+ goto do_trapsignal;
+ goto out;
+ }
+
+ va = trunc_page(far);
+ if (va >= KERNBASE) {
+ /*
+ * Don't allow user-mode faults in kernel address space.
+ */
+ if (usermode) {
+ ksig.sig = SIGSEGV;
+ ksig.code = SEGV_ACCERR;
+ goto nogo;
+ }
+
+ map = kernel_map;
+ } else {
+ /*
+ * This is a fault on non-kernel virtual memory. If curproc
+ * is NULL or curproc->p_vmspace is NULL the fault is fatal.
+ */
+ vm = (p != NULL) ? p->p_vmspace : NULL;
+ if (vm == NULL) {
+ ksig.sig = SIGSEGV;
+ ksig.code = 0;
+ goto nogo;
+ }
+
+ map = &vm->vm_map;
+ if (!usermode && (td->td_intr_nesting_level != 0 ||
+ pcb->pcb_onfault == NULL)) {
+ abort_fatal(tf, idx, fsr, far, prefetch, td, &ksig);
+ return;
+ }
+ }
+
+ ftype = (fsr & FSR_WNR) ? VM_PROT_WRITE : VM_PROT_READ;
+ if (prefetch)
+ ftype |= VM_PROT_EXECUTE;
+
+#ifdef DEBUG
+ last_fault_code = fsr;
+#endif
+
+#ifdef INVARIANTS
+ onfault = pcb->pcb_onfault;
+ pcb->pcb_onfault = NULL;
+#endif
+
+ /* Fault in the page. */
+ rv = vm_fault_trap(map, va, ftype, VM_FAULT_NORMAL, &ksig.sig,
+ &ucode);
+ ksig.code = ucode;
+
+#ifdef INVARIANTS
+ pcb->pcb_onfault = onfault;
+#endif
+
+ if (__predict_true(rv == KERN_SUCCESS))
+ goto out;
+nogo:
+ if (!usermode) {
+ if (td->td_intr_nesting_level == 0 &&
+ pcb->pcb_onfault != NULL) {
+ tf->tf_r0 = rv;
+ tf->tf_pc = (int)pcb->pcb_onfault;
+ return;
+ }
+ CTR2(KTR_TRAP, "%s: vm_fault() failed with %d", __func__, rv);
+ abort_fatal(tf, idx, fsr, far, prefetch, td, &ksig);
+ return;
+ }
+
+ ksig.addr = far;
+
+do_trapsignal:
+ call_trapsignal(td, ksig.sig, ksig.code, ksig.addr, idx);
+out:
+ if (usermode)
+ userret(td, tf);
+}
+
+/*
+ * abort_fatal() handles the following data aborts:
+ *
+ * FAULT_DEBUG - Debug Event
+ * FAULT_ACCESS_xx - Acces Bit
+ * FAULT_EA_PREC - Precise External Abort
+ * FAULT_DOMAIN_xx - Domain Fault
+ * FAULT_EA_TRAN_xx - External Translation Abort
+ * FAULT_EA_IMPREC - Imprecise External Abort
+ * + all undefined codes for ABORT
+ *
+ * We should never see these on a properly functioning system.
+ *
+ * This function is also called by the other handlers if they
+ * detect a fatal problem.
+ *
+ * Note: If 'l' is NULL, we assume we're dealing with a prefetch abort.
+ */
+static int
+abort_fatal(struct trapframe *tf, u_int idx, u_int fsr, u_int far,
+ u_int prefetch, struct thread *td, struct ksig *ksig)
+{
+ bool usermode;
+ const char *mode;
+ const char *rw_mode;
+#ifdef KDB
+ bool handled;
+#endif
+
+ usermode = TRAPF_USERMODE(tf);
+#ifdef KDTRACE_HOOKS
+ if (!usermode) {
+ if (dtrace_trap_func != NULL && (*dtrace_trap_func)(tf, far))
+ return (0);
+ }
+#endif
+
+ mode = usermode ? "user" : "kernel";
+ rw_mode = fsr & FSR_WNR ? "write" : "read";
+ disable_interrupts(PSR_I|PSR_F);
+
+ if (td != NULL) {
+ printf("Fatal %s mode data abort: '%s' on %s\n", mode,
+ aborts[idx].desc, rw_mode);
+ printf("trapframe: %p\nFSR=%08x, FAR=", tf, fsr);
+ if (idx != FAULT_EA_IMPREC)
+ printf("%08x, ", far);
+ else
+ printf("Invalid, ");
+ printf("spsr=%08x\n", tf->tf_spsr);
+ } else {
+ printf("Fatal %s mode prefetch abort at 0x%08x\n",
+ mode, tf->tf_pc);
+ printf("trapframe: %p, spsr=%08x\n", tf, tf->tf_spsr);
+ }
+
+ printf("r0 =%08x, r1 =%08x, r2 =%08x, r3 =%08x\n",
+ tf->tf_r0, tf->tf_r1, tf->tf_r2, tf->tf_r3);
+ printf("r4 =%08x, r5 =%08x, r6 =%08x, r7 =%08x\n",
+ tf->tf_r4, tf->tf_r5, tf->tf_r6, tf->tf_r7);
+ printf("r8 =%08x, r9 =%08x, r10=%08x, r11=%08x\n",
+ tf->tf_r8, tf->tf_r9, tf->tf_r10, tf->tf_r11);
+ printf("r12=%08x, ", tf->tf_r12);
+
+ if (usermode)
+ printf("usp=%08x, ulr=%08x",
+ tf->tf_usr_sp, tf->tf_usr_lr);
+ else
+ printf("ssp=%08x, slr=%08x",
+ tf->tf_svc_sp, tf->tf_svc_lr);
+ printf(", pc =%08x\n\n", tf->tf_pc);
+
+#ifdef KDB
+ if (debugger_on_trap) {
+ kdb_why = KDB_WHY_TRAP;
+ handled = kdb_trap(fsr, 0, tf);
+ kdb_why = KDB_WHY_UNSET;
+ if (handled)
+ return (0);
+ }
+#endif
+ panic("Fatal abort");
+ /*NOTREACHED*/
+}
+
+/*
+ * abort_align() handles the following data abort:
+ *
+ * FAULT_ALIGN - Alignment fault
+ *
+ * Everything should be aligned in kernel with exception of user to kernel
+ * and vice versa data copying, so if pcb_onfault is not set, it's fatal.
+ * We generate signal in case of abort from user mode.
+ */
+static int
+abort_align(struct trapframe *tf, u_int idx, u_int fsr, u_int far,
+ u_int prefetch, struct thread *td, struct ksig *ksig)
+{
+ bool usermode;
+
+ usermode = TRAPF_USERMODE(tf);
+ if (!usermode) {
+ if (td->td_intr_nesting_level == 0 && td != NULL &&
+ td->td_pcb->pcb_onfault != NULL) {
+ tf->tf_r0 = EFAULT;
+ tf->tf_pc = (int)td->td_pcb->pcb_onfault;
+ return (0);
+ }
+ abort_fatal(tf, idx, fsr, far, prefetch, td, ksig);
+ }
+ /* Deliver a bus error signal to the process */
+ ksig->code = BUS_ADRALN;
+ ksig->sig = SIGBUS;
+ ksig->addr = far;
+ return (1);
+}
+
+/*
+ * abort_icache() handles the following data abort:
+ *
+ * FAULT_ICACHE - Instruction cache maintenance
+ *
+ * According to manual, FAULT_ICACHE is translation fault during cache
+ * maintenance operation. In fact, no cache maintenance operation on
+ * not mapped virtual addresses should be called. As cache maintenance
+ * operation (except DMB, DSB, and Flush Prefetch Buffer) are priviledged,
+ * the abort is concider as fatal for now. However, all the matter with
+ * cache maintenance operation on virtual addresses could be really complex
+ * and fuzzy in SMP case, so maybe in future standard fault mechanism
+ * should be held here including vm_fault() calling.
+ */
+static int
+abort_icache(struct trapframe *tf, u_int idx, u_int fsr, u_int far,
+ u_int prefetch, struct thread *td, struct ksig *ksig)
+{
+
+ abort_fatal(tf, idx, fsr, far, prefetch, td, ksig);
+ return(0);
+}
diff --git a/sys/arm/arm/uio_machdep.c b/sys/arm/arm/uio_machdep.c
new file mode 100644
index 000000000000..45f39cc4e734
--- /dev/null
+++ b/sys/arm/arm/uio_machdep.c
@@ -0,0 +1,126 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 2004 Alan L. Cox <alc@cs.rice.edu>
+ * Copyright (c) 1982, 1986, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)kern_subr.c 8.3 (Berkeley) 1/21/94
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/uio.h>
+#include <sys/sf_buf.h>
+
+#include <vm/vm.h>
+#include <vm/vm_page.h>
+
+#include <machine/vmparam.h>
+
+/*
+ * Implement uiomove(9) from physical memory using sf_bufs to
+ * avoid the creation and destruction of ephemeral mappings.
+ */
+int
+uiomove_fromphys(vm_page_t ma[], vm_offset_t offset, int n, struct uio *uio)
+{
+ struct thread *td = curthread;
+ struct iovec *iov;
+ void *cp;
+ vm_offset_t page_offset;
+ size_t cnt;
+ int error = 0;
+ int save = 0;
+ struct sf_buf *sf;
+
+ KASSERT(uio->uio_rw == UIO_READ || uio->uio_rw == UIO_WRITE,
+ ("uiomove_fromphys: mode"));
+ KASSERT(uio->uio_segflg != UIO_USERSPACE || uio->uio_td == curthread,
+ ("uiomove_fromphys proc"));
+ save = td->td_pflags & TDP_DEADLKTREAT;
+ td->td_pflags |= TDP_DEADLKTREAT;
+ while (n > 0 && uio->uio_resid) {
+ iov = uio->uio_iov;
+ cnt = iov->iov_len;
+ if (cnt == 0) {
+ uio->uio_iov++;
+ uio->uio_iovcnt--;
+ continue;
+ }
+ if (cnt > n)
+ cnt = n;
+ page_offset = offset & PAGE_MASK;
+ cnt = min(cnt, PAGE_SIZE - page_offset);
+ sf = sf_buf_alloc(ma[offset >> PAGE_SHIFT], 0);
+ cp = (char*)sf_buf_kva(sf) + page_offset;
+ switch (uio->uio_segflg) {
+ case UIO_USERSPACE:
+ maybe_yield();
+ if (uio->uio_rw == UIO_READ)
+ error = copyout(cp, iov->iov_base, cnt);
+ else
+ error = copyin(iov->iov_base, cp, cnt);
+ if (error) {
+ sf_buf_free(sf);
+ goto out;
+ }
+ break;
+ case UIO_SYSSPACE:
+ if (uio->uio_rw == UIO_READ)
+ bcopy(cp, iov->iov_base, cnt);
+ else
+ bcopy(iov->iov_base, cp, cnt);
+ break;
+ case UIO_NOCOPY:
+ break;
+ }
+ sf_buf_free(sf);
+ iov->iov_base = (char *)iov->iov_base + cnt;
+ iov->iov_len -= cnt;
+ uio->uio_resid -= cnt;
+ uio->uio_offset += cnt;
+ offset += cnt;
+ n -= cnt;
+ }
+out:
+ if (save == 0)
+ td->td_pflags &= ~TDP_DEADLKTREAT;
+ return (error);
+}
diff --git a/sys/arm/arm/undefined.c b/sys/arm/arm/undefined.c
new file mode 100644
index 000000000000..ef9e72c89163
--- /dev/null
+++ b/sys/arm/arm/undefined.c
@@ -0,0 +1,348 @@
+/* $NetBSD: undefined.c,v 1.22 2003/11/29 22:21:29 bjh21 Exp $ */
+
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 2001 Ben Harris.
+ * Copyright (c) 1995 Mark Brinicombe.
+ * Copyright (c) 1995 Brini.
+ * All rights reserved.
+ *
+ * This code is derived from software written for Brini by Mark Brinicombe
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Brini.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * RiscBSD kernel project
+ *
+ * undefined.c
+ *
+ * Fault handler
+ *
+ * Created : 06/01/95
+ */
+
+#include "opt_ddb.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+#include <sys/signal.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/syslog.h>
+#include <sys/vmmeter.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/signalvar.h>
+#include <sys/ptrace.h>
+#include <sys/vmmeter.h>
+#ifdef KDB
+#include <sys/kdb.h>
+#endif
+
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+
+#include <machine/armreg.h>
+#include <machine/asm.h>
+#include <machine/cpu.h>
+#include <machine/frame.h>
+#include <machine/undefined.h>
+#include <machine/trap.h>
+
+#include <machine/disassem.h>
+
+#ifdef DDB
+#include <ddb/db_output.h>
+#endif
+
+#ifdef KDB
+#include <machine/db_machdep.h>
+#endif
+
+#define ARM_COPROC_INSN(insn) (((insn) & (1 << 27)) != 0)
+#define ARM_VFP_INSN(insn) ((((insn) & 0xfe000000) == 0xf2000000) || \
+ (((insn) & 0xff100000) == 0xf4000000))
+#define ARM_COPROC(insn) (((insn) >> 8) & 0xf)
+
+#define THUMB_32BIT_INSN(insn) ((insn) >= 0xe800)
+#define THUMB_COPROC_INSN(insn) (((insn) & (3 << 26)) == (3 << 26))
+#define THUMB_COPROC_UNDEFINED(insn) (((insn) & 0x3e << 20) == 0)
+#define THUMB_VFP_INSN(insn) (((insn) & (3 << 24)) == (3 << 24))
+#define THUMB_COPROC(insn) (((insn) >> 8) & 0xf)
+
+#define COPROC_VFP 10
+
+static int gdb_trapper(u_int, u_int, struct trapframe *, int);
+
+LIST_HEAD(, undefined_handler) undefined_handlers[MAX_COPROCS];
+
+void *
+install_coproc_handler(int coproc, undef_handler_t handler)
+{
+ struct undefined_handler *uh;
+
+ KASSERT(coproc >= 0 && coproc < MAX_COPROCS, ("bad coproc"));
+ KASSERT(handler != NULL, ("handler is NULL")); /* Used to be legal. */
+
+ /* XXX: M_TEMP??? */
+ uh = malloc(sizeof(*uh), M_TEMP, M_WAITOK);
+ uh->uh_handler = handler;
+ install_coproc_handler_static(coproc, uh);
+ return uh;
+}
+
+void
+install_coproc_handler_static(int coproc, struct undefined_handler *uh)
+{
+
+ LIST_INSERT_HEAD(&undefined_handlers[coproc], uh, uh_link);
+}
+
+void
+remove_coproc_handler(void *cookie)
+{
+ struct undefined_handler *uh = cookie;
+
+ LIST_REMOVE(uh, uh_link);
+ free(uh, M_TEMP);
+}
+
+static int
+gdb_trapper(u_int addr, u_int insn, struct trapframe *frame, int code)
+{
+ struct thread *td;
+ ksiginfo_t ksi;
+ int error;
+
+ td = (curthread == NULL) ? &thread0 : curthread;
+
+ if (insn == GDB_BREAKPOINT || insn == GDB5_BREAKPOINT) {
+ if (code == FAULT_USER) {
+ ksiginfo_init_trap(&ksi);
+ ksi.ksi_signo = SIGTRAP;
+ ksi.ksi_code = TRAP_BRKPT;
+ ksi.ksi_addr = (u_int32_t *)addr;
+ trapsignal(td, &ksi);
+ return 0;
+ }
+#if 0
+#ifdef KGDB
+ return !kgdb_trap(T_BREAKPOINT, frame);
+#endif
+#endif
+ }
+
+ if (code == FAULT_USER) {
+ /* TODO: No support for ptrace from Thumb-2 */
+ if ((frame->tf_spsr & PSR_T) == 0 &&
+ insn == PTRACE_BREAKPOINT) {
+ PROC_LOCK(td->td_proc);
+ _PHOLD(td->td_proc);
+ error = ptrace_clear_single_step(td);
+ _PRELE(td->td_proc);
+ PROC_UNLOCK(td->td_proc);
+ if (error == 0) {
+ ksiginfo_init_trap(&ksi);
+ ksi.ksi_signo = SIGTRAP;
+ ksi.ksi_code = TRAP_TRACE;
+ ksi.ksi_addr = (u_int32_t *)addr;
+ trapsignal(td, &ksi);
+ return (0);
+ }
+ }
+ }
+
+ return 1;
+}
+
+static struct undefined_handler gdb_uh;
+
+void
+undefined_init(void)
+{
+ int loop;
+
+ /* Not actually necessary -- the initialiser is just NULL */
+ for (loop = 0; loop < MAX_COPROCS; ++loop)
+ LIST_INIT(&undefined_handlers[loop]);
+
+ /* Install handler for GDB breakpoints */
+ gdb_uh.uh_handler = gdb_trapper;
+ install_coproc_handler_static(0, &gdb_uh);
+}
+
+void
+undefinedinstruction(struct trapframe *frame)
+{
+ struct thread *td;
+ u_int fault_pc;
+ int fault_instruction;
+ int fault_code;
+ int coprocessor;
+ struct undefined_handler *uh;
+#ifdef VERBOSE_ARM32
+ int s;
+#endif
+ ksiginfo_t ksi;
+
+ /* Enable interrupts if they were enabled before the exception. */
+ if (__predict_true(frame->tf_spsr & PSR_I) == 0)
+ enable_interrupts(PSR_I);
+ if (__predict_true(frame->tf_spsr & PSR_F) == 0)
+ enable_interrupts(PSR_F);
+
+ VM_CNT_INC(v_trap);
+
+ fault_pc = frame->tf_pc;
+
+ /*
+ * Get the current thread/proc structure or thread0/proc0 if there is
+ * none.
+ */
+ td = curthread == NULL ? &thread0 : curthread;
+
+ coprocessor = 0;
+ if ((frame->tf_spsr & PSR_T) == 0) {
+ /*
+ * Make sure the program counter is correctly aligned so we
+ * don't take an alignment fault trying to read the opcode.
+ */
+ if (__predict_false((fault_pc & 3) != 0)) {
+ ksiginfo_init_trap(&ksi);
+ ksi.ksi_signo = SIGILL;
+ ksi.ksi_code = ILL_ILLADR;
+ ksi.ksi_addr = (u_int32_t *)(intptr_t) fault_pc;
+ trapsignal(td, &ksi);
+ userret(td, frame);
+ return;
+ }
+
+ /*
+ * Should use fuword() here .. but in the interests of
+ * squeezing every bit of speed we will just use ReadWord().
+ * We know the instruction can be read as was just executed
+ * so this will never fail unless the kernel is screwed up
+ * in which case it does not really matter does it ?
+ */
+
+ fault_instruction = *(u_int32_t *)fault_pc;
+
+ /* Check for coprocessor instruction */
+
+ /*
+ * According to the datasheets you only need to look at bit
+ * 27 of the instruction to tell the difference between and
+ * undefined instruction and a coprocessor instruction
+ * following an undefined instruction trap.
+ */
+
+ if (ARM_COPROC_INSN(fault_instruction))
+ coprocessor = ARM_COPROC(fault_instruction);
+ else { /* check for special instructions */
+ if (ARM_VFP_INSN(fault_instruction))
+ coprocessor = COPROC_VFP; /* vfp / simd */
+ }
+ } else {
+#if __ARM_ARCH >= 7
+ fault_instruction = *(uint16_t *)fault_pc;
+ if (THUMB_32BIT_INSN(fault_instruction)) {
+ fault_instruction <<= 16;
+ fault_instruction |= *(uint16_t *)(fault_pc + 2);
+
+ /*
+ * Is it a Coprocessor, Advanced SIMD, or
+ * Floating-point instruction.
+ */
+ if (THUMB_COPROC_INSN(fault_instruction)) {
+ if (THUMB_COPROC_UNDEFINED(fault_instruction)) {
+ /* undefined insn */
+ } else if (THUMB_VFP_INSN(fault_instruction))
+ coprocessor = COPROC_VFP;
+ else
+ coprocessor =
+ THUMB_COPROC(fault_instruction);
+ }
+ }
+#else
+ /*
+ * No support for Thumb-2 on this cpu
+ */
+ ksiginfo_init_trap(&ksi);
+ ksi.ksi_signo = SIGILL;
+ ksi.ksi_code = ILL_ILLADR;
+ ksi.ksi_addr = (u_int32_t *)(intptr_t) fault_pc;
+ trapsignal(td, &ksi);
+ userret(td, frame);
+ return;
+#endif
+ }
+
+ if ((frame->tf_spsr & PSR_MODE) == PSR_USR32_MODE) {
+ /*
+ * Modify the fault_code to reflect the USR/SVC state at
+ * time of fault.
+ */
+ fault_code = FAULT_USER;
+ td->td_frame = frame;
+ } else
+ fault_code = 0;
+
+ /* OK this is were we do something about the instruction. */
+ LIST_FOREACH(uh, &undefined_handlers[coprocessor], uh_link)
+ if (uh->uh_handler(fault_pc, fault_instruction, frame,
+ fault_code) == 0)
+ break;
+
+ if (uh == NULL && (fault_code & FAULT_USER)) {
+ /* Fault has not been handled */
+ ksiginfo_init_trap(&ksi);
+ ksi.ksi_signo = SIGILL;
+ ksi.ksi_code = ILL_ILLOPC;
+ ksi.ksi_addr = (u_int32_t *)(intptr_t) fault_pc;
+ trapsignal(td, &ksi);
+ }
+
+ if ((fault_code & FAULT_USER) == 0) {
+ if (fault_instruction == KERNEL_BREAKPOINT) {
+#ifdef KDB
+ kdb_trap(T_BREAKPOINT, 0, frame);
+#else
+ printf("No debugger in kernel.\n");
+#endif
+ return;
+ }
+ else
+ panic("Undefined instruction in kernel.\n");
+ }
+
+ userret(td, frame);
+}
diff --git a/sys/arm/arm/unwind.c b/sys/arm/arm/unwind.c
new file mode 100644
index 000000000000..bf8ffddfd2c2
--- /dev/null
+++ b/sys/arm/arm/unwind.c
@@ -0,0 +1,575 @@
+/*
+ * Copyright 2013-2014 Andrew Turner.
+ * Copyright 2013-2014 Ian Lepore.
+ * Copyright 2013-2014 Rui Paulo.
+ * Copyright 2013 Eitan Adler.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/linker.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+#include <sys/systm.h>
+
+#include <machine/machdep.h>
+#include <machine/stack.h>
+
+#include "linker_if.h"
+
+/*
+ * Definitions for the instruction interpreter.
+ *
+ * The ARM EABI specifies how to perform the frame unwinding in the
+ * Exception Handling ABI for the ARM Architecture document. To perform
+ * the unwind we need to know the initial frame pointer, stack pointer,
+ * link register and program counter. We then find the entry within the
+ * index table that points to the function the program counter is within.
+ * This gives us either a list of three instructions to process, a 31-bit
+ * relative offset to a table of instructions, or a value telling us
+ * we can't unwind any further.
+ *
+ * When we have the instructions to process we need to decode them
+ * following table 4 in section 9.3. This describes a collection of bit
+ * patterns to encode that steps to take to update the stack pointer and
+ * link register to the correct values at the start of the function.
+ */
+
+/* A special case when we are unable to unwind past this function */
+#define EXIDX_CANTUNWIND 1
+
+/*
+ * Entry types.
+ * These are the only entry types that have been seen in the kernel.
+ */
+#define ENTRY_MASK 0xff000000
+#define ENTRY_ARM_SU16 0x80000000
+#define ENTRY_ARM_LU16 0x81000000
+
+/* Instruction masks. */
+#define INSN_VSP_MASK 0xc0
+#define INSN_VSP_SIZE_MASK 0x3f
+#define INSN_STD_MASK 0xf0
+#define INSN_STD_DATA_MASK 0x0f
+#define INSN_POP_TYPE_MASK 0x08
+#define INSN_POP_COUNT_MASK 0x07
+#define INSN_VSP_LARGE_INC_MASK 0xff
+
+/* Instruction definitions */
+#define INSN_VSP_INC 0x00
+#define INSN_VSP_DEC 0x40
+#define INSN_POP_MASKED 0x80
+#define INSN_VSP_REG 0x90
+#define INSN_POP_COUNT 0xa0
+#define INSN_FINISH 0xb0
+#define INSN_POP_REGS 0xb1
+#define INSN_VSP_LARGE_INC 0xb2
+
+/* An item in the exception index table */
+struct unwind_idx {
+ uint32_t offset;
+ uint32_t insn;
+};
+
+/*
+ * Local cache of unwind info for loaded modules.
+ *
+ * To unwind the stack through the code in a loaded module, we need to access
+ * the module's exidx unwind data. To locate that data, one must search the
+ * elf section headers for the SHT_ARM_EXIDX section. Those headers are
+ * available at the time the module is being loaded, but are discarded by time
+ * the load process has completed. Code in kern/link_elf.c locates the data we
+ * need and stores it into the linker_file structure before calling the arm
+ * machdep routine for handling loaded modules (in arm/elf_machdep.c). That
+ * function calls into this code to pass along the unwind info, which we save
+ * into one of these module_info structures.
+ *
+ * Because we have to help stack(9) gather stack info at any time, including in
+ * contexts where sleeping is not allowed, we cannot use linker_file_foreach()
+ * to walk the kernel's list of linker_file structs, because doing so requires
+ * acquiring an exclusive sx_lock. So instead, we keep a local list of these
+ * structures, one for each loaded module (and one for the kernel itself that we
+ * synthesize at init time). New entries are added to the end of this list as
+ * needed, but entries are never deleted from the list. Instead, they are
+ * cleared out in-place to mark them as unused. That means the code doing stack
+ * unwinding can always safely walk the list without locking, because the
+ * structure of the list never changes in a way that would cause the walker to
+ * follow a bad link.
+ *
+ * A cleared-out entry on the list has module start=UINTPTR_MAX and end=0, so
+ * start <= addr < end cannot be true for any value of addr being searched for.
+ * We also don't have to worry about races where we look up the unwind info just
+ * before a module is unloaded and try to access it concurrently with or just
+ * after the unloading happens in another thread, because that means the path of
+ * execution leads through a now-unloaded module, and that's already well into
+ * undefined-behavior territory.
+ *
+ * List entries marked as unused get reused when new modules are loaded. We
+ * don't worry about holding a few unused bytes of memory in the list after
+ * unloading a module.
+ */
+struct module_info {
+ uintptr_t module_start; /* Start of loaded module */
+ uintptr_t module_end; /* End of loaded module */
+ uintptr_t exidx_start; /* Start of unwind data */
+ uintptr_t exidx_end; /* End of unwind data */
+ STAILQ_ENTRY(module_info)
+ link; /* Link to next entry */
+};
+static STAILQ_HEAD(, module_info) module_list;
+
+/*
+ * Hide ugly casting in somewhat-less-ugly macros.
+ * CADDR - cast a pointer or number to caddr_t.
+ * UADDR - cast a pointer or number to uintptr_t.
+ */
+#define CADDR(addr) ((caddr_t)(void*)(uintptr_t)(addr))
+#define UADDR(addr) ((uintptr_t)(addr))
+
+/*
+ * Clear the info in an existing module_info entry on the list. The
+ * module_start/end addresses are set to values that cannot match any real
+ * memory address. The entry remains on the list, but will be ignored until it
+ * is populated with new data.
+ */
+static void
+clear_module_info(struct module_info *info)
+{
+ info->module_start = UINTPTR_MAX;
+ info->module_end = 0;
+}
+
+/*
+ * Populate an existing module_info entry (which is already on the list) with
+ * the info for a new module.
+ */
+static void
+populate_module_info(struct module_info *info, linker_file_t lf)
+{
+
+ /*
+ * Careful! The module_start and module_end fields must not be set
+ * until all other data in the structure is valid.
+ */
+ info->exidx_start = UADDR(lf->exidx_addr);
+ info->exidx_end = UADDR(lf->exidx_addr) + lf->exidx_size;
+ info->module_start = UADDR(lf->address);
+ info->module_end = UADDR(lf->address) + lf->size;
+}
+
+/*
+ * Create a new empty module_info entry and add it to the tail of the list.
+ */
+static struct module_info *
+create_module_info(void)
+{
+ struct module_info *info;
+
+ info = malloc(sizeof(*info), M_CACHE, M_WAITOK | M_ZERO);
+ clear_module_info(info);
+ STAILQ_INSERT_TAIL(&module_list, info, link);
+ return (info);
+}
+
+/*
+ * Search for a module_info entry on the list whose address range contains the
+ * given address. If the search address is zero (no module will be loaded at
+ * zero), then we're looking for an empty item to reuse, which is indicated by
+ * module_start being set to UINTPTR_MAX in the entry.
+ */
+static struct module_info *
+find_module_info(uintptr_t addr)
+{
+ struct module_info *info;
+
+ STAILQ_FOREACH(info, &module_list, link) {
+ if ((addr >= info->module_start && addr < info->module_end) ||
+ (addr == 0 && info->module_start == UINTPTR_MAX))
+ return (info);
+ }
+ return (NULL);
+}
+
+/*
+ * Handle the loading of a new module by populating a module_info for it. This
+ * is called for both preloaded and dynamically loaded modules.
+ */
+void
+unwind_module_loaded(struct linker_file *lf)
+{
+ struct module_info *info;
+
+ /*
+ * A module that contains only data may have no unwind info; don't
+ * create any module info for it.
+ */
+ if (lf->exidx_size == 0)
+ return;
+
+ /*
+ * Find an unused entry in the existing list to reuse. If we don't find
+ * one, create a new one and link it into the list. This is the only
+ * place the module_list is modified. Adding a new entry to the list
+ * will not perturb any other threads currently walking the list. This
+ * function is invoked while kern_linker is still holding its lock
+ * to prevent its module list from being modified, so we don't have to
+ * worry about racing other threads doing an insert concurrently.
+ */
+ if ((info = find_module_info(0)) == NULL) {
+ info = create_module_info();
+ }
+ populate_module_info(info, lf);
+}
+
+/* Handle the unloading of a module. */
+void
+unwind_module_unloaded(struct linker_file *lf)
+{
+ struct module_info *info;
+
+ /*
+ * A module that contains only data may have no unwind info and there
+ * won't be a list entry for it.
+ */
+ if (lf->exidx_size == 0)
+ return;
+
+ /*
+ * When a module is unloaded, we clear the info out of its entry in the
+ * module list, making that entry available for later reuse.
+ */
+ if ((info = find_module_info(UADDR(lf->address))) == NULL) {
+ printf("arm unwind: module '%s' not on list at unload time\n",
+ lf->filename);
+ return;
+ }
+ clear_module_info(info);
+}
+
+/*
+ * Initialization must run fairly early, as soon as malloc(9) is available, and
+ * definitely before witness, which uses stack(9). We synthesize a module_info
+ * entry for the kernel, because unwind_module_loaded() doesn't get called for
+ * it. Also, it is unlike other modules in that the elf metadata for locating
+ * the unwind tables might be stripped, so instead we have to use the
+ * _exidx_start/end symbols created by ldscript.arm.
+ */
+static int
+module_info_init(void *arg __unused)
+{
+ struct linker_file thekernel;
+
+ STAILQ_INIT(&module_list);
+
+ thekernel.filename = "kernel";
+ thekernel.address = CADDR(&_start);
+ thekernel.size = UADDR(&_end) - UADDR(&_start);
+ thekernel.exidx_addr = CADDR(&_exidx_start);
+ thekernel.exidx_size = UADDR(&_exidx_end) - UADDR(&_exidx_start);
+ populate_module_info(create_module_info(), &thekernel);
+
+ return (0);
+}
+SYSINIT(unwind_init, SI_SUB_KMEM, SI_ORDER_ANY, module_info_init, NULL);
+
+/* Expand a 31-bit signed value to a 32-bit signed value */
+static __inline int32_t
+expand_prel31(uint32_t prel31)
+{
+
+ return ((int32_t)(prel31 & 0x7fffffffu) << 1) / 2;
+}
+
+/*
+ * Perform a binary search of the index table to find the function
+ * with the largest address that doesn't exceed addr.
+ */
+static struct unwind_idx *
+find_index(uint32_t addr)
+{
+ struct module_info *info;
+ unsigned int min, mid, max;
+ struct unwind_idx *start;
+ struct unwind_idx *item;
+ int32_t prel31_addr;
+ uint32_t func_addr;
+
+ info = find_module_info(addr);
+ if (info == NULL)
+ return NULL;
+
+ min = 0;
+ max = (info->exidx_end - info->exidx_start) / sizeof(struct unwind_idx);
+ start = (struct unwind_idx *)CADDR(info->exidx_start);
+
+ while (min != max) {
+ mid = min + (max - min + 1) / 2;
+
+ item = &start[mid];
+
+ prel31_addr = expand_prel31(item->offset);
+ func_addr = (uint32_t)&item->offset + prel31_addr;
+
+ if (func_addr <= addr) {
+ min = mid;
+ } else {
+ max = mid - 1;
+ }
+ }
+
+ return &start[min];
+}
+
+/* Reads the next byte from the instruction list */
+static uint8_t
+unwind_exec_read_byte(struct unwind_state *state)
+{
+ uint8_t insn;
+
+ /* Read the unwind instruction */
+ insn = (*state->insn) >> (state->byte * 8);
+
+ /* Update the location of the next instruction */
+ if (state->byte == 0) {
+ state->byte = 3;
+ state->insn++;
+ state->entries--;
+ } else
+ state->byte--;
+
+ return insn;
+}
+
+/* Executes the next instruction on the list */
+static int
+unwind_exec_insn(struct unwind_state *state)
+{
+ unsigned int insn;
+ uint32_t *vsp = (uint32_t *)state->registers[SP];
+ int update_vsp = 0;
+
+ /* This should never happen */
+ if (state->entries == 0)
+ return 1;
+
+ /* Read the next instruction */
+ insn = unwind_exec_read_byte(state);
+
+ if ((insn & INSN_VSP_MASK) == INSN_VSP_INC) {
+ state->registers[SP] += ((insn & INSN_VSP_SIZE_MASK) << 2) + 4;
+
+ } else if ((insn & INSN_VSP_MASK) == INSN_VSP_DEC) {
+ state->registers[SP] -= ((insn & INSN_VSP_SIZE_MASK) << 2) + 4;
+
+ } else if ((insn & INSN_STD_MASK) == INSN_POP_MASKED) {
+ unsigned int mask, reg;
+
+ /* Load the mask */
+ mask = unwind_exec_read_byte(state);
+ mask |= (insn & INSN_STD_DATA_MASK) << 8;
+
+ /* We have a refuse to unwind instruction */
+ if (mask == 0)
+ return 1;
+
+ /* Update SP */
+ update_vsp = 1;
+
+ /* Load the registers */
+ for (reg = 4; mask && reg < 16; mask >>= 1, reg++) {
+ if (mask & 1) {
+ state->registers[reg] = *vsp++;
+ state->update_mask |= 1 << reg;
+
+ /* If we have updated SP kep its value */
+ if (reg == SP)
+ update_vsp = 0;
+ }
+ }
+
+ } else if ((insn & INSN_STD_MASK) == INSN_VSP_REG &&
+ ((insn & INSN_STD_DATA_MASK) != 13) &&
+ ((insn & INSN_STD_DATA_MASK) != 15)) {
+ /* sp = register */
+ state->registers[SP] =
+ state->registers[insn & INSN_STD_DATA_MASK];
+
+ } else if ((insn & INSN_STD_MASK) == INSN_POP_COUNT) {
+ unsigned int count, reg;
+
+ /* Read how many registers to load */
+ count = insn & INSN_POP_COUNT_MASK;
+
+ /* Update sp */
+ update_vsp = 1;
+
+ /* Pop the registers */
+ for (reg = 4; reg <= 4 + count; reg++) {
+ state->registers[reg] = *vsp++;
+ state->update_mask |= 1 << reg;
+ }
+
+ /* Check if we are in the pop r14 version */
+ if ((insn & INSN_POP_TYPE_MASK) != 0) {
+ state->registers[14] = *vsp++;
+ }
+
+ } else if (insn == INSN_FINISH) {
+ /* Stop processing */
+ state->entries = 0;
+
+ } else if (insn == INSN_POP_REGS) {
+ unsigned int mask, reg;
+
+ mask = unwind_exec_read_byte(state);
+ if (mask == 0 || (mask & 0xf0) != 0)
+ return 1;
+
+ /* Update SP */
+ update_vsp = 1;
+
+ /* Load the registers */
+ for (reg = 0; mask && reg < 4; mask >>= 1, reg++) {
+ if (mask & 1) {
+ state->registers[reg] = *vsp++;
+ state->update_mask |= 1 << reg;
+ }
+ }
+
+ } else if ((insn & INSN_VSP_LARGE_INC_MASK) == INSN_VSP_LARGE_INC) {
+ unsigned int uleb128;
+
+ /* Read the increment value */
+ uleb128 = unwind_exec_read_byte(state);
+
+ state->registers[SP] += 0x204 + (uleb128 << 2);
+
+ } else {
+ /* We hit a new instruction that needs to be implemented */
+#if 0
+ db_printf("Unhandled instruction %.2x\n", insn);
+#endif
+ return 1;
+ }
+
+ if (update_vsp) {
+ state->registers[SP] = (uint32_t)vsp;
+ }
+
+#if 0
+ db_printf("fp = %08x, sp = %08x, lr = %08x, pc = %08x\n",
+ state->registers[FP], state->registers[SP], state->registers[LR],
+ state->registers[PC]);
+#endif
+
+ return 0;
+}
+
+/* Performs the unwind of a function */
+static int
+unwind_tab(struct unwind_state *state)
+{
+ uint32_t entry;
+
+ /* Set PC to a known value */
+ state->registers[PC] = 0;
+
+ /* Read the personality */
+ entry = *state->insn & ENTRY_MASK;
+
+ if (entry == ENTRY_ARM_SU16) {
+ state->byte = 2;
+ state->entries = 1;
+ } else if (entry == ENTRY_ARM_LU16) {
+ state->byte = 1;
+ state->entries = ((*state->insn >> 16) & 0xFF) + 1;
+ } else {
+#if 0
+ db_printf("Unknown entry: %x\n", entry);
+#endif
+ return 1;
+ }
+
+ while (state->entries > 0) {
+ if (unwind_exec_insn(state) != 0)
+ return 1;
+ }
+
+ /*
+ * The program counter was not updated, load it from the link register.
+ */
+ if (state->registers[PC] == 0) {
+ state->registers[PC] = state->registers[LR];
+
+ /*
+ * If the program counter changed, flag it in the update mask.
+ */
+ if (state->start_pc != state->registers[PC])
+ state->update_mask |= 1 << PC;
+ }
+
+ return 0;
+}
+
+/*
+ * Unwind a single stack frame.
+ * Return 0 on success or 1 if the stack cannot be unwound any further.
+ *
+ * XXX The can_lock argument is no longer germane; a sweep of callers should be
+ * made to remove it after this new code has proven itself for a while.
+ */
+int
+unwind_stack_one(struct unwind_state *state, int can_lock __unused)
+{
+ struct unwind_idx *index;
+
+ /* Reset the mask of updated registers */
+ state->update_mask = 0;
+
+ /* The pc value is correct and will be overwritten, save it */
+ state->start_pc = state->registers[PC];
+
+ /* Find the item to run */
+ index = find_index(state->start_pc);
+ if (index == NULL || index->insn == EXIDX_CANTUNWIND)
+ return 1;
+
+ if (index->insn & (1U << 31)) {
+ /* The data is within the instruction */
+ state->insn = &index->insn;
+ } else {
+ /* A prel31 offset to the unwind table */
+ state->insn = (uint32_t *)
+ ((uintptr_t)&index->insn +
+ expand_prel31(index->insn));
+ }
+
+ /* Run the unwind function, return its finished/not-finished status. */
+ return (unwind_tab(state));
+}
diff --git a/sys/arm/arm/vfp.c b/sys/arm/arm/vfp.c
new file mode 100644
index 000000000000..8a929dd75f27
--- /dev/null
+++ b/sys/arm/arm/vfp.c
@@ -0,0 +1,324 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2014 Ian Lepore <ian@freebsd.org>
+ * Copyright (c) 2012 Mark Tinguely
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#ifdef VFP
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/imgact_elf.h>
+#include <sys/kernel.h>
+
+#include <machine/armreg.h>
+#include <machine/elf.h>
+#include <machine/frame.h>
+#include <machine/md_var.h>
+#include <machine/pcb.h>
+#include <machine/undefined.h>
+#include <machine/vfp.h>
+
+/* function prototypes */
+static int vfp_bounce(u_int, u_int, struct trapframe *, int);
+static void vfp_restore(struct vfp_state *);
+
+extern int vfp_exists;
+static struct undefined_handler vfp10_uh, vfp11_uh;
+/* If true the VFP unit has 32 double registers, otherwise it has 16 */
+static int is_d32;
+
+/*
+ * About .fpu directives in this file...
+ *
+ * We should need simply .fpu vfpv3, but clang 3.5 has a quirk where setting
+ * vfpv3 doesn't imply that vfp2 features are also available -- both have to be
+ * explicitly set to get all the features of both. This is probably a bug in
+ * clang, so it may get fixed and require changes here some day. Other changes
+ * are probably coming in clang too, because there is email and open PRs
+ * indicating they want to completely disable the ability to use .fpu and
+ * similar directives in inline asm. That would be catastrophic for us,
+ * hopefully they come to their senses. There was also some discusion of a new
+ * syntax such as .push fpu=vfpv3; ...; .pop fpu; and that would be ideal for
+ * us, better than what we have now really.
+ *
+ * For gcc, each .fpu directive completely overrides the prior directive, unlike
+ * with clang, but luckily on gcc saying v3 implies all the v2 features as well.
+ */
+
+#define fmxr(reg, val) \
+ __asm __volatile(" .fpu vfpv2\n .fpu vfpv3\n" \
+ " vmsr " __STRING(reg) ", %0" :: "r"(val));
+
+#define fmrx(reg) \
+({ u_int val = 0;\
+ __asm __volatile(" .fpu vfpv2\n .fpu vfpv3\n" \
+ " vmrs %0, " __STRING(reg) : "=r"(val)); \
+ val; \
+})
+
+static u_int
+get_coprocessorACR(void)
+{
+ u_int val;
+ __asm __volatile("mrc p15, 0, %0, c1, c0, 2" : "=r" (val) : : "cc");
+ return val;
+}
+
+static void
+set_coprocessorACR(u_int val)
+{
+ __asm __volatile("mcr p15, 0, %0, c1, c0, 2\n\t"
+ : : "r" (val) : "cc");
+ isb();
+}
+
+ /* called for each cpu */
+void
+vfp_init(void)
+{
+ u_int fpsid, fpexc, tmp;
+ u_int coproc, vfp_arch;
+
+ coproc = get_coprocessorACR();
+ coproc |= COPROC10 | COPROC11;
+ set_coprocessorACR(coproc);
+
+ fpsid = fmrx(fpsid); /* read the vfp system id */
+ fpexc = fmrx(fpexc); /* read the vfp exception reg */
+
+ if (!(fpsid & VFPSID_HARDSOFT_IMP)) {
+ vfp_exists = 1;
+ is_d32 = 0;
+ PCPU_SET(vfpsid, fpsid); /* save the fpsid */
+ elf_hwcap |= HWCAP_VFP;
+
+ vfp_arch =
+ (fpsid & VFPSID_SUBVERSION2_MASK) >> VFPSID_SUBVERSION_OFF;
+
+ if (vfp_arch >= VFP_ARCH3) {
+ tmp = fmrx(mvfr0);
+ PCPU_SET(vfpmvfr0, tmp);
+ elf_hwcap |= HWCAP_VFPv3;
+
+ if ((tmp & VMVFR0_RB_MASK) == 2) {
+ elf_hwcap |= HWCAP_VFPD32;
+ is_d32 = 1;
+ } else
+ elf_hwcap |= HWCAP_VFPv3D16;
+
+ tmp = fmrx(mvfr1);
+ PCPU_SET(vfpmvfr1, tmp);
+
+ if (PCPU_GET(cpuid) == 0) {
+ if ((tmp & VMVFR1_FZ_MASK) == 0x1) {
+ /* Denormals arithmetic support */
+ initial_fpscr &= ~VFPSCR_FZ;
+ thread0.td_pcb->pcb_vfpstate.fpscr =
+ initial_fpscr;
+ }
+ }
+
+ if ((tmp & VMVFR1_LS_MASK) >> VMVFR1_LS_OFF == 1 &&
+ (tmp & VMVFR1_I_MASK) >> VMVFR1_I_OFF == 1 &&
+ (tmp & VMVFR1_SP_MASK) >> VMVFR1_SP_OFF == 1)
+ elf_hwcap |= HWCAP_NEON;
+ if ((tmp & VMVFR1_FMAC_MASK) >> VMVFR1_FMAC_OFF == 1)
+ elf_hwcap |= HWCAP_VFPv4;
+ }
+
+ /* initialize the coprocess 10 and 11 calls
+ * These are called to restore the registers and enable
+ * the VFP hardware.
+ */
+ if (vfp10_uh.uh_handler == NULL) {
+ vfp10_uh.uh_handler = vfp_bounce;
+ vfp11_uh.uh_handler = vfp_bounce;
+ install_coproc_handler_static(10, &vfp10_uh);
+ install_coproc_handler_static(11, &vfp11_uh);
+ }
+ }
+}
+
+SYSINIT(vfp, SI_SUB_CPU, SI_ORDER_ANY, vfp_init, NULL);
+
+/* start VFP unit, restore the vfp registers from the PCB and retry
+ * the instruction
+ */
+static int
+vfp_bounce(u_int addr, u_int insn, struct trapframe *frame, int code)
+{
+ u_int cpu, fpexc;
+ struct pcb *curpcb;
+ ksiginfo_t ksi;
+
+ if ((code & FAULT_USER) == 0)
+ panic("undefined floating point instruction in supervisor mode");
+
+ critical_enter();
+
+ /*
+ * If the VFP is already on and we got an undefined instruction, then
+ * something tried to executate a truly invalid instruction that maps to
+ * the VFP.
+ */
+ fpexc = fmrx(fpexc);
+ if (fpexc & VFPEXC_EN) {
+ /* Clear any exceptions */
+ fmxr(fpexc, fpexc & ~(VFPEXC_EX | VFPEXC_FP2V));
+
+ /* kill the process - we do not handle emulation */
+ critical_exit();
+
+ if (fpexc & VFPEXC_EX) {
+ /* We have an exception, signal a SIGFPE */
+ ksiginfo_init_trap(&ksi);
+ ksi.ksi_signo = SIGFPE;
+ if (fpexc & VFPEXC_UFC)
+ ksi.ksi_code = FPE_FLTUND;
+ else if (fpexc & VFPEXC_OFC)
+ ksi.ksi_code = FPE_FLTOVF;
+ else if (fpexc & VFPEXC_IOC)
+ ksi.ksi_code = FPE_FLTINV;
+ ksi.ksi_addr = (void *)addr;
+ trapsignal(curthread, &ksi);
+ return 0;
+ }
+
+ return 1;
+ }
+
+ /*
+ * If the last time this thread used the VFP it was on this core, and
+ * the last thread to use the VFP on this core was this thread, then the
+ * VFP state is valid, otherwise restore this thread's state to the VFP.
+ */
+ fmxr(fpexc, fpexc | VFPEXC_EN);
+ curpcb = curthread->td_pcb;
+ cpu = PCPU_GET(cpuid);
+ if (curpcb->pcb_vfpcpu != cpu || curthread != PCPU_GET(fpcurthread)) {
+ vfp_restore(&curpcb->pcb_vfpstate);
+ curpcb->pcb_vfpcpu = cpu;
+ PCPU_SET(fpcurthread, curthread);
+ }
+
+ critical_exit();
+ return (0);
+}
+
+/*
+ * Restore the given state to the VFP hardware.
+ */
+static void
+vfp_restore(struct vfp_state *vfpsave)
+{
+ uint32_t fpexc;
+
+ /* On vfpv3 we may need to restore FPINST and FPINST2 */
+ fpexc = vfpsave->fpexec;
+ if (fpexc & VFPEXC_EX) {
+ fmxr(fpinst, vfpsave->fpinst);
+ if (fpexc & VFPEXC_FP2V)
+ fmxr(fpinst2, vfpsave->fpinst2);
+ }
+ fmxr(fpscr, vfpsave->fpscr);
+
+ __asm __volatile(
+ " .fpu vfpv2\n"
+ " .fpu vfpv3\n"
+ " vldmia %0!, {d0-d15}\n" /* d0-d15 */
+ " cmp %1, #0\n" /* -D16 or -D32? */
+ " vldmiane %0!, {d16-d31}\n" /* d16-d31 */
+ " addeq %0, %0, #128\n" /* skip missing regs */
+ : "+&r" (vfpsave) : "r" (is_d32) : "cc"
+ );
+
+ fmxr(fpexc, fpexc);
+}
+
+/*
+ * If the VFP is on, save its current state and turn it off if requested to do
+ * so. If the VFP is not on, does not change the values at *vfpsave. Caller is
+ * responsible for preventing a context switch while this is running.
+ */
+void
+vfp_store(struct vfp_state *vfpsave, boolean_t disable_vfp)
+{
+ uint32_t fpexc;
+
+ fpexc = fmrx(fpexc); /* Is the vfp enabled? */
+ if (fpexc & VFPEXC_EN) {
+ vfpsave->fpexec = fpexc;
+ vfpsave->fpscr = fmrx(fpscr);
+
+ /* On vfpv3 we may need to save FPINST and FPINST2 */
+ if (fpexc & VFPEXC_EX) {
+ vfpsave->fpinst = fmrx(fpinst);
+ if (fpexc & VFPEXC_FP2V)
+ vfpsave->fpinst2 = fmrx(fpinst2);
+ fpexc &= ~VFPEXC_EX;
+ }
+
+ __asm __volatile(
+ " .fpu vfpv2\n"
+ " .fpu vfpv3\n"
+ " vstmia %0!, {d0-d15}\n" /* d0-d15 */
+ " cmp %1, #0\n" /* -D16 or -D32? */
+ " vstmiane %0!, {d16-d31}\n" /* d16-d31 */
+ " addeq %0, %0, #128\n" /* skip missing regs */
+ : "+&r" (vfpsave) : "r" (is_d32) : "cc"
+ );
+
+ if (disable_vfp)
+ fmxr(fpexc , fpexc & ~VFPEXC_EN);
+ }
+}
+
+/*
+ * The current thread is dying. If the state currently in the hardware belongs
+ * to the current thread, set fpcurthread to NULL to indicate that the VFP
+ * hardware state does not belong to any thread. If the VFP is on, turn it off.
+ * Called only from cpu_throw(), so we don't have to worry about a context
+ * switch here.
+ */
+void
+vfp_discard(struct thread *td)
+{
+ u_int tmp;
+
+ if (PCPU_GET(fpcurthread) == td)
+ PCPU_SET(fpcurthread, NULL);
+
+ tmp = fmrx(fpexc);
+ if (tmp & VFPEXC_EN)
+ fmxr(fpexc, tmp & ~VFPEXC_EN);
+}
+
+#endif
diff --git a/sys/arm/arm/vm_machdep.c b/sys/arm/arm/vm_machdep.c
new file mode 100644
index 000000000000..03bbf75ebf71
--- /dev/null
+++ b/sys/arm/arm/vm_machdep.c
@@ -0,0 +1,320 @@
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 1982, 1986 The Regents of the University of California.
+ * Copyright (c) 1989, 1990 William Jolitz
+ * Copyright (c) 1994 John Dyson
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Systems Programming Group of the University of Utah Computer
+ * Science Department, and William Jolitz.
+ *
+ * Redistribution and use in source and binary :forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)vm_machdep.c 7.3 (Berkeley) 5/13/91
+ * Utah $Hdr: vm_machdep.c 1.16.1.1 89/06/23$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/proc.h>
+#include <sys/socketvar.h>
+#include <sys/syscall.h>
+#include <sys/sysctl.h>
+#include <sys/sysent.h>
+#include <sys/unistd.h>
+
+#include <machine/cpu.h>
+#include <machine/frame.h>
+#include <machine/pcb.h>
+#include <machine/sysarch.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_page.h>
+#include <vm/vm_map.h>
+#include <vm/vm_param.h>
+#include <vm/vm_pageout.h>
+#include <vm/uma.h>
+#include <vm/uma_int.h>
+
+#include <machine/md_var.h>
+#include <machine/vfp.h>
+
+/*
+ * struct switchframe and trapframe must both be a multiple of 8
+ * for correct stack alignment.
+ */
+_Static_assert((sizeof(struct switchframe) % 8) == 0, "Bad alignment");
+_Static_assert((sizeof(struct trapframe) % 8) == 0, "Bad alignment");
+
+uint32_t initial_fpscr = VFPSCR_DN | VFPSCR_FZ;
+
+/*
+ * Finish a fork operation, with process p2 nearly set up.
+ * Copy and update the pcb, set up the stack so that the child
+ * ready to run and return to user mode.
+ */
+void
+cpu_fork(struct thread *td1, struct proc *p2, struct thread *td2, int flags)
+{
+ struct pcb *pcb2;
+ struct trapframe *tf;
+ struct mdproc *mdp2;
+
+ if ((flags & RFPROC) == 0)
+ return;
+
+ /* Point the pcb to the top of the stack */
+ pcb2 = (struct pcb *)
+ (td2->td_kstack + td2->td_kstack_pages * PAGE_SIZE) - 1;
+#ifdef VFP
+ /* Store actual state of VFP */
+ if (curthread == td1) {
+ critical_enter();
+ vfp_store(&td1->td_pcb->pcb_vfpstate, false);
+ critical_exit();
+ }
+#endif
+ td2->td_pcb = pcb2;
+
+ /* Clone td1's pcb */
+ bcopy(td1->td_pcb, pcb2, sizeof(*pcb2));
+
+ /* Point to mdproc and then copy over td1's contents */
+ mdp2 = &p2->p_md;
+ bcopy(&td1->td_proc->p_md, mdp2, sizeof(*mdp2));
+
+ /* Point the frame to the stack in front of pcb and copy td1's frame */
+ td2->td_frame = (struct trapframe *)pcb2 - 1;
+ *td2->td_frame = *td1->td_frame;
+
+ /*
+ * Create a new fresh stack for the new process.
+ * Copy the trap frame for the return to user mode as if from a
+ * syscall. This copies most of the user mode register values.
+ */
+ pmap_set_pcb_pagedir(vmspace_pmap(p2->p_vmspace), pcb2);
+ pcb2->pcb_regs.sf_r4 = (register_t)fork_return;
+ pcb2->pcb_regs.sf_r5 = (register_t)td2;
+ pcb2->pcb_regs.sf_lr = (register_t)fork_trampoline;
+ pcb2->pcb_regs.sf_sp = STACKALIGN(td2->td_frame);
+ pcb2->pcb_regs.sf_tpidrurw = (register_t)get_tls();
+
+ pcb2->pcb_vfpcpu = -1;
+ pcb2->pcb_vfpstate.fpscr = initial_fpscr;
+
+ tf = td2->td_frame;
+ tf->tf_spsr &= ~PSR_C;
+ tf->tf_r0 = 0;
+ tf->tf_r1 = 0;
+
+ /* Setup to release spin count in fork_exit(). */
+ td2->td_md.md_spinlock_count = 1;
+ td2->td_md.md_saved_cspr = PSR_SVC32_MODE;
+}
+
+void
+cpu_thread_swapin(struct thread *td)
+{
+}
+
+void
+cpu_thread_swapout(struct thread *td)
+{
+}
+
+void
+cpu_set_syscall_retval(struct thread *td, int error)
+{
+ struct trapframe *frame;
+
+ frame = td->td_frame;
+ switch (error) {
+ case 0:
+ frame->tf_r0 = td->td_retval[0];
+ frame->tf_r1 = td->td_retval[1];
+ frame->tf_spsr &= ~PSR_C; /* carry bit */
+ break;
+ case ERESTART:
+ /*
+ * Reconstruct the pc to point at the swi.
+ */
+#if __ARM_ARCH >= 7
+ if ((frame->tf_spsr & PSR_T) != 0)
+ frame->tf_pc -= THUMB_INSN_SIZE;
+ else
+#endif
+ frame->tf_pc -= INSN_SIZE;
+ break;
+ case EJUSTRETURN:
+ /* nothing to do */
+ break;
+ default:
+ frame->tf_r0 = error;
+ frame->tf_spsr |= PSR_C; /* carry bit */
+ break;
+ }
+}
+
+/*
+ * Initialize machine state, mostly pcb and trap frame for a new
+ * thread, about to return to userspace. Put enough state in the new
+ * thread's PCB to get it to go back to the fork_return(), which
+ * finalizes the thread state and handles peculiarities of the first
+ * return to userspace for the new thread.
+ */
+void
+cpu_copy_thread(struct thread *td, struct thread *td0)
+{
+
+ bcopy(td0->td_frame, td->td_frame, sizeof(struct trapframe));
+ bcopy(td0->td_pcb, td->td_pcb, sizeof(struct pcb));
+
+ td->td_pcb->pcb_regs.sf_r4 = (register_t)fork_return;
+ td->td_pcb->pcb_regs.sf_r5 = (register_t)td;
+ td->td_pcb->pcb_regs.sf_lr = (register_t)fork_trampoline;
+ td->td_pcb->pcb_regs.sf_sp = STACKALIGN(td->td_frame);
+
+ td->td_frame->tf_spsr &= ~PSR_C;
+ td->td_frame->tf_r0 = 0;
+
+ /* Setup to release spin count in fork_exit(). */
+ td->td_md.md_spinlock_count = 1;
+ td->td_md.md_saved_cspr = PSR_SVC32_MODE;
+}
+
+/*
+ * Set that machine state for performing an upcall that starts
+ * the entry function with the given argument.
+ */
+void
+cpu_set_upcall(struct thread *td, void (*entry)(void *), void *arg,
+ stack_t *stack)
+{
+ struct trapframe *tf = td->td_frame;
+
+ tf->tf_usr_sp = STACKALIGN((int)stack->ss_sp + stack->ss_size);
+ tf->tf_pc = (int)entry;
+ tf->tf_r0 = (int)arg;
+ tf->tf_spsr = PSR_USR32_MODE;
+}
+
+int
+cpu_set_user_tls(struct thread *td, void *tls_base)
+{
+
+ td->td_pcb->pcb_regs.sf_tpidrurw = (register_t)tls_base;
+ if (td == curthread)
+ set_tls(tls_base);
+ return (0);
+}
+
+void
+cpu_thread_exit(struct thread *td)
+{
+}
+
+void
+cpu_thread_alloc(struct thread *td)
+{
+ td->td_pcb = (struct pcb *)(td->td_kstack + td->td_kstack_pages *
+ PAGE_SIZE) - 1;
+ /*
+ * Ensure td_frame is aligned to an 8 byte boundary as it will be
+ * placed into the stack pointer which must be 8 byte aligned in
+ * the ARM EABI.
+ */
+ td->td_frame = (struct trapframe *)((caddr_t)td->td_pcb) - 1;
+}
+
+void
+cpu_thread_free(struct thread *td)
+{
+}
+
+void
+cpu_thread_clean(struct thread *td)
+{
+}
+
+/*
+ * Intercept the return address from a freshly forked process that has NOT
+ * been scheduled yet.
+ *
+ * This is needed to make kernel threads stay in kernel mode.
+ */
+void
+cpu_fork_kthread_handler(struct thread *td, void (*func)(void *), void *arg)
+{
+ td->td_pcb->pcb_regs.sf_r4 = (register_t)func; /* function */
+ td->td_pcb->pcb_regs.sf_r5 = (register_t)arg; /* first arg */
+}
+
+/*
+ * Software interrupt handler for queued VM system processing.
+ */
+void
+swi_vm(void *dummy)
+{
+
+ if (busdma_swi_pending)
+ busdma_swi();
+}
+
+void
+cpu_exit(struct thread *td)
+{
+}
+
+bool
+cpu_exec_vmspace_reuse(struct proc *p __unused, vm_map_t map __unused)
+{
+
+ return (true);
+}
+
+int
+cpu_procctl(struct thread *td __unused, int idtype __unused, id_t id __unused,
+ int com __unused, void *data __unused)
+{
+
+ return (EINVAL);
+}
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_audio.c b/sys/arm/broadcom/bcm2835/bcm2835_audio.c
new file mode 100644
index 000000000000..ae2c9a14fc62
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2835_audio.c
@@ -0,0 +1,968 @@
+/*-
+ * Copyright (c) 2015 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_KERNEL_OPTION_HEADERS
+#include "opt_snd.h"
+#endif
+
+#include <dev/sound/pcm/sound.h>
+#include <dev/sound/chip.h>
+
+#include "mixer_if.h"
+
+#include "interface/compat/vchi_bsd.h"
+#include "interface/vchi/vchi.h"
+#include "interface/vchiq_arm/vchiq.h"
+
+#include "vc_vchi_audioserv_defs.h"
+
+SND_DECLARE_FILE("$FreeBSD$");
+
+/* Audio destination */
+#define DEST_AUTO 0
+#define DEST_HEADPHONES 1
+#define DEST_HDMI 2
+
+/* Playback state */
+#define PLAYBACK_IDLE 0
+#define PLAYBACK_PLAYING 1
+#define PLAYBACK_STOPPING 2
+
+/* Worker thread state */
+#define WORKER_RUNNING 0
+#define WORKER_STOPPING 1
+#define WORKER_STOPPED 2
+
+/*
+ * Worker thread flags, set to 1 in flags_pending
+ * when driver requests one or another operation
+ * from worker. Cleared to 0 once worker performs
+ * the operations.
+ */
+#define AUDIO_PARAMS (1 << 0)
+#define AUDIO_PLAY (1 << 1)
+#define AUDIO_STOP (1 << 2)
+
+#define VCHIQ_AUDIO_PACKET_SIZE 4000
+#define VCHIQ_AUDIO_BUFFER_SIZE 10*VCHIQ_AUDIO_PACKET_SIZE
+
+#define VCHIQ_AUDIO_MAX_VOLUME
+/* volume in terms of 0.01dB */
+#define VCHIQ_AUDIO_VOLUME_MIN -10239
+#define VCHIQ_AUDIO_VOLUME(db100) (uint32_t)(-((db100) << 8)/100)
+
+/* dB levels with 5% volume step */
+static int db_levels[] = {
+ VCHIQ_AUDIO_VOLUME_MIN, -4605, -3794, -3218, -2772,
+ -2407, -2099, -1832, -1597, -1386,
+ -1195, -1021, -861, -713, -575,
+ -446, -325, -210, -102, 0,
+};
+
+static uint32_t bcm2835_audio_playfmt[] = {
+ SND_FORMAT(AFMT_U8, 1, 0),
+ SND_FORMAT(AFMT_U8, 2, 0),
+ SND_FORMAT(AFMT_S8, 1, 0),
+ SND_FORMAT(AFMT_S8, 2, 0),
+ SND_FORMAT(AFMT_S16_LE, 1, 0),
+ SND_FORMAT(AFMT_S16_LE, 2, 0),
+ SND_FORMAT(AFMT_U16_LE, 1, 0),
+ SND_FORMAT(AFMT_U16_LE, 2, 0),
+ 0
+};
+
+static struct pcmchan_caps bcm2835_audio_playcaps = {8000, 48000, bcm2835_audio_playfmt, 0};
+
+struct bcm2835_audio_info;
+
+struct bcm2835_audio_chinfo {
+ struct bcm2835_audio_info *parent;
+ struct pcm_channel *channel;
+ struct snd_dbuf *buffer;
+ uint32_t fmt, spd, blksz;
+
+ /* Pointer to first unsubmitted sample */
+ uint32_t unsubmittedptr;
+ /*
+ * Number of bytes in "submitted but not played"
+ * pseudo-buffer
+ */
+ int available_space;
+ int playback_state;
+ uint64_t callbacks;
+ uint64_t submitted_samples;
+ uint64_t retrieved_samples;
+ uint64_t underruns;
+ int starved;
+};
+
+struct bcm2835_audio_info {
+ device_t dev;
+ unsigned int bufsz;
+ struct bcm2835_audio_chinfo pch;
+ uint32_t dest, volume;
+ struct intr_config_hook intr_hook;
+
+ /* VCHI data */
+ VCHI_INSTANCE_T vchi_instance;
+ VCHI_CONNECTION_T *vchi_connection;
+ VCHI_SERVICE_HANDLE_T vchi_handle;
+
+ struct mtx lock;
+ struct cv worker_cv;
+
+ uint32_t flags_pending;
+
+ /* Worker thread state */
+ int worker_state;
+};
+
+#define BCM2835_AUDIO_LOCK(sc) mtx_lock(&(sc)->lock)
+#define BCM2835_AUDIO_LOCKED(sc) mtx_assert(&(sc)->lock, MA_OWNED)
+#define BCM2835_AUDIO_UNLOCK(sc) mtx_unlock(&(sc)->lock)
+
+static const char *
+dest_description(uint32_t dest)
+{
+ switch (dest) {
+ case DEST_AUTO:
+ return "AUTO";
+ break;
+
+ case DEST_HEADPHONES:
+ return "HEADPHONES";
+ break;
+
+ case DEST_HDMI:
+ return "HDMI";
+ break;
+ default:
+ return "UNKNOWN";
+ break;
+ }
+}
+
+static void
+bcm2835_worker_update_params(struct bcm2835_audio_info *sc)
+{
+
+ BCM2835_AUDIO_LOCKED(sc);
+
+ sc->flags_pending |= AUDIO_PARAMS;
+ cv_signal(&sc->worker_cv);
+}
+
+static void
+bcm2835_worker_play_start(struct bcm2835_audio_info *sc)
+{
+ BCM2835_AUDIO_LOCK(sc);
+ sc->flags_pending &= ~(AUDIO_STOP);
+ sc->flags_pending |= AUDIO_PLAY;
+ cv_signal(&sc->worker_cv);
+ BCM2835_AUDIO_UNLOCK(sc);
+}
+
+static void
+bcm2835_worker_play_stop(struct bcm2835_audio_info *sc)
+{
+ BCM2835_AUDIO_LOCK(sc);
+ sc->flags_pending &= ~(AUDIO_PLAY);
+ sc->flags_pending |= AUDIO_STOP;
+ cv_signal(&sc->worker_cv);
+ BCM2835_AUDIO_UNLOCK(sc);
+}
+
+static void
+bcm2835_audio_callback(void *param, const VCHI_CALLBACK_REASON_T reason, void *msg_handle)
+{
+ struct bcm2835_audio_info *sc = (struct bcm2835_audio_info *)param;
+ int32_t status;
+ uint32_t msg_len;
+ VC_AUDIO_MSG_T m;
+
+ if (reason != VCHI_CALLBACK_MSG_AVAILABLE)
+ return;
+
+ status = vchi_msg_dequeue(sc->vchi_handle,
+ &m, sizeof m, &msg_len, VCHI_FLAGS_NONE);
+ if (m.type == VC_AUDIO_MSG_TYPE_RESULT) {
+ if (m.u.result.success) {
+ device_printf(sc->dev,
+ "msg type %08x failed\n",
+ m.type);
+ }
+ } else if (m.type == VC_AUDIO_MSG_TYPE_COMPLETE) {
+ struct bcm2835_audio_chinfo *ch = m.u.complete.cookie;
+
+ int count = m.u.complete.count & 0xffff;
+ int perr = (m.u.complete.count & (1U << 30)) != 0;
+ ch->callbacks++;
+ if (perr)
+ ch->underruns++;
+
+ BCM2835_AUDIO_LOCK(sc);
+ if (ch->playback_state != PLAYBACK_IDLE) {
+ /* Prevent LOR */
+ BCM2835_AUDIO_UNLOCK(sc);
+ chn_intr(sc->pch.channel);
+ BCM2835_AUDIO_LOCK(sc);
+ }
+ /* We should check again, state might have changed */
+ if (ch->playback_state != PLAYBACK_IDLE) {
+ if (!perr) {
+ if ((ch->available_space + count)> VCHIQ_AUDIO_BUFFER_SIZE) {
+ device_printf(sc->dev, "inconsistent data in callback:\n");
+ device_printf(sc->dev, "available_space == %d, count = %d, perr=%d\n",
+ ch->available_space, count, perr);
+ device_printf(sc->dev,
+ "retrieved_samples = %lld, submitted_samples = %lld\n",
+ ch->retrieved_samples, ch->submitted_samples);
+ }
+ ch->available_space += count;
+ ch->retrieved_samples += count;
+ }
+ if (perr || (ch->available_space >= VCHIQ_AUDIO_PACKET_SIZE))
+ cv_signal(&sc->worker_cv);
+ }
+ BCM2835_AUDIO_UNLOCK(sc);
+ } else
+ printf("%s: unknown m.type: %d\n", __func__, m.type);
+}
+
+/* VCHIQ stuff */
+static void
+bcm2835_audio_init(struct bcm2835_audio_info *sc)
+{
+ int status;
+
+ /* Initialize and create a VCHI connection */
+ status = vchi_initialise(&sc->vchi_instance);
+ if (status != 0) {
+ printf("vchi_initialise failed: %d\n", status);
+ return;
+ }
+
+ status = vchi_connect(NULL, 0, sc->vchi_instance);
+ if (status != 0) {
+ printf("vchi_connect failed: %d\n", status);
+ return;
+ }
+
+ SERVICE_CREATION_T params = {
+ VCHI_VERSION_EX(VC_AUDIOSERV_VER, VC_AUDIOSERV_MIN_VER),
+ VC_AUDIO_SERVER_NAME, /* 4cc service code */
+ sc->vchi_connection, /* passed in fn pointers */
+ 0, /* rx fifo size */
+ 0, /* tx fifo size */
+ bcm2835_audio_callback, /* service callback */
+ sc, /* service callback parameter */
+ 1,
+ 1,
+ 0 /* want crc check on bulk transfers */
+ };
+
+ status = vchi_service_open(sc->vchi_instance, &params,
+ &sc->vchi_handle);
+
+ if (status != 0)
+ sc->vchi_handle = VCHIQ_SERVICE_HANDLE_INVALID;
+}
+
+static void
+bcm2835_audio_release(struct bcm2835_audio_info *sc)
+{
+ int success;
+
+ if (sc->vchi_handle != VCHIQ_SERVICE_HANDLE_INVALID) {
+ success = vchi_service_close(sc->vchi_handle);
+ if (success != 0)
+ printf("vchi_service_close failed: %d\n", success);
+ vchi_service_release(sc->vchi_handle);
+ sc->vchi_handle = VCHIQ_SERVICE_HANDLE_INVALID;
+ }
+
+ vchi_disconnect(sc->vchi_instance);
+}
+
+static void
+bcm2835_audio_reset_channel(struct bcm2835_audio_chinfo *ch)
+{
+
+ ch->available_space = VCHIQ_AUDIO_BUFFER_SIZE;
+ ch->unsubmittedptr = 0;
+ sndbuf_reset(ch->buffer);
+}
+
+static void
+bcm2835_audio_start(struct bcm2835_audio_chinfo *ch)
+{
+ VC_AUDIO_MSG_T m;
+ int ret;
+ struct bcm2835_audio_info *sc = ch->parent;
+
+ if (sc->vchi_handle != VCHIQ_SERVICE_HANDLE_INVALID) {
+ m.type = VC_AUDIO_MSG_TYPE_START;
+ ret = vchi_msg_queue(sc->vchi_handle,
+ &m, sizeof m, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
+
+ if (ret != 0)
+ printf("%s: vchi_msg_queue failed (err %d)\n", __func__, ret);
+ }
+}
+
+static void
+bcm2835_audio_stop(struct bcm2835_audio_chinfo *ch)
+{
+ VC_AUDIO_MSG_T m;
+ int ret;
+ struct bcm2835_audio_info *sc = ch->parent;
+
+ if (sc->vchi_handle != VCHIQ_SERVICE_HANDLE_INVALID) {
+ m.type = VC_AUDIO_MSG_TYPE_STOP;
+ m.u.stop.draining = 0;
+
+ ret = vchi_msg_queue(sc->vchi_handle,
+ &m, sizeof m, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
+
+ if (ret != 0)
+ printf("%s: vchi_msg_queue failed (err %d)\n", __func__, ret);
+ }
+}
+
+static void
+bcm2835_audio_open(struct bcm2835_audio_info *sc)
+{
+ VC_AUDIO_MSG_T m;
+ int ret;
+
+ if (sc->vchi_handle != VCHIQ_SERVICE_HANDLE_INVALID) {
+ m.type = VC_AUDIO_MSG_TYPE_OPEN;
+ ret = vchi_msg_queue(sc->vchi_handle,
+ &m, sizeof m, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
+
+ if (ret != 0)
+ printf("%s: vchi_msg_queue failed (err %d)\n", __func__, ret);
+ }
+}
+
+static void
+bcm2835_audio_update_controls(struct bcm2835_audio_info *sc, uint32_t volume, uint32_t dest)
+{
+ VC_AUDIO_MSG_T m;
+ int ret, db;
+
+ if (sc->vchi_handle != VCHIQ_SERVICE_HANDLE_INVALID) {
+ m.type = VC_AUDIO_MSG_TYPE_CONTROL;
+ m.u.control.dest = dest;
+ if (volume > 99)
+ volume = 99;
+ db = db_levels[volume/5];
+ m.u.control.volume = VCHIQ_AUDIO_VOLUME(db);
+
+ ret = vchi_msg_queue(sc->vchi_handle,
+ &m, sizeof m, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
+
+ if (ret != 0)
+ printf("%s: vchi_msg_queue failed (err %d)\n", __func__, ret);
+ }
+}
+
+static void
+bcm2835_audio_update_params(struct bcm2835_audio_info *sc, uint32_t fmt, uint32_t speed)
+{
+ VC_AUDIO_MSG_T m;
+ int ret;
+
+ if (sc->vchi_handle != VCHIQ_SERVICE_HANDLE_INVALID) {
+ m.type = VC_AUDIO_MSG_TYPE_CONFIG;
+ m.u.config.channels = AFMT_CHANNEL(fmt);
+ m.u.config.samplerate = speed;
+ m.u.config.bps = AFMT_BIT(fmt);
+
+ ret = vchi_msg_queue(sc->vchi_handle,
+ &m, sizeof m, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
+
+ if (ret != 0)
+ printf("%s: vchi_msg_queue failed (err %d)\n", __func__, ret);
+ }
+}
+
+static bool
+bcm2835_audio_buffer_should_sleep(struct bcm2835_audio_chinfo *ch)
+{
+
+ if (ch->playback_state != PLAYBACK_PLAYING)
+ return (true);
+
+ /* Not enough data */
+ if (sndbuf_getready(ch->buffer) < VCHIQ_AUDIO_PACKET_SIZE) {
+ printf("starve\n");
+ ch->starved++;
+ return (true);
+ }
+
+ /* Not enough free space */
+ if (ch->available_space < VCHIQ_AUDIO_PACKET_SIZE) {
+ return (true);
+ }
+
+ return (false);
+}
+
+static void
+bcm2835_audio_write_samples(struct bcm2835_audio_chinfo *ch, void *buf, uint32_t count)
+{
+ struct bcm2835_audio_info *sc = ch->parent;
+ VC_AUDIO_MSG_T m;
+ int ret;
+
+ if (sc->vchi_handle == VCHIQ_SERVICE_HANDLE_INVALID) {
+ return;
+ }
+
+ m.type = VC_AUDIO_MSG_TYPE_WRITE;
+ m.u.write.count = count;
+ m.u.write.max_packet = VCHIQ_AUDIO_PACKET_SIZE;
+ m.u.write.callback = NULL;
+ m.u.write.cookie = ch;
+ m.u.write.silence = 0;
+
+ ret = vchi_msg_queue(sc->vchi_handle,
+ &m, sizeof m, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
+
+ if (ret != 0)
+ printf("%s: vchi_msg_queue failed (err %d)\n", __func__, ret);
+
+ while (count > 0) {
+ int bytes = MIN((int)m.u.write.max_packet, (int)count);
+ ret = vchi_msg_queue(sc->vchi_handle,
+ buf, bytes, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
+ if (ret != 0)
+ printf("%s: vchi_msg_queue failed: %d\n",
+ __func__, ret);
+ buf = (char *)buf + bytes;
+ count -= bytes;
+ }
+}
+
+static void
+bcm2835_audio_worker(void *data)
+{
+ struct bcm2835_audio_info *sc = (struct bcm2835_audio_info *)data;
+ struct bcm2835_audio_chinfo *ch = &sc->pch;
+ uint32_t speed, format;
+ uint32_t volume, dest;
+ uint32_t flags;
+ uint32_t count, size, readyptr;
+ uint8_t *buf;
+
+ ch->playback_state = PLAYBACK_IDLE;
+
+ while (1) {
+ if (sc->worker_state != WORKER_RUNNING)
+ break;
+
+ BCM2835_AUDIO_LOCK(sc);
+ /*
+ * wait until there are flags set or buffer is ready
+ * to consume more samples
+ */
+ while ((sc->flags_pending == 0) &&
+ bcm2835_audio_buffer_should_sleep(ch)) {
+ cv_wait_sig(&sc->worker_cv, &sc->lock);
+ }
+ flags = sc->flags_pending;
+ /* Clear pending flags */
+ sc->flags_pending = 0;
+ BCM2835_AUDIO_UNLOCK(sc);
+
+ /* Requested to change parameters */
+ if (flags & AUDIO_PARAMS) {
+ BCM2835_AUDIO_LOCK(sc);
+ speed = ch->spd;
+ format = ch->fmt;
+ volume = sc->volume;
+ dest = sc->dest;
+ BCM2835_AUDIO_UNLOCK(sc);
+ if (ch->playback_state == PLAYBACK_IDLE)
+ bcm2835_audio_update_params(sc, format, speed);
+ bcm2835_audio_update_controls(sc, volume, dest);
+ }
+
+ /* Requested to stop playback */
+ if ((flags & AUDIO_STOP) &&
+ (ch->playback_state == PLAYBACK_PLAYING)) {
+ bcm2835_audio_stop(ch);
+ BCM2835_AUDIO_LOCK(sc);
+ bcm2835_audio_reset_channel(&sc->pch);
+ ch->playback_state = PLAYBACK_IDLE;
+ BCM2835_AUDIO_UNLOCK(sc);
+ continue;
+ }
+
+ /* Requested to start playback */
+ if ((flags & AUDIO_PLAY) &&
+ (ch->playback_state == PLAYBACK_IDLE)) {
+ BCM2835_AUDIO_LOCK(sc);
+ ch->playback_state = PLAYBACK_PLAYING;
+ BCM2835_AUDIO_UNLOCK(sc);
+ bcm2835_audio_start(ch);
+ }
+
+ if (ch->playback_state == PLAYBACK_IDLE)
+ continue;
+
+ if (sndbuf_getready(ch->buffer) == 0)
+ continue;
+
+ count = sndbuf_getready(ch->buffer);
+ size = sndbuf_getsize(ch->buffer);
+ readyptr = sndbuf_getreadyptr(ch->buffer);
+
+ BCM2835_AUDIO_LOCK(sc);
+ if (readyptr + count > size)
+ count = size - readyptr;
+ count = min(count, ch->available_space);
+ count -= (count % VCHIQ_AUDIO_PACKET_SIZE);
+ BCM2835_AUDIO_UNLOCK(sc);
+
+ if (count < VCHIQ_AUDIO_PACKET_SIZE)
+ continue;
+
+ buf = (uint8_t*)sndbuf_getbuf(ch->buffer) + readyptr;
+
+ bcm2835_audio_write_samples(ch, buf, count);
+ BCM2835_AUDIO_LOCK(sc);
+ ch->unsubmittedptr = (ch->unsubmittedptr + count) % sndbuf_getsize(ch->buffer);
+ ch->available_space -= count;
+ ch->submitted_samples += count;
+ KASSERT(ch->available_space >= 0, ("ch->available_space == %d\n", ch->available_space));
+ BCM2835_AUDIO_UNLOCK(sc);
+ }
+
+ BCM2835_AUDIO_LOCK(sc);
+ sc->worker_state = WORKER_STOPPED;
+ cv_signal(&sc->worker_cv);
+ BCM2835_AUDIO_UNLOCK(sc);
+
+ kproc_exit(0);
+}
+
+static void
+bcm2835_audio_create_worker(struct bcm2835_audio_info *sc)
+{
+ struct proc *newp;
+
+ sc->worker_state = WORKER_RUNNING;
+ if (kproc_create(bcm2835_audio_worker, (void*)sc, &newp, 0, 0,
+ "bcm2835_audio_worker") != 0) {
+ printf("failed to create bcm2835_audio_worker\n");
+ }
+}
+
+/* -------------------------------------------------------------------- */
+/* channel interface for VCHI audio */
+static void *
+bcmchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
+{
+ struct bcm2835_audio_info *sc = devinfo;
+ struct bcm2835_audio_chinfo *ch = &sc->pch;
+ void *buffer;
+
+ if (dir == PCMDIR_REC)
+ return NULL;
+
+ ch->parent = sc;
+ ch->channel = c;
+ ch->buffer = b;
+
+ /* default values */
+ ch->spd = 44100;
+ ch->fmt = SND_FORMAT(AFMT_S16_LE, 2, 0);
+ ch->blksz = VCHIQ_AUDIO_PACKET_SIZE;
+
+ buffer = malloc(sc->bufsz, M_DEVBUF, M_WAITOK | M_ZERO);
+
+ if (sndbuf_setup(ch->buffer, buffer, sc->bufsz) != 0) {
+ device_printf(sc->dev, "sndbuf_setup failed\n");
+ free(buffer, M_DEVBUF);
+ return NULL;
+ }
+
+ BCM2835_AUDIO_LOCK(sc);
+ bcm2835_worker_update_params(sc);
+ BCM2835_AUDIO_UNLOCK(sc);
+
+ return ch;
+}
+
+static int
+bcmchan_free(kobj_t obj, void *data)
+{
+ struct bcm2835_audio_chinfo *ch = data;
+ void *buffer;
+
+ buffer = sndbuf_getbuf(ch->buffer);
+ if (buffer)
+ free(buffer, M_DEVBUF);
+
+ return (0);
+}
+
+static int
+bcmchan_setformat(kobj_t obj, void *data, uint32_t format)
+{
+ struct bcm2835_audio_chinfo *ch = data;
+ struct bcm2835_audio_info *sc = ch->parent;
+
+ BCM2835_AUDIO_LOCK(sc);
+ ch->fmt = format;
+ bcm2835_worker_update_params(sc);
+ BCM2835_AUDIO_UNLOCK(sc);
+
+ return 0;
+}
+
+static uint32_t
+bcmchan_setspeed(kobj_t obj, void *data, uint32_t speed)
+{
+ struct bcm2835_audio_chinfo *ch = data;
+ struct bcm2835_audio_info *sc = ch->parent;
+
+ BCM2835_AUDIO_LOCK(sc);
+ ch->spd = speed;
+ bcm2835_worker_update_params(sc);
+ BCM2835_AUDIO_UNLOCK(sc);
+
+ return ch->spd;
+}
+
+static uint32_t
+bcmchan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
+{
+ struct bcm2835_audio_chinfo *ch = data;
+
+ return ch->blksz;
+}
+
+static int
+bcmchan_trigger(kobj_t obj, void *data, int go)
+{
+ struct bcm2835_audio_chinfo *ch = data;
+ struct bcm2835_audio_info *sc = ch->parent;
+
+ if (!PCMTRIG_COMMON(go))
+ return (0);
+
+ switch (go) {
+ case PCMTRIG_START:
+ /* kickstart data flow */
+ chn_intr(sc->pch.channel);
+ ch->submitted_samples = 0;
+ ch->retrieved_samples = 0;
+ bcm2835_worker_play_start(sc);
+ break;
+
+ case PCMTRIG_STOP:
+ case PCMTRIG_ABORT:
+ bcm2835_worker_play_stop(sc);
+ break;
+
+ default:
+ break;
+ }
+ return 0;
+}
+
+static uint32_t
+bcmchan_getptr(kobj_t obj, void *data)
+{
+ struct bcm2835_audio_chinfo *ch = data;
+ struct bcm2835_audio_info *sc = ch->parent;
+ uint32_t ret;
+
+ BCM2835_AUDIO_LOCK(sc);
+ ret = ch->unsubmittedptr;
+ BCM2835_AUDIO_UNLOCK(sc);
+
+ return ret;
+}
+
+static struct pcmchan_caps *
+bcmchan_getcaps(kobj_t obj, void *data)
+{
+
+ return &bcm2835_audio_playcaps;
+}
+
+static kobj_method_t bcmchan_methods[] = {
+ KOBJMETHOD(channel_init, bcmchan_init),
+ KOBJMETHOD(channel_free, bcmchan_free),
+ KOBJMETHOD(channel_setformat, bcmchan_setformat),
+ KOBJMETHOD(channel_setspeed, bcmchan_setspeed),
+ KOBJMETHOD(channel_setblocksize, bcmchan_setblocksize),
+ KOBJMETHOD(channel_trigger, bcmchan_trigger),
+ KOBJMETHOD(channel_getptr, bcmchan_getptr),
+ KOBJMETHOD(channel_getcaps, bcmchan_getcaps),
+ KOBJMETHOD_END
+};
+CHANNEL_DECLARE(bcmchan);
+
+/************************************************************/
+
+static int
+bcmmix_init(struct snd_mixer *m)
+{
+
+ mix_setdevs(m, SOUND_MASK_VOLUME);
+
+ return (0);
+}
+
+static int
+bcmmix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
+{
+ struct bcm2835_audio_info *sc = mix_getdevinfo(m);
+
+ switch (dev) {
+ case SOUND_MIXER_VOLUME:
+ BCM2835_AUDIO_LOCK(sc);
+ sc->volume = left;
+ bcm2835_worker_update_params(sc);
+ BCM2835_AUDIO_UNLOCK(sc);
+
+ break;
+
+ default:
+ break;
+ }
+
+ return left | (left << 8);
+}
+
+static kobj_method_t bcmmixer_methods[] = {
+ KOBJMETHOD(mixer_init, bcmmix_init),
+ KOBJMETHOD(mixer_set, bcmmix_set),
+ KOBJMETHOD_END
+};
+
+MIXER_DECLARE(bcmmixer);
+
+static int
+sysctl_bcm2835_audio_dest(SYSCTL_HANDLER_ARGS)
+{
+ struct bcm2835_audio_info *sc = arg1;
+ int val;
+ int err;
+
+ val = sc->dest;
+ err = sysctl_handle_int(oidp, &val, 0, req);
+ if (err || !req->newptr) /* error || read request */
+ return (err);
+
+ if ((val < 0) || (val > 2))
+ return (EINVAL);
+
+ BCM2835_AUDIO_LOCK(sc);
+ sc->dest = val;
+ bcm2835_worker_update_params(sc);
+ BCM2835_AUDIO_UNLOCK(sc);
+
+ if (bootverbose)
+ device_printf(sc->dev, "destination set to %s\n", dest_description(val));
+
+ return (0);
+}
+
+static void
+vchi_audio_sysctl_init(struct bcm2835_audio_info *sc)
+{
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid *tree_node;
+ struct sysctl_oid_list *tree;
+
+ /*
+ * Add system sysctl tree/handlers.
+ */
+ ctx = device_get_sysctl_ctx(sc->dev);
+ tree_node = device_get_sysctl_tree(sc->dev);
+ tree = SYSCTL_CHILDREN(tree_node);
+ SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "dest",
+ CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_NEEDGIANT, sc, sizeof(*sc),
+ sysctl_bcm2835_audio_dest, "IU", "audio destination, "
+ "0 - auto, 1 - headphones, 2 - HDMI");
+ SYSCTL_ADD_UQUAD(ctx, tree, OID_AUTO, "callbacks",
+ CTLFLAG_RD, &sc->pch.callbacks,
+ "callbacks total");
+ SYSCTL_ADD_UQUAD(ctx, tree, OID_AUTO, "submitted",
+ CTLFLAG_RD, &sc->pch.submitted_samples,
+ "last play submitted samples");
+ SYSCTL_ADD_UQUAD(ctx, tree, OID_AUTO, "retrieved",
+ CTLFLAG_RD, &sc->pch.retrieved_samples,
+ "last play retrieved samples");
+ SYSCTL_ADD_UQUAD(ctx, tree, OID_AUTO, "underruns",
+ CTLFLAG_RD, &sc->pch.underruns,
+ "callback underruns");
+ SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "freebuffer",
+ CTLFLAG_RD, &sc->pch.available_space,
+ sc->pch.available_space, "callbacks total");
+ SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "starved",
+ CTLFLAG_RD, &sc->pch.starved,
+ sc->pch.starved, "number of starved conditions");
+}
+
+static void
+bcm2835_audio_identify(driver_t *driver, device_t parent)
+{
+
+ BUS_ADD_CHILD(parent, 0, "pcm", 0);
+}
+
+static int
+bcm2835_audio_probe(device_t dev)
+{
+
+ device_set_desc(dev, "VCHIQ audio");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static void
+bcm2835_audio_delayed_init(void *xsc)
+{
+ struct bcm2835_audio_info *sc;
+ char status[SND_STATUSLEN];
+
+ sc = xsc;
+
+ config_intrhook_disestablish(&sc->intr_hook);
+
+ bcm2835_audio_init(sc);
+ bcm2835_audio_open(sc);
+ sc->volume = 75;
+ sc->dest = DEST_AUTO;
+
+ if (mixer_init(sc->dev, &bcmmixer_class, sc)) {
+ device_printf(sc->dev, "mixer_init failed\n");
+ goto no;
+ }
+
+ if (pcm_register(sc->dev, sc, 1, 0)) {
+ device_printf(sc->dev, "pcm_register failed\n");
+ goto no;
+ }
+
+ pcm_addchan(sc->dev, PCMDIR_PLAY, &bcmchan_class, sc);
+ snprintf(status, SND_STATUSLEN, "at VCHIQ");
+ pcm_setstatus(sc->dev, status);
+
+ bcm2835_audio_reset_channel(&sc->pch);
+ bcm2835_audio_create_worker(sc);
+
+ vchi_audio_sysctl_init(sc);
+
+no:
+ ;
+}
+
+static int
+bcm2835_audio_attach(device_t dev)
+{
+ struct bcm2835_audio_info *sc;
+
+ sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO);
+
+ sc->dev = dev;
+ sc->bufsz = VCHIQ_AUDIO_BUFFER_SIZE;
+
+ mtx_init(&sc->lock, device_get_nameunit(dev),
+ "bcm_audio_lock", MTX_DEF);
+ cv_init(&sc->worker_cv, "worker_cv");
+ sc->vchi_handle = VCHIQ_SERVICE_HANDLE_INVALID;
+
+ /*
+ * We need interrupts enabled for VCHI to work properly,
+ * so delay initialization until it happens.
+ */
+ sc->intr_hook.ich_func = bcm2835_audio_delayed_init;
+ sc->intr_hook.ich_arg = sc;
+
+ if (config_intrhook_establish(&sc->intr_hook) != 0)
+ goto no;
+
+ return 0;
+
+no:
+ return ENXIO;
+}
+
+static int
+bcm2835_audio_detach(device_t dev)
+{
+ int r;
+ struct bcm2835_audio_info *sc;
+ sc = pcm_getdevinfo(dev);
+
+ /* Stop worker thread */
+ BCM2835_AUDIO_LOCK(sc);
+ sc->worker_state = WORKER_STOPPING;
+ cv_signal(&sc->worker_cv);
+ /* Wait for thread to exit */
+ while (sc->worker_state != WORKER_STOPPED)
+ cv_wait_sig(&sc->worker_cv, &sc->lock);
+ BCM2835_AUDIO_UNLOCK(sc);
+
+ r = pcm_unregister(dev);
+ if (r)
+ return r;
+
+ mtx_destroy(&sc->lock);
+ cv_destroy(&sc->worker_cv);
+
+ bcm2835_audio_release(sc);
+
+ free(sc, M_DEVBUF);
+
+ return 0;
+}
+
+static device_method_t bcm2835_audio_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_identify, bcm2835_audio_identify),
+ DEVMETHOD(device_probe, bcm2835_audio_probe),
+ DEVMETHOD(device_attach, bcm2835_audio_attach),
+ DEVMETHOD(device_detach, bcm2835_audio_detach),
+ { 0, 0 }
+};
+
+static driver_t bcm2835_audio_driver = {
+ "pcm",
+ bcm2835_audio_methods,
+ PCM_SOFTC_SIZE,
+};
+
+DRIVER_MODULE(bcm2835_audio, vchiq, bcm2835_audio_driver, pcm_devclass, 0, 0);
+MODULE_DEPEND(bcm2835_audio, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
+MODULE_DEPEND(bcm2835_audio, vchiq, 1, 1, 1);
+MODULE_VERSION(bcm2835_audio, 1);
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_bsc.c b/sys/arm/broadcom/bcm2835/bcm2835_bsc.c
new file mode 100644
index 000000000000..7c24d6a5959c
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2835_bsc.c
@@ -0,0 +1,727 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2001 Tsubai Masanari.
+ * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * Copyright (c) 2013 Luiz Otavio O Souza <loos@freebsd.org>
+ * Copyright (c) 2017 Ian Lepore <ian@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Driver for bcm2835 i2c-compatible two-wire bus, named 'BSC' on this SoC.
+ *
+ * This controller can only perform complete transfers, it does not provide
+ * low-level control over sending start/repeat-start/stop sequences on the bus.
+ * In addition, bugs in the silicon make it somewhat difficult to perform a
+ * repeat-start, and limit the repeat-start to a read following a write on
+ * the same slave device. (The i2c protocol allows a repeat start to change
+ * direction or not, and change slave address or not at any time.)
+ *
+ * The repeat-start bug and workaround are described in a problem report at
+ * https://github.com/raspberrypi/linux/issues/254 with the crucial part being
+ * in a comment block from a fragment of a GPU i2c driver, containing this:
+ *
+ * -----------------------------------------------------------------------------
+ * - See i2c.v: The I2C peripheral samples the values for rw_bit and xfer_count
+ * - in the IDLE state if start is set.
+ * -
+ * - We want to generate a ReSTART not a STOP at the end of the TX phase. In
+ * - order to do that we must ensure the state machine goes RACK1 -> RACK2 ->
+ * - SRSTRT1 (not RACK1 -> RACK2 -> SSTOP1).
+ * -
+ * - So, in the RACK2 state when (TX) xfer_count==0 we must therefore have
+ * - already set, ready to be sampled:
+ * - READ ; rw_bit <= I2CC bit 0 -- must be "read"
+ * - ST; start <= I2CC bit 7 -- must be "Go" in order to not issue STOP
+ * - DLEN; xfer_count <= I2CDLEN -- must be equal to our read amount
+ * -
+ * - The plan to do this is:
+ * - 1. Start the sub-address write, but don't let it finish
+ * - (keep xfer_count > 0)
+ * - 2. Populate READ, DLEN and ST in preparation for ReSTART read sequence
+ * - 3. Let TX finish (write the rest of the data)
+ * - 4. Read back data as it arrives
+ * -----------------------------------------------------------------------------
+ *
+ * The transfer function below scans the list of messages passed to it, looking
+ * for a read following a write to the same slave. When it finds that, it
+ * starts the write without prefilling the tx fifo, which holds xfer_count>0,
+ * then presets the direction, length, and start command for the following read,
+ * as described above. Then the tx fifo is filled and the rest of the transfer
+ * proceeds as normal, with the controller automatically supplying a
+ * repeat-start on the bus when the write operation finishes.
+ *
+ * XXX I suspect the controller may be able to do a repeat-start on any
+ * write->read or write->write transition, even when the slave addresses differ.
+ * It's unclear whether the slave address can be prestaged along with the
+ * direction and length while the write xfer_count is being held at zero. In
+ * fact, if it can't do this, then it couldn't be used to read EDID data.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/bus.h>
+#include <machine/resource.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+
+#include <dev/iicbus/iicbus.h>
+#include <dev/iicbus/iiconf.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/broadcom/bcm2835/bcm2835_bscreg.h>
+#include <arm/broadcom/bcm2835/bcm2835_bscvar.h>
+
+#include "iicbus_if.h"
+
+static struct ofw_compat_data compat_data[] = {
+ {"broadcom,bcm2835-bsc", 1},
+ {"brcm,bcm2708-i2c", 1},
+ {"brcm,bcm2835-i2c", 1},
+ {NULL, 0}
+};
+
+#define DEVICE_DEBUGF(sc, lvl, fmt, args...) \
+ if ((lvl) <= (sc)->sc_debug) \
+ device_printf((sc)->sc_dev, fmt, ##args)
+
+#define DEBUGF(sc, lvl, fmt, args...) \
+ if ((lvl) <= (sc)->sc_debug) \
+ printf(fmt, ##args)
+
+static void bcm_bsc_intr(void *);
+static int bcm_bsc_detach(device_t);
+
+static void
+bcm_bsc_modifyreg(struct bcm_bsc_softc *sc, uint32_t off, uint32_t mask,
+ uint32_t value)
+{
+ uint32_t reg;
+
+ mtx_assert(&sc->sc_mtx, MA_OWNED);
+ reg = BCM_BSC_READ(sc, off);
+ reg &= ~mask;
+ reg |= value;
+ BCM_BSC_WRITE(sc, off, reg);
+}
+
+static int
+bcm_bsc_clock_proc(SYSCTL_HANDLER_ARGS)
+{
+ struct bcm_bsc_softc *sc;
+ uint32_t clk;
+
+ sc = (struct bcm_bsc_softc *)arg1;
+ BCM_BSC_LOCK(sc);
+ clk = BCM_BSC_READ(sc, BCM_BSC_CLOCK);
+ BCM_BSC_UNLOCK(sc);
+ clk &= 0xffff;
+ if (clk == 0)
+ clk = 32768;
+ clk = BCM_BSC_CORE_CLK / clk;
+
+ return (sysctl_handle_int(oidp, &clk, 0, req));
+}
+
+static int
+bcm_bsc_clkt_proc(SYSCTL_HANDLER_ARGS)
+{
+ struct bcm_bsc_softc *sc;
+ uint32_t clkt;
+ int error;
+
+ sc = (struct bcm_bsc_softc *)arg1;
+
+ BCM_BSC_LOCK(sc);
+ clkt = BCM_BSC_READ(sc, BCM_BSC_CLKT);
+ BCM_BSC_UNLOCK(sc);
+ clkt &= 0xffff;
+ error = sysctl_handle_int(oidp, &clkt, sizeof(clkt), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ BCM_BSC_LOCK(sc);
+ BCM_BSC_WRITE(sc, BCM_BSC_CLKT, clkt & 0xffff);
+ BCM_BSC_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+bcm_bsc_fall_proc(SYSCTL_HANDLER_ARGS)
+{
+ struct bcm_bsc_softc *sc;
+ uint32_t clk, reg;
+ int error;
+
+ sc = (struct bcm_bsc_softc *)arg1;
+
+ BCM_BSC_LOCK(sc);
+ reg = BCM_BSC_READ(sc, BCM_BSC_DELAY);
+ BCM_BSC_UNLOCK(sc);
+ reg >>= 16;
+ error = sysctl_handle_int(oidp, &reg, sizeof(reg), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ BCM_BSC_LOCK(sc);
+ clk = BCM_BSC_READ(sc, BCM_BSC_CLOCK);
+ clk = BCM_BSC_CORE_CLK / clk;
+ if (reg > clk / 2)
+ reg = clk / 2 - 1;
+ bcm_bsc_modifyreg(sc, BCM_BSC_DELAY, 0xffff0000, reg << 16);
+ BCM_BSC_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+bcm_bsc_rise_proc(SYSCTL_HANDLER_ARGS)
+{
+ struct bcm_bsc_softc *sc;
+ uint32_t clk, reg;
+ int error;
+
+ sc = (struct bcm_bsc_softc *)arg1;
+
+ BCM_BSC_LOCK(sc);
+ reg = BCM_BSC_READ(sc, BCM_BSC_DELAY);
+ BCM_BSC_UNLOCK(sc);
+ reg &= 0xffff;
+ error = sysctl_handle_int(oidp, &reg, sizeof(reg), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ BCM_BSC_LOCK(sc);
+ clk = BCM_BSC_READ(sc, BCM_BSC_CLOCK);
+ clk = BCM_BSC_CORE_CLK / clk;
+ if (reg > clk / 2)
+ reg = clk / 2 - 1;
+ bcm_bsc_modifyreg(sc, BCM_BSC_DELAY, 0xffff, reg);
+ BCM_BSC_UNLOCK(sc);
+
+ return (0);
+}
+
+static void
+bcm_bsc_sysctl_init(struct bcm_bsc_softc *sc)
+{
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid *tree_node;
+ struct sysctl_oid_list *tree;
+
+ /*
+ * Add system sysctl tree/handlers.
+ */
+ ctx = device_get_sysctl_ctx(sc->sc_dev);
+ tree_node = device_get_sysctl_tree(sc->sc_dev);
+ tree = SYSCTL_CHILDREN(tree_node);
+ SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "frequency",
+ CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_NEEDGIANT,
+ sc, sizeof(*sc),
+ bcm_bsc_clock_proc, "IU", "I2C BUS clock frequency");
+ SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "clock_stretch",
+ CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_NEEDGIANT,
+ sc, sizeof(*sc),
+ bcm_bsc_clkt_proc, "IU", "I2C BUS clock stretch timeout");
+ SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "fall_edge_delay",
+ CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_NEEDGIANT,
+ sc, sizeof(*sc),
+ bcm_bsc_fall_proc, "IU", "I2C BUS falling edge delay");
+ SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "rise_edge_delay",
+ CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_NEEDGIANT,
+ sc, sizeof(*sc),
+ bcm_bsc_rise_proc, "IU", "I2C BUS rising edge delay");
+ SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "debug",
+ CTLFLAG_RWTUN, &sc->sc_debug, 0,
+ "Enable debug; 1=reads/writes, 2=add starts/stops");
+}
+
+static void
+bcm_bsc_reset(struct bcm_bsc_softc *sc)
+{
+
+ /* Enable the BSC Controller, disable interrupts. */
+ BCM_BSC_WRITE(sc, BCM_BSC_CTRL, BCM_BSC_CTRL_I2CEN);
+ /* Clear pending interrupts. */
+ BCM_BSC_WRITE(sc, BCM_BSC_STATUS, BCM_BSC_STATUS_CLKT |
+ BCM_BSC_STATUS_ERR | BCM_BSC_STATUS_DONE);
+ /* Clear the FIFO. */
+ bcm_bsc_modifyreg(sc, BCM_BSC_CTRL, BCM_BSC_CTRL_CLEAR0,
+ BCM_BSC_CTRL_CLEAR0);
+}
+
+static int
+bcm_bsc_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "BCM2708/2835 BSC controller");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+bcm_bsc_attach(device_t dev)
+{
+ struct bcm_bsc_softc *sc;
+ int rid;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+
+ rid = 0;
+ sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (!sc->sc_mem_res) {
+ device_printf(dev, "cannot allocate memory window\n");
+ return (ENXIO);
+ }
+
+ sc->sc_bst = rman_get_bustag(sc->sc_mem_res);
+ sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res);
+
+ rid = 0;
+ sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE | RF_SHAREABLE);
+ if (!sc->sc_irq_res) {
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+ device_printf(dev, "cannot allocate interrupt\n");
+ return (ENXIO);
+ }
+
+ /* Hook up our interrupt handler. */
+ if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+ NULL, bcm_bsc_intr, sc, &sc->sc_intrhand)) {
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+ device_printf(dev, "cannot setup the interrupt handler\n");
+ return (ENXIO);
+ }
+
+ mtx_init(&sc->sc_mtx, "bcm_bsc", NULL, MTX_DEF);
+
+ bcm_bsc_sysctl_init(sc);
+
+ /* Enable the BSC controller. Flush the FIFO. */
+ BCM_BSC_LOCK(sc);
+ bcm_bsc_reset(sc);
+ BCM_BSC_UNLOCK(sc);
+
+ sc->sc_iicbus = device_add_child(dev, "iicbus", -1);
+ if (sc->sc_iicbus == NULL) {
+ bcm_bsc_detach(dev);
+ return (ENXIO);
+ }
+
+ /* Probe and attach the iicbus when interrupts are available. */
+ return (bus_delayed_attach_children(dev));
+}
+
+static int
+bcm_bsc_detach(device_t dev)
+{
+ struct bcm_bsc_softc *sc;
+
+ bus_generic_detach(dev);
+
+ sc = device_get_softc(dev);
+ if (sc->sc_iicbus != NULL)
+ device_delete_child(dev, sc->sc_iicbus);
+ mtx_destroy(&sc->sc_mtx);
+ if (sc->sc_intrhand)
+ bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand);
+ if (sc->sc_irq_res)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
+ if (sc->sc_mem_res)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+
+ return (0);
+}
+
+static void
+bcm_bsc_empty_rx_fifo(struct bcm_bsc_softc *sc)
+{
+ uint32_t status;
+
+ /* Assumes sc_totlen > 0 and BCM_BSC_STATUS_RXD is asserted on entry. */
+ do {
+ if (sc->sc_resid == 0) {
+ sc->sc_data = sc->sc_curmsg->buf;
+ sc->sc_dlen = sc->sc_curmsg->len;
+ sc->sc_resid = sc->sc_dlen;
+ ++sc->sc_curmsg;
+ }
+ do {
+ *sc->sc_data = BCM_BSC_READ(sc, BCM_BSC_DATA);
+ DEBUGF(sc, 1, "0x%02x ", *sc->sc_data);
+ ++sc->sc_data;
+ --sc->sc_resid;
+ --sc->sc_totlen;
+ status = BCM_BSC_READ(sc, BCM_BSC_STATUS);
+ } while (sc->sc_resid > 0 && (status & BCM_BSC_STATUS_RXD));
+ } while (sc->sc_totlen > 0 && (status & BCM_BSC_STATUS_RXD));
+}
+
+static void
+bcm_bsc_fill_tx_fifo(struct bcm_bsc_softc *sc)
+{
+ uint32_t status;
+
+ /* Assumes sc_totlen > 0 and BCM_BSC_STATUS_TXD is asserted on entry. */
+ do {
+ if (sc->sc_resid == 0) {
+ sc->sc_data = sc->sc_curmsg->buf;
+ sc->sc_dlen = sc->sc_curmsg->len;
+ sc->sc_resid = sc->sc_dlen;
+ ++sc->sc_curmsg;
+ }
+ do {
+ BCM_BSC_WRITE(sc, BCM_BSC_DATA, *sc->sc_data);
+ DEBUGF(sc, 1, "0x%02x ", *sc->sc_data);
+ ++sc->sc_data;
+ --sc->sc_resid;
+ --sc->sc_totlen;
+ status = BCM_BSC_READ(sc, BCM_BSC_STATUS);
+ } while (sc->sc_resid > 0 && (status & BCM_BSC_STATUS_TXD));
+ /*
+ * If a repeat-start was pending and we just hit the end of a tx
+ * buffer, see if it's also the end of the writes that preceeded
+ * the repeat-start. If so, log the repeat-start and the start
+ * of the following read, and return because we're not writing
+ * anymore (and TXD will be true because there's room to write
+ * in the fifo).
+ */
+ if (sc->sc_replen > 0 && sc->sc_resid == 0) {
+ sc->sc_replen -= sc->sc_dlen;
+ if (sc->sc_replen == 0) {
+ DEBUGF(sc, 1, " err=0\n");
+ DEVICE_DEBUGF(sc, 2, "rstart 0x%02x\n",
+ sc->sc_curmsg->slave | 0x01);
+ DEVICE_DEBUGF(sc, 1,
+ "read 0x%02x len %d: ",
+ sc->sc_curmsg->slave | 0x01,
+ sc->sc_totlen);
+ sc->sc_flags |= BCM_I2C_READ;
+ return;
+ }
+ }
+ } while (sc->sc_totlen > 0 && (status & BCM_BSC_STATUS_TXD));
+}
+
+static void
+bcm_bsc_intr(void *arg)
+{
+ struct bcm_bsc_softc *sc;
+ uint32_t status;
+
+ sc = (struct bcm_bsc_softc *)arg;
+
+ BCM_BSC_LOCK(sc);
+
+ /* The I2C interrupt is shared among all the BSC controllers. */
+ if ((sc->sc_flags & BCM_I2C_BUSY) == 0) {
+ BCM_BSC_UNLOCK(sc);
+ return;
+ }
+
+ status = BCM_BSC_READ(sc, BCM_BSC_STATUS);
+ DEBUGF(sc, 4, " <intrstatus=0x%08x> ", status);
+
+ /* RXD and DONE can assert together, empty fifo before checking done. */
+ if ((sc->sc_flags & BCM_I2C_READ) && (status & BCM_BSC_STATUS_RXD))
+ bcm_bsc_empty_rx_fifo(sc);
+
+ /* Check for completion. */
+ if (status & (BCM_BSC_STATUS_ERRBITS | BCM_BSC_STATUS_DONE)) {
+ sc->sc_flags |= BCM_I2C_DONE;
+ if (status & BCM_BSC_STATUS_ERRBITS)
+ sc->sc_flags |= BCM_I2C_ERROR;
+ /* Disable interrupts. */
+ bcm_bsc_reset(sc);
+ wakeup(sc);
+ } else if (!(sc->sc_flags & BCM_I2C_READ)) {
+ /*
+ * Don't check for TXD until after determining whether the
+ * transfer is complete; TXD will be asserted along with ERR or
+ * DONE if there is room in the fifo.
+ */
+ if ((status & BCM_BSC_STATUS_TXD) && sc->sc_totlen > 0)
+ bcm_bsc_fill_tx_fifo(sc);
+ }
+
+ BCM_BSC_UNLOCK(sc);
+}
+
+static int
+bcm_bsc_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
+{
+ struct bcm_bsc_softc *sc;
+ struct iic_msg *endmsgs, *nxtmsg;
+ uint32_t readctl, status;
+ int err;
+ uint16_t curlen;
+ uint8_t curisread, curslave, nxtisread, nxtslave;
+
+ sc = device_get_softc(dev);
+ BCM_BSC_LOCK(sc);
+
+ /* If the controller is busy wait until it is available. */
+ while (sc->sc_flags & BCM_I2C_BUSY)
+ mtx_sleep(dev, &sc->sc_mtx, 0, "bscbusw", 0);
+
+ /* Now we have control over the BSC controller. */
+ sc->sc_flags = BCM_I2C_BUSY;
+
+ DEVICE_DEBUGF(sc, 3, "Transfer %d msgs\n", nmsgs);
+
+ /* Clear the FIFO and the pending interrupts. */
+ bcm_bsc_reset(sc);
+
+ /*
+ * Perform all the transfers requested in the array of msgs. Note that
+ * it is bcm_bsc_empty_rx_fifo() and bcm_bsc_fill_tx_fifo() that advance
+ * sc->sc_curmsg through the array of messages, as the data from each
+ * message is fully consumed, but it is this loop that notices when we
+ * have no more messages to process.
+ */
+ err = 0;
+ sc->sc_resid = 0;
+ sc->sc_curmsg = msgs;
+ endmsgs = &msgs[nmsgs];
+ while (sc->sc_curmsg < endmsgs) {
+ readctl = 0;
+ curslave = sc->sc_curmsg->slave >> 1;
+ curisread = sc->sc_curmsg->flags & IIC_M_RD;
+ sc->sc_replen = 0;
+ sc->sc_totlen = sc->sc_curmsg->len;
+ /*
+ * Scan for scatter/gather IO (same slave and direction) or
+ * repeat-start (read following write for the same slave).
+ */
+ for (nxtmsg = sc->sc_curmsg + 1; nxtmsg < endmsgs; ++nxtmsg) {
+ nxtslave = nxtmsg->slave >> 1;
+ if (curslave == nxtslave) {
+ nxtisread = nxtmsg->flags & IIC_M_RD;
+ if (curisread == nxtisread) {
+ /*
+ * Same slave and direction, this
+ * message will be part of the same
+ * transfer as the previous one.
+ */
+ sc->sc_totlen += nxtmsg->len;
+ continue;
+ } else if (curisread == IIC_M_WR) {
+ /*
+ * Read after write to same slave means
+ * repeat-start, remember how many bytes
+ * come before the repeat-start, switch
+ * the direction to IIC_M_RD, and gather
+ * up following reads to the same slave.
+ */
+ curisread = IIC_M_RD;
+ sc->sc_replen = sc->sc_totlen;
+ sc->sc_totlen += nxtmsg->len;
+ continue;
+ }
+ }
+ break;
+ }
+
+ /*
+ * curslave and curisread temporaries from above may refer to
+ * the after-repstart msg, reset them to reflect sc_curmsg.
+ */
+ curisread = (sc->sc_curmsg->flags & IIC_M_RD) ? 1 : 0;
+ curslave = sc->sc_curmsg->slave | curisread;
+
+ /* Write the slave address. */
+ BCM_BSC_WRITE(sc, BCM_BSC_SLAVE, curslave >> 1);
+
+ DEVICE_DEBUGF(sc, 2, "start 0x%02x\n", curslave);
+
+ /*
+ * Either set up read length and direction variables for a
+ * simple transfer or get the hardware started on the first
+ * piece of a transfer that involves a repeat-start and set up
+ * the read length and direction vars for the second piece.
+ */
+ if (sc->sc_replen == 0) {
+ DEVICE_DEBUGF(sc, 1, "%-6s 0x%02x len %d: ",
+ (curisread) ? "read" : "write", curslave,
+ sc->sc_totlen);
+ curlen = sc->sc_totlen;
+ if (curisread) {
+ readctl = BCM_BSC_CTRL_READ;
+ sc->sc_flags |= BCM_I2C_READ;
+ } else {
+ readctl = 0;
+ sc->sc_flags &= ~BCM_I2C_READ;
+ }
+ } else {
+ DEVICE_DEBUGF(sc, 1, "%-6s 0x%02x len %d: ",
+ (curisread) ? "read" : "write", curslave,
+ sc->sc_replen);
+
+ /*
+ * Start the write transfer with an empty fifo and wait
+ * for the 'transfer active' status bit to light up;
+ * that indicates that the hardware has latched the
+ * direction and length for the write, and we can safely
+ * reload those registers and issue the start for the
+ * following read; interrupts are not enabled here.
+ */
+ BCM_BSC_WRITE(sc, BCM_BSC_DLEN, sc->sc_replen);
+ BCM_BSC_WRITE(sc, BCM_BSC_CTRL, BCM_BSC_CTRL_I2CEN |
+ BCM_BSC_CTRL_ST);
+ do {
+ status = BCM_BSC_READ(sc, BCM_BSC_STATUS);
+ if (status & BCM_BSC_STATUS_ERR) {
+ /* no ACK on slave addr */
+ err = EIO;
+ goto xfer_done;
+ }
+ } while ((status & BCM_BSC_STATUS_TA) == 0);
+ /*
+ * Set curlen and readctl for the repeat-start read that
+ * we need to set up below, but set sc_flags to write,
+ * because that is the operation in progress right now.
+ */
+ curlen = sc->sc_totlen - sc->sc_replen;
+ readctl = BCM_BSC_CTRL_READ;
+ sc->sc_flags &= ~BCM_I2C_READ;
+ }
+
+ /*
+ * Start the transfer with interrupts enabled, then if doing a
+ * write, fill the tx fifo. Not prefilling the fifo until after
+ * this start command is the key workaround for making
+ * repeat-start work, and it's harmless to do it in this order
+ * for a regular write too.
+ */
+ BCM_BSC_WRITE(sc, BCM_BSC_DLEN, curlen);
+ BCM_BSC_WRITE(sc, BCM_BSC_CTRL, readctl | BCM_BSC_CTRL_I2CEN |
+ BCM_BSC_CTRL_ST | BCM_BSC_CTRL_INT_ALL);
+
+ if (!(sc->sc_curmsg->flags & IIC_M_RD)) {
+ bcm_bsc_fill_tx_fifo(sc);
+ }
+
+ /* Wait for the transaction to complete. */
+ while (err == 0 && !(sc->sc_flags & BCM_I2C_DONE)) {
+ err = mtx_sleep(sc, &sc->sc_mtx, 0, "bsciow", hz);
+ }
+ /* Check for errors. */
+ if (err == 0 && (sc->sc_flags & BCM_I2C_ERROR))
+ err = EIO;
+xfer_done:
+ DEBUGF(sc, 1, " err=%d\n", err);
+ DEVICE_DEBUGF(sc, 2, "stop\n");
+ if (err != 0)
+ break;
+ }
+
+ /* Disable interrupts, clean fifo, etc. */
+ bcm_bsc_reset(sc);
+
+ /* Clean the controller flags. */
+ sc->sc_flags = 0;
+
+ /* Wake up the threads waiting for bus. */
+ wakeup(dev);
+
+ BCM_BSC_UNLOCK(sc);
+
+ return (err);
+}
+
+static int
+bcm_bsc_iicbus_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
+{
+ struct bcm_bsc_softc *sc;
+ uint32_t busfreq;
+
+ sc = device_get_softc(dev);
+ BCM_BSC_LOCK(sc);
+ bcm_bsc_reset(sc);
+ if (sc->sc_iicbus == NULL)
+ busfreq = 100000;
+ else
+ busfreq = IICBUS_GET_FREQUENCY(sc->sc_iicbus, speed);
+ BCM_BSC_WRITE(sc, BCM_BSC_CLOCK, BCM_BSC_CORE_CLK / busfreq);
+ BCM_BSC_UNLOCK(sc);
+
+ return (IIC_ENOADDR);
+}
+
+static phandle_t
+bcm_bsc_get_node(device_t bus, device_t dev)
+{
+
+ /* We only have one child, the I2C bus, which needs our own node. */
+ return (ofw_bus_get_node(bus));
+}
+
+static device_method_t bcm_bsc_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, bcm_bsc_probe),
+ DEVMETHOD(device_attach, bcm_bsc_attach),
+ DEVMETHOD(device_detach, bcm_bsc_detach),
+
+ /* iicbus interface */
+ DEVMETHOD(iicbus_reset, bcm_bsc_iicbus_reset),
+ DEVMETHOD(iicbus_callback, iicbus_null_callback),
+ DEVMETHOD(iicbus_transfer, bcm_bsc_transfer),
+
+ /* ofw_bus interface */
+ DEVMETHOD(ofw_bus_get_node, bcm_bsc_get_node),
+
+ DEVMETHOD_END
+};
+
+static devclass_t bcm_bsc_devclass;
+
+static driver_t bcm_bsc_driver = {
+ "iichb",
+ bcm_bsc_methods,
+ sizeof(struct bcm_bsc_softc),
+};
+
+DRIVER_MODULE(iicbus, bcm2835_bsc, iicbus_driver, iicbus_devclass, 0, 0);
+DRIVER_MODULE(bcm2835_bsc, simplebus, bcm_bsc_driver, bcm_bsc_devclass, 0, 0);
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_bscreg.h b/sys/arm/broadcom/bcm2835/bcm2835_bscreg.h
new file mode 100644
index 000000000000..4b9532053807
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2835_bscreg.h
@@ -0,0 +1,69 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * Copyright (c) 2013 Luiz Otavio O Souza <loos@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _BCM2835_BSCREG_H_
+#define _BCM2835_BSCREG_H_
+
+#define BCM_BSC_CORE_CLK 150000000U
+#define BCM_BSC_CTRL 0x00
+#define BCM_BSC_CTRL_I2CEN (1 << 15)
+#define BCM_BSC_CTRL_INTR (1 << 10)
+#define BCM_BSC_CTRL_INTT (1 << 9)
+#define BCM_BSC_CTRL_INTD (1 << 8)
+#define BCM_BSC_CTRL_ST (1 << 7)
+#define BCM_BSC_CTRL_CLEAR1 (1 << 5)
+#define BCM_BSC_CTRL_CLEAR0 (1 << 4)
+#define BCM_BSC_CTRL_READ (1 << 0)
+#define BCM_BSC_CTRL_INT_ALL \
+ (BCM_BSC_CTRL_INTR | BCM_BSC_CTRL_INTT | BCM_BSC_CTRL_INTD)
+
+#define BCM_BSC_STATUS 0x04
+#define BCM_BSC_STATUS_CLKT (1 << 9)
+#define BCM_BSC_STATUS_ERR (1 << 8)
+#define BCM_BSC_STATUS_RXF (1 << 7)
+#define BCM_BSC_STATUS_TXE (1 << 6)
+#define BCM_BSC_STATUS_RXD (1 << 5)
+#define BCM_BSC_STATUS_TXD (1 << 4)
+#define BCM_BSC_STATUS_RXR (1 << 3)
+#define BCM_BSC_STATUS_TXW (1 << 2)
+#define BCM_BSC_STATUS_DONE (1 << 1)
+#define BCM_BSC_STATUS_TA (1 << 0)
+#define BCM_BSC_STATUS_ERRBITS \
+ (BCM_BSC_STATUS_CLKT | BCM_BSC_STATUS_ERR)
+
+#define BCM_BSC_DLEN 0x08
+#define BCM_BSC_SLAVE 0x0c
+#define BCM_BSC_DATA 0x10
+#define BCM_BSC_CLOCK 0x14
+#define BCM_BSC_DELAY 0x18
+#define BCM_BSC_CLKT 0x1c
+
+#endif /* _BCM2835_BSCREG_H_ */
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_bscvar.h b/sys/arm/broadcom/bcm2835/bcm2835_bscvar.h
new file mode 100644
index 000000000000..b57607ea597e
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2835_bscvar.h
@@ -0,0 +1,71 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * Copyright (c) 2013 Luiz Otavio O Souza <loos@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _BCM2835_BSCVAR_H
+#define _BCM2835_BSCVAR_H
+
+struct iic_msg;
+
+struct bcm_bsc_softc {
+ device_t sc_dev;
+ device_t sc_iicbus;
+ struct mtx sc_mtx;
+ struct resource * sc_mem_res;
+ struct resource * sc_irq_res;
+ void * sc_intrhand;
+ struct iic_msg * sc_curmsg;
+ bus_space_tag_t sc_bst;
+ bus_space_handle_t sc_bsh;
+ int sc_debug;
+ uint16_t sc_replen;
+ uint16_t sc_totlen;
+ uint16_t sc_resid;
+ uint16_t sc_dlen;
+ uint8_t * sc_data;
+ uint8_t sc_flags;
+};
+
+#define BCM_I2C_BUSY 0x01
+#define BCM_I2C_READ 0x02
+#define BCM_I2C_ERROR 0x04
+#define BCM_I2C_DONE 0x08
+
+#define BCM_BSC_WRITE(_sc, _off, _val) \
+ bus_space_write_4((_sc)->sc_bst, (_sc)->sc_bsh, _off, _val)
+#define BCM_BSC_READ(_sc, _off) \
+ bus_space_read_4((_sc)->sc_bst, (_sc)->sc_bsh, _off)
+
+#define BCM_BSC_LOCK(_sc) \
+ mtx_lock(&(_sc)->sc_mtx)
+#define BCM_BSC_UNLOCK(_sc) \
+ mtx_unlock(&(_sc)->sc_mtx)
+
+#endif /* _BCM2835_BSCVAR_H_ */
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_clkman.c b/sys/arm/broadcom/bcm2835/bcm2835_clkman.c
new file mode 100644
index 000000000000..d33ff1ff73f0
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2835_clkman.c
@@ -0,0 +1,213 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2017 Poul-Henning Kamp <phk@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/cpu.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/sema.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/broadcom/bcm2835/bcm2835_clkman.h>
+
+static struct ofw_compat_data compat_data[] = {
+ {"brcm,bcm2835-cprman", 1},
+ {"broadcom,bcm2835-cprman", 1},
+ {NULL, 0}
+};
+
+struct bcm2835_clkman_softc {
+ device_t sc_dev;
+
+ struct resource * sc_m_res;
+ bus_space_tag_t sc_m_bst;
+ bus_space_handle_t sc_m_bsh;
+};
+
+#define BCM_CLKMAN_WRITE(_sc, _off, _val) \
+ bus_space_write_4(_sc->sc_m_bst, _sc->sc_m_bsh, _off, _val)
+#define BCM_CLKMAN_READ(_sc, _off) \
+ bus_space_read_4(_sc->sc_m_bst, _sc->sc_m_bsh, _off)
+
+#define W_CMCLK(_sc, unit, _val) BCM_CLKMAN_WRITE(_sc, unit, 0x5a000000 | (_val))
+#define R_CMCLK(_sc, unit) BCM_CLKMAN_READ(_sc, unit)
+#define W_CMDIV(_sc, unit, _val) BCM_CLKMAN_WRITE(_sc, (unit) + 4, 0x5a000000 | (_val))
+#define R_CMDIV(_sc, unit) BCM_CLKMAN_READ(_sc, (unit) + 4)
+
+static int
+bcm2835_clkman_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "BCM283x Clock Manager");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+bcm2835_clkman_attach(device_t dev)
+{
+ struct bcm2835_clkman_softc *sc;
+ int rid;
+
+ if (device_get_unit(dev) != 0) {
+ device_printf(dev, "only one clk manager supported\n");
+ return (ENXIO);
+ }
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+
+ rid = 0;
+ sc->sc_m_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (!sc->sc_m_res) {
+ device_printf(dev, "cannot allocate memory window\n");
+ return (ENXIO);
+ }
+
+ sc->sc_m_bst = rman_get_bustag(sc->sc_m_res);
+ sc->sc_m_bsh = rman_get_bushandle(sc->sc_m_res);
+
+ return (bus_generic_attach(dev));
+}
+
+uint32_t
+bcm2835_clkman_set_frequency(device_t dev, uint32_t unit, uint32_t hz)
+{
+ struct bcm2835_clkman_softc *sc;
+ int i;
+ uint32_t u;
+
+ sc = device_get_softc(dev);
+
+ if (unit != BCM_PWM_CLKSRC) {
+ device_printf(sc->sc_dev,
+ "Unsupported unit 0x%x", unit);
+ return (0);
+ }
+
+ W_CMCLK(sc, unit, 6);
+ for (i = 0; i < 10; i++) {
+ u = R_CMCLK(sc, unit);
+ if (!(u&0x80))
+ break;
+ DELAY(1000);
+ }
+ if (u & 0x80) {
+ device_printf(sc->sc_dev,
+ "Failed to stop clock for unit 0x%x", unit);
+ return (0);
+ }
+ if (hz == 0)
+ return (0);
+
+ u = 500000000/hz;
+ if (u < 4) {
+ device_printf(sc->sc_dev,
+ "Frequency too high for unit 0x%x (max: 125 MHz)",
+ unit);
+ return (0);
+ }
+ if (u > 0xfff) {
+ device_printf(sc->sc_dev,
+ "Frequency too low for unit 0x%x (min: 123 kHz)",
+ unit);
+ return (0);
+ }
+ hz = 500000000/u;
+ W_CMDIV(sc, unit, u << 12);
+
+ W_CMCLK(sc, unit, 0x16);
+ for (i = 0; i < 10; i++) {
+ u = R_CMCLK(sc, unit);
+ if ((u&0x80))
+ break;
+ DELAY(1000);
+ }
+ if (!(u & 0x80)) {
+ device_printf(sc->sc_dev,
+ "Failed to start clock for unit 0x%x", unit);
+ return (0);
+ }
+ return (hz);
+}
+
+static int
+bcm2835_clkman_detach(device_t dev)
+{
+ struct bcm2835_clkman_softc *sc;
+
+ bus_generic_detach(dev);
+
+ sc = device_get_softc(dev);
+ if (sc->sc_m_res)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_m_res);
+
+ return (0);
+}
+
+static device_method_t bcm2835_clkman_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, bcm2835_clkman_probe),
+ DEVMETHOD(device_attach, bcm2835_clkman_attach),
+ DEVMETHOD(device_detach, bcm2835_clkman_detach),
+
+ DEVMETHOD_END
+};
+
+static devclass_t bcm2835_clkman_devclass;
+static driver_t bcm2835_clkman_driver = {
+ "bcm2835_clkman",
+ bcm2835_clkman_methods,
+ sizeof(struct bcm2835_clkman_softc),
+};
+
+DRIVER_MODULE(bcm2835_clkman, simplebus, bcm2835_clkman_driver,
+ bcm2835_clkman_devclass, 0, 0);
+MODULE_VERSION(bcm2835_clkman, 1);
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_clkman.h b/sys/arm/broadcom/bcm2835/bcm2835_clkman.h
new file mode 100644
index 000000000000..488bb1d65504
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2835_clkman.h
@@ -0,0 +1,43 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2017 Poul-Henning Kamp <phk@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _BCM2835_CLKMAN_H_
+#define _BCM2835_CLKMAN_H_
+
+// Offset into BAR0 for unit
+enum bcm2835_clksrc {
+ BCM_GPIO0_CLKSRC = 0x70,
+ BCM_GPIO1_CLKSRC = 0x78,
+ BCM_GPIO2_CLKSRC = 0x80,
+ BCM_PWM_CLKSRC = 0xa0,
+};
+
+uint32_t bcm2835_clkman_set_frequency(device_t, uint32_t, uint32_t);
+
+#endif /* _BCM2835_CLKMAN_H_ */
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_cpufreq.c b/sys/arm/broadcom/bcm2835/bcm2835_cpufreq.c
new file mode 100644
index 000000000000..1c767ad629d6
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2835_cpufreq.c
@@ -0,0 +1,1606 @@
+/*-
+ * Copyright (C) 2013-2015 Daisuke Aoyama <aoyama@peach.ne.jp>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/cpu.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/sema.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/broadcom/bcm2835/bcm2835_firmware.h>
+#include <arm/broadcom/bcm2835/bcm2835_vcbus.h>
+
+#include "cpufreq_if.h"
+
+#ifdef DEBUG
+#define DPRINTF(fmt, ...) do { \
+ printf("%s:%u: ", __func__, __LINE__); \
+ printf(fmt, ##__VA_ARGS__); \
+} while (0)
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
+#define HZ2MHZ(freq) ((freq) / (1000 * 1000))
+#define MHZ2HZ(freq) ((freq) * (1000 * 1000))
+
+#ifdef SOC_BCM2835
+#define OFFSET2MVOLT(val) (1200 + ((val) * 25))
+#define MVOLT2OFFSET(val) (((val) - 1200) / 25)
+#define DEFAULT_ARM_FREQUENCY 700
+#define DEFAULT_LOWEST_FREQ 300
+#else
+#define OFFSET2MVOLT(val) (((val) / 1000))
+#define MVOLT2OFFSET(val) (((val) * 1000))
+#define DEFAULT_ARM_FREQUENCY 600
+#define DEFAULT_LOWEST_FREQ 600
+#endif
+#define DEFAULT_CORE_FREQUENCY 250
+#define DEFAULT_SDRAM_FREQUENCY 400
+#define TRANSITION_LATENCY 1000
+#define MIN_OVER_VOLTAGE -16
+#define MAX_OVER_VOLTAGE 6
+#define MSG_ERROR -999999999
+#define MHZSTEP 100
+#define HZSTEP (MHZ2HZ(MHZSTEP))
+#define TZ_ZEROC 2731
+
+#define VC_LOCK(sc) do { \
+ sema_wait(&vc_sema); \
+ } while (0)
+#define VC_UNLOCK(sc) do { \
+ sema_post(&vc_sema); \
+ } while (0)
+
+/* ARM->VC mailbox property semaphore */
+static struct sema vc_sema;
+
+static struct sysctl_ctx_list bcm2835_sysctl_ctx;
+
+struct bcm2835_cpufreq_softc {
+ device_t dev;
+ device_t firmware;
+ int arm_max_freq;
+ int arm_min_freq;
+ int core_max_freq;
+ int core_min_freq;
+ int sdram_max_freq;
+ int sdram_min_freq;
+ int max_voltage_core;
+ int min_voltage_core;
+
+ /* the values written in mbox */
+ int voltage_core;
+ int voltage_sdram;
+ int voltage_sdram_c;
+ int voltage_sdram_i;
+ int voltage_sdram_p;
+ int turbo_mode;
+
+ /* initial hook for waiting mbox intr */
+ struct intr_config_hook init_hook;
+};
+
+static struct ofw_compat_data compat_data[] = {
+ { "broadcom,bcm2835-vc", 1 },
+ { "broadcom,bcm2708-vc", 1 },
+ { "brcm,bcm2709", 1 },
+ { "brcm,bcm2835", 1 },
+ { "brcm,bcm2836", 1 },
+ { "brcm,bcm2837", 1 },
+ { "brcm,bcm2711", 1 },
+ { NULL, 0 }
+};
+
+static int cpufreq_verbose = 0;
+TUNABLE_INT("hw.bcm2835.cpufreq.verbose", &cpufreq_verbose);
+static int cpufreq_lowest_freq = DEFAULT_LOWEST_FREQ;
+TUNABLE_INT("hw.bcm2835.cpufreq.lowest_freq", &cpufreq_lowest_freq);
+
+#ifdef PROP_DEBUG
+static void
+bcm2835_dump(const void *data, int len)
+{
+ const uint8_t *p = (const uint8_t*)data;
+ int i;
+
+ printf("dump @ %p:\n", data);
+ for (i = 0; i < len; i++) {
+ printf("%2.2x ", p[i]);
+ if ((i % 4) == 3)
+ printf(" ");
+ if ((i % 16) == 15)
+ printf("\n");
+ }
+ printf("\n");
+}
+#endif
+
+static int
+bcm2835_cpufreq_get_clock_rate(struct bcm2835_cpufreq_softc *sc,
+ uint32_t clock_id)
+{
+ union msg_get_clock_rate_body msg;
+ int rate;
+ int err;
+
+ /*
+ * Get clock rate
+ * Tag: 0x00030002
+ * Request:
+ * Length: 4
+ * Value:
+ * u32: clock id
+ * Response:
+ * Length: 8
+ * Value:
+ * u32: clock id
+ * u32: rate (in Hz)
+ */
+
+ /* setup single tag buffer */
+ memset(&msg, 0, sizeof(msg));
+ msg.req.clock_id = clock_id;
+
+ /* call mailbox property */
+ err = bcm2835_firmware_property(sc->firmware,
+ BCM2835_FIRMWARE_TAG_GET_CLOCK_RATE, &msg, sizeof(msg));
+ if (err) {
+ device_printf(sc->dev, "can't get clock rate (id=%u)\n",
+ clock_id);
+ return (MSG_ERROR);
+ }
+
+ /* result (Hz) */
+ rate = (int)msg.resp.rate_hz;
+ DPRINTF("clock = %d(Hz)\n", rate);
+ return (rate);
+}
+
+static int
+bcm2835_cpufreq_get_max_clock_rate(struct bcm2835_cpufreq_softc *sc,
+ uint32_t clock_id)
+{
+ union msg_get_clock_rate_body msg;
+ int rate;
+ int err;
+
+ /*
+ * Get max clock rate
+ * Tag: 0x00030004
+ * Request:
+ * Length: 4
+ * Value:
+ * u32: clock id
+ * Response:
+ * Length: 8
+ * Value:
+ * u32: clock id
+ * u32: rate (in Hz)
+ */
+
+ /* setup single tag buffer */
+ memset(&msg, 0, sizeof(msg));
+ msg.req.clock_id = clock_id;
+
+ /* call mailbox property */
+ err = bcm2835_firmware_property(sc->firmware,
+ BCM2835_FIRMWARE_TAG_GET_MAX_CLOCK_RATE, &msg, sizeof(msg));
+ if (err) {
+ device_printf(sc->dev, "can't get max clock rate (id=%u)\n",
+ clock_id);
+ return (MSG_ERROR);
+ }
+
+ /* result (Hz) */
+ rate = (int)msg.resp.rate_hz;
+ DPRINTF("clock = %d(Hz)\n", rate);
+ return (rate);
+}
+
+static int
+bcm2835_cpufreq_get_min_clock_rate(struct bcm2835_cpufreq_softc *sc,
+ uint32_t clock_id)
+{
+ union msg_get_clock_rate_body msg;
+ int rate;
+ int err;
+
+ /*
+ * Get min clock rate
+ * Tag: 0x00030007
+ * Request:
+ * Length: 4
+ * Value:
+ * u32: clock id
+ * Response:
+ * Length: 8
+ * Value:
+ * u32: clock id
+ * u32: rate (in Hz)
+ */
+
+ /* setup single tag buffer */
+ memset(&msg, 0, sizeof(msg));
+ msg.req.clock_id = clock_id;
+
+ /* call mailbox property */
+ err = bcm2835_firmware_property(sc->firmware,
+ BCM2835_FIRMWARE_TAG_GET_MIN_CLOCK_RATE, &msg, sizeof(msg));
+ if (err) {
+ device_printf(sc->dev, "can't get min clock rate (id=%u)\n",
+ clock_id);
+ return (MSG_ERROR);
+ }
+
+ /* result (Hz) */
+ rate = (int)msg.resp.rate_hz;
+ DPRINTF("clock = %d(Hz)\n", rate);
+ return (rate);
+}
+
+static int
+bcm2835_cpufreq_set_clock_rate(struct bcm2835_cpufreq_softc *sc,
+ uint32_t clock_id, uint32_t rate_hz)
+{
+ union msg_set_clock_rate_body msg;
+ int rate;
+ int err;
+
+ /*
+ * Set clock rate
+ * Tag: 0x00038002
+ * Request:
+ * Length: 8
+ * Value:
+ * u32: clock id
+ * u32: rate (in Hz)
+ * Response:
+ * Length: 8
+ * Value:
+ * u32: clock id
+ * u32: rate (in Hz)
+ */
+
+ /* setup single tag buffer */
+ memset(&msg, 0, sizeof(msg));
+ msg.req.clock_id = clock_id;
+ msg.req.rate_hz = rate_hz;
+
+ /* call mailbox property */
+ err = bcm2835_firmware_property(sc->firmware,
+ BCM2835_FIRMWARE_TAG_SET_CLOCK_RATE, &msg, sizeof(msg));
+ if (err) {
+ device_printf(sc->dev, "can't set clock rate (id=%u)\n",
+ clock_id);
+ return (MSG_ERROR);
+ }
+
+ /* workaround for core clock */
+ if (clock_id == BCM2835_FIRMWARE_CLOCK_ID_CORE) {
+ /* for safety (may change voltage without changing clock) */
+ DELAY(TRANSITION_LATENCY);
+
+ /*
+ * XXX: the core clock is unable to change at once,
+ * to change certainly, write it twice now.
+ */
+
+ /* setup single tag buffer */
+ memset(&msg, 0, sizeof(msg));
+ msg.req.clock_id = clock_id;
+ msg.req.rate_hz = rate_hz;
+
+ /* call mailbox property */
+ err = bcm2835_firmware_property(sc->firmware,
+ BCM2835_FIRMWARE_TAG_SET_CLOCK_RATE, &msg, sizeof(msg));
+ if (err) {
+ device_printf(sc->dev,
+ "can't set clock rate (id=%u)\n", clock_id);
+ return (MSG_ERROR);
+ }
+ }
+
+ /* result (Hz) */
+ rate = (int)msg.resp.rate_hz;
+ DPRINTF("clock = %d(Hz)\n", rate);
+ return (rate);
+}
+
+static int
+bcm2835_cpufreq_get_turbo(struct bcm2835_cpufreq_softc *sc)
+{
+ union msg_get_turbo_body msg;
+ int level;
+ int err;
+
+ /*
+ * Get turbo
+ * Tag: 0x00030009
+ * Request:
+ * Length: 4
+ * Value:
+ * u32: id
+ * Response:
+ * Length: 8
+ * Value:
+ * u32: id
+ * u32: level
+ */
+
+ /* setup single tag buffer */
+ memset(&msg, 0, sizeof(msg));
+ msg.req.id = 0;
+
+ /* call mailbox property */
+ err = bcm2835_firmware_property(sc->firmware,
+ BCM2835_FIRMWARE_TAG_GET_TURBO, &msg, sizeof(msg));
+ if (err) {
+ device_printf(sc->dev, "can't get turbo\n");
+ return (MSG_ERROR);
+ }
+
+ /* result 0=non-turbo, 1=turbo */
+ level = (int)msg.resp.level;
+ DPRINTF("level = %d\n", level);
+ return (level);
+}
+
+static int
+bcm2835_cpufreq_set_turbo(struct bcm2835_cpufreq_softc *sc, uint32_t level)
+{
+ union msg_set_turbo_body msg;
+ int value;
+ int err;
+
+ /*
+ * Set turbo
+ * Tag: 0x00038009
+ * Request:
+ * Length: 8
+ * Value:
+ * u32: id
+ * u32: level
+ * Response:
+ * Length: 8
+ * Value:
+ * u32: id
+ * u32: level
+ */
+
+ /* replace unknown value to OFF */
+ if (level != BCM2835_FIRMWARE_TURBO_ON &&
+ level != BCM2835_FIRMWARE_TURBO_OFF)
+ level = BCM2835_FIRMWARE_TURBO_OFF;
+
+ /* setup single tag buffer */
+ memset(&msg, 0, sizeof(msg));
+ msg.req.id = 0;
+ msg.req.level = level;
+
+ /* call mailbox property */
+ err = bcm2835_firmware_property(sc->firmware,
+ BCM2835_FIRMWARE_TAG_SET_TURBO, &msg, sizeof(msg));
+ if (err) {
+ device_printf(sc->dev, "can't set turbo\n");
+ return (MSG_ERROR);
+ }
+
+ /* result 0=non-turbo, 1=turbo */
+ value = (int)msg.resp.level;
+ DPRINTF("level = %d\n", value);
+ return (value);
+}
+
+static int
+bcm2835_cpufreq_get_voltage(struct bcm2835_cpufreq_softc *sc,
+ uint32_t voltage_id)
+{
+ union msg_get_voltage_body msg;
+ int value;
+ int err;
+
+ /*
+ * Get voltage
+ * Tag: 0x00030003
+ * Request:
+ * Length: 4
+ * Value:
+ * u32: voltage id
+ * Response:
+ * Length: 8
+ * Value:
+ * u32: voltage id
+ * u32: value (offset from 1.2V in units of 0.025V)
+ */
+
+ /* setup single tag buffer */
+ memset(&msg, 0, sizeof(msg));
+ msg.req.voltage_id = voltage_id;
+
+ /* call mailbox property */
+ err = bcm2835_firmware_property(sc->firmware,
+ BCM2835_FIRMWARE_TAG_GET_VOLTAGE, &msg, sizeof(msg));
+ if (err) {
+ device_printf(sc->dev, "can't get voltage\n");
+ return (MSG_ERROR);
+ }
+
+ /* result (offset from 1.2V) */
+ value = (int)msg.resp.value;
+ DPRINTF("value = %d\n", value);
+ return (value);
+}
+
+static int
+bcm2835_cpufreq_get_max_voltage(struct bcm2835_cpufreq_softc *sc,
+ uint32_t voltage_id)
+{
+ union msg_get_voltage_body msg;
+ int value;
+ int err;
+
+ /*
+ * Get voltage
+ * Tag: 0x00030005
+ * Request:
+ * Length: 4
+ * Value:
+ * u32: voltage id
+ * Response:
+ * Length: 8
+ * Value:
+ * u32: voltage id
+ * u32: value (offset from 1.2V in units of 0.025V)
+ */
+
+ /* setup single tag buffer */
+ memset(&msg, 0, sizeof(msg));
+ msg.req.voltage_id = voltage_id;
+
+ /* call mailbox property */
+ err = bcm2835_firmware_property(sc->firmware,
+ BCM2835_FIRMWARE_TAG_GET_MAX_VOLTAGE, &msg, sizeof(msg));
+ if (err) {
+ device_printf(sc->dev, "can't get max voltage\n");
+ return (MSG_ERROR);
+ }
+
+ /* result (offset from 1.2V) */
+ value = (int)msg.resp.value;
+ DPRINTF("value = %d\n", value);
+ return (value);
+}
+static int
+bcm2835_cpufreq_get_min_voltage(struct bcm2835_cpufreq_softc *sc,
+ uint32_t voltage_id)
+{
+ union msg_get_voltage_body msg;
+ int value;
+ int err;
+
+ /*
+ * Get voltage
+ * Tag: 0x00030008
+ * Request:
+ * Length: 4
+ * Value:
+ * u32: voltage id
+ * Response:
+ * Length: 8
+ * Value:
+ * u32: voltage id
+ * u32: value (offset from 1.2V in units of 0.025V)
+ */
+
+ /* setup single tag buffer */
+ memset(&msg, 0, sizeof(msg));
+ msg.req.voltage_id = voltage_id;
+
+ /* call mailbox property */
+ err = bcm2835_firmware_property(sc->firmware,
+ BCM2835_FIRMWARE_TAG_GET_MIN_VOLTAGE, &msg, sizeof(msg));
+ if (err) {
+ device_printf(sc->dev, "can't get min voltage\n");
+ return (MSG_ERROR);
+ }
+
+ /* result (offset from 1.2V) */
+ value = (int)msg.resp.value;
+ DPRINTF("value = %d\n", value);
+ return (value);
+}
+
+static int
+bcm2835_cpufreq_set_voltage(struct bcm2835_cpufreq_softc *sc,
+ uint32_t voltage_id, int32_t value)
+{
+ union msg_set_voltage_body msg;
+ int err;
+
+ /*
+ * Set voltage
+ * Tag: 0x00038003
+ * Request:
+ * Length: 4
+ * Value:
+ * u32: voltage id
+ * u32: value (offset from 1.2V in units of 0.025V)
+ * Response:
+ * Length: 8
+ * Value:
+ * u32: voltage id
+ * u32: value (offset from 1.2V in units of 0.025V)
+ */
+
+ /*
+ * over_voltage:
+ * 0 (1.2 V). Values above 6 are only allowed when force_turbo or
+ * current_limit_override are specified (which set the warranty bit).
+ */
+ if (value > MAX_OVER_VOLTAGE || value < MIN_OVER_VOLTAGE) {
+ /* currently not supported */
+ device_printf(sc->dev, "not supported voltage: %d\n", value);
+ return (MSG_ERROR);
+ }
+
+ /* setup single tag buffer */
+ memset(&msg, 0, sizeof(msg));
+ msg.req.voltage_id = voltage_id;
+ msg.req.value = (uint32_t)value;
+
+ /* call mailbox property */
+ err = bcm2835_firmware_property(sc->firmware,
+ BCM2835_FIRMWARE_TAG_SET_VOLTAGE, &msg, sizeof(msg));
+ if (err) {
+ device_printf(sc->dev, "can't set voltage\n");
+ return (MSG_ERROR);
+ }
+
+ /* result (offset from 1.2V) */
+ value = (int)msg.resp.value;
+ DPRINTF("value = %d\n", value);
+ return (value);
+}
+
+static int
+bcm2835_cpufreq_get_temperature(struct bcm2835_cpufreq_softc *sc)
+{
+ union msg_get_temperature_body msg;
+ int value;
+ int err;
+
+ /*
+ * Get temperature
+ * Tag: 0x00030006
+ * Request:
+ * Length: 4
+ * Value:
+ * u32: temperature id
+ * Response:
+ * Length: 8
+ * Value:
+ * u32: temperature id
+ * u32: value
+ */
+
+ /* setup single tag buffer */
+ memset(&msg, 0, sizeof(msg));
+ msg.req.temperature_id = 0;
+
+ /* call mailbox property */
+ err = bcm2835_firmware_property(sc->firmware,
+ BCM2835_FIRMWARE_TAG_GET_TEMPERATURE, &msg, sizeof(msg));
+ if (err) {
+ device_printf(sc->dev, "can't get temperature\n");
+ return (MSG_ERROR);
+ }
+
+ /* result (temperature of degree C) */
+ value = (int)msg.resp.value;
+ DPRINTF("value = %d\n", value);
+ return (value);
+}
+
+static int
+sysctl_bcm2835_cpufreq_arm_freq(SYSCTL_HANDLER_ARGS)
+{
+ struct bcm2835_cpufreq_softc *sc = arg1;
+ int val;
+ int err;
+
+ /* get realtime value */
+ VC_LOCK(sc);
+ val = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_FIRMWARE_CLOCK_ID_ARM);
+ VC_UNLOCK(sc);
+ if (val == MSG_ERROR)
+ return (EIO);
+
+ err = sysctl_handle_int(oidp, &val, 0, req);
+ if (err || !req->newptr) /* error || read request */
+ return (err);
+
+ /* write request */
+ VC_LOCK(sc);
+ err = bcm2835_cpufreq_set_clock_rate(sc, BCM2835_FIRMWARE_CLOCK_ID_ARM,
+ val);
+ VC_UNLOCK(sc);
+ if (err == MSG_ERROR) {
+ device_printf(sc->dev, "set clock arm_freq error\n");
+ return (EIO);
+ }
+ DELAY(TRANSITION_LATENCY);
+
+ return (0);
+}
+
+static int
+sysctl_bcm2835_cpufreq_core_freq(SYSCTL_HANDLER_ARGS)
+{
+ struct bcm2835_cpufreq_softc *sc = arg1;
+ int val;
+ int err;
+
+ /* get realtime value */
+ VC_LOCK(sc);
+ val = bcm2835_cpufreq_get_clock_rate(sc,
+ BCM2835_FIRMWARE_CLOCK_ID_CORE);
+ VC_UNLOCK(sc);
+ if (val == MSG_ERROR)
+ return (EIO);
+
+ err = sysctl_handle_int(oidp, &val, 0, req);
+ if (err || !req->newptr) /* error || read request */
+ return (err);
+
+ /* write request */
+ VC_LOCK(sc);
+ err = bcm2835_cpufreq_set_clock_rate(sc, BCM2835_FIRMWARE_CLOCK_ID_CORE,
+ val);
+ if (err == MSG_ERROR) {
+ VC_UNLOCK(sc);
+ device_printf(sc->dev, "set clock core_freq error\n");
+ return (EIO);
+ }
+ VC_UNLOCK(sc);
+ DELAY(TRANSITION_LATENCY);
+
+ return (0);
+}
+
+static int
+sysctl_bcm2835_cpufreq_sdram_freq(SYSCTL_HANDLER_ARGS)
+{
+ struct bcm2835_cpufreq_softc *sc = arg1;
+ int val;
+ int err;
+
+ /* get realtime value */
+ VC_LOCK(sc);
+ val = bcm2835_cpufreq_get_clock_rate(sc,
+ BCM2835_FIRMWARE_CLOCK_ID_SDRAM);
+ VC_UNLOCK(sc);
+ if (val == MSG_ERROR)
+ return (EIO);
+
+ err = sysctl_handle_int(oidp, &val, 0, req);
+ if (err || !req->newptr) /* error || read request */
+ return (err);
+
+ /* write request */
+ VC_LOCK(sc);
+ err = bcm2835_cpufreq_set_clock_rate(sc,
+ BCM2835_FIRMWARE_CLOCK_ID_SDRAM, val);
+ VC_UNLOCK(sc);
+ if (err == MSG_ERROR) {
+ device_printf(sc->dev, "set clock sdram_freq error\n");
+ return (EIO);
+ }
+ DELAY(TRANSITION_LATENCY);
+
+ return (0);
+}
+
+static int
+sysctl_bcm2835_cpufreq_turbo(SYSCTL_HANDLER_ARGS)
+{
+ struct bcm2835_cpufreq_softc *sc = arg1;
+ int val;
+ int err;
+
+ /* get realtime value */
+ VC_LOCK(sc);
+ val = bcm2835_cpufreq_get_turbo(sc);
+ VC_UNLOCK(sc);
+ if (val == MSG_ERROR)
+ return (EIO);
+
+ err = sysctl_handle_int(oidp, &val, 0, req);
+ if (err || !req->newptr) /* error || read request */
+ return (err);
+
+ /* write request */
+ if (val > 0)
+ sc->turbo_mode = BCM2835_FIRMWARE_TURBO_ON;
+ else
+ sc->turbo_mode = BCM2835_FIRMWARE_TURBO_OFF;
+
+ VC_LOCK(sc);
+ err = bcm2835_cpufreq_set_turbo(sc, sc->turbo_mode);
+ VC_UNLOCK(sc);
+ if (err == MSG_ERROR) {
+ device_printf(sc->dev, "set turbo error\n");
+ return (EIO);
+ }
+ DELAY(TRANSITION_LATENCY);
+
+ return (0);
+}
+
+static int
+sysctl_bcm2835_cpufreq_voltage_core(SYSCTL_HANDLER_ARGS)
+{
+ struct bcm2835_cpufreq_softc *sc = arg1;
+ int val;
+ int err;
+
+ /* get realtime value */
+ VC_LOCK(sc);
+ val = bcm2835_cpufreq_get_voltage(sc, BCM2835_FIRMWARE_VOLTAGE_ID_CORE);
+ VC_UNLOCK(sc);
+ if (val == MSG_ERROR)
+ return (EIO);
+
+ err = sysctl_handle_int(oidp, &val, 0, req);
+ if (err || !req->newptr) /* error || read request */
+ return (err);
+
+ /* write request */
+ if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE)
+ return (EINVAL);
+ sc->voltage_core = val;
+
+ VC_LOCK(sc);
+ err = bcm2835_cpufreq_set_voltage(sc, BCM2835_FIRMWARE_VOLTAGE_ID_CORE,
+ sc->voltage_core);
+ VC_UNLOCK(sc);
+ if (err == MSG_ERROR) {
+ device_printf(sc->dev, "set voltage core error\n");
+ return (EIO);
+ }
+ DELAY(TRANSITION_LATENCY);
+
+ return (0);
+}
+
+static int
+sysctl_bcm2835_cpufreq_voltage_sdram_c(SYSCTL_HANDLER_ARGS)
+{
+ struct bcm2835_cpufreq_softc *sc = arg1;
+ int val;
+ int err;
+
+ /* get realtime value */
+ VC_LOCK(sc);
+ val = bcm2835_cpufreq_get_voltage(sc,
+ BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_C);
+ VC_UNLOCK(sc);
+ if (val == MSG_ERROR)
+ return (EIO);
+
+ err = sysctl_handle_int(oidp, &val, 0, req);
+ if (err || !req->newptr) /* error || read request */
+ return (err);
+
+ /* write request */
+ if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE)
+ return (EINVAL);
+ sc->voltage_sdram_c = val;
+
+ VC_LOCK(sc);
+ err = bcm2835_cpufreq_set_voltage(sc,
+ BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_C,
+ sc->voltage_sdram_c);
+ VC_UNLOCK(sc);
+ if (err == MSG_ERROR) {
+ device_printf(sc->dev, "set voltage sdram_c error\n");
+ return (EIO);
+ }
+ DELAY(TRANSITION_LATENCY);
+
+ return (0);
+}
+
+static int
+sysctl_bcm2835_cpufreq_voltage_sdram_i(SYSCTL_HANDLER_ARGS)
+{
+ struct bcm2835_cpufreq_softc *sc = arg1;
+ int val;
+ int err;
+
+ /* get realtime value */
+ VC_LOCK(sc);
+ val = bcm2835_cpufreq_get_voltage(sc,
+ BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_I);
+ VC_UNLOCK(sc);
+ if (val == MSG_ERROR)
+ return (EIO);
+
+ err = sysctl_handle_int(oidp, &val, 0, req);
+ if (err || !req->newptr) /* error || read request */
+ return (err);
+
+ /* write request */
+ if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE)
+ return (EINVAL);
+ sc->voltage_sdram_i = val;
+
+ VC_LOCK(sc);
+ err = bcm2835_cpufreq_set_voltage(sc,
+ BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_I, sc->voltage_sdram_i);
+ VC_UNLOCK(sc);
+ if (err == MSG_ERROR) {
+ device_printf(sc->dev, "set voltage sdram_i error\n");
+ return (EIO);
+ }
+ DELAY(TRANSITION_LATENCY);
+
+ return (0);
+}
+
+static int
+sysctl_bcm2835_cpufreq_voltage_sdram_p(SYSCTL_HANDLER_ARGS)
+{
+ struct bcm2835_cpufreq_softc *sc = arg1;
+ int val;
+ int err;
+
+ /* get realtime value */
+ VC_LOCK(sc);
+ val = bcm2835_cpufreq_get_voltage(sc,
+ BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_P);
+ VC_UNLOCK(sc);
+ if (val == MSG_ERROR)
+ return (EIO);
+
+ err = sysctl_handle_int(oidp, &val, 0, req);
+ if (err || !req->newptr) /* error || read request */
+ return (err);
+
+ /* write request */
+ if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE)
+ return (EINVAL);
+ sc->voltage_sdram_p = val;
+
+ VC_LOCK(sc);
+ err = bcm2835_cpufreq_set_voltage(sc,
+ BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_P, sc->voltage_sdram_p);
+ VC_UNLOCK(sc);
+ if (err == MSG_ERROR) {
+ device_printf(sc->dev, "set voltage sdram_p error\n");
+ return (EIO);
+ }
+ DELAY(TRANSITION_LATENCY);
+
+ return (0);
+}
+
+static int
+sysctl_bcm2835_cpufreq_voltage_sdram(SYSCTL_HANDLER_ARGS)
+{
+ struct bcm2835_cpufreq_softc *sc = arg1;
+ int val;
+ int err;
+
+ /* multiple write only */
+ if (!req->newptr)
+ return (EINVAL);
+ val = 0;
+ err = sysctl_handle_int(oidp, &val, 0, req);
+ if (err)
+ return (err);
+
+ /* write request */
+ if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE)
+ return (EINVAL);
+ sc->voltage_sdram = val;
+
+ VC_LOCK(sc);
+ err = bcm2835_cpufreq_set_voltage(sc,
+ BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_C, val);
+ if (err == MSG_ERROR) {
+ VC_UNLOCK(sc);
+ device_printf(sc->dev, "set voltage sdram_c error\n");
+ return (EIO);
+ }
+ err = bcm2835_cpufreq_set_voltage(sc,
+ BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_I, val);
+ if (err == MSG_ERROR) {
+ VC_UNLOCK(sc);
+ device_printf(sc->dev, "set voltage sdram_i error\n");
+ return (EIO);
+ }
+ err = bcm2835_cpufreq_set_voltage(sc,
+ BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_P, val);
+ if (err == MSG_ERROR) {
+ VC_UNLOCK(sc);
+ device_printf(sc->dev, "set voltage sdram_p error\n");
+ return (EIO);
+ }
+ VC_UNLOCK(sc);
+ DELAY(TRANSITION_LATENCY);
+
+ return (0);
+}
+
+static int
+sysctl_bcm2835_cpufreq_temperature(SYSCTL_HANDLER_ARGS)
+{
+ struct bcm2835_cpufreq_softc *sc = arg1;
+ int val;
+ int err;
+
+ /* get realtime value */
+ VC_LOCK(sc);
+ val = bcm2835_cpufreq_get_temperature(sc);
+ VC_UNLOCK(sc);
+ if (val == MSG_ERROR)
+ return (EIO);
+
+ err = sysctl_handle_int(oidp, &val, 0, req);
+ if (err || !req->newptr) /* error || read request */
+ return (err);
+
+ /* write request */
+ return (EINVAL);
+}
+
+static int
+sysctl_bcm2835_devcpu_temperature(SYSCTL_HANDLER_ARGS)
+{
+ struct bcm2835_cpufreq_softc *sc = arg1;
+ int val;
+ int err;
+
+ /* get realtime value */
+ VC_LOCK(sc);
+ val = bcm2835_cpufreq_get_temperature(sc);
+ VC_UNLOCK(sc);
+ if (val == MSG_ERROR)
+ return (EIO);
+
+ /* 1/1000 celsius (raw) to 1/10 kelvin */
+ val = val / 100 + TZ_ZEROC;
+
+ err = sysctl_handle_int(oidp, &val, 0, req);
+ if (err || !req->newptr) /* error || read request */
+ return (err);
+
+ /* write request */
+ return (EINVAL);
+}
+
+static void
+bcm2835_cpufreq_init(void *arg)
+{
+ struct bcm2835_cpufreq_softc *sc = arg;
+ struct sysctl_ctx_list *ctx;
+ device_t cpu;
+ int arm_freq, core_freq, sdram_freq;
+ int arm_max_freq, arm_min_freq, core_max_freq, core_min_freq;
+ int sdram_max_freq, sdram_min_freq;
+ int voltage_core, voltage_sdram_c, voltage_sdram_i, voltage_sdram_p;
+ int max_voltage_core, min_voltage_core;
+ int max_voltage_sdram_c, min_voltage_sdram_c;
+ int max_voltage_sdram_i, min_voltage_sdram_i;
+ int max_voltage_sdram_p, min_voltage_sdram_p;
+ int turbo, temperature;
+
+ VC_LOCK(sc);
+
+ /* current clock */
+ arm_freq = bcm2835_cpufreq_get_clock_rate(sc,
+ BCM2835_FIRMWARE_CLOCK_ID_ARM);
+ core_freq = bcm2835_cpufreq_get_clock_rate(sc,
+ BCM2835_FIRMWARE_CLOCK_ID_CORE);
+ sdram_freq = bcm2835_cpufreq_get_clock_rate(sc,
+ BCM2835_FIRMWARE_CLOCK_ID_SDRAM);
+
+ /* max/min clock */
+ arm_max_freq = bcm2835_cpufreq_get_max_clock_rate(sc,
+ BCM2835_FIRMWARE_CLOCK_ID_ARM);
+ arm_min_freq = bcm2835_cpufreq_get_min_clock_rate(sc,
+ BCM2835_FIRMWARE_CLOCK_ID_ARM);
+ core_max_freq = bcm2835_cpufreq_get_max_clock_rate(sc,
+ BCM2835_FIRMWARE_CLOCK_ID_CORE);
+ core_min_freq = bcm2835_cpufreq_get_min_clock_rate(sc,
+ BCM2835_FIRMWARE_CLOCK_ID_CORE);
+ sdram_max_freq = bcm2835_cpufreq_get_max_clock_rate(sc,
+ BCM2835_FIRMWARE_CLOCK_ID_SDRAM);
+ sdram_min_freq = bcm2835_cpufreq_get_min_clock_rate(sc,
+ BCM2835_FIRMWARE_CLOCK_ID_SDRAM);
+
+ /* turbo mode */
+ turbo = bcm2835_cpufreq_get_turbo(sc);
+ if (turbo > 0)
+ sc->turbo_mode = BCM2835_FIRMWARE_TURBO_ON;
+ else
+ sc->turbo_mode = BCM2835_FIRMWARE_TURBO_OFF;
+
+ /* voltage */
+ voltage_core = bcm2835_cpufreq_get_voltage(sc,
+ BCM2835_FIRMWARE_VOLTAGE_ID_CORE);
+ voltage_sdram_c = bcm2835_cpufreq_get_voltage(sc,
+ BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_C);
+ voltage_sdram_i = bcm2835_cpufreq_get_voltage(sc,
+ BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_I);
+ voltage_sdram_p = bcm2835_cpufreq_get_voltage(sc,
+ BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_P);
+
+ /* current values (offset from 1.2V) */
+ sc->voltage_core = voltage_core;
+ sc->voltage_sdram = voltage_sdram_c;
+ sc->voltage_sdram_c = voltage_sdram_c;
+ sc->voltage_sdram_i = voltage_sdram_i;
+ sc->voltage_sdram_p = voltage_sdram_p;
+
+ /* max/min voltage */
+ max_voltage_core = bcm2835_cpufreq_get_max_voltage(sc,
+ BCM2835_FIRMWARE_VOLTAGE_ID_CORE);
+ min_voltage_core = bcm2835_cpufreq_get_min_voltage(sc,
+ BCM2835_FIRMWARE_VOLTAGE_ID_CORE);
+ max_voltage_sdram_c = bcm2835_cpufreq_get_max_voltage(sc,
+ BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_C);
+ max_voltage_sdram_i = bcm2835_cpufreq_get_max_voltage(sc,
+ BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_I);
+ max_voltage_sdram_p = bcm2835_cpufreq_get_max_voltage(sc,
+ BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_P);
+ min_voltage_sdram_c = bcm2835_cpufreq_get_min_voltage(sc,
+ BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_C);
+ min_voltage_sdram_i = bcm2835_cpufreq_get_min_voltage(sc,
+ BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_I);
+ min_voltage_sdram_p = bcm2835_cpufreq_get_min_voltage(sc,
+ BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_P);
+
+ /* temperature */
+ temperature = bcm2835_cpufreq_get_temperature(sc);
+
+ /* show result */
+ if (cpufreq_verbose || bootverbose) {
+ device_printf(sc->dev, "Boot settings:\n");
+ device_printf(sc->dev,
+ "current ARM %dMHz, Core %dMHz, SDRAM %dMHz, Turbo %s\n",
+ HZ2MHZ(arm_freq), HZ2MHZ(core_freq), HZ2MHZ(sdram_freq),
+ (sc->turbo_mode == BCM2835_FIRMWARE_TURBO_ON) ? "ON":"OFF");
+
+ device_printf(sc->dev,
+ "max/min ARM %d/%dMHz, Core %d/%dMHz, SDRAM %d/%dMHz\n",
+ HZ2MHZ(arm_max_freq), HZ2MHZ(arm_min_freq),
+ HZ2MHZ(core_max_freq), HZ2MHZ(core_min_freq),
+ HZ2MHZ(sdram_max_freq), HZ2MHZ(sdram_min_freq));
+
+ device_printf(sc->dev,
+ "current Core %dmV, SDRAM_C %dmV, SDRAM_I %dmV, "
+ "SDRAM_P %dmV\n",
+ OFFSET2MVOLT(voltage_core), OFFSET2MVOLT(voltage_sdram_c),
+ OFFSET2MVOLT(voltage_sdram_i),
+ OFFSET2MVOLT(voltage_sdram_p));
+
+ device_printf(sc->dev,
+ "max/min Core %d/%dmV, SDRAM_C %d/%dmV, SDRAM_I %d/%dmV, "
+ "SDRAM_P %d/%dmV\n",
+ OFFSET2MVOLT(max_voltage_core),
+ OFFSET2MVOLT(min_voltage_core),
+ OFFSET2MVOLT(max_voltage_sdram_c),
+ OFFSET2MVOLT(min_voltage_sdram_c),
+ OFFSET2MVOLT(max_voltage_sdram_i),
+ OFFSET2MVOLT(min_voltage_sdram_i),
+ OFFSET2MVOLT(max_voltage_sdram_p),
+ OFFSET2MVOLT(min_voltage_sdram_p));
+
+ device_printf(sc->dev,
+ "Temperature %d.%dC\n", (temperature / 1000),
+ (temperature % 1000) / 100);
+ } else { /* !cpufreq_verbose && !bootverbose */
+ device_printf(sc->dev,
+ "ARM %dMHz, Core %dMHz, SDRAM %dMHz, Turbo %s\n",
+ HZ2MHZ(arm_freq), HZ2MHZ(core_freq), HZ2MHZ(sdram_freq),
+ (sc->turbo_mode == BCM2835_FIRMWARE_TURBO_ON) ? "ON":"OFF");
+ }
+
+ /* keep in softc (MHz/mV) */
+ sc->arm_max_freq = HZ2MHZ(arm_max_freq);
+ sc->arm_min_freq = HZ2MHZ(arm_min_freq);
+ sc->core_max_freq = HZ2MHZ(core_max_freq);
+ sc->core_min_freq = HZ2MHZ(core_min_freq);
+ sc->sdram_max_freq = HZ2MHZ(sdram_max_freq);
+ sc->sdram_min_freq = HZ2MHZ(sdram_min_freq);
+ sc->max_voltage_core = OFFSET2MVOLT(max_voltage_core);
+ sc->min_voltage_core = OFFSET2MVOLT(min_voltage_core);
+
+ /* if turbo is on, set to max values */
+ if (sc->turbo_mode == BCM2835_FIRMWARE_TURBO_ON) {
+ bcm2835_cpufreq_set_clock_rate(sc,
+ BCM2835_FIRMWARE_CLOCK_ID_ARM, arm_max_freq);
+ DELAY(TRANSITION_LATENCY);
+ bcm2835_cpufreq_set_clock_rate(sc,
+ BCM2835_FIRMWARE_CLOCK_ID_CORE, core_max_freq);
+ DELAY(TRANSITION_LATENCY);
+ bcm2835_cpufreq_set_clock_rate(sc,
+ BCM2835_FIRMWARE_CLOCK_ID_SDRAM, sdram_max_freq);
+ DELAY(TRANSITION_LATENCY);
+ } else {
+ bcm2835_cpufreq_set_clock_rate(sc,
+ BCM2835_FIRMWARE_CLOCK_ID_ARM, arm_min_freq);
+ DELAY(TRANSITION_LATENCY);
+ bcm2835_cpufreq_set_clock_rate(sc,
+ BCM2835_FIRMWARE_CLOCK_ID_CORE, core_min_freq);
+ DELAY(TRANSITION_LATENCY);
+ bcm2835_cpufreq_set_clock_rate(sc,
+ BCM2835_FIRMWARE_CLOCK_ID_SDRAM, sdram_min_freq);
+ DELAY(TRANSITION_LATENCY);
+ }
+
+ VC_UNLOCK(sc);
+
+ /* add human readable temperature to dev.cpu node */
+ cpu = device_get_parent(sc->dev);
+ if (cpu != NULL) {
+ ctx = device_get_sysctl_ctx(cpu);
+ SYSCTL_ADD_PROC(ctx,
+ SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)), OID_AUTO,
+ "temperature",
+ CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, 0,
+ sysctl_bcm2835_devcpu_temperature, "IK",
+ "Current SoC temperature");
+ }
+
+ /* release this hook (continue boot) */
+ config_intrhook_disestablish(&sc->init_hook);
+}
+
+static void
+bcm2835_cpufreq_identify(driver_t *driver, device_t parent)
+{
+ const struct ofw_compat_data *compat;
+ phandle_t root;
+
+ root = OF_finddevice("/");
+ for (compat = compat_data; compat->ocd_str != NULL; compat++)
+ if (ofw_bus_node_is_compatible(root, compat->ocd_str))
+ break;
+
+ if (compat->ocd_data == 0)
+ return;
+
+ DPRINTF("driver=%p, parent=%p\n", driver, parent);
+ if (device_find_child(parent, "bcm2835_cpufreq", -1) != NULL)
+ return;
+ if (BUS_ADD_CHILD(parent, 0, "bcm2835_cpufreq", -1) == NULL)
+ device_printf(parent, "add child failed\n");
+}
+
+static int
+bcm2835_cpufreq_probe(device_t dev)
+{
+
+ if (device_get_unit(dev) != 0)
+ return (ENXIO);
+ device_set_desc(dev, "CPU Frequency Control");
+
+ return (0);
+}
+
+static int
+bcm2835_cpufreq_attach(device_t dev)
+{
+ struct bcm2835_cpufreq_softc *sc;
+ struct sysctl_oid *oid;
+
+ /* set self dev */
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ sc->firmware = devclass_get_device(
+ devclass_find("bcm2835_firmware"), 0);
+ if (sc->firmware == NULL) {
+ device_printf(dev, "Unable to find firmware device\n");
+ return (ENXIO);
+ }
+
+ /* initial values */
+ sc->arm_max_freq = -1;
+ sc->arm_min_freq = -1;
+ sc->core_max_freq = -1;
+ sc->core_min_freq = -1;
+ sc->sdram_max_freq = -1;
+ sc->sdram_min_freq = -1;
+ sc->max_voltage_core = 0;
+ sc->min_voltage_core = 0;
+
+ /* setup sysctl at first device */
+ if (device_get_unit(dev) == 0) {
+ sysctl_ctx_init(&bcm2835_sysctl_ctx);
+ /* create node for hw.cpufreq */
+ oid = SYSCTL_ADD_NODE(&bcm2835_sysctl_ctx,
+ SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, "cpufreq",
+ CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "");
+
+ /* Frequency (Hz) */
+ SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
+ OID_AUTO, "arm_freq",
+ CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0,
+ sysctl_bcm2835_cpufreq_arm_freq, "IU",
+ "ARM frequency (Hz)");
+ SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
+ OID_AUTO, "core_freq",
+ CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0,
+ sysctl_bcm2835_cpufreq_core_freq, "IU",
+ "Core frequency (Hz)");
+ SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
+ OID_AUTO, "sdram_freq",
+ CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0,
+ sysctl_bcm2835_cpufreq_sdram_freq, "IU",
+ "SDRAM frequency (Hz)");
+
+ /* Turbo state */
+ SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
+ OID_AUTO, "turbo",
+ CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0,
+ sysctl_bcm2835_cpufreq_turbo, "IU",
+ "Disables dynamic clocking");
+
+ /* Voltage (offset from 1.2V in units of 0.025V) */
+ SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
+ OID_AUTO, "voltage_core",
+ CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0,
+ sysctl_bcm2835_cpufreq_voltage_core, "I",
+ "ARM/GPU core voltage"
+ "(offset from 1.2V in units of 0.025V)");
+ SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
+ OID_AUTO, "voltage_sdram",
+ CTLTYPE_INT | CTLFLAG_WR | CTLFLAG_NEEDGIANT, sc,
+ 0, sysctl_bcm2835_cpufreq_voltage_sdram, "I",
+ "SDRAM voltage (offset from 1.2V in units of 0.025V)");
+
+ /* Voltage individual SDRAM */
+ SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
+ OID_AUTO, "voltage_sdram_c",
+ CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc,
+ 0, sysctl_bcm2835_cpufreq_voltage_sdram_c, "I",
+ "SDRAM controller voltage"
+ "(offset from 1.2V in units of 0.025V)");
+ SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
+ OID_AUTO, "voltage_sdram_i",
+ CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc,
+ 0, sysctl_bcm2835_cpufreq_voltage_sdram_i, "I",
+ "SDRAM I/O voltage (offset from 1.2V in units of 0.025V)");
+ SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
+ OID_AUTO, "voltage_sdram_p",
+ CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc,
+ 0, sysctl_bcm2835_cpufreq_voltage_sdram_p, "I",
+ "SDRAM phy voltage (offset from 1.2V in units of 0.025V)");
+
+ /* Temperature */
+ SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
+ OID_AUTO, "temperature",
+ CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, 0,
+ sysctl_bcm2835_cpufreq_temperature, "I",
+ "SoC temperature (thousandths of a degree C)");
+ }
+
+ /* ARM->VC lock */
+ sema_init(&vc_sema, 1, "vcsema");
+
+ /* register callback for using mbox when interrupts are enabled */
+ sc->init_hook.ich_func = bcm2835_cpufreq_init;
+ sc->init_hook.ich_arg = sc;
+
+ if (config_intrhook_establish(&sc->init_hook) != 0) {
+ device_printf(dev, "config_intrhook_establish failed\n");
+ return (ENOMEM);
+ }
+
+ /* this device is controlled by cpufreq(4) */
+ cpufreq_register(dev);
+
+ return (0);
+}
+
+static int
+bcm2835_cpufreq_detach(device_t dev)
+{
+
+ sema_destroy(&vc_sema);
+
+ return (cpufreq_unregister(dev));
+}
+
+static int
+bcm2835_cpufreq_set(device_t dev, const struct cf_setting *cf)
+{
+ struct bcm2835_cpufreq_softc *sc;
+ uint32_t rate_hz, rem;
+ int resp_freq, arm_freq, min_freq, core_freq;
+#ifdef DEBUG
+ int cur_freq;
+#endif
+
+ if (cf == NULL || cf->freq < 0)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+
+ /* setting clock (Hz) */
+ rate_hz = (uint32_t)MHZ2HZ(cf->freq);
+ rem = rate_hz % HZSTEP;
+ rate_hz -= rem;
+ if (rate_hz == 0)
+ return (EINVAL);
+
+ /* adjust min freq */
+ min_freq = sc->arm_min_freq;
+ if (sc->turbo_mode != BCM2835_FIRMWARE_TURBO_ON)
+ if (min_freq > cpufreq_lowest_freq)
+ min_freq = cpufreq_lowest_freq;
+
+ if (rate_hz < MHZ2HZ(min_freq) || rate_hz > MHZ2HZ(sc->arm_max_freq))
+ return (EINVAL);
+
+ /* set new value and verify it */
+ VC_LOCK(sc);
+#ifdef DEBUG
+ cur_freq = bcm2835_cpufreq_get_clock_rate(sc,
+ BCM2835_FIRMWARE_CLOCK_ID_ARM);
+#endif
+ resp_freq = bcm2835_cpufreq_set_clock_rate(sc,
+ BCM2835_FIRMWARE_CLOCK_ID_ARM, rate_hz);
+ DELAY(TRANSITION_LATENCY);
+ arm_freq = bcm2835_cpufreq_get_clock_rate(sc,
+ BCM2835_FIRMWARE_CLOCK_ID_ARM);
+
+ /*
+ * if non-turbo and lower than or equal min_freq,
+ * clock down core and sdram to default first.
+ */
+ if (sc->turbo_mode != BCM2835_FIRMWARE_TURBO_ON) {
+ core_freq = bcm2835_cpufreq_get_clock_rate(sc,
+ BCM2835_FIRMWARE_CLOCK_ID_CORE);
+ if (rate_hz > MHZ2HZ(sc->arm_min_freq)) {
+ bcm2835_cpufreq_set_clock_rate(sc,
+ BCM2835_FIRMWARE_CLOCK_ID_CORE,
+ MHZ2HZ(sc->core_max_freq));
+ DELAY(TRANSITION_LATENCY);
+ bcm2835_cpufreq_set_clock_rate(sc,
+ BCM2835_FIRMWARE_CLOCK_ID_SDRAM,
+ MHZ2HZ(sc->sdram_max_freq));
+ DELAY(TRANSITION_LATENCY);
+ } else {
+ if (sc->core_min_freq < DEFAULT_CORE_FREQUENCY &&
+ core_freq > DEFAULT_CORE_FREQUENCY) {
+ /* first, down to 250, then down to min */
+ DELAY(TRANSITION_LATENCY);
+ bcm2835_cpufreq_set_clock_rate(sc,
+ BCM2835_FIRMWARE_CLOCK_ID_CORE,
+ MHZ2HZ(DEFAULT_CORE_FREQUENCY));
+ DELAY(TRANSITION_LATENCY);
+ /* reset core voltage */
+ bcm2835_cpufreq_set_voltage(sc,
+ BCM2835_FIRMWARE_VOLTAGE_ID_CORE, 0);
+ DELAY(TRANSITION_LATENCY);
+ }
+ bcm2835_cpufreq_set_clock_rate(sc,
+ BCM2835_FIRMWARE_CLOCK_ID_CORE,
+ MHZ2HZ(sc->core_min_freq));
+ DELAY(TRANSITION_LATENCY);
+ bcm2835_cpufreq_set_clock_rate(sc,
+ BCM2835_FIRMWARE_CLOCK_ID_SDRAM,
+ MHZ2HZ(sc->sdram_min_freq));
+ DELAY(TRANSITION_LATENCY);
+ }
+ }
+
+ VC_UNLOCK(sc);
+
+ if (resp_freq < 0 || arm_freq < 0 || resp_freq != arm_freq) {
+ device_printf(dev, "wrong freq\n");
+ return (EIO);
+ }
+ DPRINTF("cpufreq: %d -> %d\n", cur_freq, arm_freq);
+
+ return (0);
+}
+
+static int
+bcm2835_cpufreq_get(device_t dev, struct cf_setting *cf)
+{
+ struct bcm2835_cpufreq_softc *sc;
+ int arm_freq;
+
+ if (cf == NULL)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+ memset(cf, CPUFREQ_VAL_UNKNOWN, sizeof(*cf));
+ cf->dev = NULL;
+
+ /* get cuurent value */
+ VC_LOCK(sc);
+ arm_freq = bcm2835_cpufreq_get_clock_rate(sc,
+ BCM2835_FIRMWARE_CLOCK_ID_ARM);
+ VC_UNLOCK(sc);
+ if (arm_freq < 0) {
+ device_printf(dev, "can't get clock\n");
+ return (EINVAL);
+ }
+
+ /* CPU clock in MHz or 100ths of a percent. */
+ cf->freq = HZ2MHZ(arm_freq);
+ /* Voltage in mV. */
+ cf->volts = CPUFREQ_VAL_UNKNOWN;
+ /* Power consumed in mW. */
+ cf->power = CPUFREQ_VAL_UNKNOWN;
+ /* Transition latency in us. */
+ cf->lat = TRANSITION_LATENCY;
+ /* Driver providing this setting. */
+ cf->dev = dev;
+
+ return (0);
+}
+
+static int
+bcm2835_cpufreq_make_freq_list(device_t dev, struct cf_setting *sets,
+ int *count)
+{
+ struct bcm2835_cpufreq_softc *sc;
+ int freq, min_freq, volts, rem;
+ int idx;
+
+ sc = device_get_softc(dev);
+ freq = sc->arm_max_freq;
+ min_freq = sc->arm_min_freq;
+
+ /* adjust head freq to STEP */
+ rem = freq % MHZSTEP;
+ freq -= rem;
+ if (freq < min_freq)
+ freq = min_freq;
+
+ /* if non-turbo, add extra low freq */
+ if (sc->turbo_mode != BCM2835_FIRMWARE_TURBO_ON)
+ if (min_freq > cpufreq_lowest_freq)
+ min_freq = cpufreq_lowest_freq;
+
+#ifdef SOC_BCM2835
+ /* from freq to min_freq */
+ for (idx = 0; idx < *count && freq >= min_freq; idx++) {
+ if (freq > sc->arm_min_freq)
+ volts = sc->max_voltage_core;
+ else
+ volts = sc->min_voltage_core;
+ sets[idx].freq = freq;
+ sets[idx].volts = volts;
+ sets[idx].lat = TRANSITION_LATENCY;
+ sets[idx].dev = dev;
+ freq -= MHZSTEP;
+ }
+#else
+ /* XXX RPi2 have only 900/600MHz */
+ idx = 0;
+ volts = sc->min_voltage_core;
+ sets[idx].freq = freq;
+ sets[idx].volts = volts;
+ sets[idx].lat = TRANSITION_LATENCY;
+ sets[idx].dev = dev;
+ idx++;
+ if (freq != min_freq) {
+ sets[idx].freq = min_freq;
+ sets[idx].volts = volts;
+ sets[idx].lat = TRANSITION_LATENCY;
+ sets[idx].dev = dev;
+ idx++;
+ }
+#endif
+ *count = idx;
+
+ return (0);
+}
+
+static int
+bcm2835_cpufreq_settings(device_t dev, struct cf_setting *sets, int *count)
+{
+ struct bcm2835_cpufreq_softc *sc;
+
+ if (sets == NULL || count == NULL)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+ if (sc->arm_min_freq < 0 || sc->arm_max_freq < 0) {
+ printf("device is not configured\n");
+ return (EINVAL);
+ }
+
+ /* fill data with unknown value */
+ memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * (*count));
+ /* create new array up to count */
+ bcm2835_cpufreq_make_freq_list(dev, sets, count);
+
+ return (0);
+}
+
+static int
+bcm2835_cpufreq_type(device_t dev, int *type)
+{
+
+ if (type == NULL)
+ return (EINVAL);
+ *type = CPUFREQ_TYPE_ABSOLUTE;
+
+ return (0);
+}
+
+static device_method_t bcm2835_cpufreq_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_identify, bcm2835_cpufreq_identify),
+ DEVMETHOD(device_probe, bcm2835_cpufreq_probe),
+ DEVMETHOD(device_attach, bcm2835_cpufreq_attach),
+ DEVMETHOD(device_detach, bcm2835_cpufreq_detach),
+
+ /* cpufreq interface */
+ DEVMETHOD(cpufreq_drv_set, bcm2835_cpufreq_set),
+ DEVMETHOD(cpufreq_drv_get, bcm2835_cpufreq_get),
+ DEVMETHOD(cpufreq_drv_settings, bcm2835_cpufreq_settings),
+ DEVMETHOD(cpufreq_drv_type, bcm2835_cpufreq_type),
+
+ DEVMETHOD_END
+};
+
+static devclass_t bcm2835_cpufreq_devclass;
+static driver_t bcm2835_cpufreq_driver = {
+ "bcm2835_cpufreq",
+ bcm2835_cpufreq_methods,
+ sizeof(struct bcm2835_cpufreq_softc),
+};
+
+DRIVER_MODULE(bcm2835_cpufreq, cpu, bcm2835_cpufreq_driver,
+ bcm2835_cpufreq_devclass, 0, 0);
+MODULE_DEPEND(bcm2835_cpufreq, bcm2835_firmware, 1, 1, 1);
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_dma.c b/sys/arm/broadcom/bcm2835/bcm2835_dma.c
new file mode 100644
index 000000000000..cab8639bb607
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2835_dma.c
@@ -0,0 +1,770 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Daisuke Aoyama <aoyama@peach.ne.jp>
+ * Copyright (c) 2013 Oleksandr Tymoshenko <gonzo@bluezbox.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/queue.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <machine/bus.h>
+
+#include "bcm2835_dma.h"
+#include "bcm2835_vcbus.h"
+
+#define MAX_REG 9
+
+/* private flags */
+#define BCM_DMA_CH_USED 0x00000001
+#define BCM_DMA_CH_FREE 0x40000000
+#define BCM_DMA_CH_UNMAP 0x80000000
+
+/* Register Map (4.2.1.2) */
+#define BCM_DMA_CS(n) (0x100*(n) + 0x00)
+#define CS_ACTIVE (1 << 0)
+#define CS_END (1 << 1)
+#define CS_INT (1 << 2)
+#define CS_DREQ (1 << 3)
+#define CS_ISPAUSED (1 << 4)
+#define CS_ISHELD (1 << 5)
+#define CS_ISWAIT (1 << 6)
+#define CS_ERR (1 << 8)
+#define CS_WAITWRT (1 << 28)
+#define CS_DISDBG (1 << 29)
+#define CS_ABORT (1 << 30)
+#define CS_RESET (1U << 31)
+#define BCM_DMA_CBADDR(n) (0x100*(n) + 0x04)
+#define BCM_DMA_INFO(n) (0x100*(n) + 0x08)
+#define INFO_INT_EN (1 << 0)
+#define INFO_TDMODE (1 << 1)
+#define INFO_WAIT_RESP (1 << 3)
+#define INFO_D_INC (1 << 4)
+#define INFO_D_WIDTH (1 << 5)
+#define INFO_D_DREQ (1 << 6)
+#define INFO_S_INC (1 << 8)
+#define INFO_S_WIDTH (1 << 9)
+#define INFO_S_DREQ (1 << 10)
+#define INFO_WAITS_SHIFT (21)
+#define INFO_PERMAP_SHIFT (16)
+#define INFO_PERMAP_MASK (0x1f << INFO_PERMAP_SHIFT)
+
+#define BCM_DMA_SRC(n) (0x100*(n) + 0x0C)
+#define BCM_DMA_DST(n) (0x100*(n) + 0x10)
+#define BCM_DMA_LEN(n) (0x100*(n) + 0x14)
+#define BCM_DMA_STRIDE(n) (0x100*(n) + 0x18)
+#define BCM_DMA_CBNEXT(n) (0x100*(n) + 0x1C)
+#define BCM_DMA_DEBUG(n) (0x100*(n) + 0x20)
+#define DEBUG_ERROR_MASK (7)
+
+#define BCM_DMA_INT_STATUS 0xfe0
+#define BCM_DMA_ENABLE 0xff0
+
+/* relative offset from BCM_VC_DMA0_BASE (p.39) */
+#define BCM_DMA_CH(n) (0x100*(n))
+
+/* channels used by GPU */
+#define BCM_DMA_CH_BULK 0
+#define BCM_DMA_CH_FAST1 2
+#define BCM_DMA_CH_FAST2 3
+
+#define BCM_DMA_CH_GPU_MASK ((1 << BCM_DMA_CH_BULK) | \
+ (1 << BCM_DMA_CH_FAST1) | \
+ (1 << BCM_DMA_CH_FAST2))
+
+/* DMA Control Block - 256bit aligned (p.40) */
+struct bcm_dma_cb {
+ uint32_t info; /* Transfer Information */
+ uint32_t src; /* Source Address */
+ uint32_t dst; /* Destination Address */
+ uint32_t len; /* Transfer Length */
+ uint32_t stride; /* 2D Mode Stride */
+ uint32_t next; /* Next Control Block Address */
+ uint32_t rsvd1; /* Reserved */
+ uint32_t rsvd2; /* Reserved */
+};
+
+#ifdef DEBUG
+static void bcm_dma_cb_dump(struct bcm_dma_cb *cb);
+static void bcm_dma_reg_dump(int ch);
+#endif
+
+/* DMA channel private info */
+struct bcm_dma_ch {
+ int ch;
+ uint32_t flags;
+ struct bcm_dma_cb * cb;
+ uint32_t vc_cb;
+ bus_dmamap_t dma_map;
+ void (*intr_func)(int, void *);
+ void * intr_arg;
+};
+
+struct bcm_dma_softc {
+ device_t sc_dev;
+ struct mtx sc_mtx;
+ struct resource * sc_mem;
+ struct resource * sc_irq[BCM_DMA_CH_MAX];
+ void * sc_intrhand[BCM_DMA_CH_MAX];
+ struct bcm_dma_ch sc_dma_ch[BCM_DMA_CH_MAX];
+ bus_dma_tag_t sc_dma_tag;
+};
+
+static struct bcm_dma_softc *bcm_dma_sc = NULL;
+static uint32_t bcm_dma_channel_mask;
+
+static struct ofw_compat_data compat_data[] = {
+ {"broadcom,bcm2835-dma", 1},
+ {"brcm,bcm2835-dma", 1},
+ {NULL, 0}
+};
+
+static void
+bcm_dmamap_cb(void *arg, bus_dma_segment_t *segs,
+ int nseg, int err)
+{
+ bus_addr_t *addr;
+
+ if (err)
+ return;
+
+ addr = (bus_addr_t*)arg;
+ *addr = ARMC_TO_VCBUS(segs[0].ds_addr);
+}
+
+static void
+bcm_dma_reset(device_t dev, int ch)
+{
+ struct bcm_dma_softc *sc = device_get_softc(dev);
+ struct bcm_dma_cb *cb;
+ uint32_t cs;
+ int count;
+
+ if (ch < 0 || ch >= BCM_DMA_CH_MAX)
+ return;
+
+ cs = bus_read_4(sc->sc_mem, BCM_DMA_CS(ch));
+
+ if (cs & CS_ACTIVE) {
+ /* pause current task */
+ bus_write_4(sc->sc_mem, BCM_DMA_CS(ch), 0);
+
+ count = 1000;
+ do {
+ cs = bus_read_4(sc->sc_mem, BCM_DMA_CS(ch));
+ } while (!(cs & CS_ISPAUSED) && (count-- > 0));
+
+ if (!(cs & CS_ISPAUSED)) {
+ device_printf(dev,
+ "Can't abort DMA transfer at channel %d\n", ch);
+ }
+
+ bus_write_4(sc->sc_mem, BCM_DMA_CBNEXT(ch), 0);
+
+ /* Complete everything, clear interrupt */
+ bus_write_4(sc->sc_mem, BCM_DMA_CS(ch),
+ CS_ABORT | CS_INT | CS_END| CS_ACTIVE);
+ }
+
+ /* clear control blocks */
+ bus_write_4(sc->sc_mem, BCM_DMA_CBADDR(ch), 0);
+ bus_write_4(sc->sc_mem, BCM_DMA_CBNEXT(ch), 0);
+
+ /* Reset control block */
+ cb = sc->sc_dma_ch[ch].cb;
+ bzero(cb, sizeof(*cb));
+ cb->info = INFO_WAIT_RESP;
+}
+
+static int
+bcm_dma_init(device_t dev)
+{
+ struct bcm_dma_softc *sc = device_get_softc(dev);
+ uint32_t reg;
+ struct bcm_dma_ch *ch;
+ void *cb_virt;
+ vm_paddr_t cb_phys;
+ int err;
+ int i;
+
+ /*
+ * Only channels set in bcm_dma_channel_mask can be controlled by us.
+ * The others are out of our control as well as the corresponding bits
+ * in both BCM_DMA_ENABLE and BCM_DMA_INT_STATUS global registers. As
+ * these registers are RW ones, there is no safe way how to write only
+ * the bits which can be controlled by us.
+ *
+ * Fortunately, after reset, all channels are enabled in BCM_DMA_ENABLE
+ * register and all statuses are cleared in BCM_DMA_INT_STATUS one.
+ * Not touching these registers is a trade off between correct
+ * initialization which does not count on anything and not messing up
+ * something we have no control over.
+ */
+ reg = bus_read_4(sc->sc_mem, BCM_DMA_ENABLE);
+ if ((reg & bcm_dma_channel_mask) != bcm_dma_channel_mask)
+ device_printf(dev, "channels are not enabled\n");
+ reg = bus_read_4(sc->sc_mem, BCM_DMA_INT_STATUS);
+ if ((reg & bcm_dma_channel_mask) != 0)
+ device_printf(dev, "statuses are not cleared\n");
+
+ /*
+ * Allocate DMA chunks control blocks based on p.40 of the peripheral
+ * spec - control block should be 32-bit aligned. The DMA controller
+ * has a full 32-bit register dedicated to this address, so we do not
+ * need to bother with the per-SoC peripheral restrictions.
+ */
+ err = bus_dma_tag_create(bus_get_dma_tag(dev),
+ 1, 0, BUS_SPACE_MAXADDR_32BIT,
+ BUS_SPACE_MAXADDR, NULL, NULL,
+ sizeof(struct bcm_dma_cb), 1,
+ sizeof(struct bcm_dma_cb),
+ BUS_DMA_ALLOCNOW, NULL, NULL,
+ &sc->sc_dma_tag);
+
+ if (err) {
+ device_printf(dev, "failed allocate DMA tag\n");
+ return (err);
+ }
+
+ /* setup initial settings */
+ for (i = 0; i < BCM_DMA_CH_MAX; i++) {
+ ch = &sc->sc_dma_ch[i];
+
+ bzero(ch, sizeof(struct bcm_dma_ch));
+ ch->ch = i;
+ ch->flags = BCM_DMA_CH_UNMAP;
+
+ if ((bcm_dma_channel_mask & (1 << i)) == 0)
+ continue;
+
+ err = bus_dmamem_alloc(sc->sc_dma_tag, &cb_virt,
+ BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO,
+ &ch->dma_map);
+ if (err) {
+ device_printf(dev, "cannot allocate DMA memory\n");
+ break;
+ }
+
+ /*
+ * Least alignment for busdma-allocated stuff is cache
+ * line size, so just make sure nothing stupid happened
+ * and we got properly aligned address
+ */
+ if ((uintptr_t)cb_virt & 0x1f) {
+ device_printf(dev,
+ "DMA address is not 32-bytes aligned: %p\n",
+ (void*)cb_virt);
+ break;
+ }
+
+ err = bus_dmamap_load(sc->sc_dma_tag, ch->dma_map, cb_virt,
+ sizeof(struct bcm_dma_cb), bcm_dmamap_cb, &cb_phys,
+ BUS_DMA_WAITOK);
+ if (err) {
+ device_printf(dev, "cannot load DMA memory\n");
+ break;
+ }
+
+ ch->cb = cb_virt;
+ ch->vc_cb = cb_phys;
+ ch->flags = BCM_DMA_CH_FREE;
+ ch->cb->info = INFO_WAIT_RESP;
+
+ /* reset DMA engine */
+ bus_write_4(sc->sc_mem, BCM_DMA_CS(i), CS_RESET);
+ }
+
+ return (0);
+}
+
+/*
+ * Allocate DMA channel for further use, returns channel # or
+ * BCM_DMA_CH_INVALID
+ */
+int
+bcm_dma_allocate(int req_ch)
+{
+ struct bcm_dma_softc *sc = bcm_dma_sc;
+ int ch = BCM_DMA_CH_INVALID;
+ int i;
+
+ if (req_ch >= BCM_DMA_CH_MAX)
+ return (BCM_DMA_CH_INVALID);
+
+ /* Auto(req_ch < 0) or CH specified */
+ mtx_lock(&sc->sc_mtx);
+
+ if (req_ch < 0) {
+ for (i = 0; i < BCM_DMA_CH_MAX; i++) {
+ if (sc->sc_dma_ch[i].flags & BCM_DMA_CH_FREE) {
+ ch = i;
+ sc->sc_dma_ch[ch].flags &= ~BCM_DMA_CH_FREE;
+ sc->sc_dma_ch[ch].flags |= BCM_DMA_CH_USED;
+ break;
+ }
+ }
+ }
+ else {
+ if (sc->sc_dma_ch[req_ch].flags & BCM_DMA_CH_FREE) {
+ ch = req_ch;
+ sc->sc_dma_ch[ch].flags &= ~BCM_DMA_CH_FREE;
+ sc->sc_dma_ch[ch].flags |= BCM_DMA_CH_USED;
+ }
+ }
+
+ mtx_unlock(&sc->sc_mtx);
+ return (ch);
+}
+
+/*
+ * Frees allocated channel. Returns 0 on success, -1 otherwise
+ */
+int
+bcm_dma_free(int ch)
+{
+ struct bcm_dma_softc *sc = bcm_dma_sc;
+
+ if (ch < 0 || ch >= BCM_DMA_CH_MAX)
+ return (-1);
+
+ mtx_lock(&sc->sc_mtx);
+ if (sc->sc_dma_ch[ch].flags & BCM_DMA_CH_USED) {
+ sc->sc_dma_ch[ch].flags |= BCM_DMA_CH_FREE;
+ sc->sc_dma_ch[ch].flags &= ~BCM_DMA_CH_USED;
+ sc->sc_dma_ch[ch].intr_func = NULL;
+ sc->sc_dma_ch[ch].intr_arg = NULL;
+
+ /* reset DMA engine */
+ bcm_dma_reset(sc->sc_dev, ch);
+ }
+
+ mtx_unlock(&sc->sc_mtx);
+ return (0);
+}
+
+/*
+ * Assign handler function for channel interrupt
+ * Returns 0 on success, -1 otherwise
+ */
+int
+bcm_dma_setup_intr(int ch, void (*func)(int, void *), void *arg)
+{
+ struct bcm_dma_softc *sc = bcm_dma_sc;
+ struct bcm_dma_cb *cb;
+
+ if (ch < 0 || ch >= BCM_DMA_CH_MAX)
+ return (-1);
+
+ if (!(sc->sc_dma_ch[ch].flags & BCM_DMA_CH_USED))
+ return (-1);
+
+ sc->sc_dma_ch[ch].intr_func = func;
+ sc->sc_dma_ch[ch].intr_arg = arg;
+ cb = sc->sc_dma_ch[ch].cb;
+ cb->info |= INFO_INT_EN;
+
+ return (0);
+}
+
+/*
+ * Setup DMA source parameters
+ * ch - channel number
+ * dreq - hardware DREQ # or BCM_DMA_DREQ_NONE if
+ * source is physical memory
+ * inc_addr - BCM_DMA_INC_ADDR if source address
+ * should be increased after each access or
+ * BCM_DMA_SAME_ADDR if address should remain
+ * the same
+ * width - size of read operation, BCM_DMA_32BIT
+ * for 32bit bursts, BCM_DMA_128BIT for 128 bits
+ *
+ * Returns 0 on success, -1 otherwise
+ */
+int
+bcm_dma_setup_src(int ch, int dreq, int inc_addr, int width)
+{
+ struct bcm_dma_softc *sc = bcm_dma_sc;
+ uint32_t info;
+
+ if (ch < 0 || ch >= BCM_DMA_CH_MAX)
+ return (-1);
+
+ if (!(sc->sc_dma_ch[ch].flags & BCM_DMA_CH_USED))
+ return (-1);
+
+ info = sc->sc_dma_ch[ch].cb->info;
+ info &= ~INFO_PERMAP_MASK;
+ info |= (dreq << INFO_PERMAP_SHIFT) & INFO_PERMAP_MASK;
+
+ if (dreq)
+ info |= INFO_S_DREQ;
+ else
+ info &= ~INFO_S_DREQ;
+
+ if (width == BCM_DMA_128BIT)
+ info |= INFO_S_WIDTH;
+ else
+ info &= ~INFO_S_WIDTH;
+
+ if (inc_addr == BCM_DMA_INC_ADDR)
+ info |= INFO_S_INC;
+ else
+ info &= ~INFO_S_INC;
+
+ sc->sc_dma_ch[ch].cb->info = info;
+
+ return (0);
+}
+
+/*
+ * Setup DMA destination parameters
+ * ch - channel number
+ * dreq - hardware DREQ # or BCM_DMA_DREQ_NONE if
+ * destination is physical memory
+ * inc_addr - BCM_DMA_INC_ADDR if source address
+ * should be increased after each access or
+ * BCM_DMA_SAME_ADDR if address should remain
+ * the same
+ * width - size of write operation, BCM_DMA_32BIT
+ * for 32bit bursts, BCM_DMA_128BIT for 128 bits
+ *
+ * Returns 0 on success, -1 otherwise
+ */
+int
+bcm_dma_setup_dst(int ch, int dreq, int inc_addr, int width)
+{
+ struct bcm_dma_softc *sc = bcm_dma_sc;
+ uint32_t info;
+
+ if (ch < 0 || ch >= BCM_DMA_CH_MAX)
+ return (-1);
+
+ if (!(sc->sc_dma_ch[ch].flags & BCM_DMA_CH_USED))
+ return (-1);
+
+ info = sc->sc_dma_ch[ch].cb->info;
+ info &= ~INFO_PERMAP_MASK;
+ info |= (dreq << INFO_PERMAP_SHIFT) & INFO_PERMAP_MASK;
+
+ if (dreq)
+ info |= INFO_D_DREQ;
+ else
+ info &= ~INFO_D_DREQ;
+
+ if (width == BCM_DMA_128BIT)
+ info |= INFO_D_WIDTH;
+ else
+ info &= ~INFO_D_WIDTH;
+
+ if (inc_addr == BCM_DMA_INC_ADDR)
+ info |= INFO_D_INC;
+ else
+ info &= ~INFO_D_INC;
+
+ sc->sc_dma_ch[ch].cb->info = info;
+
+ return (0);
+}
+
+#ifdef DEBUG
+void
+bcm_dma_cb_dump(struct bcm_dma_cb *cb)
+{
+
+ printf("DMA CB ");
+ printf("INFO: %8.8x ", cb->info);
+ printf("SRC: %8.8x ", cb->src);
+ printf("DST: %8.8x ", cb->dst);
+ printf("LEN: %8.8x ", cb->len);
+ printf("\n");
+ printf("STRIDE: %8.8x ", cb->stride);
+ printf("NEXT: %8.8x ", cb->next);
+ printf("RSVD1: %8.8x ", cb->rsvd1);
+ printf("RSVD2: %8.8x ", cb->rsvd2);
+ printf("\n");
+}
+
+void
+bcm_dma_reg_dump(int ch)
+{
+ struct bcm_dma_softc *sc = bcm_dma_sc;
+ int i;
+ uint32_t reg;
+
+ if (ch < 0 || ch >= BCM_DMA_CH_MAX)
+ return;
+
+ printf("DMA%d: ", ch);
+ for (i = 0; i < MAX_REG; i++) {
+ reg = bus_read_4(sc->sc_mem, BCM_DMA_CH(ch) + i*4);
+ printf("%8.8x ", reg);
+ }
+ printf("\n");
+}
+#endif
+
+/*
+ * Start DMA transaction
+ * ch - channel number
+ * src, dst - source and destination address in
+ * ARM physical memory address space.
+ * len - amount of bytes to be transferred
+ *
+ * Returns 0 on success, -1 otherwise
+ */
+int
+bcm_dma_start(int ch, vm_paddr_t src, vm_paddr_t dst, int len)
+{
+ struct bcm_dma_softc *sc = bcm_dma_sc;
+ struct bcm_dma_cb *cb;
+
+ if (ch < 0 || ch >= BCM_DMA_CH_MAX)
+ return (-1);
+
+ if (!(sc->sc_dma_ch[ch].flags & BCM_DMA_CH_USED))
+ return (-1);
+
+ cb = sc->sc_dma_ch[ch].cb;
+ cb->src = ARMC_TO_VCBUS(src);
+ cb->dst = ARMC_TO_VCBUS(dst);
+
+ cb->len = len;
+
+ bus_dmamap_sync(sc->sc_dma_tag,
+ sc->sc_dma_ch[ch].dma_map, BUS_DMASYNC_PREWRITE);
+
+ bus_write_4(sc->sc_mem, BCM_DMA_CBADDR(ch),
+ sc->sc_dma_ch[ch].vc_cb);
+ bus_write_4(sc->sc_mem, BCM_DMA_CS(ch), CS_ACTIVE);
+
+#ifdef DEBUG
+ bcm_dma_cb_dump(sc->sc_dma_ch[ch].cb);
+ bcm_dma_reg_dump(ch);
+#endif
+
+ return (0);
+}
+
+/*
+ * Get length requested for DMA transaction
+ * ch - channel number
+ *
+ * Returns size of transaction, 0 if channel is invalid
+ */
+uint32_t
+bcm_dma_length(int ch)
+{
+ struct bcm_dma_softc *sc = bcm_dma_sc;
+ struct bcm_dma_cb *cb;
+
+ if (ch < 0 || ch >= BCM_DMA_CH_MAX)
+ return (0);
+
+ if (!(sc->sc_dma_ch[ch].flags & BCM_DMA_CH_USED))
+ return (0);
+
+ cb = sc->sc_dma_ch[ch].cb;
+
+ return (cb->len);
+}
+
+static void
+bcm_dma_intr(void *arg)
+{
+ struct bcm_dma_softc *sc = bcm_dma_sc;
+ struct bcm_dma_ch *ch = (struct bcm_dma_ch *)arg;
+ uint32_t cs, debug;
+
+ /* my interrupt? */
+ cs = bus_read_4(sc->sc_mem, BCM_DMA_CS(ch->ch));
+
+ /*
+ * Is it an active channel? Our diagnostics could be better here, but
+ * it's not necessarily an easy task to resolve a rid/resource to an
+ * actual irq number. We'd want to do this to set a flag indicating
+ * whether the irq is shared or not, so we know to complain.
+ */
+ if (!(ch->flags & BCM_DMA_CH_USED))
+ return;
+
+ /* Again, we can't complain here. The same logic applies. */
+ if (!(cs & (CS_INT | CS_ERR)))
+ return;
+
+ if (cs & CS_ERR) {
+ debug = bus_read_4(sc->sc_mem, BCM_DMA_DEBUG(ch->ch));
+ device_printf(sc->sc_dev, "DMA error %d on CH%d\n",
+ debug & DEBUG_ERROR_MASK, ch->ch);
+ bus_write_4(sc->sc_mem, BCM_DMA_DEBUG(ch->ch),
+ debug & DEBUG_ERROR_MASK);
+ bcm_dma_reset(sc->sc_dev, ch->ch);
+ }
+
+ if (cs & CS_INT) {
+ /* acknowledge interrupt */
+ bus_write_4(sc->sc_mem, BCM_DMA_CS(ch->ch),
+ CS_INT | CS_END);
+
+ /* Prepare for possible access to len field */
+ bus_dmamap_sync(sc->sc_dma_tag, ch->dma_map,
+ BUS_DMASYNC_POSTWRITE);
+
+ /* save callback function and argument */
+ if (ch->intr_func)
+ ch->intr_func(ch->ch, ch->intr_arg);
+ }
+}
+
+static int
+bcm_dma_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "BCM2835 DMA Controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+bcm_dma_attach(device_t dev)
+{
+ struct bcm_dma_softc *sc = device_get_softc(dev);
+ phandle_t node;
+ int rid, err = 0;
+ int i;
+
+ sc->sc_dev = dev;
+
+ if (bcm_dma_sc)
+ return (ENXIO);
+
+ for (i = 0; i < BCM_DMA_CH_MAX; i++) {
+ sc->sc_irq[i] = NULL;
+ sc->sc_intrhand[i] = NULL;
+ }
+
+ /* Get DMA channel mask. */
+ node = ofw_bus_get_node(sc->sc_dev);
+ if (OF_getencprop(node, "brcm,dma-channel-mask", &bcm_dma_channel_mask,
+ sizeof(bcm_dma_channel_mask)) == -1 &&
+ OF_getencprop(node, "broadcom,channels", &bcm_dma_channel_mask,
+ sizeof(bcm_dma_channel_mask)) == -1) {
+ device_printf(dev, "could not get channel mask property\n");
+ return (ENXIO);
+ }
+
+ /* Mask out channels used by GPU. */
+ bcm_dma_channel_mask &= ~BCM_DMA_CH_GPU_MASK;
+
+ /* DMA0 - DMA14 */
+ rid = 0;
+ sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
+ if (sc->sc_mem == NULL) {
+ device_printf(dev, "could not allocate memory resource\n");
+ return (ENXIO);
+ }
+
+ /* IRQ DMA0 - DMA11 XXX NOT USE DMA12(spurious?) */
+ for (rid = 0; rid < BCM_DMA_CH_MAX; rid++) {
+ if ((bcm_dma_channel_mask & (1 << rid)) == 0)
+ continue;
+
+ sc->sc_irq[rid] = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE | RF_SHAREABLE);
+ if (sc->sc_irq[rid] == NULL) {
+ device_printf(dev, "cannot allocate interrupt\n");
+ err = ENXIO;
+ goto fail;
+ }
+ if (bus_setup_intr(dev, sc->sc_irq[rid], INTR_TYPE_MISC | INTR_MPSAFE,
+ NULL, bcm_dma_intr, &sc->sc_dma_ch[rid],
+ &sc->sc_intrhand[rid])) {
+ device_printf(dev, "cannot setup interrupt handler\n");
+ err = ENXIO;
+ goto fail;
+ }
+ }
+
+ mtx_init(&sc->sc_mtx, "bcmdma", "bcmdma", MTX_DEF);
+ bcm_dma_sc = sc;
+
+ err = bcm_dma_init(dev);
+ if (err)
+ goto fail;
+
+ return (err);
+
+fail:
+ if (sc->sc_mem)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem);
+
+ for (i = 0; i < BCM_DMA_CH_MAX; i++) {
+ if (sc->sc_intrhand[i])
+ bus_teardown_intr(dev, sc->sc_irq[i], sc->sc_intrhand[i]);
+ if (sc->sc_irq[i])
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq[i]);
+ }
+
+ return (err);
+}
+
+static device_method_t bcm_dma_methods[] = {
+ DEVMETHOD(device_probe, bcm_dma_probe),
+ DEVMETHOD(device_attach, bcm_dma_attach),
+ { 0, 0 }
+};
+
+static driver_t bcm_dma_driver = {
+ "bcm_dma",
+ bcm_dma_methods,
+ sizeof(struct bcm_dma_softc),
+};
+
+static devclass_t bcm_dma_devclass;
+
+DRIVER_MODULE(bcm_dma, simplebus, bcm_dma_driver, bcm_dma_devclass, 0, 0);
+MODULE_VERSION(bcm_dma, 1);
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_dma.h b/sys/arm/broadcom/bcm2835/bcm2835_dma.h
new file mode 100644
index 000000000000..44c2cd34e804
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2835_dma.h
@@ -0,0 +1,66 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Daisuke Aoyama <aoyama@peach.ne.jp>
+ * Copyright (c) 2013 Oleksandr Tymoshenko <gonzo@bluezbox.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _BCM2835_DMA_H_
+#define _BCM2835_DMA_H_
+
+#define BCM_DMA_BLOCK_SIZE 512
+
+/* DMA0-DMA15 but DMA15 is special */
+#define BCM_DMA_CH_MAX 12
+
+/* request CH for any nubmer */
+#define BCM_DMA_CH_INVALID (-1)
+#define BCM_DMA_CH_ANY (-1)
+
+/* Peripheral DREQ Signals (4.2.1.3) */
+#define BCM_DMA_DREQ_NONE 0
+/*
+ * XXX This doesn't seem to work for the Raspberry Pi 4, but the peripheral
+ * documentation still lists it at 11.
+ */
+#define BCM_DMA_DREQ_EMMC 11
+#define BCM_DMA_DREQ_SDHOST 13
+
+#define BCM_DMA_SAME_ADDR 0
+#define BCM_DMA_INC_ADDR 1
+
+#define BCM_DMA_32BIT 0
+#define BCM_DMA_128BIT 1
+
+int bcm_dma_allocate(int req_ch);
+int bcm_dma_free(int ch);
+int bcm_dma_setup_intr(int ch, void (*func)(int, void *), void *arg);
+int bcm_dma_setup_src(int ch, int dreq, int inc_addr, int width);
+int bcm_dma_setup_dst(int ch, int dreq, int inc_addr, int width);
+int bcm_dma_start(int ch, vm_paddr_t src, vm_paddr_t dst, int len);
+uint32_t bcm_dma_length(int ch);
+
+#endif /* _BCM2835_DMA_H_ */
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_fb.c b/sys/arm/broadcom/bcm2835/bcm2835_fb.c
new file mode 100644
index 000000000000..40c4a29c3e95
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2835_fb.c
@@ -0,0 +1,851 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/consio.h>
+#include <sys/fbio.h>
+#include <sys/kdb.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <dev/fb/fbreg.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/syscons/syscons.h>
+
+#include <arm/broadcom/bcm2835/bcm2835_mbox_prop.h>
+
+#include "mbox_if.h"
+
+struct argb {
+ uint8_t a;
+ uint8_t r;
+ uint8_t g;
+ uint8_t b;
+};
+
+static struct argb bcmfb_palette[16] = {
+ {0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0xaa},
+ {0x00, 0x00, 0xaa, 0x00},
+ {0x00, 0x00, 0xaa, 0xaa},
+ {0x00, 0xaa, 0x00, 0x00},
+ {0x00, 0xaa, 0x00, 0xaa},
+ {0x00, 0xaa, 0x55, 0x00},
+ {0x00, 0xaa, 0xaa, 0xaa},
+ {0x00, 0x55, 0x55, 0x55},
+ {0x00, 0x55, 0x55, 0xff},
+ {0x00, 0x55, 0xff, 0x55},
+ {0x00, 0x55, 0xff, 0xff},
+ {0x00, 0xff, 0x55, 0x55},
+ {0x00, 0xff, 0x55, 0xff},
+ {0x00, 0xff, 0xff, 0x55},
+ {0x00, 0xff, 0xff, 0xff}
+};
+
+/* mouse pointer from dev/syscons/scgfbrndr.c */
+static u_char mouse_pointer[16] = {
+ 0x00, 0x40, 0x60, 0x70, 0x78, 0x7c, 0x7e, 0x68,
+ 0x0c, 0x0c, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00
+};
+
+#define BCMFB_FONT_HEIGHT 16
+#define BCMFB_FONT_WIDTH 8
+#define FB_WIDTH 640
+#define FB_HEIGHT 480
+#define FB_DEPTH 24
+
+struct bcmsc_softc {
+ /* Videoadpater part */
+ video_adapter_t va;
+
+ intptr_t fb_addr;
+ intptr_t fb_paddr;
+ unsigned int fb_size;
+
+ unsigned int height;
+ unsigned int width;
+ unsigned int depth;
+ unsigned int stride;
+
+ unsigned int xmargin;
+ unsigned int ymargin;
+
+ unsigned char *font;
+ int fbswap;
+ int initialized;
+};
+
+static struct bcmsc_softc bcmsc;
+
+static struct ofw_compat_data compat_data[] = {
+ {"broadcom,bcm2835-fb", 1},
+ {"brcm,bcm2708-fb", 1},
+ {NULL, 0}
+};
+
+static int bcm_fb_probe(device_t);
+static int bcm_fb_attach(device_t);
+static void bcmfb_update_margins(video_adapter_t *adp);
+static int bcmfb_configure(int);
+
+static int
+bcm_fb_probe(device_t dev)
+{
+ int error;
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "BCM2835 framebuffer device");
+ error = sc_probe_unit(device_get_unit(dev),
+ device_get_flags(dev) | SC_AUTODETECT_KBD);
+ if (error != 0)
+ return (error);
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+bcm_fb_attach(device_t dev)
+{
+ struct bcm2835_fb_config fb;
+ struct bcmsc_softc *sc;
+
+ sc = (struct bcmsc_softc *)vid_get_adapter(vid_find_adapter(
+ "bcmfb", 0));
+ if (sc != NULL)
+ device_set_softc(dev, sc);
+ else
+ sc = device_get_softc(dev);
+
+ memset(&fb, 0, sizeof(fb));
+ if (bcm2835_mbox_fb_get_w_h(&fb) != 0)
+ return (ENXIO);
+ fb.bpp = FB_DEPTH;
+ fb.vxres = fb.xres;
+ fb.vyres = fb.yres;
+ fb.xoffset = fb.yoffset = 0;
+ if (bcm2835_mbox_fb_init(&fb) != 0)
+ return (ENXIO);
+
+ sc->fb_addr = (intptr_t)pmap_mapdev(fb.base, fb.size);
+ sc->fb_paddr = fb.base;
+ sc->fb_size = fb.size;
+ sc->depth = fb.bpp;
+ sc->stride = fb.pitch;
+ sc->width = fb.xres;
+ sc->height = fb.yres;
+ bcmfb_update_margins(&sc->va);
+
+ if (sc_attach_unit(device_get_unit(dev),
+ device_get_flags(dev) | SC_AUTODETECT_KBD) != 0) {
+ device_printf(dev, "failed to attach syscons\n");
+ return (ENXIO);
+ }
+
+ device_printf(dev, "%dx%d(%dx%d@%d,%d) %dbpp\n", fb.xres, fb.yres,
+ fb.vxres, fb.vyres, fb.xoffset, fb.yoffset, fb.bpp);
+ device_printf(dev,
+ "fbswap: %d, pitch %d, base 0x%08x, screen_size %d\n",
+ sc->fbswap, fb.pitch, fb.base, fb.size);
+
+ return (0);
+}
+
+static device_method_t bcm_fb_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, bcm_fb_probe),
+ DEVMETHOD(device_attach, bcm_fb_attach),
+ { 0, 0 }
+};
+
+static devclass_t bcm_fb_devclass;
+
+static driver_t bcm_fb_driver = {
+ "fb",
+ bcm_fb_methods,
+ sizeof(struct bcmsc_softc),
+};
+
+DRIVER_MODULE(bcm2835fb, ofwbus, bcm_fb_driver, bcm_fb_devclass, 0, 0);
+DRIVER_MODULE(bcm2835fb, simplebus, bcm_fb_driver, bcm_fb_devclass, 0, 0);
+
+/*
+ * Video driver routines and glue.
+ */
+static vi_probe_t bcmfb_probe;
+static vi_init_t bcmfb_init;
+static vi_get_info_t bcmfb_get_info;
+static vi_query_mode_t bcmfb_query_mode;
+static vi_set_mode_t bcmfb_set_mode;
+static vi_save_font_t bcmfb_save_font;
+static vi_load_font_t bcmfb_load_font;
+static vi_show_font_t bcmfb_show_font;
+static vi_save_palette_t bcmfb_save_palette;
+static vi_load_palette_t bcmfb_load_palette;
+static vi_set_border_t bcmfb_set_border;
+static vi_save_state_t bcmfb_save_state;
+static vi_load_state_t bcmfb_load_state;
+static vi_set_win_org_t bcmfb_set_win_org;
+static vi_read_hw_cursor_t bcmfb_read_hw_cursor;
+static vi_set_hw_cursor_t bcmfb_set_hw_cursor;
+static vi_set_hw_cursor_shape_t bcmfb_set_hw_cursor_shape;
+static vi_blank_display_t bcmfb_blank_display;
+static vi_mmap_t bcmfb_mmap;
+static vi_ioctl_t bcmfb_ioctl;
+static vi_clear_t bcmfb_clear;
+static vi_fill_rect_t bcmfb_fill_rect;
+static vi_bitblt_t bcmfb_bitblt;
+static vi_diag_t bcmfb_diag;
+static vi_save_cursor_palette_t bcmfb_save_cursor_palette;
+static vi_load_cursor_palette_t bcmfb_load_cursor_palette;
+static vi_copy_t bcmfb_copy;
+static vi_putp_t bcmfb_putp;
+static vi_putc_t bcmfb_putc;
+static vi_puts_t bcmfb_puts;
+static vi_putm_t bcmfb_putm;
+
+static video_switch_t bcmfbvidsw = {
+ .probe = bcmfb_probe,
+ .init = bcmfb_init,
+ .get_info = bcmfb_get_info,
+ .query_mode = bcmfb_query_mode,
+ .set_mode = bcmfb_set_mode,
+ .save_font = bcmfb_save_font,
+ .load_font = bcmfb_load_font,
+ .show_font = bcmfb_show_font,
+ .save_palette = bcmfb_save_palette,
+ .load_palette = bcmfb_load_palette,
+ .set_border = bcmfb_set_border,
+ .save_state = bcmfb_save_state,
+ .load_state = bcmfb_load_state,
+ .set_win_org = bcmfb_set_win_org,
+ .read_hw_cursor = bcmfb_read_hw_cursor,
+ .set_hw_cursor = bcmfb_set_hw_cursor,
+ .set_hw_cursor_shape = bcmfb_set_hw_cursor_shape,
+ .blank_display = bcmfb_blank_display,
+ .mmap = bcmfb_mmap,
+ .ioctl = bcmfb_ioctl,
+ .clear = bcmfb_clear,
+ .fill_rect = bcmfb_fill_rect,
+ .bitblt = bcmfb_bitblt,
+ .diag = bcmfb_diag,
+ .save_cursor_palette = bcmfb_save_cursor_palette,
+ .load_cursor_palette = bcmfb_load_cursor_palette,
+ .copy = bcmfb_copy,
+ .putp = bcmfb_putp,
+ .putc = bcmfb_putc,
+ .puts = bcmfb_puts,
+ .putm = bcmfb_putm,
+};
+
+VIDEO_DRIVER(bcmfb, bcmfbvidsw, bcmfb_configure);
+
+static vr_init_t bcmrend_init;
+static vr_clear_t bcmrend_clear;
+static vr_draw_border_t bcmrend_draw_border;
+static vr_draw_t bcmrend_draw;
+static vr_set_cursor_t bcmrend_set_cursor;
+static vr_draw_cursor_t bcmrend_draw_cursor;
+static vr_blink_cursor_t bcmrend_blink_cursor;
+static vr_set_mouse_t bcmrend_set_mouse;
+static vr_draw_mouse_t bcmrend_draw_mouse;
+
+/*
+ * We use our own renderer; this is because we must emulate a hardware
+ * cursor.
+ */
+static sc_rndr_sw_t bcmrend = {
+ bcmrend_init,
+ bcmrend_clear,
+ bcmrend_draw_border,
+ bcmrend_draw,
+ bcmrend_set_cursor,
+ bcmrend_draw_cursor,
+ bcmrend_blink_cursor,
+ bcmrend_set_mouse,
+ bcmrend_draw_mouse
+};
+
+RENDERER(bcmfb, 0, bcmrend, gfb_set);
+RENDERER_MODULE(bcmfb, gfb_set);
+
+static void
+bcmrend_init(scr_stat* scp)
+{
+}
+
+static void
+bcmrend_clear(scr_stat* scp, int c, int attr)
+{
+}
+
+static void
+bcmrend_draw_border(scr_stat* scp, int color)
+{
+}
+
+static void
+bcmrend_draw(scr_stat* scp, int from, int count, int flip)
+{
+ video_adapter_t* adp = scp->sc->adp;
+ int i, c, a;
+
+ if (!flip) {
+ /* Normal printing */
+ vidd_puts(adp, from, (uint16_t*)sc_vtb_pointer(&scp->vtb, from), count);
+ } else {
+ /* This is for selections and such: invert the color attribute */
+ for (i = count; i-- > 0; ++from) {
+ c = sc_vtb_getc(&scp->vtb, from);
+ a = sc_vtb_geta(&scp->vtb, from) >> 8;
+ vidd_putc(adp, from, c, (a >> 4) | ((a & 0xf) << 4));
+ }
+ }
+}
+
+static void
+bcmrend_set_cursor(scr_stat* scp, int base, int height, int blink)
+{
+}
+
+static void
+bcmrend_draw_cursor(scr_stat* scp, int off, int blink, int on, int flip)
+{
+ int bytes, col, i, j, row;
+ struct bcmsc_softc *sc;
+ uint8_t *addr;
+ video_adapter_t *adp;
+
+ adp = scp->sc->adp;
+ sc = (struct bcmsc_softc *)adp;
+
+ if (scp->curs_attr.height <= 0)
+ return;
+
+ if (sc->fb_addr == 0)
+ return;
+
+ if (off >= adp->va_info.vi_width * adp->va_info.vi_height)
+ return;
+
+ /* calculate the coordinates in the video buffer */
+ row = (off / adp->va_info.vi_width) * adp->va_info.vi_cheight;
+ col = (off % adp->va_info.vi_width) * adp->va_info.vi_cwidth;
+
+ addr = (uint8_t *)sc->fb_addr
+ + (row + sc->ymargin)*(sc->stride)
+ + (sc->depth/8) * (col + sc->xmargin);
+
+ bytes = sc->depth / 8;
+ /* our cursor consists of simply inverting the char under it */
+ for (i = 0; i < adp->va_info.vi_cheight; i++) {
+ for (j = 0; j < adp->va_info.vi_cwidth; j++) {
+ switch (sc->depth) {
+ case 32:
+ case 24:
+ addr[bytes*j + 2] ^= 0xff;
+ /* FALLTHROUGH */
+ case 16:
+ addr[bytes*j + 1] ^= 0xff;
+ addr[bytes*j] ^= 0xff;
+ break;
+ default:
+ break;
+ }
+ }
+
+ addr += sc->stride;
+ }
+}
+
+static void
+bcmrend_blink_cursor(scr_stat* scp, int at, int flip)
+{
+}
+
+static void
+bcmrend_set_mouse(scr_stat* scp)
+{
+}
+
+static void
+bcmrend_draw_mouse(scr_stat* scp, int x, int y, int on)
+{
+ vidd_putm(scp->sc->adp, x, y, mouse_pointer, 0xffffffff, 16, 8);
+}
+
+static uint16_t bcmfb_static_window[ROW*COL];
+extern u_char dflt_font_16[];
+
+/*
+ * Update videoadapter settings after changing resolution
+ */
+static void
+bcmfb_update_margins(video_adapter_t *adp)
+{
+ struct bcmsc_softc *sc;
+ video_info_t *vi;
+
+ sc = (struct bcmsc_softc *)adp;
+ vi = &adp->va_info;
+
+ sc->xmargin = (sc->width - (vi->vi_width * vi->vi_cwidth)) / 2;
+ sc->ymargin = (sc->height - (vi->vi_height * vi->vi_cheight)) / 2;
+}
+
+static int
+bcmfb_configure(int flags)
+{
+ char bootargs[2048], *n, *p, *v;
+ pcell_t cell;
+ phandle_t chosen, display, root;
+ struct bcmsc_softc *sc;
+
+ sc = &bcmsc;
+ if (sc->initialized)
+ return (0);
+
+ sc->width = 0;
+ sc->height = 0;
+
+ /*
+ * It seems there is no way to let syscons framework know
+ * that framebuffer resolution has changed. So just try
+ * to fetch data from FDT bootargs, FDT display data and
+ * finally go with defaults if everything else has failed.
+ */
+ chosen = OF_finddevice("/chosen");
+ if (chosen != -1 &&
+ OF_getprop(chosen, "bootargs", &bootargs, sizeof(bootargs)) > 0) {
+ p = bootargs;
+ while ((v = strsep(&p, " ")) != NULL) {
+ if (*v == '\0')
+ continue;
+ n = strsep(&v, "=");
+ if (strcmp(n, "bcm2708_fb.fbwidth") == 0 && v != NULL)
+ sc->width = (unsigned int)strtol(v, NULL, 0);
+ else if (strcmp(n, "bcm2708_fb.fbheight") == 0 &&
+ v != NULL)
+ sc->height = (unsigned int)strtol(v, NULL, 0);
+ else if (strcmp(n, "bcm2708_fb.fbswap") == 0 &&
+ v != NULL)
+ if (*v == '1')
+ sc->fbswap = 1;
+ }
+ }
+
+ root = OF_finddevice("/");
+ if ((root != -1) &&
+ (display = fdt_find_compatible(root, "broadcom,bcm2835-fb", 1))) {
+ if (sc->width == 0) {
+ if ((OF_getencprop(display, "broadcom,width",
+ &cell, sizeof(cell))) > 0)
+ sc->width = cell;
+ }
+
+ if (sc->height == 0) {
+ if ((OF_getencprop(display, "broadcom,height",
+ &cell, sizeof(cell))) > 0)
+ sc->height = cell;
+ }
+ }
+
+ if (sc->width == 0)
+ sc->width = FB_WIDTH;
+ if (sc->height == 0)
+ sc->height = FB_HEIGHT;
+
+ bcmfb_init(0, &sc->va, 0);
+ sc->initialized = 1;
+
+ return (0);
+}
+
+static int
+bcmfb_probe(int unit, video_adapter_t **adp, void *arg, int flags)
+{
+
+ return (0);
+}
+
+static int
+bcmfb_init(int unit, video_adapter_t *adp, int flags)
+{
+ struct bcmsc_softc *sc;
+ video_info_t *vi;
+
+ sc = (struct bcmsc_softc *)adp;
+ vi = &adp->va_info;
+
+ vid_init_struct(adp, "bcmfb", -1, unit);
+
+ sc->font = dflt_font_16;
+ vi->vi_cheight = BCMFB_FONT_HEIGHT;
+ vi->vi_cwidth = BCMFB_FONT_WIDTH;
+ vi->vi_width = sc->width / vi->vi_cwidth;
+ vi->vi_height = sc->height / vi->vi_cheight;
+
+ /*
+ * Clamp width/height to syscons maximums
+ */
+ if (vi->vi_width > COL)
+ vi->vi_width = COL;
+ if (vi->vi_height > ROW)
+ vi->vi_height = ROW;
+
+ sc->xmargin = (sc->width - (vi->vi_width * vi->vi_cwidth)) / 2;
+ sc->ymargin = (sc->height - (vi->vi_height * vi->vi_cheight)) / 2;
+
+ adp->va_window = (vm_offset_t) bcmfb_static_window;
+ adp->va_flags |= V_ADP_FONT /* | V_ADP_COLOR | V_ADP_MODECHANGE */;
+
+ vid_register(&sc->va);
+
+ return (0);
+}
+
+static int
+bcmfb_get_info(video_adapter_t *adp, int mode, video_info_t *info)
+{
+ bcopy(&adp->va_info, info, sizeof(*info));
+ return (0);
+}
+
+static int
+bcmfb_query_mode(video_adapter_t *adp, video_info_t *info)
+{
+ return (0);
+}
+
+static int
+bcmfb_set_mode(video_adapter_t *adp, int mode)
+{
+ return (0);
+}
+
+static int
+bcmfb_save_font(video_adapter_t *adp, int page, int size, int width,
+ u_char *data, int c, int count)
+{
+ return (0);
+}
+
+static int
+bcmfb_load_font(video_adapter_t *adp, int page, int size, int width,
+ u_char *data, int c, int count)
+{
+ struct bcmsc_softc *sc;
+
+ sc = (struct bcmsc_softc *)adp;
+ sc->font = data;
+
+ return (0);
+}
+
+static int
+bcmfb_show_font(video_adapter_t *adp, int page)
+{
+ return (0);
+}
+
+static int
+bcmfb_save_palette(video_adapter_t *adp, u_char *palette)
+{
+ return (0);
+}
+
+static int
+bcmfb_load_palette(video_adapter_t *adp, u_char *palette)
+{
+ return (0);
+}
+
+static int
+bcmfb_set_border(video_adapter_t *adp, int border)
+{
+ return (bcmfb_blank_display(adp, border));
+}
+
+static int
+bcmfb_save_state(video_adapter_t *adp, void *p, size_t size)
+{
+ return (0);
+}
+
+static int
+bcmfb_load_state(video_adapter_t *adp, void *p)
+{
+ return (0);
+}
+
+static int
+bcmfb_set_win_org(video_adapter_t *adp, off_t offset)
+{
+ return (0);
+}
+
+static int
+bcmfb_read_hw_cursor(video_adapter_t *adp, int *col, int *row)
+{
+ *col = *row = 0;
+
+ return (0);
+}
+
+static int
+bcmfb_set_hw_cursor(video_adapter_t *adp, int col, int row)
+{
+ return (0);
+}
+
+static int
+bcmfb_set_hw_cursor_shape(video_adapter_t *adp, int base, int height,
+ int celsize, int blink)
+{
+ return (0);
+}
+
+static int
+bcmfb_blank_display(video_adapter_t *adp, int mode)
+{
+
+ struct bcmsc_softc *sc;
+
+ sc = (struct bcmsc_softc *)adp;
+ if (sc && sc->fb_addr)
+ memset((void*)sc->fb_addr, 0, sc->fb_size);
+
+ return (0);
+}
+
+static int
+bcmfb_mmap(video_adapter_t *adp, vm_ooffset_t offset, vm_paddr_t *paddr,
+ int prot, vm_memattr_t *memattr)
+{
+ struct bcmsc_softc *sc;
+
+ sc = (struct bcmsc_softc *)adp;
+
+ /*
+ * This might be a legacy VGA mem request: if so, just point it at the
+ * framebuffer, since it shouldn't be touched
+ */
+ if (offset < sc->stride*sc->height) {
+ *paddr = sc->fb_paddr + offset;
+ return (0);
+ }
+
+ return (EINVAL);
+}
+
+static int
+bcmfb_ioctl(video_adapter_t *adp, u_long cmd, caddr_t data)
+{
+ struct bcmsc_softc *sc;
+ struct fbtype *fb;
+
+ sc = (struct bcmsc_softc *)adp;
+
+ switch (cmd) {
+ case FBIOGTYPE:
+ fb = (struct fbtype *)data;
+ fb->fb_type = FBTYPE_PCIMISC;
+ fb->fb_height = sc->height;
+ fb->fb_width = sc->width;
+ fb->fb_depth = sc->depth;
+ if (sc->depth <= 1 || sc->depth > 8)
+ fb->fb_cmsize = 0;
+ else
+ fb->fb_cmsize = 1 << sc->depth;
+ fb->fb_size = sc->fb_size;
+ break;
+ default:
+ return (fb_commonioctl(adp, cmd, data));
+ }
+
+ return (0);
+}
+
+static int
+bcmfb_clear(video_adapter_t *adp)
+{
+
+ return (bcmfb_blank_display(adp, 0));
+}
+
+static int
+bcmfb_fill_rect(video_adapter_t *adp, int val, int x, int y, int cx, int cy)
+{
+
+ return (0);
+}
+
+static int
+bcmfb_bitblt(video_adapter_t *adp, ...)
+{
+
+ return (0);
+}
+
+static int
+bcmfb_diag(video_adapter_t *adp, int level)
+{
+
+ return (0);
+}
+
+static int
+bcmfb_save_cursor_palette(video_adapter_t *adp, u_char *palette)
+{
+
+ return (0);
+}
+
+static int
+bcmfb_load_cursor_palette(video_adapter_t *adp, u_char *palette)
+{
+
+ return (0);
+}
+
+static int
+bcmfb_copy(video_adapter_t *adp, vm_offset_t src, vm_offset_t dst, int n)
+{
+
+ return (0);
+}
+
+static int
+bcmfb_putp(video_adapter_t *adp, vm_offset_t off, uint32_t p, uint32_t a,
+ int size, int bpp, int bit_ltor, int byte_ltor)
+{
+
+ return (0);
+}
+
+static int
+bcmfb_putc(video_adapter_t *adp, vm_offset_t off, uint8_t c, uint8_t a)
+{
+ int bytes, col, i, j, k, row;
+ struct bcmsc_softc *sc;
+ u_char *p;
+ uint8_t *addr, fg, bg, color;
+ uint16_t rgb;
+
+ sc = (struct bcmsc_softc *)adp;
+
+ if (sc->fb_addr == 0)
+ return (0);
+
+ row = (off / adp->va_info.vi_width) * adp->va_info.vi_cheight;
+ col = (off % adp->va_info.vi_width) * adp->va_info.vi_cwidth;
+ p = sc->font + c*BCMFB_FONT_HEIGHT;
+ addr = (uint8_t *)sc->fb_addr
+ + (row + sc->ymargin)*(sc->stride)
+ + (sc->depth/8) * (col + sc->xmargin);
+
+ fg = a & 0xf ;
+ bg = (a >> 4) & 0xf;
+
+ bytes = sc->depth / 8;
+ for (i = 0; i < BCMFB_FONT_HEIGHT; i++) {
+ for (j = 0, k = 7; j < 8; j++, k--) {
+ if ((p[i] & (1 << k)) == 0)
+ color = bg;
+ else
+ color = fg;
+
+ switch (sc->depth) {
+ case 32:
+ case 24:
+ if (sc->fbswap) {
+ addr[bytes * j + 0] =
+ bcmfb_palette[color].b;
+ addr[bytes * j + 1] =
+ bcmfb_palette[color].g;
+ addr[bytes * j + 2] =
+ bcmfb_palette[color].r;
+ } else {
+ addr[bytes * j + 0] =
+ bcmfb_palette[color].r;
+ addr[bytes * j + 1] =
+ bcmfb_palette[color].g;
+ addr[bytes * j + 2] =
+ bcmfb_palette[color].b;
+ }
+ if (sc->depth == 32)
+ addr[bytes * j + 3] =
+ bcmfb_palette[color].a;
+ break;
+ case 16:
+ rgb = (bcmfb_palette[color].r >> 3) << 11;
+ rgb |= (bcmfb_palette[color].g >> 2) << 5;
+ rgb |= (bcmfb_palette[color].b >> 3);
+ addr[bytes * j] = rgb & 0xff;
+ addr[bytes * j + 1] = (rgb >> 8) & 0xff;
+ default:
+ /* Not supported yet */
+ break;
+ }
+ }
+
+ addr += (sc->stride);
+ }
+
+ return (0);
+}
+
+static int
+bcmfb_puts(video_adapter_t *adp, vm_offset_t off, u_int16_t *s, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ bcmfb_putc(adp, off + i, s[i] & 0xff, (s[i] & 0xff00) >> 8);
+
+ return (0);
+}
+
+static int
+bcmfb_putm(video_adapter_t *adp, int x, int y, uint8_t *pixel_image,
+ uint32_t pixel_mask, int size, int width)
+{
+
+ return (0);
+}
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_fbd.c b/sys/arm/broadcom/bcm2835/bcm2835_fbd.c
new file mode 100644
index 000000000000..93849b3551d9
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2835_fbd.c
@@ -0,0 +1,287 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * Copyright (c) 2012, 2013 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * Portions of this software were developed by Oleksandr Rybalko
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bio.h>
+#include <sys/bus.h>
+#include <sys/fbio.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/fb/fbreg.h>
+#include <dev/vt/vt.h>
+#include <dev/vt/colors/vt_termcolors.h>
+
+#include <arm/broadcom/bcm2835/bcm2835_mbox_prop.h>
+
+#include "fb_if.h"
+#include "mbox_if.h"
+
+#define FB_DEPTH 24
+
+struct bcmsc_softc {
+ struct fb_info info;
+ int fbswap;
+ struct bcm2835_fb_config fb;
+ device_t dev;
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"broadcom,bcm2835-fb", 1},
+ {"brcm,bcm2708-fb", 1},
+ {NULL, 0}
+};
+
+static int bcm_fb_probe(device_t);
+static int bcm_fb_attach(device_t);
+
+static int
+bcm_fb_init(struct bcmsc_softc *sc, struct bcm2835_fb_config *fb)
+{
+ int err;
+
+ err = 0;
+
+ memset(fb, 0, sizeof(*fb));
+ if (bcm2835_mbox_fb_get_w_h(fb) != 0)
+ return (ENXIO);
+ if (bcm2835_mbox_fb_get_bpp(fb) != 0)
+ return (ENXIO);
+ if (fb->bpp < FB_DEPTH) {
+ device_printf(sc->dev, "changing fb bpp from %d to %d\n",
+ fb->bpp, FB_DEPTH);
+ fb->bpp = FB_DEPTH;
+ } else
+ device_printf(sc->dev, "keeping existing fb bpp of %d\n",
+ fb->bpp);
+
+ fb->vxres = fb->xres;
+ fb->vyres = fb->yres;
+ fb->xoffset = fb->yoffset = 0;
+
+ if ((err = bcm2835_mbox_fb_init(fb)) != 0) {
+ device_printf(sc->dev, "bcm2835_mbox_fb_init failed, err=%d\n",
+ err);
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static int
+bcm_fb_setup_fbd(struct bcmsc_softc *sc)
+{
+ struct bcm2835_fb_config fb;
+ device_t fbd;
+ int err;
+
+ err = bcm_fb_init(sc, &fb);
+ if (err)
+ return (err);
+
+ memset(&sc->info, 0, sizeof(sc->info));
+ sc->info.fb_name = device_get_nameunit(sc->dev);
+
+ sc->info.fb_vbase = (intptr_t)pmap_mapdev(fb.base, fb.size);
+ sc->info.fb_pbase = fb.base;
+ sc->info.fb_size = fb.size;
+ sc->info.fb_bpp = sc->info.fb_depth = fb.bpp;
+ sc->info.fb_stride = fb.pitch;
+ sc->info.fb_width = fb.xres;
+ sc->info.fb_height = fb.yres;
+#ifdef VM_MEMATTR_WRITE_COMBINING
+ sc->info.fb_flags = FB_FLAG_MEMATTR;
+ sc->info.fb_memattr = VM_MEMATTR_WRITE_COMBINING;
+#endif
+
+ if (sc->fbswap) {
+ switch (sc->info.fb_bpp) {
+ case 24:
+ vt_generate_cons_palette(sc->info.fb_cmap,
+ COLOR_FORMAT_RGB, 0xff, 0, 0xff, 8, 0xff, 16);
+ sc->info.fb_cmsize = 16;
+ break;
+ case 32:
+ vt_generate_cons_palette(sc->info.fb_cmap,
+ COLOR_FORMAT_RGB, 0xff, 16, 0xff, 8, 0xff, 0);
+ sc->info.fb_cmsize = 16;
+ break;
+ }
+ }
+
+ fbd = device_add_child(sc->dev, "fbd", device_get_unit(sc->dev));
+ if (fbd == NULL) {
+ device_printf(sc->dev, "Failed to add fbd child\n");
+ pmap_unmapdev(sc->info.fb_vbase, sc->info.fb_size);
+ return (ENXIO);
+ } else if (device_probe_and_attach(fbd) != 0) {
+ device_printf(sc->dev, "Failed to attach fbd device\n");
+ device_delete_child(sc->dev, fbd);
+ pmap_unmapdev(sc->info.fb_vbase, sc->info.fb_size);
+ return (ENXIO);
+ }
+
+ device_printf(sc->dev, "%dx%d(%dx%d@%d,%d) %dbpp\n", fb.xres, fb.yres,
+ fb.vxres, fb.vyres, fb.xoffset, fb.yoffset, fb.bpp);
+ device_printf(sc->dev,
+ "fbswap: %d, pitch %d, base 0x%08x, screen_size %d\n",
+ sc->fbswap, fb.pitch, fb.base, fb.size);
+
+ return (0);
+}
+
+static int
+bcm_fb_resync_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ struct bcmsc_softc *sc = arg1;
+ struct bcm2835_fb_config fb;
+ int val;
+ int err;
+
+ val = 0;
+ err = sysctl_handle_int(oidp, &val, 0, req);
+ if (err || !req->newptr) /* error || read request */
+ return (err);
+
+ bcm_fb_init(sc, &fb);
+
+ return (0);
+}
+
+static void
+bcm_fb_sysctl_init(struct bcmsc_softc *sc)
+{
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid *tree_node;
+ struct sysctl_oid_list *tree;
+
+ /*
+ * Add system sysctl tree/handlers.
+ */
+ ctx = device_get_sysctl_ctx(sc->dev);
+ tree_node = device_get_sysctl_tree(sc->dev);
+ tree = SYSCTL_CHILDREN(tree_node);
+ SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "resync",
+ CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_NEEDGIANT, sc, sizeof(*sc),
+ bcm_fb_resync_sysctl, "IU", "Set to resync framebuffer with VC");
+}
+
+static int
+bcm_fb_probe(device_t dev)
+{
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "BCM2835 VT framebuffer driver");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+bcm_fb_attach(device_t dev)
+{
+ char bootargs[2048], *n, *p, *v;
+ int err;
+ phandle_t chosen;
+ struct bcmsc_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ /* Newer firmware versions needs an inverted color palette. */
+ sc->fbswap = 0;
+ chosen = OF_finddevice("/chosen");
+ if (chosen != -1 &&
+ OF_getprop(chosen, "bootargs", &bootargs, sizeof(bootargs)) > 0) {
+ p = bootargs;
+ while ((v = strsep(&p, " ")) != NULL) {
+ if (*v == '\0')
+ continue;
+ n = strsep(&v, "=");
+ if (strcmp(n, "bcm2708_fb.fbswap") == 0 && v != NULL)
+ if (*v == '1')
+ sc->fbswap = 1;
+ }
+ }
+
+ bcm_fb_sysctl_init(sc);
+
+ err = bcm_fb_setup_fbd(sc);
+ if (err)
+ return (err);
+
+ return (0);
+}
+
+static struct fb_info *
+bcm_fb_helper_getinfo(device_t dev)
+{
+ struct bcmsc_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ return (&sc->info);
+}
+
+static device_method_t bcm_fb_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, bcm_fb_probe),
+ DEVMETHOD(device_attach, bcm_fb_attach),
+
+ /* Framebuffer service methods */
+ DEVMETHOD(fb_getinfo, bcm_fb_helper_getinfo),
+
+ DEVMETHOD_END
+};
+
+static devclass_t bcm_fb_devclass;
+
+static driver_t bcm_fb_driver = {
+ "fb",
+ bcm_fb_methods,
+ sizeof(struct bcmsc_softc),
+};
+
+DRIVER_MODULE(bcm2835fb, ofwbus, bcm_fb_driver, bcm_fb_devclass, 0, 0);
+DRIVER_MODULE(bcm2835fb, simplebus, bcm_fb_driver, bcm_fb_devclass, 0, 0);
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_firmware.c b/sys/arm/broadcom/bcm2835/bcm2835_firmware.c
new file mode 100644
index 000000000000..1a061e60a823
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2835_firmware.c
@@ -0,0 +1,184 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2020 Andrew Turner
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+
+#include <dev/fdt/simplebus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/broadcom/bcm2835/bcm2835_firmware.h>
+#include <arm/broadcom/bcm2835/bcm2835_mbox.h>
+#include <arm/broadcom/bcm2835/bcm2835_mbox_prop.h>
+#include <arm/broadcom/bcm2835/bcm2835_vcbus.h>
+
+struct bcm2835_firmware_softc {
+ struct simplebus_softc sc;
+ phandle_t sc_mbox;
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"raspberrypi,bcm2835-firmware", 1},
+ {NULL, 0}
+};
+
+static int sysctl_bcm2835_firmware_get_revision(SYSCTL_HANDLER_ARGS);
+
+static int
+bcm2835_firmware_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "BCM2835 Firmware");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+bcm2835_firmware_attach(device_t dev)
+{
+ struct bcm2835_firmware_softc *sc;
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid *tree_node;
+ struct sysctl_oid_list *tree;
+ phandle_t node, mbox;
+ int rv;
+
+ sc = device_get_softc(dev);
+
+ node = ofw_bus_get_node(dev);
+ rv = OF_getencprop(node, "mboxes", &mbox, sizeof(mbox));
+ if (rv <= 0) {
+ device_printf(dev, "can't read mboxes property\n");
+ return (ENXIO);
+ }
+ sc->sc_mbox = mbox;
+
+ OF_device_register_xref(OF_xref_from_node(node), dev);
+
+ ctx = device_get_sysctl_ctx(dev);
+ tree_node = device_get_sysctl_tree(dev);
+ tree = SYSCTL_CHILDREN(tree_node);
+ SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "revision",
+ CTLTYPE_UINT | CTLFLAG_RD, sc, sizeof(*sc),
+ sysctl_bcm2835_firmware_get_revision, "IU",
+ "Firmware revision");
+
+ /* The firmwaare doesn't have a ranges property */
+ sc->sc.flags |= SB_FLAG_NO_RANGES;
+ return (simplebus_attach(dev));
+}
+
+int
+bcm2835_firmware_property(device_t dev, uint32_t prop, void *data, size_t len)
+{
+ struct {
+ struct bcm2835_mbox_hdr hdr;
+ struct bcm2835_mbox_tag_hdr tag_hdr;
+ uint32_t data[];
+ } *msg_hdr;
+ size_t msg_len;
+ int err;
+
+ /*
+ * The message is processed in 32-bit chunks so must be a multiple
+ * of 32-bits.
+ */
+ if ((len & (sizeof(uint32_t) - 1)) != 0)
+ return (EINVAL);
+
+ msg_len = sizeof(*msg_hdr) + len + sizeof(uint32_t);
+ msg_hdr = malloc(sizeof(*msg_hdr) + msg_len + sizeof(uint32_t),
+ M_DEVBUF, M_WAITOK | M_ZERO);
+
+ msg_hdr->hdr.buf_size = msg_len;
+ msg_hdr->hdr.code = BCM2835_MBOX_CODE_REQ;
+ msg_hdr->tag_hdr.tag = prop;
+ msg_hdr->tag_hdr.val_buf_size = len;
+ memcpy(msg_hdr->data, data, len);
+ msg_hdr->data[len / sizeof(uint32_t)] = 0;
+
+ err = bcm2835_mbox_property(msg_hdr, msg_len);
+ if (err == 0) {
+ memcpy(data, msg_hdr->data, len);
+ }
+
+ free(msg_hdr, M_DEVBUF);
+
+ return (err);
+}
+
+static int
+sysctl_bcm2835_firmware_get_revision(SYSCTL_HANDLER_ARGS)
+{
+ struct bcm2835_firmware_softc *sc = arg1;
+ uint32_t rev;
+ int err;
+
+ if (bcm2835_firmware_property(sc->sc.dev,
+ BCM2835_MBOX_TAG_FIRMWARE_REVISION, &rev, sizeof(rev)) != 0)
+ return (ENXIO);
+
+ err = sysctl_handle_int(oidp, &rev, sizeof(rev), req);
+ if (err || !req->newptr) /* error || read request */
+ return (err);
+
+ /* write request */
+ return (EINVAL);
+}
+
+static device_method_t bcm2835_firmware_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, bcm2835_firmware_probe),
+ DEVMETHOD(device_attach, bcm2835_firmware_attach),
+
+ DEVMETHOD_END
+};
+
+static devclass_t bcm2835_firmware_devclass;
+DEFINE_CLASS_1(bcm2835_firmware, bcm2835_firmware_driver,
+ bcm2835_firmware_methods, sizeof(struct bcm2835_firmware_softc),
+ simplebus_driver);
+
+EARLY_DRIVER_MODULE(bcm2835_firmware, simplebus, bcm2835_firmware_driver,
+ bcm2835_firmware_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LAST);
+MODULE_DEPEND(bcm2835_firmware, mbox, 1, 1, 1);
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_firmware.h b/sys/arm/broadcom/bcm2835/bcm2835_firmware.h
new file mode 100644
index 000000000000..666e450914f8
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2835_firmware.h
@@ -0,0 +1,202 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2020 Andrew Turner
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _BCM2835_FIRMWARE_H_
+#define _BCM2835_FIRMWARE_H_
+
+#define BCM2835_FIRMWARE_TAG_GET_CLOCK_RATE 0x00030002
+#define BCM2835_FIRMWARE_TAG_SET_CLOCK_RATE 0x00038002
+#define BCM2835_FIRMWARE_TAG_GET_MAX_CLOCK_RATE 0x00030004
+#define BCM2835_FIRMWARE_TAG_GET_MIN_CLOCK_RATE 0x00030007
+
+#define BCM2835_FIRMWARE_CLOCK_ID_EMMC 0x00000001
+#define BCM2835_FIRMWARE_CLOCK_ID_UART 0x00000002
+#define BCM2835_FIRMWARE_CLOCK_ID_ARM 0x00000003
+#define BCM2835_FIRMWARE_CLOCK_ID_CORE 0x00000004
+#define BCM2835_FIRMWARE_CLOCK_ID_V3D 0x00000005
+#define BCM2835_FIRMWARE_CLOCK_ID_H264 0x00000006
+#define BCM2835_FIRMWARE_CLOCK_ID_ISP 0x00000007
+#define BCM2835_FIRMWARE_CLOCK_ID_SDRAM 0x00000008
+#define BCM2835_FIRMWARE_CLOCK_ID_PIXEL 0x00000009
+#define BCM2835_FIRMWARE_CLOCK_ID_PWM 0x0000000a
+#define BCM2838_FIRMWARE_CLOCK_ID_EMMC2 0x0000000c
+
+union msg_get_clock_rate_body {
+ struct {
+ uint32_t clock_id;
+ } req;
+ struct {
+ uint32_t clock_id;
+ uint32_t rate_hz;
+ } resp;
+};
+
+union msg_set_clock_rate_body {
+ struct {
+ uint32_t clock_id;
+ uint32_t rate_hz;
+ } req;
+ struct {
+ uint32_t clock_id;
+ uint32_t rate_hz;
+ } resp;
+};
+
+#define BCM2835_FIRMWARE_TAG_GET_VOLTAGE 0x00030003
+#define BCM2835_FIRMWARE_TAG_SET_VOLTAGE 0x00038003
+#define BCM2835_FIRMWARE_TAG_GET_MAX_VOLTAGE 0x00030005
+#define BCM2835_FIRMWARE_TAG_GET_MIN_VOLTAGE 0x00030008
+
+#define BCM2835_FIRMWARE_VOLTAGE_ID_CORE 0x00000001
+#define BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_C 0x00000002
+#define BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_P 0x00000003
+#define BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_I 0x00000004
+
+union msg_get_voltage_body {
+ struct {
+ uint32_t voltage_id;
+ } req;
+ struct {
+ uint32_t voltage_id;
+ uint32_t value;
+ } resp;
+};
+
+union msg_set_voltage_body {
+ struct {
+ uint32_t voltage_id;
+ uint32_t value;
+ } req;
+ struct {
+ uint32_t voltage_id;
+ uint32_t value;
+ } resp;
+};
+
+#define BCM2835_FIRMWARE_TAG_GET_TEMPERATURE 0x00030006
+#define BCM2835_FIRMWARE_TAG_GET_MAX_TEMPERATURE 0x0003000a
+
+union msg_get_temperature_body {
+ struct {
+ uint32_t temperature_id;
+ } req;
+ struct {
+ uint32_t temperature_id;
+ uint32_t value;
+ } resp;
+};
+
+#define BCM2835_FIRMWARE_TAG_GET_TURBO 0x00030009
+#define BCM2835_FIRMWARE_TAG_SET_TURBO 0x00038009
+
+#define BCM2835_FIRMWARE_TURBO_ON 1
+#define BCM2835_FIRMWARE_TURBO_OFF 0
+
+union msg_get_turbo_body {
+ struct {
+ uint32_t id;
+ } req;
+ struct {
+ uint32_t id;
+ uint32_t level;
+ } resp;
+};
+
+union msg_set_turbo_body {
+ struct {
+ uint32_t id;
+ uint32_t level;
+ } req;
+ struct {
+ uint32_t id;
+ uint32_t level;
+ } resp;
+};
+
+#define BCM2835_FIRMWARE_TAG_GET_GPIO_STATE 0x00030041
+#define BCM2835_FIRMWARE_TAG_SET_GPIO_STATE 0x00038041
+#define BCM2835_FIRMWARE_TAG_GET_GPIO_CONFIG 0x00030043
+#define BCM2835_FIRMWARE_TAG_SET_GPIO_CONFIG 0x00038043
+
+#define BCM2835_FIRMWARE_GPIO_IN 0
+#define BCM2835_FIRMWARE_GPIO_OUT 1
+
+union msg_get_gpio_state {
+ struct {
+ uint32_t gpio;
+ } req;
+ struct {
+ uint32_t gpio;
+ uint32_t state;
+ } resp;
+};
+
+union msg_set_gpio_state {
+ struct {
+ uint32_t gpio;
+ uint32_t state;
+ } req;
+ struct {
+ uint32_t gpio;
+ } resp;
+};
+
+union msg_get_gpio_config {
+ struct {
+ uint32_t gpio;
+ } req;
+ struct {
+ uint32_t gpio;
+ uint32_t dir;
+ uint32_t pol;
+ uint32_t term_en;
+ uint32_t term_pull_up;
+ } resp;
+};
+
+union msg_set_gpio_config {
+ struct {
+ uint32_t gpio;
+ uint32_t dir;
+ uint32_t pol;
+ uint32_t term_en;
+ uint32_t term_pull_up;
+ uint32_t state;
+ } req;
+ struct {
+ uint32_t gpio;
+ } resp;
+};
+
+int bcm2835_firmware_property(device_t, uint32_t, void *, size_t);
+
+#endif
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_ft5406.c b/sys/arm/broadcom/bcm2835/bcm2835_ft5406.c
new file mode 100644
index 000000000000..d9a01b4a929a
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2835_ft5406.c
@@ -0,0 +1,336 @@
+/*-
+ * Copyright (C) 2016 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/cpu.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/selinfo.h>
+#include <sys/poll.h>
+#include <sys/uio.h>
+#include <sys/conf.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/evdev/input.h>
+#include <dev/evdev/evdev.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <arm/broadcom/bcm2835/bcm2835_mbox.h>
+#include <arm/broadcom/bcm2835/bcm2835_mbox_prop.h>
+#include <arm/broadcom/bcm2835/bcm2835_vcbus.h>
+
+#include "mbox_if.h"
+
+#ifdef DEBUG
+#define DPRINTF(fmt, ...) do { \
+ printf("%s:%u: ", __func__, __LINE__); \
+ printf(fmt, ##__VA_ARGS__); \
+} while (0)
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
+#define FT5406_LOCK(_sc) \
+ mtx_lock(&(_sc)->sc_mtx)
+#define FT5406_UNLOCK(_sc) \
+ mtx_unlock(&(_sc)->sc_mtx)
+#define FT5406_LOCK_INIT(_sc) \
+ mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \
+ "ft5406", MTX_DEF)
+#define FT5406_LOCK_DESTROY(_sc) \
+ mtx_destroy(&_sc->sc_mtx);
+#define FT5406_LOCK_ASSERT(_sc) \
+ mtx_assert(&(_sc)->sc_mtx, MA_OWNED)
+
+#define FT5406_DEVICE_MODE 0
+#define FT5406_GESTURE_ID 1
+#define FT5406_NUM_POINTS 2
+#define FT5406_POINT_XH(n) (0 + 3 + (n)*6)
+#define FT5406_POINT_XL(n) (1 + 3 + (n)*6)
+#define FT5406_POINT_YH(n) (2 + 3 + (n)*6)
+#define FT5406_POINT_YL(n) (3 + 3 + (n)*6)
+#define FT5406_WINDOW_SIZE 64
+
+#define GET_NUM_POINTS(buf) (buf[FT5406_NUM_POINTS])
+#define GET_X(buf, n) (((buf[FT5406_POINT_XH(n)] & 0xf) << 8) | \
+ (buf[FT5406_POINT_XL(n)]))
+#define GET_Y(buf, n) (((buf[FT5406_POINT_YH(n)] & 0xf) << 8) | \
+ (buf[FT5406_POINT_YL(n)]))
+#define GET_TOUCH_ID(buf, n) ((buf[FT5406_POINT_YH(n)] >> 4) & 0xf)
+
+#define NO_POINTS 99
+#define SCREEN_WIDTH 800
+#define SCREEN_HEIGHT 480
+#define SCREEN_WIDTH_MM 155
+#define SCREEN_HEIGHT_MM 86
+#define SCREEN_RES_X (SCREEN_WIDTH / SCREEN_WIDTH_MM)
+#define SCREEN_RES_Y (SCREEN_HEIGHT / SCREEN_HEIGHT_MM)
+#define MAX_TOUCH_ID (10 - 1)
+
+struct ft5406ts_softc {
+ device_t sc_dev;
+ struct mtx sc_mtx;
+ int sc_tick;
+ struct callout sc_callout;
+
+ /* mbox buffer (mapped to KVA) */
+ uint8_t *touch_buf;
+
+ /* initial hook for waiting mbox intr */
+ struct intr_config_hook sc_init_hook;
+
+ struct evdev_dev *sc_evdev;
+
+ uint8_t sc_window[FT5406_WINDOW_SIZE];
+};
+
+static evdev_open_t ft5406ts_ev_open;
+static evdev_close_t ft5406ts_ev_close;
+
+static const struct evdev_methods ft5406ts_evdev_methods = {
+ .ev_open = &ft5406ts_ev_open,
+ .ev_close = &ft5406ts_ev_close,
+};
+
+static void
+ft5406ts_callout(void *data)
+{
+ struct ft5406ts_softc *sc = (struct ft5406ts_softc *)data;
+ int points;
+ int id, i, x, y;
+
+ FT5406_LOCK_ASSERT(sc);
+
+ memcpy(sc->sc_window, sc->touch_buf, FT5406_WINDOW_SIZE);
+ sc->touch_buf[FT5406_NUM_POINTS] = NO_POINTS;
+
+ points = GET_NUM_POINTS(sc->sc_window);
+ /*
+ * No update from VC - do nothing.
+ */
+ if (points == NO_POINTS)
+ goto out;
+
+ for (i = 0; i < points; i++) {
+ id = GET_TOUCH_ID(sc->sc_window, i);
+ x = GET_X(sc->sc_window, i);
+ y = GET_Y(sc->sc_window, i);
+
+ if (id > MAX_TOUCH_ID) {
+ device_printf(sc->sc_dev, "bad touch id: %d", id);
+ continue;
+ }
+ evdev_push_event(sc->sc_evdev, EV_ABS, ABS_MT_SLOT, id);
+ evdev_push_event(sc->sc_evdev, EV_ABS, ABS_MT_TRACKING_ID, id);
+ evdev_push_event(sc->sc_evdev, EV_ABS, ABS_MT_POSITION_X, x);
+ evdev_push_event(sc->sc_evdev, EV_ABS, ABS_MT_POSITION_Y, y);
+ }
+ evdev_sync(sc->sc_evdev);
+out:
+ callout_reset(&sc->sc_callout, sc->sc_tick, ft5406ts_callout, sc);
+}
+
+static int
+ft5406ts_ev_close(struct evdev_dev *evdev)
+{
+ struct ft5406ts_softc *sc = evdev_get_softc(evdev);
+
+ FT5406_LOCK_ASSERT(sc);
+
+ callout_stop(&sc->sc_callout);
+
+ return (0);
+}
+
+static int
+ft5406ts_ev_open(struct evdev_dev *evdev)
+{
+ struct ft5406ts_softc *sc = evdev_get_softc(evdev);
+
+ FT5406_LOCK_ASSERT(sc);
+
+ callout_reset(&sc->sc_callout, sc->sc_tick, ft5406ts_callout, sc);
+
+ return (0);
+}
+
+static void
+ft5406ts_init(void *arg)
+{
+ struct ft5406ts_softc *sc = arg;
+ struct bcm2835_mbox_tag_touchbuf msg;
+ uint32_t touchbuf;
+ int err;
+
+ /* release this hook (continue boot) */
+ config_intrhook_disestablish(&sc->sc_init_hook);
+
+ memset(&msg, 0, sizeof(msg));
+ msg.hdr.buf_size = sizeof(msg);
+ msg.hdr.code = BCM2835_MBOX_CODE_REQ;
+ msg.tag_hdr.tag = BCM2835_MBOX_TAG_GET_TOUCHBUF;
+ msg.tag_hdr.val_buf_size = sizeof(msg.body);
+ msg.tag_hdr.val_len = sizeof(msg.body);
+ msg.end_tag = 0;
+
+ /* call mailbox property */
+ err = bcm2835_mbox_property(&msg, sizeof(msg));
+ if (err) {
+ device_printf(sc->sc_dev, "failed to get touchbuf address\n");
+ return;
+ }
+
+ if (msg.body.resp.address == 0) {
+ device_printf(sc->sc_dev, "touchscreen not detected\n");
+ return;
+ }
+
+ touchbuf = VCBUS_TO_ARMC(msg.body.resp.address);
+ sc->touch_buf = (uint8_t*)pmap_mapdev(touchbuf, FT5406_WINDOW_SIZE);
+
+ /* 60Hz */
+ sc->sc_tick = hz * 17 / 1000;
+ if (sc->sc_tick == 0)
+ sc->sc_tick = 1;
+
+ sc->sc_evdev = evdev_alloc();
+ evdev_set_name(sc->sc_evdev, device_get_desc(sc->sc_dev));
+ evdev_set_phys(sc->sc_evdev, device_get_nameunit(sc->sc_dev));
+ evdev_set_id(sc->sc_evdev, BUS_HOST, 0, 0, 0);
+ evdev_set_methods(sc->sc_evdev, sc, &ft5406ts_evdev_methods);
+ evdev_set_flag(sc->sc_evdev, EVDEV_FLAG_MT_STCOMPAT);
+ evdev_set_flag(sc->sc_evdev, EVDEV_FLAG_MT_AUTOREL);
+ evdev_support_prop(sc->sc_evdev, INPUT_PROP_DIRECT);
+ evdev_support_event(sc->sc_evdev, EV_SYN);
+ evdev_support_event(sc->sc_evdev, EV_ABS);
+
+ evdev_support_abs(sc->sc_evdev, ABS_MT_SLOT, 0,
+ MAX_TOUCH_ID, 0, 0, 0);
+ evdev_support_abs(sc->sc_evdev, ABS_MT_TRACKING_ID, -1,
+ MAX_TOUCH_ID, 0, 0, 0);
+ evdev_support_abs(sc->sc_evdev, ABS_MT_POSITION_X, 0,
+ SCREEN_WIDTH, 0, 0, SCREEN_RES_X);
+ evdev_support_abs(sc->sc_evdev, ABS_MT_POSITION_Y, 0,
+ SCREEN_HEIGHT, 0, 0, SCREEN_RES_Y);
+
+ err = evdev_register_mtx(sc->sc_evdev, &sc->sc_mtx);
+ if (err) {
+ evdev_free(sc->sc_evdev);
+ sc->sc_evdev = NULL; /* Avoid double free */
+ return;
+ }
+
+ sc->touch_buf[FT5406_NUM_POINTS] = NO_POINTS;
+ callout_init_mtx(&sc->sc_callout, &sc->sc_mtx, 0);
+}
+
+static int
+ft5406ts_probe(device_t dev)
+{
+
+ if (!ofw_bus_is_compatible(dev, "rpi,rpi-ft5406"))
+ return (ENXIO);
+
+ device_set_desc(dev, "FT5406 touchscreen (VC memory interface)");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ft5406ts_attach(device_t dev)
+{
+ struct ft5406ts_softc *sc;
+
+ /* set self dev */
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+
+ /* register callback for using mbox when interrupts are enabled */
+ sc->sc_init_hook.ich_func = ft5406ts_init;
+ sc->sc_init_hook.ich_arg = sc;
+
+ FT5406_LOCK_INIT(sc);
+
+ if (config_intrhook_establish(&sc->sc_init_hook) != 0) {
+ device_printf(dev, "config_intrhook_establish failed\n");
+ FT5406_LOCK_DESTROY(sc);
+ return (ENOMEM);
+ }
+
+ return (0);
+}
+
+static int
+ft5406ts_detach(device_t dev)
+{
+ struct ft5406ts_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ evdev_free(sc->sc_evdev);
+
+ FT5406_LOCK_DESTROY(sc);
+
+ return (0);
+}
+
+static device_method_t ft5406ts_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ft5406ts_probe),
+ DEVMETHOD(device_attach, ft5406ts_attach),
+ DEVMETHOD(device_detach, ft5406ts_detach),
+
+ DEVMETHOD_END
+};
+
+static devclass_t ft5406ts_devclass;
+static driver_t ft5406ts_driver = {
+ "ft5406ts",
+ ft5406ts_methods,
+ sizeof(struct ft5406ts_softc),
+};
+
+DRIVER_MODULE(ft5406ts, ofwbus, ft5406ts_driver, ft5406ts_devclass, 0, 0);
+MODULE_DEPEND(ft5406ts, evdev, 1, 1, 1);
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_gpio.c b/sys/arm/broadcom/bcm2835/bcm2835_gpio.c
new file mode 100644
index 000000000000..61513df9c008
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2835_gpio.c
@@ -0,0 +1,1323 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@FreeBSD.org>
+ * Copyright (c) 2012-2015 Luiz Otavio O Souza <loos@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/gpio.h>
+#include <sys/interrupt.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <dev/fdt/fdt_pinctrl.h>
+#include <dev/gpio/gpiobusvar.h>
+#include <dev/ofw/ofw_bus.h>
+
+#include "gpio_if.h"
+
+#include "pic_if.h"
+
+#ifdef DEBUG
+#define dprintf(fmt, args...) do { printf("%s(): ", __func__); \
+ printf(fmt,##args); } while (0)
+#else
+#define dprintf(fmt, args...)
+#endif
+
+#define BCM_GPIO_IRQS 4
+#define BCM_GPIO_PINS 54
+#define BCM_GPIO_PINS_PER_BANK 32
+
+#define BCM_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \
+ GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN | GPIO_INTR_LEVEL_LOW | \
+ GPIO_INTR_LEVEL_HIGH | GPIO_INTR_EDGE_RISING | \
+ GPIO_INTR_EDGE_FALLING | GPIO_INTR_EDGE_BOTH)
+
+#define BCM2835_FSEL_GPIO_IN 0
+#define BCM2835_FSEL_GPIO_OUT 1
+#define BCM2835_FSEL_ALT5 2
+#define BCM2835_FSEL_ALT4 3
+#define BCM2835_FSEL_ALT0 4
+#define BCM2835_FSEL_ALT1 5
+#define BCM2835_FSEL_ALT2 6
+#define BCM2835_FSEL_ALT3 7
+
+#define BCM2835_PUD_OFF 0
+#define BCM2835_PUD_DOWN 1
+#define BCM2835_PUD_UP 2
+
+static struct resource_spec bcm_gpio_res_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE }, /* bank 0 interrupt */
+ { SYS_RES_IRQ, 1, RF_ACTIVE }, /* bank 1 interrupt */
+ { -1, 0, 0 }
+};
+
+struct bcm_gpio_sysctl {
+ struct bcm_gpio_softc *sc;
+ uint32_t pin;
+};
+
+struct bcm_gpio_irqsrc {
+ struct intr_irqsrc bgi_isrc;
+ uint32_t bgi_irq;
+ uint32_t bgi_mode;
+ uint32_t bgi_mask;
+};
+
+struct bcm_gpio_softc {
+ device_t sc_dev;
+ device_t sc_busdev;
+ struct mtx sc_mtx;
+ struct resource * sc_res[BCM_GPIO_IRQS + 1];
+ bus_space_tag_t sc_bst;
+ bus_space_handle_t sc_bsh;
+ void * sc_intrhand[BCM_GPIO_IRQS];
+ int sc_gpio_npins;
+ int sc_ro_npins;
+ int sc_ro_pins[BCM_GPIO_PINS];
+ struct gpio_pin sc_gpio_pins[BCM_GPIO_PINS];
+ struct bcm_gpio_sysctl sc_sysctl[BCM_GPIO_PINS];
+ struct bcm_gpio_irqsrc sc_isrcs[BCM_GPIO_PINS];
+};
+
+enum bcm_gpio_pud {
+ BCM_GPIO_NONE,
+ BCM_GPIO_PULLDOWN,
+ BCM_GPIO_PULLUP,
+};
+
+#define BCM_GPIO_LOCK(_sc) mtx_lock_spin(&(_sc)->sc_mtx)
+#define BCM_GPIO_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->sc_mtx)
+#define BCM_GPIO_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED)
+#define BCM_GPIO_WRITE(_sc, _off, _val) \
+ bus_space_write_4((_sc)->sc_bst, (_sc)->sc_bsh, _off, _val)
+#define BCM_GPIO_READ(_sc, _off) \
+ bus_space_read_4((_sc)->sc_bst, (_sc)->sc_bsh, _off)
+#define BCM_GPIO_CLEAR_BITS(_sc, _off, _bits) \
+ BCM_GPIO_WRITE(_sc, _off, BCM_GPIO_READ(_sc, _off) & ~(_bits))
+#define BCM_GPIO_SET_BITS(_sc, _off, _bits) \
+ BCM_GPIO_WRITE(_sc, _off, BCM_GPIO_READ(_sc, _off) | _bits)
+#define BCM_GPIO_BANK(a) (a / BCM_GPIO_PINS_PER_BANK)
+#define BCM_GPIO_MASK(a) (1U << (a % BCM_GPIO_PINS_PER_BANK))
+
+#define BCM_GPIO_GPFSEL(_bank) (0x00 + _bank * 4) /* Function Select */
+#define BCM_GPIO_GPSET(_bank) (0x1c + _bank * 4) /* Pin Out Set */
+#define BCM_GPIO_GPCLR(_bank) (0x28 + _bank * 4) /* Pin Out Clear */
+#define BCM_GPIO_GPLEV(_bank) (0x34 + _bank * 4) /* Pin Level */
+#define BCM_GPIO_GPEDS(_bank) (0x40 + _bank * 4) /* Event Status */
+#define BCM_GPIO_GPREN(_bank) (0x4c + _bank * 4) /* Rising Edge irq */
+#define BCM_GPIO_GPFEN(_bank) (0x58 + _bank * 4) /* Falling Edge irq */
+#define BCM_GPIO_GPHEN(_bank) (0x64 + _bank * 4) /* High Level irq */
+#define BCM_GPIO_GPLEN(_bank) (0x70 + _bank * 4) /* Low Level irq */
+#define BCM_GPIO_GPAREN(_bank) (0x7c + _bank * 4) /* Async Rising Edge */
+#define BCM_GPIO_GPAFEN(_bank) (0x88 + _bank * 4) /* Async Falling Egde */
+#define BCM_GPIO_GPPUD(_bank) (0x94) /* Pin Pull up/down */
+#define BCM_GPIO_GPPUDCLK(_bank) (0x98 + _bank * 4) /* Pin Pull up clock */
+
+static struct ofw_compat_data compat_data[] = {
+ {"broadcom,bcm2835-gpio", 1},
+ {"brcm,bcm2835-gpio", 1},
+ {NULL, 0}
+};
+
+static struct bcm_gpio_softc *bcm_gpio_sc = NULL;
+
+static int bcm_gpio_intr_bank0(void *arg);
+static int bcm_gpio_intr_bank1(void *arg);
+static int bcm_gpio_pic_attach(struct bcm_gpio_softc *sc);
+static int bcm_gpio_pic_detach(struct bcm_gpio_softc *sc);
+
+static int
+bcm_gpio_pin_is_ro(struct bcm_gpio_softc *sc, int pin)
+{
+ int i;
+
+ for (i = 0; i < sc->sc_ro_npins; i++)
+ if (pin == sc->sc_ro_pins[i])
+ return (1);
+ return (0);
+}
+
+static uint32_t
+bcm_gpio_get_function(struct bcm_gpio_softc *sc, uint32_t pin)
+{
+ uint32_t bank, func, offset;
+
+ /* Five banks, 10 pins per bank, 3 bits per pin. */
+ bank = pin / 10;
+ offset = (pin - bank * 10) * 3;
+
+ BCM_GPIO_LOCK(sc);
+ func = (BCM_GPIO_READ(sc, BCM_GPIO_GPFSEL(bank)) >> offset) & 7;
+ BCM_GPIO_UNLOCK(sc);
+
+ return (func);
+}
+
+static void
+bcm_gpio_func_str(uint32_t nfunc, char *buf, int bufsize)
+{
+
+ switch (nfunc) {
+ case BCM2835_FSEL_GPIO_IN:
+ strncpy(buf, "input", bufsize);
+ break;
+ case BCM2835_FSEL_GPIO_OUT:
+ strncpy(buf, "output", bufsize);
+ break;
+ case BCM2835_FSEL_ALT0:
+ strncpy(buf, "alt0", bufsize);
+ break;
+ case BCM2835_FSEL_ALT1:
+ strncpy(buf, "alt1", bufsize);
+ break;
+ case BCM2835_FSEL_ALT2:
+ strncpy(buf, "alt2", bufsize);
+ break;
+ case BCM2835_FSEL_ALT3:
+ strncpy(buf, "alt3", bufsize);
+ break;
+ case BCM2835_FSEL_ALT4:
+ strncpy(buf, "alt4", bufsize);
+ break;
+ case BCM2835_FSEL_ALT5:
+ strncpy(buf, "alt5", bufsize);
+ break;
+ default:
+ strncpy(buf, "invalid", bufsize);
+ }
+}
+
+static int
+bcm_gpio_str_func(char *func, uint32_t *nfunc)
+{
+
+ if (strcasecmp(func, "input") == 0)
+ *nfunc = BCM2835_FSEL_GPIO_IN;
+ else if (strcasecmp(func, "output") == 0)
+ *nfunc = BCM2835_FSEL_GPIO_OUT;
+ else if (strcasecmp(func, "alt0") == 0)
+ *nfunc = BCM2835_FSEL_ALT0;
+ else if (strcasecmp(func, "alt1") == 0)
+ *nfunc = BCM2835_FSEL_ALT1;
+ else if (strcasecmp(func, "alt2") == 0)
+ *nfunc = BCM2835_FSEL_ALT2;
+ else if (strcasecmp(func, "alt3") == 0)
+ *nfunc = BCM2835_FSEL_ALT3;
+ else if (strcasecmp(func, "alt4") == 0)
+ *nfunc = BCM2835_FSEL_ALT4;
+ else if (strcasecmp(func, "alt5") == 0)
+ *nfunc = BCM2835_FSEL_ALT5;
+ else
+ return (-1);
+
+ return (0);
+}
+
+static uint32_t
+bcm_gpio_func_flag(uint32_t nfunc)
+{
+
+ switch (nfunc) {
+ case BCM2835_FSEL_GPIO_IN:
+ return (GPIO_PIN_INPUT);
+ case BCM2835_FSEL_GPIO_OUT:
+ return (GPIO_PIN_OUTPUT);
+ }
+ return (0);
+}
+
+static void
+bcm_gpio_set_function(struct bcm_gpio_softc *sc, uint32_t pin, uint32_t f)
+{
+ uint32_t bank, data, offset;
+
+ /* Must be called with lock held. */
+ BCM_GPIO_LOCK_ASSERT(sc);
+
+ /* Five banks, 10 pins per bank, 3 bits per pin. */
+ bank = pin / 10;
+ offset = (pin - bank * 10) * 3;
+
+ data = BCM_GPIO_READ(sc, BCM_GPIO_GPFSEL(bank));
+ data &= ~(7 << offset);
+ data |= (f << offset);
+ BCM_GPIO_WRITE(sc, BCM_GPIO_GPFSEL(bank), data);
+}
+
+static void
+bcm_gpio_set_pud(struct bcm_gpio_softc *sc, uint32_t pin, uint32_t state)
+{
+ uint32_t bank;
+
+ /* Must be called with lock held. */
+ BCM_GPIO_LOCK_ASSERT(sc);
+
+ bank = BCM_GPIO_BANK(pin);
+ BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUD(0), state);
+ BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUDCLK(bank), BCM_GPIO_MASK(pin));
+ BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUD(0), 0);
+ BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUDCLK(bank), 0);
+}
+
+static void
+bcm_gpio_set_alternate(device_t dev, uint32_t pin, uint32_t nfunc)
+{
+ struct bcm_gpio_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+ BCM_GPIO_LOCK(sc);
+
+ /* Set the pin function. */
+ bcm_gpio_set_function(sc, pin, nfunc);
+
+ /* Update the pin flags. */
+ for (i = 0; i < sc->sc_gpio_npins; i++) {
+ if (sc->sc_gpio_pins[i].gp_pin == pin)
+ break;
+ }
+ if (i < sc->sc_gpio_npins)
+ sc->sc_gpio_pins[i].gp_flags = bcm_gpio_func_flag(nfunc);
+
+ BCM_GPIO_UNLOCK(sc);
+}
+
+static void
+bcm_gpio_pin_configure(struct bcm_gpio_softc *sc, struct gpio_pin *pin,
+ unsigned int flags)
+{
+
+ BCM_GPIO_LOCK(sc);
+
+ /*
+ * Manage input/output.
+ */
+ if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) {
+ pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT);
+ if (flags & GPIO_PIN_OUTPUT) {
+ pin->gp_flags |= GPIO_PIN_OUTPUT;
+ bcm_gpio_set_function(sc, pin->gp_pin,
+ BCM2835_FSEL_GPIO_OUT);
+ } else {
+ pin->gp_flags |= GPIO_PIN_INPUT;
+ bcm_gpio_set_function(sc, pin->gp_pin,
+ BCM2835_FSEL_GPIO_IN);
+ }
+ }
+
+ /* Manage Pull-up/pull-down. */
+ pin->gp_flags &= ~(GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN);
+ if (flags & (GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN)) {
+ if (flags & GPIO_PIN_PULLUP) {
+ pin->gp_flags |= GPIO_PIN_PULLUP;
+ bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_PULLUP);
+ } else {
+ pin->gp_flags |= GPIO_PIN_PULLDOWN;
+ bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_PULLDOWN);
+ }
+ } else
+ bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_NONE);
+
+ BCM_GPIO_UNLOCK(sc);
+}
+
+static device_t
+bcm_gpio_get_bus(device_t dev)
+{
+ struct bcm_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ return (sc->sc_busdev);
+}
+
+static int
+bcm_gpio_pin_max(device_t dev, int *maxpin)
+{
+
+ *maxpin = BCM_GPIO_PINS - 1;
+ return (0);
+}
+
+static int
+bcm_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
+{
+ struct bcm_gpio_softc *sc = device_get_softc(dev);
+ int i;
+
+ for (i = 0; i < sc->sc_gpio_npins; i++) {
+ if (sc->sc_gpio_pins[i].gp_pin == pin)
+ break;
+ }
+
+ if (i >= sc->sc_gpio_npins)
+ return (EINVAL);
+
+ BCM_GPIO_LOCK(sc);
+ *caps = sc->sc_gpio_pins[i].gp_caps;
+ BCM_GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+bcm_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
+{
+ struct bcm_gpio_softc *sc = device_get_softc(dev);
+ int i;
+
+ for (i = 0; i < sc->sc_gpio_npins; i++) {
+ if (sc->sc_gpio_pins[i].gp_pin == pin)
+ break;
+ }
+
+ if (i >= sc->sc_gpio_npins)
+ return (EINVAL);
+
+ BCM_GPIO_LOCK(sc);
+ *flags = sc->sc_gpio_pins[i].gp_flags;
+ BCM_GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+bcm_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
+{
+ struct bcm_gpio_softc *sc = device_get_softc(dev);
+ int i;
+
+ for (i = 0; i < sc->sc_gpio_npins; i++) {
+ if (sc->sc_gpio_pins[i].gp_pin == pin)
+ break;
+ }
+
+ if (i >= sc->sc_gpio_npins)
+ return (EINVAL);
+
+ BCM_GPIO_LOCK(sc);
+ memcpy(name, sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME);
+ BCM_GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+bcm_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
+{
+ struct bcm_gpio_softc *sc = device_get_softc(dev);
+ int i;
+
+ for (i = 0; i < sc->sc_gpio_npins; i++) {
+ if (sc->sc_gpio_pins[i].gp_pin == pin)
+ break;
+ }
+
+ if (i >= sc->sc_gpio_npins)
+ return (EINVAL);
+
+ /* We never touch on read-only/reserved pins. */
+ if (bcm_gpio_pin_is_ro(sc, pin))
+ return (EINVAL);
+
+ bcm_gpio_pin_configure(sc, &sc->sc_gpio_pins[i], flags);
+
+ return (0);
+}
+
+static int
+bcm_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
+{
+ struct bcm_gpio_softc *sc = device_get_softc(dev);
+ uint32_t bank, reg;
+ int i;
+
+ for (i = 0; i < sc->sc_gpio_npins; i++) {
+ if (sc->sc_gpio_pins[i].gp_pin == pin)
+ break;
+ }
+ if (i >= sc->sc_gpio_npins)
+ return (EINVAL);
+ /* We never write to read-only/reserved pins. */
+ if (bcm_gpio_pin_is_ro(sc, pin))
+ return (EINVAL);
+ BCM_GPIO_LOCK(sc);
+ bank = BCM_GPIO_BANK(pin);
+ if (value)
+ reg = BCM_GPIO_GPSET(bank);
+ else
+ reg = BCM_GPIO_GPCLR(bank);
+ BCM_GPIO_WRITE(sc, reg, BCM_GPIO_MASK(pin));
+ BCM_GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+bcm_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
+{
+ struct bcm_gpio_softc *sc = device_get_softc(dev);
+ uint32_t bank, reg_data;
+ int i;
+
+ for (i = 0; i < sc->sc_gpio_npins; i++) {
+ if (sc->sc_gpio_pins[i].gp_pin == pin)
+ break;
+ }
+ if (i >= sc->sc_gpio_npins)
+ return (EINVAL);
+ bank = BCM_GPIO_BANK(pin);
+ BCM_GPIO_LOCK(sc);
+ reg_data = BCM_GPIO_READ(sc, BCM_GPIO_GPLEV(bank));
+ BCM_GPIO_UNLOCK(sc);
+ *val = (reg_data & BCM_GPIO_MASK(pin)) ? 1 : 0;
+
+ return (0);
+}
+
+static int
+bcm_gpio_pin_toggle(device_t dev, uint32_t pin)
+{
+ struct bcm_gpio_softc *sc = device_get_softc(dev);
+ uint32_t bank, data, reg;
+ int i;
+
+ for (i = 0; i < sc->sc_gpio_npins; i++) {
+ if (sc->sc_gpio_pins[i].gp_pin == pin)
+ break;
+ }
+ if (i >= sc->sc_gpio_npins)
+ return (EINVAL);
+ /* We never write to read-only/reserved pins. */
+ if (bcm_gpio_pin_is_ro(sc, pin))
+ return (EINVAL);
+ BCM_GPIO_LOCK(sc);
+ bank = BCM_GPIO_BANK(pin);
+ data = BCM_GPIO_READ(sc, BCM_GPIO_GPLEV(bank));
+ if (data & BCM_GPIO_MASK(pin))
+ reg = BCM_GPIO_GPCLR(bank);
+ else
+ reg = BCM_GPIO_GPSET(bank);
+ BCM_GPIO_WRITE(sc, reg, BCM_GPIO_MASK(pin));
+ BCM_GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+bcm_gpio_func_proc(SYSCTL_HANDLER_ARGS)
+{
+ char buf[16];
+ struct bcm_gpio_softc *sc;
+ struct bcm_gpio_sysctl *sc_sysctl;
+ uint32_t nfunc;
+ int error;
+
+ sc_sysctl = arg1;
+ sc = sc_sysctl->sc;
+
+ /* Get the current pin function. */
+ nfunc = bcm_gpio_get_function(sc, sc_sysctl->pin);
+ bcm_gpio_func_str(nfunc, buf, sizeof(buf));
+
+ error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+ /* Ignore changes on read-only pins. */
+ if (bcm_gpio_pin_is_ro(sc, sc_sysctl->pin))
+ return (0);
+ /* Parse the user supplied string and check for a valid pin function. */
+ if (bcm_gpio_str_func(buf, &nfunc) != 0)
+ return (EINVAL);
+
+ /* Update the pin alternate function. */
+ bcm_gpio_set_alternate(sc->sc_dev, sc_sysctl->pin, nfunc);
+
+ return (0);
+}
+
+static void
+bcm_gpio_sysctl_init(struct bcm_gpio_softc *sc)
+{
+ char pinbuf[3];
+ struct bcm_gpio_sysctl *sc_sysctl;
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid *tree_node, *pin_node, *pinN_node;
+ struct sysctl_oid_list *tree, *pin_tree, *pinN_tree;
+ int i;
+
+ /*
+ * Add per-pin sysctl tree/handlers.
+ */
+ ctx = device_get_sysctl_ctx(sc->sc_dev);
+ tree_node = device_get_sysctl_tree(sc->sc_dev);
+ tree = SYSCTL_CHILDREN(tree_node);
+ pin_node = SYSCTL_ADD_NODE(ctx, tree, OID_AUTO, "pin",
+ CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "GPIO Pins");
+ pin_tree = SYSCTL_CHILDREN(pin_node);
+
+ for (i = 0; i < sc->sc_gpio_npins; i++) {
+ snprintf(pinbuf, sizeof(pinbuf), "%d", i);
+ pinN_node = SYSCTL_ADD_NODE(ctx, pin_tree, OID_AUTO, pinbuf,
+ CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "GPIO Pin");
+ pinN_tree = SYSCTL_CHILDREN(pinN_node);
+
+ sc->sc_sysctl[i].sc = sc;
+ sc_sysctl = &sc->sc_sysctl[i];
+ sc_sysctl->sc = sc;
+ sc_sysctl->pin = sc->sc_gpio_pins[i].gp_pin;
+ SYSCTL_ADD_PROC(ctx, pinN_tree, OID_AUTO, "function",
+ CTLFLAG_RW | CTLTYPE_STRING | CTLFLAG_NEEDGIANT, sc_sysctl,
+ sizeof(struct bcm_gpio_sysctl), bcm_gpio_func_proc,
+ "A", "Pin Function");
+ }
+}
+
+static int
+bcm_gpio_get_ro_pins(struct bcm_gpio_softc *sc, phandle_t node,
+ const char *propname, const char *label)
+{
+ int i, need_comma, npins, range_start, range_stop;
+ pcell_t *pins;
+
+ /* Get the property data. */
+ npins = OF_getencprop_alloc_multi(node, propname, sizeof(*pins),
+ (void **)&pins);
+ if (npins < 0)
+ return (-1);
+ if (npins == 0) {
+ OF_prop_free(pins);
+ return (0);
+ }
+ for (i = 0; i < npins; i++)
+ sc->sc_ro_pins[i + sc->sc_ro_npins] = pins[i];
+ sc->sc_ro_npins += npins;
+ need_comma = 0;
+ device_printf(sc->sc_dev, "%s pins: ", label);
+ range_start = range_stop = pins[0];
+ for (i = 1; i < npins; i++) {
+ if (pins[i] != range_stop + 1) {
+ if (need_comma)
+ printf(",");
+ if (range_start != range_stop)
+ printf("%d-%d", range_start, range_stop);
+ else
+ printf("%d", range_start);
+ range_start = range_stop = pins[i];
+ need_comma = 1;
+ } else
+ range_stop++;
+ }
+ if (need_comma)
+ printf(",");
+ if (range_start != range_stop)
+ printf("%d-%d.\n", range_start, range_stop);
+ else
+ printf("%d.\n", range_start);
+ OF_prop_free(pins);
+
+ return (0);
+}
+
+static int
+bcm_gpio_get_reserved_pins(struct bcm_gpio_softc *sc)
+{
+ char *name;
+ phandle_t gpio, node, reserved;
+ ssize_t len;
+
+ /* Get read-only pins if they're provided */
+ gpio = ofw_bus_get_node(sc->sc_dev);
+ if (bcm_gpio_get_ro_pins(sc, gpio, "broadcom,read-only",
+ "read-only") != 0)
+ return (0);
+ /* Traverse the GPIO subnodes to find the reserved pins node. */
+ reserved = 0;
+ node = OF_child(gpio);
+ while ((node != 0) && (reserved == 0)) {
+ len = OF_getprop_alloc(node, "name", (void **)&name);
+ if (len == -1)
+ return (-1);
+ if (strcmp(name, "reserved") == 0)
+ reserved = node;
+ OF_prop_free(name);
+ node = OF_peer(node);
+ }
+ if (reserved == 0)
+ return (-1);
+ /* Get the reserved pins. */
+ if (bcm_gpio_get_ro_pins(sc, reserved, "broadcom,pins",
+ "reserved") != 0)
+ return (-1);
+
+ return (0);
+}
+
+static int
+bcm_gpio_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "BCM2708/2835 GPIO controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+bcm_gpio_intr_attach(device_t dev)
+{
+ struct bcm_gpio_softc *sc;
+
+ /*
+ * Only first two interrupt lines are used. Third line is
+ * mirrored second line and forth line is common for all banks.
+ */
+ sc = device_get_softc(dev);
+ if (sc->sc_res[1] == NULL || sc->sc_res[2] == NULL)
+ return (-1);
+
+ if (bcm_gpio_pic_attach(sc) != 0) {
+ device_printf(dev, "unable to attach PIC\n");
+ return (-1);
+ }
+ if (bus_setup_intr(dev, sc->sc_res[1], INTR_TYPE_MISC | INTR_MPSAFE,
+ bcm_gpio_intr_bank0, NULL, sc, &sc->sc_intrhand[0]) != 0)
+ return (-1);
+ if (bus_setup_intr(dev, sc->sc_res[2], INTR_TYPE_MISC | INTR_MPSAFE,
+ bcm_gpio_intr_bank1, NULL, sc, &sc->sc_intrhand[1]) != 0)
+ return (-1);
+
+ return (0);
+}
+
+static void
+bcm_gpio_intr_detach(device_t dev)
+{
+ struct bcm_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (sc->sc_intrhand[0] != NULL)
+ bus_teardown_intr(dev, sc->sc_res[1], sc->sc_intrhand[0]);
+ if (sc->sc_intrhand[1] != NULL)
+ bus_teardown_intr(dev, sc->sc_res[2], sc->sc_intrhand[1]);
+
+ bcm_gpio_pic_detach(sc);
+}
+
+static int
+bcm_gpio_attach(device_t dev)
+{
+ int i, j;
+ phandle_t gpio;
+ struct bcm_gpio_softc *sc;
+ uint32_t func;
+
+ if (bcm_gpio_sc != NULL)
+ return (ENXIO);
+
+ bcm_gpio_sc = sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+ mtx_init(&sc->sc_mtx, "bcm gpio", "gpio", MTX_SPIN);
+ if (bus_alloc_resources(dev, bcm_gpio_res_spec, sc->sc_res) != 0) {
+ device_printf(dev, "cannot allocate resources\n");
+ goto fail;
+ }
+ sc->sc_bst = rman_get_bustag(sc->sc_res[0]);
+ sc->sc_bsh = rman_get_bushandle(sc->sc_res[0]);
+ /* Setup the GPIO interrupt handler. */
+ if (bcm_gpio_intr_attach(dev)) {
+ device_printf(dev, "unable to setup the gpio irq handler\n");
+ goto fail;
+ }
+ /* Find our node. */
+ gpio = ofw_bus_get_node(sc->sc_dev);
+ if (!OF_hasprop(gpio, "gpio-controller"))
+ /* Node is not a GPIO controller. */
+ goto fail;
+ /*
+ * Find the read-only pins. These are pins we never touch or bad
+ * things could happen.
+ */
+ if (bcm_gpio_get_reserved_pins(sc) == -1)
+ goto fail;
+ /* Initialize the software controlled pins. */
+ for (i = 0, j = 0; j < BCM_GPIO_PINS; j++) {
+ snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME,
+ "pin %d", j);
+ func = bcm_gpio_get_function(sc, j);
+ sc->sc_gpio_pins[i].gp_pin = j;
+ sc->sc_gpio_pins[i].gp_caps = BCM_GPIO_DEFAULT_CAPS;
+ sc->sc_gpio_pins[i].gp_flags = bcm_gpio_func_flag(func);
+ i++;
+ }
+ sc->sc_gpio_npins = i;
+ bcm_gpio_sysctl_init(sc);
+ sc->sc_busdev = gpiobus_attach_bus(dev);
+ if (sc->sc_busdev == NULL)
+ goto fail;
+
+ fdt_pinctrl_register(dev, "brcm,pins");
+ fdt_pinctrl_configure_tree(dev);
+
+ return (0);
+
+fail:
+ bcm_gpio_intr_detach(dev);
+ bus_release_resources(dev, bcm_gpio_res_spec, sc->sc_res);
+ mtx_destroy(&sc->sc_mtx);
+
+ return (ENXIO);
+}
+
+static int
+bcm_gpio_detach(device_t dev)
+{
+
+ return (EBUSY);
+}
+
+static inline void
+bcm_gpio_modify(struct bcm_gpio_softc *sc, uint32_t reg, uint32_t mask,
+ bool set_bits)
+{
+
+ if (set_bits)
+ BCM_GPIO_SET_BITS(sc, reg, mask);
+ else
+ BCM_GPIO_CLEAR_BITS(sc, reg, mask);
+}
+
+static inline void
+bcm_gpio_isrc_eoi(struct bcm_gpio_softc *sc, struct bcm_gpio_irqsrc *bgi)
+{
+ uint32_t bank;
+
+ /* Write 1 to clear. */
+ bank = BCM_GPIO_BANK(bgi->bgi_irq);
+ BCM_GPIO_WRITE(sc, BCM_GPIO_GPEDS(bank), bgi->bgi_mask);
+}
+
+static inline bool
+bcm_gpio_isrc_is_level(struct bcm_gpio_irqsrc *bgi)
+{
+
+ return (bgi->bgi_mode == GPIO_INTR_LEVEL_LOW ||
+ bgi->bgi_mode == GPIO_INTR_LEVEL_HIGH);
+}
+
+static inline void
+bcm_gpio_isrc_mask(struct bcm_gpio_softc *sc, struct bcm_gpio_irqsrc *bgi)
+{
+ uint32_t bank;
+
+ bank = BCM_GPIO_BANK(bgi->bgi_irq);
+ BCM_GPIO_LOCK(sc);
+ switch (bgi->bgi_mode) {
+ case GPIO_INTR_LEVEL_LOW:
+ BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPLEN(bank), bgi->bgi_mask);
+ break;
+ case GPIO_INTR_LEVEL_HIGH:
+ BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPHEN(bank), bgi->bgi_mask);
+ break;
+ case GPIO_INTR_EDGE_RISING:
+ BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPREN(bank), bgi->bgi_mask);
+ break;
+ case GPIO_INTR_EDGE_FALLING:
+ BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPFEN(bank), bgi->bgi_mask);
+ break;
+ case GPIO_INTR_EDGE_BOTH:
+ BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPREN(bank), bgi->bgi_mask);
+ BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPFEN(bank), bgi->bgi_mask);
+ break;
+ }
+ BCM_GPIO_UNLOCK(sc);
+}
+
+static inline void
+bcm_gpio_isrc_unmask(struct bcm_gpio_softc *sc, struct bcm_gpio_irqsrc *bgi)
+{
+ uint32_t bank;
+
+ bank = BCM_GPIO_BANK(bgi->bgi_irq);
+ BCM_GPIO_LOCK(sc);
+ switch (bgi->bgi_mode) {
+ case GPIO_INTR_LEVEL_LOW:
+ BCM_GPIO_SET_BITS(sc, BCM_GPIO_GPLEN(bank), bgi->bgi_mask);
+ break;
+ case GPIO_INTR_LEVEL_HIGH:
+ BCM_GPIO_SET_BITS(sc, BCM_GPIO_GPHEN(bank), bgi->bgi_mask);
+ break;
+ case GPIO_INTR_EDGE_RISING:
+ BCM_GPIO_SET_BITS(sc, BCM_GPIO_GPREN(bank), bgi->bgi_mask);
+ break;
+ case GPIO_INTR_EDGE_FALLING:
+ BCM_GPIO_SET_BITS(sc, BCM_GPIO_GPFEN(bank), bgi->bgi_mask);
+ break;
+ case GPIO_INTR_EDGE_BOTH:
+ BCM_GPIO_SET_BITS(sc, BCM_GPIO_GPREN(bank), bgi->bgi_mask);
+ BCM_GPIO_SET_BITS(sc, BCM_GPIO_GPFEN(bank), bgi->bgi_mask);
+ break;
+ }
+ BCM_GPIO_UNLOCK(sc);
+}
+
+static int
+bcm_gpio_intr_internal(struct bcm_gpio_softc *sc, uint32_t bank)
+{
+ u_int irq;
+ struct bcm_gpio_irqsrc *bgi;
+ uint32_t reg;
+
+ /* Do not care of spurious interrupt on GPIO. */
+ reg = BCM_GPIO_READ(sc, BCM_GPIO_GPEDS(bank));
+ while (reg != 0) {
+ irq = BCM_GPIO_PINS_PER_BANK * bank + ffs(reg) - 1;
+ bgi = sc->sc_isrcs + irq;
+ if (!bcm_gpio_isrc_is_level(bgi))
+ bcm_gpio_isrc_eoi(sc, bgi);
+ if (intr_isrc_dispatch(&bgi->bgi_isrc,
+ curthread->td_intr_frame) != 0) {
+ bcm_gpio_isrc_mask(sc, bgi);
+ if (bcm_gpio_isrc_is_level(bgi))
+ bcm_gpio_isrc_eoi(sc, bgi);
+ device_printf(sc->sc_dev, "Stray irq %u disabled\n",
+ irq);
+ }
+ reg &= ~bgi->bgi_mask;
+ }
+ return (FILTER_HANDLED);
+}
+
+static int
+bcm_gpio_intr_bank0(void *arg)
+{
+
+ return (bcm_gpio_intr_internal(arg, 0));
+}
+
+static int
+bcm_gpio_intr_bank1(void *arg)
+{
+
+ return (bcm_gpio_intr_internal(arg, 1));
+}
+
+static int
+bcm_gpio_pic_attach(struct bcm_gpio_softc *sc)
+{
+ int error;
+ uint32_t irq;
+ const char *name;
+
+ name = device_get_nameunit(sc->sc_dev);
+ for (irq = 0; irq < BCM_GPIO_PINS; irq++) {
+ sc->sc_isrcs[irq].bgi_irq = irq;
+ sc->sc_isrcs[irq].bgi_mask = BCM_GPIO_MASK(irq);
+ sc->sc_isrcs[irq].bgi_mode = GPIO_INTR_CONFORM;
+
+ error = intr_isrc_register(&sc->sc_isrcs[irq].bgi_isrc,
+ sc->sc_dev, 0, "%s,%u", name, irq);
+ if (error != 0)
+ return (error); /* XXX deregister ISRCs */
+ }
+ if (intr_pic_register(sc->sc_dev,
+ OF_xref_from_node(ofw_bus_get_node(sc->sc_dev))) == NULL)
+ return (ENXIO);
+
+ return (0);
+}
+
+static int
+bcm_gpio_pic_detach(struct bcm_gpio_softc *sc)
+{
+
+ /*
+ * There has not been established any procedure yet
+ * how to detach PIC from living system correctly.
+ */
+ device_printf(sc->sc_dev, "%s: not implemented yet\n", __func__);
+ return (EBUSY);
+}
+
+static void
+bcm_gpio_pic_config_intr(struct bcm_gpio_softc *sc, struct bcm_gpio_irqsrc *bgi,
+ uint32_t mode)
+{
+ uint32_t bank;
+
+ bank = BCM_GPIO_BANK(bgi->bgi_irq);
+ BCM_GPIO_LOCK(sc);
+ bcm_gpio_modify(sc, BCM_GPIO_GPREN(bank), bgi->bgi_mask,
+ mode == GPIO_INTR_EDGE_RISING || mode == GPIO_INTR_EDGE_BOTH);
+ bcm_gpio_modify(sc, BCM_GPIO_GPFEN(bank), bgi->bgi_mask,
+ mode == GPIO_INTR_EDGE_FALLING || mode == GPIO_INTR_EDGE_BOTH);
+ bcm_gpio_modify(sc, BCM_GPIO_GPHEN(bank), bgi->bgi_mask,
+ mode == GPIO_INTR_LEVEL_HIGH);
+ bcm_gpio_modify(sc, BCM_GPIO_GPLEN(bank), bgi->bgi_mask,
+ mode == GPIO_INTR_LEVEL_LOW);
+ bgi->bgi_mode = mode;
+ BCM_GPIO_UNLOCK(sc);
+}
+
+static void
+bcm_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct bcm_gpio_softc *sc = device_get_softc(dev);
+ struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc;
+
+ bcm_gpio_isrc_mask(sc, bgi);
+}
+
+static void
+bcm_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct bcm_gpio_softc *sc = device_get_softc(dev);
+ struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc;
+
+ arm_irq_memory_barrier(bgi->bgi_irq);
+ bcm_gpio_isrc_unmask(sc, bgi);
+}
+
+static int
+bcm_gpio_pic_map_fdt(struct bcm_gpio_softc *sc, struct intr_map_data_fdt *daf,
+ u_int *irqp, uint32_t *modep)
+{
+ u_int irq;
+ uint32_t mode;
+
+ /*
+ * The first cell is the interrupt number.
+ * The second cell is used to specify flags:
+ * bits[3:0] trigger type and level flags:
+ * 1 = low-to-high edge triggered.
+ * 2 = high-to-low edge triggered.
+ * 4 = active high level-sensitive.
+ * 8 = active low level-sensitive.
+ */
+ if (daf->ncells != 2)
+ return (EINVAL);
+
+ irq = daf->cells[0];
+ if (irq >= BCM_GPIO_PINS || bcm_gpio_pin_is_ro(sc, irq))
+ return (EINVAL);
+
+ /* Only reasonable modes are supported. */
+ if (daf->cells[1] == 1)
+ mode = GPIO_INTR_EDGE_RISING;
+ else if (daf->cells[1] == 2)
+ mode = GPIO_INTR_EDGE_FALLING;
+ else if (daf->cells[1] == 3)
+ mode = GPIO_INTR_EDGE_BOTH;
+ else if (daf->cells[1] == 4)
+ mode = GPIO_INTR_LEVEL_HIGH;
+ else if (daf->cells[1] == 8)
+ mode = GPIO_INTR_LEVEL_LOW;
+ else
+ return (EINVAL);
+
+ *irqp = irq;
+ if (modep != NULL)
+ *modep = mode;
+ return (0);
+}
+
+static int
+bcm_gpio_pic_map_gpio(struct bcm_gpio_softc *sc, struct intr_map_data_gpio *dag,
+ u_int *irqp, uint32_t *modep)
+{
+ u_int irq;
+ uint32_t mode;
+
+ irq = dag->gpio_pin_num;
+ if (irq >= BCM_GPIO_PINS || bcm_gpio_pin_is_ro(sc, irq))
+ return (EINVAL);
+
+ mode = dag->gpio_intr_mode;
+ if (mode != GPIO_INTR_LEVEL_LOW && mode != GPIO_INTR_LEVEL_HIGH &&
+ mode != GPIO_INTR_EDGE_RISING && mode != GPIO_INTR_EDGE_FALLING &&
+ mode != GPIO_INTR_EDGE_BOTH)
+ return (EINVAL);
+
+ *irqp = irq;
+ if (modep != NULL)
+ *modep = mode;
+ return (0);
+}
+
+static int
+bcm_gpio_pic_map(struct bcm_gpio_softc *sc, struct intr_map_data *data,
+ u_int *irqp, uint32_t *modep)
+{
+
+ switch (data->type) {
+ case INTR_MAP_DATA_FDT:
+ return (bcm_gpio_pic_map_fdt(sc,
+ (struct intr_map_data_fdt *)data, irqp, modep));
+ case INTR_MAP_DATA_GPIO:
+ return (bcm_gpio_pic_map_gpio(sc,
+ (struct intr_map_data_gpio *)data, irqp, modep));
+ default:
+ return (ENOTSUP);
+ }
+}
+
+static int
+bcm_gpio_pic_map_intr(device_t dev, struct intr_map_data *data,
+ struct intr_irqsrc **isrcp)
+{
+ int error;
+ u_int irq;
+ struct bcm_gpio_softc *sc = device_get_softc(dev);
+
+ error = bcm_gpio_pic_map(sc, data, &irq, NULL);
+ if (error == 0)
+ *isrcp = &sc->sc_isrcs[irq].bgi_isrc;
+ return (error);
+}
+
+static void
+bcm_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct bcm_gpio_softc *sc = device_get_softc(dev);
+ struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc;
+
+ if (bcm_gpio_isrc_is_level(bgi))
+ bcm_gpio_isrc_eoi(sc, bgi);
+}
+
+static void
+bcm_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+
+ bcm_gpio_pic_enable_intr(dev, isrc);
+}
+
+static void
+bcm_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct bcm_gpio_softc *sc = device_get_softc(dev);
+ struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc;
+
+ bcm_gpio_isrc_mask(sc, bgi);
+ if (bcm_gpio_isrc_is_level(bgi))
+ bcm_gpio_isrc_eoi(sc, bgi);
+}
+
+static int
+bcm_gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ u_int irq;
+ uint32_t mode;
+ struct bcm_gpio_softc *sc;
+ struct bcm_gpio_irqsrc *bgi;
+
+ if (data == NULL)
+ return (ENOTSUP);
+
+ sc = device_get_softc(dev);
+ bgi = (struct bcm_gpio_irqsrc *)isrc;
+
+ /* Get and check config for an interrupt. */
+ if (bcm_gpio_pic_map(sc, data, &irq, &mode) != 0 || bgi->bgi_irq != irq)
+ return (EINVAL);
+
+ /*
+ * If this is a setup for another handler,
+ * only check that its configuration match.
+ */
+ if (isrc->isrc_handlers != 0)
+ return (bgi->bgi_mode == mode ? 0 : EINVAL);
+
+ bcm_gpio_pic_config_intr(sc, bgi, mode);
+ return (0);
+}
+
+static int
+bcm_gpio_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ struct bcm_gpio_softc *sc = device_get_softc(dev);
+ struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc;
+
+ if (isrc->isrc_handlers == 0)
+ bcm_gpio_pic_config_intr(sc, bgi, GPIO_INTR_CONFORM);
+ return (0);
+}
+
+static phandle_t
+bcm_gpio_get_node(device_t bus, device_t dev)
+{
+
+ /* We only have one child, the GPIO bus, which needs our own node. */
+ return (ofw_bus_get_node(bus));
+}
+
+static int
+bcm_gpio_configure_pins(device_t dev, phandle_t cfgxref)
+{
+ phandle_t cfgnode;
+ int i, pintuples, pulltuples;
+ uint32_t pin;
+ uint32_t *pins;
+ uint32_t *pulls;
+ uint32_t function;
+ static struct bcm_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+ cfgnode = OF_node_from_xref(cfgxref);
+
+ pins = NULL;
+ pintuples = OF_getencprop_alloc_multi(cfgnode, "brcm,pins",
+ sizeof(*pins), (void **)&pins);
+
+ char name[32];
+ OF_getprop(cfgnode, "name", &name, sizeof(name));
+
+ if (pintuples < 0)
+ return (ENOENT);
+
+ if (pintuples == 0)
+ return (0); /* Empty property is not an error. */
+
+ if (OF_getencprop(cfgnode, "brcm,function", &function,
+ sizeof(function)) <= 0) {
+ OF_prop_free(pins);
+ return (EINVAL);
+ }
+
+ pulls = NULL;
+ pulltuples = OF_getencprop_alloc_multi(cfgnode, "brcm,pull",
+ sizeof(*pulls), (void **)&pulls);
+
+ if ((pulls != NULL) && (pulltuples != pintuples)) {
+ OF_prop_free(pins);
+ OF_prop_free(pulls);
+ return (EINVAL);
+ }
+
+ for (i = 0; i < pintuples; i++) {
+ pin = pins[i];
+ bcm_gpio_set_alternate(dev, pin, function);
+ if (bootverbose)
+ device_printf(dev, "set pin %d to func %d", pin, function);
+ if (pulls) {
+ if (bootverbose)
+ printf(", pull %d", pulls[i]);
+ switch (pulls[i]) {
+ /* Convert to gpio(4) flags */
+ case BCM2835_PUD_OFF:
+ bcm_gpio_pin_setflags(dev, pin, 0);
+ break;
+ case BCM2835_PUD_UP:
+ bcm_gpio_pin_setflags(dev, pin, GPIO_PIN_PULLUP);
+ break;
+ case BCM2835_PUD_DOWN:
+ bcm_gpio_pin_setflags(dev, pin, GPIO_PIN_PULLDOWN);
+ break;
+ default:
+ printf("%s: invalid pull value for pin %d: %d\n",
+ name, pin, pulls[i]);
+ }
+ }
+ if (bootverbose)
+ printf("\n");
+ }
+
+ OF_prop_free(pins);
+ if (pulls)
+ OF_prop_free(pulls);
+
+ return (0);
+}
+
+static device_method_t bcm_gpio_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, bcm_gpio_probe),
+ DEVMETHOD(device_attach, bcm_gpio_attach),
+ DEVMETHOD(device_detach, bcm_gpio_detach),
+
+ /* GPIO protocol */
+ DEVMETHOD(gpio_get_bus, bcm_gpio_get_bus),
+ DEVMETHOD(gpio_pin_max, bcm_gpio_pin_max),
+ DEVMETHOD(gpio_pin_getname, bcm_gpio_pin_getname),
+ DEVMETHOD(gpio_pin_getflags, bcm_gpio_pin_getflags),
+ DEVMETHOD(gpio_pin_getcaps, bcm_gpio_pin_getcaps),
+ DEVMETHOD(gpio_pin_setflags, bcm_gpio_pin_setflags),
+ DEVMETHOD(gpio_pin_get, bcm_gpio_pin_get),
+ DEVMETHOD(gpio_pin_set, bcm_gpio_pin_set),
+ DEVMETHOD(gpio_pin_toggle, bcm_gpio_pin_toggle),
+
+ /* Interrupt controller interface */
+ DEVMETHOD(pic_disable_intr, bcm_gpio_pic_disable_intr),
+ DEVMETHOD(pic_enable_intr, bcm_gpio_pic_enable_intr),
+ DEVMETHOD(pic_map_intr, bcm_gpio_pic_map_intr),
+ DEVMETHOD(pic_post_filter, bcm_gpio_pic_post_filter),
+ DEVMETHOD(pic_post_ithread, bcm_gpio_pic_post_ithread),
+ DEVMETHOD(pic_pre_ithread, bcm_gpio_pic_pre_ithread),
+ DEVMETHOD(pic_setup_intr, bcm_gpio_pic_setup_intr),
+ DEVMETHOD(pic_teardown_intr, bcm_gpio_pic_teardown_intr),
+
+ /* ofw_bus interface */
+ DEVMETHOD(ofw_bus_get_node, bcm_gpio_get_node),
+
+ /* fdt_pinctrl interface */
+ DEVMETHOD(fdt_pinctrl_configure, bcm_gpio_configure_pins),
+
+ DEVMETHOD_END
+};
+
+static devclass_t bcm_gpio_devclass;
+
+static driver_t bcm_gpio_driver = {
+ "gpio",
+ bcm_gpio_methods,
+ sizeof(struct bcm_gpio_softc),
+};
+
+EARLY_DRIVER_MODULE(bcm_gpio, simplebus, bcm_gpio_driver, bcm_gpio_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_intr.c b/sys/arm/broadcom/bcm2835/bcm2835_intr.c
new file mode 100644
index 000000000000..93c044fe6419
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2835_intr.c
@@ -0,0 +1,453 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Damjan Marion <dmarion@Freebsd.org>
+ * All rights reserved.
+ *
+ * Based on OMAP3 INTC code by Ben Gray
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/ktr.h>
+#include <sys/module.h>
+#include <sys/proc.h>
+#include <sys/rman.h>
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "pic_if.h"
+
+#define INTC_PENDING_BASIC 0x00
+#define INTC_PENDING_BANK1 0x04
+#define INTC_PENDING_BANK2 0x08
+#define INTC_FIQ_CONTROL 0x0C
+#define INTC_ENABLE_BANK1 0x10
+#define INTC_ENABLE_BANK2 0x14
+#define INTC_ENABLE_BASIC 0x18
+#define INTC_DISABLE_BANK1 0x1C
+#define INTC_DISABLE_BANK2 0x20
+#define INTC_DISABLE_BASIC 0x24
+
+#define INTC_PENDING_BASIC_ARM 0x0000FF
+#define INTC_PENDING_BASIC_GPU1_PEND 0x000100
+#define INTC_PENDING_BASIC_GPU2_PEND 0x000200
+#define INTC_PENDING_BASIC_GPU1_7 0x000400
+#define INTC_PENDING_BASIC_GPU1_9 0x000800
+#define INTC_PENDING_BASIC_GPU1_10 0x001000
+#define INTC_PENDING_BASIC_GPU1_18 0x002000
+#define INTC_PENDING_BASIC_GPU1_19 0x004000
+#define INTC_PENDING_BASIC_GPU2_21 0x008000
+#define INTC_PENDING_BASIC_GPU2_22 0x010000
+#define INTC_PENDING_BASIC_GPU2_23 0x020000
+#define INTC_PENDING_BASIC_GPU2_24 0x040000
+#define INTC_PENDING_BASIC_GPU2_25 0x080000
+#define INTC_PENDING_BASIC_GPU2_30 0x100000
+#define INTC_PENDING_BASIC_MASK 0x1FFFFF
+
+#define INTC_PENDING_BASIC_GPU1_MASK (INTC_PENDING_BASIC_GPU1_7 | \
+ INTC_PENDING_BASIC_GPU1_9 | \
+ INTC_PENDING_BASIC_GPU1_10 | \
+ INTC_PENDING_BASIC_GPU1_18 | \
+ INTC_PENDING_BASIC_GPU1_19)
+
+#define INTC_PENDING_BASIC_GPU2_MASK (INTC_PENDING_BASIC_GPU2_21 | \
+ INTC_PENDING_BASIC_GPU2_22 | \
+ INTC_PENDING_BASIC_GPU2_23 | \
+ INTC_PENDING_BASIC_GPU2_24 | \
+ INTC_PENDING_BASIC_GPU2_25 | \
+ INTC_PENDING_BASIC_GPU2_30)
+
+#define INTC_PENDING_BANK1_MASK (~((1 << 7) | (1 << 9) | (1 << 10) | \
+ (1 << 18) | (1 << 19)))
+#define INTC_PENDING_BANK2_MASK (~((1 << 21) | (1 << 22) | (1 << 23) | \
+ (1 << 24) | (1 << 25) | (1 << 30)))
+
+#define BANK1_START 8
+#define BANK1_END (BANK1_START + 32 - 1)
+#define BANK2_START (BANK1_START + 32)
+#define BANK2_END (BANK2_START + 32 - 1)
+
+#define IS_IRQ_BASIC(n) (((n) >= 0) && ((n) < BANK1_START))
+#define IS_IRQ_BANK1(n) (((n) >= BANK1_START) && ((n) <= BANK1_END))
+#define IS_IRQ_BANK2(n) (((n) >= BANK2_START) && ((n) <= BANK2_END))
+#define IRQ_BANK1(n) ((n) - BANK1_START)
+#define IRQ_BANK2(n) ((n) - BANK2_START)
+
+#ifdef DEBUG
+#define dprintf(fmt, args...) printf(fmt, ##args)
+#else
+#define dprintf(fmt, args...)
+#endif
+
+#define BCM_INTC_NIRQS 72 /* 8 + 32 + 32 */
+
+struct bcm_intc_irqsrc {
+ struct intr_irqsrc bii_isrc;
+ u_int bii_irq;
+ uint16_t bii_disable_reg;
+ uint16_t bii_enable_reg;
+ uint32_t bii_mask;
+};
+
+struct bcm_intc_softc {
+ device_t sc_dev;
+ struct resource * intc_res;
+ bus_space_tag_t intc_bst;
+ bus_space_handle_t intc_bsh;
+ struct resource * intc_irq_res;
+ void * intc_irq_hdl;
+ struct bcm_intc_irqsrc intc_isrcs[BCM_INTC_NIRQS];
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"broadcom,bcm2835-armctrl-ic", 1},
+ {"brcm,bcm2835-armctrl-ic", 1},
+ {"brcm,bcm2836-armctrl-ic", 1},
+ {NULL, 0}
+};
+
+static struct bcm_intc_softc *bcm_intc_sc = NULL;
+
+#define intc_read_4(_sc, reg) \
+ bus_space_read_4((_sc)->intc_bst, (_sc)->intc_bsh, (reg))
+#define intc_write_4(_sc, reg, val) \
+ bus_space_write_4((_sc)->intc_bst, (_sc)->intc_bsh, (reg), (val))
+
+static inline void
+bcm_intc_isrc_mask(struct bcm_intc_softc *sc, struct bcm_intc_irqsrc *bii)
+{
+
+ intc_write_4(sc, bii->bii_disable_reg, bii->bii_mask);
+}
+
+static inline void
+bcm_intc_isrc_unmask(struct bcm_intc_softc *sc, struct bcm_intc_irqsrc *bii)
+{
+
+ intc_write_4(sc, bii->bii_enable_reg, bii->bii_mask);
+}
+
+static inline int
+bcm2835_intc_active_intr(struct bcm_intc_softc *sc)
+{
+ uint32_t pending, pending_gpu;
+
+ pending = intc_read_4(sc, INTC_PENDING_BASIC) & INTC_PENDING_BASIC_MASK;
+ if (pending == 0)
+ return (-1);
+ if (pending & INTC_PENDING_BASIC_ARM)
+ return (ffs(pending) - 1);
+ if (pending & INTC_PENDING_BASIC_GPU1_MASK) {
+ if (pending & INTC_PENDING_BASIC_GPU1_7)
+ return (BANK1_START + 7);
+ if (pending & INTC_PENDING_BASIC_GPU1_9)
+ return (BANK1_START + 9);
+ if (pending & INTC_PENDING_BASIC_GPU1_10)
+ return (BANK1_START + 10);
+ if (pending & INTC_PENDING_BASIC_GPU1_18)
+ return (BANK1_START + 18);
+ if (pending & INTC_PENDING_BASIC_GPU1_19)
+ return (BANK1_START + 19);
+ }
+ if (pending & INTC_PENDING_BASIC_GPU2_MASK) {
+ if (pending & INTC_PENDING_BASIC_GPU2_21)
+ return (BANK2_START + 21);
+ if (pending & INTC_PENDING_BASIC_GPU2_22)
+ return (BANK2_START + 22);
+ if (pending & INTC_PENDING_BASIC_GPU2_23)
+ return (BANK2_START + 23);
+ if (pending & INTC_PENDING_BASIC_GPU2_24)
+ return (BANK2_START + 24);
+ if (pending & INTC_PENDING_BASIC_GPU2_25)
+ return (BANK2_START + 25);
+ if (pending & INTC_PENDING_BASIC_GPU2_30)
+ return (BANK2_START + 30);
+ }
+ if (pending & INTC_PENDING_BASIC_GPU1_PEND) {
+ pending_gpu = intc_read_4(sc, INTC_PENDING_BANK1);
+ pending_gpu &= INTC_PENDING_BANK1_MASK;
+ if (pending_gpu != 0)
+ return (BANK1_START + ffs(pending_gpu) - 1);
+ }
+ if (pending & INTC_PENDING_BASIC_GPU2_PEND) {
+ pending_gpu = intc_read_4(sc, INTC_PENDING_BANK2);
+ pending_gpu &= INTC_PENDING_BANK2_MASK;
+ if (pending_gpu != 0)
+ return (BANK2_START + ffs(pending_gpu) - 1);
+ }
+ return (-1); /* It shouldn't end here, but it's hardware. */
+}
+
+static int
+bcm2835_intc_intr(void *arg)
+{
+ int irq, num;
+ struct bcm_intc_softc *sc = arg;
+
+ for (num = 0; ; num++) {
+ irq = bcm2835_intc_active_intr(sc);
+ if (irq == -1)
+ break;
+ if (intr_isrc_dispatch(&sc->intc_isrcs[irq].bii_isrc,
+ curthread->td_intr_frame) != 0) {
+ bcm_intc_isrc_mask(sc, &sc->intc_isrcs[irq]);
+ device_printf(sc->sc_dev, "Stray irq %u disabled\n",
+ irq);
+ }
+ arm_irq_memory_barrier(0); /* XXX */
+ }
+ if (num == 0 && bootverbose)
+ device_printf(sc->sc_dev, "Spurious interrupt detected\n");
+
+ return (FILTER_HANDLED);
+}
+
+static void
+bcm_intc_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct bcm_intc_irqsrc *bii = (struct bcm_intc_irqsrc *)isrc;
+
+ arm_irq_memory_barrier(bii->bii_irq);
+ bcm_intc_isrc_unmask(device_get_softc(dev), bii);
+}
+
+static void
+bcm_intc_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+
+ bcm_intc_isrc_mask(device_get_softc(dev),
+ (struct bcm_intc_irqsrc *)isrc);
+}
+
+static int
+bcm_intc_map_intr(device_t dev, struct intr_map_data *data,
+ struct intr_irqsrc **isrcp)
+{
+ u_int irq;
+ struct intr_map_data_fdt *daf;
+ struct bcm_intc_softc *sc;
+ bool valid;
+
+ if (data->type != INTR_MAP_DATA_FDT)
+ return (ENOTSUP);
+
+ daf = (struct intr_map_data_fdt *)data;
+ if (daf->ncells == 1)
+ irq = daf->cells[0];
+ else if (daf->ncells == 2) {
+ valid = true;
+ switch (daf->cells[0]) {
+ case 0:
+ irq = daf->cells[1];
+ if (irq >= BANK1_START)
+ valid = false;
+ break;
+ case 1:
+ irq = daf->cells[1] + BANK1_START;
+ if (irq > BANK1_END)
+ valid = false;
+ break;
+ case 2:
+ irq = daf->cells[1] + BANK2_START;
+ if (irq > BANK2_END)
+ valid = false;
+ break;
+ default:
+ valid = false;
+ break;
+ }
+
+ if (!valid) {
+ device_printf(dev,
+ "invalid IRQ config: bank=%d, irq=%d\n",
+ daf->cells[0], daf->cells[1]);
+ return (EINVAL);
+ }
+ }
+ else
+ return (EINVAL);
+
+ if (irq >= BCM_INTC_NIRQS)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+ *isrcp = &sc->intc_isrcs[irq].bii_isrc;
+ return (0);
+}
+
+static void
+bcm_intc_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+
+ bcm_intc_disable_intr(dev, isrc);
+}
+
+static void
+bcm_intc_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+
+ bcm_intc_enable_intr(dev, isrc);
+}
+
+static void
+bcm_intc_post_filter(device_t dev, struct intr_irqsrc *isrc)
+{
+}
+
+static int
+bcm_intc_pic_register(struct bcm_intc_softc *sc, intptr_t xref)
+{
+ struct bcm_intc_irqsrc *bii;
+ int error;
+ uint32_t irq;
+ const char *name;
+
+ name = device_get_nameunit(sc->sc_dev);
+ for (irq = 0; irq < BCM_INTC_NIRQS; irq++) {
+ bii = &sc->intc_isrcs[irq];
+ bii->bii_irq = irq;
+ if (IS_IRQ_BASIC(irq)) {
+ bii->bii_disable_reg = INTC_DISABLE_BASIC;
+ bii->bii_enable_reg = INTC_ENABLE_BASIC;
+ bii->bii_mask = 1 << irq;
+ } else if (IS_IRQ_BANK1(irq)) {
+ bii->bii_disable_reg = INTC_DISABLE_BANK1;
+ bii->bii_enable_reg = INTC_ENABLE_BANK1;
+ bii->bii_mask = 1 << IRQ_BANK1(irq);
+ } else if (IS_IRQ_BANK2(irq)) {
+ bii->bii_disable_reg = INTC_DISABLE_BANK2;
+ bii->bii_enable_reg = INTC_ENABLE_BANK2;
+ bii->bii_mask = 1 << IRQ_BANK2(irq);
+ } else
+ return (ENXIO);
+
+ error = intr_isrc_register(&bii->bii_isrc, sc->sc_dev, 0,
+ "%s,%u", name, irq);
+ if (error != 0)
+ return (error);
+ }
+ if (intr_pic_register(sc->sc_dev, xref) == NULL)
+ return (ENXIO);
+
+ return (0);
+}
+
+static int
+bcm_intc_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "BCM2835 Interrupt Controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+bcm_intc_attach(device_t dev)
+{
+ struct bcm_intc_softc *sc = device_get_softc(dev);
+ int rid = 0;
+ intptr_t xref;
+ sc->sc_dev = dev;
+
+ if (bcm_intc_sc)
+ return (ENXIO);
+
+ sc->intc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
+ if (sc->intc_res == NULL) {
+ device_printf(dev, "could not allocate memory resource\n");
+ return (ENXIO);
+ }
+
+ xref = OF_xref_from_node(ofw_bus_get_node(dev));
+ if (bcm_intc_pic_register(sc, xref) != 0) {
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->intc_res);
+ device_printf(dev, "could not register PIC\n");
+ return (ENXIO);
+ }
+
+ rid = 0;
+ sc->intc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (sc->intc_irq_res == NULL) {
+ if (intr_pic_claim_root(dev, xref, bcm2835_intc_intr, sc, 0) != 0) {
+ /* XXX clean up */
+ device_printf(dev, "could not set PIC as a root\n");
+ return (ENXIO);
+ }
+ } else {
+ if (bus_setup_intr(dev, sc->intc_irq_res, INTR_TYPE_CLK,
+ bcm2835_intc_intr, NULL, sc, &sc->intc_irq_hdl)) {
+ /* XXX clean up */
+ device_printf(dev, "could not setup irq handler\n");
+ return (ENXIO);
+ }
+ }
+ sc->intc_bst = rman_get_bustag(sc->intc_res);
+ sc->intc_bsh = rman_get_bushandle(sc->intc_res);
+
+ bcm_intc_sc = sc;
+
+ return (0);
+}
+
+static device_method_t bcm_intc_methods[] = {
+ DEVMETHOD(device_probe, bcm_intc_probe),
+ DEVMETHOD(device_attach, bcm_intc_attach),
+
+ DEVMETHOD(pic_disable_intr, bcm_intc_disable_intr),
+ DEVMETHOD(pic_enable_intr, bcm_intc_enable_intr),
+ DEVMETHOD(pic_map_intr, bcm_intc_map_intr),
+ DEVMETHOD(pic_post_filter, bcm_intc_post_filter),
+ DEVMETHOD(pic_post_ithread, bcm_intc_post_ithread),
+ DEVMETHOD(pic_pre_ithread, bcm_intc_pre_ithread),
+ { 0, 0 }
+};
+
+static driver_t bcm_intc_driver = {
+ "intc",
+ bcm_intc_methods,
+ sizeof(struct bcm_intc_softc),
+};
+
+static devclass_t bcm_intc_devclass;
+
+EARLY_DRIVER_MODULE(intc, simplebus, bcm_intc_driver, bcm_intc_devclass,
+ 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_machdep.c b/sys/arm/broadcom/bcm2835/bcm2835_machdep.c
new file mode 100644
index 000000000000..683fb4986a41
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2835_machdep.c
@@ -0,0 +1,156 @@
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 2012 Oleksandr Tymoshenko.
+ * Copyright (c) 1994-1998 Mark Brinicombe.
+ * Copyright (c) 1994 Brini.
+ * All rights reserved.
+ *
+ * This code is derived from software written for Brini by Mark Brinicombe
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Brini.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: FreeBSD: //depot/projects/arm/src/sys/arm/at91/kb920x_machdep.c, rev 45
+ */
+
+#include "opt_ddb.h"
+#include "opt_platform.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/devmap.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/bus.h>
+#include <machine/machdep.h>
+#include <machine/platform.h>
+#include <machine/platformvar.h>
+
+#include <dev/ofw/openfirm.h>
+
+#include <arm/broadcom/bcm2835/bcm2835_wdog.h>
+#include <arm/broadcom/bcm2835/bcm2836_mp.h>
+
+#include "platform_if.h"
+
+#ifdef SOC_BCM2835
+static platform_devmap_init_t bcm2835_devmap_init;
+#endif
+#ifdef SOC_BCM2836
+static platform_devmap_init_t bcm2836_devmap_init;
+#endif
+static platform_late_init_t bcm2835_late_init;
+static platform_cpu_reset_t bcm2835_cpu_reset;
+
+static void
+bcm2835_late_init(platform_t plat)
+{
+ phandle_t system;
+ pcell_t cells[2];
+ int len;
+
+ system = OF_finddevice("/system");
+ if (system != -1) {
+ len = OF_getencprop(system, "linux,serial", cells,
+ sizeof(cells));
+ if (len > 0)
+ board_set_serial(((uint64_t)cells[0]) << 32 | cells[1]);
+
+ len = OF_getencprop(system, "linux,revision", cells,
+ sizeof(cells));
+ if (len > 0)
+ board_set_revision(cells[0]);
+ }
+}
+
+#ifdef SOC_BCM2835
+/*
+ * Set up static device mappings.
+ * All on-chip peripherals exist in a 16MB range starting at 0x20000000.
+ * Map the entire range using 1MB section mappings.
+ */
+static int
+bcm2835_devmap_init(platform_t plat)
+{
+
+ devmap_add_entry(0x20000000, 0x01000000);
+ return (0);
+}
+#endif
+
+#ifdef SOC_BCM2836
+static int
+bcm2836_devmap_init(platform_t plat)
+{
+
+ devmap_add_entry(0x3f000000, 0x01000000);
+ return (0);
+}
+#endif
+
+static void
+bcm2835_cpu_reset(platform_t plat)
+{
+ bcmwd_watchdog_reset();
+}
+
+#ifdef SOC_BCM2835
+static platform_method_t bcm2835_methods[] = {
+ PLATFORMMETHOD(platform_devmap_init, bcm2835_devmap_init),
+ PLATFORMMETHOD(platform_late_init, bcm2835_late_init),
+ PLATFORMMETHOD(platform_cpu_reset, bcm2835_cpu_reset),
+
+ PLATFORMMETHOD_END,
+};
+FDT_PLATFORM_DEF2(bcm2835, bcm2835_legacy, "bcm2835 (legacy)", 0, "raspberrypi,model-b", 100);
+FDT_PLATFORM_DEF2(bcm2835, bcm2835, "bcm2835", 0, "brcm,bcm2835", 100);
+#endif
+
+#if defined(SOC_BCM2836) || defined(SOC_BRCM_BCM2837)
+static platform_method_t bcm2836_methods[] = {
+ PLATFORMMETHOD(platform_devmap_init, bcm2836_devmap_init),
+ PLATFORMMETHOD(platform_late_init, bcm2835_late_init),
+ PLATFORMMETHOD(platform_cpu_reset, bcm2835_cpu_reset),
+
+#ifdef SMP
+ PLATFORMMETHOD(platform_mp_start_ap, bcm2836_mp_start_ap),
+ PLATFORMMETHOD(platform_mp_setmaxid, bcm2836_mp_setmaxid),
+#endif
+
+ PLATFORMMETHOD_END,
+};
+FDT_PLATFORM_DEF2(bcm2836, bcm2836_legacy, "bcm2836 (legacy)", 0, "brcm,bcm2709", 100);
+FDT_PLATFORM_DEF2(bcm2836, bcm2836, "bcm2836", 0, "brcm,bcm2836", 100);
+FDT_PLATFORM_DEF2(bcm2836, bcm2837, "bcm2837", 0, "brcm,bcm2837", 100);
+#endif /* defined(SOC_BCM2836) || defined(SOC_BRCM_BCM2837) */
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_mbox.c b/sys/arm/broadcom/bcm2835/bcm2835_mbox.c
new file mode 100644
index 000000000000..d31989b7ddeb
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2835_mbox.c
@@ -0,0 +1,594 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/sx.h>
+#include <sys/rman.h>
+#include <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/broadcom/bcm2835/bcm2835_firmware.h>
+#include <arm/broadcom/bcm2835/bcm2835_mbox.h>
+#include <arm/broadcom/bcm2835/bcm2835_mbox_prop.h>
+#include <arm/broadcom/bcm2835/bcm2835_vcbus.h>
+
+#include "mbox_if.h"
+
+#define REG_READ 0x00
+#define REG_POL 0x10
+#define REG_SENDER 0x14
+#define REG_STATUS 0x18
+#define STATUS_FULL 0x80000000
+#define STATUS_EMPTY 0x40000000
+#define REG_CONFIG 0x1C
+#define CONFIG_DATA_IRQ 0x00000001
+#define REG_WRITE 0x20 /* This is Mailbox 1 address */
+
+#define MBOX_MSG(chan, data) (((data) & ~0xf) | ((chan) & 0xf))
+#define MBOX_CHAN(msg) ((msg) & 0xf)
+#define MBOX_DATA(msg) ((msg) & ~0xf)
+
+#define MBOX_LOCK(sc) do { \
+ mtx_lock(&(sc)->lock); \
+} while(0)
+
+#define MBOX_UNLOCK(sc) do { \
+ mtx_unlock(&(sc)->lock); \
+} while(0)
+
+#ifdef DEBUG
+#define dprintf(fmt, args...) printf(fmt, ##args)
+#else
+#define dprintf(fmt, args...)
+#endif
+
+struct bcm_mbox_softc {
+ struct mtx lock;
+ struct resource * mem_res;
+ struct resource * irq_res;
+ void* intr_hl;
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+ int msg[BCM2835_MBOX_CHANS];
+ int have_message[BCM2835_MBOX_CHANS];
+ struct sx property_chan_lock;
+};
+
+#define mbox_read_4(sc, reg) \
+ bus_space_read_4((sc)->bst, (sc)->bsh, reg)
+#define mbox_write_4(sc, reg, val) \
+ bus_space_write_4((sc)->bst, (sc)->bsh, reg, val)
+
+static struct ofw_compat_data compat_data[] = {
+ {"broadcom,bcm2835-mbox", 1},
+ {"brcm,bcm2835-mbox", 1},
+ {NULL, 0}
+};
+
+static int
+bcm_mbox_read_msg(struct bcm_mbox_softc *sc, int *ochan)
+{
+#ifdef DEBUG
+ uint32_t data;
+#endif
+ uint32_t msg;
+ int chan;
+
+ msg = mbox_read_4(sc, REG_READ);
+ dprintf("bcm_mbox_intr: raw data %08x\n", msg);
+ chan = MBOX_CHAN(msg);
+#ifdef DEBUG
+ data = MBOX_DATA(msg);
+#endif
+ if (sc->msg[chan]) {
+ printf("bcm_mbox_intr: channel %d oveflow\n", chan);
+ return (1);
+ }
+ dprintf("bcm_mbox_intr: chan %d, data %08x\n", chan, data);
+ sc->msg[chan] = msg;
+
+ if (ochan != NULL)
+ *ochan = chan;
+
+ return (0);
+}
+
+static void
+bcm_mbox_intr(void *arg)
+{
+ struct bcm_mbox_softc *sc = arg;
+ int chan;
+
+ MBOX_LOCK(sc);
+ while (!(mbox_read_4(sc, REG_STATUS) & STATUS_EMPTY))
+ if (bcm_mbox_read_msg(sc, &chan) == 0) {
+ sc->have_message[chan] = 1;
+ wakeup(&sc->have_message[chan]);
+ }
+ MBOX_UNLOCK(sc);
+}
+
+static int
+bcm_mbox_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "BCM2835 VideoCore Mailbox");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+bcm_mbox_attach(device_t dev)
+{
+ struct bcm_mbox_softc *sc = device_get_softc(dev);
+ int i;
+ int rid = 0;
+
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "could not allocate memory resource\n");
+ return (ENXIO);
+ }
+
+ sc->bst = rman_get_bustag(sc->mem_res);
+ sc->bsh = rman_get_bushandle(sc->mem_res);
+
+ rid = 0;
+ sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
+ if (sc->irq_res == NULL) {
+ device_printf(dev, "could not allocate interrupt resource\n");
+ return (ENXIO);
+ }
+
+ /* Setup and enable the timer */
+ if (bus_setup_intr(dev, sc->irq_res, INTR_MPSAFE | INTR_TYPE_MISC,
+ NULL, bcm_mbox_intr, sc, &sc->intr_hl) != 0) {
+ bus_release_resource(dev, SYS_RES_IRQ, rid, sc->irq_res);
+ device_printf(dev, "Unable to setup the clock irq handler.\n");
+ return (ENXIO);
+ }
+
+ mtx_init(&sc->lock, "vcio mbox", NULL, MTX_DEF);
+ for (i = 0; i < BCM2835_MBOX_CHANS; i++) {
+ sc->msg[i] = 0;
+ sc->have_message[i] = 0;
+ }
+
+ sx_init(&sc->property_chan_lock, "mboxprop");
+
+ /* Read all pending messages */
+ while ((mbox_read_4(sc, REG_STATUS) & STATUS_EMPTY) == 0)
+ (void)mbox_read_4(sc, REG_READ);
+
+ mbox_write_4(sc, REG_CONFIG, CONFIG_DATA_IRQ);
+
+ return (0);
+}
+
+/*
+ * Mailbox API
+ */
+static int
+bcm_mbox_write(device_t dev, int chan, uint32_t data)
+{
+ int limit = 1000;
+ struct bcm_mbox_softc *sc = device_get_softc(dev);
+
+ dprintf("bcm_mbox_write: chan %d, data %08x\n", chan, data);
+ MBOX_LOCK(sc);
+ sc->have_message[chan] = 0;
+ while ((mbox_read_4(sc, REG_STATUS) & STATUS_FULL) && --limit)
+ DELAY(5);
+ if (limit == 0) {
+ printf("bcm_mbox_write: STATUS_FULL stuck");
+ MBOX_UNLOCK(sc);
+ return (EAGAIN);
+ }
+ mbox_write_4(sc, REG_WRITE, MBOX_MSG(chan, data));
+ MBOX_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+bcm_mbox_read(device_t dev, int chan, uint32_t *data)
+{
+ struct bcm_mbox_softc *sc = device_get_softc(dev);
+ int err, read_chan;
+
+ dprintf("bcm_mbox_read: chan %d\n", chan);
+
+ err = 0;
+ MBOX_LOCK(sc);
+ if (!cold) {
+ if (sc->have_message[chan] == 0) {
+ if (mtx_sleep(&sc->have_message[chan], &sc->lock, 0,
+ "mbox", 10*hz) != 0) {
+ device_printf(dev, "timeout waiting for message on chan %d\n", chan);
+ err = ETIMEDOUT;
+ }
+ }
+ } else {
+ do {
+ /* Wait for a message */
+ while ((mbox_read_4(sc, REG_STATUS) & STATUS_EMPTY))
+ ;
+ /* Read the message */
+ if (bcm_mbox_read_msg(sc, &read_chan) != 0) {
+ err = EINVAL;
+ goto out;
+ }
+ } while (read_chan != chan);
+ }
+ /*
+ * get data from intr handler, the same channel is never coming
+ * because of holding sc lock.
+ */
+ *data = MBOX_DATA(sc->msg[chan]);
+ sc->msg[chan] = 0;
+ sc->have_message[chan] = 0;
+out:
+ MBOX_UNLOCK(sc);
+ dprintf("bcm_mbox_read: chan %d, data %08x\n", chan, *data);
+
+ return (err);
+}
+
+static device_method_t bcm_mbox_methods[] = {
+ DEVMETHOD(device_probe, bcm_mbox_probe),
+ DEVMETHOD(device_attach, bcm_mbox_attach),
+
+ DEVMETHOD(mbox_read, bcm_mbox_read),
+ DEVMETHOD(mbox_write, bcm_mbox_write),
+
+ DEVMETHOD_END
+};
+
+static driver_t bcm_mbox_driver = {
+ "mbox",
+ bcm_mbox_methods,
+ sizeof(struct bcm_mbox_softc),
+};
+
+static devclass_t bcm_mbox_devclass;
+
+EARLY_DRIVER_MODULE(mbox, simplebus, bcm_mbox_driver, bcm_mbox_devclass, 0, 0,
+ BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LAST);
+
+static void
+bcm2835_mbox_dma_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err)
+{
+ bus_addr_t *addr;
+
+ if (err)
+ return;
+ addr = (bus_addr_t *)arg;
+ *addr = ARMC_TO_VCBUS(segs[0].ds_addr);
+}
+
+static void *
+bcm2835_mbox_init_dma(device_t dev, size_t len, bus_dma_tag_t *tag,
+ bus_dmamap_t *map, bus_addr_t *phys)
+{
+ void *buf;
+ int err;
+
+ err = bus_dma_tag_create(bus_get_dma_tag(dev), 16, 0,
+ bcm283x_dmabus_peripheral_lowaddr(), BUS_SPACE_MAXADDR, NULL, NULL,
+ len, 1, len, 0, NULL, NULL, tag);
+ if (err != 0) {
+ device_printf(dev, "can't create DMA tag\n");
+ return (NULL);
+ }
+
+ err = bus_dmamem_alloc(*tag, &buf, 0, map);
+ if (err != 0) {
+ bus_dma_tag_destroy(*tag);
+ device_printf(dev, "can't allocate dmamem\n");
+ return (NULL);
+ }
+
+ err = bus_dmamap_load(*tag, *map, buf, len, bcm2835_mbox_dma_cb,
+ phys, 0);
+ if (err != 0) {
+ bus_dmamem_free(*tag, buf, *map);
+ bus_dma_tag_destroy(*tag);
+ device_printf(dev, "can't load DMA map\n");
+ return (NULL);
+ }
+
+ return (buf);
+}
+
+static int
+bcm2835_mbox_err(device_t dev, bus_addr_t msg_phys, uint32_t resp_phys,
+ struct bcm2835_mbox_hdr *msg, size_t len)
+{
+ int idx;
+ struct bcm2835_mbox_tag_hdr *tag;
+ uint8_t *last;
+
+ if ((uint32_t)msg_phys != resp_phys) {
+ device_printf(dev, "response channel mismatch\n");
+ return (EIO);
+ }
+ if (msg->code != BCM2835_MBOX_CODE_RESP_SUCCESS) {
+ device_printf(dev, "mbox response error\n");
+ return (EIO);
+ }
+
+ /* Loop until the end tag. */
+ tag = (struct bcm2835_mbox_tag_hdr *)(msg + 1);
+ last = (uint8_t *)msg + len;
+ for (idx = 0; tag->tag != 0; idx++) {
+ /*
+ * When setting the GPIO config or state the firmware doesn't
+ * set tag->val_len correctly.
+ */
+ if ((tag->tag == BCM2835_FIRMWARE_TAG_SET_GPIO_CONFIG ||
+ tag->tag == BCM2835_FIRMWARE_TAG_SET_GPIO_STATE) &&
+ tag->val_len == 0) {
+ tag->val_len = BCM2835_MBOX_TAG_VAL_LEN_RESPONSE |
+ tag->val_buf_size;
+ }
+ if ((tag->val_len & BCM2835_MBOX_TAG_VAL_LEN_RESPONSE) == 0) {
+ device_printf(dev, "tag %d response error\n", idx);
+ return (EIO);
+ }
+ /* Clear the response bit. */
+ tag->val_len &= ~BCM2835_MBOX_TAG_VAL_LEN_RESPONSE;
+
+ /* Next tag. */
+ tag = (struct bcm2835_mbox_tag_hdr *)((uint8_t *)tag +
+ sizeof(*tag) + tag->val_buf_size);
+
+ if ((uint8_t *)tag > last) {
+ device_printf(dev, "mbox buffer size error\n");
+ return (EIO);
+ }
+ }
+
+ return (0);
+}
+
+int
+bcm2835_mbox_property(void *msg, size_t msg_size)
+{
+ struct bcm_mbox_softc *sc;
+ bus_dma_tag_t msg_tag;
+ bus_dmamap_t msg_map;
+ bus_addr_t msg_phys;
+ char *buf;
+ uint32_t reg;
+ device_t mbox;
+ int err;
+
+ /* get mbox device */
+ mbox = devclass_get_device(devclass_find("mbox"), 0);
+ if (mbox == NULL)
+ return (ENXIO);
+
+ sc = device_get_softc(mbox);
+ sx_xlock(&sc->property_chan_lock);
+
+ /* Allocate memory for the message */
+ buf = bcm2835_mbox_init_dma(mbox, msg_size, &msg_tag, &msg_map,
+ &msg_phys);
+ if (buf == NULL) {
+ err = ENOMEM;
+ goto out;
+ }
+
+ memcpy(buf, msg, msg_size);
+
+ bus_dmamap_sync(msg_tag, msg_map,
+ BUS_DMASYNC_PREWRITE);
+
+ MBOX_WRITE(mbox, BCM2835_MBOX_CHAN_PROP, (uint32_t)msg_phys);
+ MBOX_READ(mbox, BCM2835_MBOX_CHAN_PROP, &reg);
+
+ bus_dmamap_sync(msg_tag, msg_map,
+ BUS_DMASYNC_PREREAD);
+
+ memcpy(msg, buf, msg_size);
+
+ err = bcm2835_mbox_err(mbox, msg_phys, reg,
+ (struct bcm2835_mbox_hdr *)msg, msg_size);
+
+ bus_dmamap_unload(msg_tag, msg_map);
+ bus_dmamem_free(msg_tag, buf, msg_map);
+ bus_dma_tag_destroy(msg_tag);
+out:
+ sx_xunlock(&sc->property_chan_lock);
+ return (err);
+}
+
+int
+bcm2835_mbox_set_power_state(uint32_t device_id, boolean_t on)
+{
+ struct msg_set_power_state msg;
+ int err;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.hdr.buf_size = sizeof(msg);
+ msg.hdr.code = BCM2835_MBOX_CODE_REQ;
+ msg.tag_hdr.tag = BCM2835_MBOX_TAG_SET_POWER_STATE;
+ msg.tag_hdr.val_buf_size = sizeof(msg.body);
+ msg.tag_hdr.val_len = sizeof(msg.body.req);
+ msg.body.req.device_id = device_id;
+ msg.body.req.state = (on ? BCM2835_MBOX_POWER_ON : 0) |
+ BCM2835_MBOX_POWER_WAIT;
+ msg.end_tag = 0;
+
+ err = bcm2835_mbox_property(&msg, sizeof(msg));
+
+ return (err);
+}
+
+int
+bcm2835_mbox_notify_xhci_reset(uint32_t pci_dev_addr)
+{
+ struct msg_notify_xhci_reset msg;
+ int err;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.hdr.buf_size = sizeof(msg);
+ msg.hdr.code = BCM2835_MBOX_CODE_REQ;
+ msg.tag_hdr.tag = BCM2835_MBOX_TAG_NOTIFY_XHCI_RESET;
+ msg.tag_hdr.val_buf_size = sizeof(msg.body);
+ msg.tag_hdr.val_len = sizeof(msg.body.req);
+ msg.body.req.pci_device_addr = pci_dev_addr;
+ msg.end_tag = 0;
+
+ err = bcm2835_mbox_property(&msg, sizeof(msg));
+
+ return (err);
+}
+
+int
+bcm2835_mbox_get_clock_rate(uint32_t clock_id, uint32_t *hz)
+{
+ struct msg_get_clock_rate msg;
+ int err;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.hdr.buf_size = sizeof(msg);
+ msg.hdr.code = BCM2835_MBOX_CODE_REQ;
+ msg.tag_hdr.tag = BCM2835_MBOX_TAG_GET_CLOCK_RATE;
+ msg.tag_hdr.val_buf_size = sizeof(msg.body);
+ msg.tag_hdr.val_len = sizeof(msg.body.req);
+ msg.body.req.clock_id = clock_id;
+ msg.end_tag = 0;
+
+ err = bcm2835_mbox_property(&msg, sizeof(msg));
+ *hz = msg.body.resp.rate_hz;
+
+ return (err);
+}
+
+int
+bcm2835_mbox_fb_get_w_h(struct bcm2835_fb_config *fb)
+{
+ int err;
+ struct msg_fb_get_w_h msg;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.hdr.buf_size = sizeof(msg);
+ msg.hdr.code = BCM2835_MBOX_CODE_REQ;
+ BCM2835_MBOX_INIT_TAG(&msg.physical_w_h, GET_PHYSICAL_W_H);
+ msg.physical_w_h.tag_hdr.val_len = 0;
+ msg.end_tag = 0;
+
+ err = bcm2835_mbox_property(&msg, sizeof(msg));
+ if (err == 0) {
+ fb->xres = msg.physical_w_h.body.resp.width;
+ fb->yres = msg.physical_w_h.body.resp.height;
+ }
+
+ return (err);
+}
+
+int
+bcm2835_mbox_fb_get_bpp(struct bcm2835_fb_config *fb)
+{
+ int err;
+ struct msg_fb_get_bpp msg;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.hdr.buf_size = sizeof(msg);
+ msg.hdr.code = BCM2835_MBOX_CODE_REQ;
+ BCM2835_MBOX_INIT_TAG(&msg.bpp, GET_DEPTH);
+ msg.bpp.tag_hdr.val_len = 0;
+ msg.end_tag = 0;
+
+ err = bcm2835_mbox_property(&msg, sizeof(msg));
+ if (err == 0)
+ fb->bpp = msg.bpp.body.resp.bpp;
+
+ return (err);
+}
+
+int
+bcm2835_mbox_fb_init(struct bcm2835_fb_config *fb)
+{
+ int err;
+ struct msg_fb_setup msg;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.hdr.buf_size = sizeof(msg);
+ msg.hdr.code = BCM2835_MBOX_CODE_REQ;
+ BCM2835_MBOX_INIT_TAG(&msg.physical_w_h, SET_PHYSICAL_W_H);
+ msg.physical_w_h.body.req.width = fb->xres;
+ msg.physical_w_h.body.req.height = fb->yres;
+ BCM2835_MBOX_INIT_TAG(&msg.virtual_w_h, SET_VIRTUAL_W_H);
+ msg.virtual_w_h.body.req.width = fb->vxres;
+ msg.virtual_w_h.body.req.height = fb->vyres;
+ BCM2835_MBOX_INIT_TAG(&msg.offset, SET_VIRTUAL_OFFSET);
+ msg.offset.body.req.x = fb->xoffset;
+ msg.offset.body.req.y = fb->yoffset;
+ BCM2835_MBOX_INIT_TAG(&msg.depth, SET_DEPTH);
+ msg.depth.body.req.bpp = fb->bpp;
+ BCM2835_MBOX_INIT_TAG(&msg.alpha, SET_ALPHA_MODE);
+ msg.alpha.body.req.alpha = BCM2835_MBOX_ALPHA_MODE_IGNORED;
+ BCM2835_MBOX_INIT_TAG(&msg.buffer, ALLOCATE_BUFFER);
+ msg.buffer.body.req.alignment = PAGE_SIZE;
+ BCM2835_MBOX_INIT_TAG(&msg.pitch, GET_PITCH);
+ msg.end_tag = 0;
+
+ err = bcm2835_mbox_property(&msg, sizeof(msg));
+ if (err == 0) {
+ fb->xres = msg.physical_w_h.body.resp.width;
+ fb->yres = msg.physical_w_h.body.resp.height;
+ fb->vxres = msg.virtual_w_h.body.resp.width;
+ fb->vyres = msg.virtual_w_h.body.resp.height;
+ fb->xoffset = msg.offset.body.resp.x;
+ fb->yoffset = msg.offset.body.resp.y;
+ fb->pitch = msg.pitch.body.resp.pitch;
+ fb->base = VCBUS_TO_ARMC(msg.buffer.body.resp.fb_address);
+ fb->size = msg.buffer.body.resp.fb_size;
+ }
+
+ return (err);
+}
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_mbox.h b/sys/arm/broadcom/bcm2835/bcm2835_mbox.h
new file mode 100644
index 000000000000..3cd8699a704b
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2835_mbox.h
@@ -0,0 +1,44 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _BCM2835_MBOX_H_
+#define _BCM2835_MBOX_H_
+
+#define BCM2835_MBOX_CHAN_POWER 0
+#define BCM2835_MBOX_CHAN_FB 1
+#define BCM2835_MBOX_CHAN_VUART 2
+#define BCM2835_MBOX_CHAN_VCHIQ 3
+#define BCM2835_MBOX_CHAN_LEDS 4
+#define BCM2835_MBOX_CHAN_BUTTONS 5
+#define BCM2835_MBOX_CHAN_TS 6
+#define BCM2835_MBOX_CHAN_PROP 8
+#define BCM2835_MBOX_CHANS 9
+
+#endif /* _BCM2835_MBOX_H_ */
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_mbox_prop.h b/sys/arm/broadcom/bcm2835/bcm2835_mbox_prop.h
new file mode 100644
index 000000000000..df8c96d68f25
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2835_mbox_prop.h
@@ -0,0 +1,464 @@
+/*-
+ * Copyright (C) 2013-2014 Daisuke Aoyama <aoyama@peach.ne.jp>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _BCM2835_MBOX_PROP_H_
+#define _BCM2835_MBOX_PROP_H_
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+/*
+ * Mailbox property interface:
+ * https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface
+ */
+#define BCM2835_MBOX_CODE_REQ 0
+#define BCM2835_MBOX_CODE_RESP_SUCCESS 0x80000000
+#define BCM2835_MBOX_CODE_RESP_ERROR 0x80000001
+#define BCM2835_MBOX_TAG_VAL_LEN_RESPONSE 0x80000000
+
+struct bcm2835_mbox_hdr {
+ uint32_t buf_size;
+ uint32_t code;
+};
+
+struct bcm2835_mbox_tag_hdr {
+ uint32_t tag;
+ uint32_t val_buf_size;
+ uint32_t val_len;
+};
+
+#define BCM2835_MBOX_INIT_TAG(tag_, tagid_) do { \
+ (tag_)->tag_hdr.tag = BCM2835_MBOX_TAG_##tagid_; \
+ (tag_)->tag_hdr.val_buf_size = sizeof((tag_)->body); \
+ (tag_)->tag_hdr.val_len = sizeof((tag_)->body.req); \
+} while (0)
+
+#define BCM2835_MBOX_TAG_FIRMWARE_REVISION 0x00000001
+
+#define BCM2835_MBOX_POWER_ID_EMMC 0x00000000
+#define BCM2835_MBOX_POWER_ID_UART0 0x00000001
+#define BCM2835_MBOX_POWER_ID_UART1 0x00000002
+#define BCM2835_MBOX_POWER_ID_USB_HCD 0x00000003
+#define BCM2835_MBOX_POWER_ID_I2C0 0x00000004
+#define BCM2835_MBOX_POWER_ID_I2C1 0x00000005
+#define BCM2835_MBOX_POWER_ID_I2C2 0x00000006
+#define BCM2835_MBOX_POWER_ID_SPI 0x00000007
+#define BCM2835_MBOX_POWER_ID_CCP2TX 0x00000008
+
+#define BCM2835_MBOX_POWER_ON (1 << 0)
+#define BCM2835_MBOX_POWER_WAIT (1 << 1)
+
+#define BCM2835_MBOX_TAG_GET_POWER_STATE 0x00020001
+#define BCM2835_MBOX_TAG_SET_POWER_STATE 0x00028001
+
+struct msg_get_power_state {
+ struct bcm2835_mbox_hdr hdr;
+ struct bcm2835_mbox_tag_hdr tag_hdr;
+ union {
+ struct {
+ uint32_t device_id;
+ } req;
+ struct {
+ uint32_t device_id;
+ uint32_t state;
+ } resp;
+ } body;
+ uint32_t end_tag;
+};
+
+struct msg_set_power_state {
+ struct bcm2835_mbox_hdr hdr;
+ struct bcm2835_mbox_tag_hdr tag_hdr;
+ union {
+ struct {
+ uint32_t device_id;
+ uint32_t state;
+ } req;
+ struct {
+ uint32_t device_id;
+ uint32_t state;
+ } resp;
+ } body;
+ uint32_t end_tag;
+};
+
+/* Sets the power state for a given device */
+int bcm2835_mbox_set_power_state(uint32_t, boolean_t);
+
+#define BCM2835_MBOX_TAG_NOTIFY_XHCI_RESET 0x00030058
+
+struct msg_notify_xhci_reset {
+ struct bcm2835_mbox_hdr hdr;
+ struct bcm2835_mbox_tag_hdr tag_hdr;
+ union {
+ struct {
+ uint32_t pci_device_addr;
+ } req;
+ struct {
+ } resp;
+ } body;
+ uint32_t end_tag;
+};
+
+/* Prompts the VideoCore processor to reload the xhci firmware. */
+int bcm2835_mbox_notify_xhci_reset(uint32_t);
+
+#define BCM2835_MBOX_CLOCK_ID_EMMC 0x00000001
+#define BCM2838_MBOX_CLOCK_ID_EMMC2 0x0000000c
+
+#define BCM2835_MBOX_TAG_GET_CLOCK_RATE 0x00030002
+
+struct msg_get_clock_rate {
+ struct bcm2835_mbox_hdr hdr;
+ struct bcm2835_mbox_tag_hdr tag_hdr;
+ union {
+ struct {
+ uint32_t clock_id;
+ } req;
+ struct {
+ uint32_t clock_id;
+ uint32_t rate_hz;
+ } resp;
+ } body;
+ uint32_t end_tag;
+};
+
+int bcm2835_mbox_get_clock_rate(uint32_t, uint32_t *);
+
+#define BCM2835_MBOX_TURBO_ON 1
+#define BCM2835_MBOX_TURBO_OFF 0
+
+#define BCM2835_MBOX_TAG_GET_TURBO 0x00030009
+#define BCM2835_MBOX_TAG_SET_TURBO 0x00038009
+
+struct msg_get_turbo {
+ struct bcm2835_mbox_hdr hdr;
+ struct bcm2835_mbox_tag_hdr tag_hdr;
+ union {
+ struct {
+ uint32_t id;
+ } req;
+ struct {
+ uint32_t id;
+ uint32_t level;
+ } resp;
+ } body;
+ uint32_t end_tag;
+};
+
+struct msg_set_turbo {
+ struct bcm2835_mbox_hdr hdr;
+ struct bcm2835_mbox_tag_hdr tag_hdr;
+ union {
+ struct {
+ uint32_t id;
+ uint32_t level;
+ } req;
+ struct {
+ uint32_t id;
+ uint32_t level;
+ } resp;
+ } body;
+ uint32_t end_tag;
+};
+
+#define BCM2835_MBOX_VOLTAGE_ID_CORE 0x00000001
+#define BCM2835_MBOX_VOLTAGE_ID_SDRAM_C 0x00000002
+#define BCM2835_MBOX_VOLTAGE_ID_SDRAM_P 0x00000003
+#define BCM2835_MBOX_VOLTAGE_ID_SDRAM_I 0x00000004
+
+#define BCM2835_MBOX_TAG_GET_VOLTAGE 0x00030003
+#define BCM2835_MBOX_TAG_SET_VOLTAGE 0x00038003
+#define BCM2835_MBOX_TAG_GET_MAX_VOLTAGE 0x00030005
+#define BCM2835_MBOX_TAG_GET_MIN_VOLTAGE 0x00030008
+
+struct msg_get_voltage {
+ struct bcm2835_mbox_hdr hdr;
+ struct bcm2835_mbox_tag_hdr tag_hdr;
+ union {
+ struct {
+ uint32_t voltage_id;
+ } req;
+ struct {
+ uint32_t voltage_id;
+ uint32_t value;
+ } resp;
+ } body;
+ uint32_t end_tag;
+};
+
+struct msg_set_voltage {
+ struct bcm2835_mbox_hdr hdr;
+ struct bcm2835_mbox_tag_hdr tag_hdr;
+ union {
+ struct {
+ uint32_t voltage_id;
+ uint32_t value;
+ } req;
+ struct {
+ uint32_t voltage_id;
+ uint32_t value;
+ } resp;
+ } body;
+ uint32_t end_tag;
+};
+
+struct msg_get_max_voltage {
+ struct bcm2835_mbox_hdr hdr;
+ struct bcm2835_mbox_tag_hdr tag_hdr;
+ union {
+ struct {
+ uint32_t voltage_id;
+ } req;
+ struct {
+ uint32_t voltage_id;
+ uint32_t value;
+ } resp;
+ } body;
+ uint32_t end_tag;
+};
+
+struct msg_get_min_voltage {
+ struct bcm2835_mbox_hdr hdr;
+ struct bcm2835_mbox_tag_hdr tag_hdr;
+ union {
+ struct {
+ uint32_t voltage_id;
+ } req;
+ struct {
+ uint32_t voltage_id;
+ uint32_t value;
+ } resp;
+ } body;
+ uint32_t end_tag;
+};
+
+#define BCM2835_MBOX_TAG_GET_TEMPERATURE 0x00030006
+#define BCM2835_MBOX_TAG_GET_MAX_TEMPERATURE 0x0003000a
+
+struct msg_get_temperature {
+ struct bcm2835_mbox_hdr hdr;
+ struct bcm2835_mbox_tag_hdr tag_hdr;
+ union {
+ struct {
+ uint32_t temperature_id;
+ } req;
+ struct {
+ uint32_t temperature_id;
+ uint32_t value;
+ } resp;
+ } body;
+ uint32_t end_tag;
+};
+
+struct msg_get_max_temperature {
+ struct bcm2835_mbox_hdr hdr;
+ struct bcm2835_mbox_tag_hdr tag_hdr;
+ union {
+ struct {
+ uint32_t temperature_id;
+ } req;
+ struct {
+ uint32_t temperature_id;
+ uint32_t value;
+ } resp;
+ } body;
+ uint32_t end_tag;
+};
+
+#define BCM2835_MBOX_TAG_GET_PHYSICAL_W_H 0x00040003
+#define BCM2835_MBOX_TAG_SET_PHYSICAL_W_H 0x00048003
+#define BCM2835_MBOX_TAG_GET_VIRTUAL_W_H 0x00040004
+#define BCM2835_MBOX_TAG_SET_VIRTUAL_W_H 0x00048004
+
+struct bcm2835_mbox_tag_fb_w_h {
+ struct bcm2835_mbox_tag_hdr tag_hdr;
+ union {
+ struct {
+ uint32_t width;
+ uint32_t height;
+ } req;
+ struct {
+ uint32_t width;
+ uint32_t height;
+ } resp;
+ } body;
+};
+
+#define BCM2835_MBOX_TAG_GET_DEPTH 0x00040005
+#define BCM2835_MBOX_TAG_SET_DEPTH 0x00048005
+
+struct bcm2835_mbox_tag_depth {
+ struct bcm2835_mbox_tag_hdr tag_hdr;
+ union {
+ struct {
+ uint32_t bpp;
+ } req;
+ struct {
+ uint32_t bpp;
+ } resp;
+ } body;
+};
+
+#define BCM2835_MBOX_TAG_GET_ALPHA_MODE 0x00040007
+#define BCM2835_MBOX_TAG_SET_ALPHA_MODE 0x00048007
+
+#define BCM2835_MBOX_ALPHA_MODE_0_OPAQUE 0
+#define BCM2835_MBOX_ALPHA_MODE_0_TRANSPARENT 1
+#define BCM2835_MBOX_ALPHA_MODE_IGNORED 2
+
+struct bcm2835_mbox_tag_alpha_mode {
+ struct bcm2835_mbox_tag_hdr tag_hdr;
+ union {
+ struct {
+ uint32_t alpha;
+ } req;
+ struct {
+ uint32_t alpha;
+ } resp;
+ } body;
+};
+
+#define BCM2835_MBOX_TAG_GET_VIRTUAL_OFFSET 0x00040009
+#define BCM2835_MBOX_TAG_SET_VIRTUAL_OFFSET 0x00048009
+
+struct bcm2835_mbox_tag_virtual_offset {
+ struct bcm2835_mbox_tag_hdr tag_hdr;
+ union {
+ struct {
+ uint32_t x;
+ uint32_t y;
+ } req;
+ struct {
+ uint32_t x;
+ uint32_t y;
+ } resp;
+ } body;
+};
+
+#define BCM2835_MBOX_TAG_GET_PITCH 0x00040008
+
+struct bcm2835_mbox_tag_pitch {
+ struct bcm2835_mbox_tag_hdr tag_hdr;
+ union {
+ struct {
+ } req;
+ struct {
+ uint32_t pitch;
+ } resp;
+ } body;
+};
+
+#define BCM2835_MBOX_TAG_ALLOCATE_BUFFER 0x00040001
+
+struct bcm2835_mbox_tag_allocate_buffer {
+ struct bcm2835_mbox_tag_hdr tag_hdr;
+ union {
+ struct {
+ uint32_t alignment;
+ } req;
+ struct {
+ uint32_t fb_address;
+ uint32_t fb_size;
+ } resp;
+ } body;
+};
+
+#define BCM2835_MBOX_TAG_RELEASE_BUFFER 0x00048001
+
+struct bcm2835_mbox_tag_release_buffer {
+ struct bcm2835_mbox_tag_hdr tag_hdr;
+ union {
+ struct {
+ } req;
+ struct {
+ } resp;
+ } body;
+};
+
+#define BCM2835_MBOX_TAG_GET_TOUCHBUF 0x0004000f
+
+struct bcm2835_mbox_tag_touchbuf {
+ struct bcm2835_mbox_hdr hdr;
+ struct bcm2835_mbox_tag_hdr tag_hdr;
+ union {
+ struct {
+ } req;
+ struct {
+ uint32_t address;
+ } resp;
+ } body;
+ uint32_t end_tag;
+};
+
+struct bcm2835_fb_config {
+ uint32_t xres;
+ uint32_t yres;
+ uint32_t vxres;
+ uint32_t vyres;
+ uint32_t xoffset;
+ uint32_t yoffset;
+ uint32_t bpp;
+ uint32_t pitch;
+ uint32_t base;
+ uint32_t size;
+};
+
+struct msg_fb_get_w_h {
+ struct bcm2835_mbox_hdr hdr;
+ struct bcm2835_mbox_tag_fb_w_h physical_w_h;
+ uint32_t end_tag;
+};
+
+int bcm2835_mbox_fb_get_w_h(struct bcm2835_fb_config *);
+
+struct msg_fb_get_bpp {
+ struct bcm2835_mbox_hdr hdr;
+ struct bcm2835_mbox_tag_depth bpp;
+ uint32_t end_tag;
+};
+
+int bcm2835_mbox_fb_get_bpp(struct bcm2835_fb_config *);
+
+struct msg_fb_setup {
+ struct bcm2835_mbox_hdr hdr;
+ struct bcm2835_mbox_tag_fb_w_h physical_w_h;
+ struct bcm2835_mbox_tag_fb_w_h virtual_w_h;
+ struct bcm2835_mbox_tag_virtual_offset offset;
+ struct bcm2835_mbox_tag_depth depth;
+ struct bcm2835_mbox_tag_alpha_mode alpha;
+ struct bcm2835_mbox_tag_allocate_buffer buffer;
+ struct bcm2835_mbox_tag_pitch pitch;
+ uint32_t end_tag;
+};
+
+int bcm2835_mbox_fb_init(struct bcm2835_fb_config *);
+
+int bcm2835_mbox_property(void *, size_t);
+
+#endif /* _BCM2835_MBOX_PROP_H_ */
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_pwm.c b/sys/arm/broadcom/bcm2835/bcm2835_pwm.c
new file mode 100644
index 000000000000..348c1435f85d
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2835_pwm.c
@@ -0,0 +1,512 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2017 Poul-Henning Kamp <phk@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/broadcom/bcm2835/bcm2835_clkman.h>
+
+static struct ofw_compat_data compat_data[] = {
+ {"broadcom,bcm2835-pwm", 1},
+ {"brcm,bcm2835-pwm", 1},
+ {NULL, 0}
+};
+
+struct bcm_pwm_softc {
+ device_t sc_dev;
+
+ struct resource * sc_mem_res;
+ bus_space_tag_t sc_m_bst;
+ bus_space_handle_t sc_m_bsh;
+
+ device_t clkman;
+
+ uint32_t freq; /* shared between channels 1 and 2 */
+ uint32_t period; /* channel 1 */
+ uint32_t ratio;
+ uint32_t mode;
+ uint32_t period2; /* channel 2 */
+ uint32_t ratio2;
+ uint32_t mode2;
+};
+
+#define BCM_PWM_MEM_WRITE(_sc, _off, _val) \
+ bus_space_write_4(_sc->sc_m_bst, _sc->sc_m_bsh, _off, _val)
+#define BCM_PWM_MEM_READ(_sc, _off) \
+ bus_space_read_4(_sc->sc_m_bst, _sc->sc_m_bsh, _off)
+#define BCM_PWM_CLK_WRITE(_sc, _off, _val) \
+ bus_space_write_4(_sc->sc_c_bst, _sc->sc_c_bsh, _off, _val)
+#define BCM_PWM_CLK_READ(_sc, _off) \
+ bus_space_read_4(_sc->sc_c_bst, _sc->sc_c_bsh, _off)
+
+#define W_CTL(_sc, _val) BCM_PWM_MEM_WRITE(_sc, 0x00, _val)
+#define R_CTL(_sc) BCM_PWM_MEM_READ(_sc, 0x00)
+#define W_STA(_sc, _val) BCM_PWM_MEM_WRITE(_sc, 0x04, _val)
+#define R_STA(_sc) BCM_PWM_MEM_READ(_sc, 0x04)
+#define W_RNG(_sc, _val) BCM_PWM_MEM_WRITE(_sc, 0x10, _val)
+#define R_RNG(_sc) BCM_PWM_MEM_READ(_sc, 0x10)
+#define W_DAT(_sc, _val) BCM_PWM_MEM_WRITE(_sc, 0x14, _val)
+#define R_DAT(_sc) BCM_PWM_MEM_READ(_sc, 0x14)
+#define W_RNG2(_sc, _val) BCM_PWM_MEM_WRITE(_sc, 0x20, _val)
+#define R_RNG2(_sc) BCM_PWM_MEM_READ(_sc, 0x20)
+#define W_DAT2(_sc, _val) BCM_PWM_MEM_WRITE(_sc, 0x24, _val)
+#define R_DAT2(_sc) BCM_PWM_MEM_READ(_sc, 0x24)
+
+static int
+bcm_pwm_reconf(struct bcm_pwm_softc *sc)
+{
+ uint32_t u, ctlr;
+
+ /* Disable PWM */
+ W_CTL(sc, 0);
+
+ /* Stop PWM clock */
+ (void)bcm2835_clkman_set_frequency(sc->clkman, BCM_PWM_CLKSRC, 0);
+
+ ctlr = 0; /* pre-assign zero, enable bits, write to CTL at end */
+
+ if (sc->mode == 0 && sc->mode2 == 0) /* both modes are zero */
+ return 0; /* device is now off - return */
+
+ /* set the PWM clock frequency */
+ /* TODO: should I only do this if it changes and not stop it first? */
+ u = bcm2835_clkman_set_frequency(sc->clkman, BCM_PWM_CLKSRC, sc->freq);
+ if (u == 0)
+ return (EINVAL);
+ sc->freq = u;
+
+ /* control register CTL bits:
+ * (from BCM2835 ARM Peripherals manual, section 9.6)
+ *
+ * 15 MSEN2 chan 2 M/S enable; 0 for PWM algo, 1 for M/S transmission
+ * 14 unused; always reads as 0
+ * 13 USEF2 chan 2 use FIFO (0 uses data; 1 uses FIFO)
+ * 12 POLA2 chan 2 invert polarity (0 normal, 1 inverted polarity)
+ * 11 SBIT2 chan 2 'Silence' bit (when not transmitting data)
+ * 10 RPTL2 chan 2 FIFO repeat last data (1 repeats, 0 interrupts)
+ * 9 MODE2 chan 2 PWM/Serializer mode (0 PWM, 1 Serializer)
+ * 8 PWEN2 chan 2 enable (0 disable, 1 enable)
+ * 7 MSEN1 chan 1 M/S enable; 0 for PWM algo, 1 for M/S transmission
+ * 6 CLRF1 chan 1 clear FIFO (set 1 to clear; always reads as 0)
+ * 5 USEF1 chan 1 use FIFO (0 uses data; 1 uses FIFO)
+ * 4 POLA1 chan 1 invert polarity (0 normal, 1 inverted polarity)
+ * 3 SBIT1 chan 1 'Silence' bit (when not transmitting data)
+ * 2 RTPL1 chan 1 FIFO repeat last data (1 repeats, 0 interrupts)
+ * 1 MODE1 chan 1 PWM/Serializer mode (0 PWM, 1 Serializer)
+ * 0 PWMEN1 chan 1 enable (0 disable, 1 enable)
+ *
+ * Notes on M/S enable: when this bit is '1', a simple M/S ratio is used. In short,
+ * the value of 'ratio' is the number of 'on' bits, and the total length of the data is
+ * defined by 'period'. So if 'ratio' is 2500 and 'period' is 10000, then the output
+ * remains 'on' for 2500 clocks, and goes 'off' for the remaining 7500 clocks.
+ * When the M/S enable is '0', a more complicated algorithm effectively 'dithers' the
+ * pulses in order to obtain the desired ratio. For details, see section 9.3 of the
+ * BCM2835 ARM Peripherals manual.
+ */
+
+ if (sc->mode != 0) {
+ /* Config PWM Channel 1 */
+ W_RNG(sc, sc->period);
+ if (sc->ratio > sc->period)
+ sc->ratio = sc->period;
+ W_DAT(sc, sc->ratio);
+
+ /* Start PWM Channel 1 */
+ if (sc->mode == 1)
+ ctlr |= 0x81; /* chan 1 enable + chan 1 M/S enable */
+ else
+ ctlr |= 0x1; /* chan 1 enable */
+ }
+
+ if (sc->mode2 != 0) {
+ /* Config PWM Channel 2 */
+ W_RNG2(sc, sc->period2);
+ if (sc->ratio2 > sc->period2)
+ sc->ratio2 = sc->period2;
+ W_DAT2(sc, sc->ratio2);
+
+ /* Start PWM Channel 2 */
+ if (sc->mode2 == 1)
+ ctlr |= 0x8100; /* chan 2 enable + chan 2 M/S enable */
+ else
+ ctlr |= 0x100; /* chan 2 enable */
+ }
+
+ /* write CTL register with updated value */
+ W_CTL(sc, ctlr);
+
+ return (0);
+}
+
+static int
+bcm_pwm_pwm_freq_proc(SYSCTL_HANDLER_ARGS)
+{
+ struct bcm_pwm_softc *sc;
+ uint32_t r;
+ int error;
+
+ sc = (struct bcm_pwm_softc *)arg1;
+ if (sc->mode == 1)
+ r = sc->freq / sc->period;
+ else
+ r = 0;
+ error = sysctl_handle_int(oidp, &r, sizeof(r), req);
+ return (error);
+}
+
+static int
+bcm_pwm_mode_proc(SYSCTL_HANDLER_ARGS)
+{
+ struct bcm_pwm_softc *sc;
+ uint32_t r;
+ int error;
+
+ sc = (struct bcm_pwm_softc *)arg1;
+ r = sc->mode;
+ error = sysctl_handle_int(oidp, &r, sizeof(r), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+ if (r > 2)
+ return (EINVAL);
+ sc->mode = r;
+ return (bcm_pwm_reconf(sc));
+}
+
+static int
+bcm_pwm_freq_proc(SYSCTL_HANDLER_ARGS)
+{
+ struct bcm_pwm_softc *sc;
+ uint32_t r;
+ int error;
+
+ sc = (struct bcm_pwm_softc *)arg1;
+ r = sc->freq;
+ error = sysctl_handle_int(oidp, &r, sizeof(r), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+ if (r > 125000000)
+ return (EINVAL);
+ sc->freq = r;
+ return (bcm_pwm_reconf(sc));
+}
+
+static int
+bcm_pwm_period_proc(SYSCTL_HANDLER_ARGS)
+{
+ struct bcm_pwm_softc *sc;
+ int error;
+
+ sc = (struct bcm_pwm_softc *)arg1;
+ error = sysctl_handle_int(oidp, &sc->period, sizeof(sc->period), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+ return (bcm_pwm_reconf(sc));
+}
+
+static int
+bcm_pwm_ratio_proc(SYSCTL_HANDLER_ARGS)
+{
+ struct bcm_pwm_softc *sc;
+ uint32_t r;
+ int error;
+
+ sc = (struct bcm_pwm_softc *)arg1;
+ r = sc->ratio;
+ error = sysctl_handle_int(oidp, &r, sizeof(r), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+ if (r > sc->period) // XXX >= ?
+ return (EINVAL);
+ sc->ratio = r;
+ W_DAT(sc, sc->ratio);
+ return (0);
+}
+
+static int
+bcm_pwm_pwm_freq2_proc(SYSCTL_HANDLER_ARGS)
+{
+ struct bcm_pwm_softc *sc;
+ uint32_t r;
+ int error;
+
+ sc = (struct bcm_pwm_softc *)arg1;
+ if (sc->mode2 == 1)
+ r = sc->freq / sc->period2;
+ else
+ r = 0;
+ error = sysctl_handle_int(oidp, &r, sizeof(r), req);
+ return (error);
+}
+
+static int
+bcm_pwm_mode2_proc(SYSCTL_HANDLER_ARGS)
+{
+ struct bcm_pwm_softc *sc;
+ uint32_t r;
+ int error;
+
+ sc = (struct bcm_pwm_softc *)arg1;
+ r = sc->mode2;
+ error = sysctl_handle_int(oidp, &r, sizeof(r), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+ if (r > 2)
+ return (EINVAL);
+ sc->mode2 = r;
+ return (bcm_pwm_reconf(sc));
+}
+
+static int
+bcm_pwm_period2_proc(SYSCTL_HANDLER_ARGS)
+{
+ struct bcm_pwm_softc *sc;
+ int error;
+
+ sc = (struct bcm_pwm_softc *)arg1;
+ error = sysctl_handle_int(oidp, &sc->period2, sizeof(sc->period2), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+ return (bcm_pwm_reconf(sc));
+}
+
+static int
+bcm_pwm_ratio2_proc(SYSCTL_HANDLER_ARGS)
+{
+ struct bcm_pwm_softc *sc;
+ uint32_t r;
+ int error;
+
+ sc = (struct bcm_pwm_softc *)arg1;
+ r = sc->ratio2;
+ error = sysctl_handle_int(oidp, &r, sizeof(r), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+ if (r > sc->period2) // XXX >= ?
+ return (EINVAL);
+ sc->ratio2 = r;
+ W_DAT(sc, sc->ratio2);
+ return (0);
+}
+
+static int
+bcm_pwm_reg_proc(SYSCTL_HANDLER_ARGS)
+{
+ struct bcm_pwm_softc *sc;
+ uint32_t reg;
+ int error;
+
+ sc = (struct bcm_pwm_softc *)arg1;
+ reg = BCM_PWM_MEM_READ(sc, arg2 & 0xff);
+
+ error = sysctl_handle_int(oidp, &reg, sizeof(reg), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ BCM_PWM_MEM_WRITE(sc, arg2, reg);
+ return (0);
+}
+
+static void
+bcm_pwm_sysctl_init(struct bcm_pwm_softc *sc)
+{
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid *tree_node;
+ struct sysctl_oid_list *tree;
+
+ /*
+ * Add system sysctl tree/handlers.
+ */
+ ctx = device_get_sysctl_ctx(sc->sc_dev);
+ tree_node = device_get_sysctl_tree(sc->sc_dev);
+ tree = SYSCTL_CHILDREN(tree_node);
+ if (bootverbose) {
+#define RR(x,y) \
+ SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, y, \
+ CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_NEEDGIANT, \
+ sc, 0x##x, \
+ bcm_pwm_reg_proc, "IU", "Register 0x" #x " " y);
+
+ RR(24, "DAT2")
+ RR(20, "RNG2")
+ RR(18, "FIF1")
+ RR(14, "DAT1")
+ RR(10, "RNG1")
+ RR(08, "DMAC")
+ RR(04, "STA")
+ RR(00, "CTL")
+#undef RR
+ }
+
+ SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "pwm_freq",
+ CTLFLAG_RD | CTLTYPE_UINT | CTLFLAG_NEEDGIANT, sc, 0,
+ bcm_pwm_pwm_freq_proc, "IU", "PWM frequency ch 1 (Hz)");
+ SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "period",
+ CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_NEEDGIANT, sc, 0,
+ bcm_pwm_period_proc, "IU", "PWM period ch 1 (#clocks)");
+ SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "ratio",
+ CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_NEEDGIANT, sc, 0,
+ bcm_pwm_ratio_proc, "IU", "PWM ratio ch 1 (0...period)");
+ SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "freq",
+ CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_NEEDGIANT, sc, 0,
+ bcm_pwm_freq_proc, "IU", "PWM clock (Hz)");
+ SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "mode",
+ CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_NEEDGIANT, sc, 0,
+ bcm_pwm_mode_proc, "IU", "PWM mode ch 1 (0=off, 1=pwm, 2=dither)");
+ SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "pwm_freq2",
+ CTLFLAG_RD | CTLTYPE_UINT | CTLFLAG_NEEDGIANT, sc, 0,
+ bcm_pwm_pwm_freq2_proc, "IU", "PWM frequency ch 2 (Hz)");
+ SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "period2",
+ CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_NEEDGIANT, sc, 0,
+ bcm_pwm_period2_proc, "IU", "PWM period ch 2 (#clocks)");
+ SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "ratio2",
+ CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_NEEDGIANT, sc, 0,
+ bcm_pwm_ratio2_proc, "IU", "PWM ratio ch 2 (0...period)");
+ SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "mode2",
+ CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_NEEDGIANT, sc, 0,
+ bcm_pwm_mode2_proc, "IU", "PWM mode ch 2 (0=off, 1=pwm, 2=dither)");
+}
+
+static int
+bcm_pwm_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "BCM2708/2835 PWM controller");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+bcm_pwm_attach(device_t dev)
+{
+ struct bcm_pwm_softc *sc;
+ int rid;
+
+ if (device_get_unit(dev) != 0) {
+ device_printf(dev, "only one PWM controller supported\n");
+ return (ENXIO);
+ }
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+
+ sc->clkman = devclass_get_device(devclass_find("bcm2835_clkman"), 0);
+ if (sc->clkman == NULL) {
+ device_printf(dev, "cannot find Clock Manager\n");
+ return (ENXIO);
+ }
+
+ rid = 0;
+ sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (!sc->sc_mem_res) {
+ device_printf(dev, "cannot allocate memory window\n");
+ return (ENXIO);
+ }
+
+ sc->sc_m_bst = rman_get_bustag(sc->sc_mem_res);
+ sc->sc_m_bsh = rman_get_bushandle(sc->sc_mem_res);
+
+ /* Add sysctl nodes. */
+ bcm_pwm_sysctl_init(sc);
+
+ sc->freq = 125000000; /* 125 Mhz */
+ sc->period = 10000; /* 12.5 khz */
+ sc->ratio = 2500; /* 25% */
+ sc->period2 = 10000; /* 12.5 khz */
+ sc->ratio2 = 2500; /* 25% */
+
+ return (bus_generic_attach(dev));
+}
+
+static int
+bcm_pwm_detach(device_t dev)
+{
+ struct bcm_pwm_softc *sc;
+
+ bus_generic_detach(dev);
+
+ sc = device_get_softc(dev);
+ sc->mode = 0;
+ sc->mode2 = 0;
+ (void)bcm_pwm_reconf(sc);
+ if (sc->sc_mem_res)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+
+ return (0);
+}
+
+static phandle_t
+bcm_pwm_get_node(device_t bus, device_t dev)
+{
+
+ return (ofw_bus_get_node(bus));
+}
+
+static device_method_t bcm_pwm_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, bcm_pwm_probe),
+ DEVMETHOD(device_attach, bcm_pwm_attach),
+ DEVMETHOD(device_detach, bcm_pwm_detach),
+ DEVMETHOD(ofw_bus_get_node, bcm_pwm_get_node),
+
+ DEVMETHOD_END
+};
+
+static devclass_t bcm_pwm_devclass;
+
+static driver_t bcm_pwm_driver = {
+ "pwm",
+ bcm_pwm_methods,
+ sizeof(struct bcm_pwm_softc),
+};
+
+DRIVER_MODULE(bcm2835_pwm, simplebus, bcm_pwm_driver, bcm_pwm_devclass, 0, 0);
+MODULE_DEPEND(bcm2835_pwm, bcm2835_clkman, 1, 1, 1);
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_rng.c b/sys/arm/broadcom/bcm2835/bcm2835_rng.c
new file mode 100644
index 000000000000..b73580c5eb53
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2835_rng.c
@@ -0,0 +1,467 @@
+/*
+ * Copyright (c) 2015, 2016, Stephen J. Kiernan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/ktr.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/random.h>
+#include <sys/sbuf.h>
+#include <sys/sysctl.h>
+#include <sys/selinfo.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/random/randomdev.h>
+#include <dev/random/random_harvestq.h>
+
+static device_attach_t bcm2835_rng_attach;
+static device_detach_t bcm2835_rng_detach;
+static device_probe_t bcm2835_rng_probe;
+
+#define RNG_CTRL 0x00 /* RNG Control Register */
+#define RNG_COMBLK1_OSC 0x003f0000 /* Combiner Blk 1 Oscillator */
+#define RNG_COMBLK1_OSC_SHIFT 16
+#define RNG_COMBLK2_OSC 0x0fc00000 /* Combiner Blk 2 Oscillator */
+#define RNG_COMBLK2_OSC_SHIFT 22
+#define RNG_JCLK_BYP_DIV_CNT 0x0000ff00 /* Jitter clk bypass divider
+ count */
+#define RNG_JCLK_BYP_DIV_CNT_SHIFT 8
+#define RNG_JCLK_BYP_SRC 0x00000020 /* Jitter clk bypass source */
+#define RNG_JCLK_BYP_SEL 0x00000010 /* Jitter clk bypass select */
+#define RNG_RBG2X 0x00000002 /* RBG 2X SPEED */
+#define RNG_RBGEN_BIT 0x00000001 /* Enable RNG bit */
+
+#define RNG_STATUS 0x04 /* RNG status register */
+#define RND_VAL_SHIFT 24 /* Shift for valid words */
+#define RND_VAL_MASK 0x000000ff /* Number valid words mask */
+#define RND_VAL_WARM_CNT 0x40000 /* RNG Warm Up count */
+#define RND_WARM_CNT 0xfffff /* RNG Warm Up Count mask */
+
+#define RNG_DATA 0x08 /* RNG Data Register */
+#define RNG_FF_THRES 0x0c
+#define RNG_FF_THRES_MASK 0x0000001f
+
+#define RNG_INT_MASK 0x10
+#define RNG_INT_OFF_BIT 0x00000001
+
+#define RNG_FF_DEFAULT 0x10 /* FIFO threshold default */
+
+#define RNG_FIFO_WORDS (RNG_FF_DEFAULT / sizeof(uint32_t))
+
+#define RNG_NUM_OSCILLATORS 6
+#define RNG_STALL_COUNT_DEFAULT 10
+
+#define RNG_CALLOUT_TICKS (hz * 4)
+
+struct bcm2835_rng_softc {
+ device_t sc_dev;
+ struct resource * sc_mem_res;
+ struct resource * sc_irq_res;
+ void * sc_intr_hdl;
+ uint32_t sc_buf[RNG_FIFO_WORDS];
+ struct callout sc_rngto;
+ int sc_stall_count;
+ int sc_rbg2x;
+ long sc_underrun;
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"broadcom,bcm2835-rng", 1},
+ {"brcm,bcm2835-rng", 1},
+ {NULL, 0}
+};
+
+static __inline void
+bcm2835_rng_stat_inc_underrun(struct bcm2835_rng_softc *sc)
+{
+
+ atomic_add_long(&sc->sc_underrun, 1);
+}
+
+static __inline uint32_t
+bcm2835_rng_read4(struct bcm2835_rng_softc *sc, bus_size_t off)
+{
+
+ return bus_read_4(sc->sc_mem_res, off);
+}
+
+static __inline void
+bcm2835_rng_read_multi4(struct bcm2835_rng_softc *sc, bus_size_t off,
+ uint32_t *datap, bus_size_t count)
+{
+
+ bus_read_multi_4(sc->sc_mem_res, off, datap, count);
+}
+
+static __inline void
+bcm2835_rng_write4(struct bcm2835_rng_softc *sc, bus_size_t off, uint32_t val)
+{
+
+ bus_write_4(sc->sc_mem_res, off, val);
+}
+
+static void
+bcm2835_rng_dump_registers(struct bcm2835_rng_softc *sc, struct sbuf *sbp)
+{
+ uint32_t comblk2_osc, comblk1_osc, jclk_byp_div, val;
+ int i;
+
+ /* Display RNG control register contents */
+ val = bcm2835_rng_read4(sc, RNG_CTRL);
+ sbuf_printf(sbp, "RNG_CTRL (%08x)\n", val);
+
+ comblk2_osc = (val & RNG_COMBLK2_OSC) >> RNG_COMBLK2_OSC_SHIFT;
+ sbuf_printf(sbp, " RNG_COMBLK2_OSC (%02x)\n", comblk2_osc);
+ for (i = 0; i < RNG_NUM_OSCILLATORS; i++)
+ if ((comblk2_osc & (1 << i)) == 0)
+ sbuf_printf(sbp, " Oscillator %d enabled\n", i + 1);
+
+ comblk1_osc = (val & RNG_COMBLK1_OSC) >> RNG_COMBLK1_OSC_SHIFT;
+ sbuf_printf(sbp, " RNG_COMBLK1_OSC (%02x)\n", comblk1_osc);
+ for (i = 0; i < RNG_NUM_OSCILLATORS; i++)
+ if ((comblk1_osc & (1 << i)) == 0)
+ sbuf_printf(sbp, " Oscillator %d enabled\n", i + 1);
+
+ jclk_byp_div = (val & RNG_JCLK_BYP_DIV_CNT) >>
+ RNG_JCLK_BYP_DIV_CNT_SHIFT;
+ sbuf_printf(sbp,
+ " RNG_JCLK_BYP_DIV_CNT (%02x)\n APB clock frequency / %d\n",
+ jclk_byp_div, 2 * (jclk_byp_div + 1));
+
+ sbuf_printf(sbp, " RNG_JCLK_BYP_SRC:\n %s\n",
+ (val & RNG_JCLK_BYP_SRC) ? "Use divided down APB clock" :
+ "Use RNG clock (APB clock)");
+
+ sbuf_printf(sbp, " RNG_JCLK_BYP_SEL:\n %s\n",
+ (val & RNG_JCLK_BYP_SEL) ? "Bypass internal jitter clock" :
+ "Use internal jitter clock");
+
+ if ((val & RNG_RBG2X) != 0)
+ sbuf_cat(sbp, " RNG_RBG2X: RNG 2X SPEED enabled\n");
+
+ if ((val & RNG_RBGEN_BIT) != 0)
+ sbuf_cat(sbp, " RNG_RBGEN_BIT: RBG enabled\n");
+
+ /* Display RNG status register contents */
+ val = bcm2835_rng_read4(sc, RNG_STATUS);
+ sbuf_printf(sbp, "RNG_CTRL (%08x)\n", val);
+ sbuf_printf(sbp, " RND_VAL: %02x\n",
+ (val >> RND_VAL_SHIFT) & RND_VAL_MASK);
+ sbuf_printf(sbp, " RND_WARM_CNT: %05x\n", val & RND_WARM_CNT);
+
+ /* Display FIFO threshold register contents */
+ val = bcm2835_rng_read4(sc, RNG_FF_THRES);
+ sbuf_printf(sbp, "RNG_FF_THRES: %05x\n", val & RNG_FF_THRES_MASK);
+
+ /* Display interrupt mask register contents */
+ val = bcm2835_rng_read4(sc, RNG_INT_MASK);
+ sbuf_printf(sbp, "RNG_INT_MASK: interrupt %s\n",
+ ((val & RNG_INT_OFF_BIT) != 0) ? "disabled" : "enabled");
+}
+
+static void
+bcm2835_rng_disable_intr(struct bcm2835_rng_softc *sc)
+{
+ uint32_t mask;
+
+ /* Set the interrupt off bit in the interrupt mask register */
+ mask = bcm2835_rng_read4(sc, RNG_INT_MASK);
+ mask |= RNG_INT_OFF_BIT;
+ bcm2835_rng_write4(sc, RNG_INT_MASK, mask);
+}
+
+static void
+bcm2835_rng_start(struct bcm2835_rng_softc *sc)
+{
+ uint32_t ctrl;
+
+ /* Disable the interrupt */
+ bcm2835_rng_disable_intr(sc);
+
+ /* Set the warmup count */
+ bcm2835_rng_write4(sc, RNG_STATUS, RND_VAL_WARM_CNT);
+
+ /* Enable the RNG */
+ ctrl = bcm2835_rng_read4(sc, RNG_CTRL);
+ ctrl |= RNG_RBGEN_BIT;
+ if (sc->sc_rbg2x)
+ ctrl |= RNG_RBG2X;
+ bcm2835_rng_write4(sc, RNG_CTRL, ctrl);
+}
+
+static void
+bcm2835_rng_stop(struct bcm2835_rng_softc *sc)
+{
+ uint32_t ctrl;
+
+ /* Disable the RNG */
+ ctrl = bcm2835_rng_read4(sc, RNG_CTRL);
+ ctrl &= ~RNG_RBGEN_BIT;
+ bcm2835_rng_write4(sc, RNG_CTRL, ctrl);
+}
+
+static void
+bcm2835_rng_harvest(void *arg)
+{
+ uint32_t *dest;
+ uint32_t status;
+ u_int cnt, nread, num_avail, num_words;
+ int seen_underrun, num_stalls;
+ struct bcm2835_rng_softc *sc = arg;
+
+ dest = sc->sc_buf;
+ nread = num_words = 0;
+ seen_underrun = num_stalls = 0;
+ for (cnt = sizeof(sc->sc_buf) / sizeof(uint32_t); cnt > 0;
+ cnt -= num_words) {
+ /* Read status register to find out how many words available */
+ status = bcm2835_rng_read4(sc, RNG_STATUS);
+ num_avail = (status >> RND_VAL_SHIFT) & RND_VAL_MASK;
+
+ /* If we have none... */
+ if (num_avail == 0) {
+ bcm2835_rng_stat_inc_underrun(sc);
+ if (++seen_underrun >= sc->sc_stall_count) {
+ if (num_stalls++ > 0) {
+ device_printf(sc->sc_dev,
+ "RNG stalled, disabling device\n");
+ bcm2835_rng_stop(sc);
+ break;
+ } else {
+ device_printf(sc->sc_dev,
+ "Too many underruns, resetting\n");
+ bcm2835_rng_stop(sc);
+ bcm2835_rng_start(sc);
+ seen_underrun = 0;
+ }
+ }
+ /* Try again */
+ continue;
+ }
+
+ CTR2(KTR_DEV, "%s: %d words available in RNG FIFO",
+ device_get_nameunit(sc->sc_dev), num_avail);
+
+ /* Pull MIN(num_avail, cnt) words from the FIFO */
+ num_words = (num_avail > cnt) ? cnt : num_avail;
+ bcm2835_rng_read_multi4(sc, RNG_DATA, dest,
+ num_words);
+ dest += num_words;
+ nread += num_words;
+ }
+
+ cnt = nread * sizeof(uint32_t);
+ if (cnt > 0)
+ random_harvest_queue(sc->sc_buf, cnt, RANDOM_PURE_BROADCOM);
+
+ callout_reset(&sc->sc_rngto, RNG_CALLOUT_TICKS, bcm2835_rng_harvest, sc);
+}
+
+static int
+sysctl_bcm2835_rng_2xspeed(SYSCTL_HANDLER_ARGS)
+{
+ struct bcm2835_rng_softc *sc = arg1;
+ int error, rbg2x;
+
+ rbg2x = sc->sc_rbg2x;
+ error = sysctl_handle_int(oidp, &rbg2x, 0, req);
+ if (error)
+ return (error);
+ if (req->newptr == NULL)
+ return (error);
+ if (rbg2x == sc->sc_rbg2x)
+ return (0);
+
+ /* Reset the RNG */
+ bcm2835_rng_stop(sc);
+ sc->sc_rbg2x = rbg2x;
+ bcm2835_rng_start(sc);
+
+ return (0);
+}
+
+#ifdef BCM2835_RNG_DEBUG_REGISTERS
+static int
+sysctl_bcm2835_rng_dump(SYSCTL_HANDLER_ARGS)
+{
+ struct sbuf sb;
+ struct bcm2835_rng_softc *sc = arg1;
+ int error;
+
+ error = sysctl_wire_old_buffer(req, 0);
+ if (error != 0)
+ return (error);
+ sbuf_new_for_sysctl(&sb, NULL, 128, req);
+ bcm2835_rng_dump_registers(sc, &sb);
+ error = sbuf_finish(&sb);
+ sbuf_delete(&sb);
+ return (error);
+}
+#endif
+
+static int
+bcm2835_rng_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Broadcom BCM2835 RNG");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+bcm2835_rng_attach(device_t dev)
+{
+ struct bcm2835_rng_softc *sc;
+ struct sysctl_ctx_list *sysctl_ctx;
+ struct sysctl_oid *sysctl_tree;
+ int error, rid;
+
+ error = 0;
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+ sc->sc_stall_count = RNG_STALL_COUNT_DEFAULT;
+
+ /* Initialize callout */
+ callout_init(&sc->sc_rngto, CALLOUT_MPSAFE);
+
+ TUNABLE_INT_FETCH("bcmrng.2xspeed", &sc->sc_rbg2x);
+ TUNABLE_INT_FETCH("bcmrng.stall_count", &sc->sc_stall_count);
+
+ /* Allocate memory resources */
+ rid = 0;
+ sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->sc_mem_res == NULL) {
+ bcm2835_rng_detach(dev);
+ return (ENXIO);
+ }
+
+ /* Start the RNG */
+ bcm2835_rng_start(sc);
+
+ /* Dump the registers if booting verbose */
+ if (bootverbose) {
+ struct sbuf sb;
+
+ (void) sbuf_new(&sb, NULL, 256,
+ SBUF_AUTOEXTEND | SBUF_INCLUDENUL);
+ bcm2835_rng_dump_registers(sc, &sb);
+ sbuf_trim(&sb);
+ error = sbuf_finish(&sb);
+ if (error == 0)
+ device_printf(dev, "%s", sbuf_data(&sb));
+ sbuf_delete(&sb);
+ }
+
+ sysctl_ctx = device_get_sysctl_ctx(dev);
+ sysctl_tree = device_get_sysctl_tree(dev);
+ SYSCTL_ADD_LONG(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO,
+ "underrun", CTLFLAG_RD, &sc->sc_underrun,
+ "Number of FIFO underruns");
+ SYSCTL_ADD_PROC(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO,
+ "2xspeed", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0,
+ sysctl_bcm2835_rng_2xspeed, "I", "Enable RBG 2X SPEED");
+ SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO,
+ "stall_count", CTLFLAG_RW, &sc->sc_stall_count,
+ RNG_STALL_COUNT_DEFAULT, "Number of underruns to assume RNG stall");
+#ifdef BCM2835_RNG_DEBUG_REGISTERS
+ SYSCTL_ADD_PROC(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO,
+ "dumpregs", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, 0,
+ sysctl_bcm2835_rng_dump, "S", "Dump RNG registers");
+#endif
+
+ /*
+ * Schedule the initial harvesting one second from now, which should give the
+ * hardware RNG plenty of time to generate the first random bytes.
+ */
+ callout_reset(&sc->sc_rngto, hz, bcm2835_rng_harvest, sc);
+
+ return (0);
+}
+
+static int
+bcm2835_rng_detach(device_t dev)
+{
+ struct bcm2835_rng_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ /* Stop the RNG */
+ bcm2835_rng_stop(sc);
+
+ /* Drain the callout it */
+ callout_drain(&sc->sc_rngto);
+
+ /* Release memory resource */
+ if (sc->sc_mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+
+ return (0);
+}
+
+static device_method_t bcm2835_rng_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, bcm2835_rng_probe),
+ DEVMETHOD(device_attach, bcm2835_rng_attach),
+ DEVMETHOD(device_detach, bcm2835_rng_detach),
+
+ DEVMETHOD_END
+};
+
+static driver_t bcm2835_rng_driver = {
+ "bcmrng",
+ bcm2835_rng_methods,
+ sizeof(struct bcm2835_rng_softc)
+};
+static devclass_t bcm2835_rng_devclass;
+
+DRIVER_MODULE(bcm2835_rng, simplebus, bcm2835_rng_driver,
+ bcm2835_rng_devclass, 0, 0);
+DRIVER_MODULE(bcm2835_rng, ofwbus, bcm2835_rng_driver, bcm2835_rng_devclass, 0,
+ 0);
+MODULE_VERSION(bcm2835_rng, 1);
+MODULE_DEPEND(bcm2835_rng, randomdev, 1, 1, 1);
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c b/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c
new file mode 100644
index 000000000000..cd9b60743be3
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c
@@ -0,0 +1,869 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+#include <sys/taskqueue.h>
+
+#include <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/mmc/bridge.h>
+#include <dev/mmc/mmcreg.h>
+#include <dev/mmc/mmc_fdt_helpers.h>
+
+#include <dev/sdhci/sdhci.h>
+
+#include "mmcbr_if.h"
+#include "sdhci_if.h"
+
+#include "opt_mmccam.h"
+
+#include "bcm2835_dma.h"
+#include <arm/broadcom/bcm2835/bcm2835_mbox_prop.h>
+#ifdef NOTYET
+#include <arm/broadcom/bcm2835/bcm2835_clkman.h>
+#endif
+#include <arm/broadcom/bcm2835/bcm2835_vcbus.h>
+
+#define BCM2835_DEFAULT_SDHCI_FREQ 50
+#define BCM2838_DEFAULT_SDHCI_FREQ 100
+
+#define BCM_SDHCI_BUFFER_SIZE 512
+/*
+ * NUM_DMA_SEGS is the number of DMA segments we want to accommodate on average.
+ * We add in a number of segments based on how much we may need to spill into
+ * another segment due to crossing page boundaries. e.g. up to PAGE_SIZE, an
+ * extra page is needed as we can cross a page boundary exactly once.
+ */
+#define NUM_DMA_SEGS 1
+#define NUM_DMA_SPILL_SEGS \
+ ((((NUM_DMA_SEGS * BCM_SDHCI_BUFFER_SIZE) - 1) / PAGE_SIZE) + 1)
+#define ALLOCATED_DMA_SEGS (NUM_DMA_SEGS + NUM_DMA_SPILL_SEGS)
+#define BCM_DMA_MAXSIZE (NUM_DMA_SEGS * BCM_SDHCI_BUFFER_SIZE)
+
+#define BCM_SDHCI_SLOT_LEFT(slot) \
+ ((slot)->curcmd->data->len - (slot)->offset)
+
+#define BCM_SDHCI_SEGSZ_LEFT(slot) \
+ min(BCM_DMA_MAXSIZE, \
+ rounddown(BCM_SDHCI_SLOT_LEFT(slot), BCM_SDHCI_BUFFER_SIZE))
+
+#define DATA_PENDING_MASK (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL)
+#define DATA_XFER_MASK (DATA_PENDING_MASK | SDHCI_INT_DATA_END)
+
+#ifdef DEBUG
+static int bcm2835_sdhci_debug = 0;
+
+TUNABLE_INT("hw.bcm2835.sdhci.debug", &bcm2835_sdhci_debug);
+SYSCTL_INT(_hw_sdhci, OID_AUTO, bcm2835_sdhci_debug, CTLFLAG_RWTUN,
+ &bcm2835_sdhci_debug, 0, "bcm2835 SDHCI debug level");
+
+#define dprintf(fmt, args...) \
+ do { \
+ if (bcm2835_sdhci_debug) \
+ printf("%s: " fmt, __func__, ##args); \
+ } while (0)
+#else
+#define dprintf(fmt, args...)
+#endif
+
+static int bcm2835_sdhci_hs = 1;
+static int bcm2835_sdhci_pio_mode = 0;
+
+struct bcm_mmc_conf {
+ int clock_id;
+ int clock_src;
+ int default_freq;
+ int quirks;
+ int emmc_dreq;
+};
+
+struct bcm_mmc_conf bcm2835_sdhci_conf = {
+ .clock_id = BCM2835_MBOX_CLOCK_ID_EMMC,
+ .clock_src = -1,
+ .default_freq = BCM2835_DEFAULT_SDHCI_FREQ,
+ .quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
+ SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | SDHCI_QUIRK_DONT_SET_HISPD_BIT |
+ SDHCI_QUIRK_MISSING_CAPS,
+ .emmc_dreq = BCM_DMA_DREQ_EMMC,
+};
+
+struct bcm_mmc_conf bcm2838_emmc2_conf = {
+ .clock_id = BCM2838_MBOX_CLOCK_ID_EMMC2,
+ .clock_src = -1,
+ .default_freq = BCM2838_DEFAULT_SDHCI_FREQ,
+ .quirks = 0,
+ .emmc_dreq = BCM_DMA_DREQ_NONE,
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"broadcom,bcm2835-sdhci", (uintptr_t)&bcm2835_sdhci_conf},
+ {"brcm,bcm2835-sdhci", (uintptr_t)&bcm2835_sdhci_conf},
+ {"brcm,bcm2835-mmc", (uintptr_t)&bcm2835_sdhci_conf},
+ {"brcm,bcm2711-emmc2", (uintptr_t)&bcm2838_emmc2_conf},
+ {"brcm,bcm2838-emmc2", (uintptr_t)&bcm2838_emmc2_conf},
+ {NULL, 0}
+};
+
+TUNABLE_INT("hw.bcm2835.sdhci.hs", &bcm2835_sdhci_hs);
+TUNABLE_INT("hw.bcm2835.sdhci.pio_mode", &bcm2835_sdhci_pio_mode);
+
+struct bcm_sdhci_softc {
+ device_t sc_dev;
+ struct resource * sc_mem_res;
+ struct resource * sc_irq_res;
+ bus_space_tag_t sc_bst;
+ bus_space_handle_t sc_bsh;
+ void * sc_intrhand;
+ struct mmc_request * sc_req;
+ struct sdhci_slot sc_slot;
+ struct mmc_fdt_helper sc_mmc_helper;
+ int sc_dma_ch;
+ bus_dma_tag_t sc_dma_tag;
+ bus_dmamap_t sc_dma_map;
+ vm_paddr_t sc_sdhci_buffer_phys;
+ bus_addr_t dmamap_seg_addrs[ALLOCATED_DMA_SEGS];
+ bus_size_t dmamap_seg_sizes[ALLOCATED_DMA_SEGS];
+ int dmamap_seg_count;
+ int dmamap_seg_index;
+ int dmamap_status;
+ uint32_t blksz_and_count;
+ uint32_t cmd_and_mode;
+ bool need_update_blk;
+#ifdef NOTYET
+ device_t clkman;
+#endif
+ struct bcm_mmc_conf * conf;
+};
+
+static int bcm_sdhci_probe(device_t);
+static int bcm_sdhci_attach(device_t);
+static int bcm_sdhci_detach(device_t);
+static void bcm_sdhci_intr(void *);
+
+static int bcm_sdhci_get_ro(device_t, device_t);
+static void bcm_sdhci_dma_intr(int ch, void *arg);
+static void bcm_sdhci_start_dma(struct sdhci_slot *slot);
+
+static void
+bcm_sdhci_dmacb(void *arg, bus_dma_segment_t *segs, int nseg, int err)
+{
+ struct bcm_sdhci_softc *sc = arg;
+ int i;
+
+ /* Sanity check: we can only ever have one mapping at a time. */
+ KASSERT(sc->dmamap_seg_count == 0, ("leaked DMA segment"));
+ sc->dmamap_status = err;
+ sc->dmamap_seg_count = nseg;
+
+ /* Note nseg is guaranteed to be zero if err is non-zero. */
+ for (i = 0; i < nseg; i++) {
+ sc->dmamap_seg_addrs[i] = segs[i].ds_addr;
+ sc->dmamap_seg_sizes[i] = segs[i].ds_len;
+ }
+}
+
+static int
+bcm_sdhci_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Broadcom 2708 SDHCI controller");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+bcm_sdhci_attach(device_t dev)
+{
+ struct bcm_sdhci_softc *sc = device_get_softc(dev);
+ int rid, err;
+ phandle_t node;
+ pcell_t cell;
+ u_int default_freq;
+
+ sc->sc_dev = dev;
+ sc->sc_req = NULL;
+
+ sc->conf = (struct bcm_mmc_conf *)ofw_bus_search_compatible(dev,
+ compat_data)->ocd_data;
+ if (sc->conf == 0)
+ return (ENXIO);
+
+ err = bcm2835_mbox_set_power_state(BCM2835_MBOX_POWER_ID_EMMC, TRUE);
+ if (err != 0) {
+ if (bootverbose)
+ device_printf(dev, "Unable to enable the power\n");
+ return (err);
+ }
+
+ default_freq = 0;
+ err = bcm2835_mbox_get_clock_rate(sc->conf->clock_id, &default_freq);
+ if (err == 0) {
+ /* Convert to MHz */
+ default_freq /= 1000000;
+ }
+ if (default_freq == 0) {
+ node = ofw_bus_get_node(sc->sc_dev);
+ if ((OF_getencprop(node, "clock-frequency", &cell,
+ sizeof(cell))) > 0)
+ default_freq = cell / 1000000;
+ }
+ if (default_freq == 0)
+ default_freq = sc->conf->default_freq;
+
+ if (bootverbose)
+ device_printf(dev, "SDHCI frequency: %dMHz\n", default_freq);
+#ifdef NOTYET
+ if (sc->conf->clock_src > 0) {
+ uint32_t f;
+ sc->clkman = devclass_get_device(
+ devclass_find("bcm2835_clkman"), 0);
+ if (sc->clkman == NULL) {
+ device_printf(dev, "cannot find Clock Manager\n");
+ return (ENXIO);
+ }
+
+ f = bcm2835_clkman_set_frequency(sc->clkman,
+ sc->conf->clock_src, default_freq);
+ if (f == 0)
+ return (EINVAL);
+
+ if (bootverbose)
+ device_printf(dev, "Clock source frequency: %dMHz\n",
+ f);
+ }
+#endif
+
+ rid = 0;
+ sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (!sc->sc_mem_res) {
+ device_printf(dev, "cannot allocate memory window\n");
+ err = ENXIO;
+ goto fail;
+ }
+
+ sc->sc_bst = rman_get_bustag(sc->sc_mem_res);
+ sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res);
+
+ rid = 0;
+ sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE | RF_SHAREABLE);
+ if (!sc->sc_irq_res) {
+ device_printf(dev, "cannot allocate interrupt\n");
+ err = ENXIO;
+ goto fail;
+ }
+
+ if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ NULL, bcm_sdhci_intr, sc, &sc->sc_intrhand)) {
+ device_printf(dev, "cannot setup interrupt handler\n");
+ err = ENXIO;
+ goto fail;
+ }
+
+ if (!bcm2835_sdhci_pio_mode)
+ sc->sc_slot.opt = SDHCI_PLATFORM_TRANSFER;
+
+ sc->sc_slot.caps = SDHCI_CAN_VDD_330 | SDHCI_CAN_VDD_180;
+ if (bcm2835_sdhci_hs)
+ sc->sc_slot.caps |= SDHCI_CAN_DO_HISPD;
+ sc->sc_slot.caps |= (default_freq << SDHCI_CLOCK_BASE_SHIFT);
+ sc->sc_slot.quirks = sc->conf->quirks;
+
+ sdhci_init_slot(dev, &sc->sc_slot, 0);
+ mmc_fdt_parse(dev, 0, &sc->sc_mmc_helper, &sc->sc_slot.host);
+
+ sc->sc_dma_ch = bcm_dma_allocate(BCM_DMA_CH_ANY);
+ if (sc->sc_dma_ch == BCM_DMA_CH_INVALID)
+ goto fail;
+
+ err = bcm_dma_setup_intr(sc->sc_dma_ch, bcm_sdhci_dma_intr, sc);
+ if (err != 0) {
+ device_printf(dev,
+ "cannot setup dma interrupt handler\n");
+ err = ENXIO;
+ goto fail;
+ }
+
+ /* Allocate bus_dma resources. */
+ err = bus_dma_tag_create(bus_get_dma_tag(dev),
+ 1, 0, bcm283x_dmabus_peripheral_lowaddr(),
+ BUS_SPACE_MAXADDR, NULL, NULL,
+ BCM_DMA_MAXSIZE, ALLOCATED_DMA_SEGS, BCM_SDHCI_BUFFER_SIZE,
+ BUS_DMA_ALLOCNOW, NULL, NULL,
+ &sc->sc_dma_tag);
+
+ if (err) {
+ device_printf(dev, "failed allocate DMA tag");
+ goto fail;
+ }
+
+ err = bus_dmamap_create(sc->sc_dma_tag, 0, &sc->sc_dma_map);
+ if (err) {
+ device_printf(dev, "bus_dmamap_create failed\n");
+ goto fail;
+ }
+
+ /* FIXME: Fix along with other BUS_SPACE_PHYSADDR instances */
+ sc->sc_sdhci_buffer_phys = rman_get_start(sc->sc_mem_res) +
+ SDHCI_BUFFER;
+
+ bus_generic_probe(dev);
+ bus_generic_attach(dev);
+
+ sdhci_start_slot(&sc->sc_slot);
+
+ /* Seed our copies. */
+ sc->blksz_and_count = SDHCI_READ_4(dev, &sc->sc_slot, SDHCI_BLOCK_SIZE);
+ sc->cmd_and_mode = SDHCI_READ_4(dev, &sc->sc_slot, SDHCI_TRANSFER_MODE);
+
+ return (0);
+
+fail:
+ if (sc->sc_intrhand)
+ bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand);
+ if (sc->sc_irq_res)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
+ if (sc->sc_mem_res)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+
+ return (err);
+}
+
+static int
+bcm_sdhci_detach(device_t dev)
+{
+
+ return (EBUSY);
+}
+
+static void
+bcm_sdhci_intr(void *arg)
+{
+ struct bcm_sdhci_softc *sc = arg;
+
+ sdhci_generic_intr(&sc->sc_slot);
+}
+
+static int
+bcm_sdhci_update_ios(device_t bus, device_t child)
+{
+#ifdef EXT_RESOURCES
+ struct bcm_sdhci_softc *sc;
+ struct mmc_ios *ios;
+#endif
+ int rv;
+
+#ifdef EXT_RESOURCES
+ sc = device_get_softc(bus);
+ ios = &sc->sc_slot.host.ios;
+
+ if (ios->power_mode == power_up) {
+ if (sc->sc_mmc_helper.vmmc_supply)
+ regulator_enable(sc->sc_mmc_helper.vmmc_supply);
+ if (sc->sc_mmc_helper.vqmmc_supply)
+ regulator_enable(sc->sc_mmc_helper.vqmmc_supply);
+ }
+#endif
+
+ rv = sdhci_generic_update_ios(bus, child);
+ if (rv != 0)
+ return (rv);
+
+#ifdef EXT_RESOURCES
+ if (ios->power_mode == power_off) {
+ if (sc->sc_mmc_helper.vmmc_supply)
+ regulator_disable(sc->sc_mmc_helper.vmmc_supply);
+ if (sc->sc_mmc_helper.vqmmc_supply)
+ regulator_disable(sc->sc_mmc_helper.vqmmc_supply);
+ }
+#endif
+
+ return (0);
+}
+
+static int
+bcm_sdhci_get_ro(device_t bus, device_t child)
+{
+
+ return (0);
+}
+
+static inline uint32_t
+RD4(struct bcm_sdhci_softc *sc, bus_size_t off)
+{
+ uint32_t val = bus_space_read_4(sc->sc_bst, sc->sc_bsh, off);
+ return val;
+}
+
+static inline void
+WR4(struct bcm_sdhci_softc *sc, bus_size_t off, uint32_t val)
+{
+
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, off, val);
+ /*
+ * The Arasan HC has a bug where it may lose the content of
+ * consecutive writes to registers that are within two SD-card
+ * clock cycles of each other (a clock domain crossing problem).
+ */
+ if (sc->sc_slot.clock > 0)
+ DELAY(((2 * 1000000) / sc->sc_slot.clock) + 1);
+}
+
+static uint8_t
+bcm_sdhci_read_1(device_t dev, struct sdhci_slot *slot, bus_size_t off)
+{
+ struct bcm_sdhci_softc *sc = device_get_softc(dev);
+ uint32_t val = RD4(sc, off & ~3);
+
+ return ((val >> (off & 3)*8) & 0xff);
+}
+
+static uint16_t
+bcm_sdhci_read_2(device_t dev, struct sdhci_slot *slot, bus_size_t off)
+{
+ struct bcm_sdhci_softc *sc = device_get_softc(dev);
+ uint32_t val32;
+
+ /*
+ * Standard 32-bit handling of command and transfer mode, as
+ * well as block size and count.
+ */
+ if ((off == SDHCI_BLOCK_SIZE || off == SDHCI_BLOCK_COUNT) &&
+ sc->need_update_blk)
+ val32 = sc->blksz_and_count;
+ else if (off == SDHCI_TRANSFER_MODE || off == SDHCI_COMMAND_FLAGS)
+ val32 = sc->cmd_and_mode;
+ else
+ val32 = RD4(sc, off & ~3);
+
+ return ((val32 >> (off & 3)*8) & 0xffff);
+}
+
+static uint32_t
+bcm_sdhci_read_4(device_t dev, struct sdhci_slot *slot, bus_size_t off)
+{
+ struct bcm_sdhci_softc *sc = device_get_softc(dev);
+
+ return RD4(sc, off);
+}
+
+static void
+bcm_sdhci_read_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off,
+ uint32_t *data, bus_size_t count)
+{
+ struct bcm_sdhci_softc *sc = device_get_softc(dev);
+
+ bus_space_read_multi_4(sc->sc_bst, sc->sc_bsh, off, data, count);
+}
+
+static void
+bcm_sdhci_write_1(device_t dev, struct sdhci_slot *slot, bus_size_t off,
+ uint8_t val)
+{
+ struct bcm_sdhci_softc *sc = device_get_softc(dev);
+ uint32_t val32 = RD4(sc, off & ~3);
+ val32 &= ~(0xff << (off & 3)*8);
+ val32 |= (val << (off & 3)*8);
+ WR4(sc, off & ~3, val32);
+}
+
+static void
+bcm_sdhci_write_2(device_t dev, struct sdhci_slot *slot, bus_size_t off,
+ uint16_t val)
+{
+ struct bcm_sdhci_softc *sc = device_get_softc(dev);
+ uint32_t val32;
+
+ /*
+ * If we have a queued up 16bit value for blk size or count, use and
+ * update the saved value rather than doing any real register access.
+ * If we did not touch either since the last write, then read from
+ * register as at least block count can change.
+ * Similarly, if we are about to issue a command, always use the saved
+ * value for transfer mode as we can never write that without issuing
+ * a command.
+ */
+ if ((off == SDHCI_BLOCK_SIZE || off == SDHCI_BLOCK_COUNT) &&
+ sc->need_update_blk)
+ val32 = sc->blksz_and_count;
+ else if (off == SDHCI_COMMAND_FLAGS)
+ val32 = sc->cmd_and_mode;
+ else
+ val32 = RD4(sc, off & ~3);
+
+ val32 &= ~(0xffff << (off & 3)*8);
+ val32 |= (val << (off & 3)*8);
+
+ if (off == SDHCI_TRANSFER_MODE)
+ sc->cmd_and_mode = val32;
+ else if (off == SDHCI_BLOCK_SIZE || off == SDHCI_BLOCK_COUNT) {
+ sc->blksz_and_count = val32;
+ sc->need_update_blk = true;
+ } else {
+ if (off == SDHCI_COMMAND_FLAGS) {
+ /* If we saved blk writes, do them now before cmd. */
+ if (sc->need_update_blk) {
+ WR4(sc, SDHCI_BLOCK_SIZE, sc->blksz_and_count);
+ sc->need_update_blk = false;
+ }
+ /* Always save cmd and mode registers. */
+ sc->cmd_and_mode = val32;
+ }
+ WR4(sc, off & ~3, val32);
+ }
+}
+
+static void
+bcm_sdhci_write_4(device_t dev, struct sdhci_slot *slot, bus_size_t off,
+ uint32_t val)
+{
+ struct bcm_sdhci_softc *sc = device_get_softc(dev);
+ WR4(sc, off, val);
+}
+
+static void
+bcm_sdhci_write_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off,
+ uint32_t *data, bus_size_t count)
+{
+ struct bcm_sdhci_softc *sc = device_get_softc(dev);
+
+ bus_space_write_multi_4(sc->sc_bst, sc->sc_bsh, off, data, count);
+}
+
+static void
+bcm_sdhci_start_dma_seg(struct bcm_sdhci_softc *sc)
+{
+ struct sdhci_slot *slot;
+ vm_paddr_t pdst, psrc;
+ int err, idx, len, sync_op, width;
+
+ slot = &sc->sc_slot;
+ mtx_assert(&slot->mtx, MA_OWNED);
+ idx = sc->dmamap_seg_index++;
+ len = sc->dmamap_seg_sizes[idx];
+ slot->offset += len;
+ width = (len & 0xf ? BCM_DMA_32BIT : BCM_DMA_128BIT);
+
+ if (slot->curcmd->data->flags & MMC_DATA_READ) {
+ /*
+ * Peripherals on the AXI bus do not need DREQ pacing for reads
+ * from the ARM core, so we can safely set this to NONE.
+ */
+ bcm_dma_setup_src(sc->sc_dma_ch, BCM_DMA_DREQ_NONE,
+ BCM_DMA_SAME_ADDR, BCM_DMA_32BIT);
+ bcm_dma_setup_dst(sc->sc_dma_ch, BCM_DMA_DREQ_NONE,
+ BCM_DMA_INC_ADDR, width);
+ psrc = sc->sc_sdhci_buffer_phys;
+ pdst = sc->dmamap_seg_addrs[idx];
+ sync_op = BUS_DMASYNC_PREREAD;
+ } else {
+ /*
+ * The ordering here is important, because the last write to
+ * dst/src in the dma control block writes the real dreq value.
+ */
+ bcm_dma_setup_src(sc->sc_dma_ch, BCM_DMA_DREQ_NONE,
+ BCM_DMA_INC_ADDR, width);
+ bcm_dma_setup_dst(sc->sc_dma_ch, sc->conf->emmc_dreq,
+ BCM_DMA_SAME_ADDR, BCM_DMA_32BIT);
+ psrc = sc->dmamap_seg_addrs[idx];
+ pdst = sc->sc_sdhci_buffer_phys;
+ sync_op = BUS_DMASYNC_PREWRITE;
+ }
+
+ /*
+ * When starting a new DMA operation do the busdma sync operation, and
+ * disable SDCHI data interrrupts because we'll be driven by DMA
+ * interrupts (or SDHCI error interrupts) until the IO is done.
+ */
+ if (idx == 0) {
+ bus_dmamap_sync(sc->sc_dma_tag, sc->sc_dma_map, sync_op);
+
+ slot->intmask &= ~DATA_XFER_MASK;
+ bcm_sdhci_write_4(sc->sc_dev, slot, SDHCI_SIGNAL_ENABLE,
+ slot->intmask);
+ }
+
+ /*
+ * Start the DMA transfer. Only programming errors (like failing to
+ * allocate a channel) cause a non-zero return from bcm_dma_start().
+ */
+ err = bcm_dma_start(sc->sc_dma_ch, psrc, pdst, len);
+ KASSERT((err == 0), ("bcm2835_sdhci: failed DMA start"));
+}
+
+static void
+bcm_sdhci_dma_exit(struct bcm_sdhci_softc *sc)
+{
+ struct sdhci_slot *slot = &sc->sc_slot;
+
+ mtx_assert(&slot->mtx, MA_OWNED);
+
+ /* Re-enable interrupts */
+ slot->intmask |= DATA_XFER_MASK;
+ bcm_sdhci_write_4(slot->bus, slot, SDHCI_SIGNAL_ENABLE,
+ slot->intmask);
+}
+
+static void
+bcm_sdhci_dma_unload(struct bcm_sdhci_softc *sc)
+{
+ struct sdhci_slot *slot = &sc->sc_slot;
+
+ if (sc->dmamap_seg_count == 0)
+ return;
+ if ((slot->curcmd->data->flags & MMC_DATA_READ) != 0)
+ bus_dmamap_sync(sc->sc_dma_tag, sc->sc_dma_map,
+ BUS_DMASYNC_POSTREAD);
+ else
+ bus_dmamap_sync(sc->sc_dma_tag, sc->sc_dma_map,
+ BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(sc->sc_dma_tag, sc->sc_dma_map);
+
+ sc->dmamap_seg_count = 0;
+ sc->dmamap_seg_index = 0;
+}
+
+static void
+bcm_sdhci_dma_intr(int ch, void *arg)
+{
+ struct bcm_sdhci_softc *sc = (struct bcm_sdhci_softc *)arg;
+ struct sdhci_slot *slot = &sc->sc_slot;
+ uint32_t reg;
+
+ mtx_lock(&slot->mtx);
+ if (slot->curcmd == NULL)
+ goto out;
+ /*
+ * If there are more segments for the current dma, start the next one.
+ * Otherwise unload the dma map and decide what to do next based on the
+ * status of the sdhci controller and whether there's more data left.
+ */
+ if (sc->dmamap_seg_index < sc->dmamap_seg_count) {
+ bcm_sdhci_start_dma_seg(sc);
+ goto out;
+ }
+
+ bcm_sdhci_dma_unload(sc);
+
+ /*
+ * If we had no further segments pending, we need to determine how to
+ * proceed next. If the 'data/space pending' bit is already set and we
+ * can continue via DMA, do so. Otherwise, re-enable interrupts and
+ * return.
+ */
+ reg = bcm_sdhci_read_4(slot->bus, slot, SDHCI_INT_STATUS) &
+ DATA_XFER_MASK;
+ if ((reg & DATA_PENDING_MASK) != 0 &&
+ BCM_SDHCI_SEGSZ_LEFT(slot) >= BCM_SDHCI_BUFFER_SIZE) {
+ /* ACK any pending interrupts */
+ bcm_sdhci_write_4(slot->bus, slot, SDHCI_INT_STATUS,
+ DATA_PENDING_MASK);
+
+ bcm_sdhci_start_dma(slot);
+ if (slot->curcmd->error != 0) {
+ /* We won't recover from this error for this command. */
+ bcm_sdhci_dma_unload(sc);
+ bcm_sdhci_dma_exit(sc);
+ sdhci_finish_data(slot);
+ }
+ } else if ((reg & SDHCI_INT_DATA_END) != 0) {
+ bcm_sdhci_dma_exit(sc);
+ bcm_sdhci_write_4(slot->bus, slot, SDHCI_INT_STATUS,
+ reg);
+ slot->flags &= ~PLATFORM_DATA_STARTED;
+ sdhci_finish_data(slot);
+ } else {
+ bcm_sdhci_dma_exit(sc);
+ }
+out:
+ mtx_unlock(&slot->mtx);
+}
+
+static void
+bcm_sdhci_start_dma(struct sdhci_slot *slot)
+{
+ struct bcm_sdhci_softc *sc = device_get_softc(slot->bus);
+ uint8_t *buf;
+ size_t left;
+
+ mtx_assert(&slot->mtx, MA_OWNED);
+
+ left = BCM_SDHCI_SEGSZ_LEFT(slot);
+ buf = (uint8_t *)slot->curcmd->data->data + slot->offset;
+ KASSERT(left != 0,
+ ("%s: DMA handling incorrectly indicated", __func__));
+
+ /*
+ * No need to check segment count here; if we've not yet unloaded
+ * previous segments, we'll catch that in bcm_sdhci_dmacb.
+ */
+ if (bus_dmamap_load(sc->sc_dma_tag, sc->sc_dma_map, buf, left,
+ bcm_sdhci_dmacb, sc, BUS_DMA_NOWAIT) != 0 ||
+ sc->dmamap_status != 0) {
+ slot->curcmd->error = MMC_ERR_NO_MEMORY;
+ return;
+ }
+
+ /* DMA start */
+ bcm_sdhci_start_dma_seg(sc);
+}
+
+static int
+bcm_sdhci_will_handle_transfer(device_t dev, struct sdhci_slot *slot)
+{
+#ifdef INVARIANTS
+ struct bcm_sdhci_softc *sc = device_get_softc(slot->bus);
+#endif
+
+ /*
+ * This indicates that we somehow let a data interrupt slip by into the
+ * SDHCI framework, when it should not have. This really needs to be
+ * caught and fixed ASAP, as it really shouldn't happen.
+ */
+ KASSERT(sc->dmamap_seg_count == 0,
+ ("data pending interrupt pushed through SDHCI framework"));
+
+ /*
+ * Do not use DMA for transfers less than our block size. Checking
+ * alignment serves little benefit, as we round transfer sizes down to
+ * a multiple of the block size and push the transfer back to
+ * SDHCI-driven PIO once we're below the block size.
+ */
+ if (BCM_SDHCI_SEGSZ_LEFT(slot) < BCM_DMA_BLOCK_SIZE)
+ return (0);
+
+ return (1);
+}
+
+static void
+bcm_sdhci_start_transfer(device_t dev, struct sdhci_slot *slot,
+ uint32_t *intmask)
+{
+
+ /* DMA transfer FIFO 1KB */
+ bcm_sdhci_start_dma(slot);
+}
+
+static void
+bcm_sdhci_finish_transfer(device_t dev, struct sdhci_slot *slot)
+{
+ struct bcm_sdhci_softc *sc = device_get_softc(slot->bus);
+
+ /*
+ * Clean up. Interrupts are clearly enabled, because we received an
+ * SDHCI_INT_DATA_END to get this far -- just make sure we don't leave
+ * anything laying around.
+ */
+ if (sc->dmamap_seg_count != 0) {
+ /*
+ * Our segment math should have worked out such that we would
+ * never finish the transfer without having used up all of the
+ * segments. If we haven't, that means we must have erroneously
+ * regressed to SDHCI-driven PIO to finish the operation and
+ * this is certainly caused by developer-error.
+ */
+ bcm_sdhci_dma_unload(sc);
+ }
+
+ sdhci_finish_data(slot);
+}
+
+static device_method_t bcm_sdhci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, bcm_sdhci_probe),
+ DEVMETHOD(device_attach, bcm_sdhci_attach),
+ DEVMETHOD(device_detach, bcm_sdhci_detach),
+
+ /* Bus interface */
+ DEVMETHOD(bus_read_ivar, sdhci_generic_read_ivar),
+ DEVMETHOD(bus_write_ivar, sdhci_generic_write_ivar),
+ DEVMETHOD(bus_add_child, bus_generic_add_child),
+
+ /* MMC bridge interface */
+ DEVMETHOD(mmcbr_update_ios, bcm_sdhci_update_ios),
+ DEVMETHOD(mmcbr_request, sdhci_generic_request),
+ DEVMETHOD(mmcbr_get_ro, bcm_sdhci_get_ro),
+ DEVMETHOD(mmcbr_acquire_host, sdhci_generic_acquire_host),
+ DEVMETHOD(mmcbr_release_host, sdhci_generic_release_host),
+
+ /* Platform transfer methods */
+ DEVMETHOD(sdhci_platform_will_handle, bcm_sdhci_will_handle_transfer),
+ DEVMETHOD(sdhci_platform_start_transfer, bcm_sdhci_start_transfer),
+ DEVMETHOD(sdhci_platform_finish_transfer, bcm_sdhci_finish_transfer),
+ /* SDHCI registers accessors */
+ DEVMETHOD(sdhci_read_1, bcm_sdhci_read_1),
+ DEVMETHOD(sdhci_read_2, bcm_sdhci_read_2),
+ DEVMETHOD(sdhci_read_4, bcm_sdhci_read_4),
+ DEVMETHOD(sdhci_read_multi_4, bcm_sdhci_read_multi_4),
+ DEVMETHOD(sdhci_write_1, bcm_sdhci_write_1),
+ DEVMETHOD(sdhci_write_2, bcm_sdhci_write_2),
+ DEVMETHOD(sdhci_write_4, bcm_sdhci_write_4),
+ DEVMETHOD(sdhci_write_multi_4, bcm_sdhci_write_multi_4),
+
+ DEVMETHOD_END
+};
+
+static devclass_t bcm_sdhci_devclass;
+
+static driver_t bcm_sdhci_driver = {
+ "sdhci_bcm",
+ bcm_sdhci_methods,
+ sizeof(struct bcm_sdhci_softc),
+};
+
+DRIVER_MODULE(sdhci_bcm, simplebus, bcm_sdhci_driver, bcm_sdhci_devclass,
+ NULL, NULL);
+#ifdef NOTYET
+MODULE_DEPEND(sdhci_bcm, bcm2835_clkman, 1, 1, 1);
+#endif
+SDHCI_DEPEND(sdhci_bcm);
+#ifndef MMCCAM
+MMC_DECLARE_BRIDGE(sdhci_bcm);
+#endif
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_sdhost.c b/sys/arm/broadcom/bcm2835/bcm2835_sdhost.c
new file mode 100644
index 000000000000..5eb94c6f360f
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2835_sdhost.c
@@ -0,0 +1,1290 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2018 Klaus P. Ohrhallinger <k@7he.at>
+ * All rights reserved.
+ *
+ * Based on bcm2835_sdhci.c:
+ * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * pin 48-53 - card slot
+ * pin 34-39 - radio module
+ *
+ * alt-0 - rubbish SDHCI (0x7e202000) aka sdhost
+ * alt-3 - advanced SDHCI (0x7e300000) aka sdhci/mmc/sdio
+ *
+ * driving card slot with mmc:
+ *
+ * sdhost_pins {
+ * brcm,pins = <0x30 0x31 0x32 0x33 0x34 0x35>;
+ * brcm,function = <0x7>;
+ * brcm,pull = <0x0 0x2 0x2 0x2 0x2 0x2>;
+ * phandle = <0x17>;
+ * };
+ * sdio_pins {
+ * brcm,pins = <0x22 0x23 0x24 0x25 0x26 0x27>;
+ * brcm,function = <0x4>;
+ * brcm,pull = <0x0 0x2 0x2 0x2 0x2 0x2>;
+ * phandle = <0x18>;
+ * };
+ *
+ * driving card slot with sdhost:
+ *
+ * sdhost_pins {
+ * brcm,pins = <0x30 0x31 0x32 0x33 0x34 0x35>;
+ * brcm,function = <0x4>;
+ * brcm,pull = <0x0 0x2 0x2 0x2 0x2 0x2>;
+ * phandle = <0x17>;
+ * };
+ * sdio_pins {
+ * brcm,pins = <0x22 0x23 0x24 0x25 0x26 0x27>;
+ * brcm,function = <0x7>;
+ * brcm,pull = <0x0 0x2 0x2 0x2 0x2 0x2>;
+ * phandle = <0x18>;
+ * };
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kobj.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+#include <sys/taskqueue.h>
+#include <sys/gpio.h>
+
+#include <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/mmc/bridge.h>
+#include <dev/mmc/mmcreg.h>
+
+#include <dev/sdhci/sdhci.h>
+
+#include "mmcbr_if.h"
+#include "sdhci_if.h"
+
+#include "opt_mmccam.h"
+
+#include "bcm2835_dma.h"
+#include <arm/broadcom/bcm2835/bcm2835_mbox_prop.h>
+#include "bcm2835_vcbus.h"
+
+/* #define SDHOST_DEBUG */
+
+/* Registers */
+#define HC_COMMAND 0x00 /* Command and flags */
+#define HC_ARGUMENT 0x04
+#define HC_TIMEOUTCOUNTER 0x08
+#define HC_CLOCKDIVISOR 0x0c
+#define HC_RESPONSE_0 0x10
+#define HC_RESPONSE_1 0x14
+#define HC_RESPONSE_2 0x18
+#define HC_RESPONSE_3 0x1c
+#define HC_HOSTSTATUS 0x20
+#define HC_POWER 0x30
+#define HC_DEBUG 0x34
+#define HC_HOSTCONFIG 0x38
+#define HC_BLOCKSIZE 0x3c
+#define HC_DATAPORT 0x40
+#define HC_BLOCKCOUNT 0x50
+
+/* Flags for HC_COMMAND register */
+#define HC_CMD_ENABLE 0x8000
+#define HC_CMD_FAILED 0x4000
+#define HC_CMD_BUSY 0x0800
+#define HC_CMD_RESPONSE_NONE 0x0400
+#define HC_CMD_RESPONSE_LONG 0x0200
+#define HC_CMD_WRITE 0x0080
+#define HC_CMD_READ 0x0040
+#define HC_CMD_COMMAND_MASK 0x003f
+
+#define HC_CLOCKDIVISOR_MAXVAL 0x07ff
+
+/* Flags for HC_HOSTSTATUS register */
+#define HC_HSTST_HAVEDATA 0x0001
+#define HC_HSTST_ERROR_FIFO 0x0008
+#define HC_HSTST_ERROR_CRC7 0x0010
+#define HC_HSTST_ERROR_CRC16 0x0020
+#define HC_HSTST_TIMEOUT_CMD 0x0040
+#define HC_HSTST_TIMEOUT_DATA 0x0080
+#define HC_HSTST_INT_BLOCK 0x0200
+#define HC_HSTST_INT_BUSY 0x0400
+
+#define HC_HSTST_RESET 0xffff
+
+#define HC_HSTST_MASK_ERROR_DATA (HC_HSTST_ERROR_FIFO | \
+ HC_HSTST_ERROR_CRC7 | HC_HSTST_ERROR_CRC16 | HC_HSTST_TIMEOUT_DATA)
+
+#define HC_HSTST_MASK_ERROR_ALL (HC_HSTST_MASK_ERROR_DATA | \
+ HC_HSTST_TIMEOUT_CMD)
+
+/* Flags for HC_HOSTCONFIG register */
+#define HC_HSTCF_INTBUS_WIDE 0x0002
+#define HC_HSTCF_EXTBUS_4BIT 0x0004
+#define HC_HSTCF_SLOW_CARD 0x0008
+#define HC_HSTCF_INT_DATA 0x0010
+#define HC_HSTCF_INT_BLOCK 0x0100
+#define HC_HSTCF_INT_BUSY 0x0400
+
+/* Flags for HC_DEBUG register */
+#define HC_DBG_FIFO_THRESH_WRITE_SHIFT 9
+#define HC_DBG_FIFO_THRESH_READ_SHIFT 14
+#define HC_DBG_FIFO_THRESH_MASK 0x001f
+
+/* Settings */
+#define HC_FIFO_SIZE 16
+#define HC_FIFO_THRESH_READ 4
+#define HC_FIFO_THRESH_WRITE 4
+
+#define HC_TIMEOUT_DEFAULT 0x00f00000
+
+#define BCM2835_DEFAULT_SDHCI_FREQ 50
+
+static int bcm2835_sdhost_debug = 0;
+
+#ifdef SDHOST_DEBUG
+
+TUNABLE_INT("hw.bcm2835.sdhost.debug", &bcm2835_sdhost_debug);
+SYSCTL_INT(_hw_sdhci, OID_AUTO, bcm2835_sdhost_debug, CTLFLAG_RWTUN,
+ &bcm2835_sdhost_debug, 0, "bcm2835-sdhost Debug level");
+
+#define dprintf(fmt, args...) \
+ do { \
+ if (bcm2835_sdhost_debug > 0) \
+ printf(fmt,##args); \
+ } while (0)
+#else
+
+#define dprintf(fmt, args...)
+
+#endif /* ! SDHOST_DEBUG */
+
+static struct ofw_compat_data compat_data[] = {
+ {"brcm,bcm2835-sdhost", 1},
+ {NULL, 0}
+};
+
+struct bcm_sdhost_softc {
+ device_t sc_dev;
+ struct resource * sc_mem_res;
+ struct resource * sc_irq_res;
+ bus_space_tag_t sc_bst;
+ bus_space_handle_t sc_bsh;
+ void * sc_intrhand;
+ struct mmc_request * sc_req;
+ struct sdhci_slot sc_slot;
+
+ struct mtx mtx;
+
+ char cmdbusy;
+ char mmc_app_cmd;
+
+ u_int32_t sdhci_int_status;
+ u_int32_t sdhci_signal_enable;
+ u_int32_t sdhci_present_state;
+ u_int32_t sdhci_blocksize;
+ u_int32_t sdhci_blockcount;
+
+ u_int32_t sdcard_rca;
+};
+
+static int bcm_sdhost_probe(device_t);
+static int bcm_sdhost_attach(device_t);
+static int bcm_sdhost_detach(device_t);
+static void bcm_sdhost_intr(void *);
+
+static int bcm_sdhost_get_ro(device_t, device_t);
+
+static inline uint32_t
+RD4(struct bcm_sdhost_softc *sc, bus_size_t off)
+{
+ uint32_t val;
+
+ val = bus_space_read_4(sc->sc_bst, sc->sc_bsh, off);
+
+ return (val);
+}
+
+static inline void
+WR4(struct bcm_sdhost_softc *sc, bus_size_t off, uint32_t val)
+{
+
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, off, val);
+}
+
+#ifdef notyet
+static inline uint16_t
+RD2(struct bcm_sdhost_softc *sc, bus_size_t off)
+{
+ uint32_t val;
+
+ val = RD4(sc, off & ~3);
+
+ return ((val >> (off & 3)*8) & 0xffff);
+}
+#endif
+
+static inline uint8_t
+RD1(struct bcm_sdhost_softc *sc, bus_size_t off)
+{
+ uint32_t val;
+
+ val = RD4(sc, off & ~3);
+
+ return ((val >> (off & 3)*8) & 0xff);
+}
+
+static inline void
+WR2(struct bcm_sdhost_softc *sc, bus_size_t off, uint16_t val)
+{
+ uint32_t val32;
+
+ val32 = RD4(sc, off & ~3);
+ val32 &= ~(0xffff << (off & 3)*8);
+ val32 |= (val << (off & 3)*8);
+ WR4(sc, off & ~3, val32);
+}
+
+static inline void
+WR1(struct bcm_sdhost_softc *sc, bus_size_t off, uint8_t val)
+{
+ uint32_t val32;
+
+ val32 = RD4(sc, off & ~3);
+ val32 &= ~(0xff << (off & 3)*8);
+ val32 |= (val << (off & 3)*8);
+ WR4(sc, off & ~3, val32);
+}
+
+static void
+bcm_sdhost_print_regs(struct bcm_sdhost_softc *sc, struct sdhci_slot *slot,
+ int line, int error)
+{
+
+ if (bcm2835_sdhost_debug > 0 || error > 0) {
+ printf("%s: sc=%p slot=%p\n",
+ __func__, sc, slot);
+ printf("HC_COMMAND: 0x%08x\n",
+ RD4(sc, HC_COMMAND));
+ printf("HC_ARGUMENT: 0x%08x\n",
+ RD4(sc, HC_ARGUMENT));
+ printf("HC_TIMEOUTCOUNTER: 0x%08x\n",
+ RD4(sc, HC_TIMEOUTCOUNTER));
+ printf("HC_CLOCKDIVISOR: 0x%08x\n",
+ RD4(sc, HC_CLOCKDIVISOR));
+ printf("HC_RESPONSE_0: 0x%08x\n",
+ RD4(sc, HC_RESPONSE_0));
+ printf("HC_RESPONSE_1: 0x%08x\n",
+ RD4(sc, HC_RESPONSE_1));
+ printf("HC_RESPONSE_2: 0x%08x\n",
+ RD4(sc, HC_RESPONSE_2));
+ printf("HC_RESPONSE_3: 0x%08x\n",
+ RD4(sc, HC_RESPONSE_3));
+ printf("HC_HOSTSTATUS: 0x%08x\n",
+ RD4(sc, HC_HOSTSTATUS));
+ printf("HC_POWER: 0x%08x\n",
+ RD4(sc, HC_POWER));
+ printf("HC_DEBUG: 0x%08x\n",
+ RD4(sc, HC_DEBUG));
+ printf("HC_HOSTCONFIG: 0x%08x\n",
+ RD4(sc, HC_HOSTCONFIG));
+ printf("HC_BLOCKSIZE: 0x%08x\n",
+ RD4(sc, HC_BLOCKSIZE));
+ printf("HC_BLOCKCOUNT: 0x%08x\n",
+ RD4(sc, HC_BLOCKCOUNT));
+
+ } else {
+ /*
+ printf("%04d | HC_COMMAND: 0x%08x HC_ARGUMENT: 0x%08x "
+ "HC_HOSTSTATUS: 0x%08x HC_HOSTCONFIG: 0x%08x\n",
+ line, RD4(sc, HC_COMMAND), RD4(sc, HC_ARGUMENT),
+ RD4(sc, HC_HOSTSTATUS), RD4(sc, HC_HOSTCONFIG));
+ */
+ }
+}
+
+static void
+bcm_sdhost_reset(device_t dev, struct sdhci_slot *slot)
+{
+ struct bcm_sdhost_softc *sc = device_get_softc(dev);
+ u_int32_t dbg;
+
+ WR4(sc, HC_POWER, 0);
+
+ WR4(sc, HC_COMMAND, 0);
+ WR4(sc, HC_ARGUMENT, 0);
+ WR4(sc, HC_TIMEOUTCOUNTER, HC_TIMEOUT_DEFAULT);
+ WR4(sc, HC_CLOCKDIVISOR, 0);
+ WR4(sc, HC_HOSTSTATUS, HC_HSTST_RESET);
+ WR4(sc, HC_HOSTCONFIG, 0);
+ WR4(sc, HC_BLOCKSIZE, 0);
+ WR4(sc, HC_BLOCKCOUNT, 0);
+
+ dbg = RD4(sc, HC_DEBUG);
+ dbg &= ~( (HC_DBG_FIFO_THRESH_MASK << HC_DBG_FIFO_THRESH_READ_SHIFT) |
+ (HC_DBG_FIFO_THRESH_MASK << HC_DBG_FIFO_THRESH_WRITE_SHIFT) );
+ dbg |= (HC_FIFO_THRESH_READ << HC_DBG_FIFO_THRESH_READ_SHIFT) |
+ (HC_FIFO_THRESH_WRITE << HC_DBG_FIFO_THRESH_WRITE_SHIFT);
+ WR4(sc, HC_DEBUG, dbg);
+
+ DELAY(250000);
+
+ WR4(sc, HC_POWER, 1);
+
+ DELAY(250000);
+
+ sc->sdhci_present_state = SDHCI_CARD_PRESENT | SDHCI_CARD_STABLE |
+ SDHCI_WRITE_PROTECT;
+
+ WR4(sc, HC_CLOCKDIVISOR, HC_CLOCKDIVISOR_MAXVAL);
+ WR4(sc, HC_HOSTCONFIG, HC_HSTCF_INT_BUSY);
+}
+
+static int
+bcm_sdhost_probe(device_t dev)
+{
+
+ dprintf("%s:\n", __func__);
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Broadcom 2708 SDHOST controller");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+bcm_sdhost_attach(device_t dev)
+{
+ struct bcm_sdhost_softc *sc = device_get_softc(dev);
+ int rid, err;
+ u_int default_freq;
+
+ dprintf("%s: dev=%p sc=%p unit=%d\n",
+ __func__, dev, sc, device_get_unit(dev));
+
+ mtx_init(&sc->mtx, "BCM SDHOST mtx", "bcm_sdhost",
+ MTX_DEF | MTX_RECURSE);
+
+ sc->sc_dev = dev;
+ sc->sc_req = NULL;
+
+ sc->cmdbusy = 0;
+ sc->mmc_app_cmd = 0;
+ sc->sdhci_int_status = 0;
+ sc->sdhci_signal_enable = 0;
+ sc->sdhci_present_state = 0;
+ sc->sdhci_blocksize = 0;
+ sc->sdhci_blockcount = 0;
+
+ sc->sdcard_rca = 0;
+
+ default_freq = 50;
+ err = 0;
+
+ if (bootverbose)
+ device_printf(dev, "SDHCI frequency: %dMHz\n", default_freq);
+
+ rid = 0;
+ sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (!sc->sc_mem_res) {
+ device_printf(dev, "cannot allocate memory window\n");
+ err = ENXIO;
+ goto fail;
+ }
+
+ sc->sc_bst = rman_get_bustag(sc->sc_mem_res);
+ sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res);
+
+ bcm_sdhost_reset(dev, &sc->sc_slot);
+
+ bcm_sdhost_print_regs(sc, &sc->sc_slot, __LINE__, 0);
+
+ rid = 0;
+ sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (!sc->sc_irq_res) {
+ device_printf(dev, "cannot allocate interrupt\n");
+ err = ENXIO;
+ goto fail;
+ }
+
+ if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ NULL, bcm_sdhost_intr, sc, &sc->sc_intrhand)) {
+ device_printf(dev, "cannot setup interrupt handler\n");
+ err = ENXIO;
+ goto fail;
+ }
+
+ sc->sc_slot.caps = 0;
+ sc->sc_slot.caps |= SDHCI_CAN_VDD_330;
+ sc->sc_slot.caps |= SDHCI_CAN_DO_HISPD;
+ sc->sc_slot.caps |= (default_freq << SDHCI_CLOCK_BASE_SHIFT);
+
+ sc->sc_slot.quirks = 0;
+ sc->sc_slot.quirks |= SDHCI_QUIRK_MISSING_CAPS;
+ sc->sc_slot.quirks |= SDHCI_QUIRK_DONT_SHIFT_RESPONSE;
+
+ sc->sc_slot.opt = 0;
+
+ /* XXX ?
+ sc->slot->timeout_clk = ...;
+ */
+
+ sdhci_init_slot(dev, &sc->sc_slot, 0);
+
+ bus_generic_probe(dev);
+ bus_generic_attach(dev);
+
+ sdhci_start_slot(&sc->sc_slot);
+
+ return (0);
+
+ fail:
+ if (sc->sc_intrhand)
+ bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand);
+ if (sc->sc_irq_res)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
+ if (sc->sc_mem_res)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+
+ return (err);
+}
+
+static int
+bcm_sdhost_detach(device_t dev)
+{
+
+ dprintf("%s:\n", __func__);
+
+ return (EBUSY);
+}
+
+/*
+ * rv 0 --> command finished
+ * rv 1 --> command timed out
+ */
+static inline int
+bcm_sdhost_waitcommand(struct bcm_sdhost_softc *sc)
+{
+ int timeout = 1000;
+
+ mtx_assert(&sc->mtx, MA_OWNED);
+
+ while ((RD4(sc, HC_COMMAND) & HC_CMD_ENABLE) && --timeout > 0) {
+ DELAY(100);
+ }
+
+ return ((timeout > 0) ? 0 : 1);
+}
+
+static int
+bcm_sdhost_waitcommand_status(struct bcm_sdhost_softc *sc)
+{
+ u_int32_t cdst;
+ int i;
+
+ /* wait for card to change status from
+ * ''prg'' to ''trn''
+ * card status: sd specs p. 103
+ */
+ i = 0;
+ do {
+ DELAY(1000);
+ WR4(sc, HC_ARGUMENT, sc->sdcard_rca << 16);
+ WR4(sc, HC_COMMAND,
+ MMC_SEND_STATUS | HC_CMD_ENABLE);
+ bcm_sdhost_waitcommand(sc);
+ cdst = RD4(sc, HC_RESPONSE_0);
+ dprintf("%s: card status %08x (cs %d)\n",
+ __func__, cdst, (cdst & 0x0e00) >> 9);
+ if (i++ > 100) {
+ printf("%s: giving up, "
+ "card status %08x (cs %d)\n",
+ __func__, cdst,
+ (cdst & 0x0e00) >> 9);
+ return (1);
+ break;
+ }
+ } while (((cdst & 0x0e00) >> 9) != 4);
+
+ return (0);
+}
+
+static void
+bcm_sdhost_intr(void *arg)
+{
+ struct bcm_sdhost_softc *sc = arg;
+ struct sdhci_slot *slot = &sc->sc_slot;
+ uint32_t hstst;
+ uint32_t cmd;
+
+ mtx_lock(&sc->mtx);
+
+ hstst = RD4(sc, HC_HOSTSTATUS);
+ cmd = RD4(sc, HC_COMMAND);
+ if (hstst & HC_HSTST_HAVEDATA) {
+ if (cmd & HC_CMD_READ) {
+ sc->sdhci_present_state |= SDHCI_DATA_AVAILABLE;
+ sc->sdhci_int_status |= SDHCI_INT_DATA_AVAIL;
+ } else if (cmd & HC_CMD_WRITE) {
+ sc->sdhci_present_state |= SDHCI_SPACE_AVAILABLE;
+ sc->sdhci_int_status |= SDHCI_INT_SPACE_AVAIL;
+ } else {
+ panic("%s: hstst & HC_HSTST_HAVEDATA but no "
+ "HC_CMD_READ or HC_CMD_WRITE: cmd=%0x8 "
+ "hstst=%08x\n", __func__, cmd, hstst);
+ }
+ } else {
+ sc->sdhci_present_state &=
+ ~(SDHCI_DATA_AVAILABLE|SDHCI_SPACE_AVAILABLE);
+ sc->sdhci_int_status &=
+ ~(SDHCI_INT_DATA_AVAIL|SDHCI_INT_SPACE_AVAIL);
+ }
+
+ if (hstst & HC_HSTST_MASK_ERROR_ALL) {
+ printf("%s: ERROR: HC_HOSTSTATUS: %08x\n", __func__, hstst);
+ bcm_sdhost_print_regs(sc, &sc->sc_slot, __LINE__, 1);
+ sc->sdhci_int_status |= SDHCI_INT_ERROR;
+ } else {
+ sc->sdhci_int_status &= ~SDHCI_INT_ERROR;
+ }
+
+ dprintf("%s: hstst=%08x offset=%08lx sdhci_present_state=%08x "
+ "sdhci_int_status=%08x\n", __func__, hstst, slot->offset,
+ sc->sdhci_present_state, sc->sdhci_int_status);
+
+ sdhci_generic_intr(&sc->sc_slot);
+
+ sc->sdhci_int_status &=
+ ~(SDHCI_INT_ERROR|SDHCI_INT_DATA_AVAIL|SDHCI_INT_DATA_END);
+ sc->sdhci_present_state &= ~SDHCI_DATA_AVAILABLE;
+
+ if ((hstst & HC_HSTST_HAVEDATA) &&
+ (sc->sdhci_blocksize * sc->sdhci_blockcount == slot->offset)) {
+ dprintf("%s: offset=%08lx sdhci_blocksize=%08x "
+ "sdhci_blockcount=%08x\n", __func__, slot->offset,
+ sc->sdhci_blocksize, sc->sdhci_blockcount);
+ sc->sdhci_int_status &=
+ ~(SDHCI_INT_DATA_AVAIL|SDHCI_INT_SPACE_AVAIL);
+ sc->sdhci_int_status |= SDHCI_INT_DATA_END;
+ sdhci_generic_intr(&sc->sc_slot);
+ sc->sdhci_int_status &= ~SDHCI_INT_DATA_END;
+
+ if ((cmd & HC_CMD_COMMAND_MASK) == MMC_READ_MULTIPLE_BLOCK ||
+ (cmd & HC_CMD_COMMAND_MASK) == MMC_WRITE_MULTIPLE_BLOCK) {
+ WR4(sc, HC_ARGUMENT, 0x00000000);
+ WR4(sc, HC_COMMAND,
+ MMC_STOP_TRANSMISSION | HC_CMD_ENABLE);
+
+ if (bcm_sdhost_waitcommand(sc)) {
+ printf("%s: timeout #1\n", __func__);
+ bcm_sdhost_print_regs(sc, &sc->sc_slot,
+ __LINE__, 1);
+ }
+ }
+
+ if (cmd & HC_CMD_WRITE) {
+ if (bcm_sdhost_waitcommand_status(sc) != 0)
+ sc->sdhci_int_status |= SDHCI_INT_ERROR;
+ }
+
+ slot->data_done = 1;
+
+ sc->sdhci_int_status |= SDHCI_INT_RESPONSE;
+ sdhci_generic_intr(&sc->sc_slot);
+ sc->sdhci_int_status &= ~(SDHCI_INT_RESPONSE|SDHCI_INT_ERROR);
+ }
+
+ /* this resets the interrupt */
+ WR4(sc, HC_HOSTSTATUS,
+ (HC_HSTST_INT_BUSY|HC_HSTST_INT_BLOCK|HC_HSTST_HAVEDATA));
+
+ mtx_unlock(&sc->mtx);
+}
+
+static int
+bcm_sdhost_get_ro(device_t bus, device_t child)
+{
+
+ dprintf("%s:\n", __func__);
+
+ return (0);
+}
+
+static bool
+bcm_sdhost_get_card_present(device_t dev, struct sdhci_slot *slot)
+{
+
+ dprintf("%s:\n", __func__);
+
+ return (1);
+}
+
+static void
+bcm_sdhost_command(device_t dev, struct sdhci_slot *slot, uint16_t val)
+{
+ struct bcm_sdhost_softc *sc = device_get_softc(dev);
+ struct mmc_data *data = slot->curcmd->data;
+ uint16_t val2;
+ uint8_t opcode;
+ uint8_t flags;
+
+ mtx_assert(&sc->mtx, MA_OWNED);
+
+ if (RD4(sc, HC_COMMAND) & HC_CMD_ENABLE) {
+ panic("%s: HC_CMD_ENABLE on entry\n", __func__);
+ }
+
+ if (sc->cmdbusy == 1)
+ panic("%s: cmdbusy\n", __func__);
+
+ sc->cmdbusy = 1;
+
+ val2 = ((val >> 8) & HC_CMD_COMMAND_MASK) | HC_CMD_ENABLE;
+
+ opcode = val >> 8;
+ flags = val & 0xff;
+
+ if (opcode == MMC_APP_CMD)
+ sc->mmc_app_cmd = 1;
+
+ if ((flags & SDHCI_CMD_RESP_MASK) == SDHCI_CMD_RESP_LONG)
+ val2 |= HC_CMD_RESPONSE_LONG;
+ else if ((flags & SDHCI_CMD_RESP_MASK) == SDHCI_CMD_RESP_SHORT_BUSY)
+ /* XXX XXX when enabled, cmd 7 (select card) blocks forever */
+ ;/*val2 |= HC_CMD_BUSY; */
+ else if ((flags & SDHCI_CMD_RESP_MASK) == SDHCI_CMD_RESP_SHORT)
+ ;
+ else
+ val2 |= HC_CMD_RESPONSE_NONE;
+
+ if (val2 & HC_CMD_BUSY)
+ sc->sdhci_present_state |=
+ SDHCI_CMD_INHIBIT | SDHCI_DAT_INHIBIT;
+
+ if (data != NULL && data->flags & MMC_DATA_READ)
+ val2 |= HC_CMD_READ;
+ else if (data != NULL && data->flags & MMC_DATA_WRITE)
+ val2 |= HC_CMD_WRITE;
+
+ dprintf("%s: SDHCI_COMMAND_FLAGS --> HC_COMMAND %04x --> %04x\n",
+ __func__, val, val2);
+
+ if (opcode == MMC_READ_MULTIPLE_BLOCK ||
+ opcode == MMC_WRITE_MULTIPLE_BLOCK) {
+ u_int32_t save_sdarg;
+
+ dprintf("%s: issuing MMC_SET_BLOCK_COUNT: CMD %08x ARG %08x\n",
+ __func__, MMC_SET_BLOCK_COUNT | HC_CMD_ENABLE,
+ sc->sdhci_blockcount);
+
+ save_sdarg = RD4(sc, HC_ARGUMENT);
+ WR4(sc, HC_ARGUMENT, sc->sdhci_blockcount);
+ WR4(sc, HC_COMMAND, MMC_SET_BLOCK_COUNT | HC_CMD_ENABLE);
+
+ /* Seems to always return timeout */
+
+ if (bcm_sdhost_waitcommand(sc)) {
+ printf("%s: timeout #2\n", __func__);
+ bcm_sdhost_print_regs(sc, &sc->sc_slot, __LINE__, 1);
+ } else {
+ bcm_sdhost_print_regs(sc, &sc->sc_slot, __LINE__, 0);
+ }
+ WR4(sc, HC_ARGUMENT, save_sdarg);
+
+ } else if (opcode == MMC_SELECT_CARD) {
+ sc->sdcard_rca = (RD4(sc, HC_ARGUMENT) >> 16);
+ }
+
+ /* actually issuing the command */
+ WR4(sc, HC_COMMAND, val2);
+
+ if (val2 & HC_CMD_READ || val2 & HC_CMD_WRITE) {
+ u_int8_t hstcfg;
+
+ hstcfg = RD4(sc, HC_HOSTCONFIG);
+ hstcfg |= (HC_HSTCF_INT_BUSY | HC_HSTCF_INT_DATA);
+ WR4(sc, HC_HOSTCONFIG, hstcfg);
+ slot->data_done = 0;
+
+ if (bcm_sdhost_waitcommand(sc)) {
+ printf("%s: timeout #3\n", __func__);
+ bcm_sdhost_print_regs(sc, &sc->sc_slot, __LINE__, 1);
+ }
+
+ } else if (opcode == MMC_ERASE) {
+ if (bcm_sdhost_waitcommand_status(sc) != 0) {
+ printf("%s: timeout #4\n", __func__);
+ bcm_sdhost_print_regs(sc, &sc->sc_slot, __LINE__, 1);
+ }
+ slot->data_done = 1;
+ sc->sdhci_present_state &=
+ ~(SDHCI_CMD_INHIBIT | SDHCI_DAT_INHIBIT);
+
+ } else {
+ if (bcm_sdhost_waitcommand(sc)) {
+ printf("%s: timeout #5\n", __func__);
+ bcm_sdhost_print_regs(sc, &sc->sc_slot, __LINE__, 1);
+ }
+ slot->data_done = 1;
+ sc->sdhci_present_state &=
+ ~(SDHCI_CMD_INHIBIT | SDHCI_DAT_INHIBIT);
+ }
+
+ bcm_sdhost_print_regs(sc, &sc->sc_slot, __LINE__, 0);
+
+ if (RD4(sc, HC_HOSTSTATUS) & HC_HSTST_TIMEOUT_CMD)
+ slot->curcmd->error = MMC_ERR_TIMEOUT;
+ else if (RD4(sc, HC_COMMAND) & HC_CMD_FAILED)
+ slot->curcmd->error = MMC_ERR_FAILED;
+
+ dprintf("%s: curcmd->flags=%d data_done=%d\n",
+ __func__, slot->curcmd->flags, slot->data_done);
+
+ if (val2 & HC_CMD_RESPONSE_NONE)
+ slot->curcmd->error = 0;
+
+ if (sc->mmc_app_cmd == 1 && opcode != MMC_APP_CMD)
+ sc->mmc_app_cmd = 0;
+
+ if (RD4(sc, HC_COMMAND) & HC_CMD_ENABLE) {
+ bcm_sdhost_print_regs(sc, &sc->sc_slot, __LINE__, 1);
+ panic("%s: still HC_CMD_ENABLE on exit\n", __func__);
+ }
+
+ sc->cmdbusy = 0;
+
+ if (!(val2 & HC_CMD_READ || val2 & HC_CMD_WRITE))
+ sc->sdhci_int_status |= SDHCI_INT_RESPONSE;
+
+ /* HACK, so sdhci_finish_command() does not
+ * have to be exported
+ */
+ mtx_unlock(&slot->mtx);
+ sdhci_generic_intr(slot);
+ mtx_lock(&slot->mtx);
+ sc->sdhci_int_status &= ~SDHCI_INT_RESPONSE;
+}
+
+static uint8_t
+bcm_sdhost_read_1(device_t dev, struct sdhci_slot *slot, bus_size_t off)
+{
+ struct bcm_sdhost_softc *sc = device_get_softc(dev);
+ uint32_t val1, val2;
+
+ mtx_lock(&sc->mtx);
+
+ switch (off) {
+ case SDHCI_HOST_CONTROL:
+ val1 = RD4(sc, HC_HOSTCONFIG);
+ val2 = 0;
+ if (val1 & HC_HSTCF_EXTBUS_4BIT)
+ val2 |= SDHCI_CTRL_4BITBUS;
+ dprintf("%s: SDHCI_HOST_CONTROL --> HC_HOSTCONFIG val2 %02x\n",
+ __func__, val2);
+ break;
+ case SDHCI_POWER_CONTROL:
+ val1 = RD1(sc, HC_POWER);
+ val2 = (val1 == 1) ? 0x0f : 0;
+ dprintf("%s: SDHCI_POWER_CONTROL --> HC_POWER val2 %02x\n",
+ __func__, val2);
+ break;
+ case SDHCI_BLOCK_GAP_CONTROL:
+ dprintf("%s: SDHCI_BLOCK_GAP_CONTROL\n", __func__);
+ val2 = 0;
+ break;
+ case SDHCI_WAKE_UP_CONTROL:
+ dprintf("%s: SDHCI_WAKE_UP_CONTROL\n", __func__);
+ val2 = 0;
+ break;
+ case SDHCI_TIMEOUT_CONTROL:
+ dprintf("%s: SDHCI_TIMEOUT_CONTROL\n", __func__);
+ val2 = 0;
+ break;
+ case SDHCI_SOFTWARE_RESET:
+ dprintf("%s: SDHCI_SOFTWARE_RESET\n", __func__);
+ val2 = 0;
+ break;
+ case SDHCI_ADMA_ERR:
+ dprintf("%s: SDHCI_ADMA_ERR\n", __func__);
+ val2 = 0;
+ break;
+ default:
+ dprintf("%s: UNKNOWN off=%08lx\n", __func__, off);
+ val2 = 0;
+ break;
+ }
+
+ mtx_unlock(&sc->mtx);
+
+ return (val2);
+}
+
+static uint16_t
+bcm_sdhost_read_2(device_t dev, struct sdhci_slot *slot, bus_size_t off)
+{
+ struct bcm_sdhost_softc *sc = device_get_softc(dev);
+ uint32_t val2, val; /* = RD4(sc, off & ~3); */
+
+ mtx_lock(&sc->mtx);
+
+ switch (off) {
+ case SDHCI_BLOCK_SIZE:
+ val2 = sc->sdhci_blocksize;
+ dprintf("%s: SDHCI_BLOCK_SIZE --> HC_BLOCKSIZE %08x\n",
+ __func__, val2);
+ break;
+ case SDHCI_BLOCK_COUNT:
+ val2 = sc->sdhci_blockcount;
+ dprintf("%s: SDHCI_BLOCK_COUNT --> HC_BLOCKCOUNT %08x\n",
+ __func__, val2);
+ break;
+ case SDHCI_TRANSFER_MODE:
+ dprintf("%s: SDHCI_TRANSFER_MODE\n", __func__);
+ val2 = 0;
+ break;
+ case SDHCI_CLOCK_CONTROL:
+ val = RD4(sc, HC_CLOCKDIVISOR);
+ val2 = (val << SDHCI_DIVIDER_SHIFT) |
+ SDHCI_CLOCK_CARD_EN | SDHCI_CLOCK_INT_EN |
+ SDHCI_CLOCK_INT_STABLE;
+ dprintf("%s: SDHCI_CLOCK_CONTROL %04x --> %04x\n",
+ __func__, val, val2);
+ break;
+ case SDHCI_ACMD12_ERR:
+ dprintf("%s: SDHCI_ACMD12_ERR\n", __func__);
+ val2 = 0;
+ break;
+ case SDHCI_HOST_CONTROL2:
+ dprintf("%s: SDHCI_HOST_CONTROL2\n", __func__);
+ val2 = 0;
+ break;
+ case SDHCI_SLOT_INT_STATUS:
+ dprintf("%s: SDHCI_SLOT_INT_STATUS\n", __func__);
+ val2 = 0;
+ break;
+ case SDHCI_HOST_VERSION:
+ dprintf("%s: SDHCI_HOST_VERSION\n", __func__);
+ val2 = 0;
+ break;
+ default:
+ dprintf("%s: UNKNOWN off=%08lx\n", __func__, off);
+ val2 = 0;
+ break;
+ }
+
+ mtx_unlock(&sc->mtx);
+
+ return (val2);
+}
+
+static uint32_t
+bcm_sdhost_read_4(device_t dev, struct sdhci_slot *slot, bus_size_t off)
+{
+ struct bcm_sdhost_softc *sc = device_get_softc(dev);
+ uint32_t val2;
+
+ mtx_lock(&sc->mtx);
+
+ switch (off) {
+ case SDHCI_DMA_ADDRESS:
+ dprintf("%s: SDHCI_DMA_ADDRESS\n", __func__);
+ val2 = 0;
+ break;
+ case SDHCI_ARGUMENT:
+ dprintf("%s: SDHCI_ARGUMENT\n", __func__);
+ val2 = (RD4(sc, HC_COMMAND) << 16) |
+ (RD4(sc, HC_ARGUMENT) & 0x0000ffff);
+ break;
+ case SDHCI_RESPONSE + 0:
+ val2 = RD4(sc, HC_RESPONSE_0);
+ dprintf("%s: SDHCI_RESPONSE+0 %08x\n", __func__, val2);
+ break;
+ case SDHCI_RESPONSE + 4:
+ val2 = RD4(sc, HC_RESPONSE_1);
+ dprintf("%s: SDHCI_RESPONSE+4 %08x\n", __func__, val2);
+ break;
+ case SDHCI_RESPONSE + 8:
+ val2 = RD4(sc, HC_RESPONSE_2);
+ dprintf("%s: SDHCI_RESPONSE+8 %08x\n", __func__, val2);
+ break;
+ case SDHCI_RESPONSE + 12:
+ val2 = RD4(sc, HC_RESPONSE_3);
+ dprintf("%s: SDHCI_RESPONSE+12 %08x\n", __func__, val2);
+ break;
+ case SDHCI_BUFFER:
+ dprintf("%s: SDHCI_BUFFER\n", __func__);
+ val2 = 0;
+ break;
+ case SDHCI_PRESENT_STATE:
+ dprintf("%s: SDHCI_PRESENT_STATE %08x\n",
+ __func__, sc->sdhci_present_state);
+ val2 = sc->sdhci_present_state;
+ break;
+ case SDHCI_INT_STATUS:
+ dprintf("%s: SDHCI_INT_STATUS %08x\n",
+ __func__, sc->sdhci_int_status);
+ val2 = sc->sdhci_int_status;
+ break;
+ case SDHCI_INT_ENABLE:
+ dprintf("%s: SDHCI_INT_ENABLE\n", __func__);
+ val2 = 0;
+ break;
+ case SDHCI_SIGNAL_ENABLE:
+ dprintf("%s: SDHCI_SIGNAL_ENABLE %08x\n",
+ __func__, sc->sdhci_signal_enable);
+ val2 = sc->sdhci_signal_enable;
+ break;
+ case SDHCI_CAPABILITIES:
+ val2 = 0;
+ break;
+ case SDHCI_CAPABILITIES2:
+ dprintf("%s: SDHCI_CAPABILITIES2\n", __func__);
+ val2 = 0;
+ break;
+ case SDHCI_MAX_CURRENT:
+ dprintf("%s: SDHCI_MAX_CURRENT\n", __func__);
+ val2 = 0;
+ break;
+ case SDHCI_ADMA_ADDRESS_LO:
+ dprintf("%s: SDHCI_ADMA_ADDRESS_LO\n", __func__);
+ val2 = 0;
+ break;
+ default:
+ dprintf("%s: UNKNOWN off=%08lx\n", __func__, off);
+ val2 = 0;
+ break;
+ }
+
+ mtx_unlock(&sc->mtx);
+
+ return (val2);
+}
+
+static void
+bcm_sdhost_read_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off,
+ uint32_t *data, bus_size_t count)
+{
+ struct bcm_sdhost_softc *sc = device_get_softc(dev);
+ bus_size_t i;
+ bus_size_t avail;
+ uint32_t edm;
+
+ mtx_lock(&sc->mtx);
+
+ dprintf("%s: off=%08lx count=%08lx\n", __func__, off, count);
+
+ for (i = 0; i < count;) {
+ edm = RD4(sc, HC_DEBUG);
+ avail = ((edm >> 4) & 0x1f);
+ if (i + avail > count)
+ avail = count - i;
+ if (avail > 0)
+ bus_space_read_multi_4(sc->sc_bst, sc->sc_bsh,
+ HC_DATAPORT, data + i, avail);
+ i += avail;
+ DELAY(1);
+ }
+
+ mtx_unlock(&sc->mtx);
+}
+
+static void
+bcm_sdhost_write_1(device_t dev, struct sdhci_slot *slot,
+ bus_size_t off, uint8_t val)
+{
+ struct bcm_sdhost_softc *sc = device_get_softc(dev);
+ uint32_t val2;
+
+ mtx_lock(&sc->mtx);
+
+ switch (off) {
+ case SDHCI_HOST_CONTROL:
+ val2 = RD4(sc, HC_HOSTCONFIG);
+ val2 |= HC_HSTCF_INT_BUSY;
+ val2 |= HC_HSTCF_INTBUS_WIDE | HC_HSTCF_SLOW_CARD;
+ if (val & SDHCI_CTRL_4BITBUS)
+ val2 |= HC_HSTCF_EXTBUS_4BIT;
+ dprintf("%s: SDHCI_HOST_CONTROL --> HC_HOSTC %04x --> %04x\n",
+ __func__, val, val2);
+ WR4(sc, HC_HOSTCONFIG, val2);
+ break;
+ case SDHCI_POWER_CONTROL:
+ val2 = (val != 0) ? 1 : 0;
+ dprintf("%s: SDHCI_POWER_CONTROL --> HC_POWER %02x --> %02x\n",
+ __func__, val, val2);
+ WR1(sc, HC_POWER, val2);
+ break;
+ case SDHCI_BLOCK_GAP_CONTROL:
+ dprintf("%s: SDHCI_BLOCK_GAP_CONTROL val=%02x\n",
+ __func__, val);
+ break;
+ case SDHCI_TIMEOUT_CONTROL:
+ dprintf("%s: SDHCI_TIMEOUT_CONTROL val=%02x\n",
+ __func__, val);
+ break;
+ case SDHCI_SOFTWARE_RESET:
+ dprintf("%s: SDHCI_SOFTWARE_RESET val=%02x\n",
+ __func__, val);
+ break;
+ case SDHCI_ADMA_ERR:
+ dprintf("%s: SDHCI_ADMA_ERR val=%02x\n",
+ __func__, val);
+ break;
+ default:
+ dprintf("%s: UNKNOWN off=%08lx val=%08x\n",
+ __func__, off, val);
+ break;
+ }
+
+ mtx_unlock(&sc->mtx);
+}
+
+static void
+bcm_sdhost_write_2(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint16_t val)
+{
+ struct bcm_sdhost_softc *sc = device_get_softc(dev);
+ uint16_t val2;
+
+ mtx_lock(&sc->mtx);
+
+ switch (off) {
+ case SDHCI_BLOCK_SIZE:
+ dprintf("%s: SDHCI_BLOCK_SIZE val=%04x\n" ,
+ __func__, val);
+ sc->sdhci_blocksize = val;
+ WR2(sc, HC_BLOCKSIZE, val);
+ break;
+
+ case SDHCI_BLOCK_COUNT:
+ dprintf("%s: SDHCI_BLOCK_COUNT val=%04x\n" ,
+ __func__, val);
+ sc->sdhci_blockcount = val;
+ WR2(sc, HC_BLOCKCOUNT, val);
+ break;
+
+ case SDHCI_TRANSFER_MODE:
+ dprintf("%s: SDHCI_TRANSFER_MODE val=%04x\n" ,
+ __func__, val);
+ break;
+
+ case SDHCI_COMMAND_FLAGS:
+ bcm_sdhost_command(dev, slot, val);
+ break;
+
+ case SDHCI_CLOCK_CONTROL:
+ val2 = (val & ~SDHCI_DIVIDER_MASK) >> SDHCI_DIVIDER_SHIFT;
+ /* get crc16 errors with cdiv=0 */
+ if (val2 == 0)
+ val2 = 1;
+ dprintf("%s: SDHCI_CLOCK_CONTROL %04x --> SCDIV %04x\n",
+ __func__, val, val2);
+ WR4(sc, HC_CLOCKDIVISOR, val2);
+ break;
+
+ case SDHCI_ACMD12_ERR:
+ dprintf("%s: SDHCI_ACMD12_ERR val=%04x\n" ,
+ __func__, val);
+ break;
+
+ case SDHCI_HOST_CONTROL2:
+ dprintf("%s: SDHCI_HOST_CONTROL2 val=%04x\n" ,
+ __func__, val);
+ break;
+
+ case SDHCI_SLOT_INT_STATUS:
+ dprintf("%s: SDHCI_SLOT_INT_STATUS val=%04x\n" ,
+ __func__, val);
+ break;
+
+ default:
+ dprintf("%s: UNKNOWN off=%08lx val=%04x\n",
+ __func__, off, val);
+ break;
+ }
+
+ mtx_unlock(&sc->mtx);
+}
+
+static void
+bcm_sdhost_write_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint32_t val)
+{
+ struct bcm_sdhost_softc *sc = device_get_softc(dev);
+ uint32_t val2;
+ uint32_t hstcfg;
+
+ mtx_lock(&sc->mtx);
+
+ switch (off) {
+ case SDHCI_ARGUMENT:
+ val2 = val;
+ dprintf("%s: SDHCI_ARGUMENT --> HC_ARGUMENT val=%08x\n",
+ __func__, val);
+ WR4(sc, HC_ARGUMENT, val2);
+ break;
+ case SDHCI_INT_STATUS:
+ dprintf("%s: SDHCI_INT_STATUS val=%08x\n",
+ __func__, val);
+ sc->sdhci_int_status = val;
+ break;
+ case SDHCI_INT_ENABLE:
+ dprintf("%s: SDHCI_INT_ENABLE val=%08x\n" ,
+ __func__, val);
+ break;
+ case SDHCI_SIGNAL_ENABLE:
+ sc->sdhci_signal_enable = val;
+ hstcfg = RD4(sc, HC_HOSTCONFIG);
+ if (val != 0)
+ hstcfg &= ~(HC_HSTCF_INT_BLOCK | HC_HSTCF_INT_DATA);
+ else
+ hstcfg |= (HC_HSTCF_INT_BUSY|HC_HSTCF_INT_BLOCK|
+ HC_HSTCF_INT_DATA);
+ hstcfg |= HC_HSTCF_INT_BUSY;
+ dprintf("%s: SDHCI_SIGNAL_ENABLE --> HC_HOSTC %08x --> %08x\n" ,
+ __func__, val, hstcfg);
+ WR4(sc, HC_HOSTCONFIG, hstcfg);
+ break;
+ case SDHCI_CAPABILITIES:
+ dprintf("%s: SDHCI_CAPABILITIES val=%08x\n",
+ __func__, val);
+ break;
+ case SDHCI_CAPABILITIES2:
+ dprintf("%s: SDHCI_CAPABILITIES2 val=%08x\n",
+ __func__, val);
+ break;
+ case SDHCI_MAX_CURRENT:
+ dprintf("%s: SDHCI_MAX_CURRENT val=%08x\n",
+ __func__, val);
+ break;
+ case SDHCI_ADMA_ADDRESS_LO:
+ dprintf("%s: SDHCI_ADMA_ADDRESS_LO val=%08x\n",
+ __func__, val);
+ break;
+ default:
+ dprintf("%s: UNKNOWN off=%08lx val=%08x\n",
+ __func__, off, val);
+ break;
+ }
+
+ mtx_unlock(&sc->mtx);
+}
+
+static void
+bcm_sdhost_write_multi_4(device_t dev, struct sdhci_slot *slot,
+ bus_size_t off, uint32_t *data, bus_size_t count)
+{
+ struct bcm_sdhost_softc *sc = device_get_softc(dev);
+ bus_size_t i;
+ bus_size_t space;
+ uint32_t edm;
+
+ mtx_lock(&sc->mtx);
+
+ dprintf("%s: off=%08lx count=%02lx\n", __func__, off, count);
+
+ for (i = 0; i < count;) {
+ edm = RD4(sc, HC_DEBUG);
+ space = HC_FIFO_SIZE - ((edm >> 4) & 0x1f);
+ if (i + space > count)
+ space = count - i;
+ if (space > 0)
+ bus_space_write_multi_4(sc->sc_bst, sc->sc_bsh,
+ HC_DATAPORT, data + i, space);
+ i += space;
+ DELAY(1);
+ }
+
+ /* wait until FIFO is really empty */
+ while (((RD4(sc, HC_DEBUG) >> 4) & 0x1f) > 0)
+ DELAY(1);
+
+ mtx_unlock(&sc->mtx);
+}
+
+static device_method_t bcm_sdhost_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, bcm_sdhost_probe),
+ DEVMETHOD(device_attach, bcm_sdhost_attach),
+ DEVMETHOD(device_detach, bcm_sdhost_detach),
+
+ /* Bus interface */
+ DEVMETHOD(bus_read_ivar, sdhci_generic_read_ivar),
+ DEVMETHOD(bus_write_ivar, sdhci_generic_write_ivar),
+
+ /* MMC bridge interface */
+ DEVMETHOD(mmcbr_update_ios, sdhci_generic_update_ios),
+ DEVMETHOD(mmcbr_request, sdhci_generic_request),
+ DEVMETHOD(mmcbr_get_ro, bcm_sdhost_get_ro),
+ DEVMETHOD(mmcbr_acquire_host, sdhci_generic_acquire_host),
+ DEVMETHOD(mmcbr_release_host, sdhci_generic_release_host),
+
+ /* SDHCI registers accessors */
+ DEVMETHOD(sdhci_read_1, bcm_sdhost_read_1),
+ DEVMETHOD(sdhci_read_2, bcm_sdhost_read_2),
+ DEVMETHOD(sdhci_read_4, bcm_sdhost_read_4),
+ DEVMETHOD(sdhci_read_multi_4, bcm_sdhost_read_multi_4),
+ DEVMETHOD(sdhci_write_1, bcm_sdhost_write_1),
+ DEVMETHOD(sdhci_write_2, bcm_sdhost_write_2),
+ DEVMETHOD(sdhci_write_4, bcm_sdhost_write_4),
+ DEVMETHOD(sdhci_write_multi_4, bcm_sdhost_write_multi_4),
+ DEVMETHOD(sdhci_get_card_present,bcm_sdhost_get_card_present),
+
+ DEVMETHOD_END
+};
+
+static devclass_t bcm_sdhost_devclass;
+
+static driver_t bcm_sdhost_driver = {
+ "sdhost_bcm",
+ bcm_sdhost_methods,
+ sizeof(struct bcm_sdhost_softc),
+};
+
+DRIVER_MODULE(sdhost_bcm, simplebus, bcm_sdhost_driver, bcm_sdhost_devclass,
+ NULL, NULL);
+SDHCI_DEPEND(sdhost_bcm);
+#ifndef MMCCAM
+MMC_DECLARE_BRIDGE(sdhost_bcm);
+#endif
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_spi.c b/sys/arm/broadcom/bcm2835/bcm2835_spi.c
new file mode 100644
index 000000000000..f659bcc5023e
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2835_spi.c
@@ -0,0 +1,572 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * Copyright (c) 2013 Luiz Otavio O Souza <loos@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/intr.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/spibus/spi.h>
+#include <dev/spibus/spibusvar.h>
+
+#include <arm/broadcom/bcm2835/bcm2835_spireg.h>
+#include <arm/broadcom/bcm2835/bcm2835_spivar.h>
+
+#include "spibus_if.h"
+
+static struct ofw_compat_data compat_data[] = {
+ {"broadcom,bcm2835-spi", 1},
+ {"brcm,bcm2835-spi", 1},
+ {NULL, 0}
+};
+
+static void bcm_spi_intr(void *);
+
+#ifdef BCM_SPI_DEBUG
+static void
+bcm_spi_printr(device_t dev)
+{
+ struct bcm_spi_softc *sc;
+ uint32_t reg;
+
+ sc = device_get_softc(dev);
+ reg = BCM_SPI_READ(sc, SPI_CS);
+ device_printf(dev, "CS=%b\n", reg,
+ "\20\1CS0\2CS1\3CPHA\4CPOL\7CSPOL"
+ "\10TA\11DMAEN\12INTD\13INTR\14ADCS\15REN\16LEN"
+ "\21DONE\22RXD\23TXD\24RXR\25RXF\26CSPOL0\27CSPOL1"
+ "\30CSPOL2\31DMA_LEN\32LEN_LONG");
+ reg = BCM_SPI_READ(sc, SPI_CLK) & SPI_CLK_MASK;
+ if (reg % 2)
+ reg--;
+ if (reg == 0)
+ reg = 65536;
+ device_printf(dev, "CLK=%uMhz/%d=%luhz\n",
+ SPI_CORE_CLK / 1000000, reg, SPI_CORE_CLK / reg);
+ reg = BCM_SPI_READ(sc, SPI_DLEN) & SPI_DLEN_MASK;
+ device_printf(dev, "DLEN=%d\n", reg);
+ reg = BCM_SPI_READ(sc, SPI_LTOH) & SPI_LTOH_MASK;
+ device_printf(dev, "LTOH=%d\n", reg);
+ reg = BCM_SPI_READ(sc, SPI_DC);
+ device_printf(dev, "DC=RPANIC=%#x RDREQ=%#x TPANIC=%#x TDREQ=%#x\n",
+ (reg & SPI_DC_RPANIC_MASK) >> SPI_DC_RPANIC_SHIFT,
+ (reg & SPI_DC_RDREQ_MASK) >> SPI_DC_RDREQ_SHIFT,
+ (reg & SPI_DC_TPANIC_MASK) >> SPI_DC_TPANIC_SHIFT,
+ (reg & SPI_DC_TDREQ_MASK) >> SPI_DC_TDREQ_SHIFT);
+}
+#endif
+
+static void
+bcm_spi_modifyreg(struct bcm_spi_softc *sc, uint32_t off, uint32_t mask,
+ uint32_t value)
+{
+ uint32_t reg;
+
+ mtx_assert(&sc->sc_mtx, MA_OWNED);
+ reg = BCM_SPI_READ(sc, off);
+ reg &= ~mask;
+ reg |= value;
+ BCM_SPI_WRITE(sc, off, reg);
+}
+
+static int
+bcm_spi_clock_proc(SYSCTL_HANDLER_ARGS)
+{
+ struct bcm_spi_softc *sc;
+ uint32_t clk;
+ int error;
+
+ sc = (struct bcm_spi_softc *)arg1;
+
+ BCM_SPI_LOCK(sc);
+ clk = BCM_SPI_READ(sc, SPI_CLK);
+ BCM_SPI_UNLOCK(sc);
+ clk &= 0xffff;
+ if (clk == 0)
+ clk = 65536;
+ clk = SPI_CORE_CLK / clk;
+
+ error = sysctl_handle_int(oidp, &clk, sizeof(clk), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ return (0);
+}
+
+static int
+bcm_spi_cs_bit_proc(SYSCTL_HANDLER_ARGS, uint32_t bit)
+{
+ struct bcm_spi_softc *sc;
+ uint32_t reg;
+ int error;
+
+ sc = (struct bcm_spi_softc *)arg1;
+ BCM_SPI_LOCK(sc);
+ reg = BCM_SPI_READ(sc, SPI_CS);
+ BCM_SPI_UNLOCK(sc);
+ reg = (reg & bit) ? 1 : 0;
+
+ error = sysctl_handle_int(oidp, &reg, sizeof(reg), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ return (0);
+}
+
+static int
+bcm_spi_cpol_proc(SYSCTL_HANDLER_ARGS)
+{
+
+ return (bcm_spi_cs_bit_proc(oidp, arg1, arg2, req, SPI_CS_CPOL));
+}
+
+static int
+bcm_spi_cpha_proc(SYSCTL_HANDLER_ARGS)
+{
+
+ return (bcm_spi_cs_bit_proc(oidp, arg1, arg2, req, SPI_CS_CPHA));
+}
+
+static int
+bcm_spi_cspol0_proc(SYSCTL_HANDLER_ARGS)
+{
+
+ return (bcm_spi_cs_bit_proc(oidp, arg1, arg2, req, SPI_CS_CSPOL0));
+}
+
+static int
+bcm_spi_cspol1_proc(SYSCTL_HANDLER_ARGS)
+{
+
+ return (bcm_spi_cs_bit_proc(oidp, arg1, arg2, req, SPI_CS_CSPOL1));
+}
+
+static int
+bcm_spi_cspol2_proc(SYSCTL_HANDLER_ARGS)
+{
+
+ return (bcm_spi_cs_bit_proc(oidp, arg1, arg2, req, SPI_CS_CSPOL2));
+}
+
+static void
+bcm_spi_sysctl_init(struct bcm_spi_softc *sc)
+{
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid *tree_node;
+ struct sysctl_oid_list *tree;
+
+ /*
+ * Add system sysctl tree/handlers.
+ */
+ ctx = device_get_sysctl_ctx(sc->sc_dev);
+ tree_node = device_get_sysctl_tree(sc->sc_dev);
+ tree = SYSCTL_CHILDREN(tree_node);
+ SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "clock",
+ CTLFLAG_RD | CTLTYPE_UINT | CTLFLAG_NEEDGIANT, sc, sizeof(*sc),
+ bcm_spi_clock_proc, "IU", "SPI BUS clock frequency");
+ SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "cpol",
+ CTLFLAG_RD | CTLTYPE_UINT | CTLFLAG_NEEDGIANT, sc, sizeof(*sc),
+ bcm_spi_cpol_proc, "IU", "SPI BUS clock polarity");
+ SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "cpha",
+ CTLFLAG_RD | CTLTYPE_UINT | CTLFLAG_NEEDGIANT, sc, sizeof(*sc),
+ bcm_spi_cpha_proc, "IU", "SPI BUS clock phase");
+ SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "cspol0",
+ CTLFLAG_RD | CTLTYPE_UINT | CTLFLAG_NEEDGIANT, sc, sizeof(*sc),
+ bcm_spi_cspol0_proc, "IU", "SPI BUS chip select 0 polarity");
+ SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "cspol1",
+ CTLFLAG_RD | CTLTYPE_UINT | CTLFLAG_NEEDGIANT, sc, sizeof(*sc),
+ bcm_spi_cspol1_proc, "IU", "SPI BUS chip select 1 polarity");
+ SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "cspol2",
+ CTLFLAG_RD | CTLTYPE_UINT | CTLFLAG_NEEDGIANT, sc, sizeof(*sc),
+ bcm_spi_cspol2_proc, "IU", "SPI BUS chip select 2 polarity");
+}
+
+static int
+bcm_spi_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "BCM2708/2835 SPI controller");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+bcm_spi_attach(device_t dev)
+{
+ struct bcm_spi_softc *sc;
+ int rid;
+
+ if (device_get_unit(dev) != 0) {
+ device_printf(dev, "only one SPI controller supported\n");
+ return (ENXIO);
+ }
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+
+ rid = 0;
+ sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (!sc->sc_mem_res) {
+ device_printf(dev, "cannot allocate memory window\n");
+ return (ENXIO);
+ }
+
+ sc->sc_bst = rman_get_bustag(sc->sc_mem_res);
+ sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res);
+
+ rid = 0;
+ sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (!sc->sc_irq_res) {
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+ device_printf(dev, "cannot allocate interrupt\n");
+ return (ENXIO);
+ }
+
+ /* Hook up our interrupt handler. */
+ if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+ NULL, bcm_spi_intr, sc, &sc->sc_intrhand)) {
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+ device_printf(dev, "cannot setup the interrupt handler\n");
+ return (ENXIO);
+ }
+
+ mtx_init(&sc->sc_mtx, "bcm_spi", NULL, MTX_DEF);
+
+ /* Add sysctl nodes. */
+ bcm_spi_sysctl_init(sc);
+
+#ifdef BCM_SPI_DEBUG
+ bcm_spi_printr(dev);
+#endif
+
+ /*
+ * Enable the SPI controller. Clear the rx and tx FIFO.
+ * Defaults to SPI mode 0.
+ */
+ BCM_SPI_WRITE(sc, SPI_CS, SPI_CS_CLEAR_RXFIFO | SPI_CS_CLEAR_TXFIFO);
+
+#ifdef BCM_SPI_DEBUG
+ bcm_spi_printr(dev);
+#endif
+
+ device_add_child(dev, "spibus", -1);
+
+ return (bus_generic_attach(dev));
+}
+
+static int
+bcm_spi_detach(device_t dev)
+{
+ struct bcm_spi_softc *sc;
+
+ bus_generic_detach(dev);
+
+ sc = device_get_softc(dev);
+ mtx_destroy(&sc->sc_mtx);
+ if (sc->sc_intrhand)
+ bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand);
+ if (sc->sc_irq_res)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
+ if (sc->sc_mem_res)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+
+ return (0);
+}
+
+static void
+bcm_spi_fill_fifo(struct bcm_spi_softc *sc)
+{
+ struct spi_command *cmd;
+ uint32_t cs, written;
+ uint8_t *data;
+
+ cmd = sc->sc_cmd;
+ cs = BCM_SPI_READ(sc, SPI_CS) & (SPI_CS_TA | SPI_CS_TXD);
+ while (sc->sc_written < sc->sc_len &&
+ cs == (SPI_CS_TA | SPI_CS_TXD)) {
+ data = (uint8_t *)cmd->tx_cmd;
+ written = sc->sc_written++;
+ if (written >= cmd->tx_cmd_sz) {
+ data = (uint8_t *)cmd->tx_data;
+ written -= cmd->tx_cmd_sz;
+ }
+ BCM_SPI_WRITE(sc, SPI_FIFO, data[written]);
+ cs = BCM_SPI_READ(sc, SPI_CS) & (SPI_CS_TA | SPI_CS_TXD);
+ }
+}
+
+static void
+bcm_spi_drain_fifo(struct bcm_spi_softc *sc)
+{
+ struct spi_command *cmd;
+ uint32_t cs, read;
+ uint8_t *data;
+
+ cmd = sc->sc_cmd;
+ cs = BCM_SPI_READ(sc, SPI_CS) & SPI_CS_RXD;
+ while (sc->sc_read < sc->sc_len && cs == SPI_CS_RXD) {
+ data = (uint8_t *)cmd->rx_cmd;
+ read = sc->sc_read++;
+ if (read >= cmd->rx_cmd_sz) {
+ data = (uint8_t *)cmd->rx_data;
+ read -= cmd->rx_cmd_sz;
+ }
+ data[read] = BCM_SPI_READ(sc, SPI_FIFO) & 0xff;
+ cs = BCM_SPI_READ(sc, SPI_CS) & SPI_CS_RXD;
+ }
+}
+
+static void
+bcm_spi_intr(void *arg)
+{
+ struct bcm_spi_softc *sc;
+
+ sc = (struct bcm_spi_softc *)arg;
+ BCM_SPI_LOCK(sc);
+
+ /* Filter stray interrupts. */
+ if ((sc->sc_flags & BCM_SPI_BUSY) == 0) {
+ BCM_SPI_UNLOCK(sc);
+ return;
+ }
+
+ /* TX - Fill up the FIFO. */
+ bcm_spi_fill_fifo(sc);
+
+ /* RX - Drain the FIFO. */
+ bcm_spi_drain_fifo(sc);
+
+ /* Check for end of transfer. */
+ if (sc->sc_written == sc->sc_len && sc->sc_read == sc->sc_len) {
+ /* Disable interrupts and the SPI engine. */
+ bcm_spi_modifyreg(sc, SPI_CS,
+ SPI_CS_TA | SPI_CS_INTR | SPI_CS_INTD, 0);
+ wakeup(sc->sc_dev);
+ }
+
+ BCM_SPI_UNLOCK(sc);
+}
+
+static int
+bcm_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
+{
+ struct bcm_spi_softc *sc;
+ uint32_t cs, mode, clock;
+ int err;
+
+ sc = device_get_softc(dev);
+
+ KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz,
+ ("TX/RX command sizes should be equal"));
+ KASSERT(cmd->tx_data_sz == cmd->rx_data_sz,
+ ("TX/RX data sizes should be equal"));
+
+ /* Get the bus speed, mode, and chip select for this child. */
+
+ spibus_get_cs(child, &cs);
+ if ((cs & (~SPIBUS_CS_HIGH)) > 2) {
+ device_printf(dev,
+ "Invalid chip select %u requested by %s\n", cs,
+ device_get_nameunit(child));
+ return (EINVAL);
+ }
+
+ spibus_get_clock(child, &clock);
+ if (clock == 0) {
+ device_printf(dev,
+ "Invalid clock %uHz requested by %s\n", clock,
+ device_get_nameunit(child));
+ return (EINVAL);
+ }
+
+ spibus_get_mode(child, &mode);
+ if (mode > 3) {
+ device_printf(dev,
+ "Invalid mode %u requested by %s\n", mode,
+ device_get_nameunit(child));
+ return (EINVAL);
+ }
+
+ /* If the controller is in use wait until it is available. */
+ BCM_SPI_LOCK(sc);
+ while (sc->sc_flags & BCM_SPI_BUSY)
+ mtx_sleep(dev, &sc->sc_mtx, 0, "bcm_spi", 0);
+
+ /* Now we have control over SPI controller. */
+ sc->sc_flags = BCM_SPI_BUSY;
+
+ /* Clear the FIFO. */
+ bcm_spi_modifyreg(sc, SPI_CS,
+ SPI_CS_CLEAR_RXFIFO | SPI_CS_CLEAR_TXFIFO,
+ SPI_CS_CLEAR_RXFIFO | SPI_CS_CLEAR_TXFIFO);
+
+ /* Save a pointer to the SPI command. */
+ sc->sc_cmd = cmd;
+ sc->sc_read = 0;
+ sc->sc_written = 0;
+ sc->sc_len = cmd->tx_cmd_sz + cmd->tx_data_sz;
+
+#ifdef BCM2835_SPI_USE_CS_HIGH /* TODO: for when behavior is correct */
+ /*
+ * Assign CS polarity first, while the CS indicates 'inactive'.
+ * This will need to set the correct polarity bit based on the 'cs', and
+ * the polarity bit will remain in this state, even after the transaction
+ * is complete.
+ */
+ if((cs & ~SPIBUS_CS_HIGH) == 0) {
+ bcm_spi_modifyreg(sc, SPI_CS,
+ SPI_CS_CSPOL0,
+ ((cs & (SPIBUS_CS_HIGH)) ? SPI_CS_CSPOL0 : 0));
+ }
+ else if((cs & ~SPIBUS_CS_HIGH) == 1) {
+ bcm_spi_modifyreg(sc, SPI_CS,
+ SPI_CS_CSPOL1,
+ ((cs & (SPIBUS_CS_HIGH)) ? SPI_CS_CSPOL1 : 0));
+ }
+ else if((cs & ~SPIBUS_CS_HIGH) == 2) {
+ bcm_spi_modifyreg(sc, SPI_CS,
+ SPI_CS_CSPOL2,
+ ((cs & (SPIBUS_CS_HIGH)) ? SPI_CS_CSPOL2 : 0));
+ }
+#endif
+
+ /*
+ * Set the mode in 'SPI_CS' (clock phase and polarity bits).
+ * This must happen before CS output pin is active.
+ * Otherwise, you might glitch and drop the first bit.
+ */
+ bcm_spi_modifyreg(sc, SPI_CS,
+ SPI_CS_CPOL | SPI_CS_CPHA,
+ ((mode & SPIBUS_MODE_CPHA) ? SPI_CS_CPHA : 0) |
+ ((mode & SPIBUS_MODE_CPOL) ? SPI_CS_CPOL : 0));
+
+ /*
+ * Set the clock divider in 'SPI_CLK - see 'bcm_spi_clock_proc()'.
+ */
+
+ /* calculate 'clock' as a divider value from freq */
+ clock = SPI_CORE_CLK / clock;
+ if (clock <= 1)
+ clock = 2;
+ else if (clock % 2)
+ clock--;
+ if (clock > 0xffff)
+ clock = 0;
+
+ BCM_SPI_WRITE(sc, SPI_CLK, clock);
+
+ /*
+ * Set the CS for this transaction, enable interrupts and announce
+ * we're ready to tx. This will kick off the first interrupt.
+ */
+ bcm_spi_modifyreg(sc, SPI_CS,
+ SPI_CS_MASK | SPI_CS_TA | SPI_CS_INTR | SPI_CS_INTD,
+ (cs & (~SPIBUS_CS_HIGH)) | /* cs is the lower 2 bits of the reg */
+ SPI_CS_TA | SPI_CS_INTR | SPI_CS_INTD);
+
+ /* Wait for the transaction to complete. */
+ err = mtx_sleep(dev, &sc->sc_mtx, 0, "bcm_spi", hz * 2);
+
+ /* Make sure the SPI engine and interrupts are disabled. */
+ bcm_spi_modifyreg(sc, SPI_CS, SPI_CS_TA | SPI_CS_INTR | SPI_CS_INTD, 0);
+
+ /* Release the controller and wakeup the next thread waiting for it. */
+ sc->sc_flags = 0;
+ wakeup_one(dev);
+ BCM_SPI_UNLOCK(sc);
+
+ /*
+ * Check for transfer timeout. The SPI controller doesn't
+ * return errors.
+ */
+ if (err == EWOULDBLOCK) {
+ device_printf(sc->sc_dev, "SPI error (timeout)\n");
+ err = EIO;
+ }
+
+ return (err);
+}
+
+static phandle_t
+bcm_spi_get_node(device_t bus, device_t dev)
+{
+
+ /* We only have one child, the SPI bus, which needs our own node. */
+ return (ofw_bus_get_node(bus));
+}
+
+static device_method_t bcm_spi_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, bcm_spi_probe),
+ DEVMETHOD(device_attach, bcm_spi_attach),
+ DEVMETHOD(device_detach, bcm_spi_detach),
+
+ /* SPI interface */
+ DEVMETHOD(spibus_transfer, bcm_spi_transfer),
+
+ /* ofw_bus interface */
+ DEVMETHOD(ofw_bus_get_node, bcm_spi_get_node),
+
+ DEVMETHOD_END
+};
+
+static devclass_t bcm_spi_devclass;
+
+static driver_t bcm_spi_driver = {
+ "spi",
+ bcm_spi_methods,
+ sizeof(struct bcm_spi_softc),
+};
+
+DRIVER_MODULE(bcm2835_spi, simplebus, bcm_spi_driver, bcm_spi_devclass, 0, 0);
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_spireg.h b/sys/arm/broadcom/bcm2835/bcm2835_spireg.h
new file mode 100644
index 000000000000..a658f16c92b9
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2835_spireg.h
@@ -0,0 +1,77 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * Copyright (c) 2013 Luiz Otavio O Souza <loos@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _BCM2835_SPIREG_H_
+#define _BCM2835_SPIREG_H_
+
+#define SPI_CORE_CLK 250000000U
+#define SPI_CS 0x00
+#define SPI_CS_LEN_LONG (1 << 25)
+#define SPI_CS_DMA_LEN (1 << 24)
+#define SPI_CS_CSPOL2 (1 << 23)
+#define SPI_CS_CSPOL1 (1 << 22)
+#define SPI_CS_CSPOL0 (1 << 21)
+#define SPI_CS_RXF (1 << 20)
+#define SPI_CS_RXR (1 << 19)
+#define SPI_CS_TXD (1 << 18)
+#define SPI_CS_RXD (1 << 17)
+#define SPI_CS_DONE (1 << 16)
+#define SPI_CS_LEN (1 << 13)
+#define SPI_CS_REN (1 << 12)
+#define SPI_CS_ADCS (1 << 11)
+#define SPI_CS_INTR (1 << 10)
+#define SPI_CS_INTD (1 << 9)
+#define SPI_CS_DMAEN (1 << 8)
+#define SPI_CS_TA (1 << 7)
+#define SPI_CS_CSPOL (1 << 6)
+#define SPI_CS_CLEAR_RXFIFO (1 << 5)
+#define SPI_CS_CLEAR_TXFIFO (1 << 4)
+#define SPI_CS_CPOL (1 << 3)
+#define SPI_CS_CPHA (1 << 2)
+#define SPI_CS_MASK 0x3
+#define SPI_FIFO 0x04
+#define SPI_CLK 0x08
+#define SPI_CLK_MASK 0xffff
+#define SPI_DLEN 0x0c
+#define SPI_DLEN_MASK 0xffff
+#define SPI_LTOH 0x10
+#define SPI_LTOH_MASK 0xf
+#define SPI_DC 0x14
+#define SPI_DC_RPANIC_SHIFT 24
+#define SPI_DC_RPANIC_MASK (0xff << SPI_DC_RPANIC_SHIFT)
+#define SPI_DC_RDREQ_SHIFT 16
+#define SPI_DC_RDREQ_MASK (0xff << SPI_DC_RDREQ_SHIFT)
+#define SPI_DC_TPANIC_SHIFT 8
+#define SPI_DC_TPANIC_MASK (0xff << SPI_DC_TPANIC_SHIFT)
+#define SPI_DC_TDREQ_SHIFT 0
+#define SPI_DC_TDREQ_MASK 0xff
+
+#endif /* _BCM2835_SPIREG_H_ */
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_spivar.h b/sys/arm/broadcom/bcm2835/bcm2835_spivar.h
new file mode 100644
index 000000000000..75a17a449cb5
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2835_spivar.h
@@ -0,0 +1,62 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * Copyright (c) 2013 Luiz Otavio O Souza <loos@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _BCM2835_SPIVAR_H_
+#define _BCM2835_SPIVAR_H_
+
+struct bcm_spi_softc {
+ device_t sc_dev;
+ struct mtx sc_mtx;
+ struct resource * sc_mem_res;
+ struct resource * sc_irq_res;
+ struct spi_command *sc_cmd;
+ bus_space_tag_t sc_bst;
+ bus_space_handle_t sc_bsh;
+ uint32_t sc_len;
+ uint32_t sc_read;
+ uint32_t sc_flags;
+ uint32_t sc_written;
+ void * sc_intrhand;
+};
+
+#define BCM_SPI_BUSY 0x1
+
+#define BCM_SPI_WRITE(_sc, _off, _val) \
+ bus_space_write_4(_sc->sc_bst, _sc->sc_bsh, _off, _val)
+#define BCM_SPI_READ(_sc, _off) \
+ bus_space_read_4(_sc->sc_bst, _sc->sc_bsh, _off)
+
+#define BCM_SPI_LOCK(_sc) \
+ mtx_lock(&(_sc)->sc_mtx)
+#define BCM_SPI_UNLOCK(_sc) \
+ mtx_unlock(&(_sc)->sc_mtx)
+
+#endif /* _BCM2835_SPIVAR_H_ */
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_systimer.c b/sys/arm/broadcom/bcm2835/bcm2835_systimer.c
new file mode 100644
index 000000000000..c5c3435b6ef7
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2835_systimer.c
@@ -0,0 +1,317 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * Copyright (c) 2012 Damjan Marion <dmarion@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+#include <machine/machdep.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+
+#define BCM2835_NUM_TIMERS 4
+
+#define DEFAULT_TIMER 3
+#define DEFAULT_TIMER_NAME "BCM2835-3"
+#define DEFAULT_FREQUENCY 1000000
+#define MIN_PERIOD 5LLU
+
+#define SYSTIMER_CS 0x00
+#define SYSTIMER_CLO 0x04
+#define SYSTIMER_CHI 0x08
+#define SYSTIMER_C0 0x0C
+#define SYSTIMER_C1 0x10
+#define SYSTIMER_C2 0x14
+#define SYSTIMER_C3 0x18
+
+struct systimer {
+ int index;
+ bool enabled;
+ struct eventtimer et;
+};
+
+struct bcm_systimer_softc {
+ struct resource* mem_res;
+ struct resource* irq_res[BCM2835_NUM_TIMERS];
+ void* intr_hl[BCM2835_NUM_TIMERS];
+ uint32_t sysclk_freq;
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+ struct systimer st[BCM2835_NUM_TIMERS];
+};
+
+static struct resource_spec bcm_systimer_irq_spec[] = {
+ { SYS_RES_IRQ, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 1, RF_ACTIVE },
+ { SYS_RES_IRQ, 2, RF_ACTIVE },
+ { SYS_RES_IRQ, 3, RF_ACTIVE },
+ { -1, 0, 0 }
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"broadcom,bcm2835-system-timer", 1},
+ {"brcm,bcm2835-system-timer", 1},
+ {NULL, 0}
+};
+
+static struct bcm_systimer_softc *bcm_systimer_sc = NULL;
+
+/* Read/Write macros for Timer used as timecounter */
+#define bcm_systimer_tc_read_4(reg) \
+ bus_space_read_4(bcm_systimer_sc->bst, \
+ bcm_systimer_sc->bsh, reg)
+
+#define bcm_systimer_tc_write_4(reg, val) \
+ bus_space_write_4(bcm_systimer_sc->bst, \
+ bcm_systimer_sc->bsh, reg, val)
+
+static unsigned bcm_systimer_tc_get_timecount(struct timecounter *);
+
+static delay_func bcm_systimer_delay;
+
+static struct timecounter bcm_systimer_tc = {
+ .tc_name = DEFAULT_TIMER_NAME,
+ .tc_get_timecount = bcm_systimer_tc_get_timecount,
+ .tc_poll_pps = NULL,
+ .tc_counter_mask = ~0u,
+ .tc_frequency = 0,
+ .tc_quality = 1000,
+};
+
+static unsigned
+bcm_systimer_tc_get_timecount(struct timecounter *tc)
+{
+ if (bcm_systimer_sc == NULL)
+ return (0);
+
+ return bcm_systimer_tc_read_4(SYSTIMER_CLO);
+}
+
+static int
+bcm_systimer_start(struct eventtimer *et, sbintime_t first, sbintime_t period)
+{
+ struct systimer *st = et->et_priv;
+ uint32_t clo, clo1;
+ uint32_t count;
+ register_t s;
+
+ if (first != 0) {
+ count = ((uint32_t)et->et_frequency * first) >> 32;
+
+ s = intr_disable();
+ clo = bcm_systimer_tc_read_4(SYSTIMER_CLO);
+restart:
+ clo += count;
+ /*
+ * Clear pending interrupts
+ */
+ bcm_systimer_tc_write_4(SYSTIMER_CS, (1 << st->index));
+ bcm_systimer_tc_write_4(SYSTIMER_C0 + st->index*4, clo);
+ clo1 = bcm_systimer_tc_read_4(SYSTIMER_CLO);
+ if ((int32_t)(clo1 - clo) >= 0) {
+ count *= 2;
+ clo = clo1;
+ goto restart;
+ }
+ st->enabled = 1;
+ intr_restore(s);
+
+ return (0);
+ }
+
+ return (EINVAL);
+}
+
+static int
+bcm_systimer_stop(struct eventtimer *et)
+{
+ struct systimer *st = et->et_priv;
+ st->enabled = 0;
+
+ return (0);
+}
+
+static int
+bcm_systimer_intr(void *arg)
+{
+ struct systimer *st = (struct systimer *)arg;
+ uint32_t cs;
+
+ cs = bcm_systimer_tc_read_4(SYSTIMER_CS);
+ if ((cs & (1 << st->index)) == 0)
+ return (FILTER_STRAY);
+
+ /* ACK interrupt */
+ bcm_systimer_tc_write_4(SYSTIMER_CS, (1 << st->index));
+ if (st->enabled) {
+ if (st->et.et_active) {
+ st->et.et_event_cb(&st->et, st->et.et_arg);
+ }
+ }
+
+ return (FILTER_HANDLED);
+}
+
+static int
+bcm_systimer_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+bcm_systimer_attach(device_t dev)
+{
+ struct bcm_systimer_softc *sc = device_get_softc(dev);
+ int err;
+ int rid = 0;
+
+ if (bcm_systimer_sc != NULL)
+ return (EINVAL);
+
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "could not allocate memory resource\n");
+ return (ENXIO);
+ }
+
+ sc->bst = rman_get_bustag(sc->mem_res);
+ sc->bsh = rman_get_bushandle(sc->mem_res);
+
+ /* Request the IRQ resources */
+ err = bus_alloc_resources(dev, bcm_systimer_irq_spec,
+ sc->irq_res);
+ if (err) {
+ device_printf(dev, "Error: could not allocate irq resources\n");
+ return (ENXIO);
+ }
+
+ /* TODO: get frequency from FDT */
+ sc->sysclk_freq = DEFAULT_FREQUENCY;
+
+ /* Setup and enable the timer */
+ if (bus_setup_intr(dev, sc->irq_res[DEFAULT_TIMER], INTR_TYPE_CLK,
+ bcm_systimer_intr, NULL, &sc->st[DEFAULT_TIMER],
+ &sc->intr_hl[DEFAULT_TIMER]) != 0) {
+ bus_release_resources(dev, bcm_systimer_irq_spec,
+ sc->irq_res);
+ device_printf(dev, "Unable to setup the clock irq handler.\n");
+ return (ENXIO);
+ }
+
+ sc->st[DEFAULT_TIMER].index = DEFAULT_TIMER;
+ sc->st[DEFAULT_TIMER].enabled = 0;
+ sc->st[DEFAULT_TIMER].et.et_name = DEFAULT_TIMER_NAME;
+ sc->st[DEFAULT_TIMER].et.et_flags = ET_FLAGS_ONESHOT;
+ sc->st[DEFAULT_TIMER].et.et_quality = 1000;
+ sc->st[DEFAULT_TIMER].et.et_frequency = sc->sysclk_freq;
+ sc->st[DEFAULT_TIMER].et.et_min_period =
+ (MIN_PERIOD << 32) / sc->st[DEFAULT_TIMER].et.et_frequency + 1;
+ sc->st[DEFAULT_TIMER].et.et_max_period =
+ (0x7ffffffeLLU << 32) / sc->st[DEFAULT_TIMER].et.et_frequency;
+ sc->st[DEFAULT_TIMER].et.et_start = bcm_systimer_start;
+ sc->st[DEFAULT_TIMER].et.et_stop = bcm_systimer_stop;
+ sc->st[DEFAULT_TIMER].et.et_priv = &sc->st[DEFAULT_TIMER];
+ et_register(&sc->st[DEFAULT_TIMER].et);
+
+ bcm_systimer_sc = sc;
+
+ if (device_get_unit(dev) == 0)
+ arm_set_delay(bcm_systimer_delay, sc);
+
+ bcm_systimer_tc.tc_frequency = DEFAULT_FREQUENCY;
+ tc_init(&bcm_systimer_tc);
+
+ return (0);
+}
+
+static device_method_t bcm_systimer_methods[] = {
+ DEVMETHOD(device_probe, bcm_systimer_probe),
+ DEVMETHOD(device_attach, bcm_systimer_attach),
+ { 0, 0 }
+};
+
+static driver_t bcm_systimer_driver = {
+ "systimer",
+ bcm_systimer_methods,
+ sizeof(struct bcm_systimer_softc),
+};
+
+static devclass_t bcm_systimer_devclass;
+
+DRIVER_MODULE(bcm_systimer, simplebus, bcm_systimer_driver, bcm_systimer_devclass, 0, 0);
+
+static void
+bcm_systimer_delay(int usec, void *arg)
+{
+ struct bcm_systimer_softc *sc;
+ int32_t counts;
+ uint32_t first, last;
+
+ sc = (struct bcm_systimer_softc *) arg;
+
+ /* Get the number of times to count */
+ counts = usec * (bcm_systimer_tc.tc_frequency / 1000000) + 1;
+
+ first = bcm_systimer_tc_read_4(SYSTIMER_CLO);
+
+ while (counts > 0) {
+ last = bcm_systimer_tc_read_4(SYSTIMER_CLO);
+ if (last == first)
+ continue;
+ if (last>first) {
+ counts -= (int32_t)(last - first);
+ } else {
+ counts -= (int32_t)((0xFFFFFFFF - first) + last);
+ }
+ first = last;
+ }
+}
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_vcbus.c b/sys/arm/broadcom/bcm2835/bcm2835_vcbus.c
new file mode 100644
index 000000000000..7e5033f032c4
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2835_vcbus.c
@@ -0,0 +1,280 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * This file contains facilities for runtime determination of address space
+ * mappings for use in DMA/mailbox interactions. This is only used for the
+ * arm64 SoC because the 32-bit SoC used the same mappings.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+
+#include <arm/broadcom/bcm2835/bcm2835_vcbus.h>
+
+/*
+ * This structure describes mappings that need to take place when transforming
+ * ARM core addresses into vcbus addresses for use with the DMA/mailbox
+ * interfaces. Currently, we only deal with peripheral/SDRAM address spaces
+ * here.
+ *
+ * The SDRAM address space is consistently mapped starting at 0 and extends to
+ * the size of the installed SDRAM.
+ *
+ * Peripherals are mapped further up at spots that vary per-SOC.
+ */
+struct bcm283x_memory_mapping {
+ vm_paddr_t armc_start;
+ vm_paddr_t armc_size;
+ vm_paddr_t vcbus_start;
+};
+
+static struct bcm283x_memory_mapping bcm2835_memmap[] = {
+ {
+ /* SDRAM */
+ .armc_start = 0x00000000,
+ .armc_size = BCM2835_ARM_IO_BASE,
+ .vcbus_start = BCM2835_VCBUS_SDRAM_BASE,
+ },
+ {
+ /* Peripherals */
+ .armc_start = BCM2835_ARM_IO_BASE,
+ .armc_size = BCM28XX_ARM_IO_SIZE,
+ .vcbus_start = BCM2835_VCBUS_IO_BASE,
+ },
+ { 0, 0, 0 },
+};
+
+static struct bcm283x_memory_mapping bcm2836_memmap[] = {
+ {
+ /* SDRAM */
+ .armc_start = 0x00000000,
+ .armc_size = BCM2836_ARM_IO_BASE,
+ .vcbus_start = BCM2836_VCBUS_SDRAM_BASE,
+ },
+ {
+ /* Peripherals */
+ .armc_start = BCM2836_ARM_IO_BASE,
+ .armc_size = BCM28XX_ARM_IO_SIZE,
+ .vcbus_start = BCM2836_VCBUS_IO_BASE,
+ },
+ { 0, 0, 0 },
+};
+
+static struct bcm283x_memory_mapping bcm2837_memmap[] = {
+ {
+ /* SDRAM */
+ .armc_start = 0x00000000,
+ .armc_size = BCM2837_ARM_IO_BASE,
+ .vcbus_start = BCM2837_VCBUS_SDRAM_BASE,
+ },
+ {
+ /* Peripherals */
+ .armc_start = BCM2837_ARM_IO_BASE,
+ .armc_size = BCM28XX_ARM_IO_SIZE,
+ .vcbus_start = BCM2837_VCBUS_IO_BASE,
+ },
+ { 0, 0, 0 },
+};
+
+/*
+ * The BCM2838 supports up to 4GB of SDRAM, but unfortunately we can still only
+ * map the first 1GB into the "legacy master view" (vcbus) address space. Thus,
+ * peripherals can still only access the lower end of SDRAM. For this reason,
+ * we also capture the main-peripheral busdma restriction below.
+ */
+static struct bcm283x_memory_mapping bcm2838_memmap[] = {
+ {
+ /* SDRAM */
+ .armc_start = 0x00000000,
+ .armc_size = 0x40000000,
+ .vcbus_start = BCM2838_VCBUS_SDRAM_BASE,
+ },
+ {
+ /* Main peripherals */
+ .armc_start = BCM2838_ARM_IO_BASE,
+ .armc_size = BCM28XX_ARM_IO_SIZE,
+ .vcbus_start = BCM2838_VCBUS_IO_BASE,
+ },
+ { 0, 0, 0 },
+};
+
+static struct bcm283x_memory_soc_cfg {
+ struct bcm283x_memory_mapping *memmap;
+ const char *soc_compat;
+ bus_addr_t busdma_lowaddr;
+} bcm283x_memory_configs[] = {
+ /* Legacy */
+ {
+ .memmap = bcm2835_memmap,
+ .soc_compat = "raspberrypi,model-b",
+ .busdma_lowaddr = BUS_SPACE_MAXADDR_32BIT,
+ },
+ /* Modern */
+ {
+ .memmap = bcm2835_memmap,
+ .soc_compat = "brcm,bcm2835",
+ .busdma_lowaddr = BUS_SPACE_MAXADDR_32BIT,
+ },
+ /* Legacy */
+ {
+ .memmap = bcm2836_memmap,
+ .soc_compat = "brcm,bcm2709",
+ .busdma_lowaddr = BUS_SPACE_MAXADDR_32BIT,
+ },
+ /* Modern */
+ {
+ .memmap = bcm2836_memmap,
+ .soc_compat = "brcm,bcm2836",
+ .busdma_lowaddr = BUS_SPACE_MAXADDR_32BIT,
+ },
+ {
+ .memmap = bcm2837_memmap,
+ .soc_compat = "brcm,bcm2837",
+ .busdma_lowaddr = BUS_SPACE_MAXADDR_32BIT,
+ },
+ {
+ .memmap = bcm2838_memmap,
+ .soc_compat = "brcm,bcm2711",
+ .busdma_lowaddr = BCM2838_PERIPH_MAXADDR,
+ },
+ {
+ .memmap = bcm2838_memmap,
+ .soc_compat = "brcm,bcm2838",
+ .busdma_lowaddr = BCM2838_PERIPH_MAXADDR,
+ },
+};
+
+static struct bcm283x_memory_soc_cfg *booted_soc_memcfg;
+
+static struct bcm283x_memory_soc_cfg *
+bcm283x_get_current_memcfg(void)
+{
+ phandle_t root;
+ int i;
+
+ /* We'll cache it once we decide, because it won't change per-boot. */
+ if (booted_soc_memcfg != NULL)
+ return (booted_soc_memcfg);
+
+ KASSERT(nitems(bcm283x_memory_configs) != 0,
+ ("No SOC memory configurations enabled!"));
+
+ root = OF_finddevice("/");
+ for (i = 0; i < nitems(bcm283x_memory_configs); ++i) {
+ booted_soc_memcfg = &bcm283x_memory_configs[i];
+ if (bootverbose)
+ printf("Checking root against %s\n",
+ booted_soc_memcfg->soc_compat);
+ if (ofw_bus_node_is_compatible(root,
+ booted_soc_memcfg->soc_compat))
+ return (booted_soc_memcfg);
+ }
+
+ /*
+ * The kernel doesn't fit the board; we can't really make a reasonable
+ * guess, as these SOC are different enough that something will blow up
+ * later.
+ */
+ panic("No suitable SOC memory configuration found.");
+}
+
+#define BCM283X_MEMMAP_ISTERM(ent) \
+ ((ent)->armc_start == 0 && (ent)->armc_size == 0 && \
+ (ent)->vcbus_start == 0)
+
+vm_paddr_t
+bcm283x_armc_to_vcbus(vm_paddr_t pa)
+{
+ struct bcm283x_memory_soc_cfg *cfg;
+ struct bcm283x_memory_mapping *map, *ment;
+
+ /* Guaranteed not NULL if we haven't panicked yet. */
+ cfg = bcm283x_get_current_memcfg();
+ map = cfg->memmap;
+ for (ment = map; !BCM283X_MEMMAP_ISTERM(ment); ++ment) {
+ if (pa >= ment->armc_start &&
+ pa < ment->armc_start + ment->armc_size) {
+ return (pa - ment->armc_start) + ment->vcbus_start;
+ }
+ }
+
+ /*
+ * Assume 1:1 mapping for anything else, but complain about it on
+ * verbose boots.
+ */
+ if (bootverbose)
+ printf("bcm283x_vcbus: No armc -> vcbus mapping found: %jx\n",
+ (uintmax_t)pa);
+ return (pa);
+}
+
+vm_paddr_t
+bcm283x_vcbus_to_armc(vm_paddr_t vca)
+{
+ struct bcm283x_memory_soc_cfg *cfg;
+ struct bcm283x_memory_mapping *map, *ment;
+
+ /* Guaranteed not NULL if we haven't panicked yet. */
+ cfg = bcm283x_get_current_memcfg();
+ map = cfg->memmap;
+ for (ment = map; !BCM283X_MEMMAP_ISTERM(ment); ++ment) {
+ if (vca >= ment->vcbus_start &&
+ vca < ment->vcbus_start + ment->armc_size) {
+ return (vca - ment->vcbus_start) + ment->armc_start;
+ }
+ }
+
+ /*
+ * Assume 1:1 mapping for anything else, but complain about it on
+ * verbose boots.
+ */
+ if (bootverbose)
+ printf("bcm283x_vcbus: No vcbus -> armc mapping found: %jx\n",
+ (uintmax_t)vca);
+ return (vca);
+}
+
+bus_addr_t
+bcm283x_dmabus_peripheral_lowaddr(void)
+{
+ struct bcm283x_memory_soc_cfg *cfg;
+
+ cfg = bcm283x_get_current_memcfg();
+ return (cfg->busdma_lowaddr);
+}
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_vcbus.h b/sys/arm/broadcom/bcm2835/bcm2835_vcbus.h
new file mode 100644
index 000000000000..77b408311e61
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2835_vcbus.h
@@ -0,0 +1,81 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Defines for converting physical address to VideoCore bus address and back
+ */
+
+#ifndef _BCM2835_VCBUS_H_
+#define _BCM2835_VCBUS_H_
+
+#define BCM2835_VCBUS_SDRAM_CACHED 0x40000000
+#define BCM2835_VCBUS_SDRAM_UNCACHED 0xC0000000
+
+#define BCM2835_ARM_IO_BASE 0x20000000
+#define BCM2835_VCBUS_IO_BASE 0x7E000000
+#define BCM2835_VCBUS_SDRAM_BASE BCM2835_VCBUS_SDRAM_CACHED
+
+#define BCM2836_ARM_IO_BASE 0x3f000000
+#define BCM2836_VCBUS_IO_BASE BCM2835_VCBUS_IO_BASE
+#define BCM2836_VCBUS_SDRAM_BASE BCM2835_VCBUS_SDRAM_UNCACHED
+
+#define BCM2837_ARM_IO_BASE BCM2836_ARM_IO_BASE
+#define BCM2837_VCBUS_IO_BASE BCM2835_VCBUS_IO_BASE
+#define BCM2837_VCBUS_SDRAM_BASE BCM2835_VCBUS_SDRAM_UNCACHED
+
+#define BCM2838_ARM_IO_BASE 0xfe000000
+#define BCM2838_VCBUS_IO_BASE BCM2835_VCBUS_IO_BASE
+#define BCM2838_VCBUS_SDRAM_BASE BCM2835_VCBUS_SDRAM_UNCACHED
+
+/*
+ * Max allowed SDRAM mapping for most peripherals. The Raspberry Pi 4 has more
+ * than 1 GB of SDRAM, but only the lowest 1 GB is mapped into the "Legacy
+ * Master view" of the address space accessible by the DMA engine. Technically,
+ * we can slide this window around to whatever similarly sized range is
+ * convenient, but this is the most useful window given how busdma(9) works and
+ * that the window must be reconfigured for all channels in a given DMA engine.
+ * The DMA lite engine's window can be configured separately from the 30-bit DMA
+ * engine.
+ */
+#define BCM2838_PERIPH_MAXADDR 0x3fffffff
+
+#define BCM28XX_ARM_IO_SIZE 0x01000000
+
+vm_paddr_t bcm283x_armc_to_vcbus(vm_paddr_t pa);
+vm_paddr_t bcm283x_vcbus_to_armc(vm_paddr_t vca);
+bus_addr_t bcm283x_dmabus_peripheral_lowaddr(void);
+
+#define ARMC_TO_VCBUS(pa) bcm283x_armc_to_vcbus(pa)
+#define VCBUS_TO_ARMC(vca) bcm283x_vcbus_to_armc(vca)
+
+/* Compatibility name for vchiq arm interface. */
+#define PHYS_TO_VCBUS ARMC_TO_VCBUS
+
+#endif /* _BCM2835_VCBUS_H_ */
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_vcio.c b/sys/arm/broadcom/bcm2835/bcm2835_vcio.c
new file mode 100644
index 000000000000..40fb4e0ad8ee
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2835_vcio.c
@@ -0,0 +1,118 @@
+/*-
+ * Copyright (c) 2015 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/ioccom.h>
+#include <sys/conf.h>
+#include <sys/proc.h>
+
+#include <arm/broadcom/bcm2835/bcm2835_mbox_prop.h>
+
+MALLOC_DECLARE(M_VCIO);
+MALLOC_DEFINE(M_VCIO, "vcio", "VCIO temporary buffers");
+
+static struct cdev *sdev;
+static d_ioctl_t vcio_ioctl;
+
+static struct cdevsw vcio_devsw = {
+ /* version */ .d_version = D_VERSION,
+ /* ioctl */ .d_ioctl = vcio_ioctl,
+};
+
+#define VCIO_IOC_MAGIC 100
+#define IOCTL_MBOX_PROPERTY _IOWR(VCIO_IOC_MAGIC, 0, char *)
+
+int
+vcio_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int mode,
+ struct thread *td)
+{
+ int error;
+ void *ptr;
+ uint32_t size;
+ uint8_t *property;
+
+ error = 0;
+ switch(cmd) {
+ case IOCTL_MBOX_PROPERTY:
+ memcpy (&ptr, arg, sizeof(ptr));
+ error = copyin(ptr, &size, sizeof(size));
+
+ if (error != 0)
+ break;
+ property = malloc(size, M_VCIO, M_WAITOK);
+
+ error = copyin(ptr, property, size);
+ if (error) {
+ free(property, M_VCIO);
+ break;
+ }
+
+ error = bcm2835_mbox_property(property, size);
+ if (error) {
+ free(property, M_VCIO);
+ break;
+ }
+
+ error = copyout(property, ptr, size);
+ free(property, M_VCIO);
+
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ return (error);
+}
+
+static int
+vcio_load(module_t mod, int cmd, void *arg)
+{
+ int err = 0;
+
+ switch (cmd) {
+ case MOD_LOAD:
+ sdev = make_dev(&vcio_devsw, 0, UID_ROOT, GID_WHEEL, 0600, "vcio");
+ break;
+
+ case MOD_UNLOAD:
+ destroy_dev(sdev);
+ break;
+
+ default:
+ err = EOPNOTSUPP;
+ break;
+ }
+
+ return(err);
+}
+
+DEV_MODULE(vcio, vcio_load, NULL);
+MODULE_DEPEND(vcio, mbox, 1, 1, 1);
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_wdog.c b/sys/arm/broadcom/bcm2835/bcm2835_wdog.c
new file mode 100644
index 000000000000..816123943a2a
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2835_wdog.c
@@ -0,0 +1,261 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Alexander Rybalko <ray@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/eventhandler.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/reboot.h>
+#include <sys/rman.h>
+#include <sys/systm.h>
+#include <sys/watchdog.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+#include <machine/machdep.h>
+
+#include <arm/broadcom/bcm2835/bcm2835_wdog.h>
+
+#define BCM2835_PASSWORD 0x5a
+
+#define BCM2835_WDOG_RESET 0
+#define BCM2835_PASSWORD_MASK 0xff000000
+#define BCM2835_PASSWORD_SHIFT 24
+#define BCM2835_WDOG_TIME_MASK 0x000fffff
+#define BCM2835_WDOG_TIME_SHIFT 0
+
+#define READ(_sc, _r) bus_space_read_4((_sc)->bst, (_sc)->bsh, (_r) + (_sc)->regs_offset)
+#define WRITE(_sc, _r, _v) bus_space_write_4((_sc)->bst, (_sc)->bsh, (_r) + (_sc)->regs_offset, (_v))
+
+#define BCM2835_RSTC_WRCFG_CLR 0xffffffcf
+#define BCM2835_RSTC_WRCFG_SET 0x00000030
+#define BCM2835_RSTC_WRCFG_FULL_RESET 0x00000020
+#define BCM2835_RSTC_RESET 0x00000102
+
+#define BCM2835_RSTC_REG 0x00
+#define BCM2835_RSTS_REG 0x04
+#define BCM2835_WDOG_REG 0x08
+
+static struct bcmwd_softc *bcmwd_lsc = NULL;
+
+struct bcmwd_softc {
+ device_t dev;
+ struct resource * res;
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+ int wdog_armed;
+ int wdog_period;
+ char wdog_passwd;
+ struct mtx mtx;
+ int regs_offset;
+};
+
+#define BSD_DTB 1
+#define UPSTREAM_DTB 2
+#define UPSTREAM_DTB_REGS_OFFSET 0x1c
+
+static struct ofw_compat_data compat_data[] = {
+ {"broadcom,bcm2835-wdt", BSD_DTB},
+ {"brcm,bcm2835-pm-wdt", UPSTREAM_DTB},
+ {"brcm,bcm2835-pm", UPSTREAM_DTB},
+ {NULL, 0}
+};
+
+static void bcmwd_watchdog_fn(void *private, u_int cmd, int *error);
+static void bcmwd_reboot_system(void *, int);
+
+static int
+bcmwd_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "BCM2708/2835 Watchdog");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+bcmwd_attach(device_t dev)
+{
+ struct bcmwd_softc *sc;
+ int rid;
+
+ if (bcmwd_lsc != NULL)
+ return (ENXIO);
+
+ sc = device_get_softc(dev);
+ sc->wdog_period = 7;
+ sc->wdog_passwd = BCM2835_PASSWORD;
+ sc->wdog_armed = 0;
+ sc->dev = dev;
+
+ rid = 0;
+ sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
+ if (sc->res == NULL) {
+ device_printf(dev, "could not allocate memory resource\n");
+ return (ENXIO);
+ }
+
+ sc->bst = rman_get_bustag(sc->res);
+ sc->bsh = rman_get_bushandle(sc->res);
+
+ /* compensate base address difference */
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data
+ == UPSTREAM_DTB)
+ sc->regs_offset = UPSTREAM_DTB_REGS_OFFSET;
+
+ bcmwd_lsc = sc;
+ mtx_init(&sc->mtx, "BCM2835 Watchdog", "bcmwd", MTX_DEF);
+ EVENTHANDLER_REGISTER(watchdog_list, bcmwd_watchdog_fn, sc, 0);
+
+ /*
+ * Handle reboot events. This needs to happen with slightly greater
+ * priority than the PSCI handler, since PSCI reset is not properly
+ * implemented on the Pi and it just puts the Pi into a halt
+ * state.
+ */
+ EVENTHANDLER_REGISTER(shutdown_final, bcmwd_reboot_system, sc,
+ SHUTDOWN_PRI_LAST-1);
+
+ return (0);
+}
+
+static void
+bcmwd_watchdog_fn(void *private, u_int cmd, int *error)
+{
+ struct bcmwd_softc *sc;
+ uint64_t sec;
+ uint32_t ticks, reg;
+
+ sc = private;
+ mtx_lock(&sc->mtx);
+
+ cmd &= WD_INTERVAL;
+
+ if (cmd > 0) {
+ sec = ((uint64_t)1 << (cmd & WD_INTERVAL)) / 1000000000;
+ if (sec == 0 || sec > 15) {
+ /*
+ * Can't arm
+ * disable watchdog as watchdog(9) requires
+ */
+ device_printf(sc->dev,
+ "Can't arm, timeout must be between 1-15 seconds\n");
+ WRITE(sc, BCM2835_RSTC_REG,
+ (BCM2835_PASSWORD << BCM2835_PASSWORD_SHIFT) |
+ BCM2835_RSTC_RESET);
+ mtx_unlock(&sc->mtx);
+ *error = EINVAL;
+ return;
+ }
+
+ ticks = (sec << 16) & BCM2835_WDOG_TIME_MASK;
+ reg = (BCM2835_PASSWORD << BCM2835_PASSWORD_SHIFT) | ticks;
+ WRITE(sc, BCM2835_WDOG_REG, reg);
+
+ reg = READ(sc, BCM2835_RSTC_REG);
+ reg &= BCM2835_RSTC_WRCFG_CLR;
+ reg |= BCM2835_RSTC_WRCFG_FULL_RESET;
+ reg |= (BCM2835_PASSWORD << BCM2835_PASSWORD_SHIFT);
+ WRITE(sc, BCM2835_RSTC_REG, reg);
+
+ *error = 0;
+ }
+ else
+ WRITE(sc, BCM2835_RSTC_REG,
+ (BCM2835_PASSWORD << BCM2835_PASSWORD_SHIFT) |
+ BCM2835_RSTC_RESET);
+
+ mtx_unlock(&sc->mtx);
+}
+
+void
+bcmwd_watchdog_reset(void)
+{
+
+ if (bcmwd_lsc == NULL)
+ return;
+
+ WRITE(bcmwd_lsc, BCM2835_WDOG_REG,
+ (BCM2835_PASSWORD << BCM2835_PASSWORD_SHIFT) | 10);
+
+ WRITE(bcmwd_lsc, BCM2835_RSTC_REG,
+ (READ(bcmwd_lsc, BCM2835_RSTC_REG) & BCM2835_RSTC_WRCFG_CLR) |
+ (BCM2835_PASSWORD << BCM2835_PASSWORD_SHIFT) |
+ BCM2835_RSTC_WRCFG_FULL_RESET);
+}
+
+static void
+bcmwd_reboot_system(void *sc, int howto)
+{
+ int cmd, error = 0;
+
+ /* Only handle reset. */
+ if (howto & RB_HALT || howto & RB_POWEROFF)
+ return;
+
+ printf("Resetting system ... ");
+
+ cmd = WD_TO_1SEC;
+ bcmwd_watchdog_fn(sc, cmd, &error);
+
+ /* Wait for watchdog timeout. */
+ DELAY(2000000);
+
+ /* Not reached ... one hopes. */
+ printf("failed to reset (errno %d).\n", error);
+}
+
+static device_method_t bcmwd_methods[] = {
+ DEVMETHOD(device_probe, bcmwd_probe),
+ DEVMETHOD(device_attach, bcmwd_attach),
+
+ DEVMETHOD_END
+};
+
+static driver_t bcmwd_driver = {
+ "bcmwd",
+ bcmwd_methods,
+ sizeof(struct bcmwd_softc),
+};
+static devclass_t bcmwd_devclass;
+
+DRIVER_MODULE(bcmwd, simplebus, bcmwd_driver, bcmwd_devclass, 0, 0);
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_wdog.h b/sys/arm/broadcom/bcm2835/bcm2835_wdog.h
new file mode 100644
index 000000000000..214991820fdc
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2835_wdog.h
@@ -0,0 +1,34 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Alexander Rybalko <ray@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _BCM2835_WDOG_H_
+#define _BCM2835_WDOG_H_
+
+void bcmwd_watchdog_reset(void);
+#endif
diff --git a/sys/arm/broadcom/bcm2835/bcm2836.c b/sys/arm/broadcom/bcm2835/bcm2836.c
new file mode 100644
index 000000000000..33f872bec89e
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2836.c
@@ -0,0 +1,745 @@
+/*
+ * Copyright 2015 Andrew Turner.
+ * Copyright 2016 Svatopluk Kraus
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/cpuset.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/rman.h>
+#ifdef SMP
+#include <sys/smp.h>
+#endif
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+#include <machine/resource.h>
+#ifdef SMP
+#include <machine/smp.h>
+#endif
+
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/ofw_bus.h>
+
+#include "pic_if.h"
+
+#define BCM_LINTC_CONTROL_REG 0x00
+#define BCM_LINTC_PRESCALER_REG 0x08
+#define BCM_LINTC_GPU_ROUTING_REG 0x0c
+#define BCM_LINTC_PMU_ROUTING_SET_REG 0x10
+#define BCM_LINTC_PMU_ROUTING_CLR_REG 0x14
+#define BCM_LINTC_TIMER_CFG_REG(n) (0x40 + (n) * 4)
+#define BCM_LINTC_MBOX_CFG_REG(n) (0x50 + (n) * 4)
+#define BCM_LINTC_PENDING_REG(n) (0x60 + (n) * 4)
+#define BCM_LINTC_MBOX0_SET_REG(n) (0x80 + (n) * 16)
+#define BCM_LINTC_MBOX1_SET_REG(n) (0x84 + (n) * 16)
+#define BCM_LINTC_MBOX2_SET_REG(n) (0x88 + (n) * 16)
+#define BCM_LINTC_MBOX3_SET_REG(n) (0x8C + (n) * 16)
+#define BCM_LINTC_MBOX0_CLR_REG(n) (0xC0 + (n) * 16)
+#define BCM_LINTC_MBOX1_CLR_REG(n) (0xC4 + (n) * 16)
+#define BCM_LINTC_MBOX2_CLR_REG(n) (0xC8 + (n) * 16)
+#define BCM_LINTC_MBOX3_CLR_REG(n) (0xCC + (n) * 16)
+
+/* Prescaler Register */
+#define BCM_LINTC_PSR_19_2 0x80000000 /* 19.2 MHz */
+
+/* GPU Interrupt Routing Register */
+#define BCM_LINTC_GIRR_IRQ_CORE(n) (n)
+#define BCM_LINTC_GIRR_FIQ_CORE(n) ((n) << 2)
+
+/* PMU Interrupt Routing Register */
+#define BCM_LINTC_PIRR_IRQ_EN_CORE(n) (1 << (n))
+#define BCM_LINTC_PIRR_FIQ_EN_CORE(n) (1 << ((n) + 4))
+
+/* Timer Config Register */
+#define BCM_LINTC_TCR_IRQ_EN_TIMER(n) (1 << (n))
+#define BCM_LINTC_TCR_FIQ_EN_TIMER(n) (1 << ((n) + 4))
+
+/* MBOX Config Register */
+#define BCM_LINTC_MCR_IRQ_EN_MBOX(n) (1 << (n))
+#define BCM_LINTC_MCR_FIQ_EN_MBOX(n) (1 << ((n) + 4))
+
+#define BCM_LINTC_CNTPSIRQ_IRQ 0
+#define BCM_LINTC_CNTPNSIRQ_IRQ 1
+#define BCM_LINTC_CNTHPIRQ_IRQ 2
+#define BCM_LINTC_CNTVIRQ_IRQ 3
+#define BCM_LINTC_MBOX0_IRQ 4
+#define BCM_LINTC_MBOX1_IRQ 5
+#define BCM_LINTC_MBOX2_IRQ 6
+#define BCM_LINTC_MBOX3_IRQ 7
+#define BCM_LINTC_GPU_IRQ 8
+#define BCM_LINTC_PMU_IRQ 9
+#define BCM_LINTC_AXI_IRQ 10
+#define BCM_LINTC_LTIMER_IRQ 11
+
+#define BCM_LINTC_NIRQS 12
+
+#define BCM_LINTC_TIMER0_IRQ BCM_LINTC_CNTPSIRQ_IRQ
+#define BCM_LINTC_TIMER1_IRQ BCM_LINTC_CNTPNSIRQ_IRQ
+#define BCM_LINTC_TIMER2_IRQ BCM_LINTC_CNTHPIRQ_IRQ
+#define BCM_LINTC_TIMER3_IRQ BCM_LINTC_CNTVIRQ_IRQ
+
+#define BCM_LINTC_TIMER0_IRQ_MASK (1 << BCM_LINTC_TIMER0_IRQ)
+#define BCM_LINTC_TIMER1_IRQ_MASK (1 << BCM_LINTC_TIMER1_IRQ)
+#define BCM_LINTC_TIMER2_IRQ_MASK (1 << BCM_LINTC_TIMER2_IRQ)
+#define BCM_LINTC_TIMER3_IRQ_MASK (1 << BCM_LINTC_TIMER3_IRQ)
+#define BCM_LINTC_MBOX0_IRQ_MASK (1 << BCM_LINTC_MBOX0_IRQ)
+#define BCM_LINTC_GPU_IRQ_MASK (1 << BCM_LINTC_GPU_IRQ)
+#define BCM_LINTC_PMU_IRQ_MASK (1 << BCM_LINTC_PMU_IRQ)
+
+#define BCM_LINTC_UP_PENDING_MASK \
+ (BCM_LINTC_TIMER0_IRQ_MASK | \
+ BCM_LINTC_TIMER1_IRQ_MASK | \
+ BCM_LINTC_TIMER2_IRQ_MASK | \
+ BCM_LINTC_TIMER3_IRQ_MASK | \
+ BCM_LINTC_GPU_IRQ_MASK | \
+ BCM_LINTC_PMU_IRQ_MASK)
+
+#define BCM_LINTC_SMP_PENDING_MASK \
+ (BCM_LINTC_UP_PENDING_MASK | \
+ BCM_LINTC_MBOX0_IRQ_MASK)
+
+#ifdef SMP
+#define BCM_LINTC_PENDING_MASK BCM_LINTC_SMP_PENDING_MASK
+#else
+#define BCM_LINTC_PENDING_MASK BCM_LINTC_UP_PENDING_MASK
+#endif
+
+struct bcm_lintc_irqsrc {
+ struct intr_irqsrc bli_isrc;
+ u_int bli_irq;
+ union {
+ u_int bli_mask; /* for timers */
+ u_int bli_value; /* for GPU */
+ };
+};
+
+struct bcm_lintc_softc {
+ device_t bls_dev;
+ struct mtx bls_mtx;
+ struct resource * bls_mem;
+ bus_space_tag_t bls_bst;
+ bus_space_handle_t bls_bsh;
+ struct bcm_lintc_irqsrc bls_isrcs[BCM_LINTC_NIRQS];
+};
+
+static struct bcm_lintc_softc *bcm_lintc_sc;
+
+#ifdef SMP
+#define BCM_LINTC_NIPIS 32 /* only mailbox 0 is used for IPI */
+CTASSERT(INTR_IPI_COUNT <= BCM_LINTC_NIPIS);
+#endif
+
+#define BCM_LINTC_LOCK(sc) mtx_lock_spin(&(sc)->bls_mtx)
+#define BCM_LINTC_UNLOCK(sc) mtx_unlock_spin(&(sc)->bls_mtx)
+#define BCM_LINTC_LOCK_INIT(sc) mtx_init(&(sc)->bls_mtx, \
+ device_get_nameunit((sc)->bls_dev), "bmc_local_intc", MTX_SPIN)
+#define BCM_LINTC_LOCK_DESTROY(sc) mtx_destroy(&(sc)->bls_mtx)
+
+#define bcm_lintc_read_4(sc, reg) \
+ bus_space_read_4((sc)->bls_bst, (sc)->bls_bsh, (reg))
+#define bcm_lintc_write_4(sc, reg, val) \
+ bus_space_write_4((sc)->bls_bst, (sc)->bls_bsh, (reg), (val))
+
+static inline void
+bcm_lintc_rwreg_clr(struct bcm_lintc_softc *sc, uint32_t reg,
+ uint32_t mask)
+{
+
+ bcm_lintc_write_4(sc, reg, bcm_lintc_read_4(sc, reg) & ~mask);
+}
+
+static inline void
+bcm_lintc_rwreg_set(struct bcm_lintc_softc *sc, uint32_t reg,
+ uint32_t mask)
+{
+
+ bcm_lintc_write_4(sc, reg, bcm_lintc_read_4(sc, reg) | mask);
+}
+
+static void
+bcm_lintc_timer_mask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli)
+{
+ cpuset_t *cpus;
+ uint32_t cpu;
+
+ cpus = &bli->bli_isrc.isrc_cpu;
+
+ BCM_LINTC_LOCK(sc);
+ for (cpu = 0; cpu < 4; cpu++)
+ if (CPU_ISSET(cpu, cpus))
+ bcm_lintc_rwreg_clr(sc, BCM_LINTC_TIMER_CFG_REG(cpu),
+ bli->bli_mask);
+ BCM_LINTC_UNLOCK(sc);
+}
+
+static void
+bcm_lintc_timer_unmask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli)
+{
+ cpuset_t *cpus;
+ uint32_t cpu;
+
+ cpus = &bli->bli_isrc.isrc_cpu;
+
+ BCM_LINTC_LOCK(sc);
+ for (cpu = 0; cpu < 4; cpu++)
+ if (CPU_ISSET(cpu, cpus))
+ bcm_lintc_rwreg_set(sc, BCM_LINTC_TIMER_CFG_REG(cpu),
+ bli->bli_mask);
+ BCM_LINTC_UNLOCK(sc);
+}
+
+static inline void
+bcm_lintc_gpu_mask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli)
+{
+
+ /* It's accessed just and only by one core. */
+ bcm_lintc_write_4(sc, BCM_LINTC_GPU_ROUTING_REG, 0);
+}
+
+static inline void
+bcm_lintc_gpu_unmask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli)
+{
+
+ /* It's accessed just and only by one core. */
+ bcm_lintc_write_4(sc, BCM_LINTC_GPU_ROUTING_REG, bli->bli_value);
+}
+
+static inline void
+bcm_lintc_pmu_mask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli)
+{
+ cpuset_t *cpus;
+ uint32_t cpu, mask;
+
+ mask = 0;
+ cpus = &bli->bli_isrc.isrc_cpu;
+
+ BCM_LINTC_LOCK(sc);
+ for (cpu = 0; cpu < 4; cpu++)
+ if (CPU_ISSET(cpu, cpus))
+ mask |= BCM_LINTC_PIRR_IRQ_EN_CORE(cpu);
+ /* Write-clear register. */
+ bcm_lintc_write_4(sc, BCM_LINTC_PMU_ROUTING_CLR_REG, mask);
+ BCM_LINTC_UNLOCK(sc);
+}
+
+static inline void
+bcm_lintc_pmu_unmask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli)
+{
+ cpuset_t *cpus;
+ uint32_t cpu, mask;
+
+ mask = 0;
+ cpus = &bli->bli_isrc.isrc_cpu;
+
+ BCM_LINTC_LOCK(sc);
+ for (cpu = 0; cpu < 4; cpu++)
+ if (CPU_ISSET(cpu, cpus))
+ mask |= BCM_LINTC_PIRR_IRQ_EN_CORE(cpu);
+ /* Write-set register. */
+ bcm_lintc_write_4(sc, BCM_LINTC_PMU_ROUTING_SET_REG, mask);
+ BCM_LINTC_UNLOCK(sc);
+}
+
+static void
+bcm_lintc_mask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli)
+{
+
+ switch (bli->bli_irq) {
+ case BCM_LINTC_TIMER0_IRQ:
+ case BCM_LINTC_TIMER1_IRQ:
+ case BCM_LINTC_TIMER2_IRQ:
+ case BCM_LINTC_TIMER3_IRQ:
+ bcm_lintc_timer_mask(sc, bli);
+ return;
+ case BCM_LINTC_MBOX0_IRQ:
+ case BCM_LINTC_MBOX1_IRQ:
+ case BCM_LINTC_MBOX2_IRQ:
+ case BCM_LINTC_MBOX3_IRQ:
+ return;
+ case BCM_LINTC_GPU_IRQ:
+ bcm_lintc_gpu_mask(sc, bli);
+ return;
+ case BCM_LINTC_PMU_IRQ:
+ bcm_lintc_pmu_mask(sc, bli);
+ return;
+ default:
+ panic("%s: not implemented for irq %u", __func__, bli->bli_irq);
+ }
+}
+
+static void
+bcm_lintc_unmask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli)
+{
+
+ switch (bli->bli_irq) {
+ case BCM_LINTC_TIMER0_IRQ:
+ case BCM_LINTC_TIMER1_IRQ:
+ case BCM_LINTC_TIMER2_IRQ:
+ case BCM_LINTC_TIMER3_IRQ:
+ bcm_lintc_timer_unmask(sc, bli);
+ return;
+ case BCM_LINTC_MBOX0_IRQ:
+ case BCM_LINTC_MBOX1_IRQ:
+ case BCM_LINTC_MBOX2_IRQ:
+ case BCM_LINTC_MBOX3_IRQ:
+ return;
+ case BCM_LINTC_GPU_IRQ:
+ bcm_lintc_gpu_unmask(sc, bli);
+ return;
+ case BCM_LINTC_PMU_IRQ:
+ bcm_lintc_pmu_unmask(sc, bli);
+ return;
+ default:
+ panic("%s: not implemented for irq %u", __func__, bli->bli_irq);
+ }
+}
+
+#ifdef SMP
+static inline void
+bcm_lintc_ipi_write(struct bcm_lintc_softc *sc, cpuset_t cpus, u_int ipi)
+{
+ u_int cpu;
+ uint32_t mask;
+
+ mask = 1 << ipi;
+ for (cpu = 0; cpu < mp_ncpus; cpu++)
+ if (CPU_ISSET(cpu, &cpus))
+ bcm_lintc_write_4(sc, BCM_LINTC_MBOX0_SET_REG(cpu),
+ mask);
+}
+
+static inline void
+bcm_lintc_ipi_dispatch(struct bcm_lintc_softc *sc, u_int cpu,
+ struct trapframe *tf)
+{
+ u_int ipi;
+ uint32_t mask;
+
+ mask = bcm_lintc_read_4(sc, BCM_LINTC_MBOX0_CLR_REG(cpu));
+ if (mask == 0) {
+ device_printf(sc->bls_dev, "Spurious ipi detected\n");
+ return;
+ }
+
+ for (ipi = 0; mask != 0; mask >>= 1, ipi++) {
+ if ((mask & 0x01) == 0)
+ continue;
+ /*
+ * Clear an IPI before dispatching to not miss anyone
+ * and make sure that it's observed by everybody.
+ */
+ bcm_lintc_write_4(sc, BCM_LINTC_MBOX0_CLR_REG(cpu), 1 << ipi);
+#if defined(__aarch64__)
+ dsb(sy);
+#else
+ dsb();
+#endif
+ intr_ipi_dispatch(ipi, tf);
+ }
+}
+#endif
+
+static inline void
+bcm_lintc_irq_dispatch(struct bcm_lintc_softc *sc, u_int irq,
+ struct trapframe *tf)
+{
+ struct bcm_lintc_irqsrc *bli;
+
+ bli = &sc->bls_isrcs[irq];
+ if (intr_isrc_dispatch(&bli->bli_isrc, tf) != 0)
+ device_printf(sc->bls_dev, "Stray irq %u detected\n", irq);
+}
+
+static int
+bcm_lintc_intr(void *arg)
+{
+ struct bcm_lintc_softc *sc;
+ u_int cpu;
+ uint32_t num, reg;
+ struct trapframe *tf;
+
+ sc = arg;
+ cpu = PCPU_GET(cpuid);
+ tf = curthread->td_intr_frame;
+
+ for (num = 0; ; num++) {
+ reg = bcm_lintc_read_4(sc, BCM_LINTC_PENDING_REG(cpu));
+ if ((reg & BCM_LINTC_PENDING_MASK) == 0)
+ break;
+#ifdef SMP
+ if (reg & BCM_LINTC_MBOX0_IRQ_MASK)
+ bcm_lintc_ipi_dispatch(sc, cpu, tf);
+#endif
+ if (reg & BCM_LINTC_TIMER0_IRQ_MASK)
+ bcm_lintc_irq_dispatch(sc, BCM_LINTC_TIMER0_IRQ, tf);
+ if (reg & BCM_LINTC_TIMER1_IRQ_MASK)
+ bcm_lintc_irq_dispatch(sc, BCM_LINTC_TIMER1_IRQ, tf);
+ if (reg & BCM_LINTC_TIMER2_IRQ_MASK)
+ bcm_lintc_irq_dispatch(sc, BCM_LINTC_TIMER2_IRQ, tf);
+ if (reg & BCM_LINTC_TIMER3_IRQ_MASK)
+ bcm_lintc_irq_dispatch(sc, BCM_LINTC_TIMER3_IRQ, tf);
+ if (reg & BCM_LINTC_GPU_IRQ_MASK)
+ bcm_lintc_irq_dispatch(sc, BCM_LINTC_GPU_IRQ, tf);
+ if (reg & BCM_LINTC_PMU_IRQ_MASK)
+ bcm_lintc_irq_dispatch(sc, BCM_LINTC_PMU_IRQ, tf);
+
+ arm_irq_memory_barrier(0); /* XXX */
+ }
+ reg &= ~BCM_LINTC_PENDING_MASK;
+ if (reg != 0)
+ device_printf(sc->bls_dev, "Unknown interrupt(s) %x\n", reg);
+ else if (num == 0 && bootverbose)
+ device_printf(sc->bls_dev, "Spurious interrupt detected\n");
+
+ return (FILTER_HANDLED);
+}
+
+static void
+bcm_lintc_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+
+ bcm_lintc_mask(device_get_softc(dev), (struct bcm_lintc_irqsrc *)isrc);
+}
+
+static void
+bcm_lintc_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct bcm_lintc_irqsrc *bli = (struct bcm_lintc_irqsrc *)isrc;
+
+ arm_irq_memory_barrier(bli->bli_irq);
+ bcm_lintc_unmask(device_get_softc(dev), bli);
+}
+
+static int
+bcm_lintc_map_intr(device_t dev, struct intr_map_data *data,
+ struct intr_irqsrc **isrcp)
+{
+ struct intr_map_data_fdt *daf;
+ struct bcm_lintc_softc *sc;
+
+ if (data->type != INTR_MAP_DATA_FDT)
+ return (ENOTSUP);
+
+ daf = (struct intr_map_data_fdt *)data;
+ if (daf->ncells > 2 || daf->cells[0] >= BCM_LINTC_NIRQS)
+ return (EINVAL);
+
+ /* TODO: handle IRQ type here */
+
+ sc = device_get_softc(dev);
+ *isrcp = &sc->bls_isrcs[daf->cells[0]].bli_isrc;
+ return (0);
+}
+
+static void
+bcm_lintc_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct bcm_lintc_irqsrc *bli = (struct bcm_lintc_irqsrc *)isrc;
+
+ if (bli->bli_irq == BCM_LINTC_GPU_IRQ)
+ bcm_lintc_gpu_mask(device_get_softc(dev), bli);
+ else {
+ /*
+ * Handler for PPI interrupt does not make sense much unless
+ * there is one bound ithread for each core for it. Thus the
+ * interrupt can be masked on current core only while ithread
+ * bounded to this core ensures unmasking on the same core.
+ */
+ panic ("%s: handlers are not supported", __func__);
+ }
+}
+
+static void
+bcm_lintc_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct bcm_lintc_irqsrc *bli = (struct bcm_lintc_irqsrc *)isrc;
+
+ if (bli->bli_irq == BCM_LINTC_GPU_IRQ)
+ bcm_lintc_gpu_unmask(device_get_softc(dev), bli);
+ else {
+ /* See comment in bcm_lintc_pre_ithread(). */
+ panic ("%s: handlers are not supported", __func__);
+ }
+}
+
+static void
+bcm_lintc_post_filter(device_t dev, struct intr_irqsrc *isrc)
+{
+}
+
+static int
+bcm_lintc_setup_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ struct bcm_lintc_softc *sc;
+
+ if (isrc->isrc_handlers == 0 && isrc->isrc_flags & INTR_ISRCF_PPI) {
+ sc = device_get_softc(dev);
+ BCM_LINTC_LOCK(sc);
+ CPU_SET(PCPU_GET(cpuid), &isrc->isrc_cpu);
+ BCM_LINTC_UNLOCK(sc);
+ }
+ return (0);
+}
+
+#ifdef SMP
+static void
+bcm_lintc_init_rwreg_on_ap(struct bcm_lintc_softc *sc, u_int cpu, u_int irq,
+ uint32_t reg, uint32_t mask)
+{
+
+ if (intr_isrc_init_on_cpu(&sc->bls_isrcs[irq].bli_isrc, cpu))
+ bcm_lintc_rwreg_set(sc, reg, mask);
+}
+
+static void
+bcm_lintc_init_pmu_on_ap(struct bcm_lintc_softc *sc, u_int cpu)
+{
+ struct intr_irqsrc *isrc = &sc->bls_isrcs[BCM_LINTC_PMU_IRQ].bli_isrc;
+
+ if (intr_isrc_init_on_cpu(isrc, cpu)) {
+ /* Write-set register. */
+ bcm_lintc_write_4(sc, BCM_LINTC_PMU_ROUTING_SET_REG,
+ BCM_LINTC_PIRR_IRQ_EN_CORE(cpu));
+ }
+}
+
+static void
+bcm_lintc_init_secondary(device_t dev)
+{
+ u_int cpu;
+ struct bcm_lintc_softc *sc;
+
+ cpu = PCPU_GET(cpuid);
+ sc = device_get_softc(dev);
+
+ BCM_LINTC_LOCK(sc);
+ bcm_lintc_init_rwreg_on_ap(sc, cpu, BCM_LINTC_TIMER0_IRQ,
+ BCM_LINTC_TIMER_CFG_REG(cpu), BCM_LINTC_TCR_IRQ_EN_TIMER(0));
+ bcm_lintc_init_rwreg_on_ap(sc, cpu, BCM_LINTC_TIMER1_IRQ,
+ BCM_LINTC_TIMER_CFG_REG(cpu), BCM_LINTC_TCR_IRQ_EN_TIMER(1));
+ bcm_lintc_init_rwreg_on_ap(sc, cpu, BCM_LINTC_TIMER2_IRQ,
+ BCM_LINTC_TIMER_CFG_REG(cpu), BCM_LINTC_TCR_IRQ_EN_TIMER(2));
+ bcm_lintc_init_rwreg_on_ap(sc, cpu, BCM_LINTC_TIMER3_IRQ,
+ BCM_LINTC_TIMER_CFG_REG(cpu), BCM_LINTC_TCR_IRQ_EN_TIMER(3));
+ bcm_lintc_init_pmu_on_ap(sc, cpu);
+ BCM_LINTC_UNLOCK(sc);
+}
+
+static void
+bcm_lintc_ipi_send(device_t dev, struct intr_irqsrc *isrc, cpuset_t cpus,
+ u_int ipi)
+{
+ struct bcm_lintc_softc *sc = device_get_softc(dev);
+
+ KASSERT(isrc == &sc->bls_isrcs[BCM_LINTC_MBOX0_IRQ].bli_isrc,
+ ("%s: bad ISRC %p argument", __func__, isrc));
+ bcm_lintc_ipi_write(sc, cpus, ipi);
+}
+
+static int
+bcm_lintc_ipi_setup(device_t dev, u_int ipi, struct intr_irqsrc **isrcp)
+{
+ struct bcm_lintc_softc *sc = device_get_softc(dev);
+
+ KASSERT(ipi < BCM_LINTC_NIPIS, ("%s: too high ipi %u", __func__, ipi));
+
+ *isrcp = &sc->bls_isrcs[BCM_LINTC_MBOX0_IRQ].bli_isrc;
+ return (0);
+}
+#endif
+
+static int
+bcm_lintc_pic_attach(struct bcm_lintc_softc *sc)
+{
+ struct bcm_lintc_irqsrc *bisrcs;
+ struct intr_pic *pic;
+ int error;
+ u_int flags;
+ uint32_t irq;
+ const char *name;
+ intptr_t xref;
+
+ bisrcs = sc->bls_isrcs;
+ name = device_get_nameunit(sc->bls_dev);
+ for (irq = 0; irq < BCM_LINTC_NIRQS; irq++) {
+ bisrcs[irq].bli_irq = irq;
+ switch (irq) {
+ case BCM_LINTC_TIMER0_IRQ:
+ bisrcs[irq].bli_mask = BCM_LINTC_TCR_IRQ_EN_TIMER(0);
+ flags = INTR_ISRCF_PPI;
+ break;
+ case BCM_LINTC_TIMER1_IRQ:
+ bisrcs[irq].bli_mask = BCM_LINTC_TCR_IRQ_EN_TIMER(1);
+ flags = INTR_ISRCF_PPI;
+ break;
+ case BCM_LINTC_TIMER2_IRQ:
+ bisrcs[irq].bli_mask = BCM_LINTC_TCR_IRQ_EN_TIMER(2);
+ flags = INTR_ISRCF_PPI;
+ break;
+ case BCM_LINTC_TIMER3_IRQ:
+ bisrcs[irq].bli_mask = BCM_LINTC_TCR_IRQ_EN_TIMER(3);
+ flags = INTR_ISRCF_PPI;
+ break;
+ case BCM_LINTC_MBOX0_IRQ:
+ case BCM_LINTC_MBOX1_IRQ:
+ case BCM_LINTC_MBOX2_IRQ:
+ case BCM_LINTC_MBOX3_IRQ:
+ bisrcs[irq].bli_value = 0; /* not used */
+ flags = INTR_ISRCF_IPI;
+ break;
+ case BCM_LINTC_GPU_IRQ:
+ bisrcs[irq].bli_value = BCM_LINTC_GIRR_IRQ_CORE(0);
+ flags = 0;
+ break;
+ case BCM_LINTC_PMU_IRQ:
+ bisrcs[irq].bli_value = 0; /* not used */
+ flags = INTR_ISRCF_PPI;
+ break;
+ default:
+ bisrcs[irq].bli_value = 0; /* not used */
+ flags = 0;
+ break;
+ }
+
+ error = intr_isrc_register(&bisrcs[irq].bli_isrc, sc->bls_dev,
+ flags, "%s,%u", name, irq);
+ if (error != 0)
+ return (error);
+ }
+
+ xref = OF_xref_from_node(ofw_bus_get_node(sc->bls_dev));
+ pic = intr_pic_register(sc->bls_dev, xref);
+ if (pic == NULL)
+ return (ENXIO);
+
+ return (intr_pic_claim_root(sc->bls_dev, xref, bcm_lintc_intr, sc, 0));
+}
+
+static int
+bcm_lintc_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "brcm,bcm2836-l1-intc"))
+ return (ENXIO);
+ if (!ofw_bus_has_prop(dev, "interrupt-controller"))
+ return (ENXIO);
+ device_set_desc(dev, "BCM2836 Interrupt Controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+bcm_lintc_attach(device_t dev)
+{
+ struct bcm_lintc_softc *sc;
+ int cpu, rid;
+
+ sc = device_get_softc(dev);
+
+ sc->bls_dev = dev;
+ if (bcm_lintc_sc != NULL)
+ return (ENXIO);
+
+ rid = 0;
+ sc->bls_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->bls_mem == NULL) {
+ device_printf(dev, "could not allocate memory resource\n");
+ return (ENXIO);
+ }
+
+ sc->bls_bst = rman_get_bustag(sc->bls_mem);
+ sc->bls_bsh = rman_get_bushandle(sc->bls_mem);
+
+ bcm_lintc_write_4(sc, BCM_LINTC_CONTROL_REG, 0);
+ bcm_lintc_write_4(sc, BCM_LINTC_PRESCALER_REG, BCM_LINTC_PSR_19_2);
+
+ /* Disable all timers on all cores. */
+ for (cpu = 0; cpu < 4; cpu++)
+ bcm_lintc_write_4(sc, BCM_LINTC_TIMER_CFG_REG(cpu), 0);
+
+#ifdef SMP
+ /* Enable mailbox 0 on all cores used for IPI. */
+ for (cpu = 0; cpu < 4; cpu++)
+ bcm_lintc_write_4(sc, BCM_LINTC_MBOX_CFG_REG(cpu),
+ BCM_LINTC_MCR_IRQ_EN_MBOX(0));
+#endif
+
+ if (bcm_lintc_pic_attach(sc) != 0) {
+ device_printf(dev, "could not attach PIC\n");
+ return (ENXIO);
+ }
+
+ BCM_LINTC_LOCK_INIT(sc);
+ bcm_lintc_sc = sc;
+ return (0);
+}
+
+static device_method_t bcm_lintc_methods[] = {
+ DEVMETHOD(device_probe, bcm_lintc_probe),
+ DEVMETHOD(device_attach, bcm_lintc_attach),
+
+ DEVMETHOD(pic_disable_intr, bcm_lintc_disable_intr),
+ DEVMETHOD(pic_enable_intr, bcm_lintc_enable_intr),
+ DEVMETHOD(pic_map_intr, bcm_lintc_map_intr),
+ DEVMETHOD(pic_post_filter, bcm_lintc_post_filter),
+ DEVMETHOD(pic_post_ithread, bcm_lintc_post_ithread),
+ DEVMETHOD(pic_pre_ithread, bcm_lintc_pre_ithread),
+ DEVMETHOD(pic_setup_intr, bcm_lintc_setup_intr),
+#ifdef SMP
+ DEVMETHOD(pic_init_secondary, bcm_lintc_init_secondary),
+ DEVMETHOD(pic_ipi_send, bcm_lintc_ipi_send),
+ DEVMETHOD(pic_ipi_setup, bcm_lintc_ipi_setup),
+#endif
+
+ DEVMETHOD_END
+};
+
+static driver_t bcm_lintc_driver = {
+ "lintc",
+ bcm_lintc_methods,
+ sizeof(struct bcm_lintc_softc),
+};
+
+static devclass_t bcm_lintc_devclass;
+
+EARLY_DRIVER_MODULE(lintc, simplebus, bcm_lintc_driver, bcm_lintc_devclass,
+ 0, 0, BUS_PASS_INTERRUPT);
diff --git a/sys/arm/broadcom/bcm2835/bcm2836_mp.c b/sys/arm/broadcom/bcm2835/bcm2836_mp.c
new file mode 100644
index 000000000000..0f2565a2e8ae
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2836_mp.c
@@ -0,0 +1,146 @@
+/*-
+ * Copyright (C) 2015 Daisuke Aoyama <aoyama@peach.ne.jp>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/smp.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/cpu.h>
+#include <machine/smp.h>
+#include <machine/bus.h>
+#include <machine/fdt.h>
+#include <machine/intr.h>
+#include <machine/platformvar.h>
+
+#include <arm/broadcom/bcm2835/bcm2836_mp.h>
+
+#ifdef DEBUG
+#define DPRINTF(fmt, ...) do { \
+ printf("%s:%u: ", __func__, __LINE__); \
+ printf(fmt, ##__VA_ARGS__); \
+} while (0)
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
+#define ARM_LOCAL_BASE 0x40000000
+#define ARM_LOCAL_SIZE 0x00001000
+
+/* mailbox registers */
+#define MBOXINTRCTRL_CORE(n) (0x00000050 + (0x04 * (n)))
+#define MBOX0SET_CORE(n) (0x00000080 + (0x10 * (n)))
+#define MBOX1SET_CORE(n) (0x00000084 + (0x10 * (n)))
+#define MBOX2SET_CORE(n) (0x00000088 + (0x10 * (n)))
+#define MBOX3SET_CORE(n) (0x0000008C + (0x10 * (n)))
+#define MBOX0CLR_CORE(n) (0x000000C0 + (0x10 * (n)))
+#define MBOX1CLR_CORE(n) (0x000000C4 + (0x10 * (n)))
+#define MBOX2CLR_CORE(n) (0x000000C8 + (0x10 * (n)))
+#define MBOX3CLR_CORE(n) (0x000000CC + (0x10 * (n)))
+
+static bus_space_handle_t bs_periph;
+
+#define BSRD4(addr) \
+ bus_space_read_4(fdtbus_bs_tag, bs_periph, (addr))
+#define BSWR4(addr, val) \
+ bus_space_write_4(fdtbus_bs_tag, bs_periph, (addr), (val))
+
+void
+bcm2836_mp_setmaxid(platform_t plat)
+{
+
+ DPRINTF("platform_mp_setmaxid\n");
+ if (mp_ncpus != 0)
+ return;
+
+ mp_ncpus = 4;
+ mp_maxid = mp_ncpus - 1;
+ DPRINTF("mp_maxid=%d\n", mp_maxid);
+}
+
+void
+bcm2836_mp_start_ap(platform_t plat)
+{
+ uint32_t val;
+ int i, retry;
+
+ DPRINTF("platform_mp_start_ap\n");
+
+ /* initialize */
+ if (bus_space_map(fdtbus_bs_tag, ARM_LOCAL_BASE, ARM_LOCAL_SIZE,
+ 0, &bs_periph) != 0)
+ panic("can't map local peripheral\n");
+ for (i = 0; i < mp_ncpus; i++) {
+ /* clear mailbox 0/3 */
+ BSWR4(MBOX0CLR_CORE(i), 0xffffffff);
+ BSWR4(MBOX3CLR_CORE(i), 0xffffffff);
+ }
+ wmb();
+ dcache_wbinv_poc_all();
+
+ /* boot secondary CPUs */
+ for (i = 1; i < mp_ncpus; i++) {
+ /* set entry point to mailbox 3 */
+ BSWR4(MBOX3SET_CORE(i),
+ (uint32_t)pmap_kextract((vm_offset_t)mpentry));
+ /* Firmware put cores in WFE state, need SEV to wake up. */
+ dsb();
+ sev();
+
+ /* wait for bootup */
+ retry = 1000;
+ do {
+ /* check entry point */
+ val = BSRD4(MBOX3CLR_CORE(i));
+ if (val == 0)
+ break;
+ DELAY(100);
+ retry--;
+ if (retry <= 0) {
+ printf("can't start for CPU%d\n", i);
+ break;
+ }
+ } while (1);
+
+ /* dsb and sev */
+ dsb();
+ sev();
+
+ /* recode AP in CPU map */
+ CPU_SET(i, &all_cpus);
+ }
+}
diff --git a/sys/arm/broadcom/bcm2835/bcm2836_mp.h b/sys/arm/broadcom/bcm2835/bcm2836_mp.h
new file mode 100644
index 000000000000..5f9ac8179f24
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2836_mp.h
@@ -0,0 +1,37 @@
+/*-
+ * Copyright (C) 2016 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _BCM2836_MP_H_
+#define _BCM2836_MP_H_
+
+void bcm2836_mp_setmaxid(platform_t plat);
+void bcm2836_mp_start_ap(platform_t plat);
+
+#endif /* _BCM2836_MP_H_ */
diff --git a/sys/arm/broadcom/bcm2835/bcm2838_pci.c b/sys/arm/broadcom/bcm2835/bcm2838_pci.c
new file mode 100644
index 000000000000..15111f73ca5e
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2838_pci.c
@@ -0,0 +1,782 @@
+/*-
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 2020 Dr Robert Harvey Crowston <crowston@protonmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ *
+ * $FreeBSD$
+ *
+ */
+
+/*
+ * BCM2838-compatible PCI-express controller.
+ *
+ * Broadcom likes to give the same chip lots of different names. The name of
+ * this driver is taken from the Raspberry Pi 4 Broadcom 2838 chip.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/proc.h>
+#include <sys/rman.h>
+#include <sys/intr.h>
+#include <sys/mutex.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/pci/pci_host_generic.h>
+#include <dev/pci/pci_host_generic_fdt.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcib_private.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include "pcib_if.h"
+#include "msi_if.h"
+
+#define PCI_ID_VAL3 0x43c
+#define CLASS_SHIFT 0x10
+#define SUBCLASS_SHIFT 0x8
+
+#define REG_CONTROLLER_HW_REV 0x406c
+#define REG_BRIDGE_CTRL 0x9210
+#define BRIDGE_DISABLE_FLAG 0x1
+#define BRIDGE_RESET_FLAG 0x2
+#define REG_BRIDGE_SERDES_MODE 0x4204
+#define REG_DMA_CONFIG 0x4008
+#define REG_DMA_WINDOW_LOW 0x4034
+#define REG_DMA_WINDOW_HIGH 0x4038
+#define REG_DMA_WINDOW_1 0x403c
+#define REG_BRIDGE_GISB_WINDOW 0x402c
+#define REG_BRIDGE_STATE 0x4068
+#define REG_BRIDGE_LINK_STATE 0x00bc
+#define REG_BUS_WINDOW_LOW 0x400c
+#define REG_BUS_WINDOW_HIGH 0x4010
+#define REG_CPU_WINDOW_LOW 0x4070
+#define REG_CPU_WINDOW_START_HIGH 0x4080
+#define REG_CPU_WINDOW_END_HIGH 0x4084
+
+#define REG_MSI_ADDR_LOW 0x4044
+#define REG_MSI_ADDR_HIGH 0x4048
+#define REG_MSI_CONFIG 0x404c
+#define REG_MSI_CLR 0x4508
+#define REG_MSI_MASK_CLR 0x4514
+#define REG_MSI_RAISED 0x4500
+#define REG_MSI_EOI 0x4060
+#define NUM_MSI 32
+
+#define REG_EP_CONFIG_CHOICE 0x9000
+#define REG_EP_CONFIG_DATA 0x8000
+
+/*
+ * The system memory controller can address up to 16 GiB of physical memory
+ * (although at time of writing the largest memory size available for purchase
+ * is 8 GiB). However, the system DMA controller is capable of accessing only a
+ * limited portion of the address space. Worse, the PCI-e controller has further
+ * constraints for DMA, and those limitations are not wholly clear to the
+ * author. NetBSD and Linux allow DMA on the lower 3 GiB of the physical memory,
+ * but experimentation shows DMA performed above 960 MiB results in data
+ * corruption with this driver. The limit of 960 MiB is taken from OpenBSD, but
+ * apparently that value was chosen for satisfying a constraint of an unrelated
+ * peripheral.
+ *
+ * Whatever the true maximum address, 960 MiB works.
+ */
+#define DMA_HIGH_LIMIT 0x3c000000
+#define MAX_MEMORY_LOG2 0x21
+#define REG_VALUE_DMA_WINDOW_LOW (MAX_MEMORY_LOG2 - 0xf)
+#define REG_VALUE_DMA_WINDOW_HIGH 0x0
+#define DMA_WINDOW_ENABLE 0x3000
+#define REG_VALUE_DMA_WINDOW_CONFIG \
+ (((MAX_MEMORY_LOG2 - 0xf) << 0x1b) | DMA_WINDOW_ENABLE)
+
+#define REG_VALUE_MSI_CONFIG 0xffe06540
+
+struct bcm_pcib_irqsrc {
+ struct intr_irqsrc isrc;
+ u_int irq;
+ bool allocated;
+};
+
+struct bcm_pcib_softc {
+ struct generic_pcie_fdt_softc base;
+ device_t dev;
+ bus_dma_tag_t dmat;
+ struct mtx config_mtx;
+ struct mtx msi_mtx;
+ struct resource *msi_irq_res;
+ void *msi_intr_cookie;
+ struct bcm_pcib_irqsrc *msi_isrcs;
+ pci_addr_t msi_addr;
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"brcm,bcm2711-pcie", 1},
+ {"brcm,bcm7211-pcie", 1},
+ {"brcm,bcm7445-pcie", 1},
+ {NULL, 0}
+};
+
+static int
+bcm_pcib_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+ return (ENXIO);
+
+ device_set_desc(dev,
+ "BCM2838-compatible PCI-express controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static bus_dma_tag_t
+bcm_pcib_get_dma_tag(device_t dev, device_t child)
+{
+ struct bcm_pcib_softc *sc;
+
+ sc = device_get_softc(dev);
+ return (sc->dmat);
+}
+
+static void
+bcm_pcib_set_reg(struct bcm_pcib_softc *sc, uint32_t reg, uint32_t val)
+{
+
+ bus_space_write_4(sc->base.base.bst, sc->base.base.bsh, reg,
+ htole32(val));
+}
+
+static uint32_t
+bcm_pcib_read_reg(struct bcm_pcib_softc *sc, uint32_t reg)
+{
+
+ return (le32toh(bus_space_read_4(sc->base.base.bst, sc->base.base.bsh,
+ reg)));
+}
+
+static void
+bcm_pcib_reset_controller(struct bcm_pcib_softc *sc)
+{
+ uint32_t val;
+
+ val = bcm_pcib_read_reg(sc, REG_BRIDGE_CTRL);
+ val = val | BRIDGE_RESET_FLAG | BRIDGE_DISABLE_FLAG;
+ bcm_pcib_set_reg(sc, REG_BRIDGE_CTRL, val);
+
+ DELAY(100);
+
+ val = bcm_pcib_read_reg(sc, REG_BRIDGE_CTRL);
+ val = val & ~BRIDGE_RESET_FLAG;
+ bcm_pcib_set_reg(sc, REG_BRIDGE_CTRL, val);
+
+ DELAY(100);
+
+ bcm_pcib_set_reg(sc, REG_BRIDGE_SERDES_MODE, 0);
+
+ DELAY(100);
+}
+
+static void
+bcm_pcib_enable_controller(struct bcm_pcib_softc *sc)
+{
+ uint32_t val;
+
+ val = bcm_pcib_read_reg(sc, REG_BRIDGE_CTRL);
+ val = val & ~BRIDGE_DISABLE_FLAG;
+ bcm_pcib_set_reg(sc, REG_BRIDGE_CTRL, val);
+
+ DELAY(100);
+}
+
+static int
+bcm_pcib_check_ranges(device_t dev)
+{
+ struct bcm_pcib_softc *sc;
+ struct pcie_range *ranges;
+ int error = 0, i;
+
+ sc = device_get_softc(dev);
+ ranges = &sc->base.base.ranges[0];
+
+ /* The first range needs to be non-zero. */
+ if (ranges[0].size == 0) {
+ device_printf(dev, "error: first outbound memory range "
+ "(pci addr: 0x%jx, cpu addr: 0x%jx) has zero size.\n",
+ ranges[0].pci_base, ranges[0].phys_base);
+ error = ENXIO;
+ }
+
+ /*
+ * The controller can actually handle three distinct ranges, but we
+ * only implement support for one.
+ */
+ for (i = 1; (bootverbose || error) && i < MAX_RANGES_TUPLES; ++i) {
+ if (ranges[i].size > 0)
+ device_printf(dev,
+ "note: outbound memory range %d (pci addr: 0x%jx, "
+ "cpu addr: 0x%jx, size: 0x%jx) will be ignored.\n",
+ i, ranges[i].pci_base, ranges[i].phys_base,
+ ranges[i].size);
+ }
+
+ return (error);
+}
+
+static const char *
+bcm_pcib_link_state_string(uint32_t mode)
+{
+
+ switch(mode & PCIEM_LINK_STA_SPEED) {
+ case 0:
+ return ("not up");
+ case 1:
+ return ("2.5 GT/s");
+ case 2:
+ return ("5.0 GT/s");
+ case 4:
+ return ("8.0 GT/s");
+ default:
+ return ("unknown");
+ }
+}
+
+static bus_addr_t
+bcm_get_offset_and_prepare_config(struct bcm_pcib_softc *sc, u_int bus,
+ u_int slot, u_int func, u_int reg)
+{
+ /*
+ * Config for an end point is only available through a narrow window for
+ * one end point at a time. We first tell the controller which end point
+ * we want, then access it through the window.
+ */
+ uint32_t func_index;
+
+ if (bus == 0 && slot == 0 && func == 0)
+ /*
+ * Special case for root device; its config is always available
+ * through the zero-offset.
+ */
+ return (reg);
+
+ /* Tell the controller to show us the config in question. */
+ func_index = PCIE_ADDR_OFFSET(bus, slot, func, 0);
+ bcm_pcib_set_reg(sc, REG_EP_CONFIG_CHOICE, func_index);
+
+ return (REG_EP_CONFIG_DATA + reg);
+}
+
+static bool
+bcm_pcib_is_valid_quad(struct bcm_pcib_softc *sc, u_int bus, u_int slot,
+ u_int func, u_int reg)
+{
+
+ if ((bus < sc->base.base.bus_start) || (bus > sc->base.base.bus_end))
+ return (false);
+ if ((slot > PCI_SLOTMAX) || (func > PCI_FUNCMAX) || (reg > PCIE_REGMAX))
+ return (false);
+
+ if (bus == 0 && slot == 0 && func == 0)
+ return (true);
+ if (bus == 0)
+ /*
+ * Probing other slots and funcs on bus 0 will lock up the
+ * memory controller.
+ */
+ return (false);
+
+ return (true);
+}
+
+static uint32_t
+bcm_pcib_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg,
+ int bytes)
+{
+ struct bcm_pcib_softc *sc;
+ bus_space_handle_t h;
+ bus_space_tag_t t;
+ bus_addr_t offset;
+ uint32_t data;
+
+ sc = device_get_softc(dev);
+ if (!bcm_pcib_is_valid_quad(sc, bus, slot, func, reg))
+ return (~0U);
+
+ mtx_lock(&sc->config_mtx);
+ offset = bcm_get_offset_and_prepare_config(sc, bus, slot, func, reg);
+
+ t = sc->base.base.bst;
+ h = sc->base.base.bsh;
+
+ switch (bytes) {
+ case 1:
+ data = bus_space_read_1(t, h, offset);
+ break;
+ case 2:
+ data = le16toh(bus_space_read_2(t, h, offset));
+ break;
+ case 4:
+ data = le32toh(bus_space_read_4(t, h, offset));
+ break;
+ default:
+ data = ~0U;
+ break;
+ }
+
+ mtx_unlock(&sc->config_mtx);
+ return (data);
+}
+
+static void
+bcm_pcib_write_config(device_t dev, u_int bus, u_int slot,
+ u_int func, u_int reg, uint32_t val, int bytes)
+{
+ struct bcm_pcib_softc *sc;
+ bus_space_handle_t h;
+ bus_space_tag_t t;
+ uint32_t offset;
+
+ sc = device_get_softc(dev);
+ if (!bcm_pcib_is_valid_quad(sc, bus, slot, func, reg))
+ return;
+
+ mtx_lock(&sc->config_mtx);
+ offset = bcm_get_offset_and_prepare_config(sc, bus, slot, func, reg);
+
+ t = sc->base.base.bst;
+ h = sc->base.base.bsh;
+
+ switch (bytes) {
+ case 1:
+ bus_space_write_1(t, h, offset, val);
+ break;
+ case 2:
+ bus_space_write_2(t, h, offset, htole16(val));
+ break;
+ case 4:
+ bus_space_write_4(t, h, offset, htole32(val));
+ break;
+ default:
+ break;
+ }
+
+ mtx_unlock(&sc->config_mtx);
+}
+
+static void
+bcm_pcib_msi_intr_process(struct bcm_pcib_softc *sc, uint32_t interrupt_bitmap,
+ struct trapframe *tf)
+{
+ struct bcm_pcib_irqsrc *irqsrc;
+ uint32_t bit, irq;
+
+ while ((bit = ffs(interrupt_bitmap))) {
+ irq = bit - 1;
+
+ /* Acknowledge interrupt. */
+ bcm_pcib_set_reg(sc, REG_MSI_CLR, 1 << irq);
+
+ /* Send EOI. */
+ bcm_pcib_set_reg(sc, REG_MSI_EOI, 1);
+
+ /* Despatch to handler. */
+ irqsrc = &sc->msi_isrcs[irq];
+ if (intr_isrc_dispatch(&irqsrc->isrc, tf))
+ device_printf(sc->dev,
+ "note: unexpected interrupt (%d) triggered.\n",
+ irq);
+
+ /* Done with this interrupt. */
+ interrupt_bitmap = interrupt_bitmap & ~(1 << irq);
+ }
+}
+
+static int
+bcm_pcib_msi_intr(void *arg)
+{
+ struct bcm_pcib_softc *sc;
+ struct trapframe *tf;
+ uint32_t interrupt_bitmap;
+
+ sc = (struct bcm_pcib_softc *) arg;
+ tf = curthread->td_intr_frame;
+
+ while ((interrupt_bitmap = bcm_pcib_read_reg(sc, REG_MSI_RAISED)))
+ bcm_pcib_msi_intr_process(sc, interrupt_bitmap, tf);
+
+ return (FILTER_HANDLED);
+}
+
+static int
+bcm_pcib_alloc_msi(device_t dev, device_t child, int count, int maxcount,
+ device_t *pic, struct intr_irqsrc **srcs)
+{
+ struct bcm_pcib_softc *sc;
+ int first_int, i;
+
+ sc = device_get_softc(dev);
+ mtx_lock(&sc->msi_mtx);
+
+ /* Find a continguous region of free message-signalled interrupts. */
+ for (first_int = 0; first_int + count < NUM_MSI; ) {
+ for (i = first_int; i < first_int + count; ++i) {
+ if (sc->msi_isrcs[i].allocated)
+ goto next;
+ }
+ goto found;
+next:
+ first_int = i + 1;
+ }
+
+ /* No appropriate region available. */
+ mtx_unlock(&sc->msi_mtx);
+ device_printf(dev, "warning: failed to allocate %d MSI messages.\n",
+ count);
+ return (ENXIO);
+
+found:
+ /* Mark the messages as in use. */
+ for (i = 0; i < count; ++i) {
+ sc->msi_isrcs[i + first_int].allocated = true;
+ srcs[i] = &(sc->msi_isrcs[i + first_int].isrc);
+ }
+
+ mtx_unlock(&sc->msi_mtx);
+ *pic = device_get_parent(dev);
+
+ return (0);
+}
+
+static int
+bcm_pcib_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc,
+ uint64_t *addr, uint32_t *data)
+{
+ struct bcm_pcib_softc *sc;
+ struct bcm_pcib_irqsrc *msi_msg;
+
+ sc = device_get_softc(dev);
+ msi_msg = (struct bcm_pcib_irqsrc *) isrc;
+
+ *addr = sc->msi_addr;
+ *data = (REG_VALUE_MSI_CONFIG & 0xffff) | msi_msg->irq;
+ return (0);
+}
+
+static int
+bcm_pcib_release_msi(device_t dev, device_t child, int count,
+ struct intr_irqsrc **isrc)
+{
+ struct bcm_pcib_softc *sc;
+ struct bcm_pcib_irqsrc *msi_isrc;
+ int i;
+
+ sc = device_get_softc(dev);
+ mtx_lock(&sc->msi_mtx);
+
+ for (i = 0; i < count; i++) {
+ msi_isrc = (struct bcm_pcib_irqsrc *) isrc[i];
+ msi_isrc->allocated = false;
+ }
+
+ mtx_unlock(&sc->msi_mtx);
+ return (0);
+}
+
+static int
+bcm_pcib_msi_attach(device_t dev)
+{
+ struct bcm_pcib_softc *sc;
+ phandle_t node, xref;
+ char const *bcm_name;
+ int i, rid;
+
+ sc = device_get_softc(dev);
+ sc->msi_addr = 0xffffffffc;
+
+ /* Clear any pending interrupts. */
+ bcm_pcib_set_reg(sc, REG_MSI_CLR, 0xffffffff);
+
+ rid = 1;
+ sc->msi_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (sc->msi_irq_res == NULL) {
+ device_printf(dev, "could not allocate MSI irq resource.\n");
+ return (ENXIO);
+ }
+
+ sc->msi_isrcs = malloc(sizeof(*sc->msi_isrcs) * NUM_MSI, M_DEVBUF,
+ M_WAITOK | M_ZERO);
+
+ int error = bus_setup_intr(dev, sc->msi_irq_res, INTR_TYPE_BIO |
+ INTR_MPSAFE, bcm_pcib_msi_intr, NULL, sc, &sc->msi_intr_cookie);
+ if (error) {
+ device_printf(dev, "error: failed to setup MSI handler.\n");
+ return (ENXIO);
+ }
+
+ bcm_name = device_get_nameunit(dev);
+ for (i = 0; i < NUM_MSI; i++) {
+ sc->msi_isrcs[i].irq = i;
+ error = intr_isrc_register(&sc->msi_isrcs[i].isrc, dev, 0,
+ "%s,%u", bcm_name, i);
+ if (error) {
+ device_printf(dev,
+ "error: failed to register interrupt %d.\n", i);
+ return (ENXIO);
+ }
+ }
+
+ node = ofw_bus_get_node(dev);
+ xref = OF_xref_from_node(node);
+ OF_device_register_xref(xref, dev);
+
+ error = intr_msi_register(dev, xref);
+ if (error)
+ return (ENXIO);
+
+ mtx_init(&sc->msi_mtx, "bcm_pcib: msi_mtx", NULL, MTX_DEF);
+
+ bcm_pcib_set_reg(sc, REG_MSI_MASK_CLR, 0xffffffff);
+ bcm_pcib_set_reg(sc, REG_MSI_ADDR_LOW, (sc->msi_addr & 0xffffffff) | 1);
+ bcm_pcib_set_reg(sc, REG_MSI_ADDR_HIGH, (sc->msi_addr >> 32));
+ bcm_pcib_set_reg(sc, REG_MSI_CONFIG, REG_VALUE_MSI_CONFIG);
+
+ return (0);
+}
+
+static void
+bcm_pcib_relocate_bridge_window(device_t dev)
+{
+ /*
+ * In principle an out-of-bounds bridge window could be automatically
+ * adjusted at resource-activation time to lie within the bus address
+ * space by pcib_grow_window(), but that is not possible because the
+ * out-of-bounds resource allocation fails at allocation time. Instead,
+ * we will just fix up the window on the controller here, before it is
+ * re-discovered by pcib_probe_windows().
+ */
+
+ struct bcm_pcib_softc *sc;
+ pci_addr_t base, size, new_base, new_limit;
+ uint16_t val;
+
+ sc = device_get_softc(dev);
+
+ val = bcm_pcib_read_config(dev, 0, 0, 0, PCIR_MEMBASE_1, 2);
+ base = PCI_PPBMEMBASE(0, val);
+
+ val = bcm_pcib_read_config(dev, 0, 0, 0, PCIR_MEMLIMIT_1, 2);
+ size = PCI_PPBMEMLIMIT(0, val) - base;
+
+ new_base = sc->base.base.ranges[0].pci_base;
+ val = (uint16_t) (new_base >> 16);
+ bcm_pcib_write_config(dev, 0, 0, 0, PCIR_MEMBASE_1, val, 2);
+
+ new_limit = new_base + size;
+ val = (uint16_t) (new_limit >> 16);
+ bcm_pcib_write_config(dev, 0, 0, 0, PCIR_MEMLIMIT_1, val, 2);
+}
+
+static uint32_t
+encode_cpu_window_low(pci_addr_t phys_base, bus_size_t size)
+{
+
+ return (((phys_base >> 0x10) & 0xfff0) |
+ ((phys_base + size - 1) & 0xfff00000));
+}
+
+static uint32_t
+encode_cpu_window_start_high(pci_addr_t phys_base)
+{
+
+ return ((phys_base >> 0x20) & 0xff);
+}
+
+static uint32_t
+encode_cpu_window_end_high(pci_addr_t phys_base, bus_size_t size)
+{
+
+ return (((phys_base + size - 1) >> 0x20) & 0xff);
+}
+
+static int
+bcm_pcib_attach(device_t dev)
+{
+ struct bcm_pcib_softc *sc;
+ pci_addr_t phys_base, pci_base;
+ bus_size_t size;
+ uint32_t hardware_rev, bridge_state, link_state;
+ int error, tries;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ /*
+ * This tag will be used in preference to the one created in
+ * pci_host_generic.c.
+ */
+ error = bus_dma_tag_create(bus_get_dma_tag(dev), /* parent */
+ 1, 0, /* alignment, bounds */
+ DMA_HIGH_LIMIT, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ DMA_HIGH_LIMIT, /* maxsize */
+ BUS_SPACE_UNRESTRICTED, /* nsegments */
+ DMA_HIGH_LIMIT, /* maxsegsize */
+ 0, /* flags */
+ NULL, NULL, /* lockfunc, lockarg */
+ &sc->dmat);
+ if (error)
+ return (error);
+
+ error = pci_host_generic_setup_fdt(dev);
+ if (error)
+ return (error);
+
+ error = bcm_pcib_check_ranges(dev);
+ if (error)
+ return (error);
+
+ mtx_init(&sc->config_mtx, "bcm_pcib: config_mtx", NULL, MTX_DEF);
+
+ bcm_pcib_reset_controller(sc);
+
+ hardware_rev = bcm_pcib_read_reg(sc, REG_CONTROLLER_HW_REV) & 0xffff;
+ device_printf(dev, "hardware identifies as revision 0x%x.\n",
+ hardware_rev);
+
+ /*
+ * Set PCI->CPU memory window. This encodes the inbound window showing
+ * the system memory to the controller.
+ */
+ bcm_pcib_set_reg(sc, REG_DMA_WINDOW_LOW, REG_VALUE_DMA_WINDOW_LOW);
+ bcm_pcib_set_reg(sc, REG_DMA_WINDOW_HIGH, REG_VALUE_DMA_WINDOW_HIGH);
+ bcm_pcib_set_reg(sc, REG_DMA_CONFIG, REG_VALUE_DMA_WINDOW_CONFIG);
+
+ bcm_pcib_set_reg(sc, REG_BRIDGE_GISB_WINDOW, 0);
+ bcm_pcib_set_reg(sc, REG_DMA_WINDOW_1, 0);
+
+ bcm_pcib_enable_controller(sc);
+
+ /* Wait for controller to start. */
+ for(tries = 0; ; ++tries) {
+ bridge_state = bcm_pcib_read_reg(sc, REG_BRIDGE_STATE);
+
+ if ((bridge_state & 0x30) == 0x30)
+ /* Controller ready. */
+ break;
+
+ if (tries > 100) {
+ device_printf(dev,
+ "error: controller failed to start.\n");
+ return (ENXIO);
+ }
+
+ DELAY(1000);
+ }
+
+ link_state = bcm_pcib_read_reg(sc, REG_BRIDGE_LINK_STATE) >> 0x10;
+ if (!link_state) {
+ device_printf(dev, "error: controller started but link is not "
+ "up.\n");
+ return (ENXIO);
+ }
+ if (bootverbose)
+ device_printf(dev, "note: reported link speed is %s.\n",
+ bcm_pcib_link_state_string(link_state));
+
+ /*
+ * Set the CPU->PCI memory window. The map in this direction is not 1:1.
+ * Addresses seen by the CPU need to be adjusted to make sense to the
+ * controller as they pass through the window.
+ */
+ pci_base = sc->base.base.ranges[0].pci_base;
+ phys_base = sc->base.base.ranges[0].phys_base;
+ size = sc->base.base.ranges[0].size;
+
+ bcm_pcib_set_reg(sc, REG_BUS_WINDOW_LOW, pci_base & 0xffffffff);
+ bcm_pcib_set_reg(sc, REG_BUS_WINDOW_HIGH, pci_base >> 32);
+
+ bcm_pcib_set_reg(sc, REG_CPU_WINDOW_LOW,
+ encode_cpu_window_low(phys_base, size));
+ bcm_pcib_set_reg(sc, REG_CPU_WINDOW_START_HIGH,
+ encode_cpu_window_start_high(phys_base));
+ bcm_pcib_set_reg(sc, REG_CPU_WINDOW_END_HIGH,
+ encode_cpu_window_end_high(phys_base, size));
+
+ /*
+ * The controller starts up declaring itself an endpoint; readvertise it
+ * as a bridge.
+ */
+ bcm_pcib_set_reg(sc, PCI_ID_VAL3,
+ PCIC_BRIDGE << CLASS_SHIFT | PCIS_BRIDGE_PCI << SUBCLASS_SHIFT);
+
+ bcm_pcib_set_reg(sc, REG_BRIDGE_SERDES_MODE, 0x2);
+ DELAY(100);
+
+ bcm_pcib_relocate_bridge_window(dev);
+
+ /* Configure interrupts. */
+ error = bcm_pcib_msi_attach(dev);
+ if (error)
+ return (error);
+
+ /* Done. */
+ device_add_child(dev, "pci", -1);
+ return (bus_generic_attach(dev));
+}
+
+/*
+ * Device method table.
+ */
+static device_method_t bcm_pcib_methods[] = {
+ /* Bus interface. */
+ DEVMETHOD(bus_get_dma_tag, bcm_pcib_get_dma_tag),
+
+ /* Device interface. */
+ DEVMETHOD(device_probe, bcm_pcib_probe),
+ DEVMETHOD(device_attach, bcm_pcib_attach),
+
+ /* PCIB interface. */
+ DEVMETHOD(pcib_read_config, bcm_pcib_read_config),
+ DEVMETHOD(pcib_write_config, bcm_pcib_write_config),
+
+ /* MSI interface. */
+ DEVMETHOD(msi_alloc_msi, bcm_pcib_alloc_msi),
+ DEVMETHOD(msi_release_msi, bcm_pcib_release_msi),
+ DEVMETHOD(msi_map_msi, bcm_pcib_map_msi),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(pcib, bcm_pcib_driver, bcm_pcib_methods,
+ sizeof(struct bcm_pcib_softc), generic_pcie_fdt_driver);
+
+static devclass_t bcm_pcib_devclass;
+DRIVER_MODULE(bcm_pcib, simplebus, bcm_pcib_driver, bcm_pcib_devclass, 0, 0);
+
diff --git a/sys/arm/broadcom/bcm2835/bcm2838_xhci.c b/sys/arm/broadcom/bcm2835/bcm2838_xhci.c
new file mode 100644
index 000000000000..5805254c0623
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm2838_xhci.c
@@ -0,0 +1,216 @@
+/*-
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 2020 Dr Robert Harvey Crowston <crowston@protonmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ *
+ * $FreeBSD$
+ *
+ */
+
+/*
+ * VIA VL805 controller on the Raspberry Pi 4.
+ * The VL805 is a generic xhci controller. However, in the newer hardware
+ * revisions of the Raspberry Pi 4, it is incapable of loading its own firmware.
+ * Instead, the VideoCore GPU must load the firmware into the controller at the
+ * appropriate time. This driver is a shim that pre-loads the firmware before
+ * handing control to the xhci generic driver.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/usb_pci.h>
+#include <dev/usb/controller/xhci.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/broadcom/bcm2835/bcm2835_mbox_prop.h>
+
+#define VL805_FIRMWARE_REG 0x50
+#define PCIE_BUS_SHIFT 20
+#define PCIE_SLOT_SHIFT 15
+#define PCIE_FUNC_SHIFT 12
+
+static int
+bcm_xhci_probe(device_t dev)
+{
+ phandle_t root;
+ uint32_t device_id;
+
+ device_id = pci_get_devid(dev);
+ if (device_id != 0x34831106) /* VIA VL805 USB 3.0 controller. */
+ return (ENXIO);
+
+ /*
+ * The VIA chip is not unique to the Pi, but we only want to use this
+ * driver if the SoC is a Raspberry Pi 4. Walk the device tree to
+ * discover if the system is a Pi 4.
+ */
+ root = OF_finddevice("/");
+ if (root == -1)
+ return (ENXIO);
+ if (!ofw_bus_node_is_compatible(root, "raspberrypi,4-model-b"))
+ return (ENXIO);
+
+ /*
+ * On the Pi 4, the VIA chip with the firmware-loading limitation is
+ * soldered-on to a particular bus/slot/function. But, it's possible a
+ * user could desolder the VIA chip, replace it with a pci-pci bridge,
+ * then plug in a commodity VIA PCI-e card on the new bridge. In that
+ * case we don't want to try to load the firmware to a commodity
+ * expansion card.
+ */
+ if (pci_get_bus(dev) != 1 || pci_get_slot(dev) != 0 ||
+ pci_get_function(dev) != 0 )
+ return (ENXIO);
+
+ device_set_desc(dev,
+ "VL805 USB 3.0 controller (on the Raspberry Pi 4b)");
+
+ return (BUS_PROBE_SPECIFIC);
+}
+
+static uint32_t
+bcm_xhci_check_firmware(device_t dev, bool expect_loaded)
+{
+ uint32_t revision;
+ bool loaded;
+
+ revision = pci_read_config(dev, VL805_FIRMWARE_REG, 4);
+ loaded = !(revision == 0 || revision == 0xffffffff);
+
+ if (expect_loaded && !loaded)
+ device_printf(dev, "warning: xhci firmware not found.\n");
+ else if (bootverbose && !loaded)
+ device_printf(dev, "note: xhci firmware not found.\n");
+ else if (bootverbose)
+ device_printf(dev,
+ "note: xhci firmware detected; firmware is revision %x.\n",
+ revision);
+
+ if (!loaded)
+ return 0;
+
+ return (revision);
+}
+
+static void
+bcm_xhci_install_xhci_firmware(device_t dev)
+{
+ uint32_t revision, dev_addr;
+ int error;
+
+ revision = bcm_xhci_check_firmware(dev, false);
+ if (revision > 0) {
+ /*
+ * With the pre-June 2020 boot firmware, it does not seem
+ * possible to reload already-installed xhci firmware.
+ */
+ return;
+ }
+
+ /*
+ * Notify the VideoCore gpu processor that it needs to reload the xhci
+ * firmware into the xhci controller. This needs to happen after the pci
+ * bridge topology is registered with the controller.
+ */
+ if (bootverbose)
+ device_printf(dev, "note: installing xhci firmware.\n");
+
+ dev_addr =
+ pci_get_bus(dev) << PCIE_BUS_SHIFT |
+ pci_get_slot(dev) << PCIE_SLOT_SHIFT |
+ pci_get_function(dev) << PCIE_FUNC_SHIFT;
+
+ error = bcm2835_mbox_notify_xhci_reset(dev_addr);
+ if (error)
+ device_printf(dev,
+ "warning: xhci firmware install failed (error %d).\n",
+ error);
+
+ DELAY(1000);
+ bcm_xhci_check_firmware(dev, true);
+
+ return;
+}
+
+static int
+bcm_xhci_attach(device_t dev)
+{
+ struct xhci_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ bcm_xhci_install_xhci_firmware(dev);
+
+ error = xhci_pci_attach(dev);
+ if (error)
+ return (error);
+
+ /* 32 bit DMA is a limitation of the PCI-e controller, not the VL805. */
+ sc->sc_bus.dma_bits = 32;
+ if (bootverbose)
+ device_printf(dev, "note: switched to 32-bit DMA.\n");
+
+ return (0);
+}
+
+/*
+ * Device method table.
+ */
+static device_method_t bcm_xhci_methods[] = {
+ /* Device interface. */
+ DEVMETHOD(device_probe, bcm_xhci_probe),
+ DEVMETHOD(device_attach, bcm_xhci_attach),
+};
+
+DEFINE_CLASS_1(bcm_xhci, bcm_xhci_driver, bcm_xhci_methods,
+ sizeof(struct xhci_softc), xhci_pci_driver);
+
+static devclass_t xhci_devclass;
+DRIVER_MODULE(bcm_xhci, pci, bcm_xhci_driver, xhci_devclass, 0, 0); MODULE_DEPEND(bcm_xhci, usb, 1, 1, 1);
diff --git a/sys/arm/broadcom/bcm2835/bcm283x_dwc_fdt.c b/sys/arm/broadcom/bcm2835/bcm283x_dwc_fdt.c
new file mode 100644
index 000000000000..2429d09aa164
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/bcm283x_dwc_fdt.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2015 Andrew Turner.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/callout.h>
+#include <sys/condvar.h>
+#include <sys/module.h>
+
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+
+#include <dev/usb/controller/dwc_otg.h>
+#include <dev/usb/controller/dwc_otg_fdt.h>
+
+#include <arm/broadcom/bcm2835/bcm2835_mbox_prop.h>
+
+static struct ofw_compat_data compat_data[] = {
+ {"broadcom,bcm2835-usb", 1},
+ {"brcm,bcm2835-usb", 1},
+ {"brcm,bcm2708-usb", 1},
+ {NULL, 0}
+};
+
+static device_probe_t bcm283x_dwc_otg_probe;
+static device_attach_t bcm283x_dwc_otg_attach;
+
+static int
+bcm283x_dwc_otg_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "DWC OTG 2.0 integrated USB controller (bcm283x)");
+
+ return (BUS_PROBE_VENDOR);
+}
+
+static int
+bcm283x_dwc_otg_attach(device_t dev)
+{
+ int err;
+
+ err = bcm2835_mbox_set_power_state(BCM2835_MBOX_POWER_ID_USB_HCD, TRUE);
+ if (err)
+ device_printf(dev, "failed to set power state, err=%d\n", err);
+
+ return (dwc_otg_attach(dev));
+}
+
+static device_method_t bcm283x_dwc_otg_methods[] = {
+ /* bus interface */
+ DEVMETHOD(device_probe, bcm283x_dwc_otg_probe),
+ DEVMETHOD(device_attach, bcm283x_dwc_otg_attach),
+
+ DEVMETHOD_END
+};
+
+static devclass_t bcm283x_dwc_otg_devclass;
+
+DEFINE_CLASS_1(bcm283x_dwcotg, bcm283x_dwc_otg_driver, bcm283x_dwc_otg_methods,
+ sizeof(struct dwc_otg_fdt_softc), dwc_otg_driver);
+DRIVER_MODULE(bcm283x_dwcotg, simplebus, bcm283x_dwc_otg_driver,
+ bcm283x_dwc_otg_devclass, 0, 0);
+MODULE_DEPEND(bcm283x_dwcotg, usb, 1, 1, 1);
diff --git a/sys/arm/broadcom/bcm2835/files.bcm2835 b/sys/arm/broadcom/bcm2835/files.bcm2835
new file mode 100644
index 000000000000..729379066d4c
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/files.bcm2835
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+arm/broadcom/bcm2835/bcm2835_systimer.c standard
diff --git a/sys/arm/broadcom/bcm2835/files.bcm2836 b/sys/arm/broadcom/bcm2835/files.bcm2836
new file mode 100644
index 000000000000..43b6b6c25c2d
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/files.bcm2836
@@ -0,0 +1,4 @@
+# $FreeBSD$
+
+arm/broadcom/bcm2835/bcm2836.c standard
+arm/broadcom/bcm2835/bcm2836_mp.c optional smp
diff --git a/sys/arm/broadcom/bcm2835/files.bcm283x b/sys/arm/broadcom/bcm2835/files.bcm283x
new file mode 100644
index 000000000000..0af397566c17
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/files.bcm283x
@@ -0,0 +1,48 @@
+# $FreeBSD$
+
+arm/broadcom/bcm2835/bcm2835_bsc.c optional bcm2835_bsc
+arm/broadcom/bcm2835/bcm2835_cpufreq.c standard
+arm/broadcom/bcm2835/bcm2835_dma.c standard
+arm/broadcom/bcm2835/bcm2835_fb.c optional sc
+arm/broadcom/bcm2835/bcm2835_fbd.c optional vt
+arm/broadcom/bcm2835/bcm2835_firmware.c standard
+arm/broadcom/bcm2835/bcm2835_ft5406.c optional evdev bcm2835_ft5406
+arm/broadcom/bcm2835/bcm2835_gpio.c optional gpio
+arm/broadcom/bcm2835/bcm2835_intr.c standard
+arm/broadcom/bcm2835/bcm2835_machdep.c optional platform
+arm/broadcom/bcm2835/bcm2835_mbox.c standard
+arm/broadcom/bcm2835/bcm2835_rng.c optional random
+arm/broadcom/bcm2835/bcm2835_sdhci.c optional sdhci
+arm/broadcom/bcm2835/bcm2835_sdhost.c optional sdhci
+arm/broadcom/bcm2835/bcm2835_spi.c optional bcm2835_spi
+arm/broadcom/bcm2835/bcm2835_vcbus.c standard
+arm/broadcom/bcm2835/bcm2835_vcio.c standard
+arm/broadcom/bcm2835/bcm2835_wdog.c standard
+arm/broadcom/bcm2835/bcm2838_pci.c optional pci
+arm/broadcom/bcm2835/bcm2838_xhci.c optional xhci
+arm/broadcom/bcm2835/bcm283x_dwc_fdt.c optional dwcotg fdt
+
+dev/mbox/mbox_if.m standard
+
+arm/broadcom/bcm2835/bcm2835_audio.c optional sound vchiq \
+ compile-with "${NORMAL_C} -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 -I$S/contrib/vchiq"
+
+# VideoCore driver
+contrib/vchiq/interface/compat/vchi_bsd.c optional vchiq \
+ compile-with "${NORMAL_C} -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 -I$S/contrib/vchiq"
+contrib/vchiq/interface/vchiq_arm/vchiq_2835_arm.c optional vchiq \
+ compile-with "${NORMAL_C} -Wno-unused -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 -I$S/contrib/vchiq"
+contrib/vchiq/interface/vchiq_arm/vchiq_arm.c optional vchiq \
+ compile-with "${NORMAL_C} -Wno-unused -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 -I$S/contrib/vchiq"
+contrib/vchiq/interface/vchiq_arm/vchiq_connected.c optional vchiq \
+ compile-with "${NORMAL_C} -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 -I$S/contrib/vchiq"
+contrib/vchiq/interface/vchiq_arm/vchiq_core.c optional vchiq \
+ compile-with "${NORMAL_C} -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 -I$S/contrib/vchiq"
+contrib/vchiq/interface/vchiq_arm/vchiq_kern_lib.c optional vchiq \
+ compile-with "${NORMAL_C} -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 -I$S/contrib/vchiq"
+contrib/vchiq/interface/vchiq_arm/vchiq_kmod.c optional vchiq \
+ compile-with "${NORMAL_C} -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 -I$S/contrib/vchiq"
+contrib/vchiq/interface/vchiq_arm/vchiq_shim.c optional vchiq \
+ compile-with "${NORMAL_C} -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 -I$S/contrib/vchiq"
+contrib/vchiq/interface/vchiq_arm/vchiq_util.c optional vchiq \
+ compile-with "${NORMAL_C} -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 -I$S/contrib/vchiq"
diff --git a/sys/arm/broadcom/bcm2835/raspberrypi_gpio.c b/sys/arm/broadcom/bcm2835/raspberrypi_gpio.c
new file mode 100644
index 000000000000..d79d39ff1497
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/raspberrypi_gpio.c
@@ -0,0 +1,457 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@FreeBSD.org>
+ * Copyright (c) 2012-2015 Luiz Otavio O Souza <loos@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/gpio.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/sx.h>
+#include <sys/proc.h>
+
+#include <dev/gpio/gpiobusvar.h>
+#include <dev/ofw/ofw_bus.h>
+
+#include <arm/broadcom/bcm2835/bcm2835_firmware.h>
+
+#include "gpio_if.h"
+
+#define RPI_FW_GPIO_PINS 8
+#define RPI_FW_GPIO_BASE 128
+#define RPI_FW_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)
+
+struct rpi_fw_gpio_softc {
+ device_t sc_busdev;
+ device_t sc_firmware;
+ struct sx sc_sx;
+ struct gpio_pin sc_gpio_pins[RPI_FW_GPIO_PINS];
+ uint8_t sc_gpio_state;
+};
+
+#define RPI_FW_GPIO_LOCK(_sc) sx_xlock(&(_sc)->sc_sx)
+#define RPI_FW_GPIO_UNLOCK(_sc) sx_xunlock(&(_sc)->sc_sx)
+
+static struct ofw_compat_data compat_data[] = {
+ {"raspberrypi,firmware-gpio", 1},
+ {NULL, 0}
+};
+
+static int
+rpi_fw_gpio_pin_configure(struct rpi_fw_gpio_softc *sc, struct gpio_pin *pin,
+ unsigned int flags)
+{
+ union msg_get_gpio_config old_cfg;
+ union msg_set_gpio_config new_cfg;
+ int rv;
+
+ bzero(&old_cfg, sizeof(old_cfg));
+ bzero(&new_cfg, sizeof(new_cfg));
+ old_cfg.req.gpio = RPI_FW_GPIO_BASE + pin->gp_pin;
+
+ RPI_FW_GPIO_LOCK(sc);
+ rv = bcm2835_firmware_property(sc->sc_firmware,
+ BCM2835_FIRMWARE_TAG_GET_GPIO_CONFIG, &old_cfg, sizeof(old_cfg));
+ if (rv == 0 && old_cfg.resp.gpio != 0)
+ rv = EIO;
+ if (rv != 0)
+ goto fail;
+
+ new_cfg.req.gpio = RPI_FW_GPIO_BASE + pin->gp_pin;
+ if (flags & GPIO_PIN_INPUT) {
+ new_cfg.req.dir = BCM2835_FIRMWARE_GPIO_IN;
+ new_cfg.req.state = 0;
+ pin->gp_flags = GPIO_PIN_INPUT;
+ } else if (flags & GPIO_PIN_OUTPUT) {
+ new_cfg.req.dir = BCM2835_FIRMWARE_GPIO_OUT;
+ if (flags & (GPIO_PIN_PRESET_HIGH | GPIO_PIN_PRESET_LOW)) {
+ if (flags & GPIO_PIN_PRESET_HIGH) {
+ new_cfg.req.state = 1;
+ sc->sc_gpio_state |= (1 << pin->gp_pin);
+ } else {
+ new_cfg.req.state = 0;
+ sc->sc_gpio_state &= ~(1 << pin->gp_pin);
+ }
+ } else {
+ if ((sc->sc_gpio_state & (1 << pin->gp_pin)) != 0) {
+ new_cfg.req.state = 1;
+ } else {
+ new_cfg.req.state = 0;
+ }
+ }
+ pin->gp_flags = GPIO_PIN_OUTPUT;
+ } else {
+ new_cfg.req.dir = old_cfg.resp.dir;
+ /* Use the old state to decide high/low */
+ if ((sc->sc_gpio_state & (1 << pin->gp_pin)) != 0)
+ new_cfg.req.state = 1;
+ else
+ new_cfg.req.state = 0;
+ }
+ new_cfg.req.pol = old_cfg.resp.pol;
+ new_cfg.req.term_en = 0;
+ new_cfg.req.term_pull_up = 0;
+
+ rv = bcm2835_firmware_property(sc->sc_firmware,
+ BCM2835_FIRMWARE_TAG_SET_GPIO_CONFIG, &new_cfg, sizeof(new_cfg));
+
+fail:
+ RPI_FW_GPIO_UNLOCK(sc);
+
+ return (rv);
+}
+
+static device_t
+rpi_fw_gpio_get_bus(device_t dev)
+{
+ struct rpi_fw_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ return (sc->sc_busdev);
+}
+
+static int
+rpi_fw_gpio_pin_max(device_t dev, int *maxpin)
+{
+
+ *maxpin = RPI_FW_GPIO_PINS - 1;
+ return (0);
+}
+
+static int
+rpi_fw_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
+{
+ struct rpi_fw_gpio_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+ for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
+ if (sc->sc_gpio_pins[i].gp_pin == pin)
+ break;
+ }
+
+ if (i >= RPI_FW_GPIO_PINS)
+ return (EINVAL);
+
+ *caps = RPI_FW_GPIO_DEFAULT_CAPS;
+ return (0);
+}
+
+static int
+rpi_fw_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
+{
+ struct rpi_fw_gpio_softc *sc = device_get_softc(dev);
+ int i;
+
+ for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
+ if (sc->sc_gpio_pins[i].gp_pin == pin)
+ break;
+ }
+
+ if (i >= RPI_FW_GPIO_PINS)
+ return (EINVAL);
+
+ RPI_FW_GPIO_LOCK(sc);
+ *flags = sc->sc_gpio_pins[i].gp_flags;
+ RPI_FW_GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+rpi_fw_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
+{
+ struct rpi_fw_gpio_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+ for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
+ if (sc->sc_gpio_pins[i].gp_pin == pin)
+ break;
+ }
+
+ if (i >= RPI_FW_GPIO_PINS)
+ return (EINVAL);
+
+ RPI_FW_GPIO_LOCK(sc);
+ memcpy(name, sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME);
+ RPI_FW_GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+rpi_fw_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
+{
+ struct rpi_fw_gpio_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+ for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
+ if (sc->sc_gpio_pins[i].gp_pin == pin)
+ break;
+ }
+
+ if (i >= RPI_FW_GPIO_PINS)
+ return (EINVAL);
+
+ return (rpi_fw_gpio_pin_configure(sc, &sc->sc_gpio_pins[i], flags));
+}
+
+static int
+rpi_fw_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
+{
+ struct rpi_fw_gpio_softc *sc;
+ union msg_set_gpio_state state;
+ int i, rv;
+
+ sc = device_get_softc(dev);
+ for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
+ if (sc->sc_gpio_pins[i].gp_pin == pin)
+ break;
+ }
+ if (i >= RPI_FW_GPIO_PINS)
+ return (EINVAL);
+
+ state.req.gpio = RPI_FW_GPIO_BASE + pin;
+ state.req.state = value;
+
+ RPI_FW_GPIO_LOCK(sc);
+ rv = bcm2835_firmware_property(sc->sc_firmware,
+ BCM2835_FIRMWARE_TAG_SET_GPIO_STATE, &state, sizeof(state));
+ /* The firmware sets gpio to 0 on success */
+ if (rv == 0 && state.resp.gpio != 0)
+ rv = EINVAL;
+ if (rv == 0) {
+ sc->sc_gpio_pins[i].gp_flags &= ~(GPIO_PIN_PRESET_HIGH |
+ GPIO_PIN_PRESET_LOW);
+ if (value)
+ sc->sc_gpio_state |= (1 << i);
+ else
+ sc->sc_gpio_state &= ~(1 << i);
+ }
+ RPI_FW_GPIO_UNLOCK(sc);
+
+ return (rv);
+}
+
+static int
+rpi_fw_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
+{
+ struct rpi_fw_gpio_softc *sc;
+ union msg_get_gpio_state state;
+ int i, rv;
+
+ sc = device_get_softc(dev);
+ for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
+ if (sc->sc_gpio_pins[i].gp_pin == pin)
+ break;
+ }
+ if (i >= RPI_FW_GPIO_PINS)
+ return (EINVAL);
+
+ bzero(&state, sizeof(state));
+ state.req.gpio = RPI_FW_GPIO_BASE + pin;
+
+ RPI_FW_GPIO_LOCK(sc);
+ rv = bcm2835_firmware_property(sc->sc_firmware,
+ BCM2835_FIRMWARE_TAG_GET_GPIO_STATE, &state, sizeof(state));
+ RPI_FW_GPIO_UNLOCK(sc);
+
+ /* The firmware sets gpio to 0 on success */
+ if (rv == 0 && state.resp.gpio != 0)
+ rv = EINVAL;
+ if (rv == 0)
+ *val = !state.resp.state;
+
+ return (rv);
+}
+
+static int
+rpi_fw_gpio_pin_toggle(device_t dev, uint32_t pin)
+{
+ struct rpi_fw_gpio_softc *sc;
+ union msg_get_gpio_state old_state;
+ union msg_set_gpio_state new_state;
+ int i, rv;
+
+ sc = device_get_softc(dev);
+ for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
+ if (sc->sc_gpio_pins[i].gp_pin == pin)
+ break;
+ }
+ if (i >= RPI_FW_GPIO_PINS)
+ return (EINVAL);
+
+ bzero(&old_state, sizeof(old_state));
+ bzero(&new_state, sizeof(new_state));
+
+ old_state.req.gpio = RPI_FW_GPIO_BASE + pin;
+ new_state.req.gpio = RPI_FW_GPIO_BASE + pin;
+
+ RPI_FW_GPIO_LOCK(sc);
+ rv = bcm2835_firmware_property(sc->sc_firmware,
+ BCM2835_FIRMWARE_TAG_GET_GPIO_STATE, &old_state, sizeof(old_state));
+ /* The firmware sets gpio to 0 on success */
+ if (rv == 0 && old_state.resp.gpio == 0) {
+ /* Set the new state to invert the GPIO */
+ new_state.req.state = !old_state.resp.state;
+ rv = bcm2835_firmware_property(sc->sc_firmware,
+ BCM2835_FIRMWARE_TAG_SET_GPIO_STATE, &new_state,
+ sizeof(new_state));
+ }
+ if (rv == 0 && (old_state.resp.gpio != 0 || new_state.resp.gpio != 0))
+ rv = EINVAL;
+ RPI_FW_GPIO_UNLOCK(sc);
+
+ return (rv);
+}
+
+static int
+rpi_fw_gpio_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Raspberry Pi Firmware GPIO controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+rpi_fw_gpio_attach(device_t dev)
+{
+ union msg_get_gpio_config cfg;
+ struct rpi_fw_gpio_softc *sc;
+ char *names;
+ phandle_t gpio;
+ int i, nelems, elm_pos, rv;
+
+ sc = device_get_softc(dev);
+ sc->sc_firmware = device_get_parent(dev);
+ sx_init(&sc->sc_sx, "Raspberry Pi firmware gpio");
+ /* Find our node. */
+ gpio = ofw_bus_get_node(dev);
+ if (!OF_hasprop(gpio, "gpio-controller"))
+ /* This is not a GPIO controller. */
+ goto fail;
+
+ nelems = OF_getprop_alloc(gpio, "gpio-line-names", (void **)&names);
+ if (nelems <= 0)
+ names = NULL;
+ elm_pos = 0;
+ for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
+ /* Set the current pin name */
+ if (names != NULL && elm_pos < nelems &&
+ names[elm_pos] != '\0') {
+ snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME,
+ "%s", names + elm_pos);
+ /* Find the next pin name */
+ elm_pos += strlen(names + elm_pos) + 1;
+ } else {
+ snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME,
+ "pin %d", i);
+ }
+
+ sc->sc_gpio_pins[i].gp_pin = i;
+ sc->sc_gpio_pins[i].gp_caps = RPI_FW_GPIO_DEFAULT_CAPS;
+
+ bzero(&cfg, sizeof(cfg));
+ cfg.req.gpio = RPI_FW_GPIO_BASE + i;
+ rv = bcm2835_firmware_property(sc->sc_firmware,
+ BCM2835_FIRMWARE_TAG_GET_GPIO_CONFIG, &cfg, sizeof(cfg));
+ if (rv == 0 && cfg.resp.gpio == 0) {
+ if (cfg.resp.dir == BCM2835_FIRMWARE_GPIO_IN)
+ sc->sc_gpio_pins[i].gp_flags = GPIO_PIN_INPUT;
+ else
+ sc->sc_gpio_pins[i].gp_flags = GPIO_PIN_OUTPUT;
+ } else {
+ sc->sc_gpio_pins[i].gp_flags = GPIO_PIN_INPUT;
+ }
+ }
+ free(names, M_OFWPROP);
+ sc->sc_busdev = gpiobus_attach_bus(dev);
+ if (sc->sc_busdev == NULL)
+ goto fail;
+
+ return (0);
+
+fail:
+ sx_destroy(&sc->sc_sx);
+
+ return (ENXIO);
+}
+
+static int
+rpi_fw_gpio_detach(device_t dev)
+{
+
+ return (EBUSY);
+}
+
+static device_method_t rpi_fw_gpio_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, rpi_fw_gpio_probe),
+ DEVMETHOD(device_attach, rpi_fw_gpio_attach),
+ DEVMETHOD(device_detach, rpi_fw_gpio_detach),
+
+ /* GPIO protocol */
+ DEVMETHOD(gpio_get_bus, rpi_fw_gpio_get_bus),
+ DEVMETHOD(gpio_pin_max, rpi_fw_gpio_pin_max),
+ DEVMETHOD(gpio_pin_getname, rpi_fw_gpio_pin_getname),
+ DEVMETHOD(gpio_pin_getflags, rpi_fw_gpio_pin_getflags),
+ DEVMETHOD(gpio_pin_getcaps, rpi_fw_gpio_pin_getcaps),
+ DEVMETHOD(gpio_pin_setflags, rpi_fw_gpio_pin_setflags),
+ DEVMETHOD(gpio_pin_get, rpi_fw_gpio_pin_get),
+ DEVMETHOD(gpio_pin_set, rpi_fw_gpio_pin_set),
+ DEVMETHOD(gpio_pin_toggle, rpi_fw_gpio_pin_toggle),
+
+ DEVMETHOD_END
+};
+
+static devclass_t rpi_fw_gpio_devclass;
+
+static driver_t rpi_fw_gpio_driver = {
+ "gpio",
+ rpi_fw_gpio_methods,
+ sizeof(struct rpi_fw_gpio_softc),
+};
+
+EARLY_DRIVER_MODULE(rpi_fw_gpio, bcm2835_firmware, rpi_fw_gpio_driver,
+ rpi_fw_gpio_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);
diff --git a/sys/arm/broadcom/bcm2835/std.bcm2835 b/sys/arm/broadcom/bcm2835/std.bcm2835
new file mode 100644
index 000000000000..08e9d763bc8f
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/std.bcm2835
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+machine arm armv6
+cpu CPU_ARM1176
+makeoptions CONF_CFLAGS="-mcpu=arm1176jzf-s"
+options SOC_BCM2835
+
+files "../broadcom/bcm2835/files.bcm2835"
+files "../broadcom/bcm2835/files.bcm283x"
+
diff --git a/sys/arm/broadcom/bcm2835/std.bcm2836 b/sys/arm/broadcom/bcm2835/std.bcm2836
new file mode 100644
index 000000000000..ffcc562f8436
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/std.bcm2836
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+machine arm armv7
+cpu CPU_CORTEXA
+makeoptions CONF_CFLAGS="-march=armv7a"
+options SOC_BCM2836
+
+files "../broadcom/bcm2835/files.bcm2836"
+files "../broadcom/bcm2835/files.bcm283x"
+
diff --git a/sys/arm/broadcom/bcm2835/std.rpi b/sys/arm/broadcom/bcm2835/std.rpi
new file mode 100644
index 000000000000..48593c117c59
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/std.rpi
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+options LINUX_BOOT_ABI
diff --git a/sys/arm/broadcom/bcm2835/vc_vchi_audioserv_defs.h b/sys/arm/broadcom/bcm2835/vc_vchi_audioserv_defs.h
new file mode 100644
index 000000000000..143c54385916
--- /dev/null
+++ b/sys/arm/broadcom/bcm2835/vc_vchi_audioserv_defs.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2012, Broadcom Europe Ltd
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _VC_AUDIO_DEFS_H_
+#define _VC_AUDIO_DEFS_H_
+
+#define VC_AUDIOSERV_MIN_VER 1
+#define VC_AUDIOSERV_VER 2
+
+/* FourCC code used for VCHI connection */
+#define VC_AUDIO_SERVER_NAME MAKE_FOURCC("AUDS")
+
+/* Maximum message length */
+#define VC_AUDIO_MAX_MSG_LEN (sizeof( VC_AUDIO_MSG_T ))
+
+/*
+ * List of screens that are currently supported
+ * All message types supported for HOST->VC direction
+ */
+typedef enum
+{
+ VC_AUDIO_MSG_TYPE_RESULT, /* Generic result */
+ VC_AUDIO_MSG_TYPE_COMPLETE, /* playback of samples complete */
+ VC_AUDIO_MSG_TYPE_CONFIG, /* Configure */
+ VC_AUDIO_MSG_TYPE_CONTROL, /* control */
+ VC_AUDIO_MSG_TYPE_OPEN, /* open */
+ VC_AUDIO_MSG_TYPE_CLOSE, /* close/shutdown */
+ VC_AUDIO_MSG_TYPE_START, /* start output (i.e. resume) */
+ VC_AUDIO_MSG_TYPE_STOP, /* stop output (i.e. pause) */
+ VC_AUDIO_MSG_TYPE_WRITE, /* write samples */
+ VC_AUDIO_MSG_TYPE_MAX
+
+} VC_AUDIO_MSG_TYPE;
+
+static const char *vc_audio_msg_type_names[] = {
+ "VC_AUDIO_MSG_TYPE_RESULT",
+ "VC_AUDIO_MSG_TYPE_COMPLETE",
+ "VC_AUDIO_MSG_TYPE_CONFIG",
+ "VC_AUDIO_MSG_TYPE_CONTROL",
+ "VC_AUDIO_MSG_TYPE_OPEN",
+ "VC_AUDIO_MSG_TYPE_CLOSE",
+ "VC_AUDIO_MSG_TYPE_START",
+ "VC_AUDIO_MSG_TYPE_STOP",
+ "VC_AUDIO_MSG_TYPE_WRITE",
+ "VC_AUDIO_MSG_TYPE_MAX"
+};
+
+/* configure the audio */
+typedef struct
+{
+ uint32_t channels;
+ uint32_t samplerate;
+ uint32_t bps;
+
+} VC_AUDIO_CONFIG_T;
+
+typedef struct
+{
+ uint32_t volume;
+ uint32_t dest;
+
+} VC_AUDIO_CONTROL_T;
+
+typedef struct
+{
+ uint32_t dummy;
+
+} VC_AUDIO_OPEN_T;
+
+typedef struct
+{
+ uint32_t dummy;
+
+} VC_AUDIO_CLOSE_T;
+
+typedef struct
+{
+ uint32_t dummy;
+
+} VC_AUDIO_START_T;
+
+typedef struct
+{
+ uint32_t draining;
+
+} VC_AUDIO_STOP_T;
+
+typedef struct
+{
+ uint32_t count; /* in bytes */
+ void *callback;
+ void *cookie;
+ uint16_t silence;
+ uint16_t max_packet;
+} VC_AUDIO_WRITE_T;
+
+/* Generic result for a request (VC->HOST) */
+typedef struct
+{
+ int32_t success; /* Success value */
+
+} VC_AUDIO_RESULT_T;
+
+/* Generic result for a request (VC->HOST) */
+typedef struct
+{
+ int32_t count; /* Success value */
+ void *callback;
+ void *cookie;
+} VC_AUDIO_COMPLETE_T;
+
+/* Message header for all messages in HOST->VC direction */
+typedef struct
+{
+ int32_t type; /* Message type (VC_AUDIO_MSG_TYPE) */
+ union
+ {
+ VC_AUDIO_CONFIG_T config;
+ VC_AUDIO_CONTROL_T control;
+ VC_AUDIO_OPEN_T open;
+ VC_AUDIO_CLOSE_T close;
+ VC_AUDIO_START_T start;
+ VC_AUDIO_STOP_T stop;
+ VC_AUDIO_WRITE_T write;
+ VC_AUDIO_RESULT_T result;
+ VC_AUDIO_COMPLETE_T complete;
+ } u;
+} VC_AUDIO_MSG_T;
+
+#endif /* _VC_AUDIO_DEFS_H_ */
diff --git a/sys/arm/cloudabi32/cloudabi32_sysvec.c b/sys/arm/cloudabi32/cloudabi32_sysvec.c
new file mode 100644
index 000000000000..c6dfe0661b7c
--- /dev/null
+++ b/sys/arm/cloudabi32/cloudabi32_sysvec.c
@@ -0,0 +1,195 @@
+/*-
+ * Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/imgact.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/sysent.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/frame.h>
+#include <machine/pcb.h>
+#include <machine/vmparam.h>
+
+#include <compat/cloudabi/cloudabi_util.h>
+
+#include <compat/cloudabi32/cloudabi32_syscall.h>
+#include <compat/cloudabi32/cloudabi32_util.h>
+
+extern const char *cloudabi32_syscallnames[];
+extern struct sysent cloudabi32_sysent[];
+
+static void
+cloudabi32_proc_setregs(struct thread *td, struct image_params *imgp,
+ uintptr_t stack)
+{
+ struct trapframe *regs;
+
+ exec_setregs(td, imgp, stack);
+
+ /*
+ * The stack now contains a pointer to the TCB and the auxiliary
+ * vector. Let r0 point to the auxiliary vector, and set
+ * tpidrurw to the TCB.
+ */
+ regs = td->td_frame;
+ regs->tf_r0 =
+ stack + roundup(sizeof(cloudabi32_tcb_t), sizeof(register_t));
+ (void)cpu_set_user_tls(td, TO_PTR(stack));
+}
+
+static int
+cloudabi32_fetch_syscall_args(struct thread *td)
+{
+ struct trapframe *frame;
+ struct syscall_args *sa;
+ int error;
+
+ frame = td->td_frame;
+ sa = &td->td_sa;
+
+ /* Obtain system call number. */
+ sa->code = frame->tf_r12;
+ if (sa->code >= CLOUDABI32_SYS_MAXSYSCALL)
+ return (ENOSYS);
+ sa->callp = &cloudabi32_sysent[sa->code];
+
+ /* Fetch system call arguments from registers and the stack. */
+ sa->args[0] = frame->tf_r0;
+ sa->args[1] = frame->tf_r1;
+ sa->args[2] = frame->tf_r2;
+ sa->args[3] = frame->tf_r3;
+ if (sa->callp->sy_narg > 4) {
+ error = copyin((void *)td->td_frame->tf_usr_sp, &sa->args[4],
+ (sa->callp->sy_narg - 4) * sizeof(register_t));
+ if (error != 0)
+ return (error);
+ }
+
+ /* Default system call return values. */
+ td->td_retval[0] = 0;
+ td->td_retval[1] = frame->tf_r1;
+ return (0);
+}
+
+static void
+cloudabi32_set_syscall_retval(struct thread *td, int error)
+{
+ struct trapframe *frame = td->td_frame;
+
+ switch (error) {
+ case 0:
+ /* System call succeeded. */
+ frame->tf_r0 = td->td_retval[0];
+ frame->tf_r1 = td->td_retval[1];
+ frame->tf_spsr &= ~PSR_C;
+ break;
+ case ERESTART:
+ /* Restart system call. */
+ frame->tf_pc -= 4;
+ break;
+ case EJUSTRETURN:
+ break;
+ default:
+ /* System call returned an error. */
+ frame->tf_r0 = cloudabi_convert_errno(error);
+ frame->tf_spsr |= PSR_C;
+ break;
+ }
+}
+
+static void
+cloudabi32_schedtail(struct thread *td)
+{
+ struct trapframe *frame = td->td_frame;
+
+ /*
+ * Initial register values for processes returning from fork.
+ * Make sure that we only set these values when forking, not
+ * when creating a new thread.
+ */
+ if ((td->td_pflags & TDP_FORKING) != 0) {
+ frame->tf_r0 = CLOUDABI_PROCESS_CHILD;
+ frame->tf_r1 = td->td_tid;
+ }
+}
+
+int
+cloudabi32_thread_setregs(struct thread *td,
+ const cloudabi32_threadattr_t *attr, uint32_t tcb)
+{
+ struct trapframe *frame;
+ stack_t stack;
+
+ /* Perform standard register initialization. */
+ stack.ss_sp = TO_PTR(attr->stack);
+ stack.ss_size = attr->stack_len;
+ cpu_set_upcall(td, TO_PTR(attr->entry_point), NULL, &stack);
+
+ /*
+ * Pass in the thread ID of the new thread and the argument
+ * pointer provided by the parent thread in as arguments to the
+ * entry point.
+ */
+ frame = td->td_frame;
+ frame->tf_r0 = td->td_tid;
+ frame->tf_r1 = attr->argument;
+
+ /* Set up TLS. */
+ return (cpu_set_user_tls(td, TO_PTR(tcb)));
+}
+
+static struct sysentvec cloudabi32_elf_sysvec = {
+ .sv_size = CLOUDABI32_SYS_MAXSYSCALL,
+ .sv_table = cloudabi32_sysent,
+ .sv_fixup = cloudabi32_fixup,
+ .sv_name = "CloudABI ELF32",
+ .sv_coredump = elf32_coredump,
+ .sv_minuser = VM_MIN_ADDRESS,
+ .sv_maxuser = VM_MAXUSER_ADDRESS,
+ .sv_stackprot = VM_PROT_READ | VM_PROT_WRITE,
+ .sv_copyout_strings = cloudabi32_copyout_strings,
+ .sv_setregs = cloudabi32_proc_setregs,
+ .sv_flags = SV_ABI_CLOUDABI | SV_CAPSICUM | SV_ILP32,
+ .sv_set_syscall_retval = cloudabi32_set_syscall_retval,
+ .sv_fetch_syscall_args = cloudabi32_fetch_syscall_args,
+ .sv_syscallnames = cloudabi32_syscallnames,
+ .sv_schedtail = cloudabi32_schedtail,
+};
+
+INIT_SYSENTVEC(elf_sysvec, &cloudabi32_elf_sysvec);
+
+Elf32_Brandinfo cloudabi32_brand = {
+ .brand = ELFOSABI_CLOUDABI,
+ .machine = EM_ARM,
+ .sysvec = &cloudabi32_elf_sysvec,
+ .flags = BI_BRAND_ONLY_STATIC,
+};
diff --git a/sys/arm/conf/ALPINE b/sys/arm/conf/ALPINE
new file mode 100644
index 000000000000..fdfb591f8614
--- /dev/null
+++ b/sys/arm/conf/ALPINE
@@ -0,0 +1,82 @@
+# Kernel configuration for Alpine Board.
+#
+# For more information on this file, please read the config(5) manual page,
+# and/or the handbook section on Kernel Configuration Files:
+#
+# https://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html
+#
+# The handbook is also available locally in /usr/share/doc/handbook
+# if you've installed the doc distribution, otherwise always see the
+# FreeBSD World Wide Web server (https://www.FreeBSD.org/) for the
+# latest information.
+#
+# An exhaustive list of options and more detailed explanations of the
+# device lines is also present in the ../../conf/NOTES and NOTES files.
+# If you are in doubt as to the purpose or necessity of a line, check first
+# in NOTES.
+#
+# $FreeBSD$
+
+ident ALPINE
+
+include "std.armv7"
+include "../annapurna/alpine/std.alpine"
+
+makeoptions MODULES_OVERRIDE=""
+
+options SCHED_4BSD # 4BSD scheduler
+options SMP # Enable multiple cores
+options PLATFORM
+
+# Interrupt controller
+device gic
+
+# Annapurna Alpine drivers
+device al_ccu # Alpine Cache Coherency Unit
+device al_nb_service # Alpine North Bridge Service
+device al_iofic # I/O Fabric Interrupt Controller
+device al_serdes # Serializer/Deserializer
+device al_udma # Universal DMA
+
+# Pseudo devices
+device loop
+device pty
+device md
+device gpio
+
+# ATA controllers
+device ahci # AHCI-compatible SATA controllers
+device ata # Legacy ATA/SATA controllers
+
+# ATA/SCSI peripherals
+device scbus # SCSI bus (required for ATA/SCSI)
+device ch # SCSI media changers
+device da # Direct Access (disks)
+device sa # Sequential Access (tape etc)
+device cd # CD
+device pass # Passthrough device (direct ATA/SCSI access)
+device ses # Enclosure Services (SES and SAF-TE)
+#device ctl # CAM Target Layer
+
+# Serial ports
+device uart
+
+# PCI/PCIE
+device pci
+device pci_host_generic
+device al_pci # Annapurna Alpine PCI-E
+
+# Ethernet
+device ether
+device mii
+device bpf
+device al_eth # Annapurna Alpine Ethernet NIC
+options DEVICE_POLLING
+
+# USB ethernet support, requires miibus
+device miibus
+
+#FDT
+options FDT
+options FDT_DTB_STATIC
+makeoptions FDT_DTS_FILE=annapurna-alpine.dts
diff --git a/sys/arm/conf/APALIS-IMX6 b/sys/arm/conf/APALIS-IMX6
new file mode 100644
index 000000000000..9b5886216ada
--- /dev/null
+++ b/sys/arm/conf/APALIS-IMX6
@@ -0,0 +1,31 @@
+# Kernel configuration for Toradex Apalis i.MX6
+#
+# For more information on this file, please read the config(5) manual page,
+# and/or the handbook section on Kernel Configuration Files:
+#
+# https://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html
+#
+# The handbook is also available locally in /usr/share/doc/handbook
+# if you've installed the doc distribution, otherwise always see the
+# FreeBSD World Wide Web server (https://www.FreeBSD.org/) for the
+# latest information.
+#
+# An exhaustive list of options and more detailed explanations of the
+# device lines is also present in the ../../conf/NOTES and NOTES files.
+# If you are in doubt as to the purpose or necessity of a line, check first
+# in NOTES.
+#
+# $FreeBSD$
+
+#NO_UNIVERSE
+
+include "IMX6"
+ident APALIS-IMX6
+
+makeoptions MODULES_OVERRIDE=""
+makeoptions WITHOUT_MODULES="ahc"
+
+# Flattened Device Tree
+options FDT
+options FDT_DTB_STATIC
+makeoptions FDT_DTS_FILE=apalis-imx6.dts
diff --git a/sys/arm/conf/ARMADA38X b/sys/arm/conf/ARMADA38X
new file mode 100644
index 000000000000..71a2915d15b6
--- /dev/null
+++ b/sys/arm/conf/ARMADA38X
@@ -0,0 +1,104 @@
+#
+# Kernel configuration for Marvell Armada38x
+#
+# $FreeBSD$
+#
+
+include "../mv/armada38x/std.armada38x"
+include "std.armv7"
+
+ident ARMADA38X
+
+options SOC_MV_ARMADA38X
+
+makeoptions MODULES_EXTRA="dtb/mv"
+
+#options MD_ROOT
+#makeoptions MFS_IMAGE=/path/to/miniroot
+#options ROOTDEVNAME=\"ufs:md0\"
+options ROOTDEVNAME=\"/dev/da0s1a\"
+
+options SCHED_ULE # ULE scheduler
+options SMP
+
+options VM_KMEM_SIZE_MAX=0x9CCD000
+
+# Pseudo devices
+device pty
+device loop
+device md
+
+# Serial ports
+device uart
+device uart_snps
+
+# Network
+device ether
+device vlan
+device mii
+device bpf
+device mdio
+device etherswitch
+device e6000sw
+device neta
+
+# PCI
+device pci
+
+# Interrupt controllers
+device gic
+
+# Timers
+device mpcore_timer
+
+# USB
+device usb
+device ehci
+device xhci
+device umass
+device scbus
+device pass
+device da
+
+# MMC/SD/SDIO Card slot support
+device mmc # mmc/sd bus
+device mmcsd # mmc/sd flash cards
+device sdhci # mmc/sd host controller
+
+# SATA
+device ahci
+
+# I2C
+device iic
+device iicbus
+device twsi
+
+# SPI
+device spibus
+device spigen
+device mv_spi
+
+# Wireless NIC cards
+device wlan # 802.11 support
+device ath # Atheros NIC's
+device ath_pci # Atheros pci/cardbus glue
+device ath_hal
+device ath_rate_sample
+options ATH_ENABLE_11N
+
+# CESA
+device cesa
+device crypto
+device cryptodev
+
+# L2 Cache
+device pl310
+
+options PLATFORM
+
+# FDT
+options FDT
+
+# GPIO
+device gpio
+device gpioled
diff --git a/sys/arm/conf/ARMADAXP b/sys/arm/conf/ARMADAXP
new file mode 100644
index 000000000000..96c90c639a42
--- /dev/null
+++ b/sys/arm/conf/ARMADAXP
@@ -0,0 +1,88 @@
+#
+# Custom kernel for Marvell Armada XP
+#
+# For more information on this file, please read the config(5) manual page,
+# and/or the handbook section on Kernel Configuration Files:
+#
+# https://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html
+#
+# The handbook is also available locally in /usr/share/doc/handbook
+# if you've installed the doc distribution, otherwise always see the
+# FreeBSD World Wide Web server (https://www.FreeBSD.org/) for the
+# latest information.
+#
+# An exhaustive list of options and more detailed explanations of the
+# device lines is also present in the ../../conf/NOTES and NOTES files.
+# If you are in doubt as to the purpose or necessity of a line, check first
+# in NOTES.
+#
+# $FreeBSD$
+
+#NO_UNIVERSE
+
+ident MV-88F78XX0
+
+include "std.armv7"
+include "../mv/armadaxp/std.mv78x60"
+
+options SOC_MV_ARMADAXP
+
+options SCHED_ULE # ULE scheduler
+options SMP # Enable multiple cores
+
+# NFS root from boopt/dhcp
+options BOOTP
+options BOOTP_NFSROOT
+options BOOTP_NFSV3
+options BOOTP_WIRED_TO=mge0
+
+options ROOTDEVNAME=\"ufs:/dev/da0p1\"
+
+options MUTEX_NOINLINE
+options RWLOCK_NOINLINE
+options NO_FFS_SNAPSHOT
+options NO_SWAPPING
+
+# Pseudo devices
+device pty
+device loop
+device md
+
+# USB
+device usb
+device ehci
+device umass
+device scbus
+device pass
+device da
+
+# SATA
+device mvs
+
+# Serial ports
+device uart
+
+# I2C (TWSI)
+device iic
+device iicbus
+device twsi
+
+#Network
+device ether
+device mge # Marvell Gigabit Ethernet controller
+device mii
+device mdio
+device e1000phy
+device bpf
+options DEVICE_POLLING
+device vlan
+
+#PCI/PCIE
+device pci
+
+# Flattened Device Tree
+options FDT # Configure using FDT/DTB data
+options FDT_DTB_STATIC
+makeoptions FDT_DTS_FILE=db78460.dts
+
+options PLATFORM
diff --git a/sys/arm/conf/COLIBRI-VF50 b/sys/arm/conf/COLIBRI-VF50
new file mode 100644
index 000000000000..86dcb0b9ef73
--- /dev/null
+++ b/sys/arm/conf/COLIBRI-VF50
@@ -0,0 +1,28 @@
+# Kernel configuration for Toradex Colibri VF50 Evaluation Board.
+#
+# For more information on this file, please read the config(5) manual page,
+# and/or the handbook section on Kernel Configuration Files:
+#
+# https://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html
+#
+# The handbook is also available locally in /usr/share/doc/handbook
+# if you've installed the doc distribution, otherwise always see the
+# FreeBSD World Wide Web server (https://www.FreeBSD.org/) for the
+# latest information.
+#
+# An exhaustive list of options and more detailed explanations of the
+# device lines is also present in the ../../conf/NOTES and NOTES files.
+# If you are in doubt as to the purpose or necessity of a line, check first
+# in NOTES.
+#
+# $FreeBSD$
+
+#NO_UNIVERSE
+
+include "VYBRID"
+ident COLIBRI-VF50
+
+#FDT
+options FDT
+options FDT_DTB_STATIC
+makeoptions FDT_DTS_FILE=vybrid-colibri-vf50.dts
diff --git a/sys/arm/conf/COSMIC b/sys/arm/conf/COSMIC
new file mode 100644
index 000000000000..cd49dbc5b050
--- /dev/null
+++ b/sys/arm/conf/COSMIC
@@ -0,0 +1,28 @@
+# Kernel configuration for Cosmic Board (Freescale Vybrid Family development board).
+#
+# For more information on this file, please read the config(5) manual page,
+# and/or the handbook section on Kernel Configuration Files:
+#
+# https://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html
+#
+# The handbook is also available locally in /usr/share/doc/handbook
+# if you've installed the doc distribution, otherwise always see the
+# FreeBSD World Wide Web server (https://www.FreeBSD.org/) for the
+# latest information.
+#
+# An exhaustive list of options and more detailed explanations of the
+# device lines is also present in the ../../conf/NOTES and NOTES files.
+# If you are in doubt as to the purpose or necessity of a line, check first
+# in NOTES.
+#
+# $FreeBSD$
+
+#NO_UNIVERSE
+
+include "VYBRID"
+ident COSMIC
+
+#FDT
+options FDT
+options FDT_DTB_STATIC
+makeoptions FDT_DTS_FILE=vybrid-cosmic.dts
diff --git a/sys/arm/conf/DEFAULTS b/sys/arm/conf/DEFAULTS
new file mode 100644
index 000000000000..6755185f54d5
--- /dev/null
+++ b/sys/arm/conf/DEFAULTS
@@ -0,0 +1,7 @@
+#
+# DEFAULTS -- Default kernel configuration file for FreeBSD/arm
+#
+# $FreeBSD$
+
+device mem
+
diff --git a/sys/arm/conf/EFIKA_MX b/sys/arm/conf/EFIKA_MX
new file mode 100644
index 000000000000..724d742e03af
--- /dev/null
+++ b/sys/arm/conf/EFIKA_MX
@@ -0,0 +1,135 @@
+#
+# Kernel configuration for Efika MX Smarttop/Smartbook boards
+#
+# For more information on this file, please read the config(5) manual page,
+# and/or the handbook section on Kernel Configuration Files:
+#
+# https://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html
+#
+# The handbook is also available locally in /usr/share/doc/handbook
+# if you've installed the doc distribution, otherwise always see the
+# FreeBSD World Wide Web server (https://www.FreeBSD.org/) for the
+# latest information.
+#
+# An exhaustive list of options and more detailed explanations of the
+# device lines is also present in the ../../conf/NOTES and NOTES files.
+# If you are in doubt as to the purpose or necessity of a line, check first
+# in NOTES.
+#
+# $FreeBSD$
+
+ident EFIKA_MX
+
+include "std.armv7"
+include "../freescale/imx/std.imx51"
+
+makeoptions WITHOUT_MODULES="ahc"
+
+options SOC_IMX51
+
+options SCHED_4BSD # 4BSD scheduler
+#options MD_ROOT # MD is a potential root device
+#options NFSD # Network Filesystem Server
+options PLATFORM
+options INCLUDE_CONFIG_FILE # Include this file in kernel
+
+# NFS root from boopt/dhcp
+#options BOOTP
+#options BOOTP_NFSROOT
+#options BOOTP_COMPAT
+#options BOOTP_NFSV3
+#options BOOTP_WIRED_TO=ue0
+
+options ROOTDEVNAME=\"ufs:ada0s2a\"
+
+
+# kernel/memory size reduction
+#options MUTEX_NOINLINE
+#options NO_FFS_SNAPSHOT
+#options NO_SWAPPING
+#options NO_SYSCTL_DESCR
+#options RWLOCK_NOINLINE
+
+# The `bpf' device enables the Berkeley Packet Filter.
+# Be aware of the administrative consequences of enabling this!
+# Note that 'bpf' is required for DHCP.
+device bpf # Berkeley packet filter
+
+# Pseudo devices.
+device loop # Network loopback
+device ether # Ethernet support
+#device vlan # 802.1Q VLAN support
+#device tuntap # Packet tunnel.
+#device md # Memory "disks"
+#device gif # IPv6 and IPv4 tunneling
+#device firmware # firmware assist module
+
+# Serial (COM) ports
+device uart # Multi-uart driver
+
+device ata
+device atapci # Only for helper functions
+device imxata
+
+device gpio
+device gpioled
+
+device fsliic
+device iic
+device iicbus
+
+# SCSI peripherals
+device scbus # SCSI bus (required for ATA/SCSI)
+device da # Direct Access (disks)
+device cd # CD
+device pass # Passthrough device (direct ATA/SCSI access)
+
+# USB support
+options USB_HOST_ALIGN=64 # Align usb buffers to cache line size.
+device ehci # OHCI USB interface
+device usb # USB Bus (required)
+device umass # Disks/Mass storage - Requires scbus and da
+device uhid # "Human Interface Devices"
+device u3g
+
+# USB Ethernet, requires miibus
+device miibus
+device aue # ADMtek USB Ethernet
+device axe # ASIX Electronics USB Ethernet
+device cdce # Generic USB over Ethernet
+device cue # CATC USB Ethernet
+device kue # Kawasaki LSI USB Ethernet
+device rue # RealTek RTL8150 USB Ethernet
+device udav # Davicom DM9601E USB
+
+# USB Wireless
+device rum # Ralink Technology RT2501USB wireless NICs
+
+# Watchdog timer.
+# WARNING: can't be disabled!!!
+device imxwdt # Watchdog
+
+# Wireless NIC cards
+device wlan # 802.11 support
+device wlan_wep # 802.11 WEP support
+device wlan_ccmp # 802.11 CCMP support
+device wlan_tkip # 802.11 TKIP support
+device wlan_amrr # AMRR transmit rate control algorithm
+
+# HID support
+device hid # Generic HID support
+
+# Flattened Device Tree
+options FDT # Configure using FDT/DTB data
+options FDT_DTB_STATIC
+makeoptions FDT_DTS_FILE=efikamx.dts
+device fdt_pinctrl # FDT pinmux driver
+
+# NOTE: serial console will be disabled if syscons enabled
+# Uncomment following lines for framebuffer/syscons support
+device sc
+device kbdmux
+options SC_DFLT_FONT # compile font in
+makeoptions SC_DFLT_FONT=cp437
+device ukbd # Allow keyboard like HIDs to control console
+device ums
diff --git a/sys/arm/conf/GENERIC b/sys/arm/conf/GENERIC
new file mode 100644
index 000000000000..3cfe16ccfe54
--- /dev/null
+++ b/sys/arm/conf/GENERIC
@@ -0,0 +1,301 @@
+#
+# GENERICV6 -- Generic(ish) kernel config.
+#
+# For more information on this file, please read the config(5) manual page,
+# and/or the handbook section on Kernel Configuration Files:
+#
+# https://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html
+#
+# The handbook is also available locally in /usr/share/doc/handbook
+# if you've installed the doc distribution, otherwise always see the
+# FreeBSD World Wide Web server (https://www.FreeBSD.org/) for the
+# latest information.
+#
+# An exhaustive list of options and more detailed explanations of the
+# device lines is also present in the ../../conf/NOTES and NOTES files.
+# If you are in doubt as to the purpose or necessity of a line, check first
+# in NOTES.
+#
+# $FreeBSD$
+
+ident GENERIC
+
+cpu CPU_CORTEXA
+cpu CPU_MV_PJ4B
+options SMP_ON_UP
+machine arm armv7
+makeoptions CONF_CFLAGS="-march=armv7a"
+
+include "std.armv7"
+files "../allwinner/files.allwinner"
+files "../allwinner/files.allwinner_up"
+files "../allwinner/a10/files.a10"
+files "../allwinner/a13/files.a13"
+files "../allwinner/a20/files.a20"
+files "../allwinner/a31/files.a31"
+files "../allwinner/a33/files.a33"
+files "../allwinner/a83t/files.a83t"
+files "../allwinner/h3/files.h3"
+files "../broadcom/bcm2835/files.bcm2836"
+files "../broadcom/bcm2835/files.bcm283x"
+files "../freescale/imx/files.imx6"
+files "../mv/files.arm7"
+files "../nvidia/tegra124/files.tegra124"
+files "../qemu/files.qemu"
+files "../rockchip/files.rk32xx"
+files "../ti/files.ti"
+files "../ti/am335x/files.am335x"
+files "../ti/omap4/files.omap4"
+files "../xilinx/files.zynq7"
+
+options SOC_ALLWINNER_A10
+options SOC_ALLWINNER_A13
+options SOC_ALLWINNER_A20
+options SOC_ALLWINNER_A31
+options SOC_ALLWINNER_A31S
+options SOC_ALLWINNER_A33
+options SOC_ALLWINNER_A83T
+options SOC_ALLWINNER_H2PLUS
+options SOC_ALLWINNER_H3
+options SOC_BCM2836
+options SOC_BRCM_BCM2837
+options SOC_MV_ARMADA38X
+options SOC_MV_ARMADAXP
+options SOC_TI_AM335X
+options SOC_OMAP4
+
+options SCHED_ULE # ULE scheduler
+options SMP # Enable multiple cores
+options PLATFORM
+options LINUX_BOOT_ABI
+
+# EXT_RESOURCES pseudo devices
+options EXT_RESOURCES
+device clk
+device phy
+device hwreset
+device nvmem
+device regulator
+device syscon
+
+# CPU frequency control
+device cpufreq
+
+# Interrupt controller
+device gic
+
+# PMU support (for CCNT).
+device pmu
+
+# ARM Generic Timer
+device generic_timer
+device mpcore_timer
+
+# MMC/SD/SDIO Card slot support
+device dwmmc
+device sdhci # SD controller
+device mmc # mmc/sd bus
+device mmcsd # mmc/sd flash cards
+
+# ATA controllers
+device ahci # AHCI-compatible SATA controllers
+#device ata # Legacy ATA/SATA controllers
+
+# PCI
+options NEW_PCIB
+device pci
+device pci_host_generic
+
+# PCI NICs
+device re # RealTek 8139C+/8169/8169S/8110S
+
+# VirtIO
+device virtio
+device virtio_mmio
+device virtio_pci
+device virtio_blk
+device vtnet
+
+# Console and misc
+device uart
+device uart_ns8250
+device uart_snps
+device pl011
+device pty
+device snp
+device md # Memory "disks"
+device firmware # firmware assist module
+device pl310 # PL310 L2 cache controller
+device psci
+
+# I2C support
+device iicbus
+device iic
+device twsi
+device rsb # Allwinner Reduced Serial Bus
+device p2wi # Allwinner Push-Pull Two Wire
+device axp209 # AXP209 Power Management Unit
+device axp81x # AXP813/818 Power Management Unit
+device bcm2835_bsc
+device fsliic # Freescale i2c/iic
+device icee # AT24Cxxx and compatible EEPROMs
+device sy8106a # SY8106A Buck Regulator
+device ti_i2c
+device am335x_pmic # AM335x Power Management IC (TPC65217)
+device am335x_rtc # RTC support (power management only)
+device twl # TI TWLX0X0/TPS659x0 Power Management
+device twl_vreg # twl voltage regulation
+device twl_clks # twl external clocks
+
+# i2c RTCs
+device ds1307 # Dallas DS1307 RTC and compatible
+device ds13rtc # All Dallas/Maxim DS13xx RTCs
+device ds1672 # Dallas DS1672 RTC
+device ds3231 # Dallas DS3231 RTC + temperature
+device nxprtc # NXP RTCs: PCA/PFC212x PCA/PCF85xx
+device s35390a # Seiko s3539x RTCs
+
+# GPIO
+device dwgpio # Synopsys DesignWare APB GPIO Controller
+device gpio
+device gpiobacklight
+device gpioled
+device gpioregulator
+
+# EVDEV support
+device evdev # input event device support
+options EVDEV_SUPPORT # evdev support in legacy drivers
+device uinput # install /dev/uinput cdev
+device aw_cir
+
+# SPI
+device spibus
+device spigen
+device bcm2835_spi
+device mv_spi
+device ti_spi
+device zy7_qspi # Xilinx Zynq QSPI controller
+
+# ADC support
+device ti_adc
+
+# PWM
+device pwm
+
+# Watchdog support
+# If we don't enable the watchdog driver, the BealeBone could potentially
+# reboot automatically because the boot loader might have enabled the
+# watchdog.
+device ti_wdt
+device imxwdt # Watchdog. WARNING: can't be disabled!!!
+device aw_wdog # Allwinner Watchdog
+
+device scbus # SCSI bus (required for ATA/SCSI)
+device da # Direct Access (disks)
+device cd # CD
+device pass # Passthrough device (direct ATA/SCSI access)
+
+# USB support
+options USB_HOST_ALIGN=64 # Align usb buffers to cache line size.
+device usb
+device uhci
+device ohci
+device ehci
+device xhci
+device dwcotg # DWC OTG controller
+device musb
+
+device axe # USB-Ethernet
+device umass # Disks/Mass storage - Requires scbus and da
+device uhid # "Human Interface Devices"
+device ukbd # Allow keyboard like HIDs to control console
+
+# Device mode support
+device usb_template # Control of the gadget
+
+# Ethernet
+device loop
+device ether
+device vlan # 802.1Q VLAN support
+device bpf
+device mii
+device mdio
+device etherswitch
+device e6000sw
+
+# Ethernet NICs that use the common MII bus controller code.
+# NOTE: Be sure to keep the 'device miibus' line in order to use these NICs!
+device miibus
+
+device awg # 10/100/1000 integrated EMAC controller
+device cgem # Cadence GEM Gigabit Ethernet device
+device cpsw # TI Common Platform Ethernet Switch (CPSW)
+device dwc # 10/100/1000 integrated GMAC controller
+device emac # 10/100 integrated EMAC controller
+device ffec # Freescale Fast Ethernet Controller
+device neta # Marvell 10/100/1000 Network controller
+device smsc # SMSC LAN91C111
+
+# Sound support
+device sound
+
+# Framebuffer support
+device vt
+device kbdmux
+device ums
+device videomode
+device hdmi
+device vchiq
+
+# Pinmux
+device fdt_pinctrl
+
+# TI Programmable Realtime Unit support
+device ti_pruss
+
+# Mailbox support
+device ti_mbox
+
+# DMA controller
+device fslsdma
+device ti_sdma
+device a10_dmac
+device a31_dmac
+
+# Extensible Firmware Interface
+options EFI
+
+# Marvell Cryptographic Engine and Security Accelerator
+device cesa
+device crypto
+device cryptodev
+
+# RTC
+device imx6_snvs # IMX6 On-chip RTC
+device aw_rtc # Allwinner On-chip RTC
+
+# EFUSE
+device aw_sid # Allwinner Secure ID EFUSE
+
+# Thermal sensors
+device aw_thermal # Allwinner Thermal Sensor Controller
+
+# HID support
+device hid # Generic HID support
+
+# Flattened Device Tree
+options FDT # Configure using FDT/DTB data
+makeoptions MODULES_EXTRA+="dtb/allwinner"
+makeoptions MODULES_EXTRA+="dtb/am335x"
+makeoptions MODULES_EXTRA+="dtb/imx6"
+makeoptions MODULES_EXTRA+="dtb/nvidia"
+makeoptions MODULES_EXTRA+="dtb/omap4"
+makeoptions MODULES_EXTRA+="dtb/rockchip"
+makeoptions MODULES_EXTRA+="dtb/rpi"
+makeoptions MODULES_EXTRA+="dtb/zynq"
+
+# SOC-specific modules
+makeoptions MODULES_EXTRA+="allwinner"
+makeoptions MODULES_EXTRA+="arm_ti"
+makeoptions MODULES_EXTRA+="imx"
+
diff --git a/sys/arm/conf/GENERIC-MMCCAM b/sys/arm/conf/GENERIC-MMCCAM
new file mode 100644
index 000000000000..20ca990d1c3e
--- /dev/null
+++ b/sys/arm/conf/GENERIC-MMCCAM
@@ -0,0 +1,22 @@
+#
+# GEMERIC-MMCCAM
+#
+# Custom kernel for GENERIC plus MMCCAM as opposed to the prior MMC stack.
+#
+# $FreeBSD$
+
+include GENERIC
+
+ident GENERIC-MMCCAM
+
+options MMCCAM
+
+# Add CAMDEBUG stuff
+options CAMDEBUG
+options CAM_DEBUG_FLAGS=(CAM_DEBUG_INFO|CAM_DEBUG_PROBE|CAM_DEBUG_PERIPH)
+
+# pass(4) device
+device pass
+
+nodevice mmc
+nodevice mmcsd
diff --git a/sys/arm/conf/GENERIC-NODEBUG b/sys/arm/conf/GENERIC-NODEBUG
new file mode 100644
index 000000000000..6e60deff7569
--- /dev/null
+++ b/sys/arm/conf/GENERIC-NODEBUG
@@ -0,0 +1,32 @@
+#
+# GENERIC-NODEBUG -- WITNESS and INVARIANTS free kernel configuration file
+# for FreeBSD/arm
+#
+# This configuration file removes several debugging options, including
+# WITNESS and INVARIANTS checking, which are known to have significant
+# performance impact on running systems. When benchmarking new features
+# this kernel should be used instead of the standard GENERIC.
+# This kernel configuration should never appear outside of the HEAD
+# of the FreeBSD tree.
+#
+# For more information on this file, please read the config(5) manual page,
+# and/or the handbook section on Kernel Configuration Files:
+#
+# https://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html
+#
+# The handbook is also available locally in /usr/share/doc/handbook
+# if you've installed the doc distribution, otherwise always see the
+# FreeBSD World Wide Web server (https://www.FreeBSD.org/) for the
+# latest information.
+#
+# An exhaustive list of options and more detailed explanations of the
+# device lines is also present in the ../../conf/NOTES and NOTES files.
+# If you are in doubt as to the purpose or necessity of a line, check first
+# in NOTES.
+#
+# $FreeBSD$
+
+include GENERIC
+include "../../conf/std.nodebug"
+
+ident GENERIC-NODEBUG
diff --git a/sys/arm/conf/IMX53 b/sys/arm/conf/IMX53
new file mode 100644
index 000000000000..0372cb62088d
--- /dev/null
+++ b/sys/arm/conf/IMX53
@@ -0,0 +1,123 @@
+#
+# Kernel configuration for i.MX53 boards
+#
+# For more information on this file, please read the config(5) manual page,
+# and/or the handbook section on Kernel Configuration Files:
+#
+# https://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html
+#
+# The handbook is also available locally in /usr/share/doc/handbook
+# if you've installed the doc distribution, otherwise always see the
+# FreeBSD World Wide Web server (https://www.FreeBSD.org/) for the
+# latest information.
+#
+# An exhaustive list of options and more detailed explanations of the
+# device lines is also present in the ../../conf/NOTES and NOTES files.
+# If you are in doubt as to the purpose or necessity of a line, check first
+# in NOTES.
+#
+# $FreeBSD$
+
+ident IMX53
+
+include "std.armv7"
+include "../freescale/imx/std.imx53"
+
+options SOC_IMX53
+
+options SCHED_4BSD # 4BSD scheduler
+#options NFSD # Network Filesystem Server
+options PLATFORM
+options INCLUDE_CONFIG_FILE # Include this file in kernel
+
+# kernel/memory size reduction
+#options MUTEX_NOINLINE
+#options NO_FFS_SNAPSHOT
+#options NO_SWAPPING
+#options NO_SYSCTL_DESCR
+#options RWLOCK_NOINLINE
+
+# The `bpf' device enables the Berkeley Packet Filter.
+# Be aware of the administrative consequences of enabling this!
+# Note that 'bpf' is required for DHCP.
+device bpf # Berkeley packet filter
+
+# Pseudo devices.
+device loop # Network loopback
+device ether # Ethernet support
+#device vlan # 802.1Q VLAN support
+#device tuntap # Packet tunnel.
+device md # Memory "disks"
+#device gif # IPv6 and IPv4 tunneling
+#device firmware # firmware assist module
+
+# Ethernet
+device ffec # Freescale Fast Ethernet Controller
+device miibus # Standard mii bus
+
+# Serial (COM) ports
+device uart # Multi-uart driver
+
+device ata
+device atapci # Only for helper functions
+device imxata
+
+device gpio
+device gpioled
+
+device fsliic
+device iic
+device iicbus
+
+# SCSI peripherals
+device scbus # SCSI bus (required for ATA/SCSI)
+device da # Direct Access (disks)
+device cd # CD
+device pass # Passthrough device (direct ATA/SCSI access)
+
+# USB support
+options USB_HOST_ALIGN=64 # Align usb buffers to cache line size.
+device ehci # OHCI USB interface
+device usb # USB Bus (required)
+device umass # Disks/Mass storage - Requires scbus and da
+device uhid # "Human Interface Devices"
+#device ukbd # Allow keyboard like HIDs to control console
+device ums
+
+# USB Ethernet, requires miibus
+#device miibus
+#device aue # ADMtek USB Ethernet
+#device axe # ASIX Electronics USB Ethernet
+#device cdce # Generic USB over Ethernet
+#device cue # CATC USB Ethernet
+#device kue # Kawasaki LSI USB Ethernet
+#device rue # RealTek RTL8150 USB Ethernet
+#device udav # Davicom DM9601E USB
+
+# USB Wireless
+#device rum # Ralink Technology RT2501USB wireless NICs
+
+# Watchdog timer.
+# WARNING: can't be disabled!!!
+device imxwdt # Watchdog
+
+# Wireless NIC cards
+device wlan # 802.11 support
+device wlan_wep # 802.11 WEP support
+device wlan_ccmp # 802.11 CCMP support
+device wlan_tkip # 802.11 TKIP support
+device wlan_amrr # AMRR transmit rate control algorithm
+
+# MMC
+#device sdhci # SD controller
+#device mmc # SD/MMC protocol
+#device mmcsd # SDCard disk device
+
+# HID support
+device hid # Generic HID support
+
+
+# Flattened Device Tree
+options FDT # Configure using FDT/DTB data
+makeoptions MODULES_EXTRA="dtb/imx5 imx"
+device fdt_pinctrl # FDT pinmux driver
diff --git a/sys/arm/conf/IMX6 b/sys/arm/conf/IMX6
new file mode 100644
index 000000000000..6d8388ff0d60
--- /dev/null
+++ b/sys/arm/conf/IMX6
@@ -0,0 +1,139 @@
+#
+# Kernel configuration for Freescale i.MX6 systems.
+#
+# For more information on this file, please read the config(5) manual page,
+# and/or the handbook section on Kernel Configuration Files:
+#
+# https://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html
+#
+# The handbook is also available locally in /usr/share/doc/handbook
+# if you've installed the doc distribution, otherwise always see the
+# FreeBSD World Wide Web server (https://www.FreeBSD.org/) for the
+# latest information.
+#
+# An exhaustive list of options and more detailed explanations of the
+# device lines is also present in the ../../conf/NOTES and NOTES files.
+# If you are in doubt as to the purpose or necessity of a line, check first
+# in NOTES.
+#
+# $FreeBSD$
+
+ident IMX6
+include "std.armv7"
+include "../freescale/imx/std.imx6"
+
+options SOC_IMX6
+
+options SCHED_ULE # ULE scheduler
+#options NFSD # Network Filesystem Server
+options INCLUDE_CONFIG_FILE # Include this file in kernel
+options PLATFORM
+options SMP # Enable multiple cores
+
+# NFS root from boopt/dhcp
+#options BOOTP
+#options BOOTP_NFSROOT
+#options BOOTP_COMPAT
+#options BOOTP_NFSV3
+#options BOOTP_WIRED_TO=ffec0
+
+# U-Boot stuff lives on slice 1, FreeBSD on slice 2.
+options ROOTDEVNAME=\"ufs:mmcsd0s2a\"
+
+# Interrupt controller
+device gic
+# Cache controller
+device pl310 # PL310 L2 cache controller
+# ARM MPCore timer
+device mpcore_timer
+
+# Pseudo devices.
+device loop # Network loopback
+device vlan # 802.1Q VLAN support
+device tuntap # Packet tunnel.
+device md # Memory "disks"
+#device gif # IPv6 and IPv4 tunneling
+#device firmware # firmware assist module
+device ether # Ethernet support
+device miibus # Required for ethernet
+device bpf # Berkeley packet filter (required for DHCP)
+
+# General-purpose input/output
+device gpio
+
+# Serial (COM) ports
+device uart # Multi-uart driver
+
+# SDCard
+device sdhci # SD controller
+device mmc # SD/MMC protocol
+device mmcsd # SDCard disk device
+
+# SCSI peripherals
+device scbus # SCSI bus (required for ATA/SCSI)
+device da # Direct Access (disks)
+device cd # CD
+device pass # Passthrough device (direct ATA/SCSI access)
+
+# ATA controllers
+device ahci # AHCI-compatible SATA controllers
+
+# USB support
+device ehci # OHCI USB interface
+device usb # USB Bus (required)
+device umass # Disks/Mass storage - Requires scbus and da
+device uhid # "Human Interface Devices"
+device u3g # USB modems
+#device ukbd # Allow keyboard like HIDs to control console
+#device ums # USB mouse
+
+# USB Ethernet, requires miibus
+#device aue # ADMtek USB Ethernet
+#device axe # ASIX Electronics USB Ethernet
+#device cdce # Generic USB over Ethernet
+#device cue # CATC USB Ethernet
+#device kue # Kawasaki LSI USB Ethernet
+#device rue # RealTek RTL8150 USB Ethernet
+#device udav # Davicom DM9601E USB
+
+# USB Wireless
+#device rum # Ralink Technology RT2501USB wireless NICs
+
+# Wireless NIC cards
+#device wlan # 802.11 support
+#device wlan_wep # 802.11 WEP support
+#device wlan_ccmp # 802.11 CCMP support
+#device wlan_tkip # 802.11 TKIP support
+#device wlan_amrr # AMRR transmit rate control algorithm
+
+# HID support
+device hid # Generic HID support
+
+device vt
+device kbdmux
+device ukbd
+device videomode
+device hdmi
+
+# Flattened Device Tree
+options FDT # Configure using FDT/DTB data
+makeoptions MODULES_EXTRA="dtb/imx6 imx"
+device fdt_pinctrl # FDT pinmux driver
+
+# EXT_RESOURCES pseudo devices
+options EXT_RESOURCES
+device clk
+device phy
+device hwreset
+device nvmem
+device regulator
+device syscon
+
+# SoC-specific devices
+device ffec # Freescale Fast Ethernet Controller
+device fsliic # Freescale i2c/iic
+device iic # iic protocol
+device iicbus # iic bus
+device imx6_snvs # On-chip RTC
+device imxwdt # Watchdog. WARNING: can't be disabled!!!
+
diff --git a/sys/arm/conf/JETSON-TK1 b/sys/arm/conf/JETSON-TK1
new file mode 100644
index 000000000000..b12186d91c2c
--- /dev/null
+++ b/sys/arm/conf/JETSON-TK1
@@ -0,0 +1,37 @@
+# Kernel configuration for Jetson TK1 board
+#
+# For more information on this file, please read the config(5) manual page,
+# and/or the handbook section on Kernel Configuration Files:
+#
+# https://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html
+#
+# The handbook is also available locally in /usr/share/doc/handbook
+# if you've installed the doc distribution, otherwise always see the
+# FreeBSD World Wide Web server (https://www.FreeBSD.org/) for the
+# latest information.
+#
+# An exhaustive list of options and more detailed explanations of the
+# device lines is also present in the ../../conf/NOTES and NOTES files.
+# If you are in doubt as to the purpose or necessity of a line, check first
+# in NOTES.
+#
+# $FreeBSD$
+
+#NO_UNIVERSE
+
+include "TEGRA124"
+ident JETSON-TK1
+
+# Flattened Device Tree
+options FDT_DTB_STATIC
+makeoptions FDT_DTS_FILE=tegra124-jetson-tk1-fbsd.dts
+
+makeoptions MODULES_OVERRIDE=""
+#options BOOTVERBOSE
+#options BOOTHOWTO=RB_SINGLE
+
+#options ROOTDEVNAME=\"ufs:mmcsd0s2a\"
+options ROOTDEVNAME=\"ufs:ada0s1a\"
+
+# CTF doesn't works yet
+makeoptions WITHOUT_CTF=1
diff --git a/sys/arm/conf/LINT b/sys/arm/conf/LINT
new file mode 100644
index 000000000000..aaba324bf49a
--- /dev/null
+++ b/sys/arm/conf/LINT
@@ -0,0 +1,4 @@
+# $FreeBSD$
+
+include "../../conf/NOTES"
+include NOTES
diff --git a/sys/arm/conf/NOTES b/sys/arm/conf/NOTES
new file mode 100644
index 000000000000..cbc70ac48f21
--- /dev/null
+++ b/sys/arm/conf/NOTES
@@ -0,0 +1,84 @@
+# arm-specific changes for doing a LINT build.
+# $FreeBSD$
+
+
+machine arm armv7
+cpu CPU_CORTEXA
+cpu CPU_MV_PJ4B
+makeoptions CONF_CFLAGS+="-march=armv7a"
+
+# Add options for armv7 that are not in sys/conf/NOTES...
+
+options FDT # Flattened device tree support
+options FREEBSD_BOOT_LOADER # Process metadata passed from loader(8)
+options INTRNG # Include INTRNG framework
+options LINUX_BOOT_ABI # Process metadata passed from U-Boot
+options PLATFORM # Include platform_if support
+options SMP # Nearly all v7 SoCs are multicore
+options VFP # Enable floating point hardware support
+
+# NOTE: dtrace introduces CDDL-licensed components into the kernel
+device dtrace # dtrace core
+device dtraceall # include all dtrace modules
+options KDTRACE_HOOKS
+
+# Add misc devices which are specific to various arm platforms...
+
+device generic_timer # ARM Generic Timer
+device gic # Interrupt controller
+device gpio # gpio interface and bus
+device mpcore_timer # ARM MPCore Timer
+device pl310 # PL310 L2 cache controller
+device pl330 # ARM PL330 dma controller
+device pmu # PMU support (for CCNT).
+device twsi # i2c controller on Marvel and Allwinner
+device xdma # xDMA framework for SoC on-chip dma controllers
+
+# Add EXT_RESOURCES pseudo devices...
+
+options EXT_RESOURCES
+device clk
+device phy
+device hwreset
+device nvmem
+device regulator
+device syscon
+
+# Backlight subsystem
+device backlight
+
+# Undo options from sys/conf/NOTES that we do not want...
+
+nooptions COMPAT_FREEBSD4
+nooptions COMPAT_FREEBSD5
+nooptions COMPAT_FREEBSD6
+nooptions COMPAT_FREEBSD7
+nooptions COMPAT_FREEBSD9
+nooptions PPC_PROBE_CHIPSET
+nooptions MAXCPU # value is set in machine/param.h
+
+nodevice sym
+
+nodevice ccr
+nodevice cxgbe
+nodevice cxgbev
+nodevice snd_cmi
+
+nodevice mpr
+nodevice mps
+
+# Build SOC-specific modules...
+
+makeoptions MODULES_EXTRA+="allwinner"
+makeoptions MODULES_EXTRA+="arm_ti"
+makeoptions MODULES_EXTRA+="imx"
+
+# Build dtb files...
+
+makeoptions MODULES_EXTRA+="dtb/allwinner"
+makeoptions MODULES_EXTRA+="dtb/am335x"
+makeoptions MODULES_EXTRA+="dtb/imx6"
+makeoptions MODULES_EXTRA+="dtb/nvidia"
+makeoptions MODULES_EXTRA+="dtb/omap4"
+makeoptions MODULES_EXTRA+="dtb/rpi"
+makeoptions MODULES_EXTRA+="dtb/zynq"
diff --git a/sys/arm/conf/QUARTZ b/sys/arm/conf/QUARTZ
new file mode 100644
index 000000000000..3e7d239785e2
--- /dev/null
+++ b/sys/arm/conf/QUARTZ
@@ -0,0 +1,28 @@
+# Kernel configuration for Device Solutions Quartz Module.
+#
+# For more information on this file, please read the config(5) manual page,
+# and/or the handbook section on Kernel Configuration Files:
+#
+# https://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html
+#
+# The handbook is also available locally in /usr/share/doc/handbook
+# if you've installed the doc distribution, otherwise always see the
+# FreeBSD World Wide Web server (https://www.FreeBSD.org/) for the
+# latest information.
+#
+# An exhaustive list of options and more detailed explanations of the
+# device lines is also present in the ../../conf/NOTES and NOTES files.
+# If you are in doubt as to the purpose or necessity of a line, check first
+# in NOTES.
+#
+# $FreeBSD$
+
+#NO_UNIVERSE
+
+include "VYBRID"
+ident QUARTZ
+
+#FDT
+options FDT
+options FDT_DTB_STATIC
+makeoptions FDT_DTS_FILE=vybrid-quartz.dts
diff --git a/sys/arm/conf/RPI-B b/sys/arm/conf/RPI-B
new file mode 100644
index 000000000000..36eaff0c0f28
--- /dev/null
+++ b/sys/arm/conf/RPI-B
@@ -0,0 +1,101 @@
+#
+# RPI-B -- Custom configuration for the Raspberry Pi
+#
+# For more information on this file, please read the config(5) manual page,
+# and/or the handbook section on Kernel Configuration Files:
+#
+# https://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html
+#
+# The handbook is also available locally in /usr/share/doc/handbook
+# if you've installed the doc distribution, otherwise always see the
+# FreeBSD World Wide Web server (https://www.FreeBSD.org/) for the
+# latest information.
+#
+# An exhaustive list of options and more detailed explanations of the
+# device lines is also present in the ../../conf/NOTES and NOTES files.
+# If you are in doubt as to the purpose or necessity of a line, check first
+# in NOTES.
+#
+# $FreeBSD$
+
+ident RPI-B
+
+include "std.armv6"
+include "../broadcom/bcm2835/std.rpi"
+include "../broadcom/bcm2835/std.bcm2835"
+
+options SCHED_4BSD # 4BSD scheduler
+options PLATFORM
+
+# NFS root from boopt/dhcp
+#options BOOTP
+#options BOOTP_NFSROOT
+#options BOOTP_COMPAT
+#options BOOTP_NFSV3
+#options BOOTP_WIRED_TO=ue0
+
+#options ROOTDEVNAME=\"ufs:mmcsd0s2\"
+
+device bpf
+device loop
+device ether
+device uart
+device pty
+device snp
+device pl011
+
+# Device mode support
+device usb_template # Control of the gadget
+
+# Comment following lines for boot console on serial port
+device vt
+device kbdmux
+device ukbd
+
+device sdhci
+device mmc
+device mmcsd
+
+device gpio
+device gpioled
+
+# I2C
+device iic
+device iicbus
+device bcm2835_bsc
+
+device md
+
+# USB support
+device usb
+device dwcotg # DWC OTG controller
+
+# USB storage support
+device scbus
+device da
+device umass
+
+# USB ethernet support
+device smscphy
+device mii
+device smsc
+
+# SPI
+device spibus
+device bcm2835_spi
+
+device vchiq
+device sound
+
+device fdt_pinctrl
+
+# HID support
+device hid # Generic HID support
+
+# Flattened Device Tree
+options FDT # Configure using FDT/DTB data
+# Note: DTB is normally loaded and modified by RPi boot loader, then
+# handed to kernel via U-Boot and ubldr.
+#options FDT_DTB_STATIC
+#makeoptions FDT_DTS_FILE=rpi.dts
+makeoptions MODULES_EXTRA="dtb/rpi rpi_ft5406"
diff --git a/sys/arm/conf/SOCDK b/sys/arm/conf/SOCDK
new file mode 100644
index 000000000000..4c92aaea108a
--- /dev/null
+++ b/sys/arm/conf/SOCDK
@@ -0,0 +1,30 @@
+#
+# Kernel configuration for Altera Arria10 SOC Development Kit.
+#
+# For more information on this file, please read the config(5) manual page,
+# and/or the handbook section on Kernel Configuration Files:
+#
+# https://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html
+#
+# The handbook is also available locally in /usr/share/doc/handbook
+# if you've installed the doc distribution, otherwise always see the
+# FreeBSD World Wide Web server (https://www.FreeBSD.org/) for the
+# latest information.
+#
+# An exhaustive list of options and more detailed explanations of the
+# device lines is also present in the ../../conf/NOTES and NOTES files.
+# If you are in doubt as to the purpose or necessity of a line, check first
+# in NOTES.
+#
+# $FreeBSD$
+
+#NO_UNIVERSE
+
+include "SOCFPGA"
+ident SOCDK
+
+options ROOTDEVNAME=\"ufs:/dev/mmcsd0s4\"
+
+# Flattened Device Tree
+options FDT_DTB_STATIC
+makeoptions FDT_DTS_FILE=socfpga_arria10_socdk_sdmmc.dts
diff --git a/sys/arm/conf/SOCFPGA b/sys/arm/conf/SOCFPGA
new file mode 100644
index 000000000000..3d7511565307
--- /dev/null
+++ b/sys/arm/conf/SOCFPGA
@@ -0,0 +1,100 @@
+#
+# Kernel configuration for Altera SOCFPGA development kits.
+#
+# For more information on this file, please read the config(5) manual page,
+# and/or the handbook section on Kernel Configuration Files:
+#
+# https://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html
+#
+# The handbook is also available locally in /usr/share/doc/handbook
+# if you've installed the doc distribution, otherwise always see the
+# FreeBSD World Wide Web server (https://www.FreeBSD.org/) for the
+# latest information.
+#
+# An exhaustive list of options and more detailed explanations of the
+# device lines is also present in the ../../conf/NOTES and NOTES files.
+# If you are in doubt as to the purpose or necessity of a line, check first
+# in NOTES.
+#
+# $FreeBSD$
+
+ident SOCFPGA
+include "std.armv7"
+include "../altera/socfpga/std.socfpga"
+
+makeoptions MODULES_OVERRIDE=""
+
+options SCHED_ULE # ULE scheduler
+options PLATFORM # Platform based SoC
+options SMP # Enable multiple cores
+
+options SOC_ALTERA_ARRIA10
+options SOC_ALTERA_CYCLONE5
+
+# NFS root from boopt/dhcp
+#options BOOTP
+#options BOOTP_NFSROOT
+#options BOOTP_COMPAT
+#options BOOTP_NFSV3
+#options BOOTP_WIRED_TO=ue0
+
+# Interrupt controller
+device gic
+
+# ARM MPCore timer
+device mpcore_timer
+
+# DMA support
+device xdma
+device pl330
+
+# MMC/SD/SDIO Card slot support
+device mmc # mmc/sd bus
+device mmcsd # mmc/sd flash cards
+device dwmmc
+
+# Pseudo devices
+
+device loop
+device pty
+device md
+device gpio
+
+# USB support
+options USB_HOST_ALIGN=64 # Align usb buffers to cache line size.
+device usb
+device dwcotg
+
+device umass
+device scbus # SCSI bus (required for ATA/SCSI)
+device da # Direct Access (disks)
+device pass
+
+# Serial ports
+device uart
+device uart_snps
+
+# I2C (TWSI)
+device iic
+device iicbus
+
+# SPI
+device spibus
+device cqspi
+device n25q
+
+# Ethernet
+device ether
+device mii
+device smsc
+device smscphy
+device dwc
+device micphy
+
+# USB ethernet support, requires miibus
+device miibus
+device axe # ASIX Electronics USB Ethernet
+device bpf # Berkeley packet filter
+
+# Flattened Device Tree
+options FDT # Configure using FDT/DTB data
diff --git a/sys/arm/conf/SOCKIT b/sys/arm/conf/SOCKIT
new file mode 100644
index 000000000000..c83a3f260691
--- /dev/null
+++ b/sys/arm/conf/SOCKIT
@@ -0,0 +1,30 @@
+#
+# Kernel configuration for Terasic SoCKit (Altera Cyclone V SoC).
+#
+# For more information on this file, please read the config(5) manual page,
+# and/or the handbook section on Kernel Configuration Files:
+#
+# https://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html
+#
+# The handbook is also available locally in /usr/share/doc/handbook
+# if you've installed the doc distribution, otherwise always see the
+# FreeBSD World Wide Web server (https://www.FreeBSD.org/) for the
+# latest information.
+#
+# An exhaustive list of options and more detailed explanations of the
+# device lines is also present in the ../../conf/NOTES and NOTES files.
+# If you are in doubt as to the purpose or necessity of a line, check first
+# in NOTES.
+#
+# $FreeBSD$
+
+#NO_UNIVERSE
+
+include "SOCFPGA"
+ident SOCKIT
+
+options ROOTDEVNAME=\"ufs:/dev/mmcsd0s4\"
+
+# Flattened Device Tree
+options FDT_DTB_STATIC
+makeoptions FDT_DTS_FILE=socfpga_cyclone5_sockit_sdmmc.dts
diff --git a/sys/arm/conf/SOCKIT-BERI b/sys/arm/conf/SOCKIT-BERI
new file mode 100644
index 000000000000..7dd37a340502
--- /dev/null
+++ b/sys/arm/conf/SOCKIT-BERI
@@ -0,0 +1,37 @@
+#
+# Kernel configuration for Terasic SoCKit (Altera Cyclone V SoC).
+#
+# For more information on this file, please read the config(5) manual page,
+# and/or the handbook section on Kernel Configuration Files:
+#
+# https://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html
+#
+# The handbook is also available locally in /usr/share/doc/handbook
+# if you've installed the doc distribution, otherwise always see the
+# FreeBSD World Wide Web server (https://www.FreeBSD.org/) for the
+# latest information.
+#
+# An exhaustive list of options and more detailed explanations of the
+# device lines is also present in the ../../conf/NOTES and NOTES files.
+# If you are in doubt as to the purpose or necessity of a line, check first
+# in NOTES.
+#
+# $FreeBSD$
+
+#NO_UNIVERSE
+
+include "SOCFPGA"
+ident SOCKIT-BERI
+
+options ROOTDEVNAME=\"ufs:/dev/mmcsd0s4\"
+
+# BERI specific
+device beri_ring
+device beri_mem
+device beri_vtblk
+device vtbe
+device altera_pio
+
+# Flattened Device Tree
+options FDT_DTB_STATIC
+makeoptions FDT_DTS_FILE=socfpga_cyclone5_sockit_beri_sdmmc.dts
diff --git a/sys/arm/conf/TEGRA124 b/sys/arm/conf/TEGRA124
new file mode 100644
index 000000000000..68d7bf037525
--- /dev/null
+++ b/sys/arm/conf/TEGRA124
@@ -0,0 +1,142 @@
+#
+# Kernel configuration for NVIDIA Tegra124 based boards.
+#
+# For more information on this file, please read the config(5) manual page,
+# and/or the handbook section on Kernel Configuration Files:
+#
+# https://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html
+#
+# The handbook is also available locally in /usr/share/doc/handbook
+# if you've installed the doc distribution, otherwise always see the
+# FreeBSD World Wide Web server (https://www.FreeBSD.org/) for the
+# latest information.
+#
+# An exhaustive list of options and more detailed explanations of the
+# device lines is also present in the ../../conf/NOTES and NOTES files.
+# If you are in doubt as to the purpose or necessity of a line, check first
+# in NOTES.
+#
+# $FreeBSD$
+
+include "std.armv7"
+include "../nvidia/tegra124/std.tegra124"
+
+ident TEGRA124
+
+options SCHED_ULE # ULE scheduler
+options PLATFORM # Platform based SoC
+options SMP # Enable multiple cores
+options LINUX_BOOT_ABI
+
+# Interrupt controller
+device gic
+
+# ARM Generic Timer
+device generic_timer
+
+# EXT_RESOURCES pseudo devices
+options EXT_RESOURCES
+device clk
+device phy
+device hwreset
+device regulator
+device syscon
+
+# Pseudo devices.
+device loop # Network loopback
+device vlan # 802.1Q VLAN support
+#device tuntap # Packet tunnel.
+device md # Memory "disks"
+#device gif # IPv6 and IPv4 tunneling
+device firmware # firmware assist module
+device ether # Ethernet support
+device miibus # Required for ethernet
+device bpf # Berkeley packet filter (required for DHCP)
+
+
+# General-purpose input/output
+device gpio
+#device gpioled
+
+# I2C support
+device iic
+device iicbus
+device icee
+
+# Serial (COM) ports
+device uart # Multi-uart driver
+device uart_ns8250
+
+# MMC/SD/SDIO Card slot support
+device sdhci # SD controller
+device mmc # SD/MMC protocol
+device mmcsd # SDCard disk device
+
+# ATA controllers
+device ahci # AHCI-compatible SATA controllers
+
+# SCSI peripherals
+device scbus # SCSI bus (required for ATA/SCSI)
+device da # Direct Access (disks)
+device cd # CD
+device pass # Passthrough device (direct ATA/SCSI access)
+
+# USB support
+options USB_HOST_ALIGN=64 # Align usb buffers to cache line size.
+device ehci # EHCI USB interface
+device xhci # XHCI USB interface
+device tegra124_xusb_fw # Tegra XUSB firmware
+device usb # USB Bus (required)
+device umass # Disks/Mass storage - Requires scbus and da
+device uhid # "Human Interface Devices"
+#device u3g # USB modems
+device ukbd # Allow keyboard like HIDs to control console
+device ums # USB mouse
+
+# USB Ethernet, requires miibus
+#device aue # ADMtek USB Ethernet
+#device axe # ASIX Electronics USB Ethernet
+#device cdce # Generic USB over Ethernet
+#device cue # CATC USB Ethernet
+#device kue # Kawasaki LSI USB Ethernet
+#device rue # RealTek RTL8150 USB Ethernet
+#device udav # Davicom DM9601E USB
+
+# USB Wireless
+#device rum # Ralink Technology RT2501USB wireless NICs
+
+# Wireless NIC cards
+#device wlan # 802.11 support
+#device wlan_wep # 802.11 WEP support
+#device wlan_ccmp # 802.11 CCMP support
+#device wlan_tkip # 802.11 TKIP support
+#device wlan_amrr # AMRR transmit rate control algorithm
+
+# PCI
+options NEW_PCIB
+device pci
+
+# PCI Ethernet NICs that use the common MII bus controller code.
+# NOTE: Be sure to keep the 'device miibus' line in order to use these NICs!
+device re # RealTek 8139C+/8169/8169S/8110S
+
+# DRM2
+device fbd
+device vt
+device kbdmux
+device drm2
+
+# Sound
+#device sound
+#device snd_hda
+
+# HID support
+device hid # Generic HID support
+
+# Flattened Device Tree
+options FDT # Configure using FDT/DTB data
+device fdt_pinctrl
+
+# SoC-specific devices
+
+#device hwpmc
diff --git a/sys/arm/conf/VERSATILEPB b/sys/arm/conf/VERSATILEPB
new file mode 100644
index 000000000000..3312f6d4f9e8
--- /dev/null
+++ b/sys/arm/conf/VERSATILEPB
@@ -0,0 +1,75 @@
+#
+# VERSATILEPB - Configuration for QEMU version of Versatile Platform Board
+#
+# For more information on this file, please read the config(5) manual page,
+# and/or the handbook section on Kernel Configuration Files:
+#
+# https://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html
+#
+# The handbook is also available locally in /usr/share/doc/handbook
+# if you've installed the doc distribution, otherwise always see the
+# FreeBSD World Wide Web server (https://www.FreeBSD.org/) for the
+# latest information.
+#
+# An exhaustive list of options and more detailed explanations of the
+# device lines is also present in the ../../conf/NOTES and NOTES files.
+# If you are in doubt as to the purpose or necessity of a line, check first
+# in NOTES.
+#
+# $FreeBSD$
+
+ident VERSATILEPB
+machine arm armv6
+cpu CPU_ARM1176
+
+include "std.armv6"
+files "../versatile/files.versatile"
+makeoptions MODULES_OVERRIDE=""
+
+options KERNVIRTADDR=0xc0100000
+makeoptions KERNVIRTADDR=0xc0100000
+
+options SCHED_4BSD # 4BSD scheduler
+options LINUX_BOOT_ABI # Process metadata passed from Linux boot loaders
+
+options ROOTDEVNAME=\"ufs:da0s1a\"
+
+device bpf
+device loop
+device mii
+device mii_bitbang
+device smc
+device smcphy
+device ether
+device uart
+device pl011
+device pl190
+
+device pty
+device snp
+
+device pci
+
+# SCSI Controllers
+device sym # NCR/Symbios/LSI Logic 53C8XX/53C1010/53C1510D
+
+# ATA/SCSI peripherals
+device scbus # SCSI bus (required for ATA/SCSI)
+device da # Direct Access (disks)
+device pass # Passthrough device (direct ATA/SCSI access)
+
+# NOTE: serial console is disabled if syscons enabled
+# Comment following lines for headless setup
+device sc
+device kbdmux
+options SC_DFLT_FONT # compile font in
+makeoptions SC_DFLT_FONT=cp437
+
+device md
+
+options PLATFORM
+
+# Flattened Device Tree
+options FDT # Configure using FDT/DTB data
+options FDT_DTB_STATIC
+makeoptions FDT_DTS_FILE=versatile-pb.dts
diff --git a/sys/arm/conf/VYBRID b/sys/arm/conf/VYBRID
new file mode 100644
index 000000000000..e7ded5f42d82
--- /dev/null
+++ b/sys/arm/conf/VYBRID
@@ -0,0 +1,110 @@
+#
+# Kernel configuration for Vybrid Family boards.
+#
+# For more information on this file, please read the config(5) manual page,
+# and/or the handbook section on Kernel Configuration Files:
+#
+# https://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html
+#
+# The handbook is also available locally in /usr/share/doc/handbook
+# if you've installed the doc distribution, otherwise always see the
+# FreeBSD World Wide Web server (https://www.FreeBSD.org/) for the
+# latest information.
+#
+# An exhaustive list of options and more detailed explanations of the
+# device lines is also present in the ../../conf/NOTES and NOTES files.
+# If you are in doubt as to the purpose or necessity of a line, check first
+# in NOTES.
+#
+# $FreeBSD$
+
+ident VYBRID
+include "std.armv7"
+include "../freescale/vybrid/std.vybrid"
+
+options SCHED_4BSD # 4BSD scheduler
+options PLATFORM # Platform based SoC
+#options SMP # Enable multiple cores
+
+# NFS root from boopt/dhcp
+#options BOOTP
+#options BOOTP_NFSROOT
+#options BOOTP_COMPAT
+#options BOOTP_NFSV3
+#options BOOTP_WIRED_TO=ffec0
+
+#options ROOTDEVNAME=\"nfs:10.5.0.1:/tftpboot/cosmic\"
+options ROOTDEVNAME=\"ufs:/dev/da0\"
+
+options MUTEX_NOINLINE
+options RWLOCK_NOINLINE
+options NO_FFS_SNAPSHOT
+options NO_SWAPPING
+
+# Interrupt controller
+device gic
+
+# ARM MPCore timer
+device mpcore_timer
+
+# MMC/SD/SDIO Card slot support
+device mmc # mmc/sd bus
+device mmcsd # mmc/sd flash cards
+device sdhci # generic sdhci
+
+# Pseudo devices
+
+device loop
+device pty
+device md
+device gpio
+
+# USB support
+options USB_HOST_ALIGN=32 # Align usb buffers to cache line size.
+device usb
+#device musb
+device ehci
+#device ohci
+
+device umass
+device scbus # SCSI bus (required for ATA/SCSI)
+device da # Direct Access (disks)
+device pass
+
+# SATA
+#device ata
+#device atadisk
+#device mvs
+
+# Serial ports
+device uart
+
+# I2C (TWSI)
+device iic
+device iicbus
+
+# Ethernet
+device ether
+device ffec
+
+# USB ethernet support, requires miibus
+device miibus
+device axe # ASIX Electronics USB Ethernet
+device bpf # Berkeley packet filter
+
+device sound
+
+# SPI
+device spibus
+device vf_spi
+
+# Framebuffer
+device vt
+device kbdmux
+device ukbd
+
+# HID support
+device hid # Generic HID support
+
+# Flattened Device Tree
+options FDT # Configure using FDT/DTB data
diff --git a/sys/arm/conf/ZEDBOARD b/sys/arm/conf/ZEDBOARD
new file mode 100644
index 000000000000..24490a5129e5
--- /dev/null
+++ b/sys/arm/conf/ZEDBOARD
@@ -0,0 +1,85 @@
+#
+# ZEDBOARD -- Custom configuration for the Xilinx Zynq-7000 based
+# ZedBoard (www.zedboard.org) and similar Zynq boards.
+#
+# For more information on this file, please read the config(5) manual page,
+# and/or the handbook section on Kernel Configuration Files:
+#
+# https://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html
+#
+# The handbook is also available locally in /usr/share/doc/handbook
+# if you've installed the doc distribution, otherwise always see the
+# FreeBSD World Wide Web server (https://www.FreeBSD.org/) for the
+# latest information.
+#
+# An exhaustive list of options and more detailed explanations of the
+# device lines is also present in the ../../conf/NOTES and NOTES files.
+# If you are in doubt as to the purpose or necessity of a line, check first
+# in NOTES.
+#
+# $FreeBSD$
+
+ident ZEDBOARD
+
+include "std.armv7"
+include "../xilinx/std.zynq7"
+
+makeoptions MODULES_EXTRA="dtb/zynq"
+
+options SCHED_ULE # ULE scheduler
+options PLATFORM # Platform based SoC
+#options NFSSD # Network Filesystem Server
+options SMP # Enable multiple cores
+
+# NFS root from boopt/dhcp
+#options BOOTP
+#options BOOTP_NFSROOT
+#options BOOTP_COMPAT
+#options BOOTP_NFSV3
+
+options ROOTDEVNAME=\"ufs:mmcsd0s2a\"
+
+# Interrupt controller
+device gic
+
+# Cache controller
+device pl310 # PL310 L2 cache controller
+# ARM MPCore timer
+device mpcore_timer
+
+device loop
+device ether
+device cgem # Cadence GEM Gigabit Ethernet device
+device mii
+device e1000phy
+device rgephy # Zybo uses Realtek RTL8211E
+device pty
+device uart
+device gpio
+
+device spibus
+device spigen
+device mx25l
+device zy7_qspi # Xilinx Zynq QSPI controller
+device zy7_spi # Xilinx Zynq SPI controller
+
+device md
+device mmc # mmc/sd bus
+device mmcsd # mmc/sd flash cards
+device sdhci # generic sdhci
+device bpf # Berkeley packet filter
+
+# USB support
+device usb
+device ehci
+device umass
+device scbus # SCSI bus (required for ATA/SCSI)
+device da # Direct Access (disks)
+device axe # USB-Ethernet
+
+
+# Flattened Device Tree
+options FDT # Configure using FDT/DTB data
+#options FDT_DTB_STATIC
+#makeoptions FDT_DTS_FILE=zedboard.dts
+
diff --git a/sys/arm/conf/std.armv6 b/sys/arm/conf/std.armv6
new file mode 100644
index 000000000000..6db6c7a16dd7
--- /dev/null
+++ b/sys/arm/conf/std.armv6
@@ -0,0 +1,86 @@
+# Standard kernel config items for all ARMv6 systems.
+#
+# $FreeBSD$
+
+options HZ=1000
+options INTRNG # All arm systems use INTRNG these days
+options PREEMPTION # Enable kernel thread preemption
+options VIMAGE # Subsystem virtualization, e.g. VNET
+options INET # InterNETworking
+options INET6 # IPv6 communications protocols
+options TCP_HHOOK # hhook(9) framework for TCP
+device crypto # core crypto support
+options IPSEC_SUPPORT # Allow kldload of ipsec and tcpmd5
+options SCTP_SUPPORT # Allow kldload of SCTP
+options FFS # Berkeley Fast Filesystem
+options SOFTUPDATES # Enable FFS soft updates support
+options UFS_ACL # Support for access control lists
+options UFS_DIRHASH # Improve performance on big directories
+options UFS_GJOURNAL # Enable gjournal-based UFS journaling
+options QUOTA # Enable disk quotas for UFS
+options NFSCL # Network Filesystem Client
+options NFSLOCKD # Network Lock Manager
+options NFS_ROOT # NFS usable as /, requires NFSCL
+options MSDOSFS # MSDOS Filesystem
+options CD9660 # ISO 9660 Filesystem
+options PROCFS # Process filesystem (requires PSEUDOFS)
+options PSEUDOFS # Pseudo-filesystem framework
+options TMPFS # Efficient memory filesystem
+options GEOM_PART_GPT # GUID Partition Tables
+options GEOM_PART_BSD # BSD partition scheme
+options GEOM_PART_MBR # MBR partition scheme
+options GEOM_LABEL # Provides labelization
+options COMPAT_43 # Compatible with BSD 4.3 [KEEP THIS!]
+options SCSI_DELAY=5000 # Delay (in ms) before probing SCSI
+options KTRACE # ktrace(1) support
+options STACK # stack(9) support
+options SYSVSHM # SYSV-style shared memory
+options SYSVMSG # SYSV-style message queues
+options SYSVSEM # SYSV-style semaphores
+options _KPOSIX_PRIORITY_SCHEDULING # POSIX P1003_1B real-time extensions
+options PRINTF_BUFR_SIZE=128 # Prevent printf output being interspersed.
+options KBD_INSTALL_CDEV # install a CDEV entry in /dev
+options HWPMC_HOOKS # Necessary kernel hooks for hwpmc(4)
+options CAPABILITY_MODE # Capsicum capability mode
+options CAPABILITIES # Capsicum capabilites
+options FREEBSD_BOOT_LOADER # Process metadata passed from loader(8)
+options VFP # Enable floating point hardware support
+options MAC # Support for Mandatory Access Control (MAC)
+
+options COMPAT_FREEBSD10 # Compatible with FreeBSD10
+options COMPAT_FREEBSD11 # Compatible with FreeBSD11
+options COMPAT_FREEBSD12 # Compatible with FreeBSD12
+
+# DTrace support
+options KDTRACE_HOOKS # Kernel DTrace hooks
+options DDB_CTF # all architectures - kernel ELF linker loads CTF data
+makeoptions WITH_CTF=1
+
+# Debugging support. Always need this:
+makeoptions DEBUG=-g # Build kernel with gdb(1) debug symbols
+options KDB # Enable kernel debugger support.
+options KDB_TRACE # Print a stack trace for a panic.
+
+# For full debugger support use (turn off in stable branch):
+options DDB # Support DDB
+#options DEADLKRES # Enable the deadlock resolver
+options INVARIANTS # Enable calls of extra sanity checking
+options INVARIANT_SUPPORT # Extra sanity checks of internal structures, required by INVARIANTS
+options WITNESS # Enable checks to detect deadlocks and cycles
+options WITNESS_SKIPSPIN # Don't run witness on spinlocks for speed
+options MALLOC_DEBUG_MAXZONES=8 # Separate malloc(9) zones
+options ALT_BREAK_TO_DEBUGGER # Enter debugger on keyboard escape sequence
+options USB_DEBUG # Enable usb debug support code
+options VERBOSE_SYSINIT=0 # Support debug.verbose_sysinit, off by default
+
+# Optional extras, never enabled by default:
+#options BOOTVERBOSE
+#options DEBUG # May result in extreme spewage
+#options KTR
+#options KTR_COMPILE=KTR_ALL
+#options KTR_ENTRIES=16384
+#options KTR_MASK=(KTR_SPARE2)
+#options KTR_VERBOSE=0
+#options USB_REQ_DEBUG
+#options USB_VERBOSE
+
diff --git a/sys/arm/conf/std.armv7 b/sys/arm/conf/std.armv7
new file mode 100644
index 000000000000..0b842ad1f4e4
--- /dev/null
+++ b/sys/arm/conf/std.armv7
@@ -0,0 +1,85 @@
+# Standard kernel config items for all ARMv7 systems.
+#
+# $FreeBSD$
+
+options HZ=1000
+options INTRNG # All arm systems use INTRNG these days
+options PREEMPTION # Enable kernel thread preemption
+options VIMAGE # Subsystem virtualization, e.g. VNET
+options INET # InterNETworking
+options INET6 # IPv6 communications protocols
+options TCP_HHOOK # hhook(9) framework for TCP
+device crypto # core crypto support
+options IPSEC_SUPPORT # Allow kldload of ipsec and tcpmd5
+options SCTP_SUPPORT # Allow kldload of SCTP
+options FFS # Berkeley Fast Filesystem
+options SOFTUPDATES # Enable FFS soft updates support
+options UFS_ACL # Support for access control lists
+options UFS_DIRHASH # Improve performance on big directories
+options UFS_GJOURNAL # Enable gjournal-based UFS journaling
+options QUOTA # Enable disk quotas for UFS
+options NFSCL # Network Filesystem Client
+options NFSLOCKD # Network Lock Manager
+options NFS_ROOT # NFS usable as /, requires NFSCL
+options MSDOSFS # MSDOS Filesystem
+options CD9660 # ISO 9660 Filesystem
+options PROCFS # Process filesystem (requires PSEUDOFS)
+options PSEUDOFS # Pseudo-filesystem framework
+options TMPFS # Efficient memory filesystem
+options GEOM_PART_GPT # GUID Partition Tables
+options GEOM_PART_BSD # BSD partition scheme
+options GEOM_PART_MBR # MBR partition scheme
+options GEOM_LABEL # Provides labelization
+options COMPAT_43 # Compatible with BSD 4.3 [KEEP THIS!]
+options SCSI_DELAY=5000 # Delay (in ms) before probing SCSI
+options KTRACE # ktrace(1) support
+options STACK # stack(9) support
+options SYSVSHM # SYSV-style shared memory
+options SYSVMSG # SYSV-style message queues
+options SYSVSEM # SYSV-style semaphores
+options _KPOSIX_PRIORITY_SCHEDULING # POSIX P1003_1B real-time extensions
+options PRINTF_BUFR_SIZE=128 # Prevent printf output being interspersed.
+options KBD_INSTALL_CDEV # install a CDEV entry in /dev
+options HWPMC_HOOKS # Necessary kernel hooks for hwpmc(4)
+options CAPABILITY_MODE # Capsicum capability mode
+options CAPABILITIES # Capsicum capabilites
+options FREEBSD_BOOT_LOADER # Process metadata passed from loader(8)
+options VFP # Enable floating point hardware support
+options MAC # Support for Mandatory Access Control (MAC)
+
+options COMPAT_FREEBSD10 # Compatible with FreeBSD10
+options COMPAT_FREEBSD11 # Compatible with FreeBSD11
+options COMPAT_FREEBSD12 # Compatible with FreeBSD12
+
+# DTrace support
+options KDTRACE_HOOKS # Kernel DTrace hooks
+options DDB_CTF # all architectures - kernel ELF linker loads CTF data
+makeoptions WITH_CTF=1
+
+# Debugging support. Always need this:
+makeoptions DEBUG=-g # Build kernel with gdb(1) debug symbols
+options KDB # Enable kernel debugger support.
+options KDB_TRACE # Print a stack trace for a panic.
+
+# For full debugger support use (turn off in stable branch):
+options DDB # Support DDB
+#options DEADLKRES # Enable the deadlock resolver
+options INVARIANTS # Enable calls of extra sanity checking
+options INVARIANT_SUPPORT # Extra sanity checks of internal structures, required by INVARIANTS
+options WITNESS # Enable checks to detect deadlocks and cycles
+options WITNESS_SKIPSPIN # Don't run witness on spinlocks for speed
+options MALLOC_DEBUG_MAXZONES=8 # Separate malloc(9) zones
+options ALT_BREAK_TO_DEBUGGER # Enter debugger on keyboard escape sequence
+options USB_DEBUG # Enable usb debug support code
+options VERBOSE_SYSINIT=0 # Support debug.verbose_sysinit, off by default
+
+# Optional extras, never enabled by default:
+#options BOOTVERBOSE
+#options DEBUG # May result in extreme spewage
+#options KTR
+#options KTR_COMPILE=KTR_ALL
+#options KTR_ENTRIES=16384
+#options KTR_MASK=(KTR_SPARE2)
+#options KTR_VERBOSE=0
+#options USB_REQ_DEBUG
+#options USB_VERBOSE
diff --git a/sys/arm/freescale/fsl_ocotp.c b/sys/arm/freescale/fsl_ocotp.c
new file mode 100644
index 000000000000..ddfeedb5e15a
--- /dev/null
+++ b/sys/arm/freescale/fsl_ocotp.c
@@ -0,0 +1,206 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2014 Steven Lawrance <stl@koffein.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Access to the Freescale i.MX6 On-Chip One-Time-Programmable Memory
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+
+#include <arm/freescale/fsl_ocotpreg.h>
+#include <arm/freescale/fsl_ocotpvar.h>
+
+/*
+ * Find the physical address and size of the ocotp registers and devmap them,
+ * returning a pointer to the virtual address of the base.
+ *
+ * XXX This is temporary until we've worked out all the details of controlling
+ * the load order of devices. In an ideal world this device would be up and
+ * running before anything that needs it. When we're at a point to make that
+ * happen, this little block of code, and the few lines in fsl_ocotp_read_4()
+ * that refer to it can be deleted.
+ */
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <dev/fdt/fdt_common.h>
+#include <sys/devmap.h>
+
+static uint32_t *ocotp_regs;
+static vm_size_t ocotp_size;
+
+static void
+fsl_ocotp_devmap(void)
+{
+ phandle_t child, root;
+ u_long base, size;
+
+ if ((root = OF_finddevice("/")) == -1)
+ goto fatal;
+ if ((child = fdt_depth_search_compatible(root, "fsl,imx6q-ocotp", 0)) == 0)
+ goto fatal;
+ if (fdt_regsize(child, &base, &size) != 0)
+ goto fatal;
+
+ ocotp_size = (vm_size_t)size;
+
+ if ((ocotp_regs = pmap_mapdev((vm_offset_t)base, ocotp_size)) == NULL)
+ goto fatal;
+
+ return;
+fatal:
+ panic("cannot find/map the ocotp registers");
+}
+/* XXX end of temporary code */
+
+struct ocotp_softc {
+ device_t dev;
+ struct resource *mem_res;
+};
+
+static struct ocotp_softc *ocotp_sc;
+
+static inline uint32_t
+RD4(struct ocotp_softc *sc, bus_size_t off)
+{
+
+ return (bus_read_4(sc->mem_res, off));
+}
+
+static int
+ocotp_detach(device_t dev)
+{
+
+ /* The ocotp registers are always accessible. */
+ return (EBUSY);
+}
+
+static int
+ocotp_attach(device_t dev)
+{
+ struct ocotp_softc *sc;
+ int err, rid;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ /* Allocate bus_space resources. */
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "Cannot allocate memory resources\n");
+ err = ENXIO;
+ goto out;
+ }
+
+ ocotp_sc = sc;
+
+ /* We're done with the temporary mapping now. */
+ if (ocotp_regs != NULL)
+ pmap_unmapdev((vm_offset_t)ocotp_regs, ocotp_size);
+
+ err = 0;
+
+out:
+ if (err != 0)
+ ocotp_detach(dev);
+
+ return (err);
+}
+
+static int
+ocotp_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_is_compatible(dev, "fsl,imx6q-ocotp") == 0)
+ return (ENXIO);
+
+ device_set_desc(dev,
+ "Freescale On-Chip One-Time-Programmable Memory");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+uint32_t
+fsl_ocotp_read_4(bus_size_t off)
+{
+
+ if (off > FSL_OCOTP_LAST_REG)
+ panic("fsl_ocotp_read_4: offset out of range");
+
+ /* If we have a softcontext use the regular bus_space read. */
+ if (ocotp_sc != NULL)
+ return (RD4(ocotp_sc, off));
+
+ /*
+ * Otherwise establish a tempory device mapping if necessary, and read
+ * the device without any help from bus_space.
+ *
+ * XXX Eventually the code from there down can be deleted.
+ */
+ if (ocotp_regs == NULL)
+ fsl_ocotp_devmap();
+
+ return (ocotp_regs[off / 4]);
+}
+
+static device_method_t ocotp_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ocotp_probe),
+ DEVMETHOD(device_attach, ocotp_attach),
+ DEVMETHOD(device_detach, ocotp_detach),
+
+ DEVMETHOD_END
+};
+
+static driver_t ocotp_driver = {
+ "ocotp",
+ ocotp_methods,
+ sizeof(struct ocotp_softc)
+};
+
+static devclass_t ocotp_devclass;
+
+EARLY_DRIVER_MODULE(ocotp, simplebus, ocotp_driver, ocotp_devclass, 0, 0,
+ BUS_PASS_CPU + BUS_PASS_ORDER_FIRST);
diff --git a/sys/arm/freescale/fsl_ocotpreg.h b/sys/arm/freescale/fsl_ocotpreg.h
new file mode 100644
index 000000000000..80e3d3e5644f
--- /dev/null
+++ b/sys/arm/freescale/fsl_ocotpreg.h
@@ -0,0 +1,90 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2014 Steven Lawrance <stl@koffein.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef FSL_OCOTPREG_H
+#define FSL_OCOTPREG_H
+
+#define FSL_OCOTP_CTRL 0x000
+#define FSL_OCOTP_CTRL_SET 0x004
+#define FSL_OCOTP_CTRL_CLR 0x008
+#define FSL_OCOTP_CTRL_TOG 0x00C
+#define FSL_OCOTP_TIMING 0x010
+#define FSL_OCOTP_DATA 0x020
+#define FSL_OCOTP_READ_CTRL 0x030
+#define FSL_OCOTP_READ_FUSE_DATA 0x040
+#define FSL_OCOTP_SW_STICKY 0x050
+#define FSL_OCOTP_SCS 0x060
+#define FSL_OCOTP_SCS_SET 0x064
+#define FSL_OCOTP_SCS_CLR 0x068
+#define FSL_OCOTP_SCS_TOG 0x06C
+#define FSL_OCOTP_VERSION 0x090
+#define FSL_OCOTP_LOCK 0x400
+#define FSL_OCOTP_CFG0 0x410
+#define FSL_OCOTP_CFG1 0x420
+#define FSL_OCOTP_CFG2 0x430
+#define FSL_OCOTP_CFG3 0x440
+#define FSL_OCOTP_CFG3_SPEED_SHIFT 16
+#define FSL_OCOTP_CFG3_SPEED_MASK \
+ (0x03 << FSL_OCOTP_CFG3_SPEED_SHIFT)
+#define FSL_OCOTP_CFG3_SPEED_792MHZ 0
+#define FSL_OCOTP_CFG3_SPEED_852MHZ 1
+#define FSL_OCOTP_CFG3_SPEED_996MHZ 2
+#define FSL_OCOTP_CFG3_SPEED_1200MHZ 3
+#define FSL_OCOTP_CFG4 0x450
+#define FSL_OCOTP_CFG5 0x460
+#define FSL_OCOTP_CFG6 0x470
+#define FSL_OCOTP_MEM0 0x480
+#define FSL_OCOTP_MEM1 0x490
+#define FSL_OCOTP_MEM2 0x4A0
+#define FSL_OCOTP_MEM3 0x4B0
+#define FSL_OCOTP_ANA0 0x4D0
+#define FSL_OCOTP_ANA1 0x4E0
+#define FSL_OCOTP_ANA2 0x4F0
+#define FSL_OCOTP_SRK0 0x580
+#define FSL_OCOTP_SRK1 0x590
+#define FSL_OCOTP_SRK2 0x5A0
+#define FSL_OCOTP_SRK3 0x5B0
+#define FSL_OCOTP_SRK4 0x5C0
+#define FSL_OCOTP_SRK5 0x5D0
+#define FSL_OCOTP_SRK6 0x5E0
+#define FSL_OCOTP_SRK7 0x5F0
+#define FSL_OCOTP_HSJC_RESP0 0x600
+#define FSL_OCOTP_HSJC_RESP1 0x610
+#define FSL_OCOTP_MAC0 0x620
+#define FSL_OCOTP_MAC1 0x630
+#define FSL_OCOTP_GP1 0x660
+#define FSL_OCOTP_GP2 0x670
+#define FSL_OCOTP_MISC_CONF 0x6D0
+#define FSL_OCOTP_FIELD_RETURN 0x6E0
+#define FSL_OCOTP_SRK_REVOKE 0x6F0
+
+#define FSL_OCOTP_LAST_REG FSL_OCOTP_SRK_REVOKE
+
+#endif
diff --git a/sys/arm/freescale/fsl_ocotpvar.h b/sys/arm/freescale/fsl_ocotpvar.h
new file mode 100644
index 000000000000..ca7dd13e8c54
--- /dev/null
+++ b/sys/arm/freescale/fsl_ocotpvar.h
@@ -0,0 +1,36 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2014 Steven Lawrance <stl@koffein.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef FSL_OCOTPVAR_H
+#define FSL_OCOTPVAR_H
+
+uint32_t fsl_ocotp_read_4(bus_size_t _offset);
+
+#endif
diff --git a/sys/arm/freescale/imx/files.imx5 b/sys/arm/freescale/imx/files.imx5
new file mode 100644
index 000000000000..4ede403b5ae2
--- /dev/null
+++ b/sys/arm/freescale/imx/files.imx5
@@ -0,0 +1,55 @@
+# $FreeBSD$
+
+# Init
+arm/freescale/imx/imx_machdep.c standard
+arm/freescale/imx/imx51_machdep.c optional soc_imx51
+arm/freescale/imx/imx53_machdep.c optional soc_imx53
+
+# Special serial console for debuging early boot code
+#arm/freescale/imx/imx_console.c standard
+
+# UART driver (includes serial console support)
+dev/uart/uart_dev_imx.c optional uart
+
+# TrustZone Interrupt Controller
+arm/freescale/imx/tzic.c standard
+
+# IOMUX - external pins multiplexor
+arm/freescale/imx/imx_iomux.c standard
+
+# GPIO
+arm/freescale/imx/imx_gpio.c optional gpio
+
+# Generic Periodic Timer
+arm/freescale/imx/imx_gpt.c standard
+
+# Clock Configuration Manager
+arm/freescale/imx/imx51_ccm.c standard
+
+# i.MX5xx PATA controller
+dev/ata/chipsets/ata-fsl.c optional imxata
+
+# SDHCI/MMC
+dev/sdhci/fsl_sdhci.c optional sdhci
+
+# USB OH3 controller (1 OTG, 3 EHCI)
+arm/freescale/imx/imx_nop_usbphy.c optional ehci
+dev/usb/controller/ehci_imx.c optional ehci
+
+# Watchdog
+arm/freescale/imx/imx_wdog.c optional imxwdt
+
+# i2c
+arm/freescale/imx/imx_i2c.c optional fsliic
+
+# IPU - Image Processing Unit (frame buffer also)
+arm/freescale/imx/imx51_ipuv3.c optional sc
+arm/freescale/imx/imx51_ipuv3_fbd.c optional vt
+dev/vt/hw/fb/vt_early_fb.c optional vt
+
+# Fast Ethernet Controller
+dev/ffec/if_ffec.c optional ffec
+
+# SPI
+arm/freescale/imx/imx_spi.c optional imx_spi
+
diff --git a/sys/arm/freescale/imx/files.imx6 b/sys/arm/freescale/imx/files.imx6
new file mode 100644
index 000000000000..0b71e1e6bf6a
--- /dev/null
+++ b/sys/arm/freescale/imx/files.imx6
@@ -0,0 +1,64 @@
+# $FreeBSD$
+
+#
+# Standard imx6 devices and support.
+#
+arm/freescale/fsl_ocotp.c standard
+arm/freescale/imx/imx6_anatop.c standard
+arm/freescale/imx/imx6_ccm.c standard
+arm/freescale/imx/imx6_machdep.c standard
+arm/freescale/imx/imx6_mp.c optional smp
+arm/freescale/imx/imx6_pl310.c standard
+arm/freescale/imx/imx6_snvs.c optional imx6_snvs
+arm/freescale/imx/imx6_src.c standard
+arm/freescale/imx/imx_epit.c standard
+arm/freescale/imx/imx_iomux.c standard
+arm/freescale/imx/imx_machdep.c standard
+arm/freescale/imx/imx_gpt.c optional imx_gpt
+arm/freescale/imx/imx_gpio.c optional gpio
+arm/freescale/imx/imx_i2c.c optional fsliic
+arm/freescale/imx/imx_spi.c optional imx_spi
+arm/freescale/imx/imx6_sdma.c optional fslsdma
+arm/freescale/imx/imx6_audmux.c optional sound
+arm/freescale/imx/imx6_ssi.c optional sound fslsdma
+arm/freescale/imx/imx6_ahci.c optional ahci
+
+dev/hdmi/dwc_hdmi.c optional hdmi
+arm/freescale/imx/imx6_hdmi.c optional hdmi
+
+arm/freescale/imx/imx6_ipu.c optional vt
+
+#
+# Optional devices.
+#
+dev/sdhci/fsl_sdhci.c optional sdhci
+
+arm/freescale/imx/imx_wdog.c optional imxwdt
+
+dev/ffec/if_ffec.c optional ffec
+
+dev/uart/uart_dev_imx.c optional uart
+
+dev/usb/controller/ehci_imx.c optional ehci
+arm/freescale/imx/imx6_usbphy.c optional ehci
+
+#
+# Low-level serial console for debugging early kernel startup.
+#
+#arm/freescale/imx/imx_console.c standard
+
+# SDMA firmware.
+sdma-imx6q.c optional fslsdma \
+ compile-with "${AWK} -f $S/tools/fw_stub.awk sdma-imx6q.bin:sdma-imx6q -msdma -c${.TARGET}" \
+ no-implicit-rule before-depend local \
+ clean "sdma-imx6q.c"
+sdma-imx6q.fwo optional fslsdma \
+ dependency "sdma-imx6q.bin" \
+ compile-with "${LD} -m ${LD_EMULATION} -b binary -d -warn-common -r -d -o ${.TARGET} sdma-imx6q.bin" \
+ no-implicit-rule \
+ clean "sdma-imx6q.fwo"
+sdma-imx6q.bin optional fslsdma \
+ dependency "$S/contrib/dev/imx/sdma-imx6q.bin.uu" \
+ compile-with "uudecode < $S/contrib/dev/imx/sdma-imx6q.bin.uu" \
+ no-obj no-implicit-rule \
+ clean "sdma-imx6q.bin"
diff --git a/sys/arm/freescale/imx/imx51_ccm.c b/sys/arm/freescale/imx/imx51_ccm.c
new file mode 100644
index 000000000000..3db5c982b59a
--- /dev/null
+++ b/sys/arm/freescale/imx/imx51_ccm.c
@@ -0,0 +1,661 @@
+/* $NetBSD: imx51_ccm.c,v 1.1 2012/04/17 09:33:31 bsh Exp $ */
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause AND BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2010, 2011, 2012 Genetec Corporation. All rights reserved.
+ * Written by Hashimoto Kenichi for Genetec Corporation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GENETEC CORPORATION ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GENETEC CORPORATION
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) 2012, 2013 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * Portions of this software were developed by Oleksandr Rybalko
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Clock Controller Module (CCM)
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+
+#include <arm/freescale/imx/imx51_ccmvar.h>
+#include <arm/freescale/imx/imx51_ccmreg.h>
+#include <arm/freescale/imx/imx51_dpllreg.h>
+#include <arm/freescale/imx/imx_ccmvar.h>
+#include <arm/freescale/imx/imx_machdep.h>
+
+#define IMXCCMDEBUG
+#undef IMXCCMDEBUG
+
+#ifndef IMX51_OSC_FREQ
+#define IMX51_OSC_FREQ (24 * 1000 * 1000) /* 24MHz */
+#endif
+
+#ifndef IMX51_CKIL_FREQ
+#define IMX51_CKIL_FREQ 32768
+#endif
+
+/*
+ * The fdt data does not provide reg properties describing the DPLL register
+ * blocks we need to access, presumably because the needed addresses are
+ * hard-coded within the linux driver. That leaves us with no choice but to do
+ * the same thing, if we want to run with vendor-supplied fdt data. So here we
+ * have tables of the physical addresses we need for each soc, and we'll use
+ * bus_space_map() at attach() time to get access to them.
+ */
+static uint32_t imx51_dpll_addrs[IMX51_N_DPLLS] = {
+ 0x83f80000, /* DPLL1 */
+ 0x83f84000, /* DPLL2 */
+ 0x83f88000, /* DPLL3 */
+};
+
+static uint32_t imx53_dpll_addrs[IMX51_N_DPLLS] = {
+ 0x63f80000, /* DPLL1 */
+ 0x63f84000, /* DPLL2 */
+ 0x63f88000, /* DPLL3 */
+};
+
+#define DPLL_REGS_SZ (16 * 1024)
+
+struct imxccm_softc {
+ device_t sc_dev;
+ struct resource *ccmregs;
+ u_int64_t pll_freq[IMX51_N_DPLLS];
+ bus_space_tag_t pllbst;
+ bus_space_handle_t pllbsh[IMX51_N_DPLLS];
+};
+
+struct imxccm_softc *ccm_softc = NULL;
+
+static uint64_t imx51_get_pll_freq(u_int);
+
+static int imxccm_match(device_t);
+static int imxccm_attach(device_t);
+
+static device_method_t imxccm_methods[] = {
+ DEVMETHOD(device_probe, imxccm_match),
+ DEVMETHOD(device_attach, imxccm_attach),
+
+ DEVMETHOD_END
+};
+
+static driver_t imxccm_driver = {
+ "imxccm",
+ imxccm_methods,
+ sizeof(struct imxccm_softc),
+};
+
+static devclass_t imxccm_devclass;
+
+EARLY_DRIVER_MODULE(imxccm, simplebus, imxccm_driver, imxccm_devclass, 0, 0,
+ BUS_PASS_CPU);
+
+static inline uint32_t
+pll_read_4(struct imxccm_softc *sc, int pll, int reg)
+{
+
+ return (bus_space_read_4(sc->pllbst, sc->pllbsh[pll - 1], reg));
+}
+
+static inline uint32_t
+ccm_read_4(struct imxccm_softc *sc, int reg)
+{
+
+ return (bus_read_4(sc->ccmregs, reg));
+}
+
+static inline void
+ccm_write_4(struct imxccm_softc *sc, int reg, uint32_t val)
+{
+
+ bus_write_4(sc->ccmregs, reg, val);
+}
+
+static int
+imxccm_match(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "fsl,imx51-ccm") &&
+ !ofw_bus_is_compatible(dev, "fsl,imx53-ccm"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Freescale Clock Control Module");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+imxccm_attach(device_t dev)
+{
+ struct imxccm_softc *sc;
+ int idx;
+ u_int soc;
+ uint32_t *pll_addrs;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+
+ switch ((soc = imx_soc_type())) {
+ case IMXSOC_51:
+ pll_addrs = imx51_dpll_addrs;
+ break;
+ case IMXSOC_53:
+ pll_addrs = imx53_dpll_addrs;
+ break;
+ default:
+ device_printf(dev, "No support for SoC type 0x%08x\n", soc);
+ goto noclocks;
+ }
+
+ idx = 0;
+ sc->ccmregs = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &idx,
+ RF_ACTIVE);
+ if (sc->ccmregs == NULL) {
+ device_printf(dev, "could not allocate resources\n");
+ goto noclocks;
+ }
+
+ sc->pllbst = fdtbus_bs_tag;
+ for (idx = 0; idx < IMX51_N_DPLLS; ++idx) {
+ if (bus_space_map(sc->pllbst, pll_addrs[idx], DPLL_REGS_SZ, 0,
+ &sc->pllbsh[idx]) != 0) {
+ device_printf(dev, "Cannot map DPLL registers\n");
+ goto noclocks;
+ }
+ }
+
+ ccm_softc = sc;
+
+ imx51_get_pll_freq(1);
+ imx51_get_pll_freq(2);
+ imx51_get_pll_freq(3);
+
+ device_printf(dev, "PLL1=%lluMHz, PLL2=%lluMHz, PLL3=%lluMHz\n",
+ sc->pll_freq[0] / 1000000,
+ sc->pll_freq[1] / 1000000,
+ sc->pll_freq[2] / 1000000);
+ device_printf(dev, "CPU clock=%d, UART clock=%d\n",
+ imx51_get_clock(IMX51CLK_ARM_ROOT),
+ imx51_get_clock(IMX51CLK_UART_CLK_ROOT));
+ device_printf(dev,
+ "mainbus clock=%d, ahb clock=%d ipg clock=%d perclk=%d\n",
+ imx51_get_clock(IMX51CLK_MAIN_BUS_CLK),
+ imx51_get_clock(IMX51CLK_AHB_CLK_ROOT),
+ imx51_get_clock(IMX51CLK_IPG_CLK_ROOT),
+ imx51_get_clock(IMX51CLK_PERCLK_ROOT));
+
+ return (0);
+
+noclocks:
+
+ panic("Cannot continue without clock support");
+}
+
+u_int
+imx51_get_clock(enum imx51_clock clk)
+{
+ u_int freq;
+ u_int sel;
+ uint32_t cacrr; /* ARM clock root register */
+ uint32_t ccsr;
+ uint32_t cscdr1;
+ uint32_t cscmr1;
+ uint32_t cbcdr;
+ uint32_t cbcmr;
+ uint32_t cdcr;
+
+ if (ccm_softc == NULL)
+ return (0);
+
+ switch (clk) {
+ case IMX51CLK_PLL1:
+ case IMX51CLK_PLL2:
+ case IMX51CLK_PLL3:
+ return ccm_softc->pll_freq[clk-IMX51CLK_PLL1];
+ case IMX51CLK_PLL1SW:
+ ccsr = ccm_read_4(ccm_softc, CCMC_CCSR);
+ if ((ccsr & CCSR_PLL1_SW_CLK_SEL) == 0)
+ return ccm_softc->pll_freq[1-1];
+ /* step clock */
+ /* FALLTHROUGH */
+ case IMX51CLK_PLL1STEP:
+ ccsr = ccm_read_4(ccm_softc, CCMC_CCSR);
+ switch ((ccsr & CCSR_STEP_SEL_MASK) >> CCSR_STEP_SEL_SHIFT) {
+ case 0:
+ return imx51_get_clock(IMX51CLK_LP_APM);
+ case 1:
+ return 0; /* XXX PLL bypass clock */
+ case 2:
+ return ccm_softc->pll_freq[2-1] /
+ (1 + ((ccsr & CCSR_PLL2_DIV_PODF_MASK) >>
+ CCSR_PLL2_DIV_PODF_SHIFT));
+ case 3:
+ return ccm_softc->pll_freq[3-1] /
+ (1 + ((ccsr & CCSR_PLL3_DIV_PODF_MASK) >>
+ CCSR_PLL3_DIV_PODF_SHIFT));
+ }
+ /*NOTREACHED*/
+ case IMX51CLK_PLL2SW:
+ ccsr = ccm_read_4(ccm_softc, CCMC_CCSR);
+ if ((ccsr & CCSR_PLL2_SW_CLK_SEL) == 0)
+ return imx51_get_clock(IMX51CLK_PLL2);
+ return 0; /* XXX PLL2 bypass clk */
+ case IMX51CLK_PLL3SW:
+ ccsr = ccm_read_4(ccm_softc, CCMC_CCSR);
+ if ((ccsr & CCSR_PLL3_SW_CLK_SEL) == 0)
+ return imx51_get_clock(IMX51CLK_PLL3);
+ return 0; /* XXX PLL3 bypass clk */
+
+ case IMX51CLK_LP_APM:
+ ccsr = ccm_read_4(ccm_softc, CCMC_CCSR);
+ return (ccsr & CCSR_LP_APM) ?
+ imx51_get_clock(IMX51CLK_FPM) : IMX51_OSC_FREQ;
+
+ case IMX51CLK_ARM_ROOT:
+ freq = imx51_get_clock(IMX51CLK_PLL1SW);
+ cacrr = ccm_read_4(ccm_softc, CCMC_CACRR);
+ return freq / (cacrr + 1);
+
+ /* ... */
+ case IMX51CLK_MAIN_BUS_CLK_SRC:
+ cbcdr = ccm_read_4(ccm_softc, CCMC_CBCDR);
+ if ((cbcdr & CBCDR_PERIPH_CLK_SEL) == 0)
+ freq = imx51_get_clock(IMX51CLK_PLL2SW);
+ else {
+ freq = 0;
+ cbcmr = ccm_read_4(ccm_softc, CCMC_CBCMR);
+ switch ((cbcmr & CBCMR_PERIPH_APM_SEL_MASK) >>
+ CBCMR_PERIPH_APM_SEL_SHIFT) {
+ case 0:
+ freq = imx51_get_clock(IMX51CLK_PLL1SW);
+ break;
+ case 1:
+ freq = imx51_get_clock(IMX51CLK_PLL3SW);
+ break;
+ case 2:
+ freq = imx51_get_clock(IMX51CLK_LP_APM);
+ break;
+ case 3:
+ /* XXX: error */
+ break;
+ }
+ }
+ return freq;
+ case IMX51CLK_MAIN_BUS_CLK:
+ freq = imx51_get_clock(IMX51CLK_MAIN_BUS_CLK_SRC);
+ cdcr = ccm_read_4(ccm_softc, CCMC_CDCR);
+ return freq / (1 + ((cdcr & CDCR_PERIPH_CLK_DVFS_PODF_MASK) >>
+ CDCR_PERIPH_CLK_DVFS_PODF_SHIFT));
+ case IMX51CLK_AHB_CLK_ROOT:
+ freq = imx51_get_clock(IMX51CLK_MAIN_BUS_CLK);
+ cbcdr = ccm_read_4(ccm_softc, CCMC_CBCDR);
+ return freq / (1 + ((cbcdr & CBCDR_AHB_PODF_MASK) >>
+ CBCDR_AHB_PODF_SHIFT));
+ case IMX51CLK_IPG_CLK_ROOT:
+ freq = imx51_get_clock(IMX51CLK_AHB_CLK_ROOT);
+ cbcdr = ccm_read_4(ccm_softc, CCMC_CBCDR);
+ return freq / (1 + ((cbcdr & CBCDR_IPG_PODF_MASK) >>
+ CBCDR_IPG_PODF_SHIFT));
+
+ case IMX51CLK_PERCLK_ROOT:
+ cbcmr = ccm_read_4(ccm_softc, CCMC_CBCMR);
+ if (cbcmr & CBCMR_PERCLK_IPG_SEL)
+ return imx51_get_clock(IMX51CLK_IPG_CLK_ROOT);
+ if (cbcmr & CBCMR_PERCLK_LP_APM_SEL)
+ freq = imx51_get_clock(IMX51CLK_LP_APM);
+ else
+ freq = imx51_get_clock(IMX51CLK_MAIN_BUS_CLK_SRC);
+ cbcdr = ccm_read_4(ccm_softc, CCMC_CBCDR);
+
+#ifdef IMXCCMDEBUG
+ printf("cbcmr=%x cbcdr=%x\n", cbcmr, cbcdr);
+#endif
+
+ freq /= 1 + ((cbcdr & CBCDR_PERCLK_PRED1_MASK) >>
+ CBCDR_PERCLK_PRED1_SHIFT);
+ freq /= 1 + ((cbcdr & CBCDR_PERCLK_PRED2_MASK) >>
+ CBCDR_PERCLK_PRED2_SHIFT);
+ freq /= 1 + ((cbcdr & CBCDR_PERCLK_PODF_MASK) >>
+ CBCDR_PERCLK_PODF_SHIFT);
+ return freq;
+ case IMX51CLK_UART_CLK_ROOT:
+ cscdr1 = ccm_read_4(ccm_softc, CCMC_CSCDR1);
+ cscmr1 = ccm_read_4(ccm_softc, CCMC_CSCMR1);
+
+#ifdef IMXCCMDEBUG
+ printf("cscdr1=%x cscmr1=%x\n", cscdr1, cscmr1);
+#endif
+
+ sel = (cscmr1 & CSCMR1_UART_CLK_SEL_MASK) >>
+ CSCMR1_UART_CLK_SEL_SHIFT;
+
+ freq = 0; /* shut up GCC */
+ switch (sel) {
+ case 0:
+ case 1:
+ case 2:
+ freq = imx51_get_clock(IMX51CLK_PLL1SW + sel);
+ break;
+ case 3:
+ freq = imx51_get_clock(IMX51CLK_LP_APM);
+ break;
+ }
+
+ return freq / (1 + ((cscdr1 & CSCDR1_UART_CLK_PRED_MASK) >>
+ CSCDR1_UART_CLK_PRED_SHIFT)) /
+ (1 + ((cscdr1 & CSCDR1_UART_CLK_PODF_MASK) >>
+ CSCDR1_UART_CLK_PODF_SHIFT));
+ case IMX51CLK_IPU_HSP_CLK_ROOT:
+ freq = 0;
+ cbcmr = ccm_read_4(ccm_softc, CCMC_CBCMR);
+ switch ((cbcmr & CBCMR_IPU_HSP_CLK_SEL_MASK) >>
+ CBCMR_IPU_HSP_CLK_SEL_SHIFT) {
+ case 0:
+ freq = imx51_get_clock(IMX51CLK_ARM_AXI_A_CLK);
+ break;
+ case 1:
+ freq = imx51_get_clock(IMX51CLK_ARM_AXI_B_CLK);
+ break;
+ case 2:
+ freq = imx51_get_clock(
+ IMX51CLK_EMI_SLOW_CLK_ROOT);
+ break;
+ case 3:
+ freq = imx51_get_clock(IMX51CLK_AHB_CLK_ROOT);
+ break;
+ }
+ return freq;
+ default:
+ device_printf(ccm_softc->sc_dev,
+ "clock %d: not supported yet\n", clk);
+ return 0;
+ }
+}
+
+static uint64_t
+imx51_get_pll_freq(u_int pll_no)
+{
+ uint32_t dp_ctrl;
+ uint32_t dp_op;
+ uint32_t dp_mfd;
+ uint32_t dp_mfn;
+ uint32_t mfi;
+ int32_t mfn;
+ uint32_t mfd;
+ uint32_t pdf;
+ uint32_t ccr;
+ uint64_t freq = 0;
+ u_int ref = 0;
+
+ KASSERT(1 <= pll_no && pll_no <= IMX51_N_DPLLS, ("Wrong PLL id"));
+
+ dp_ctrl = pll_read_4(ccm_softc, pll_no, DPLL_DP_CTL);
+
+ if (dp_ctrl & DP_CTL_HFSM) {
+ dp_op = pll_read_4(ccm_softc, pll_no, DPLL_DP_HFS_OP);
+ dp_mfd = pll_read_4(ccm_softc, pll_no, DPLL_DP_HFS_MFD);
+ dp_mfn = pll_read_4(ccm_softc, pll_no, DPLL_DP_HFS_MFN);
+ } else {
+ dp_op = pll_read_4(ccm_softc, pll_no, DPLL_DP_OP);
+ dp_mfd = pll_read_4(ccm_softc, pll_no, DPLL_DP_MFD);
+ dp_mfn = pll_read_4(ccm_softc, pll_no, DPLL_DP_MFN);
+ }
+
+ pdf = dp_op & DP_OP_PDF_MASK;
+ mfi = max(5, (dp_op & DP_OP_MFI_MASK) >> DP_OP_MFI_SHIFT);
+ mfd = dp_mfd;
+ if (dp_mfn & 0x04000000)
+ /* 27bit signed value */
+ mfn = (uint32_t)(0xf8000000 | dp_mfn);
+ else
+ mfn = dp_mfn;
+
+ switch (dp_ctrl & DP_CTL_REF_CLK_SEL_MASK) {
+ case DP_CTL_REF_CLK_SEL_COSC:
+ /* Internal Oscillator */
+ /* TODO: get from FDT "fsl,imx-osc" */
+ ref = 24000000; /* IMX51_OSC_FREQ */
+ break;
+ case DP_CTL_REF_CLK_SEL_FPM:
+ ccr = ccm_read_4(ccm_softc, CCMC_CCR);
+ if (ccr & CCR_FPM_MULT)
+ /* TODO: get from FDT "fsl,imx-ckil" */
+ ref = 32768 * 1024;
+ else
+ /* TODO: get from FDT "fsl,imx-ckil" */
+ ref = 32768 * 512;
+ break;
+ default:
+ ref = 0;
+ }
+
+ if (dp_ctrl & DP_CTL_REF_CLK_DIV)
+ ref /= 2;
+
+ ref *= 4;
+ freq = (int64_t)ref * mfi + (int64_t)ref * mfn / (mfd + 1);
+ freq /= pdf + 1;
+
+ if (!(dp_ctrl & DP_CTL_DPDCK0_2_EN))
+ freq /= 2;
+
+#ifdef IMXCCMDEBUG
+ printf("ref: %dKHz ", ref);
+ printf("dp_ctl: %08x ", dp_ctrl);
+ printf("pdf: %3d ", pdf);
+ printf("mfi: %3d ", mfi);
+ printf("mfd: %3d ", mfd);
+ printf("mfn: %3d ", mfn);
+ printf("pll: %d\n", (uint32_t)freq);
+#endif
+
+ ccm_softc->pll_freq[pll_no-1] = freq;
+
+ return (freq);
+}
+
+void
+imx51_clk_gating(int clk_src, int mode)
+{
+ int field, group;
+ uint32_t reg;
+
+ group = CCMR_CCGR_MODULE(clk_src);
+ field = clk_src % CCMR_CCGR_NSOURCE;
+ reg = ccm_read_4(ccm_softc, CCMC_CCGR(group));
+ reg &= ~(0x03 << field * 2);
+ reg |= (mode << field * 2);
+ ccm_write_4(ccm_softc, CCMC_CCGR(group), reg);
+}
+
+int
+imx51_get_clk_gating(int clk_src)
+{
+ uint32_t reg;
+
+ reg = ccm_read_4(ccm_softc,
+ CCMC_CCGR(CCMR_CCGR_MODULE(clk_src)));
+ return ((reg >> (clk_src % CCMR_CCGR_NSOURCE) * 2) & 0x03);
+}
+
+/*
+ * Code from here down is temporary, in lieu of a SoC-independent clock API.
+ */
+
+void
+imx_ccm_usb_enable(device_t dev)
+{
+ uint32_t regval;
+
+ /*
+ * Select PLL2 as the source for the USB clock.
+ * The default is PLL3, but U-boot changes it to PLL2.
+ */
+ regval = ccm_read_4(ccm_softc, CCMC_CSCMR1);
+ regval &= ~CSCMR1_USBOH3_CLK_SEL_MASK;
+ regval |= 1 << CSCMR1_USBOH3_CLK_SEL_SHIFT;
+ ccm_write_4(ccm_softc, CCMC_CSCMR1, regval);
+
+ /*
+ * Set the USB clock pre-divider to div-by-5, post-divider to div-by-2.
+ */
+ regval = ccm_read_4(ccm_softc, CCMC_CSCDR1);
+ regval &= ~CSCDR1_USBOH3_CLK_PODF_MASK;
+ regval &= ~CSCDR1_USBOH3_CLK_PRED_MASK;
+ regval |= 4 << CSCDR1_USBOH3_CLK_PRED_SHIFT;
+ regval |= 1 << CSCDR1_USBOH3_CLK_PODF_SHIFT;
+ ccm_write_4(ccm_softc, CCMC_CSCDR1, regval);
+
+ /*
+ * The same two clocks gates are used on imx51 and imx53.
+ */
+ imx51_clk_gating(CCGR_USBOH3_IPG_AHB_CLK, CCGR_CLK_MODE_ALWAYS);
+ imx51_clk_gating(CCGR_USBOH3_60M_CLK, CCGR_CLK_MODE_ALWAYS);
+}
+
+void
+imx_ccm_usbphy_enable(device_t dev)
+{
+ uint32_t regval;
+
+ /*
+ * Select PLL3 as the source for the USBPHY clock. U-boot does this
+ * only for imx53, but the bit exists on imx51. That seems a bit
+ * strange, but we'll go with it until more is known.
+ */
+ if (imx_soc_type() == IMXSOC_53) {
+ regval = ccm_read_4(ccm_softc, CCMC_CSCMR1);
+ regval |= 1 << CSCMR1_USBPHY_CLK_SEL_SHIFT;
+ ccm_write_4(ccm_softc, CCMC_CSCMR1, regval);
+ }
+
+ /*
+ * For the imx51 there's just one phy gate control, enable it.
+ */
+ if (imx_soc_type() == IMXSOC_51) {
+ imx51_clk_gating(CCGR_USB_PHY_CLK, CCGR_CLK_MODE_ALWAYS);
+ return;
+ }
+
+ /*
+ * For imx53 we don't have a full set of clock defines yet, but the
+ * datasheet says:
+ * gate reg 4, bits 13-12 usb ph2 clock (usb_phy2_clk_enable)
+ * gate reg 4, bits 11-10 usb ph1 clock (usb_phy1_clk_enable)
+ *
+ * We should use the fdt data for the device to figure out which of
+ * the two we're working on, but for now just turn them both on.
+ */
+ if (imx_soc_type() == IMXSOC_53) {
+ imx51_clk_gating(__CCGR_NUM(4, 5), CCGR_CLK_MODE_ALWAYS);
+ imx51_clk_gating(__CCGR_NUM(4, 6), CCGR_CLK_MODE_ALWAYS);
+ return;
+ }
+}
+
+uint32_t
+imx_ccm_ecspi_hz(void)
+{
+
+ return (imx51_get_clock(IMX51CLK_CSPI_CLK_ROOT));
+}
+
+uint32_t
+imx_ccm_ipg_hz(void)
+{
+
+ return (imx51_get_clock(IMX51CLK_IPG_CLK_ROOT));
+}
+
+uint32_t
+imx_ccm_sdhci_hz(void)
+{
+
+ return (imx51_get_clock(IMX51CLK_ESDHC1_CLK_ROOT));
+}
+
+uint32_t
+imx_ccm_perclk_hz(void)
+{
+
+ return (imx51_get_clock(IMX51CLK_PERCLK_ROOT));
+}
+
+uint32_t
+imx_ccm_uart_hz(void)
+{
+
+ return (imx51_get_clock(IMX51CLK_UART_CLK_ROOT));
+}
+
+uint32_t
+imx_ccm_ahb_hz(void)
+{
+
+ return (imx51_get_clock(IMX51CLK_AHB_CLK_ROOT));
+}
diff --git a/sys/arm/freescale/imx/imx51_ccmreg.h b/sys/arm/freescale/imx/imx51_ccmreg.h
new file mode 100644
index 000000000000..f9c75f30f279
--- /dev/null
+++ b/sys/arm/freescale/imx/imx51_ccmreg.h
@@ -0,0 +1,259 @@
+/* $NetBSD: imx51_ccmreg.h,v 1.1 2012/04/17 09:33:31 bsh Exp $ */
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause AND BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2011, 2012 Genetec Corporation. All rights reserved.
+ * Written by Hashimoto Kenichi for Genetec Corporation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GENETEC CORPORATION ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GENETEC CORPORATION
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) 2012, 2013 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * Portions of this software were developed by Oleksandr Rybalko
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _IMX51_CCMREG_H
+#define _IMX51_CCMREG_H
+
+#include <sys/cdefs.h>
+
+/* register offset address */
+
+#define CCMC_BASE 0x73fd4000
+#define CCMC_CCR 0x0000
+#define CCR_FPM_MULT 0x00001000
+#define CCMC_CCDR 0x0004
+#define CCMC_CSR 0x0008
+#define CCMC_CCSR 0x000c
+#define CCSR_LP_APM 0x00000200
+#define CCSR_STEP_SEL_SHIFT 7
+#define CCSR_STEP_SEL_MASK 0x00000180
+#define CCSR_PLL2_DIV_PODF_SHIFT 5
+#define CCSR_PLL2_DIV_PODF_MASK 0x00000060
+#define CCSR_PLL3_DIV_PODF_SHIFT 3
+#define CCSR_PLL3_DIV_PODF_MASK 0x00000030
+#define CCSR_PLL1_SW_CLK_SEL 0x00000004
+#define CCSR_PLL2_SW_CLK_SEL 0x00000002
+#define CCSR_PLL3_SW_CLK_SEL 0x00000001
+#define CCMC_CACRR 0x0010
+#define CCMC_CBCDR 0x0014
+#define CBCDR_DDR_HIGH_FREQ_CLK_SEL 0x40000000
+#define CBCDR_DDR_CLK_PODF_SHIFT 27
+#define CBCDR_DDR_CLK_PODF_MASK 0x38000000
+#define CBCDR_EMI_CLK_SEL 0x04000000
+#define CBCDR_PERIPH_CLK_SEL 0x02000000
+#define CBCDR_EMI_SLOW_PODF_SHIFT 22
+#define CBCDR_EMI_SLOW_PODF_MASK 0x01c00000
+#define CBCDR_AXI_B_PODF_SHIFT 19
+#define CBCDR_AXI_B_PODF_MASK 0x00380000
+#define CBCDR_AXI_A_PODF_SHIFT 16
+#define CBCDR_AXI_A_PODF_MASK 0x1fff0000
+#define CBCDR_NFC_PODF_SHIFT 13
+#define CBCDR_NFC_PODF_MASK 0x00018000
+#define CBCDR_AHB_PODF_SHIFT 10
+#define CBCDR_AHB_PODF_MASK 0x00001c00
+#define CBCDR_IPG_PODF_SHIFT 8
+#define CBCDR_IPG_PODF_MASK 0x00000300
+#define CBCDR_PERCLK_PRED1_SHIFT 6
+#define CBCDR_PERCLK_PRED1_MASK 0x000000c0
+#define CBCDR_PERCLK_PRED2_SHIFT 3
+#define CBCDR_PERCLK_PRED2_MASK 0x00000038
+#define CBCDR_PERCLK_PODF_SHIFT 0
+#define CBCDR_PERCLK_PODF_MASK 0x00000007
+#define CCMC_CBCMR 0x0018
+#define CBCMR_PERIPH_APM_SEL_SHIFT 12
+#define CBCMR_PERIPH_APM_SEL_MASK 0x00003000
+#define CBCMR_IPU_HSP_CLK_SEL_SHIFT 6
+#define CBCMR_IPU_HSP_CLK_SEL_MASK 0x000000c0
+#define CBCMR_PERCLK_LP_APM_SEL 0x00000002
+#define CBCMR_PERCLK_IPG_SEL 0x00000001
+#define CCMC_CSCMR1 0x001c
+#define CSCMR1_UART_CLK_SEL_SHIFT 24
+#define CSCMR1_UART_CLK_SEL_MASK 0x03000000
+#define CSCMR1_USBPHY_CLK_SEL_SHIFT 26
+#define CSCMR1_USBPHY_CLK_SEL_MASK 0x04000000
+#define CSCMR1_USBOH3_CLK_SEL_SHIFT 22
+#define CSCMR1_USBOH3_CLK_SEL_MASK 0x00c00000
+#define CCMC_CSCMR2 0x0020
+#define CCMC_CSCDR1 0x0024
+#define CSCDR1_UART_CLK_PRED_SHIFT 3
+#define CSCDR1_UART_CLK_PRED_MASK 0x00000038
+#define CSCDR1_UART_CLK_PODF_SHIFT 0
+#define CSCDR1_UART_CLK_PODF_MASK 0x00000007
+#define CSCDR1_USBOH3_CLK_PRED_SHIFT 8
+#define CSCDR1_USBOH3_CLK_PRED_MASK 0x00000700
+#define CSCDR1_USBOH3_CLK_PODF_SHIFT 6
+#define CSCDR1_USBOH3_CLK_PODF_MASK 0x000000c0
+#define CCMC_CS1CDR 0x0028
+#define CCMC_CS2CDR 0x002c
+#define CCMC_CDCDR 0x0030
+#define CCMC_CSCDR2 0x0038
+#define CCMC_CSCDR3 0x003c
+#define CCMC_CSCDR4 0x0040
+#define CCMC_CWDR 0x0044
+#define CCMC_CDHIPR 0x0048
+#define CCMC_CDCR 0x004c
+#define CDCR_PERIPH_CLK_DVFS_PODF_SHIFT 0
+#define CDCR_PERIPH_CLK_DVFS_PODF_MASK 0x00000003
+#define CCMC_CTOR 0x0050
+#define CCMC_CLPCR 0x0054
+#define CCMC_CISR 0x0058
+#define CCMC_CIMR 0x005c
+#define CCMC_CCOSR 0x0060
+#define CCMC_CGPR 0x0064
+#define CCMC_CCGR(n) (0x0068 + (n) * 4)
+#define CCMC_CMEOR 0x0084
+
+#define CCMC_SIZE 0x88
+
+/* CCGR Clock Gate Register */
+
+#define CCMR_CCGR_NSOURCE 16
+#define CCMR_CCGR_NGROUPS 7
+#define CCMR_CCGR_MODULE(clk) ((clk) / CCMR_CCGR_NSOURCE)
+#define __CCGR_NUM(a, b) ((a) * 16 + (b))
+
+#define CCGR_ARM_BUS_CLK __CCGR_NUM(0, 0)
+#define CCGR_ARM_AXI_CLK __CCGR_NUM(0, 1)
+#define CCGR_ARM_DEBUG_CLK __CCGR_NUM(0, 2)
+#define CCGR_TZIC_CLK __CCGR_NUM(0, 3)
+#define CCGR_DAP_CLK __CCGR_NUM(0, 4)
+#define CCGR_TPIU_CLK __CCGR_NUM(0, 5)
+#define CCGR_CTI2_CLK __CCGR_NUM(0, 6)
+#define CCGR_CTI3_CLK __CCGR_NUM(0, 7)
+#define CCGR_AHBMUX1_CLK __CCGR_NUM(0, 8)
+#define CCGR_AHBMUX2_CLK __CCGR_NUM(0, 9)
+#define CCGR_ROMCP_CLK __CCGR_NUM(0, 10)
+#define CCGR_ROM_CLK __CCGR_NUM(0, 11)
+#define CCGR_AIPS_TZ1_CLK __CCGR_NUM(0, 12)
+#define CCGR_AIPS_TZ2_CLK __CCGR_NUM(0, 13)
+#define CCGR_AHB_MAX_CLK __CCGR_NUM(0, 14)
+#define CCGR_IIM_CLK __CCGR_NUM(0, 15)
+#define CCGR_TMAX1_CLK __CCGR_NUM(1, 0)
+#define CCGR_TMAX2_CLK __CCGR_NUM(1, 1)
+#define CCGR_TMAX3_CLK __CCGR_NUM(1, 2)
+#define CCGR_UART1_CLK __CCGR_NUM(1, 3)
+#define CCGR_UART1_SERIAL_CLK __CCGR_NUM(1, 4)
+#define CCGR_UART2_CLK __CCGR_NUM(1, 5)
+#define CCGR_UART2_SERIAL_CLK __CCGR_NUM(1, 6)
+#define CCGR_UART3_CLK __CCGR_NUM(1, 7)
+#define CCGR_UART3_SERIAL_CLK __CCGR_NUM(1, 8)
+#define CCGR_I2C1_SERIAL_CLK __CCGR_NUM(1, 9)
+#define CCGR_I2C2_SERIAL_CLK __CCGR_NUM(1, 10)
+#define CCGR_HSI2C_CLK __CCGR_NUM(1, 11)
+#define CCGR_HSI2C_SERIAL_CLK __CCGR_NUM(1, 12)
+#define CCGR_FIRI_CLK __CCGR_NUM(1, 13)
+#define CCGR_FIRI_SERIAL_CLK __CCGR_NUM(1, 14)
+#define CCGR_SCC_CLK __CCGR_NUM(1, 15)
+
+#define CCGR_USB_PHY_CLK __CCGR_NUM(2, 0)
+#define CCGR_EPIT1_CLK __CCGR_NUM(2, 1)
+#define CCGR_EPIT1_SERIAL_CLK __CCGR_NUM(2, 2)
+#define CCGR_EPIT2_CLK __CCGR_NUM(2, 3)
+#define CCGR_EPIT2_SERIAL_CLK __CCGR_NUM(2, 4)
+#define CCGR_PWM1_CLK __CCGR_NUM(2, 5)
+#define CCGR_PWM1_SERIAL_CLK __CCGR_NUM(2, 6)
+#define CCGR_PWM2_CLK __CCGR_NUM(2, 7)
+#define CCGR_PWM2_SERIAL_CLK __CCGR_NUM(2, 8)
+#define CCGR_GPT_CLK __CCGR_NUM(2, 9)
+#define CCGR_GPT_SERIAL_CLK __CCGR_NUM(2, 10)
+#define CCGR_OWIRE_CLK __CCGR_NUM(2, 11)
+#define CCGR_FEC_CLK __CCGR_NUM(2, 12)
+#define CCGR_USBOH3_IPG_AHB_CLK __CCGR_NUM(2, 13)
+#define CCGR_USBOH3_60M_CLK __CCGR_NUM(2, 14)
+#define CCGR_TVE_CLK __CCGR_NUM(2, 15)
+
+#define CCGR_ESDHC1_CLK __CCGR_NUM(3, 0)
+#define CCGR_ESDHC1_SERIAL_CLK __CCGR_NUM(3, 1)
+#define CCGR_ESDHC2_CLK __CCGR_NUM(3, 2)
+#define CCGR_ESDHC2_SERIAL_CLK __CCGR_NUM(3, 3)
+#define CCGR_ESDHC3_CLK __CCGR_NUM(3, 4)
+#define CCGR_ESDHC3_SERIAL_CLK __CCGR_NUM(3, 5)
+#define CCGR_ESDHC4_CLK __CCGR_NUM(3, 6)
+#define CCGR_ESDHC4_SERIAL_CLK __CCGR_NUM(3, 7)
+#define CCGR_SSI1_CLK __CCGR_NUM(3, 8)
+#define CCGR_SSI1_SERIAL_CLK __CCGR_NUM(3, 9)
+#define CCGR_SSI2_CLK __CCGR_NUM(3, 10)
+#define CCGR_SSI2_SERIAL_CLK __CCGR_NUM(3, 11)
+#define CCGR_SSI3_CLK __CCGR_NUM(3, 12)
+#define CCGR_SSI3_SERIAL_CLK __CCGR_NUM(3, 13)
+#define CCGR_SSI_EXT1_CLK __CCGR_NUM(3, 14)
+#define CCGR_SSI_EXT2_CLK __CCGR_NUM(3, 15)
+
+#define CCGR_PATA_CLK __CCGR_NUM(4, 0)
+#define CCGR_SIM_CLK __CCGR_NUM(4, 1)
+#define CCGR_SIM_SERIAL_CLK __CCGR_NUM(4, 2)
+#define CCGR_SAHARA_CLK __CCGR_NUM(4, 3)
+#define CCGR_RTIC_CLK __CCGR_NUM(4, 4)
+#define CCGR_ECSPI1_CLK __CCGR_NUM(4, 5)
+#define CCGR_ECSPI1_SERIAL_CLK __CCGR_NUM(4, 6)
+#define CCGR_ECSPI2_CLK __CCGR_NUM(4, 7)
+#define CCGR_ECSPI2_SERIAL_CLK __CCGR_NUM(4, 8)
+#define CCGR_CSPI_CLK __CCGR_NUM(4, 9)
+#define CCGR_SRTC_CLK __CCGR_NUM(4, 10)
+#define CCGR_SDMA_CLK __CCGR_NUM(4, 11)
+
+#define CCGR_SPBA_CLK __CCGR_NUM(5, 0)
+#define CCGR_GPU_CLK __CCGR_NUM(5, 1)
+#define CCGR_GARB_CLK __CCGR_NUM(5, 2)
+#define CCGR_VPU_CLK __CCGR_NUM(5, 3)
+#define CCGR_VPU_SERIAL_CLK __CCGR_NUM(5, 4)
+#define CCGR_IPU_CLK __CCGR_NUM(5, 5)
+#define CCGR_EMI_GARB_CLK __CCGR_NUM(6, 0)
+#define CCGR_IPU_DI0_CLK __CCGR_NUM(6, 1)
+#define CCGR_IPU_DI1_CLK __CCGR_NUM(6, 2)
+#define CCGR_GPU2D_CLK __CCGR_NUM(6, 3)
+#define CCGR_SLIMBUS_CLK __CCGR_NUM(6, 4)
+#define CCGR_SLIMBUS_SERIAL_CLK __CCGR_NUM(6, 5)
+
+#define CCGR_CLK_MODE_OFF 0
+#define CCGR_CLK_MODE_RUNMODE 1
+#define CCGR_CLK_MODE_ALWAYS 3
+
+#endif /* _IMX51_CCMREG_H */
diff --git a/sys/arm/freescale/imx/imx51_ccmvar.h b/sys/arm/freescale/imx/imx51_ccmvar.h
new file mode 100644
index 000000000000..acbb35a7e71d
--- /dev/null
+++ b/sys/arm/freescale/imx/imx51_ccmvar.h
@@ -0,0 +1,112 @@
+/* $NetBSD: imx51_ccmvar.h,v 1.1 2012/04/17 09:33:31 bsh Exp $ */
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause AND BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Genetec Corporation. All rights reserved.
+ * Written by Hashimoto Kenichi for Genetec Corporation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GENETEC CORPORATION ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GENETEC CORPORATION
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) 2012, 2013 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * Portions of this software were developed by Oleksandr Rybalko
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _ARM_IMX_IMX51_CCMVAR_H_
+#define _ARM_IMX_IMX51_CCMVAR_H_
+
+enum imx51_clock {
+ IMX51CLK_FPM,
+ IMX51CLK_PLL1,
+ IMX51CLK_PLL2,
+ IMX51CLK_PLL3,
+ IMX51CLK_PLL1SW,
+ IMX51CLK_PLL2SW,
+ IMX51CLK_PLL3SW,
+ IMX51CLK_PLL1STEP,
+ IMX51CLK_LP_APM,
+ IMX51CLK_ARM_ROOT,
+ IMX51CLK_MAIN_BUS_CLK_SRC, /* XXX */
+ IMX51CLK_MAIN_BUS_CLK,
+ IMX51CLK_EMI_SLOW_CLK_ROOT,
+ IMX51CLK_ENFC_CLK_ROOT,
+ IMX51CLK_AHB_CLK_ROOT,
+ IMX51CLK_IPG_CLK_ROOT,
+ IMX51CLK_PERCLK_ROOT,
+ IMX51CLK_DDR_CLK_ROOT,
+ IMX51CLK_ARM_AXI_CLK_ROOT,
+ IMX51CLK_ARM_AXI_A_CLK,
+ IMX51CLK_ARM_AXI_B_CLK,
+ IMX51CLK_IPU_HSP_CLK_ROOT,
+ IMX51CLK_CKIL_SYNC_CLK_ROOT,
+ IMX51CLK_USBOH3_CLK_ROOT,
+ IMX51CLK_ESDHC1_CLK_ROOT,
+ IMX51CLK_ESDHC2_CLK_ROOT,
+ IMX51CLK_ESDHC3_CLK_ROOT,
+ IMX51CLK_UART_CLK_ROOT,
+ IMX51CLK_SSI1_CLK_ROOT,
+ IMX51CLK_SSI2_CLK_ROOT,
+ IMX51CLK_SSI_EXT1_CLK_ROOT,
+ IMX51CLK_SSI_EXT2_CLK_ROOT,
+ IMX51CLK_USB_PHY_CLK_ROOT,
+ IMX51CLK_TVE_216_54_CLK_ROOT,
+ IMX51CLK_DI_CLK_ROOT,
+ IMX51CLK_SPDIF0_CLK_ROOT,
+ IMX51CLK_SPDIF1_CLK_ROOT,
+ IMX51CLK_CSPI_CLK_ROOT,
+ IMX51CLK_WRCK_CLK_ROOT,
+ IMX51CLK_LPSR_CLK_ROOT,
+ IMX51CLK_PGC_CLK_ROOT
+};
+
+u_int imx51_get_clock(enum imx51_clock);
+void imx51_clk_gating(int, int);
+int imx51_get_clk_gating(int);
+
+#endif /* _ARM_IMX_IMX51_CCMVAR_H_ */
diff --git a/sys/arm/freescale/imx/imx51_dpllreg.h b/sys/arm/freescale/imx/imx51_dpllreg.h
new file mode 100644
index 000000000000..d224333a9830
--- /dev/null
+++ b/sys/arm/freescale/imx/imx51_dpllreg.h
@@ -0,0 +1,107 @@
+/* $NetBSD: imx51_dpllreg.h,v 1.1 2012/04/17 09:33:31 bsh Exp $ */
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause AND BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Genetec Corporation. All rights reserved.
+ * Written by Hashimoto Kenichi for Genetec Corporation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GENETEC CORPORATION ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GENETEC CORPORATION
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) 2012, 2013 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * Portions of this software were developed by Oleksandr Rybalko
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _IMX51_DPLLREG_H
+#define _IMX51_DPLLREG_H
+
+#include <sys/cdefs.h>
+
+/* register offset address */
+
+#define IMX51_N_DPLLS 3 /* 1..3 */
+
+#define DPLL_BASE(n) (0x83F80000 + (0x4000 * ((n)-1)))
+#define DPLL_SIZE 0x100
+
+#define DPLL_DP_CTL 0x0000 /* 0x1223 */
+#define DP_CTL_LRF 0x00000001
+#define DP_CTL_BRM 0x00000002
+#define DP_CTL_PLM 0x00000004
+#define DP_CTL_RCP 0x00000008
+#define DP_CTL_RST 0x00000010
+#define DP_CTL_UPEN 0x00000020
+#define DP_CTL_PRE 0x00000040
+#define DP_CTL_HFSM 0x00000080
+#define DP_CTL_REF_CLK_SEL_MASK 0x00000300
+#define DP_CTL_REF_CLK_SEL_COSC 0x00000200
+#define DP_CTL_REF_CLK_SEL_FPM 0x00000300
+#define DP_CTL_REF_CLK_DIV 0x00000400
+#define DP_CTL_DPDCK0_2_EN 0x00001000
+#define DP_CTL_HIGHCLK_EN DP_CTL_DPDCK0_2_EN
+#define DP_CTL_MULCTRL 0x00002000
+#define DPLL_DP_CONFIG 0x0004 /* 2 */
+#define DPLL_DP_CONFIG_APEN 0x00000002
+#define DPLL_DP_CONFIG_LDREQ 0x00000001
+#define DPLL_DP_OP 0x0008 /* 0x80 */
+#define DP_OP_PDF_SHIFT 0
+#define DP_OP_PDF_MASK (0xf << DP_OP_PDF_SHIFT)
+#define DP_OP_MFI_SHIFT 4
+#define DP_OP_MFI_MASK (0xf << DP_OP_MFI_SHIFT)
+#define DPLL_DP_MFD 0x000C /* 2 */
+#define DPLL_DP_MFN 0x0010 /* 1 */
+#define DPLL_DP_MFNMINUS 0x0014 /* 0 */
+#define DPLL_DP_MFNPLUS 0x0018 /* 0 */
+#define DPLL_DP_HFS_OP 0x001C /* 0x80 */
+#define DPLL_DP_HFS_MFD 0x0020 /* 2 */
+#define DPLL_DP_HFS_MFN 0x0024 /* 1 */
+#define DPLL_DP_TOGC 0x0028 /* 0x20000 */
+#define DPLL_DP_DESTAT 0x002C /* 1 */
+
+#endif /* _IMX51_DPLLREG_H */
diff --git a/sys/arm/freescale/imx/imx51_ipuv3.c b/sys/arm/freescale/imx/imx51_ipuv3.c
new file mode 100644
index 000000000000..520ace1a1b53
--- /dev/null
+++ b/sys/arm/freescale/imx/imx51_ipuv3.c
@@ -0,0 +1,878 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * Copyright (c) 2012, 2013 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * Portions of this software were developed by Oleksandr Rybalko
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bio.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/queue.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/time.h>
+#include <sys/timetc.h>
+#include <sys/fbio.h>
+#include <sys/consio.h>
+
+#include <sys/kdb.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+#include <machine/resource.h>
+#include <machine/intr.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/fb/fbreg.h>
+#include <dev/syscons/syscons.h>
+
+#include <arm/freescale/imx/imx51_ccmvar.h>
+
+#include <arm/freescale/imx/imx51_ipuv3reg.h>
+
+#define IMX51_IPU_HSP_CLOCK 665000000
+#define IPU3FB_FONT_HEIGHT 16
+
+struct ipu3sc_softc {
+ device_t dev;
+ bus_addr_t pbase;
+ bus_addr_t vbase;
+
+ bus_space_tag_t iot;
+ bus_space_handle_t ioh;
+ bus_space_handle_t cm_ioh;
+ bus_space_handle_t dp_ioh;
+ bus_space_handle_t di0_ioh;
+ bus_space_handle_t di1_ioh;
+ bus_space_handle_t dctmpl_ioh;
+ bus_space_handle_t dc_ioh;
+ bus_space_handle_t dmfc_ioh;
+ bus_space_handle_t idmac_ioh;
+ bus_space_handle_t cpmem_ioh;
+};
+
+struct video_adapter_softc {
+ /* Videoadpater part */
+ video_adapter_t va;
+
+ intptr_t fb_addr;
+ intptr_t fb_paddr;
+ unsigned int fb_size;
+
+ int bpp;
+ int depth;
+ unsigned int height;
+ unsigned int width;
+ unsigned int stride;
+
+ unsigned int xmargin;
+ unsigned int ymargin;
+
+ unsigned char *font;
+ int initialized;
+};
+
+static struct ipu3sc_softc *ipu3sc_softc;
+static struct video_adapter_softc va_softc;
+
+/* FIXME: not only 2 bytes color supported */
+static uint16_t colors[16] = {
+ 0x0000, /* black */
+ 0x001f, /* blue */
+ 0x07e0, /* green */
+ 0x07ff, /* cyan */
+ 0xf800, /* red */
+ 0xf81f, /* magenta */
+ 0x3800, /* brown */
+ 0xc618, /* light grey */
+ 0xc618, /* XXX: dark grey */
+ 0x001f, /* XXX: light blue */
+ 0x07e0, /* XXX: light green */
+ 0x07ff, /* XXX: light cyan */
+ 0xf800, /* XXX: light red */
+ 0xf81f, /* XXX: light magenta */
+ 0xffe0, /* yellow */
+ 0xffff, /* white */
+};
+static uint32_t colors_24[16] = {
+ 0x000000,/* Black */
+ 0x000080,/* Blue */
+ 0x008000,/* Green */
+ 0x008080,/* Cyan */
+ 0x800000,/* Red */
+ 0x800080,/* Magenta */
+ 0xcc6600,/* brown */
+ 0xC0C0C0,/* Silver */
+ 0x808080,/* Gray */
+ 0x0000FF,/* Light Blue */
+ 0x00FF00,/* Light Green */
+ 0x00FFFF,/* Light Cyan */
+ 0xFF0000,/* Light Red */
+ 0xFF00FF,/* Light Magenta */
+ 0xFFFF00,/* Yellow */
+ 0xFFFFFF,/* White */
+
+};
+
+#define IPUV3_READ(ipuv3, module, reg) \
+ bus_space_read_4((ipuv3)->iot, (ipuv3)->module##_ioh, (reg))
+#define IPUV3_WRITE(ipuv3, module, reg, val) \
+ bus_space_write_4((ipuv3)->iot, (ipuv3)->module##_ioh, (reg), (val))
+
+#define CPMEM_CHANNEL_OFFSET(_c) ((_c) * 0x40)
+#define CPMEM_WORD_OFFSET(_w) ((_w) * 0x20)
+#define CPMEM_DP_OFFSET(_d) ((_d) * 0x10000)
+#define IMX_IPU_DP0 0
+#define IMX_IPU_DP1 1
+#define CPMEM_CHANNEL(_dp, _ch, _w) \
+ (CPMEM_DP_OFFSET(_dp) + CPMEM_CHANNEL_OFFSET(_ch) + \
+ CPMEM_WORD_OFFSET(_w))
+#define CPMEM_OFFSET(_dp, _ch, _w, _o) \
+ (CPMEM_CHANNEL((_dp), (_ch), (_w)) + (_o))
+
+#define IPUV3_DEBUG 100
+
+#ifdef IPUV3_DEBUG
+#define SUBMOD_DUMP_REG(_sc, _m, _l) \
+ { \
+ int i; \
+ printf("*** " #_m " ***\n"); \
+ for (i = 0; i <= (_l); i += 4) { \
+ if ((i % 32) == 0) \
+ printf("%04x: ", i & 0xffff); \
+ printf("0x%08x%c", IPUV3_READ((_sc), _m, i), \
+ ((i + 4) % 32)?' ':'\n'); \
+ } \
+ printf("\n"); \
+ }
+#endif
+
+#ifdef IPUV3_DEBUG
+int ipuv3_debug = IPUV3_DEBUG;
+#define DPRINTFN(n,x) if (ipuv3_debug>(n)) printf x; else
+#else
+#define DPRINTFN(n,x)
+#endif
+
+static int ipu3_fb_probe(device_t);
+static int ipu3_fb_attach(device_t);
+
+static int
+ipu3_fb_malloc(struct ipu3sc_softc *sc, size_t size)
+{
+
+ sc->vbase = (uint32_t)contigmalloc(size, M_DEVBUF, M_ZERO, 0, ~0,
+ PAGE_SIZE, 0);
+ sc->pbase = vtophys(sc->vbase);
+
+ return (0);
+}
+
+static void
+ipu3_fb_init(void *arg)
+{
+ struct ipu3sc_softc *sc = arg;
+ struct video_adapter_softc *va_sc = &va_softc;
+ uint64_t w0sh96;
+ uint32_t w1sh96;
+
+ /* FW W0[137:125] - 96 = [41:29] */
+ /* FH W0[149:138] - 96 = [53:42] */
+ w0sh96 = IPUV3_READ(sc, cpmem, CPMEM_OFFSET(IMX_IPU_DP1, 23, 0, 16));
+ w0sh96 <<= 32;
+ w0sh96 |= IPUV3_READ(sc, cpmem, CPMEM_OFFSET(IMX_IPU_DP1, 23, 0, 12));
+
+ va_sc->width = ((w0sh96 >> 29) & 0x1fff) + 1;
+ va_sc->height = ((w0sh96 >> 42) & 0x0fff) + 1;
+
+ /* SLY W1[115:102] - 96 = [19:6] */
+ w1sh96 = IPUV3_READ(sc, cpmem, CPMEM_OFFSET(IMX_IPU_DP1, 23, 1, 12));
+ va_sc->stride = ((w1sh96 >> 6) & 0x3fff) + 1;
+
+ printf("%dx%d [%d]\n", va_sc->width, va_sc->height, va_sc->stride);
+ va_sc->fb_size = va_sc->height * va_sc->stride;
+
+ ipu3_fb_malloc(sc, va_sc->fb_size);
+
+ /* DP1 + config_ch_23 + word_2 */
+ IPUV3_WRITE(sc, cpmem, CPMEM_OFFSET(IMX_IPU_DP1, 23, 1, 0),
+ ((sc->pbase >> 3) | ((sc->pbase >> 3) << 29)) & 0xffffffff);
+
+ IPUV3_WRITE(sc, cpmem, CPMEM_OFFSET(IMX_IPU_DP1, 23, 1, 4),
+ ((sc->pbase >> 3) >> 3) & 0xffffffff);
+
+ va_sc->fb_addr = (intptr_t)sc->vbase;
+ va_sc->fb_paddr = (intptr_t)sc->pbase;
+ va_sc->bpp = va_sc->stride / va_sc->width;
+ va_sc->depth = va_sc->bpp * 8;
+}
+
+static int
+ipu3_fb_probe(device_t dev)
+{
+ int error;
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "fsl,ipu3"))
+ return (ENXIO);
+
+ device_set_desc(dev, "i.MX5x Image Processing Unit v3 (FB)");
+
+ error = sc_probe_unit(device_get_unit(dev),
+ device_get_flags(dev) | SC_AUTODETECT_KBD);
+
+ if (error != 0)
+ return (error);
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ipu3_fb_attach(device_t dev)
+{
+ struct ipu3sc_softc *sc = device_get_softc(dev);
+ bus_space_tag_t iot;
+ bus_space_handle_t ioh;
+ phandle_t node;
+ pcell_t reg;
+ int err;
+ uintptr_t base;
+
+ if (ipu3sc_softc)
+ return (ENXIO);
+
+ ipu3sc_softc = sc;
+
+ if (bootverbose)
+ device_printf(dev, "clock gate status is %d\n",
+ imx51_get_clk_gating(IMX51CLK_IPU_HSP_CLK_ROOT));
+
+ sc->dev = dev;
+
+ err = (sc_attach_unit(device_get_unit(dev),
+ device_get_flags(dev) | SC_AUTODETECT_KBD));
+
+ if (err) {
+ device_printf(dev, "failed to attach syscons\n");
+ goto fail;
+ }
+
+ sc = device_get_softc(dev);
+ sc->iot = iot = fdtbus_bs_tag;
+
+ /*
+ * Retrieve the device address based on the start address in the
+ * DTS. The DTS for i.MX51 specifies 0x5e000000 as the first register
+ * address, so we just subtract IPU_CM_BASE to get the offset at which
+ * the IPU device was memory mapped.
+ * On i.MX53, the offset is 0.
+ */
+ node = ofw_bus_get_node(dev);
+ if ((OF_getencprop(node, "reg", &reg, sizeof(reg))) <= 0)
+ base = 0;
+ else
+ base = reg - IPU_CM_BASE(0);
+ /* map controller registers */
+ err = bus_space_map(iot, IPU_CM_BASE(base), IPU_CM_SIZE, 0, &ioh);
+ if (err)
+ goto fail_retarn_cm;
+ sc->cm_ioh = ioh;
+
+ /* map Display Multi FIFO Controller registers */
+ err = bus_space_map(iot, IPU_DMFC_BASE(base), IPU_DMFC_SIZE, 0, &ioh);
+ if (err)
+ goto fail_retarn_dmfc;
+ sc->dmfc_ioh = ioh;
+
+ /* map Display Interface 0 registers */
+ err = bus_space_map(iot, IPU_DI0_BASE(base), IPU_DI0_SIZE, 0, &ioh);
+ if (err)
+ goto fail_retarn_di0;
+ sc->di0_ioh = ioh;
+
+ /* map Display Interface 1 registers */
+ err = bus_space_map(iot, IPU_DI1_BASE(base), IPU_DI0_SIZE, 0, &ioh);
+ if (err)
+ goto fail_retarn_di1;
+ sc->di1_ioh = ioh;
+
+ /* map Display Processor registers */
+ err = bus_space_map(iot, IPU_DP_BASE(base), IPU_DP_SIZE, 0, &ioh);
+ if (err)
+ goto fail_retarn_dp;
+ sc->dp_ioh = ioh;
+
+ /* map Display Controller registers */
+ err = bus_space_map(iot, IPU_DC_BASE(base), IPU_DC_SIZE, 0, &ioh);
+ if (err)
+ goto fail_retarn_dc;
+ sc->dc_ioh = ioh;
+
+ /* map Image DMA Controller registers */
+ err = bus_space_map(iot, IPU_IDMAC_BASE(base), IPU_IDMAC_SIZE, 0,
+ &ioh);
+ if (err)
+ goto fail_retarn_idmac;
+ sc->idmac_ioh = ioh;
+
+ /* map CPMEM registers */
+ err = bus_space_map(iot, IPU_CPMEM_BASE(base), IPU_CPMEM_SIZE, 0,
+ &ioh);
+ if (err)
+ goto fail_retarn_cpmem;
+ sc->cpmem_ioh = ioh;
+
+ /* map DCTEMPL registers */
+ err = bus_space_map(iot, IPU_DCTMPL_BASE(base), IPU_DCTMPL_SIZE, 0,
+ &ioh);
+ if (err)
+ goto fail_retarn_dctmpl;
+ sc->dctmpl_ioh = ioh;
+
+#ifdef notyet
+ sc->ih = imx51_ipuv3_intr_establish(IMX51_INT_IPUV3, IPL_BIO,
+ ipuv3intr, sc);
+ if (sc->ih == NULL) {
+ device_printf(sc->dev,
+ "unable to establish interrupt at irq %d\n",
+ IMX51_INT_IPUV3);
+ return (ENXIO);
+ }
+#endif
+
+ /*
+ * We have to wait until interrupts are enabled.
+ * Mailbox relies on it to get data from VideoCore
+ */
+ ipu3_fb_init(sc);
+
+ return (0);
+
+fail:
+ return (ENXIO);
+fail_retarn_dctmpl:
+ bus_space_unmap(sc->iot, sc->cpmem_ioh, IPU_CPMEM_SIZE);
+fail_retarn_cpmem:
+ bus_space_unmap(sc->iot, sc->idmac_ioh, IPU_IDMAC_SIZE);
+fail_retarn_idmac:
+ bus_space_unmap(sc->iot, sc->dc_ioh, IPU_DC_SIZE);
+fail_retarn_dp:
+ bus_space_unmap(sc->iot, sc->dp_ioh, IPU_DP_SIZE);
+fail_retarn_dc:
+ bus_space_unmap(sc->iot, sc->di1_ioh, IPU_DI1_SIZE);
+fail_retarn_di1:
+ bus_space_unmap(sc->iot, sc->di0_ioh, IPU_DI0_SIZE);
+fail_retarn_di0:
+ bus_space_unmap(sc->iot, sc->dmfc_ioh, IPU_DMFC_SIZE);
+fail_retarn_dmfc:
+ bus_space_unmap(sc->iot, sc->dc_ioh, IPU_CM_SIZE);
+fail_retarn_cm:
+ device_printf(sc->dev,
+ "failed to map registers (errno=%d)\n", err);
+ return (err);
+}
+
+static device_method_t ipu3_fb_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ipu3_fb_probe),
+ DEVMETHOD(device_attach, ipu3_fb_attach),
+ { 0, 0 }
+};
+
+static devclass_t ipu3_fb_devclass;
+
+static driver_t ipu3_fb_driver = {
+ "fb",
+ ipu3_fb_methods,
+ sizeof(struct ipu3sc_softc),
+};
+
+DRIVER_MODULE(ipu3fb, simplebus, ipu3_fb_driver, ipu3_fb_devclass, 0, 0);
+
+/*
+ * Video driver routines and glue.
+ */
+static int ipu3fb_configure(int);
+static vi_probe_t ipu3fb_probe;
+static vi_init_t ipu3fb_init;
+static vi_get_info_t ipu3fb_get_info;
+static vi_query_mode_t ipu3fb_query_mode;
+static vi_set_mode_t ipu3fb_set_mode;
+static vi_save_font_t ipu3fb_save_font;
+static vi_load_font_t ipu3fb_load_font;
+static vi_show_font_t ipu3fb_show_font;
+static vi_save_palette_t ipu3fb_save_palette;
+static vi_load_palette_t ipu3fb_load_palette;
+static vi_set_border_t ipu3fb_set_border;
+static vi_save_state_t ipu3fb_save_state;
+static vi_load_state_t ipu3fb_load_state;
+static vi_set_win_org_t ipu3fb_set_win_org;
+static vi_read_hw_cursor_t ipu3fb_read_hw_cursor;
+static vi_set_hw_cursor_t ipu3fb_set_hw_cursor;
+static vi_set_hw_cursor_shape_t ipu3fb_set_hw_cursor_shape;
+static vi_blank_display_t ipu3fb_blank_display;
+static vi_mmap_t ipu3fb_mmap;
+static vi_ioctl_t ipu3fb_ioctl;
+static vi_clear_t ipu3fb_clear;
+static vi_fill_rect_t ipu3fb_fill_rect;
+static vi_bitblt_t ipu3fb_bitblt;
+static vi_diag_t ipu3fb_diag;
+static vi_save_cursor_palette_t ipu3fb_save_cursor_palette;
+static vi_load_cursor_palette_t ipu3fb_load_cursor_palette;
+static vi_copy_t ipu3fb_copy;
+static vi_putp_t ipu3fb_putp;
+static vi_putc_t ipu3fb_putc;
+static vi_puts_t ipu3fb_puts;
+static vi_putm_t ipu3fb_putm;
+
+static video_switch_t ipu3fbvidsw = {
+ .probe = ipu3fb_probe,
+ .init = ipu3fb_init,
+ .get_info = ipu3fb_get_info,
+ .query_mode = ipu3fb_query_mode,
+ .set_mode = ipu3fb_set_mode,
+ .save_font = ipu3fb_save_font,
+ .load_font = ipu3fb_load_font,
+ .show_font = ipu3fb_show_font,
+ .save_palette = ipu3fb_save_palette,
+ .load_palette = ipu3fb_load_palette,
+ .set_border = ipu3fb_set_border,
+ .save_state = ipu3fb_save_state,
+ .load_state = ipu3fb_load_state,
+ .set_win_org = ipu3fb_set_win_org,
+ .read_hw_cursor = ipu3fb_read_hw_cursor,
+ .set_hw_cursor = ipu3fb_set_hw_cursor,
+ .set_hw_cursor_shape = ipu3fb_set_hw_cursor_shape,
+ .blank_display = ipu3fb_blank_display,
+ .mmap = ipu3fb_mmap,
+ .ioctl = ipu3fb_ioctl,
+ .clear = ipu3fb_clear,
+ .fill_rect = ipu3fb_fill_rect,
+ .bitblt = ipu3fb_bitblt,
+ .diag = ipu3fb_diag,
+ .save_cursor_palette = ipu3fb_save_cursor_palette,
+ .load_cursor_palette = ipu3fb_load_cursor_palette,
+ .copy = ipu3fb_copy,
+ .putp = ipu3fb_putp,
+ .putc = ipu3fb_putc,
+ .puts = ipu3fb_puts,
+ .putm = ipu3fb_putm,
+};
+
+VIDEO_DRIVER(ipu3fb, ipu3fbvidsw, ipu3fb_configure);
+
+extern sc_rndr_sw_t txtrndrsw;
+RENDERER(ipu3fb, 0, txtrndrsw, gfb_set);
+RENDERER_MODULE(ipu3fb, gfb_set);
+
+static uint16_t ipu3fb_static_window[ROW*COL];
+extern u_char dflt_font_16[];
+
+static int
+ipu3fb_configure(int flags)
+{
+ struct video_adapter_softc *sc;
+
+ sc = &va_softc;
+
+ if (sc->initialized)
+ return 0;
+
+ sc->width = 640;
+ sc->height = 480;
+ sc->bpp = 2;
+ sc->stride = sc->width * sc->bpp;
+
+ ipu3fb_init(0, &sc->va, 0);
+
+ sc->initialized = 1;
+
+ return (0);
+}
+
+static int
+ipu3fb_probe(int unit, video_adapter_t **adp, void *arg, int flags)
+{
+
+ return (0);
+}
+
+static int
+ipu3fb_init(int unit, video_adapter_t *adp, int flags)
+{
+ struct video_adapter_softc *sc;
+ video_info_t *vi;
+
+ sc = (struct video_adapter_softc *)adp;
+ vi = &adp->va_info;
+
+ vid_init_struct(adp, "ipu3fb", -1, unit);
+
+ sc->font = dflt_font_16;
+ vi->vi_cheight = IPU3FB_FONT_HEIGHT;
+ vi->vi_cwidth = 8;
+ vi->vi_width = sc->width/8;
+ vi->vi_height = sc->height/vi->vi_cheight;
+
+ /*
+ * Clamp width/height to syscons maximums
+ */
+ if (vi->vi_width > COL)
+ vi->vi_width = COL;
+ if (vi->vi_height > ROW)
+ vi->vi_height = ROW;
+
+ sc->xmargin = (sc->width - (vi->vi_width * vi->vi_cwidth)) / 2;
+ sc->ymargin = (sc->height - (vi->vi_height * vi->vi_cheight))/2;
+
+ adp->va_window = (vm_offset_t) ipu3fb_static_window;
+ adp->va_flags |= V_ADP_FONT /* | V_ADP_COLOR | V_ADP_MODECHANGE */;
+ adp->va_line_width = sc->stride;
+ adp->va_buffer_size = sc->fb_size;
+
+ vid_register(&sc->va);
+
+ return (0);
+}
+
+static int
+ipu3fb_get_info(video_adapter_t *adp, int mode, video_info_t *info)
+{
+
+ bcopy(&adp->va_info, info, sizeof(*info));
+ return (0);
+}
+
+static int
+ipu3fb_query_mode(video_adapter_t *adp, video_info_t *info)
+{
+
+ return (0);
+}
+
+static int
+ipu3fb_set_mode(video_adapter_t *adp, int mode)
+{
+
+ return (0);
+}
+
+static int
+ipu3fb_save_font(video_adapter_t *adp, int page, int size, int width,
+ u_char *data, int c, int count)
+{
+
+ return (0);
+}
+
+static int
+ipu3fb_load_font(video_adapter_t *adp, int page, int size, int width,
+ u_char *data, int c, int count)
+{
+ struct video_adapter_softc *sc;
+
+ sc = (struct video_adapter_softc *)adp;
+ sc->font = data;
+
+ return (0);
+}
+
+static int
+ipu3fb_show_font(video_adapter_t *adp, int page)
+{
+
+ return (0);
+}
+
+static int
+ipu3fb_save_palette(video_adapter_t *adp, u_char *palette)
+{
+
+ return (0);
+}
+
+static int
+ipu3fb_load_palette(video_adapter_t *adp, u_char *palette)
+{
+
+ return (0);
+}
+
+static int
+ipu3fb_set_border(video_adapter_t *adp, int border)
+{
+
+ return (ipu3fb_blank_display(adp, border));
+}
+
+static int
+ipu3fb_save_state(video_adapter_t *adp, void *p, size_t size)
+{
+
+ return (0);
+}
+
+static int
+ipu3fb_load_state(video_adapter_t *adp, void *p)
+{
+
+ return (0);
+}
+
+static int
+ipu3fb_set_win_org(video_adapter_t *adp, off_t offset)
+{
+
+ return (0);
+}
+
+static int
+ipu3fb_read_hw_cursor(video_adapter_t *adp, int *col, int *row)
+{
+
+ *col = *row = 0;
+ return (0);
+}
+
+static int
+ipu3fb_set_hw_cursor(video_adapter_t *adp, int col, int row)
+{
+
+ return (0);
+}
+
+static int
+ipu3fb_set_hw_cursor_shape(video_adapter_t *adp, int base, int height,
+ int celsize, int blink)
+{
+
+ return (0);
+}
+
+static int
+ipu3fb_blank_display(video_adapter_t *adp, int mode)
+{
+
+ return (0);
+}
+
+static int
+ipu3fb_mmap(video_adapter_t *adp, vm_ooffset_t offset, vm_paddr_t *paddr,
+ int prot, vm_memattr_t *memattr)
+{
+ struct video_adapter_softc *sc;
+
+ sc = (struct video_adapter_softc *)adp;
+
+ /*
+ * This might be a legacy VGA mem request: if so, just point it at the
+ * framebuffer, since it shouldn't be touched
+ */
+ if (offset < sc->stride * sc->height) {
+ *paddr = sc->fb_paddr + offset;
+ return (0);
+ }
+
+ return (EINVAL);
+}
+
+static int
+ipu3fb_ioctl(video_adapter_t *adp, u_long cmd, caddr_t data)
+{
+ struct video_adapter_softc *sc;
+ struct fbtype *fb;
+
+ sc = (struct video_adapter_softc *)adp;
+
+ switch (cmd) {
+ case FBIOGTYPE:
+ fb = (struct fbtype *)data;
+ fb->fb_type = FBTYPE_PCIMISC;
+ fb->fb_height = sc->height;
+ fb->fb_width = sc->width;
+ fb->fb_depth = sc->depth;
+ if (sc->depth <= 1 || sc->depth > 8)
+ fb->fb_cmsize = 0;
+ else
+ fb->fb_cmsize = 1 << sc->depth;
+ fb->fb_size = sc->fb_size;
+ break;
+ case FBIOSCURSOR:
+ return (ENODEV);
+ default:
+ return (fb_commonioctl(adp, cmd, data));
+ }
+
+ return (0);
+}
+
+static int
+ipu3fb_clear(video_adapter_t *adp)
+{
+
+ return (ipu3fb_blank_display(adp, 0));
+}
+
+static int
+ipu3fb_fill_rect(video_adapter_t *adp, int val, int x, int y, int cx, int cy)
+{
+
+ return (0);
+}
+
+static int
+ipu3fb_bitblt(video_adapter_t *adp, ...)
+{
+
+ return (0);
+}
+
+static int
+ipu3fb_diag(video_adapter_t *adp, int level)
+{
+
+ return (0);
+}
+
+static int
+ipu3fb_save_cursor_palette(video_adapter_t *adp, u_char *palette)
+{
+
+ return (0);
+}
+
+static int
+ipu3fb_load_cursor_palette(video_adapter_t *adp, u_char *palette)
+{
+
+ return (0);
+}
+
+static int
+ipu3fb_copy(video_adapter_t *adp, vm_offset_t src, vm_offset_t dst, int n)
+{
+
+ return (0);
+}
+
+static int
+ipu3fb_putp(video_adapter_t *adp, vm_offset_t off, uint32_t p, uint32_t a,
+ int size, int bpp, int bit_ltor, int byte_ltor)
+{
+
+ return (0);
+}
+
+static int
+ipu3fb_putc(video_adapter_t *adp, vm_offset_t off, uint8_t c, uint8_t a)
+{
+ struct video_adapter_softc *sc;
+ int col, row, bpp;
+ int b, i, j, k;
+ uint8_t *addr;
+ u_char *p;
+ uint32_t fg, bg, color;
+
+ sc = (struct video_adapter_softc *)adp;
+ bpp = sc->bpp;
+
+ if (sc->fb_addr == 0)
+ return (0);
+ row = (off / adp->va_info.vi_width) * adp->va_info.vi_cheight;
+ col = (off % adp->va_info.vi_width) * adp->va_info.vi_cwidth;
+ p = sc->font + c * IPU3FB_FONT_HEIGHT;
+ addr = (uint8_t *)sc->fb_addr
+ + (row + sc->ymargin) * (sc->stride)
+ + bpp * (col + sc->xmargin);
+
+ if (bpp == 2) {
+ bg = colors[(a >> 4) & 0x0f];
+ fg = colors[a & 0x0f];
+ } else if (bpp == 3) {
+ bg = colors_24[(a >> 4) & 0x0f];
+ fg = colors_24[a & 0x0f];
+ } else {
+ return (ENXIO);
+ }
+
+ for (i = 0; i < IPU3FB_FONT_HEIGHT; i++) {
+ for (j = 0, k = 7; j < 8; j++, k--) {
+ if ((p[i] & (1 << k)) == 0)
+ color = bg;
+ else
+ color = fg;
+ /* FIXME: BPP maybe different */
+ for (b = 0; b < bpp; b ++)
+ addr[bpp * j + b] =
+ (color >> (b << 3)) & 0xff;
+ }
+
+ addr += (sc->stride);
+ }
+
+ return (0);
+}
+
+static int
+ipu3fb_puts(video_adapter_t *adp, vm_offset_t off, u_int16_t *s, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ ipu3fb_putc(adp, off + i, s[i] & 0xff, (s[i] & 0xff00) >> 8);
+
+ return (0);
+}
+
+static int
+ipu3fb_putm(video_adapter_t *adp, int x, int y, uint8_t *pixel_image,
+ uint32_t pixel_mask, int size, int width)
+{
+
+ return (0);
+}
diff --git a/sys/arm/freescale/imx/imx51_ipuv3_fbd.c b/sys/arm/freescale/imx/imx51_ipuv3_fbd.c
new file mode 100644
index 000000000000..644664fa8aa3
--- /dev/null
+++ b/sys/arm/freescale/imx/imx51_ipuv3_fbd.c
@@ -0,0 +1,364 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * Copyright (c) 2012, 2013 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * Portions of this software were developed by Oleksandr Rybalko
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bio.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/queue.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/time.h>
+#include <sys/timetc.h>
+#include <sys/fbio.h>
+#include <sys/consio.h>
+#include <sys/eventhandler.h>
+
+#include <sys/kdb.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/frame.h>
+#include <machine/intr.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/vt/vt.h>
+#include <dev/vt/colors/vt_termcolors.h>
+
+#include <arm/freescale/imx/imx51_ccmvar.h>
+
+#include <arm/freescale/imx/imx51_ipuv3reg.h>
+
+#include "fb_if.h"
+
+#define IMX51_IPU_HSP_CLOCK 665000000
+
+struct ipu3sc_softc {
+ device_t dev;
+ device_t sc_fbd; /* fbd child */
+ struct fb_info sc_info;
+
+ bus_space_tag_t iot;
+ bus_space_handle_t ioh;
+ bus_space_handle_t cm_ioh;
+ bus_space_handle_t dp_ioh;
+ bus_space_handle_t di0_ioh;
+ bus_space_handle_t di1_ioh;
+ bus_space_handle_t dctmpl_ioh;
+ bus_space_handle_t dc_ioh;
+ bus_space_handle_t dmfc_ioh;
+ bus_space_handle_t idmac_ioh;
+ bus_space_handle_t cpmem_ioh;
+};
+
+static struct ipu3sc_softc *ipu3sc_softc;
+
+#define IPUV3_READ(ipuv3, module, reg) \
+ bus_space_read_4((ipuv3)->iot, (ipuv3)->module##_ioh, (reg))
+#define IPUV3_WRITE(ipuv3, module, reg, val) \
+ bus_space_write_4((ipuv3)->iot, (ipuv3)->module##_ioh, (reg), (val))
+
+#define CPMEM_CHANNEL_OFFSET(_c) ((_c) * 0x40)
+#define CPMEM_WORD_OFFSET(_w) ((_w) * 0x20)
+#define CPMEM_DP_OFFSET(_d) ((_d) * 0x10000)
+#define IMX_IPU_DP0 0
+#define IMX_IPU_DP1 1
+#define CPMEM_CHANNEL(_dp, _ch, _w) \
+ (CPMEM_DP_OFFSET(_dp) + CPMEM_CHANNEL_OFFSET(_ch) + \
+ CPMEM_WORD_OFFSET(_w))
+#define CPMEM_OFFSET(_dp, _ch, _w, _o) \
+ (CPMEM_CHANNEL((_dp), (_ch), (_w)) + (_o))
+
+static int ipu3_fb_probe(device_t);
+static int ipu3_fb_attach(device_t);
+
+static void
+ipu3_fb_init(struct ipu3sc_softc *sc)
+{
+ uint64_t w0sh96;
+ uint32_t w1sh96;
+
+ /* FW W0[137:125] - 96 = [41:29] */
+ /* FH W0[149:138] - 96 = [53:42] */
+ w0sh96 = IPUV3_READ(sc, cpmem, CPMEM_OFFSET(IMX_IPU_DP1, 23, 0, 16));
+ w0sh96 <<= 32;
+ w0sh96 |= IPUV3_READ(sc, cpmem, CPMEM_OFFSET(IMX_IPU_DP1, 23, 0, 12));
+
+ sc->sc_info.fb_width = ((w0sh96 >> 29) & 0x1fff) + 1;
+ sc->sc_info.fb_height = ((w0sh96 >> 42) & 0x0fff) + 1;
+
+ /* SLY W1[115:102] - 96 = [19:6] */
+ w1sh96 = IPUV3_READ(sc, cpmem, CPMEM_OFFSET(IMX_IPU_DP1, 23, 1, 12));
+ sc->sc_info.fb_stride = ((w1sh96 >> 6) & 0x3fff) + 1;
+
+ printf("%dx%d [%d]\n", sc->sc_info.fb_width, sc->sc_info.fb_height,
+ sc->sc_info.fb_stride);
+ sc->sc_info.fb_size = sc->sc_info.fb_height * sc->sc_info.fb_stride;
+
+ sc->sc_info.fb_vbase = (intptr_t)contigmalloc(sc->sc_info.fb_size,
+ M_DEVBUF, M_ZERO, 0, ~0, PAGE_SIZE, 0);
+ sc->sc_info.fb_pbase = (intptr_t)vtophys(sc->sc_info.fb_vbase);
+
+ /* DP1 + config_ch_23 + word_2 */
+ IPUV3_WRITE(sc, cpmem, CPMEM_OFFSET(IMX_IPU_DP1, 23, 1, 0),
+ (((uint32_t)sc->sc_info.fb_pbase >> 3) |
+ (((uint32_t)sc->sc_info.fb_pbase >> 3) << 29)) & 0xffffffff);
+
+ IPUV3_WRITE(sc, cpmem, CPMEM_OFFSET(IMX_IPU_DP1, 23, 1, 4),
+ (((uint32_t)sc->sc_info.fb_pbase >> 3) >> 3) & 0xffffffff);
+
+ /* XXX: fetch or set it from/to IPU. */
+ sc->sc_info.fb_bpp = sc->sc_info.fb_depth = sc->sc_info.fb_stride /
+ sc->sc_info.fb_width * 8;
+}
+
+/* Use own color map, because of different RGB offset. */
+static int
+ipu3_fb_init_cmap(uint32_t *cmap, int bytespp)
+{
+
+ switch (bytespp) {
+ case 8:
+ return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB,
+ 0x7, 5, 0x7, 2, 0x3, 0));
+ case 15:
+ return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB,
+ 0x1f, 10, 0x1f, 5, 0x1f, 0));
+ case 16:
+ return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB,
+ 0x1f, 11, 0x3f, 5, 0x1f, 0));
+ case 24:
+ case 32: /* Ignore alpha. */
+ return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB,
+ 0xff, 0, 0xff, 8, 0xff, 16));
+ default:
+ return (1);
+ }
+}
+
+static int
+ipu3_fb_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "fsl,ipu3"))
+ return (ENXIO);
+
+ device_set_desc(dev, "i.MX5x Image Processing Unit v3 (FB)");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ipu3_fb_attach(device_t dev)
+{
+ struct ipu3sc_softc *sc = device_get_softc(dev);
+ bus_space_tag_t iot;
+ bus_space_handle_t ioh;
+ phandle_t node;
+ pcell_t reg;
+ int err;
+ uintptr_t base;
+
+ ipu3sc_softc = sc;
+
+ if (bootverbose)
+ device_printf(dev, "clock gate status is %d\n",
+ imx51_get_clk_gating(IMX51CLK_IPU_HSP_CLK_ROOT));
+
+ sc->dev = dev;
+
+ sc = device_get_softc(dev);
+ sc->iot = iot = fdtbus_bs_tag;
+
+ /*
+ * Retrieve the device address based on the start address in the
+ * DTS. The DTS for i.MX51 specifies 0x5e000000 as the first register
+ * address, so we just subtract IPU_CM_BASE to get the offset at which
+ * the IPU device was memory mapped.
+ * On i.MX53, the offset is 0.
+ */
+ node = ofw_bus_get_node(dev);
+ if ((OF_getencprop(node, "reg", &reg, sizeof(reg))) <= 0)
+ base = 0;
+ else
+ base = reg - IPU_CM_BASE(0);
+ /* map controller registers */
+ err = bus_space_map(iot, IPU_CM_BASE(base), IPU_CM_SIZE, 0, &ioh);
+ if (err)
+ goto fail_retarn_cm;
+ sc->cm_ioh = ioh;
+
+ /* map Display Multi FIFO Controller registers */
+ err = bus_space_map(iot, IPU_DMFC_BASE(base), IPU_DMFC_SIZE, 0, &ioh);
+ if (err)
+ goto fail_retarn_dmfc;
+ sc->dmfc_ioh = ioh;
+
+ /* map Display Interface 0 registers */
+ err = bus_space_map(iot, IPU_DI0_BASE(base), IPU_DI0_SIZE, 0, &ioh);
+ if (err)
+ goto fail_retarn_di0;
+ sc->di0_ioh = ioh;
+
+ /* map Display Interface 1 registers */
+ err = bus_space_map(iot, IPU_DI1_BASE(base), IPU_DI0_SIZE, 0, &ioh);
+ if (err)
+ goto fail_retarn_di1;
+ sc->di1_ioh = ioh;
+
+ /* map Display Processor registers */
+ err = bus_space_map(iot, IPU_DP_BASE(base), IPU_DP_SIZE, 0, &ioh);
+ if (err)
+ goto fail_retarn_dp;
+ sc->dp_ioh = ioh;
+
+ /* map Display Controller registers */
+ err = bus_space_map(iot, IPU_DC_BASE(base), IPU_DC_SIZE, 0, &ioh);
+ if (err)
+ goto fail_retarn_dc;
+ sc->dc_ioh = ioh;
+
+ /* map Image DMA Controller registers */
+ err = bus_space_map(iot, IPU_IDMAC_BASE(base), IPU_IDMAC_SIZE, 0,
+ &ioh);
+ if (err)
+ goto fail_retarn_idmac;
+ sc->idmac_ioh = ioh;
+
+ /* map CPMEM registers */
+ err = bus_space_map(iot, IPU_CPMEM_BASE(base), IPU_CPMEM_SIZE, 0,
+ &ioh);
+ if (err)
+ goto fail_retarn_cpmem;
+ sc->cpmem_ioh = ioh;
+
+ /* map DCTEMPL registers */
+ err = bus_space_map(iot, IPU_DCTMPL_BASE(base), IPU_DCTMPL_SIZE, 0,
+ &ioh);
+ if (err)
+ goto fail_retarn_dctmpl;
+ sc->dctmpl_ioh = ioh;
+
+#ifdef notyet
+ sc->ih = imx51_ipuv3_intr_establish(IMX51_INT_IPUV3, IPL_BIO,
+ ipuv3intr, sc);
+ if (sc->ih == NULL) {
+ device_printf(sc->dev,
+ "unable to establish interrupt at irq %d\n",
+ IMX51_INT_IPUV3);
+ return (ENXIO);
+ }
+#endif
+
+ /*
+ * We have to wait until interrupts are enabled.
+ * Mailbox relies on it to get data from VideoCore
+ */
+ ipu3_fb_init(sc);
+
+ sc->sc_info.fb_name = device_get_nameunit(dev);
+
+ ipu3_fb_init_cmap(sc->sc_info.fb_cmap, sc->sc_info.fb_depth);
+ sc->sc_info.fb_cmsize = 16;
+
+ /* Ask newbus to attach framebuffer device to me. */
+ sc->sc_fbd = device_add_child(dev, "fbd", device_get_unit(dev));
+ if (sc->sc_fbd == NULL)
+ device_printf(dev, "Can't attach fbd device\n");
+
+ return (bus_generic_attach(dev));
+
+fail_retarn_dctmpl:
+ bus_space_unmap(sc->iot, sc->cpmem_ioh, IPU_CPMEM_SIZE);
+fail_retarn_cpmem:
+ bus_space_unmap(sc->iot, sc->idmac_ioh, IPU_IDMAC_SIZE);
+fail_retarn_idmac:
+ bus_space_unmap(sc->iot, sc->dc_ioh, IPU_DC_SIZE);
+fail_retarn_dp:
+ bus_space_unmap(sc->iot, sc->dp_ioh, IPU_DP_SIZE);
+fail_retarn_dc:
+ bus_space_unmap(sc->iot, sc->di1_ioh, IPU_DI1_SIZE);
+fail_retarn_di1:
+ bus_space_unmap(sc->iot, sc->di0_ioh, IPU_DI0_SIZE);
+fail_retarn_di0:
+ bus_space_unmap(sc->iot, sc->dmfc_ioh, IPU_DMFC_SIZE);
+fail_retarn_dmfc:
+ bus_space_unmap(sc->iot, sc->dc_ioh, IPU_CM_SIZE);
+fail_retarn_cm:
+ device_printf(sc->dev,
+ "failed to map registers (errno=%d)\n", err);
+ return (err);
+}
+
+static struct fb_info *
+ipu3_fb_getinfo(device_t dev)
+{
+ struct ipu3sc_softc *sc = device_get_softc(dev);
+
+ return (&sc->sc_info);
+}
+
+static device_method_t ipu3_fb_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ipu3_fb_probe),
+ DEVMETHOD(device_attach, ipu3_fb_attach),
+
+ /* Framebuffer service methods */
+ DEVMETHOD(fb_getinfo, ipu3_fb_getinfo),
+ { 0, 0 }
+};
+
+static devclass_t ipu3_fb_devclass;
+
+static driver_t ipu3_fb_driver = {
+ "fb",
+ ipu3_fb_methods,
+ sizeof(struct ipu3sc_softc),
+};
+
+DRIVER_MODULE(fb, simplebus, ipu3_fb_driver, ipu3_fb_devclass, 0, 0);
diff --git a/sys/arm/freescale/imx/imx51_ipuv3reg.h b/sys/arm/freescale/imx/imx51_ipuv3reg.h
new file mode 100644
index 000000000000..137c75033812
--- /dev/null
+++ b/sys/arm/freescale/imx/imx51_ipuv3reg.h
@@ -0,0 +1,924 @@
+/* $NetBSD: imx51_ipuv3reg.h,v 1.1 2012/04/17 10:19:57 bsh Exp $ */
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause AND BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2011, 2012 Genetec Corporation. All rights reserved.
+ * Written by Hashimoto Kenichi for Genetec Corporation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GENETEC CORPORATION ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GENETEC CORPORATION
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) 2012, 2013 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * Portions of this software were developed by Oleksandr Rybalko
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _ARM_IMX_IMX51_IPUV3REG_H
+#define _ARM_IMX_IMX51_IPUV3REG_H
+
+/* register offset address */
+
+/*
+ * CM
+ * Control Module
+ */
+#define IPU_CM_CONF 0x00000000
+#define CM_CONF_CSI_SEL 0x80000000
+#define CM_CONF_IC_INPUT 0x40000000
+#define CM_CONF_CSI1_DATA_SOURCE 0x20000000
+#define CM_CONF_CSI0_DATA_SOURCE 0x10000000
+#define CM_CONF_VDI_DMFC_SYNC 0x08000000
+#define CM_CONF_IC_DMFC_SYNC 0x04000000
+#define CM_CONF_IC_DMFC_SEL 0x02000000
+#define CM_CONF_ISP_DOUBLE_FLOW 0x01000000
+#define CM_CONF_IDMAC_DISABLE 0x00400000
+#define CM_CONF_IPU_DIAGBUS_ON 0x00200000
+#define CM_CONF_IPU_DIAGBUS_MODE 0x001f0000
+#define CM_CONF_VDI_EN 0x00001000
+#define CM_CONF_SISG_EN 0x00000800
+#define CM_CONF_DMFC_EN 0x00000400
+#define CM_CONF_DC_EN 0x00000200
+#define CM_CONF_SMFC_EN 0x00000100
+#define CM_CONF_DI1_EN 0x00000080
+#define CM_CONF_DI0_EN 0x00000040
+#define CM_CONF_DP_EN 0x00000020
+#define CM_CONF_ISP_EN 0x00000010
+#define CM_CONF_IRT_EN 0x00000008
+#define CM_CONF_IC_EN 0x00000004
+#define CM_CONF_CSI1_EN 0x00000002
+#define CM_CONF_CSI0_EN 0x00000001
+#define IPU_SISG_CTRL0 0x00000004
+#define IPU_SISG_CTRL1 0x00000008
+#define IPU_CM_INT_CTRL_1 0x0000003c
+#define IPU_CM_INT_CTRL_2 0x00000040
+#define IPU_CM_INT_CTRL_3 0x00000044
+#define IPU_CM_INT_CTRL_4 0x00000048
+#define IPU_CM_INT_CTRL_5 0x0000004c
+#define IPU_CM_INT_CTRL_6 0x00000050
+#define IPU_CM_INT_CTRL_7 0x00000054
+#define IPU_CM_INT_CTRL_8 0x00000058
+#define IPU_CM_INT_CTRL_9 0x0000005c
+#define IPU_CM_INT_CTRL_10 0x00000060
+#define IPU_CM_INT_CTRL_11 0x00000064
+#define IPU_CM_INT_CTRL_12 0x00000068
+#define IPU_CM_INT_CTRL_13 0x0000006c
+#define IPU_CM_INT_CTRL_14 0x00000070
+#define IPU_CM_INT_CTRL_15 0x00000074
+#define IPU_CM_SDMA_EVENT_1 0x00000078
+#define IPU_CM_SDMA_EVENT_2 0x0000007c
+#define IPU_CM_SDMA_EVENT_3 0x00000080
+#define IPU_CM_SDMA_EVENT_4 0x00000084
+#define IPU_CM_SDMA_EVENT_7 0x00000088
+#define IPU_CM_SDMA_EVENT_8 0x0000008c
+#define IPU_CM_SDMA_EVENT_11 0x00000090
+#define IPU_CM_SDMA_EVENT_12 0x00000094
+#define IPU_CM_SDMA_EVENT_13 0x00000098
+#define IPU_CM_SDMA_EVENT_14 0x0000009c
+#define IPU_CM_SRM_PRI1 0x000000a0
+#define IPU_CM_SRM_PRI2 0x000000a4
+#define IPU_CM_FS_PROC_FLOW1 0x000000a8
+#define IPU_CM_FS_PROC_FLOW2 0x000000ac
+#define IPU_CM_FS_PROC_FLOW3 0x000000b0
+#define IPU_CM_FS_DISP_FLOW1 0x000000b4
+#define IPU_CM_FS_DISP_FLOW2 0x000000b8
+#define IPU_CM_SKIP 0x000000bc
+#define IPU_CM_DISP_ALT_CONF 0x000000c0
+#define IPU_CM_DISP_GEN 0x000000c4
+#define CM_DISP_GEN_DI0_COUNTER_RELEASE 0x01000000
+#define CM_DISP_GEN_DI1_COUNTER_RELEASE 0x00800000
+#define CM_DISP_GEN_MCU_MAX_BURST_STOP 0x00400000
+#define CM_DISP_GEN_MCU_T_SHIFT 18
+#define CM_DISP_GEN_MCU_T(n) ((n) << CM_DISP_GEN_MCU_T_SHIFT)
+#define IPU_CM_DISP_ALT1 0x000000c8
+#define IPU_CM_DISP_ALT2 0x000000cc
+#define IPU_CM_DISP_ALT3 0x000000d0
+#define IPU_CM_DISP_ALT4 0x000000d4
+#define IPU_CM_SNOOP 0x000000d8
+#define IPU_CM_MEM_RST 0x000000dc
+#define CM_MEM_START 0x80000000
+#define CM_MEM_EN 0x007fffff
+#define IPU_CM_PM 0x000000e0
+#define IPU_CM_GPR 0x000000e4
+#define CM_GPR_IPU_CH_BUF1_RDY1_CLR 0x80000000
+#define CM_GPR_IPU_CH_BUF1_RDY0_CLR 0x40000000
+#define CM_GPR_IPU_CH_BUF0_RDY1_CLR 0x20000000
+#define CM_GPR_IPU_CH_BUF0_RDY0_CLR 0x10000000
+#define CM_GPR_IPU_ALT_CH_BUF1_RDY1_CLR 0x08000000
+#define CM_GPR_IPU_ALT_CH_BUF1_RDY0_CLR 0x04000000
+#define CM_GPR_IPU_ALT_CH_BUF0_RDY1_CLR 0x02000000
+#define CM_GPR_IPU_ALT_CH_BUF0_RDY0_CLR 0x01000000
+#define CM_GPR_IPU_DI1_CLK_CHANGE_ACK_DIS 0x00800000
+#define CM_GPR_IPU_DI0_CLK_CHANGE_ACK_DIS 0x00400000
+#define CM_GPR_IPU_CH_BUF2_RDY1_CLR 0x00200000
+#define CM_GPR_IPU_CH_BUF2_RDY0_CLR 0x00100000
+#define CM_GPR_IPU_GP(n) __BIT((n))
+#define IPU_CM_CH_DB_MODE_SEL_0 0x00000150
+#define IPU_CM_CH_DB_MODE_SEL_1 0x00000154
+#define IPU_CM_ALT_CH_DB_MODE_SEL_0 0x00000168
+#define IPU_CM_ALT_CH_DB_MODE_SEL_1 0x0000016c
+#define IPU_CM_CH_TRB_MODE_SEL_0 0x00000178
+#define IPU_CM_CH_TRB_MODE_SEL_1 0x0000017c
+#define IPU_CM_INT_STAT_1 0x00000200
+#define IPU_CM_INT_STAT_2 0x00000204
+#define IPU_CM_INT_STAT_3 0x00000208
+#define IPU_CM_INT_STAT_4 0x0000020c
+#define IPU_CM_INT_STAT_5 0x00000210
+#define IPU_CM_INT_STAT_6 0x00000214
+#define IPU_CM_INT_STAT_7 0x00000218
+#define IPU_CM_INT_STAT_8 0x0000021c
+#define IPU_CM_INT_STAT_9 0x00000220
+#define IPU_CM_INT_STAT_10 0x00000224
+#define IPU_CM_INT_STAT_11 0x00000228
+#define IPU_CM_INT_STAT_12 0x0000022c
+#define IPU_CM_INT_STAT_13 0x00000230
+#define IPU_CM_INT_STAT_14 0x00000234
+#define IPU_CM_INT_STAT_15 0x00000238
+#define IPU_CM_CUR_BUF_0 0x0000023c
+#define IPU_CM_CUR_BUF_1 0x00000240
+#define IPU_CM_ALT_CUR_BUF_0 0x00000244
+#define IPU_CM_ALT_CUR_BUF_1 0x00000248
+#define IPU_CM_SRM_STAT 0x0000024c
+#define IPU_CM_PROC_TASKS_STAT 0x00000250
+#define IPU_CM_DISP_TASKS_STAT 0x00000254
+#define IPU_CM_TRIPLE_CUR_BUF_0 0x00000258
+#define IPU_CM_TRIPLE_CUR_BUF_1 0x0000025c
+#define IPU_CM_TRIPLE_CUR_BUF_2 0x00000260
+#define IPU_CM_TRIPLE_CUR_BUF_3 0x00000264
+#define IPU_CM_CH_BUF0_RDY0 0x00000268
+#define IPU_CM_CH_BUF0_RDY1 0x0000026c
+#define IPU_CM_CH_BUF1_RDY0 0x00000270
+#define IPU_CM_CH_BUF1_RDY1 0x00000274
+#define IPU_CM_ALT_CH_BUF0_RDY0 0x00000278
+#define IPU_CM_ALT_CH_BUF0_RDY1 0x0000027c
+#define IPU_CM_ALT_CH_BUF1_RDY0 0x00000280
+#define IPU_CM_ALT_CH_BUF1_RDY1 0x00000284
+#define IPU_CM_CH_BUF2_RDY0 0x00000288
+#define IPU_CM_CH_BUF2_RDY1 0x0000028c
+
+/*
+ * IDMAC
+ * Image DMA Controller
+ */
+#define IPU_IDMAC_CONF 0x00000000
+#define IPU_IDMAC_CH_EN_1 0x00000004
+#define IPU_IDMAC_CH_EN_2 0x00000008
+#define IPU_IDMAC_SEP_ALPHA 0x0000000c
+#define IPU_IDMAC_ALT_SEP_ALPHA 0x00000010
+#define IPU_IDMAC_CH_PRI_1 0x00000014
+#define IPU_IDMAC_CH_PRI_2 0x00000018
+#define IPU_IDMAC_WM_EN_1 0x0000001c
+#define IPU_IDMAC_WM_EN_2 0x00000020
+#define IPU_IDMAC_LOCK_EN_1 0x00000024
+#define IPU_IDMAC_LOCK_EN_2 0x00000028
+#define IPU_IDMAC_SUB_ADDR_0 0x0000002c
+#define IPU_IDMAC_SUB_ADDR_1 0x00000030
+#define IPU_IDMAC_SUB_ADDR_2 0x00000034
+#define IPU_IDMAC_SUB_ADDR_3 0x00000038
+#define IPU_IDMAC_SUB_ADDR_4 0x0000003c
+#define IPU_IDMAC_BNDM_EN_1 0x00000040
+#define IPU_IDMAC_BNDM_EN_2 0x00000044
+#define IPU_IDMAC_SC_CORD 0x00000048
+#define IPU_IDMAC_SC_CORD1 0x0000004c
+#define IPU_IDMAC_CH_BUSY_1 0x00000100
+#define IPU_IDMAC_CH_BUSY_2 0x00000104
+
+#define CH_PANNEL_BG 23
+#define CH_PANNEL_FG 27
+
+/*
+ * DP
+ * Display Port
+ */
+#define IPU_DP_DEBUG_CNT 0x000000bc
+#define IPU_DP_DEBUG_STAT 0x000000c0
+
+/*
+ * IC
+ * Image Converter
+ */
+#define IPU_IC_CONF 0x00000000
+#define IPU_IC_PRP_ENC_RSC 0x00000004
+#define IPU_IC_PRP_VF_RSC 0x00000008
+#define IPU_IC_PP_RSC 0x0000000c
+#define IPU_IC_CMBP_1 0x00000010
+#define IPU_IC_CMBP_2 0x00000014
+#define IPU_IC_IDMAC_1 0x00000018
+#define IPU_IC_IDMAC_2 0x0000001c
+#define IPU_IC_IDMAC_3 0x00000020
+#define IPU_IC_IDMAC_4 0x00000024
+
+/*
+ * CSI
+ * Camera Sensor Interface
+ */
+#define IPU_CSI0_SENS_CONF 0x00000000
+#define IPU_CSI0_SENS_FRM_SIZE 0x00000004
+#define IPU_CSI0_ACT_FRM_SIZE 0x00000008
+#define IPU_CSI0_OUT_FRM_CTRL 0x0000000c
+#define IPU_CSI0_TST_CTRL 0x00000010
+#define IPU_CSI0_CCIR_CODE_1 0x00000014
+#define IPU_CSI0_CCIR_CODE_2 0x00000018
+#define IPU_CSI0_CCIR_CODE_3 0x0000001c
+#define IPU_CSI0_DI 0x00000020
+#define IPU_CSI0_SKIP 0x00000024
+#define IPU_CSI0_CPD_CTRL 0x00000028
+#define IPU_CSI0_CPD_OFFSET1 0x000000ec
+#define IPU_CSI0_CPD_OFFSET2 0x000000f0
+
+#define IPU_CSI1_SENS_CONF 0x00000000
+#define IPU_CSI1_SENS_FRM_SIZE 0x00000004
+#define IPU_CSI1_ACT_FRM_SIZE 0x00000008
+#define IPU_CSI1_OUT_FRM_CTRL 0x0000000c
+#define IPU_CSI1_TST_CTRL 0x00000010
+#define IPU_CSI1_CCIR_CODE_1 0x00000014
+#define IPU_CSI1_CCIR_CODE_2 0x00000018
+#define IPU_CSI1_CCIR_CODE_3 0x0000001c
+#define IPU_CSI1_DI 0x00000020
+#define IPU_CSI1_SKIP 0x00000024
+#define IPU_CSI1_CPD_CTRL 0x00000028
+#define IPU_CSI1_CPD_OFFSET1 0x000000ec
+#define IPU_CSI1_CPD_OFFSET2 0x000000f0
+
+/*
+ * DI
+ * Display Interface
+ */
+#define IPU_DI_GENERAL 0x00000000
+#define DI_GENERAL_DISP_Y_SEL 0x70000000
+#define DI_GENERAL_CLOCK_STOP_MODE 0x0f000000
+#define DI_GENERAL_DISP_CLOCK_INIT 0x00800000
+#define DI_GENERAL_MASK_SEL 0x00400000
+#define DI_GENERAL_VSYNC_EXT 0x00200000
+#define DI_GENERAL_CLK_EXT 0x00100000
+#define DI_GENERAL_WATCHDOG_MODE 0x000c0000
+#define DI_GENERAL_POLARITY_DISP_CLK 0x00020000
+#define DI_GENERAL_SYNC_COUNT_SEL 0x0000f000
+#define DI_GENERAL_ERR_TREATMENT 0x00000800
+#define DI_GENERAL_ERM_VSYNC_SEL 0x00000400
+#define DI_GENERAL_POLARITY_CS(n) (1 << ((n) + 8))
+#define DI_GENERAL_POLARITY(n) (1 << ((n) - 1))
+
+#define IPU_DI_BS_CLKGEN0 0x00000004
+#define DI_BS_CLKGEN0_OFFSET_SHIFT 16
+#define IPU_DI_BS_CLKGEN1 0x00000008
+#define DI_BS_CLKGEN1_DOWN_SHIFT 16
+#define DI_BS_CLKGEN1_UP_SHIFT 0
+#define IPU_DI_SW_GEN0(n) (0x0000000c + ((n) - 1) * 4)
+#define DI_SW_GEN0_RUN_VAL 0x7ff80000
+#define DI_SW_GEN0_RUN_RESOL 0x00070000
+#define DI_SW_GEN0_OFFSET_VAL 0x00007ff8
+#define DI_SW_GEN0_OFFSET_RESOL 0x00000007
+#define __DI_SW_GEN0(run_val, run_resol, offset_val, offset_resol) \
+ (((run_val) << 19) | ((run_resol) << 16) | \
+ ((offset_val) << 3) | (offset_resol))
+#define IPU_DI_SW_GEN1(n) (0x00000030 + ((n) - 1) * 4)
+#define DI_SW_GEN1_CNT_POL_GEN_EN 0x60000000
+#define DI_SW_GEN1_CNT_AUTO_RELOAD 0x10000000
+#define DI_SW_GEN1_CNT_CLR_SEL 0x0e000000
+#define DI_SW_GEN1_CNT_DOWN 0x01ff0000
+#define DI_SW_GEN1_CNT_POL_TRIG_SEL 0x00007000
+#define DI_SW_GEN1_CNT_POL_CLR_SEL 0x00000e00
+#define DI_SW_GEN1_CNT_UP 0x000001ff
+#define __DI_SW_GEN1(pol_gen_en, auto_reload, clr_sel, down, pol_trig_sel, pol_clr_sel, up) \
+ (((pol_gen_en) << 29) | ((auto_reload) << 28) | \
+ ((clr_sel) << 25) | \
+ ((down) << 16) | ((pol_trig_sel) << 12) | \
+ ((pol_clr_sel) << 9) | (up))
+#define IPU_DI_SYNC_AS_GEN 0x00000054
+#define DI_SYNC_AS_GEN_SYNC_START_EN 0x10000000
+#define DI_SYNC_AS_GEN_VSYNC_SEL 0x0000e000
+#define DI_SYNC_AS_GEN_VSYNC_SEL_SHIFT 13
+#define DI_SYNC_AS_GEN_SYNC_STAR 0x00000fff
+#define IPU_DI_DW_GEN(n) (0x00000058 + (n) * 4)
+#define DI_DW_GEN_ACCESS_SIZE_SHIFT 24
+#define DI_DW_GEN_COMPONNENT_SIZE_SHIFT 16
+#define DI_DW_GEN_PIN_SHIFT(n) (((n) - 11) * 2)
+#define DI_DW_GEN_PIN(n) __BITS(DI_DW_GEN_PIN_SHIFT(n) + 1, \
+ DI_DW_GEN_PIN_SHIFT(n))
+#define IPU_DI_DW_SET(n, m) (0x00000088 + (n) * 4 + (m) * 0x30)
+#define DI_DW_SET_DOWN_SHIFT 16
+#define DI_DW_SET_UP_SHIFT 0
+#define IPU_DI_STP_REP(n) (0x00000148 + ((n - 1) / 2) * 4)
+#define DI_STP_REP_SHIFT(n) (((n - 1) % 2) * 16)
+#define DI_STP_REP_MASK(n) (0x00000fff << DI_STP_REP_SHIFT((n)))
+#define IPU_DI_SER_CONF 0x0000015c
+#define IPU_DI_SSC 0x00000160
+#define IPU_DI_POL 0x00000164
+#define DI_POL_DRDY_POLARITY_17 0x00000040
+#define DI_POL_DRDY_POLARITY_16 0x00000020
+#define DI_POL_DRDY_POLARITY_15 0x00000010
+#define DI_POL_DRDY_POLARITY_14 0x00000008
+#define DI_POL_DRDY_POLARITY_13 0x00000004
+#define DI_POL_DRDY_POLARITY_12 0x00000002
+#define DI_POL_DRDY_POLARITY_11 0x00000001
+#define IPU_DI_AW0 0x00000168
+#define IPU_DI_AW1 0x0000016c
+#define IPU_DI_SCR_CONF 0x00000170
+#define IPU_DI_STAT 0x00000174
+
+/*
+ * SMFC
+ * Sensor Multi FIFO Controller
+ */
+#define IPU_SMFC_MAP 0x00000000
+#define IPU_SMFC_WMC 0x00000004
+#define IPU_SMFC_BS 0x00000008
+
+/*
+ * DC
+ * Display Controller
+ */
+#define IPU_DC_READ_CH_CONF 0x00000000
+#define IPU_DC_READ_CH_ADDR 0x00000004
+
+#define IPU_DC_RL0_CH_0 0x00000008
+#define IPU_DC_RL1_CH_0 0x0000000c
+#define IPU_DC_RL2_CH_0 0x00000010
+#define IPU_DC_RL3_CH_0 0x00000014
+#define IPU_DC_RL4_CH_0 0x00000018
+#define IPU_DC_WR_CH_CONF_1 0x0000001c
+#define IPU_DC_WR_CH_ADDR_1 0x00000020
+#define IPU_DC_RL0_CH_1 0x00000024
+#define IPU_DC_RL1_CH_1 0x00000028
+#define IPU_DC_RL2_CH_1 0x0000002c
+#define IPU_DC_RL3_CH_1 0x00000030
+#define IPU_DC_RL4_CH_1 0x00000034
+#define IPU_DC_WR_CH_CONF_2 0x00000038
+#define IPU_DC_WR_CH_ADDR_2 0x0000003c
+#define IPU_DC_RL0_CH_2 0x00000040
+#define IPU_DC_RL1_CH_2 0x00000044
+#define IPU_DC_RL2_CH_2 0x00000048
+#define IPU_DC_RL3_CH_2 0x0000004c
+#define IPU_DC_RL4_CH_2 0x00000050
+#define IPU_DC_CMD_CH_CONF_3 0x00000054
+#define IPU_DC_CMD_CH_CONF_4 0x00000058
+#define IPU_DC_WR_CH_CONF_5 0x0000005c
+#define IPU_DC_WR_CH_ADDR_5 0x00000060
+#define IPU_DC_RL0_CH_5 0x00000064
+#define IPU_DC_RL1_CH_5 0x00000068
+#define IPU_DC_RL2_CH_5 0x0000006c
+#define IPU_DC_RL3_CH_5 0x00000070
+#define IPU_DC_RL4_CH_5 0x00000074
+#define IPU_DC_WR_CH_CONF_6 0x00000078
+#define IPU_DC_WR_CH_ADDR_6 0x0000007c
+#define IPU_DC_RL0_CH_6 0x00000080
+#define IPU_DC_RL1_CH_6 0x00000084
+#define IPU_DC_RL2_CH_6 0x00000088
+#define IPU_DC_RL3_CH_6 0x0000008c
+#define IPU_DC_RL4_CH_6 0x00000090
+#define IPU_DC_WR_CH_CONF1_8 0x00000094
+#define IPU_DC_WR_CH_CONF2_8 0x00000098
+#define IPU_DC_RL1_CH_8 0x0000009c
+#define IPU_DC_RL2_CH_8 0x000000a0
+#define IPU_DC_RL3_CH_8 0x000000a4
+#define IPU_DC_RL4_CH_8 0x000000a8
+#define IPU_DC_RL5_CH_8 0x000000ac
+#define IPU_DC_RL6_CH_8 0x000000b0
+#define IPU_DC_WR_CH_CONF1_9 0x000000b4
+#define IPU_DC_WR_CH_CONF2_9 0x000000b8
+#define IPU_DC_RL1_CH_9 0x000000bc
+#define IPU_DC_RL2_CH_9 0x000000c0
+#define IPU_DC_RL3_CH_9 0x000000c4
+#define IPU_DC_RL4_CH_9 0x000000c8
+#define IPU_DC_RL5_CH_9 0x000000cc
+#define IPU_DC_RL6_CH_9 0x000000d0
+
+#define IPU_DC_RL(chan_base, evt) ((chan_base) + (evt / 2) *0x4)
+#define DC_RL_CH_0 IPU_DC_RL0_CH_0
+#define DC_RL_CH_1 IPU_DC_RL0_CH_1
+#define DC_RL_CH_2 IPU_DC_RL0_CH_2
+#define DC_RL_CH_5 IPU_DC_RL0_CH_5
+#define DC_RL_CH_6 IPU_DC_RL0_CH_6
+#define DC_RL_CH_8 IPU_DC_RL0_CH_8
+
+#define DC_RL_EVT_NF 0
+#define DC_RL_EVT_NL 1
+#define DC_RL_EVT_EOF 2
+#define DC_RL_EVT_NFIELD 3
+#define DC_RL_EVT_EOL 4
+#define DC_RL_EVT_EOFIELD 5
+#define DC_RL_EVT_NEW_ADDR 6
+#define DC_RL_EVT_NEW_CHAN 7
+#define DC_RL_EVT_NEW_DATA 8
+
+#define IPU_DC_GEN 0x000000d4
+#define IPU_DC_DISP_CONF1_0 0x000000d8
+#define IPU_DC_DISP_CONF1_1 0x000000dc
+#define IPU_DC_DISP_CONF1_2 0x000000e0
+#define IPU_DC_DISP_CONF1_3 0x000000e4
+#define IPU_DC_DISP_CONF2_0 0x000000e8
+#define IPU_DC_DISP_CONF2_1 0x000000ec
+#define IPU_DC_DISP_CONF2_2 0x000000f0
+#define IPU_DC_DISP_CONF2_3 0x000000f4
+#define IPU_DC_DI0_CONF_1 0x000000f8
+#define IPU_DC_DI0_CONF_2 0x000000fc
+#define IPU_DC_DI1_CONF_1 0x00000100
+#define IPU_DC_DI1_CONF_2 0x00000104
+
+#define IPU_DC_MAP_CONF_PNTR(n) (0x00000108 + (n) * 4)
+#define IPU_DC_MAP_CONF_0 0x00000108
+#define IPU_DC_MAP_CONF_1 0x0000010c
+#define IPU_DC_MAP_CONF_2 0x00000110
+#define IPU_DC_MAP_CONF_3 0x00000114
+#define IPU_DC_MAP_CONF_4 0x00000118
+#define IPU_DC_MAP_CONF_5 0x0000011c
+#define IPU_DC_MAP_CONF_6 0x00000120
+#define IPU_DC_MAP_CONF_7 0x00000124
+#define IPU_DC_MAP_CONF_8 0x00000128
+#define IPU_DC_MAP_CONF_9 0x0000012c
+#define IPU_DC_MAP_CONF_10 0x00000130
+#define IPU_DC_MAP_CONF_11 0x00000134
+#define IPU_DC_MAP_CONF_12 0x00000138
+#define IPU_DC_MAP_CONF_13 0x0000013c
+#define IPU_DC_MAP_CONF_14 0x00000140
+
+#define IPU_DC_MAP_CONF_MASK(n) (0x00000144 + (n) * 4)
+#define IPU_DC_MAP_CONF_15 0x00000144
+#define IPU_DC_MAP_CONF_16 0x00000148
+#define IPU_DC_MAP_CONF_17 0x0000014c
+#define IPU_DC_MAP_CONF_18 0x00000150
+#define IPU_DC_MAP_CONF_19 0x00000154
+#define IPU_DC_MAP_CONF_20 0x00000158
+#define IPU_DC_MAP_CONF_21 0x0000015c
+#define IPU_DC_MAP_CONF_22 0x00000160
+#define IPU_DC_MAP_CONF_23 0x00000164
+#define IPU_DC_MAP_CONF_24 0x00000168
+#define IPU_DC_MAP_CONF_25 0x0000016c
+#define IPU_DC_MAP_CONF_26 0x00000170
+
+#define IPU_DC_UGDE(m, n) (0x00000174 + (m) * 0x10 + (n) +4)
+#define IPU_DC_UGDE0_0 0x00000174
+#define IPU_DC_UGDE0_1 0x00000178
+#define IPU_DC_UGDE0_2 0x0000017c
+#define IPU_DC_UGDE0_3 0x00000180
+#define IPU_DC_UGDE1_0 0x00000184
+#define IPU_DC_UGDE1_1 0x00000188
+#define IPU_DC_UGDE1_2 0x0000018c
+#define IPU_DC_UGDE1_3 0x00000190
+#define IPU_DC_UGDE2_0 0x00000194
+#define IPU_DC_UGDE2_1 0x00000198
+#define IPU_DC_UGDE2_2 0x0000019c
+#define IPU_DC_UGDE2_3 0x000001a0
+#define IPU_DC_UGDE3_0 0x000001a4
+#define IPU_DC_UGDE3_1 0x000001a8
+#define IPU_DC_UGDE3_2 0x000001ac
+#define IPU_DC_UGDE3_3 0x000001b0
+#define IPU_DC_LLA0 0x000001b4
+#define IPU_DC_LLA1 0x000001b8
+#define IPU_DC_R_LLA0 0x000001bc
+#define IPU_DC_R_LLA1 0x000001c0
+#define IPU_DC_WR_CH_ADDR_5_ALT 0x000001c4
+#define IPU_DC_STAT 0x000001c8
+
+/*
+ * DMFC
+ * Display Multi FIFO Controller
+ */
+#define IPU_DMFC_RD_CHAN 0x00000000
+#define DMFC_RD_CHAN_PPW_C 0x03000000
+#define DMFC_RD_CHAN_WM_DR_0 0x00e00000
+#define DMFC_RD_CHAN_WM_SET_0 0x001c0000
+#define DMFC_RD_CHAN_WM_EN_0 0x00020000
+#define DMFC_RD_CHAN_BURST_SIZE_0 0x000000c0
+#define IPU_DMFC_WR_CHAN 0x00000004
+#define DMFC_WR_CHAN_BUSRT_SIZE_2C 0xc0000000
+#define DMFC_WR_CHAN_FIFO_SIZE_2C 0x38000000
+#define DMFC_WR_CHAN_ST_ADDR_2C 0x07000000
+#define DMFC_WR_CHAN_BURST_SIZE_1C 0x00c00000
+#define DMFC_WR_CHAN_FIFO_SIZE_1C 0x00380000
+#define DMFC_WR_CHAN_ST_ADDR_1C 0x00070000
+#define DMFC_WR_CHAN_BURST_SIZE_2 0x0000c000
+#define DMFC_WR_CHAN_FIFO_SIZE_2 0x00003800
+#define DMFC_WR_CHAN_ST_ADDR_2 0x00000700
+#define DMFC_WR_CHAN_BURST_SIZE_1 0x000000c0
+#define DMFC_WR_CHAN_FIFO_SIZE_1 0x00000038
+#define DMFC_WR_CHAN_ST_ADDR_1 0x00000007
+#define IPU_DMFC_WR_CHAN_DEF 0x00000008
+#define DMFC_WR_CHAN_DEF_WM_CLR_2C 0xe0000000
+#define DMFC_WR_CHAN_DEF_WM_SET_2C 0x1c000000
+#define DMFC_WR_CHAN_DEF_WM_EN_2C 0x02000000
+#define DMFC_WR_CHAN_DEF_WM_CLR_1C 0x00e00000
+#define DMFC_WR_CHAN_DEF_WM_SET_1C 0x001c0000
+#define DMFC_WR_CHAN_DEF_WM_EN_1C 0x00020000
+#define DMFC_WR_CHAN_DEF_WM_CLR_2 0x0000e000
+#define DMFC_WR_CHAN_DEF_WM_SET_2 0x00001c00
+#define DMFC_WR_CHAN_DEF_WM_EN_2 0x00000200
+#define DMFC_WR_CHAN_DEF_WM_CLR_1 0x000000e0
+#define DMFC_WR_CHAN_DEF_WM_SET_1 0x0000000c
+#define DMFC_WR_CHAN_DEF_WM_EN_1 0x00000002
+#define IPU_DMFC_DP_CHAN 0x0000000c
+#define DMFC_DP_CHAN_BUSRT_SIZE_6F 0xc0000000
+#define DMFC_DP_CHAN_FIFO_SIZE_6F 0x38000000
+#define DMFC_DP_CHAN_ST_ADDR_6F 0x07000000
+#define DMFC_DP_CHAN_BURST_SIZE_6B 0x00c00000
+#define DMFC_DP_CHAN_FIFO_SIZE_6B 0x00380000
+#define DMFC_DP_CHAN_ST_ADDR_6B 0x00070000
+#define DMFC_DP_CHAN_BURST_SIZE_5F 0x0000c000
+#define DMFC_DP_CHAN_FIFO_SIZE_5F 0x00003800
+#define DMFC_DP_CHAN_ST_ADDR_5F 0x00000700
+#define DMFC_DP_CHAN_BURST_SIZE_5B 0x000000c0
+#define DMFC_DP_CHAN_FIFO_SIZE_5B 0x00000038
+#define DMFC_DP_CHAN_ST_ADDR_5B 0x00000007
+#define IPU_DMFC_DP_CHAN_DEF 0x00000010
+#define DMFC_DP_CHAN_DEF_WM_CLR_6F 0xe0000000
+#define DMFC_DP_CHAN_DEF_WM_SET_6F 0x1c000000
+#define DMFC_DP_CHAN_DEF_WM_EN_6F 0x02000000
+#define DMFC_DP_CHAN_DEF_WM_CLR_6B 0x00e00000
+#define DMFC_DP_CHAN_DEF_WM_SET_6B 0x001c0000
+#define DMFC_DP_CHAN_DEF_WM_EN_6B 0x00020000
+#define DMFC_DP_CHAN_DEF_WM_CLR_5F 0x0000e000
+#define DMFC_DP_CHAN_DEF_WM_SET_5F 0x00001c00
+#define DMFC_DP_CHAN_DEF_WM_EN_5F 0x00000200
+#define DMFC_DP_CHAN_DEF_WM_CLR_5B 0x000000e0
+#define DMFC_DP_CHAN_DEF_WM_SET_5B 0x0000001c
+#define DMFC_DP_CHAN_DEF_WM_EN_5B 0x00000002
+#define IPU_DMFC_GENERAL1 0x00000014
+#define DMFC_GENERAL1_WAIT4EOT_9 0x01000000
+#define DMFC_GENERAL1_WAIT4EOT_6F 0x00800000
+#define DMFC_GENERAL1_WAIT4EOT_6B 0x00400000
+#define DMFC_GENERAL1_WAIT4EOT_5F 0x00200000
+#define DMFC_GENERAL1_WAIT4EOT_5B 0x00100000
+#define DMFC_GENERAL1_WAIT4EOT_4 0x00080000
+#define DMFC_GENERAL1_WAIT4EOT_3 0x00040000
+#define DMFC_GENERAL1_WAIT4EOT_2 0x00020000
+#define DMFC_GENERAL1_WAIT4EOT_1 0x00010000
+#define DMFC_GENERAL1_WM_CLR_9 0x0000e000
+#define DMFC_GENERAL1_WM_SET_9 0x00001c00
+#define DMFC_GENERAL1_BURST_SIZE_9 0x00000060
+#define DMFC_GENERAL1_DCDP_SYNC_PR 0x00000003
+#define DCDP_SYNC_PR_FORBIDDEN 0
+#define DCDP_SYNC_PR_DC_DP 1
+#define DCDP_SYNC_PR_DP_DC 2
+#define DCDP_SYNC_PR_ROUNDROBIN 3
+#define IPU_DMFC_GENERAL2 0x00000018
+#define DMFC_GENERAL2_FRAME_HEIGHT_RD 0x1fff0000
+#define DMFC_GENERAL2_FRAME_WIDTH_RD 0x00001fff
+#define IPU_DMFC_IC_CTRL 0x0000001c
+#define DMFC_IC_CTRL_IC_FRAME_HEIGHT_RD 0xfff80000
+#define DMFC_IC_CTRL_IC_FRAME_WIDTH_RD 0x0007ffc0
+#define DMFC_IC_CTRL_IC_PPW_C 0x00000030
+#define DMFC_IC_CTRL_IC_IN_PORT 0x00000007
+#define IC_IN_PORT_CH28 0
+#define IC_IN_PORT_CH41 1
+#define IC_IN_PORT_DISABLE 2
+#define IC_IN_PORT_CH23 4
+#define IC_IN_PORT_CH27 5
+#define IC_IN_PORT_CH24 6
+#define IC_IN_PORT_CH29 7
+#define IPU_DMFC_WR_CHAN_ALT 0x00000020
+#define IPU_DMFC_WR_CHAN_DEF_ALT 0x00000024
+#define IPU_DMFC_DP_CHAN_ALT 0x00000028
+#define IPU_DMFC_DP_CHAN_DEF_ALT 0x0000002c
+#define DMFC_DP_CHAN_DEF_ALT_WM_CLR_6F_ALT 0xe0000000
+#define DMFC_DP_CHAN_DEF_ALT_WM_SET_6F_ALT 0x1c000000
+#define DMFC_DP_CHAN_DEF_ALT_WM_EN_6F_ALT 0x02000000
+#define DMFC_DP_CHAN_DEF_ALT_WM_CLR_6B_ALT 0x00e00000
+#define DMFC_DP_CHAN_DEF_ALT_WM_SET_6B_ALT 0x001c0000
+#define DMFC_DP_CHAN_DEF_ALT_WM_EN_6B_ALT 0x00020000
+#define DMFC_DP_CHAN_DEF_ALT_WM_CLR_5B_ALT 0x000000e0
+#define DMFC_DP_CHAN_DEF_ALT_WM_SET_5B_ALT 0x0000001c
+#define DMFC_DP_CHAN_DEF_ALT_WM_EN_5B_ALT 0x00000002
+#define IPU_DMFC_GENERAL1_ALT 0x00000030
+#define DMFC_GENERAL1_ALT_WAIT4EOT_6F_ALT 0x00800000
+#define DMFC_GENERAL1_ALT_WAIT4EOT_6B_ALT 0x00400000
+#define DMFC_GENERAL1_ALT_WAIT4EOT_5B_ALT 0x00100000
+#define DMFC_GENERAL1_ALT_WAIT4EOT_2_ALT 0x00020000
+#define IPU_DMFC_STAT 0x00000034
+#define DMFC_STAT_IC_BUFFER_EMPTY 0x02000000
+#define DMFC_STAT_IC_BUFFER_FULL 0x01000000
+#define DMFC_STAT_FIFO_EMPTY(n) __BIT(12 + (n))
+#define DMFC_STAT_FIFO_FULL(n) __BIT((n))
+
+/*
+ * VCI
+ * Video De Interkacing Module
+ */
+#define IPU_VDI_FSIZE 0x00000000
+#define IPU_VDI_C 0x00000004
+
+/*
+ * DP
+ * Display Processor
+ */
+#define IPU_DP_COM_CONF_SYNC 0x00000000
+#define DP_FG_EN_SYNC 0x00000001
+#define DP_DP_GWAM_SYNC 0x00000004
+#define IPU_DP_GRAPH_WIND_CTRL_SYNC 0x00000004
+#define IPU_DP_FG_POS_SYNC 0x00000008
+#define IPU_DP_CUR_POS_SYNC 0x0000000c
+#define IPU_DP_CUR_MAP_SYNC 0x00000010
+#define IPU_DP_CSC_SYNC_0 0x00000054
+#define IPU_DP_CSC_SYNC_1 0x00000058
+#define IPU_DP_CUR_POS_ALT 0x0000005c
+#define IPU_DP_COM_CONF_ASYNC0 0x00000060
+#define IPU_DP_GRAPH_WIND_CTRL_ASYNC0 0x00000064
+#define IPU_DP_FG_POS_ASYNC0 0x00000068
+#define IPU_DP_CUR_POS_ASYNC0 0x0000006c
+#define IPU_DP_CUR_MAP_ASYNC0 0x00000070
+#define IPU_DP_CSC_ASYNC0_0 0x000000b4
+#define IPU_DP_CSC_ASYNC0_1 0x000000b8
+#define IPU_DP_COM_CONF_ASYNC1 0x000000bc
+#define IPU_DP_GRAPH_WIND_CTRL_ASYNC1 0x000000c0
+#define IPU_DP_FG_POS_ASYNC1 0x000000c4
+#define IPU_DP_CUR_POS_ASYNC1 0x000000c8
+#define IPU_DP_CUR_MAP_ASYNC1 0x000000cc
+#define IPU_DP_CSC_ASYNC1_0 0x00000110
+#define IPU_DP_CSC_ASYNC1_1 0x00000114
+
+/* IDMA parameter */
+ /*
+ * non-Interleaved parameter
+ *
+ * param 0: XV W0[ 9: 0]
+ * YV W0[18:10]
+ * XB W0[31:19]
+ * param 1: YB W0[43:32]
+ * NSB W0[44]
+ * CF W0[45]
+ * UBO W0[61:46]
+ * param 2: UBO W0[67:62]
+ * VBO W0[89:68]
+ * IOX W0[93:90]
+ * RDRW W0[94]
+ * Reserved W0[95]
+ * param 3: Reserved W0[112:96]
+ * S0 W0[113]
+ * BNDM W0[116:114]
+ * BM W0[118:117]
+ * ROT W0[119]
+ * HF W0[120]
+ * VF W0[121]
+ * THF W0[122]
+ * CAP W0[123]
+ * CAE W0[124]
+ * FW W0[127:125]
+ * param 4: FW W0[137:128]
+ * FH W0[149:138]
+ * param 5: EBA0 W1[28:0]
+ * EBA1 W1[31:29]
+ * param 6: EBA1 W1[57:32]
+ * ILO W1[63:58]
+ * param 7: ILO W1[77:64]
+ * NPB W1[84:78]
+ * PFS W1[88:85]
+ * ALU W1[89]
+ * ALBM W1[92:90]
+ * ID W1[94:93]
+ * TH W1[95]
+ * param 8: TH W1[101:96]
+ * SLY W1[115:102]
+ * WID3 W1[127:125]
+ * param 9: SLUV W1[141:128]
+ * CRE W1[149]
+ *
+ * Interleaved parameter
+ *
+ * param 0: XV W0[ 9: 0]
+ * YV W0[18:10]
+ * XB W0[31:19]
+ * param 1: YB W0[43:32]
+ * NSB W0[44]
+ * CF W0[45]
+ * SX W0[57:46]
+ * SY W0[61:58]
+ * param 2: SY W0[68:62]
+ * NS W0[78:69]
+ * SDX W0[85:79]
+ * SM W0[95:86]
+ * param 3: SCC W0[96]
+ * SCE W0[97]
+ * SDY W0[104:98]
+ * SDRX W0[105]
+ * SDRY W0[106]
+ * BPP W0[109:107]
+ * DEC_SEL W0[111:110]
+ * DIM W0[112]
+ * SO W0[113]
+ * BNDM W0[116:114]
+ * BM W0[118:117]
+ * ROT W0[119]
+ * HF W0[120]
+ * VF W0[121]
+ * THF W0[122]
+ * CAP W0[123]
+ * CAE W0[124]
+ * FW W0[127:125]
+ * param 4: FW W0[137:128]
+ * FH W0[149:138]
+ * param 5: EBA0 W1[28:0]
+ * EBA1 W1[31:29]
+ * param 6: EBA1 W1[57:32]
+ * ILO W1[63:58]
+ * param 7: ILO W1[77:64]
+ * NPB W1[84:78]
+ * PFS W1[88:85]
+ * ALU W1[89]
+ * ALBM W1[92:90]
+ * ID W1[94:93]
+ * TH W1[95]
+ * param 8: TH W1[101:96]
+ * SL W1[115:102]
+ * WID0 W1[118:116]
+ * WID1 W1[121:119]
+ * WID2 W1[124:122]
+ * WID3 W1[127:125]
+ * param 9: OFS0 W1[132:128]
+ * OFS1 W1[137:133]
+ * OFS2 W1[142:138]
+ * OFS3 W1[147:143]
+ * SXYS W1[148]
+ * CRE W1[149]
+ * DEC_SEL2 W1[150]
+ */
+
+#define __IDMA_PARAM(word, shift, size) \
+ ((((word) & 0xff) << 16) | (((shift) & 0xff) << 8) | ((size) & 0xff))
+
+/* non-Interleaved parameter */
+/* W0 */
+#define IDMAC_Ch_PARAM_XV __IDMA_PARAM(0, 0, 10)
+#define IDMAC_Ch_PARAM_YV __IDMA_PARAM(0, 10, 9)
+#define IDMAC_Ch_PARAM_XB __IDMA_PARAM(0, 19, 13)
+#define IDMAC_Ch_PARAM_YB __IDMA_PARAM(0, 32, 12)
+#define IDMAC_Ch_PARAM_NSB __IDMA_PARAM(0, 44, 1)
+#define IDMAC_Ch_PARAM_CF __IDMA_PARAM(0, 45, 1)
+#define IDMAC_Ch_PARAM_UBO __IDMA_PARAM(0, 46, 22)
+#define IDMAC_Ch_PARAM_VBO __IDMA_PARAM(0, 68, 22)
+#define IDMAC_Ch_PARAM_IOX __IDMA_PARAM(0, 90, 4)
+#define IDMAC_Ch_PARAM_RDRW __IDMA_PARAM(0, 94, 1)
+#define IDMAC_Ch_PARAM_S0 __IDMA_PARAM(0,113, 1)
+#define IDMAC_Ch_PARAM_BNDM __IDMA_PARAM(0,114, 3)
+#define IDMAC_Ch_PARAM_BM __IDMA_PARAM(0,117, 2)
+#define IDMAC_Ch_PARAM_ROT __IDMA_PARAM(0,119, 1)
+#define IDMAC_Ch_PARAM_HF __IDMA_PARAM(0,120, 1)
+#define IDMAC_Ch_PARAM_VF __IDMA_PARAM(0,121, 1)
+#define IDMAC_Ch_PARAM_THF __IDMA_PARAM(0,122, 1)
+#define IDMAC_Ch_PARAM_CAP __IDMA_PARAM(0,123, 1)
+#define IDMAC_Ch_PARAM_CAE __IDMA_PARAM(0,124, 1)
+#define IDMAC_Ch_PARAM_FW __IDMA_PARAM(0,125, 13)
+#define IDMAC_Ch_PARAM_FH __IDMA_PARAM(0,138, 12)
+/* W1 */
+#define IDMAC_Ch_PARAM_EBA0 __IDMA_PARAM(1, 0, 29)
+#define IDMAC_Ch_PARAM_EBA1 __IDMA_PARAM(1, 29, 29)
+#define IDMAC_Ch_PARAM_ILO __IDMA_PARAM(1, 58, 20)
+#define IDMAC_Ch_PARAM_NPB __IDMA_PARAM(1, 78, 7)
+#define IDMAC_Ch_PARAM_PFS __IDMA_PARAM(1, 85, 4)
+#define IDMAC_Ch_PARAM_ALU __IDMA_PARAM(1, 89, 1)
+#define IDMAC_Ch_PARAM_ALBM __IDMA_PARAM(1, 90, 3)
+#define IDMAC_Ch_PARAM_ID __IDMA_PARAM(1, 93, 2)
+#define IDMAC_Ch_PARAM_TH __IDMA_PARAM(1, 95, 7)
+#define IDMAC_Ch_PARAM_SL __IDMA_PARAM(1,102, 14)
+#define IDMAC_Ch_PARAM_WID3 __IDMA_PARAM(1,125, 3)
+#define IDMAC_Ch_PARAM_SLUV __IDMA_PARAM(1,128, 14)
+#define IDMAC_Ch_PARAM_CRE __IDMA_PARAM(1,149, 1)
+
+/* Interleaved parameter */
+/* W0 */
+#define IDMAC_Ch_PARAM_XV __IDMA_PARAM(0, 0, 10)
+#define IDMAC_Ch_PARAM_YV __IDMA_PARAM(0, 10, 9)
+#define IDMAC_Ch_PARAM_XB __IDMA_PARAM(0, 19, 13)
+#define IDMAC_Ch_PARAM_YB __IDMA_PARAM(0, 32, 12)
+#define IDMAC_Ch_PARAM_NSB __IDMA_PARAM(0, 44, 1)
+#define IDMAC_Ch_PARAM_CF __IDMA_PARAM(0, 45, 1)
+#define IDMAC_Ch_PARAM_SX __IDMA_PARAM(0, 46, 12)
+#define IDMAC_Ch_PARAM_SY __IDMA_PARAM(0, 58, 11)
+#define IDMAC_Ch_PARAM_NS __IDMA_PARAM(0, 69, 10)
+#define IDMAC_Ch_PARAM_SDX __IDMA_PARAM(0, 79, 7)
+#define IDMAC_Ch_PARAM_SM __IDMA_PARAM(0, 86, 10)
+#define IDMAC_Ch_PARAM_SCC __IDMA_PARAM(0, 96, 1)
+#define IDMAC_Ch_PARAM_SCE __IDMA_PARAM(0, 97, 1)
+#define IDMAC_Ch_PARAM_SDY __IDMA_PARAM(0, 98, 7)
+#define IDMAC_Ch_PARAM_SDRX __IDMA_PARAM(0,105, 1)
+#define IDMAC_Ch_PARAM_SDRY __IDMA_PARAM(0,106, 1)
+#define IDMAC_Ch_PARAM_BPP __IDMA_PARAM(0,107, 3)
+#define IDMAC_Ch_PARAM_DEC_SEL __IDMA_PARAM(0,110, 2)
+#define IDMAC_Ch_PARAM_DIM __IDMA_PARAM(0,112, 1)
+#define IDMAC_Ch_PARAM_SO __IDMA_PARAM(0,113, 1)
+#define IDMAC_Ch_PARAM_BNDM __IDMA_PARAM(0,114, 3)
+#define IDMAC_Ch_PARAM_BM __IDMA_PARAM(0,117, 2)
+#define IDMAC_Ch_PARAM_ROT __IDMA_PARAM(0,119, 1)
+#define IDMAC_Ch_PARAM_HF __IDMA_PARAM(0,120, 1)
+#define IDMAC_Ch_PARAM_VF __IDMA_PARAM(0,121, 1)
+#define IDMAC_Ch_PARAM_THF __IDMA_PARAM(0,122, 1)
+#define IDMAC_Ch_PARAM_CAP __IDMA_PARAM(0,123, 1)
+#define IDMAC_Ch_PARAM_CAE __IDMA_PARAM(0,124, 1)
+#define IDMAC_Ch_PARAM_FW __IDMA_PARAM(0,125, 13)
+#define IDMAC_Ch_PARAM_FH __IDMA_PARAM(0,138, 12)
+/* W1 */
+#define IDMAC_Ch_PARAM_EBA0 __IDMA_PARAM(1, 0, 29)
+#define IDMAC_Ch_PARAM_EBA1 __IDMA_PARAM(1, 29, 29)
+#define IDMAC_Ch_PARAM_ILO __IDMA_PARAM(1, 58, 20)
+#define IDMAC_Ch_PARAM_NPB __IDMA_PARAM(1, 78, 7)
+#define IDMAC_Ch_PARAM_PFS __IDMA_PARAM(1, 85, 4)
+#define IDMAC_Ch_PARAM_ALU __IDMA_PARAM(1, 89, 1)
+#define IDMAC_Ch_PARAM_ALBM __IDMA_PARAM(1, 90, 3)
+#define IDMAC_Ch_PARAM_ID __IDMA_PARAM(1, 93, 2)
+#define IDMAC_Ch_PARAM_TH __IDMA_PARAM(1, 95, 7)
+#define IDMAC_Ch_PARAM_SL __IDMA_PARAM(1,102, 14)
+#define IDMAC_Ch_PARAM_WID0 __IDMA_PARAM(1,116, 3)
+#define IDMAC_Ch_PARAM_WID1 __IDMA_PARAM(1,119, 3)
+#define IDMAC_Ch_PARAM_WID2 __IDMA_PARAM(1,122, 3)
+#define IDMAC_Ch_PARAM_WID3 __IDMA_PARAM(1,125, 3)
+#define IDMAC_Ch_PARAM_OFS0 __IDMA_PARAM(1,128, 5)
+#define IDMAC_Ch_PARAM_OFS1 __IDMA_PARAM(1,133, 5)
+#define IDMAC_Ch_PARAM_OFS2 __IDMA_PARAM(1,138, 5)
+#define IDMAC_Ch_PARAM_OFS3 __IDMA_PARAM(1,143, 5)
+#define IDMAC_Ch_PARAM_SXYS __IDMA_PARAM(1,148, 1)
+#define IDMAC_Ch_PARAM_CRE __IDMA_PARAM(1,149, 1)
+#define IDMAC_Ch_PARAM_DEC_SEL2 __IDMA_PARAM(1,150, 1)
+
+/* XXX Temp */
+#define GPUMEM_BASE 0x20000000
+#define GPUMEM_SIZE 0x20000
+
+#define GPU_BASE 0x30000000
+#define GPU_SIZE 0x10000000
+
+/*
+ * Image Processing Unit
+ *
+ * All addresses are relative to the base SoC address.
+ */
+#define IPU_CM_BASE(_base) ((_base) + 0x1e000000)
+#define IPU_CM_SIZE 0x8000
+#define IPU_IDMAC_BASE(_base) ((_base) + 0x1e008000)
+#define IPU_IDMAC_SIZE 0x8000
+#define IPU_DP_BASE(_base) ((_base) + 0x1e018000)
+#define IPU_DP_SIZE 0x8000
+#define IPU_IC_BASE(_base) ((_base) + 0x1e020000)
+#define IPU_IC_SIZE 0x8000
+#define IPU_IRT_BASE(_base) ((_base) + 0x1e028000)
+#define IPU_IRT_SIZE 0x8000
+#define IPU_CSI0_BASE(_base) ((_base) + 0x1e030000)
+#define IPU_CSI0_SIZE 0x8000
+#define IPU_CSI1_BASE(_base) ((_base) + 0x1e038000)
+#define IPU_CSI1_SIZE 0x8000
+#define IPU_DI0_BASE(_base) ((_base) + 0x1e040000)
+#define IPU_DI0_SIZE 0x8000
+#define IPU_DI1_BASE(_base) ((_base) + 0x1e048000)
+#define IPU_DI1_SIZE 0x8000
+#define IPU_SMFC_BASE(_base) ((_base) + 0x1e050000)
+#define IPU_SMFC_SIZE 0x8000
+#define IPU_DC_BASE(_base) ((_base) + 0x1e058000)
+#define IPU_DC_SIZE 0x8000
+#define IPU_DMFC_BASE(_base) ((_base) + 0x1e060000)
+#define IPU_DMFC_SIZE 0x8000
+#define IPU_VDI_BASE(_base) ((_base) + 0x1e068000)
+#define IPU_VDI_SIZE 0x8000
+#define IPU_CPMEM_BASE(_base) ((_base) + 0x1f000000)
+#define IPU_CPMEM_SIZE 0x20000
+#define IPU_LUT_BASE(_base) ((_base) + 0x1f020000)
+#define IPU_LUT_SIZE 0x20000
+#define IPU_SRM_BASE(_base) ((_base) + 0x1f040000)
+#define IPU_SRM_SIZE 0x20000
+#define IPU_TPM_BASE(_base) ((_base) + 0x1f060000)
+#define IPU_TPM_SIZE 0x20000
+#define IPU_DCTMPL_BASE(_base) ((_base) + 0x1f080000)
+#define IPU_DCTMPL_SIZE 0x20000
+
+#endif /* _ARM_IMX_IMX51_IPUV3REG_H */
diff --git a/sys/arm/freescale/imx/imx51_machdep.c b/sys/arm/freescale/imx/imx51_machdep.c
new file mode 100644
index 000000000000..3a675f34b720
--- /dev/null
+++ b/sys/arm/freescale/imx/imx51_machdep.c
@@ -0,0 +1,104 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Ian Lepore <ian@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "opt_platform.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/reboot.h>
+#include <sys/devmap.h>
+
+#include <vm/vm.h>
+
+#include <machine/bus.h>
+#include <machine/machdep.h>
+#include <machine/platformvar.h>
+
+#include <arm/freescale/imx/imx_machdep.h>
+
+#include "platform_if.h"
+
+static platform_attach_t imx51_attach;
+static platform_devmap_init_t imx51_devmap_init;
+static platform_cpu_reset_t imx51_cpu_reset;
+
+static int
+imx51_attach(platform_t plat)
+{
+
+ /* XXX - Get rid of this stuff soon. */
+ boothowto |= RB_VERBOSE|RB_MULTIPLE;
+ bootverbose = 1;
+
+ return (0);
+}
+
+/*
+ * Set up static device mappings. This is hand-optimized platform-specific
+ * config data which covers most of the common on-chip devices with a few 1MB
+ * section mappings.
+ *
+ * Notably missing are entries for GPU, IPU, in general anything video related.
+ */
+static int
+imx51_devmap_init(platform_t plat)
+{
+
+ devmap_add_entry(0x70000000, 0x00100000);
+ devmap_add_entry(0x73f00000, 0x00100000);
+ devmap_add_entry(0x83f00000, 0x00100000);
+
+ return (0);
+}
+
+static void
+imx51_cpu_reset(platform_t plat)
+{
+
+ imx_wdog_cpu_reset(0x73F98000);
+}
+
+u_int
+imx_soc_type(void)
+{
+ return (IMXSOC_51);
+}
+
+static platform_method_t imx51_methods[] = {
+ PLATFORMMETHOD(platform_attach, imx51_attach),
+ PLATFORMMETHOD(platform_devmap_init, imx51_devmap_init),
+ PLATFORMMETHOD(platform_cpu_reset, imx51_cpu_reset),
+
+ PLATFORMMETHOD_END,
+};
+
+FDT_PLATFORM_DEF(imx51, "i.MX51", 0, "fsl,imx51", 100);
diff --git a/sys/arm/freescale/imx/imx51_sdmareg.h b/sys/arm/freescale/imx/imx51_sdmareg.h
new file mode 100644
index 000000000000..71c28f6bec14
--- /dev/null
+++ b/sys/arm/freescale/imx/imx51_sdmareg.h
@@ -0,0 +1,144 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012, 2013 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Oleksandr Rybalko under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/* Internal Registers definition for Freescale i.MX515 SDMA Core */
+
+/* SDMA Core Instruction Memory Space */
+#define SDMA_IBUS_ROM_ADDR_BASE 0x0000
+#define SDMA_IBUS_ROM_ADDR_SIZE 0x07ff
+#define SDMA_IBUS_RAM_ADDR_BASE 0x1000
+#define SDMA_IBUS_RAM_ADDR_SIZE 0x1fff
+
+/* SDMA Core Internal Registers */
+#define SDMA_MC0PTR 0x7000 /* AP (MCU) Channel 0 Pointer R */
+
+#define SDMA_CCPTR 0x7002 /* Current Channel Pointer R */
+#define SDMA_ECTL_CCPTR_MASK 0x0000ffff
+#define SDMA_ECTL_CCPTR_SHIFT 0
+
+#define SDMA_CCR 0x7003 /* Current Channel Register R */
+#define SDMA_ECTL_CCR_MASK 0x0000001f
+#define SDMA_ECTL_CCR_SHIFT 0
+
+#define SDMA_NCR 0x7004 /* Highest Pending Channel Register R */
+#define SDMA_ECTL_NCR_MASK 0x0000001f
+#define SDMA_ECTL_NCR_SHIFT 0
+
+#define SDMA_EVENTS 0x7005 /* External DMA Requests Mirror R */
+
+#define SDMA_CCPRI 0x7006 /* Current Channel Priority R */
+#define SDMA_ECTL_CCPRI_MASK 0x00000007
+#define SDMA_ECTL_CCPRI_SHIFT 0
+
+#define SDMA_NCPRI 0x7007 /* Next Channel Priority R */
+#define SDMA_ECTL_NCPRI_MASK 0x00000007
+#define SDMA_ECTL_NCPRI_SHIFT 0
+
+#define SDMA_ECOUNT 0x7009 /* OnCE Event Cell Counter R/W */
+#define SDMA_ECTL_ECOUNT_MASK 0x0000ffff
+#define SDMA_ECTL_ECOUNT_SHIFT 0
+
+#define SDMA_ECTL 0x700A /* OnCE Event Cell Control Register R/W */
+#define SDMA_ECTL_EN (1 << 13)
+#define SDMA_ECTL_CNT (1 << 12)
+#define SDMA_ECTL_ECTC_MASK 0x00000c00
+#define SDMA_ECTL_ECTC_SHIFT 10
+#define SDMA_ECTL_DTC_MASK 0x00000300
+#define SDMA_ECTL_DTC_SHIFT 8
+#define SDMA_ECTL_ATC_MASK 0x000000c0
+#define SDMA_ECTL_ATC_SHIFT 6
+#define SDMA_ECTL_ABTC_MASK 0x00000030
+#define SDMA_ECTL_ABTC_SHIFT 4
+#define SDMA_ECTL_AATC_MASK 0x0000000c
+#define SDMA_ECTL_AATC_SHIFT 2
+#define SDMA_ECTL_ATS_MASK 0x00000003
+#define SDMA_ECTL_ATS_SHIFT 0
+
+#define SDMA_EAA 0x700B /* OnCE Event Address Register A R/W */
+#define SDMA_ECTL_EAA_MASK 0x0000ffff
+#define SDMA_ECTL_EAA_SHIFT 0
+
+#define SDMA_EAB 0x700C /* OnCE Event Cell Address Register B R/W */
+#define SDMA_ECTL_EAB_MASK 0x0000ffff
+#define SDMA_ECTL_EAB_SHIFT 0
+
+#define SDMA_EAM 0x700D /* OnCE Event Cell Address Mask R/W */
+#define SDMA_ECTL_EAM_MASK 0x0000ffff
+#define SDMA_ECTL_EAM_SHIFT 0
+
+#define SDMA_ED 0x700E /* OnCE Event Cell Data Register R/W */
+#define SDMA_EDM 0x700F /* OnCE Event Cell Data Mask R/W */
+#define SDMA_RTB 0x7018 /* OnCE Real-Time Buffer R/W */
+
+#define SDMA_TB 0x7019 /* OnCE Trace Buffer R */
+#define SDMA_TB_TBF (1 << 28)
+#define SDMA_TB_TADDR_MASK 0x0fffc000
+#define SDMA_TB_TADDR_SHIFT 14
+#define SDMA_TB_CHFADDR_MASK 0x00003fff
+#define SDMA_TB_CHFADDR_SHIFT 0
+
+#define SDMA_OSTAT 0x701A /* OnCE Status R */
+#define SDMA_OSTAT_PST_MASK 0x0000f000
+#define SDMA_OSTAT_PST_SHIFT 12
+#define SDMA_OSTAT_RCV (1 << 11)
+#define SDMA_OSTAT_EDR (1 << 10)
+#define SDMA_OSTAT_ODR (1 << 9)
+#define SDMA_OSTAT_SWB (1 << 8)
+#define SDMA_OSTAT_MST (1 << 7)
+#define SDMA_OSTAT_ECDR_MASK 0x00000007
+#define SDMA_OSTAT_ECDR_SHIFT 0
+
+#define SDMA_MCHN0ADDR 0x701C /* Channel 0 Boot Address R */
+#define SDMA_MCHN0ADDR_SMS_Z (1 << 14)
+#define SDMA_MCHN0ADDR_CHN0ADDR_MASK 0x00003fff
+#define SDMA_MCHN0ADDR_CHN0ADDR_SHIFT 0
+
+#define SDMA_MODE 0x701D /* Mode Status Register R */
+#define SDMA_MODE_DSPCtrl (1 << 3)
+#define SDMA_MODE_AP_END (1 << 0)
+
+#define SDMA_LOCK 0x701E /* Lock Status Register R */
+#define SDMA_LOCK_LOCK (1 << 0)
+
+#define SDMA_EVENTS2 0x701F /* External DMA Requests Mirror #2 R */
+
+#define SDMA_HE 0x7020 /* AP Enable Register R */
+#define SDMA_PRIV 0x7022 /* Current Channel BP Privilege Register R */
+#define SDMA_PRIV_BPPRIV (1 << 0)
+#define SDMA_PRF_CNT 0x7023 /* Profile Free Running Register R/W */
+#define SDMA_PRF_CNT_SEL_MASK 0xc0000000
+#define SDMA_PRF_CNT_SEL_SHIFT 30
+#define SDMA_PRF_CNT_EN (1 << 29)
+#define SDMA_PRF_CNT_OFL (1 << 22)
+#define SDMA_PRF_CNT_COUNTER_MASK 0x003fffff
+#define SDMA_PRF_CNT_COUNTER_SHIFT 0
diff --git a/sys/arm/freescale/imx/imx51_ssireg.h b/sys/arm/freescale/imx/imx51_ssireg.h
new file mode 100644
index 000000000000..3846b4950750
--- /dev/null
+++ b/sys/arm/freescale/imx/imx51_ssireg.h
@@ -0,0 +1,182 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012, 2013 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Oleksandr Rybalko under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/* Registers definition for Freescale i.MX515 Synchronous Serial Interface */
+
+#define IMX51_SSI_STX0_REG 0x0000 /* SSI TX Data Register 0 */
+#define IMX51_SSI_STX1_REG 0x0004 /* SSI TX Data Register 1 */
+#define IMX51_SSI_SRX0_REG 0x0008 /* SSI RX Data Register 0 */
+#define IMX51_SSI_SRX1_REG 0x000C /* SSI RX Data Register 1 */
+#define IMX51_SSI_SCR_REG 0x0010 /* SSI Control Register */
+#define SSI_SCR_RFR_CLK_DIS (1 << 11) /* RX FC Disable */
+#define SSI_SCR_TFR_CLK_DIS (1 << 10) /* TX FC Disable */
+#define SSI_SCR_CLK_IST (1 << 9) /* Clock Idle */
+#define SSI_SCR_TCH_EN (1 << 8) /* 2Chan Enable */
+#define SSI_SCR_SYS_CLK_EN (1 << 7) /* System Clock En */
+#define SSI_SCR_MODE_NORMAL (0 << 5)
+#define SSI_SCR_MODE_I2S_MASTER (1 << 5)
+#define SSI_SCR_MODE_I2S_SLAVE (2 << 5)
+#define SSI_SCR_MODE_MASK (3 << 5)
+#define SSI_SCR_SYN (1 << 4) /* Sync Mode */
+#define SSI_SCR_NET (1 << 3) /* Network Mode */
+#define SSI_SCR_RE (1 << 2) /* RX Enable */
+#define SSI_SCR_TE (1 << 1) /* TX Enable */
+#define SSI_SCR_SSIEN (1 << 0) /* SSI Enable */
+
+#define IMX51_SSI_SISR_REG 0x0014 /* SSI Interrupt Status Register */
+#define SSI_SISR_RFRC (1 << 24) /* RX Frame Complete */
+#define SSI_SIR_TFRC (1 << 23) /* TX Frame Complete */
+#define SSI_SIR_CMDAU (1 << 18) /* Command Address Updated */
+#define SSI_SIR_CMDDU (1 << 17) /* Command Data Updated */
+#define SSI_SIR_RXT (1 << 16) /* RX Tag Updated */
+#define SSI_SIR_RDR1 (1 << 15) /* RX Data Ready 1 */
+#define SSI_SIR_RDR0 (1 << 14) /* RX Data Ready 0 */
+#define SSI_SIR_TDE1 (1 << 13) /* TX Data Reg Empty 1 */
+#define SSI_SIR_TDE0 (1 << 12) /* TX Data Reg Empty 0 */
+#define SSI_SIR_ROE1 (1 << 11) /* RXer Overrun Error 1 */
+#define SSI_SIR_ROE0 (1 << 10) /* RXer Overrun Error 0 */
+#define SSI_SIR_TUE1 (1 << 9) /* TXer Underrun Error 1 */
+#define SSI_SIR_TUE0 (1 << 8) /* TXer Underrun Error 0 */
+#define SSI_SIR_TFS (1 << 7) /* TX Frame Sync */
+#define SSI_SIR_RFS (1 << 6) /* RX Frame Sync */
+#define SSI_SIR_TLS (1 << 5) /* TX Last Time Slot */
+#define SSI_SIR_RLS (1 << 4) /* RX Last Time Slot */
+#define SSI_SIR_RFF1 (1 << 3) /* RX FIFO Full 1 */
+#define SSI_SIR_RFF0 (1 << 2) /* RX FIFO Full 0 */
+#define SSI_SIR_TFE1 (1 << 1) /* TX FIFO Empty 1 */
+#define SSI_SIR_TFE0 (1 << 0) /* TX FIFO Empty 0 */
+
+#define IMX51_SSI_SIER_REG 0x0018 /* SSI Interrupt Enable Register */
+/* 24-23 Enable Bit (See SISR) */
+#define SSI_SIER_RDMAE (1 << 22) /* RX DMA Enable */
+#define SSI_SIER_RIE (1 << 21) /* RX Interrupt Enable */
+#define SSI_SIER_TDMAE (1 << 20) /* TX DMA Enable */
+#define SSI_SIER_TIE (1 << 19) /* TX Interrupt Enable */
+/* 18-0 Enable Bits (See SISR) */
+
+#define IMX51_SSI_STCR_REG 0x001C /* SSI TX Configuration Register */
+#define SSI_STCR_TXBIT0 (1 << 9) /* TX Bit 0 */
+#define SSI_STCR_TFEN1 (1 << 8) /* TX FIFO Enable 1 */
+#define SSI_STCR_TFEN0 (1 << 7) /* TX FIFO Enable 0 */
+#define SSI_STCR_TFDIR (1 << 6) /* TX Frame Direction */
+#define SSI_STCR_TXDIR (1 << 5) /* TX Clock Direction */
+#define SSI_STCR_TSHFD (1 << 4) /* TX Shift Direction */
+#define SSI_STCR_TSCKP (1 << 3) /* TX Clock Polarity */
+#define SSI_STCR_TFSI (1 << 2) /* TX Frame Sync Invert */
+#define SSI_STCR_TFSL (1 << 1) /* TX Frame Sync Length */
+#define SSI_STCR_TEFS (1 << 0) /* TX Early Frame Sync */
+
+#define IMX51_SSI_SRCR_REG 0x0020 /* SSI RX Configuration Register */
+#define SSI_SRCR_RXEXT (1 << 10) /* RX Data Extension */
+#define SSI_SRCR_RXBIT0 (1 << 9) /* RX Bit 0 */
+#define SSI_SRCR_RFEN1 (1 << 8) /* RX FIFO Enable 1 */
+#define SSI_SRCR_RFEN0 (1 << 7) /* RX FIFO Enable 0 */
+#define SSI_SRCR_RFDIR (1 << 6) /* RX Frame Direction */
+#define SSI_SRCR_RXDIR (1 << 5) /* RX Clock Direction */
+#define SSI_SRCR_RSHFD (1 << 4) /* RX Shift Direction */
+#define SSI_SRCR_RSCKP (1 << 3) /* RX Clock Polarity */
+#define SSI_SRCR_RFSI (1 << 2) /* RX Frame Sync Invert */
+#define SSI_SRCR_RFSL (1 << 1) /* RX Frame Sync Length */
+#define SSI_SRCR_REFS (1 << 0) /* RX Early Frame Sync */
+
+#define IMX51_SSI_STCCR_REG 0x0024 /* TX Clock Control */
+#define IMX51_SSI_SRCCR_REG 0x0028 /* RX Clock Control */
+#define SSI_SXCCR_DIV2 (1 << 18) /* Divide By 2 */
+#define SSI_SXCCR_PSR (1 << 17) /* Prescaler Range */
+#define SSI_SXCCR_WL_MASK 0x0001e000
+#define SSI_SXCCR_WL_SHIFT 13 /* Word Length Control */
+#define SSI_SXCCR_DC_MASK 0x00001f00
+#define SSI_SXCCR_DC_SHIFT 8 /* Frame Rate Divider */
+#define SSI_SXCCR_PM_MASK 0x000000ff
+#define SSI_SXCCR_PM_SHIFT 0 /* Prescaler Modulus */
+
+#define IMX51_SSI_SFCSR_REG 0x002C /* SSI FIFO Control/Status Register */
+#define SSI_SFCSR_RFCNT1_MASK 0xf0000000
+#define SSI_SFCSR_RFCNT1_SHIFT 28 /* RX FIFO Counter 1 */
+#define SSI_SFCSR_TFCNT1_MASK 0x0f000000
+#define SSI_SFCSR_TFCNT1_SHIFT 24 /* TX FIFO Counter 1 */
+#define SSI_SFCSR_RFWM1_MASK 0x00f00000
+#define SSI_SFCSR_RFWM1_SHIFT 20 /* RX FIFO Full WaterMark 1 */
+#define SSI_SFCSR_TFWM1_MASK 0x000f0000
+#define SSI_SFCSR_TFWM1_SHIFT 16 /* TX FIFO Empty WaterMark 1 */
+#define SSI_SFCSR_RFCNT0_MASK 0x0000f000
+#define SSI_SFCSR_RFCNT0_SHIFT 12 /* RX FIFO Counter 0 */
+#define SSI_SFCSR_TFCNT0_MASK 0x00000f00
+#define SSI_SFCSR_TFCNT0_SHIFT 8 /* TX FIFO Counter 0 */
+#define SSI_SFCSR_RFWM0_MASK 0x000000f0
+#define SSI_SFCSR_RFWM0_SHIFT 4 /* RX FIFO Full WaterMark 0 */
+#define SSI_SFCSR_TFWM0_MASK 0x0000000f
+#define SSI_SFCSR_TFWM0_SHIFT 0 /* TX FIFO Empty WaterMark 0 */
+
+#define IMX51_SSI_STR_REG 0x0030 /* SSI Test Register1 */
+#define SSI_STR_TEST (1 << 15) /* Test Mode */
+#define SSI_STR_RCK2TCK (1 << 14) /* RX<->TX Clock Loop Back */
+#define SSI_STR_RFS2TFS (1 << 13) /* RX<->TX Frame Loop Back */
+#define SSI_STR_RXSTATE_MASK 0x00001f00
+#define SSI_STR_RXSTATE_SHIFT 8 /* RXer State Machine Status */
+#define SSI_STR_TXD2RXD (1 << 7) /* TX<->RX Data Loop Back */
+#define SSI_STR_TCK2RCK (1 << 6) /* TX<->RX Clock Loop Back */
+#define SSI_STR_TFS2RFS (1 << 5) /* TX<->RX Frame Loop Back */
+#define SSI_STR_TXSTATE_MASK 0x0000001f
+#define SSI_STR_TXSTATE_SHIFT 0 /* TXer State Machine Status */
+
+#define IMX51_SSI_SOR_REG 0x0034 /* SSI Option Register2 */
+#define SSI_SOR_CLKOFF (1 << 6) /* Clock Off */
+#define SSI_SOR_RX_CLR (1 << 5) /* RXer Clear */
+#define SSI_SOR_TX_CLR (1 << 4) /* TXer Clear */
+#define SSI_SOR_INIT (1 << 3) /* Initialize */
+#define SSI_SOR_WAIT_MASK 0x00000006
+#define SSI_SOR_INIT_SHIFT 1 /* Wait */
+#define SSI_SOR_SYNRST (1 << 0) /* Frame Sync Reset */
+
+#define IMX51_SSI_SACNT_REG 0x0038 /* SSI AC97 Control Register */
+#define SSI_SACNT_FRDIV_MASK 0x000007e0
+#define SSI_SACNT_FRDIV_SHIFT 5 /* Frame Rate Divider */
+#define SSI_SACNT_WR (1 << 4) /* Write Command */
+#define SSI_SACNT_RD (1 << 3) /* Read Command */
+#define SSI_SACNT_TIF (1 << 2) /* Tag in FIFO */
+#define SSI_SACNT_FV (1 << 1) /* Fixed/Variable Operation */
+#define SSI_SACNT_AC97EN (1 << 0) /* AC97 Mode Enable */
+
+#define IMX51_SSI_SACADD_REG 0x003C /* SSI AC97 Command Address Register */
+#define SSI_SACADD_MASK 0x0007ffff
+#define IMX51_SSI_SACDAT_REG 0x0040 /* SSI AC97 Command Data Register */
+#define SSI_SACDAT_MASK 0x000fffff
+#define IMX51_SSI_SATAG_REG 0x0044 /* SSI AC97 Tag Register */
+#define SSI_SATAG_MASK 0x0000ffff
+#define IMX51_SSI_STMSK_REG 0x0048 /* SSI TX Time Slot Mask Register */
+#define IMX51_SSI_SRMSK_REG 0x004C /* SSI RX Time Slot Mask Register */
+#define IMX51_SSI_SACCST_REG 0x0050 /* SSI AC97 Channel Status Register */
+#define IMX51_SSI_SACCEN_REG 0x0054 /* SSI AC97 Channel Enable Register */
+#define IMX51_SSI_SACCDIS_REG 0x0058 /* SSI AC97 Channel Disable Register */
+#define SSI_SAC_MASK 0x000003ff /* SACCST,SACCEN,SACCDIS */
diff --git a/sys/arm/freescale/imx/imx51_tzicreg.h b/sys/arm/freescale/imx/imx51_tzicreg.h
new file mode 100644
index 000000000000..0c3c811e00de
--- /dev/null
+++ b/sys/arm/freescale/imx/imx51_tzicreg.h
@@ -0,0 +1,87 @@
+/* $NetBSD: imx51_tzicreg.h,v 1.1 2010/11/13 07:11:03 bsh Exp $ */
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause AND BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2010 Genetec Corporation. All rights reserved.
+ * Written by Hashimoto Kenichi for Genetec Corporation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GENETEC CORPORATION ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GENETEC CORPORATION
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) 2012, 2013 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * Portions of this software were developed by Oleksandr Rybalko
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _IMX51_TZICREG_H_
+#define _IMX51_TZICREG_H_
+
+#include <sys/cdefs.h>
+
+#define TZIC_SIZE 0x4000
+#define TZIC_INTCNTL 0x0000
+#define INTCNTL_NSEN_MASK 0x80000000
+#define INTCNTL_NSEN 0x00010000
+#define INTCNTL_EN 0x00000001
+#define TZIC_INTTYPE 0x0004
+#define TZIC_PRIOMASK 0x000c
+#define TZIC_SYNCCTRL 0x0010
+#define TZIC_DSMINT 0x0014
+#define TZIC_INTSEC(n) (0x0080 + 0x04 * (n))
+#define TZIC_ENSET(n) (0x0100 + 0x04 * (n))
+#define TZIC_ENCLEAR(n) (0x0180 + 0x04 * (n))
+#define TZIC_SRCSET(n) (0x0200 + 0x04 * (n))
+#define TZIC_SRCCLAR(n) (0x0280 + 0x04 * (n))
+#define TZIC_PRIORITY(n) (0x0400 + 0x04 * (n))
+#define TZIC_PND(n) (0x0d00 + 0x04 * (n))
+#define TZIC_HIPND(n) (0x0d80 + 0x04 * (n))
+#define TZIC_WAKEUP(n) (0x0e00 + 0x04 * (n))
+#define TZIC_SWINT 0x0f00
+
+#define TZIC_INTNUM 128
+#endif /* _IMX51_TZICRREG_H_ */
diff --git a/sys/arm/freescale/imx/imx53_machdep.c b/sys/arm/freescale/imx/imx53_machdep.c
new file mode 100644
index 000000000000..8530ed193520
--- /dev/null
+++ b/sys/arm/freescale/imx/imx53_machdep.c
@@ -0,0 +1,100 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Ian Lepore <ian@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "opt_platform.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/reboot.h>
+#include <sys/devmap.h>
+
+#include <vm/vm.h>
+
+#include <machine/bus.h>
+#include <machine/machdep.h>
+#include <machine/platformvar.h>
+
+#include <arm/freescale/imx/imx_machdep.h>
+
+#include "platform_if.h"
+
+static platform_attach_t imx53_attach;
+static platform_devmap_init_t imx53_devmap_init;
+static platform_cpu_reset_t imx53_cpu_reset;
+
+static int
+imx53_attach(platform_t plat)
+{
+
+ return (0);
+}
+
+/*
+ * Set up static device mappings. This is hand-optimized platform-specific
+ * config data which covers most of the common on-chip devices with a few 1MB
+ * section mappings.
+ *
+ * Notably missing are entries for GPU, IPU, in general anything video related.
+ */
+static int
+imx53_devmap_init(platform_t plat)
+{
+
+ devmap_add_entry(0x50000000, 0x00100000);
+ devmap_add_entry(0x53f00000, 0x00100000);
+ devmap_add_entry(0x63f00000, 0x00100000);
+
+ return (0);
+}
+
+static void
+imx53_cpu_reset(platform_t plat)
+{
+
+ imx_wdog_cpu_reset(0x53F98000);
+}
+
+u_int
+imx_soc_type(void)
+{
+ return (IMXSOC_53);
+}
+
+static platform_method_t imx53_methods[] = {
+ PLATFORMMETHOD(platform_attach, imx53_attach),
+ PLATFORMMETHOD(platform_devmap_init, imx53_devmap_init),
+ PLATFORMMETHOD(platform_cpu_reset, imx53_cpu_reset),
+
+ PLATFORMMETHOD_END,
+};
+
+FDT_PLATFORM_DEF(imx53, "i.MX53", 0, "fsl,imx53", 100);
diff --git a/sys/arm/freescale/imx/imx6_ahci.c b/sys/arm/freescale/imx/imx6_ahci.c
new file mode 100644
index 000000000000..8b49c71c34a0
--- /dev/null
+++ b/sys/arm/freescale/imx/imx6_ahci.c
@@ -0,0 +1,363 @@
+/*-
+ * Copyright (c) 2017 Rogiel Sulzbach <rogiel@allogica.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+
+#include <machine/bus.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/ahci/ahci.h>
+#include <arm/freescale/imx/imx_iomuxreg.h>
+#include <arm/freescale/imx/imx_iomuxvar.h>
+#include <arm/freescale/imx/imx_ccmvar.h>
+
+#define SATA_TIMER1MS 0x000000e0
+
+#define SATA_P0PHYCR 0x00000178
+#define SATA_P0PHYCR_CR_READ (1 << 19)
+#define SATA_P0PHYCR_CR_WRITE (1 << 18)
+#define SATA_P0PHYCR_CR_CAP_DATA (1 << 17)
+#define SATA_P0PHYCR_CR_CAP_ADDR (1 << 16)
+#define SATA_P0PHYCR_CR_DATA_IN(v) ((v) & 0xffff)
+
+#define SATA_P0PHYSR 0x0000017c
+#define SATA_P0PHYSR_CR_ACK (1 << 18)
+#define SATA_P0PHYSR_CR_DATA_OUT(v) ((v) & 0xffff)
+
+/* phy registers */
+#define SATA_PHY_CLOCK_RESET 0x7f3f
+#define SATA_PHY_CLOCK_RESET_RST (1 << 0)
+
+#define SATA_PHY_LANE0_OUT_STAT 0x2003
+#define SATA_PHY_LANE0_OUT_STAT_RX_PLL_STATE (1 << 1)
+
+static struct ofw_compat_data compat_data[] = {
+ {"fsl,imx6q-ahci", true},
+ {NULL, false}
+};
+
+static int
+imx6_ahci_phy_ctrl(struct ahci_controller* sc, uint32_t bitmask, bool on)
+{
+ uint32_t v;
+ int timeout;
+ bool state;
+
+ v = ATA_INL(sc->r_mem, SATA_P0PHYCR);
+ if (on) {
+ v |= bitmask;
+ } else {
+ v &= ~bitmask;
+ }
+ ATA_OUTL(sc->r_mem, SATA_P0PHYCR, v);
+
+ for (timeout = 5000; timeout > 0; --timeout) {
+ v = ATA_INL(sc->r_mem, SATA_P0PHYSR);
+ state = (v & SATA_P0PHYSR_CR_ACK) == SATA_P0PHYSR_CR_ACK;
+ if(state == on) {
+ break;
+ }
+ DELAY(100);
+ }
+
+ if (timeout > 0) {
+ return (0);
+ }
+
+ return (ETIMEDOUT);
+}
+
+static int
+imx6_ahci_phy_addr(struct ahci_controller* sc, uint32_t addr)
+{
+ int error;
+
+ DELAY(100);
+
+ ATA_OUTL(sc->r_mem, SATA_P0PHYCR, addr);
+
+ error = imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_CAP_ADDR, true);
+ if (error != 0) {
+ device_printf(sc->dev,
+ "%s: timeout on SATA_P0PHYCR_CR_CAP_ADDR=1\n",
+ __FUNCTION__);
+ return (error);
+ }
+
+ error = imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_CAP_ADDR, false);
+ if (error != 0) {
+ device_printf(sc->dev,
+ "%s: timeout on SATA_P0PHYCR_CR_CAP_ADDR=0\n",
+ __FUNCTION__);
+ return (error);
+ }
+
+ return (0);
+}
+
+static int
+imx6_ahci_phy_write(struct ahci_controller* sc, uint32_t addr,
+ uint16_t data)
+{
+ int error;
+
+ error = imx6_ahci_phy_addr(sc, addr);
+ if (error != 0) {
+ device_printf(sc->dev, "%s: error on imx6_ahci_phy_addr\n",
+ __FUNCTION__);
+ return (error);
+ }
+
+ ATA_OUTL(sc->r_mem, SATA_P0PHYCR, data);
+
+ error = imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_CAP_DATA, true);
+ if (error != 0) {
+ device_printf(sc->dev,
+ "%s: error on SATA_P0PHYCR_CR_CAP_DATA=1\n", __FUNCTION__);
+ return (error);
+ }
+ if (imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_CAP_DATA, false) != 0) {
+ device_printf(sc->dev,
+ "%s: error on SATA_P0PHYCR_CR_CAP_DATA=0\n", __FUNCTION__);
+ return (error);
+ }
+
+ if ((addr == SATA_PHY_CLOCK_RESET) && data) {
+ /* we can't check ACK after RESET */
+ ATA_OUTL(sc->r_mem, SATA_P0PHYCR,
+ SATA_P0PHYCR_CR_DATA_IN(data) | SATA_P0PHYCR_CR_WRITE);
+ return (0);
+ }
+
+ error = imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_WRITE, true);
+ if (error != 0) {
+ device_printf(sc->dev, "%s: error on SATA_P0PHYCR_CR_WRITE=1\n",
+ __FUNCTION__);
+ return (error);
+ }
+
+ error = imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_WRITE, false);
+ if (error != 0) {
+ device_printf(sc->dev, "%s: error on SATA_P0PHYCR_CR_WRITE=0\n",
+ __FUNCTION__);
+ return (error);
+ }
+
+ return (0);
+}
+
+static int
+imx6_ahci_phy_read(struct ahci_controller* sc, uint32_t addr, uint16_t* val)
+{
+ int error;
+ uint32_t v;
+
+ error = imx6_ahci_phy_addr(sc, addr);
+ if (error != 0) {
+ device_printf(sc->dev, "%s: error on imx6_ahci_phy_addr\n",
+ __FUNCTION__);
+ return (error);
+ }
+
+ error = imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_READ, true);
+ if (error != 0) {
+ device_printf(sc->dev, "%s: error on SATA_P0PHYCR_CR_READ=1\n",
+ __FUNCTION__);
+ return (error);
+ }
+
+ v = ATA_INL(sc->r_mem, SATA_P0PHYSR);
+
+ error = imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_READ, false);
+ if (error != 0) {
+ device_printf(sc->dev, "%s: error on SATA_P0PHYCR_CR_READ=0\n",
+ __FUNCTION__);
+ return (error);
+ }
+
+ *val = SATA_P0PHYSR_CR_DATA_OUT(v);
+ return (0);
+}
+
+static int
+imx6_ahci_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev)) {
+ return (ENXIO);
+ }
+
+ if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) {
+ return (ENXIO);
+ }
+ device_set_desc(dev, "i.MX6 Integrated AHCI controller");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+imx6_ahci_attach(device_t dev)
+{
+ struct ahci_controller* ctlr;
+ uint16_t pllstat;
+ uint32_t v;
+ int error, timeout;
+
+ ctlr = device_get_softc(dev);
+
+ /* Power up the controller and phy. */
+ error = imx6_ccm_sata_enable();
+ if (error != 0) {
+ device_printf(dev, "error enabling controller and phy\n");
+ return (error);
+ }
+
+ ctlr->vendorid = 0;
+ ctlr->deviceid = 0;
+ ctlr->subvendorid = 0;
+ ctlr->subdeviceid = 0;
+ ctlr->numirqs = 1;
+ ctlr->r_rid = 0;
+ if ((ctlr->r_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &ctlr->r_rid, RF_ACTIVE)) == NULL) {
+ return (ENXIO);
+ }
+
+ v = imx_iomux_gpr_get(IOMUX_GPR13);
+ /* Clear out existing values; these numbers are bitmasks. */
+ v &= ~(IOMUX_GPR13_SATA_PHY_8(7) |
+ IOMUX_GPR13_SATA_PHY_7(0x1f) |
+ IOMUX_GPR13_SATA_PHY_6(7) |
+ IOMUX_GPR13_SATA_SPEED(1) |
+ IOMUX_GPR13_SATA_PHY_5(1) |
+ IOMUX_GPR13_SATA_PHY_4(7) |
+ IOMUX_GPR13_SATA_PHY_3(0xf) |
+ IOMUX_GPR13_SATA_PHY_2(0x1f) |
+ IOMUX_GPR13_SATA_PHY_1(1) |
+ IOMUX_GPR13_SATA_PHY_0(1));
+ /* setting */
+ v |= IOMUX_GPR13_SATA_PHY_8(5) | /* Rx 3.0db */
+ IOMUX_GPR13_SATA_PHY_7(0x12) | /* Rx SATA2m */
+ IOMUX_GPR13_SATA_PHY_6(3) | /* Rx DPLL mode */
+ IOMUX_GPR13_SATA_SPEED(1) | /* 3.0GHz */
+ IOMUX_GPR13_SATA_PHY_5(0) | /* SpreadSpectram */
+ IOMUX_GPR13_SATA_PHY_4(4) | /* Tx Attenuation 9/16 */
+ IOMUX_GPR13_SATA_PHY_3(0) | /* Tx Boost 0db */
+ IOMUX_GPR13_SATA_PHY_2(0x11) | /* Tx Level 1.104V */
+ IOMUX_GPR13_SATA_PHY_1(1); /* PLL clock enable */
+ imx_iomux_gpr_set(IOMUX_GPR13, v);
+
+ /* phy reset */
+ error = imx6_ahci_phy_write(ctlr, SATA_PHY_CLOCK_RESET,
+ SATA_PHY_CLOCK_RESET_RST);
+ if (error != 0) {
+ device_printf(dev, "cannot reset PHY\n");
+ goto fail;
+ }
+
+ for (timeout = 50; timeout > 0; --timeout) {
+ DELAY(100);
+ error = imx6_ahci_phy_read(ctlr, SATA_PHY_LANE0_OUT_STAT,
+ &pllstat);
+ if (error != 0) {
+ device_printf(dev, "cannot read LANE0 status\n");
+ goto fail;
+ }
+ if (pllstat & SATA_PHY_LANE0_OUT_STAT_RX_PLL_STATE) {
+ break;
+ }
+ }
+ if (timeout <= 0) {
+ device_printf(dev, "time out reading LANE0 status\n");
+ error = ETIMEDOUT;
+ goto fail;
+ }
+
+ /* Support Staggered Spin-up */
+ v = ATA_INL(ctlr->r_mem, AHCI_CAP);
+ ATA_OUTL(ctlr->r_mem, AHCI_CAP, v | AHCI_CAP_SSS);
+
+ /* Ports Implemented. must set 1 */
+ v = ATA_INL(ctlr->r_mem, AHCI_PI);
+ ATA_OUTL(ctlr->r_mem, AHCI_PI, v | (1 << 0));
+
+ /* set 1ms-timer = AHB clock / 1000 */
+ ATA_OUTL(ctlr->r_mem, SATA_TIMER1MS,
+ imx_ccm_ahb_hz() / 1000);
+
+ /*
+ * Note: ahci_attach will release ctlr->r_mem on errors automatically
+ */
+ return (ahci_attach(dev));
+
+fail:
+ bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem);
+ return (error);
+}
+
+static int
+imx6_ahci_detach(device_t dev)
+{
+
+ return (ahci_detach(dev));
+}
+
+static device_method_t imx6_ahci_ata_methods[] = {
+ /* device probe, attach and detach methods */
+ DEVMETHOD(device_probe, imx6_ahci_probe),
+ DEVMETHOD(device_attach, imx6_ahci_attach),
+ DEVMETHOD(device_detach, imx6_ahci_detach),
+
+ /* ahci bus methods */
+ DEVMETHOD(bus_print_child, ahci_print_child),
+ DEVMETHOD(bus_alloc_resource, ahci_alloc_resource),
+ DEVMETHOD(bus_release_resource, ahci_release_resource),
+ DEVMETHOD(bus_setup_intr, ahci_setup_intr),
+ DEVMETHOD(bus_teardown_intr, ahci_teardown_intr),
+ DEVMETHOD(bus_child_location_str, ahci_child_location_str),
+
+ DEVMETHOD_END
+};
+
+static driver_t ahci_ata_driver = {
+ "ahci",
+ imx6_ahci_ata_methods,
+ sizeof(struct ahci_controller)
+};
+
+DRIVER_MODULE(imx6_ahci, simplebus, ahci_ata_driver, ahci_devclass, 0, 0);
+MODULE_DEPEND(imx6_ahci, ahci, 1, 1, 1);
+SIMPLEBUS_PNP_INFO(compat_data)
diff --git a/sys/arm/freescale/imx/imx6_anatop.c b/sys/arm/freescale/imx/imx6_anatop.c
new file mode 100644
index 000000000000..2a1b06bbd6ce
--- /dev/null
+++ b/sys/arm/freescale/imx/imx6_anatop.c
@@ -0,0 +1,818 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Ian Lepore <ian@freebsd.org>
+ * Copyright (c) 2014 Steven Lawrance <stl@koffein.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Analog PLL and power regulator driver for Freescale i.MX6 family of SoCs.
+ * Also, temperature montoring and cpu frequency control. It was Freescale who
+ * kitchen-sinked this device, not us. :)
+ *
+ * We don't really do anything with analog PLLs, but the registers for
+ * controlling them belong to the same block as the power regulator registers.
+ * Since the newbus hierarchy makes it hard for anyone other than us to get at
+ * them, we just export a couple public functions to allow the imx6 CCM clock
+ * driver to read and write those registers.
+ *
+ * We also don't do anything about power regulation yet, but when the need
+ * arises, this would be the place for that code to live.
+ *
+ * I have no idea where the "anatop" name comes from. It's in the standard DTS
+ * source describing i.MX6 SoCs, and in the linux and u-boot code which comes
+ * from Freescale, but it's not in the SoC manual.
+ *
+ * Note that temperature values throughout this code are handled in two types of
+ * units. Items with '_cnt' in the name use the hardware temperature count
+ * units (higher counts are lower temperatures). Items with '_val' in the name
+ * are deci-Celcius, which are converted to/from deci-Kelvins in the sysctl
+ * handlers (dK is the standard unit for temperature in sysctl).
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/callout.h>
+#include <sys/kernel.h>
+#include <sys/limits.h>
+#include <sys/sysctl.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+
+#include <arm/arm/mpcore_timervar.h>
+#include <arm/freescale/fsl_ocotpreg.h>
+#include <arm/freescale/fsl_ocotpvar.h>
+#include <arm/freescale/imx/imx_ccmvar.h>
+#include <arm/freescale/imx/imx_machdep.h>
+#include <arm/freescale/imx/imx6_anatopreg.h>
+#include <arm/freescale/imx/imx6_anatopvar.h>
+
+static struct resource_spec imx6_anatop_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+#define MEMRES 0
+#define IRQRES 1
+
+struct imx6_anatop_softc {
+ device_t dev;
+ struct resource *res[2];
+ struct intr_config_hook
+ intr_setup_hook;
+ uint32_t cpu_curmhz;
+ uint32_t cpu_curmv;
+ uint32_t cpu_minmhz;
+ uint32_t cpu_minmv;
+ uint32_t cpu_maxmhz;
+ uint32_t cpu_maxmv;
+ uint32_t cpu_maxmhz_hw;
+ boolean_t cpu_overclock_enable;
+ boolean_t cpu_init_done;
+ uint32_t refosc_mhz;
+ void *temp_intrhand;
+ uint32_t temp_high_val;
+ uint32_t temp_high_cnt;
+ uint32_t temp_last_cnt;
+ uint32_t temp_room_cnt;
+ struct callout temp_throttle_callout;
+ sbintime_t temp_throttle_delay;
+ uint32_t temp_throttle_reset_cnt;
+ uint32_t temp_throttle_trigger_cnt;
+ uint32_t temp_throttle_val;
+};
+
+static struct imx6_anatop_softc *imx6_anatop_sc;
+
+/*
+ * Table of "operating points".
+ * These are combinations of frequency and voltage blessed by Freescale.
+ * While the datasheet says the ARM voltage can be as low as 925mV at
+ * 396MHz, it also says that the ARM and SOC voltages can't differ by
+ * more than 200mV, and the minimum SOC voltage is 1150mV, so that
+ * dictates the 950mV entry in this table.
+ */
+static struct oppt {
+ uint32_t mhz;
+ uint32_t mv;
+} imx6_oppt_table[] = {
+ { 396, 950},
+ { 792, 1150},
+ { 852, 1225},
+ { 996, 1225},
+ {1200, 1275},
+};
+
+/*
+ * Table of CPU max frequencies. This is used to translate the max frequency
+ * value (0-3) from the ocotp CFG3 register into a mhz value that can be looked
+ * up in the operating points table.
+ */
+static uint32_t imx6_ocotp_mhz_tab[] = {792, 852, 996, 1200};
+
+#define TZ_ZEROC 2731 /* deci-Kelvin <-> deci-Celcius offset. */
+
+uint32_t
+imx6_anatop_read_4(bus_size_t offset)
+{
+
+ KASSERT(imx6_anatop_sc != NULL, ("imx6_anatop_read_4 sc NULL"));
+
+ return (bus_read_4(imx6_anatop_sc->res[MEMRES], offset));
+}
+
+void
+imx6_anatop_write_4(bus_size_t offset, uint32_t value)
+{
+
+ KASSERT(imx6_anatop_sc != NULL, ("imx6_anatop_write_4 sc NULL"));
+
+ bus_write_4(imx6_anatop_sc->res[MEMRES], offset, value);
+}
+
+static void
+vdd_set(struct imx6_anatop_softc *sc, int mv)
+{
+ int newtarg, newtargSoc, oldtarg;
+ uint32_t delay, pmureg;
+ static boolean_t init_done = false;
+
+ /*
+ * The datasheet says VDD_PU and VDD_SOC must be equal, and VDD_ARM
+ * can't be more than 50mV above or 200mV below them. We keep them the
+ * same except in the case of the lowest operating point, which is
+ * handled as a special case below.
+ */
+
+ pmureg = imx6_anatop_read_4(IMX6_ANALOG_PMU_REG_CORE);
+ oldtarg = pmureg & IMX6_ANALOG_PMU_REG0_TARG_MASK;
+
+ /* Convert mV to target value. Clamp target to valid range. */
+ if (mv < 725)
+ newtarg = 0x00;
+ else if (mv > 1450)
+ newtarg = 0x1F;
+ else
+ newtarg = (mv - 700) / 25;
+
+ /*
+ * The SOC voltage can't go below 1150mV, and thus because of the 200mV
+ * rule, the ARM voltage can't go below 950mV. The 950 is encoded in
+ * our oppt table, here we handle the SOC 1150 rule as a special case.
+ * (1150-700/25=18).
+ */
+ newtargSoc = (newtarg < 18) ? 18 : newtarg;
+
+ /*
+ * The first time through the 3 voltages might not be equal so use a
+ * long conservative delay. After that we need to delay 3uS for every
+ * 25mV step upward; we actually delay 6uS because empirically, it works
+ * and the 3uS per step recommended by the docs doesn't (3uS fails when
+ * going from 400->1200, but works for smaller changes).
+ */
+ if (init_done) {
+ if (newtarg == oldtarg)
+ return;
+ else if (newtarg > oldtarg)
+ delay = (newtarg - oldtarg) * 6;
+ else
+ delay = 0;
+ } else {
+ delay = (700 / 25) * 6;
+ init_done = true;
+ }
+
+ /*
+ * Make the change and wait for it to take effect.
+ */
+ pmureg &= ~(IMX6_ANALOG_PMU_REG0_TARG_MASK |
+ IMX6_ANALOG_PMU_REG1_TARG_MASK |
+ IMX6_ANALOG_PMU_REG2_TARG_MASK);
+
+ pmureg |= newtarg << IMX6_ANALOG_PMU_REG0_TARG_SHIFT;
+ pmureg |= newtarg << IMX6_ANALOG_PMU_REG1_TARG_SHIFT;
+ pmureg |= newtargSoc << IMX6_ANALOG_PMU_REG2_TARG_SHIFT;
+
+ imx6_anatop_write_4(IMX6_ANALOG_PMU_REG_CORE, pmureg);
+ DELAY(delay);
+ sc->cpu_curmv = newtarg * 25 + 700;
+}
+
+static inline uint32_t
+cpufreq_mhz_from_div(struct imx6_anatop_softc *sc, uint32_t corediv,
+ uint32_t plldiv)
+{
+
+ return ((sc->refosc_mhz * (plldiv / 2)) / (corediv + 1));
+}
+
+static inline void
+cpufreq_mhz_to_div(struct imx6_anatop_softc *sc, uint32_t cpu_mhz,
+ uint32_t *corediv, uint32_t *plldiv)
+{
+
+ *corediv = (cpu_mhz < 650) ? 1 : 0;
+ *plldiv = ((*corediv + 1) * cpu_mhz) / (sc->refosc_mhz / 2);
+}
+
+static inline uint32_t
+cpufreq_actual_mhz(struct imx6_anatop_softc *sc, uint32_t cpu_mhz)
+{
+ uint32_t corediv, plldiv;
+
+ cpufreq_mhz_to_div(sc, cpu_mhz, &corediv, &plldiv);
+ return (cpufreq_mhz_from_div(sc, corediv, plldiv));
+}
+
+static struct oppt *
+cpufreq_nearest_oppt(struct imx6_anatop_softc *sc, uint32_t cpu_newmhz)
+{
+ int d, diff, i, nearest;
+
+ if (cpu_newmhz > sc->cpu_maxmhz_hw && !sc->cpu_overclock_enable)
+ cpu_newmhz = sc->cpu_maxmhz_hw;
+
+ diff = INT_MAX;
+ nearest = 0;
+ for (i = 0; i < nitems(imx6_oppt_table); ++i) {
+ d = abs((int)cpu_newmhz - (int)imx6_oppt_table[i].mhz);
+ if (diff > d) {
+ diff = d;
+ nearest = i;
+ }
+ }
+ return (&imx6_oppt_table[nearest]);
+}
+
+static void
+cpufreq_set_clock(struct imx6_anatop_softc * sc, struct oppt *op)
+{
+ uint32_t corediv, plldiv, timeout, wrk32;
+
+ /* If increasing the frequency, we must first increase the voltage. */
+ if (op->mhz > sc->cpu_curmhz) {
+ vdd_set(sc, op->mv);
+ }
+
+ /*
+ * I can't find a documented procedure for changing the ARM PLL divisor,
+ * but some trial and error came up with this:
+ * - Set the bypass clock source to REF_CLK_24M (source #0).
+ * - Set the PLL into bypass mode; cpu should now be running at 24mhz.
+ * - Change the divisor.
+ * - Wait for the LOCK bit to come on; it takes ~50 loop iterations.
+ * - Turn off bypass mode; cpu should now be running at the new speed.
+ */
+ cpufreq_mhz_to_div(sc, op->mhz, &corediv, &plldiv);
+ imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_ARM_CLR,
+ IMX6_ANALOG_CCM_PLL_ARM_CLK_SRC_MASK);
+ imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_ARM_SET,
+ IMX6_ANALOG_CCM_PLL_ARM_BYPASS);
+
+ wrk32 = imx6_anatop_read_4(IMX6_ANALOG_CCM_PLL_ARM);
+ wrk32 &= ~IMX6_ANALOG_CCM_PLL_ARM_DIV_MASK;
+ wrk32 |= plldiv;
+ imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_ARM, wrk32);
+
+ timeout = 10000;
+ while ((imx6_anatop_read_4(IMX6_ANALOG_CCM_PLL_ARM) &
+ IMX6_ANALOG_CCM_PLL_ARM_LOCK) == 0)
+ if (--timeout == 0)
+ panic("imx6_set_cpu_clock(): PLL never locked");
+
+ imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_ARM_CLR,
+ IMX6_ANALOG_CCM_PLL_ARM_BYPASS);
+ imx_ccm_set_cacrr(corediv);
+
+ /* If lowering the frequency, it is now safe to lower the voltage. */
+ if (op->mhz < sc->cpu_curmhz)
+ vdd_set(sc, op->mv);
+ sc->cpu_curmhz = op->mhz;
+
+ /* Tell the mpcore timer that its frequency has changed. */
+ arm_tmr_change_frequency(
+ cpufreq_actual_mhz(sc, sc->cpu_curmhz) * 1000000 / 2);
+}
+
+static int
+cpufreq_sysctl_minmhz(SYSCTL_HANDLER_ARGS)
+{
+ struct imx6_anatop_softc *sc;
+ struct oppt * op;
+ uint32_t temp;
+ int err;
+
+ sc = arg1;
+
+ temp = sc->cpu_minmhz;
+ err = sysctl_handle_int(oidp, &temp, 0, req);
+ if (err != 0 || req->newptr == NULL)
+ return (err);
+
+ op = cpufreq_nearest_oppt(sc, temp);
+ if (op->mhz > sc->cpu_maxmhz)
+ return (ERANGE);
+ else if (op->mhz == sc->cpu_minmhz)
+ return (0);
+
+ /*
+ * Value changed, update softc. If the new min is higher than the
+ * current speed, raise the current speed to match.
+ */
+ sc->cpu_minmhz = op->mhz;
+ if (sc->cpu_minmhz > sc->cpu_curmhz) {
+ cpufreq_set_clock(sc, op);
+ }
+ return (err);
+}
+
+static int
+cpufreq_sysctl_maxmhz(SYSCTL_HANDLER_ARGS)
+{
+ struct imx6_anatop_softc *sc;
+ struct oppt * op;
+ uint32_t temp;
+ int err;
+
+ sc = arg1;
+
+ temp = sc->cpu_maxmhz;
+ err = sysctl_handle_int(oidp, &temp, 0, req);
+ if (err != 0 || req->newptr == NULL)
+ return (err);
+
+ op = cpufreq_nearest_oppt(sc, temp);
+ if (op->mhz < sc->cpu_minmhz)
+ return (ERANGE);
+ else if (op->mhz == sc->cpu_maxmhz)
+ return (0);
+
+ /*
+ * Value changed, update softc and hardware. The hardware update is
+ * unconditional. We always try to run at max speed, so any change of
+ * the max means we need to change the current speed too, regardless of
+ * whether it is higher or lower than the old max.
+ */
+ sc->cpu_maxmhz = op->mhz;
+ cpufreq_set_clock(sc, op);
+
+ return (err);
+}
+
+static void
+cpufreq_initialize(struct imx6_anatop_softc *sc)
+{
+ uint32_t cfg3speed;
+ struct oppt * op;
+
+ SYSCTL_ADD_INT(NULL, SYSCTL_STATIC_CHILDREN(_hw_imx),
+ OID_AUTO, "cpu_mhz", CTLFLAG_RD, &sc->cpu_curmhz, 0,
+ "CPU frequency");
+
+ SYSCTL_ADD_PROC(NULL, SYSCTL_STATIC_CHILDREN(_hw_imx),
+ OID_AUTO, "cpu_minmhz",
+ CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NOFETCH | CTLFLAG_NEEDGIANT,
+ sc, 0, cpufreq_sysctl_minmhz, "IU", "Minimum CPU frequency");
+
+ SYSCTL_ADD_PROC(NULL, SYSCTL_STATIC_CHILDREN(_hw_imx),
+ OID_AUTO, "cpu_maxmhz",
+ CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NOFETCH | CTLFLAG_NEEDGIANT,
+ sc, 0, cpufreq_sysctl_maxmhz, "IU", "Maximum CPU frequency");
+
+ SYSCTL_ADD_INT(NULL, SYSCTL_STATIC_CHILDREN(_hw_imx),
+ OID_AUTO, "cpu_maxmhz_hw", CTLFLAG_RD, &sc->cpu_maxmhz_hw, 0,
+ "Maximum CPU frequency allowed by hardware");
+
+ SYSCTL_ADD_INT(NULL, SYSCTL_STATIC_CHILDREN(_hw_imx),
+ OID_AUTO, "cpu_overclock_enable", CTLFLAG_RWTUN,
+ &sc->cpu_overclock_enable, 0,
+ "Allow setting CPU frequency higher than cpu_maxmhz_hw");
+
+ /*
+ * XXX 24mhz shouldn't be hard-coded, should get this from imx6_ccm
+ * (even though in the real world it will always be 24mhz). Oh wait a
+ * sec, I never wrote imx6_ccm.
+ */
+ sc->refosc_mhz = 24;
+
+ /*
+ * Get the maximum speed this cpu can be set to. The values in the
+ * OCOTP CFG3 register are not documented in the reference manual.
+ * The following info was in an archived email found via web search:
+ * - 2b'11: 1200000000Hz;
+ * - 2b'10: 996000000Hz;
+ * - 2b'01: 852000000Hz; -- i.MX6Q Only, exclusive with 996MHz.
+ * - 2b'00: 792000000Hz;
+ * The default hardware max speed can be overridden by a tunable.
+ */
+ cfg3speed = (fsl_ocotp_read_4(FSL_OCOTP_CFG3) &
+ FSL_OCOTP_CFG3_SPEED_MASK) >> FSL_OCOTP_CFG3_SPEED_SHIFT;
+ sc->cpu_maxmhz_hw = imx6_ocotp_mhz_tab[cfg3speed];
+ sc->cpu_maxmhz = sc->cpu_maxmhz_hw;
+
+ TUNABLE_INT_FETCH("hw.imx6.cpu_minmhz", &sc->cpu_minmhz);
+ op = cpufreq_nearest_oppt(sc, sc->cpu_minmhz);
+ sc->cpu_minmhz = op->mhz;
+ sc->cpu_minmv = op->mv;
+
+ TUNABLE_INT_FETCH("hw.imx6.cpu_maxmhz", &sc->cpu_maxmhz);
+ op = cpufreq_nearest_oppt(sc, sc->cpu_maxmhz);
+ sc->cpu_maxmhz = op->mhz;
+ sc->cpu_maxmv = op->mv;
+
+ /*
+ * Set the CPU to maximum speed.
+ *
+ * We won't have thermal throttling until interrupts are enabled, but we
+ * want to run at full speed through all the device init stuff. This
+ * basically assumes that a single core can't overheat before interrupts
+ * are enabled; empirical testing shows that to be a safe assumption.
+ */
+ cpufreq_set_clock(sc, op);
+}
+
+static inline uint32_t
+temp_from_count(struct imx6_anatop_softc *sc, uint32_t count)
+{
+
+ return (((sc->temp_high_val - (count - sc->temp_high_cnt) *
+ (sc->temp_high_val - 250) /
+ (sc->temp_room_cnt - sc->temp_high_cnt))));
+}
+
+static inline uint32_t
+temp_to_count(struct imx6_anatop_softc *sc, uint32_t temp)
+{
+
+ return ((sc->temp_room_cnt - sc->temp_high_cnt) *
+ (sc->temp_high_val - temp) / (sc->temp_high_val - 250) +
+ sc->temp_high_cnt);
+}
+
+static void
+temp_update_count(struct imx6_anatop_softc *sc)
+{
+ uint32_t val;
+
+ val = imx6_anatop_read_4(IMX6_ANALOG_TEMPMON_TEMPSENSE0);
+ if (!(val & IMX6_ANALOG_TEMPMON_TEMPSENSE0_VALID))
+ return;
+ sc->temp_last_cnt =
+ (val & IMX6_ANALOG_TEMPMON_TEMPSENSE0_TEMP_CNT_MASK) >>
+ IMX6_ANALOG_TEMPMON_TEMPSENSE0_TEMP_CNT_SHIFT;
+}
+
+static int
+temp_sysctl_handler(SYSCTL_HANDLER_ARGS)
+{
+ struct imx6_anatop_softc *sc = arg1;
+ uint32_t t;
+
+ temp_update_count(sc);
+
+ t = temp_from_count(sc, sc->temp_last_cnt) + TZ_ZEROC;
+
+ return (sysctl_handle_int(oidp, &t, 0, req));
+}
+
+static int
+temp_throttle_sysctl_handler(SYSCTL_HANDLER_ARGS)
+{
+ struct imx6_anatop_softc *sc = arg1;
+ int err;
+ uint32_t temp;
+
+ temp = sc->temp_throttle_val + TZ_ZEROC;
+ err = sysctl_handle_int(oidp, &temp, 0, req);
+ if (temp < TZ_ZEROC)
+ return (ERANGE);
+ temp -= TZ_ZEROC;
+ if (err != 0 || req->newptr == NULL || temp == sc->temp_throttle_val)
+ return (err);
+
+ /* Value changed, update counts in softc and hardware. */
+ sc->temp_throttle_val = temp;
+ sc->temp_throttle_trigger_cnt = temp_to_count(sc, sc->temp_throttle_val);
+ sc->temp_throttle_reset_cnt = temp_to_count(sc, sc->temp_throttle_val - 100);
+ imx6_anatop_write_4(IMX6_ANALOG_TEMPMON_TEMPSENSE0_CLR,
+ IMX6_ANALOG_TEMPMON_TEMPSENSE0_ALARM_MASK);
+ imx6_anatop_write_4(IMX6_ANALOG_TEMPMON_TEMPSENSE0_SET,
+ (sc->temp_throttle_trigger_cnt <<
+ IMX6_ANALOG_TEMPMON_TEMPSENSE0_ALARM_SHIFT));
+ return (err);
+}
+
+static void
+tempmon_gofast(struct imx6_anatop_softc *sc)
+{
+
+ if (sc->cpu_curmhz < sc->cpu_maxmhz) {
+ cpufreq_set_clock(sc, cpufreq_nearest_oppt(sc, sc->cpu_maxmhz));
+ }
+}
+
+static void
+tempmon_goslow(struct imx6_anatop_softc *sc)
+{
+
+ if (sc->cpu_curmhz > sc->cpu_minmhz) {
+ cpufreq_set_clock(sc, cpufreq_nearest_oppt(sc, sc->cpu_minmhz));
+ }
+}
+
+static int
+tempmon_intr(void *arg)
+{
+ struct imx6_anatop_softc *sc = arg;
+
+ /*
+ * XXX Note that this code doesn't currently run (for some mysterious
+ * reason we just never get an interrupt), so the real monitoring is
+ * done by tempmon_throttle_check().
+ */
+ tempmon_goslow(sc);
+ /* XXX Schedule callout to speed back up eventually. */
+ return (FILTER_HANDLED);
+}
+
+static void
+tempmon_throttle_check(void *arg)
+{
+ struct imx6_anatop_softc *sc = arg;
+
+ /* Lower counts are higher temperatures. */
+ if (sc->temp_last_cnt < sc->temp_throttle_trigger_cnt)
+ tempmon_goslow(sc);
+ else if (sc->temp_last_cnt > (sc->temp_throttle_reset_cnt))
+ tempmon_gofast(sc);
+
+ callout_reset_sbt(&sc->temp_throttle_callout, sc->temp_throttle_delay,
+ 0, tempmon_throttle_check, sc, 0);
+
+}
+
+static void
+initialize_tempmon(struct imx6_anatop_softc *sc)
+{
+ uint32_t cal;
+
+ /*
+ * Fetch calibration data: a sensor count at room temperature (25C),
+ * a sensor count at a high temperature, and that temperature
+ */
+ cal = fsl_ocotp_read_4(FSL_OCOTP_ANA1);
+ sc->temp_room_cnt = (cal & 0xFFF00000) >> 20;
+ sc->temp_high_cnt = (cal & 0x000FFF00) >> 8;
+ sc->temp_high_val = (cal & 0x000000FF) * 10;
+
+ /*
+ * Throttle to a lower cpu freq at 10C below the "hot" temperature, and
+ * reset back to max cpu freq at 5C below the trigger.
+ */
+ sc->temp_throttle_val = sc->temp_high_val - 100;
+ sc->temp_throttle_trigger_cnt =
+ temp_to_count(sc, sc->temp_throttle_val);
+ sc->temp_throttle_reset_cnt =
+ temp_to_count(sc, sc->temp_throttle_val - 50);
+
+ /*
+ * Set the sensor to sample automatically at 16Hz (32.768KHz/0x800), set
+ * the throttle count, and begin making measurements.
+ */
+ imx6_anatop_write_4(IMX6_ANALOG_TEMPMON_TEMPSENSE1, 0x0800);
+ imx6_anatop_write_4(IMX6_ANALOG_TEMPMON_TEMPSENSE0,
+ (sc->temp_throttle_trigger_cnt <<
+ IMX6_ANALOG_TEMPMON_TEMPSENSE0_ALARM_SHIFT) |
+ IMX6_ANALOG_TEMPMON_TEMPSENSE0_MEASURE);
+
+ /*
+ * XXX Note that the alarm-interrupt feature isn't working yet, so
+ * we'll use a callout handler to check at 10Hz. Make sure we have an
+ * initial temperature reading before starting up the callouts so we
+ * don't get a bogus reading of zero.
+ */
+ while (sc->temp_last_cnt == 0)
+ temp_update_count(sc);
+ sc->temp_throttle_delay = 100 * SBT_1MS;
+ callout_init(&sc->temp_throttle_callout, 0);
+ callout_reset_sbt(&sc->temp_throttle_callout, sc->temp_throttle_delay,
+ 0, tempmon_throttle_check, sc, 0);
+
+ SYSCTL_ADD_PROC(NULL, SYSCTL_STATIC_CHILDREN(_hw_imx),
+ OID_AUTO, "temperature",
+ CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, 0,
+ temp_sysctl_handler, "IK", "Current die temperature");
+ SYSCTL_ADD_PROC(NULL, SYSCTL_STATIC_CHILDREN(_hw_imx),
+ OID_AUTO, "throttle_temperature",
+ CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc,
+ 0, temp_throttle_sysctl_handler, "IK",
+ "Throttle CPU when exceeding this temperature");
+}
+
+static void
+intr_setup(void *arg)
+{
+ int rid;
+ struct imx6_anatop_softc *sc;
+
+ sc = arg;
+ rid = 0;
+ sc->res[IRQRES] = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (sc->res[IRQRES] != NULL) {
+ bus_setup_intr(sc->dev, sc->res[IRQRES],
+ INTR_TYPE_MISC | INTR_MPSAFE, tempmon_intr, NULL, sc,
+ &sc->temp_intrhand);
+ } else {
+ device_printf(sc->dev, "Cannot allocate IRQ resource\n");
+ }
+ config_intrhook_disestablish(&sc->intr_setup_hook);
+}
+
+static void
+imx6_anatop_new_pass(device_t dev)
+{
+ struct imx6_anatop_softc *sc;
+ const int cpu_init_pass = BUS_PASS_CPU + BUS_PASS_ORDER_MIDDLE;
+
+ /*
+ * We attach during BUS_PASS_BUS (because some day we will be a
+ * simplebus that has regulator devices as children), but some of our
+ * init work cannot be done until BUS_PASS_CPU (we rely on other devices
+ * that attach on the CPU pass).
+ */
+ sc = device_get_softc(dev);
+ if (!sc->cpu_init_done && bus_current_pass >= cpu_init_pass) {
+ sc->cpu_init_done = true;
+ cpufreq_initialize(sc);
+ initialize_tempmon(sc);
+ if (bootverbose) {
+ device_printf(sc->dev, "CPU %uMHz @ %umV\n",
+ sc->cpu_curmhz, sc->cpu_curmv);
+ }
+ }
+ bus_generic_new_pass(dev);
+}
+
+static int
+imx6_anatop_detach(device_t dev)
+{
+
+ /* This device can never detach. */
+ return (EBUSY);
+}
+
+static int
+imx6_anatop_attach(device_t dev)
+{
+ struct imx6_anatop_softc *sc;
+ int err;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ /* Allocate bus_space resources. */
+ if (bus_alloc_resources(dev, imx6_anatop_spec, sc->res)) {
+ device_printf(dev, "Cannot allocate resources\n");
+ err = ENXIO;
+ goto out;
+ }
+
+ sc->intr_setup_hook.ich_func = intr_setup;
+ sc->intr_setup_hook.ich_arg = sc;
+ config_intrhook_establish(&sc->intr_setup_hook);
+
+ SYSCTL_ADD_UINT(device_get_sysctl_ctx(sc->dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)),
+ OID_AUTO, "cpu_voltage", CTLFLAG_RD,
+ &sc->cpu_curmv, 0, "Current CPU voltage in millivolts");
+
+ imx6_anatop_sc = sc;
+
+ /*
+ * Other code seen on the net sets this SELFBIASOFF flag around the same
+ * time the temperature sensor is set up, although it's unclear how the
+ * two are related (if at all).
+ */
+ imx6_anatop_write_4(IMX6_ANALOG_PMU_MISC0_SET,
+ IMX6_ANALOG_PMU_MISC0_SELFBIASOFF);
+
+ /*
+ * Some day, when we're ready to deal with the actual anatop regulators
+ * that are described in fdt data as children of this "bus", this would
+ * be the place to invoke a simplebus helper routine to instantiate the
+ * children from the fdt data.
+ */
+
+ err = 0;
+
+out:
+
+ if (err != 0) {
+ bus_release_resources(dev, imx6_anatop_spec, sc->res);
+ }
+
+ return (err);
+}
+
+uint32_t
+pll4_configure_output(uint32_t mfi, uint32_t mfn, uint32_t mfd)
+{
+ int reg;
+
+ /*
+ * Audio PLL (PLL4).
+ * PLL output frequency = Fref * (DIV_SELECT + NUM/DENOM)
+ */
+
+ reg = (IMX6_ANALOG_CCM_PLL_AUDIO_ENABLE);
+ reg &= ~(IMX6_ANALOG_CCM_PLL_AUDIO_DIV_SELECT_MASK << \
+ IMX6_ANALOG_CCM_PLL_AUDIO_DIV_SELECT_SHIFT);
+ reg |= (mfi << IMX6_ANALOG_CCM_PLL_AUDIO_DIV_SELECT_SHIFT);
+ imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_AUDIO, reg);
+ imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_AUDIO_NUM, mfn);
+ imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_AUDIO_DENOM, mfd);
+
+ return (0);
+}
+
+static int
+imx6_anatop_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_is_compatible(dev, "fsl,imx6q-anatop") == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Freescale i.MX6 Analog PLLs and Power");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+uint32_t
+imx6_get_cpu_clock(void)
+{
+ uint32_t corediv, plldiv;
+
+ corediv = imx_ccm_get_cacrr();
+ plldiv = imx6_anatop_read_4(IMX6_ANALOG_CCM_PLL_ARM) &
+ IMX6_ANALOG_CCM_PLL_ARM_DIV_MASK;
+ return (cpufreq_mhz_from_div(imx6_anatop_sc, corediv, plldiv));
+}
+
+static device_method_t imx6_anatop_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, imx6_anatop_probe),
+ DEVMETHOD(device_attach, imx6_anatop_attach),
+ DEVMETHOD(device_detach, imx6_anatop_detach),
+
+ /* Bus interface */
+ DEVMETHOD(bus_new_pass, imx6_anatop_new_pass),
+
+ DEVMETHOD_END
+};
+
+static driver_t imx6_anatop_driver = {
+ "imx6_anatop",
+ imx6_anatop_methods,
+ sizeof(struct imx6_anatop_softc)
+};
+
+static devclass_t imx6_anatop_devclass;
+
+EARLY_DRIVER_MODULE(imx6_anatop, simplebus, imx6_anatop_driver,
+ imx6_anatop_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
+EARLY_DRIVER_MODULE(imx6_anatop, ofwbus, imx6_anatop_driver,
+ imx6_anatop_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
diff --git a/sys/arm/freescale/imx/imx6_anatopreg.h b/sys/arm/freescale/imx/imx6_anatopreg.h
new file mode 100644
index 000000000000..e82695261328
--- /dev/null
+++ b/sys/arm/freescale/imx/imx6_anatopreg.h
@@ -0,0 +1,193 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Ian Lepore <ian@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef IMX6_ANATOPREG_H
+#define IMX6_ANATOPREG_H
+
+#define IMX6_ANALOG_CCM_PLL_ARM 0x000
+#define IMX6_ANALOG_CCM_PLL_ARM_SET 0x004
+#define IMX6_ANALOG_CCM_PLL_ARM_CLR 0x008
+#define IMX6_ANALOG_CCM_PLL_ARM_TOG 0x00C
+#define IMX6_ANALOG_CCM_PLL_ARM_DIV_MASK 0x7F
+#define IMX6_ANALOG_CCM_PLL_ARM_LOCK (1U << 31)
+#define IMX6_ANALOG_CCM_PLL_ARM_BYPASS (1 << 16)
+#define IMX6_ANALOG_CCM_PLL_ARM_CLK_SRC_MASK (0x03 << 16)
+#define IMX6_ANALOG_CCM_PLL_USB1 0x010
+#define IMX6_ANALOG_CCM_PLL_USB1_SET 0x014
+#define IMX6_ANALOG_CCM_PLL_USB1_CLR 0x018
+#define IMX6_ANALOG_CCM_PLL_USB1_TOG 0x01C
+#define IMX6_ANALOG_CCM_PLL_USB_LOCK (1U << 31)
+#define IMX6_ANALOG_CCM_PLL_USB_BYPASS (1 << 16)
+#define IMX6_ANALOG_CCM_PLL_USB_ENABLE (1 << 13)
+#define IMX6_ANALOG_CCM_PLL_USB_POWER (1 << 12)
+#define IMX6_ANALOG_CCM_PLL_USB_EN_USB_CLKS (1 << 6)
+#define IMX6_ANALOG_CCM_PLL_USB2 0x020
+#define IMX6_ANALOG_CCM_PLL_USB2_SET 0x024
+#define IMX6_ANALOG_CCM_PLL_USB2_CLR 0x028
+#define IMX6_ANALOG_CCM_PLL_USB2_TOG 0x02C
+#define IMX6_ANALOG_CCM_PLL_SYS 0x030
+#define IMX6_ANALOG_CCM_PLL_SYS_SET 0x034
+#define IMX6_ANALOG_CCM_PLL_SYS_CLR 0x038
+#define IMX6_ANALOG_CCM_PLL_SYS_TOG 0x03C
+#define IMX6_ANALOG_CCM_PLL_SYS_SS 0x040
+#define IMX6_ANALOG_CCM_PLL_SYS_NUM 0x050
+#define IMX6_ANALOG_CCM_PLL_SYS_DENOM 0x060
+#define IMX6_ANALOG_CCM_PLL_AUDIO 0x070
+#define IMX6_ANALOG_CCM_PLL_AUDIO_ENABLE (1 << 13)
+#define IMX6_ANALOG_CCM_PLL_AUDIO_DIV_SELECT_SHIFT 0
+#define IMX6_ANALOG_CCM_PLL_AUDIO_DIV_SELECT_MASK 0x7f
+#define IMX6_ANALOG_CCM_PLL_AUDIO_SET 0x074
+#define IMX6_ANALOG_CCM_PLL_AUDIO_CLR 0x078
+#define IMX6_ANALOG_CCM_PLL_AUDIO_TOG 0x07C
+#define IMX6_ANALOG_CCM_PLL_AUDIO_NUM 0x080
+#define IMX6_ANALOG_CCM_PLL_AUDIO_DENOM 0x090
+#define IMX6_ANALOG_CCM_PLL_VIDEO 0x0A0
+#define IMX6_ANALOG_CCM_PLL_VIDEO_SET 0x0A4
+#define IMX6_ANALOG_CCM_PLL_VIDEO_CLR 0x0A8
+#define IMX6_ANALOG_CCM_PLL_VIDEO_TOG 0x0AC
+#define IMX6_ANALOG_CCM_PLL_VIDEO_NUM 0x0B0
+#define IMX6_ANALOG_CCM_PLL_VIDEO_DENOM 0x0C0
+#define IMX6_ANALOG_CCM_PLL_MLB 0x0D0
+#define IMX6_ANALOG_CCM_PLL_MLB_SET 0x0D4
+#define IMX6_ANALOG_CCM_PLL_MLB_CLR 0x0D8
+#define IMX6_ANALOG_CCM_PLL_MLB_TOG 0x0DC
+#define IMX6_ANALOG_CCM_PLL_ENET 0x0E0
+#define IMX6_ANALOG_CCM_PLL_ENET_SET 0x0E4
+#define IMX6_ANALOG_CCM_PLL_ENET_CLR 0x0E8
+#define IMX6_ANALOG_CCM_PLL_ENET_TOG 0x0EC
+#define IMX6_ANALOG_CCM_PFD_480 0x0F0
+#define IMX6_ANALOG_CCM_PFD_480_SET 0x0F4
+#define IMX6_ANALOG_CCM_PFD_480_CLR 0x0F8
+#define IMX6_ANALOG_CCM_PFD_480_TOG 0x0FC
+#define IMX6_ANALOG_CCM_PFD_528 0x100
+#define IMX6_ANALOG_CCM_PFD_528_SET 0x104
+#define IMX6_ANALOG_CCM_PFD_528_CLR 0x108
+#define IMX6_ANALOG_CCM_PFD_528_TOG 0x10C
+
+#define IMX6_ANALOG_PMU_REG_CORE 0x140
+#define IMX6_ANALOG_PMU_REG2_TARG_SHIFT 18
+#define IMX6_ANALOG_PMU_REG2_TARG_MASK \
+ (0x1f << IMX6_ANALOG_PMU_REG2_TARG_SHIFT)
+#define IMX6_ANALOG_PMU_REG1_TARG_SHIFT 9
+#define IMX6_ANALOG_PMU_REG1_TARG_MASK \
+ (0x1f << IMX6_ANALOG_PMU_REG1_TARG_SHIFT)
+#define IMX6_ANALOG_PMU_REG0_TARG_SHIFT 0
+#define IMX6_ANALOG_PMU_REG0_TARG_MASK \
+ (0x1f << IMX6_ANALOG_PMU_REG0_TARG_SHIFT)
+
+#define IMX6_ANALOG_PMU_MISC0 0x150
+#define IMX6_ANALOG_PMU_MISC0_SET 0x154
+#define IMX6_ANALOG_PMU_MISC0_CLR 0x158
+#define IMX6_ANALOG_PMU_MISC0_TOG 0x15C
+#define IMX6_ANALOG_PMU_MISC0_SELFBIASOFF (1 << 3)
+
+#define IMX6_ANALOG_PMU_MISC1 0x160
+#define IMX6_ANALOG_PMU_MISC1_SET 0x164
+#define IMX6_ANALOG_PMU_MISC1_CLR 0x168
+#define IMX6_ANALOG_PMU_MISC1_TOG 0x16C
+#define IMX6_ANALOG_PMU_MISC1_IRQ_TEMPSENSE (1 << 29)
+
+#define IMX6_ANALOG_PMU_MISC2 0x170
+#define IMX6_ANALOG_PMU_MISC2_SET 0x174
+#define IMX6_ANALOG_PMU_MISC2_CLR 0x178
+#define IMX6_ANALOG_PMU_MISC2_TOG 0x17C
+
+/*
+ * Note that the ANALOG_CCM_MISCn registers are the same as the PMU_MISCn
+ * registers; some bits conceptually belong to the PMU and some to the CCM.
+ */
+#define IMX6_ANALOG_CCM_MISC0 IMX6_ANALOG_PMU_MISC0
+#define IMX6_ANALOG_CCM_MISC0_SET IMX6_ANALOG_PMU_MISC0_SET
+#define IMX6_ANALOG_CCM_MISC0_CLR IMX6_ANALOG_PMU_MISC0_CLR
+#define IMX6_ANALOG_CCM_MISC0_TOG IMX6_ANALOG_PMU_MISC0_TOG
+
+#define IMX6_ANALOG_CCM_MISC2 IMX6_ANALOG_PMU_MISC2
+#define IMX6_ANALOG_CCM_MISC2_SET IMX6_ANALOG_PMU_MISC2_SET
+#define IMX6_ANALOG_CCM_MISC2_CLR IMX6_ANALOG_PMU_MISC2_CLR
+#define IMX6_ANALOG_CCM_MISC2_TOG IMX6_ANALOG_PMU_MISC2_TOG
+
+#define IMX6_ANALOG_TEMPMON_TEMPSENSE0 0x180
+#define IMX6_ANALOG_TEMPMON_TEMPSENSE0_SET 0x184
+#define IMX6_ANALOG_TEMPMON_TEMPSENSE0_CLR 0x188
+#define IMX6_ANALOG_TEMPMON_TEMPSENSE0_TOG 0x18C
+#define IMX6_ANALOG_TEMPMON_TEMPSENSE0_TOG 0x18C
+#define IMX6_ANALOG_TEMPMON_TEMPSENSE0_ALARM_SHIFT 20
+#define IMX6_ANALOG_TEMPMON_TEMPSENSE0_ALARM_MASK \
+ (0xfff << IMX6_ANALOG_TEMPMON_TEMPSENSE0_ALARM_SHIFT)
+#define IMX6_ANALOG_TEMPMON_TEMPSENSE0_TEMP_CNT_SHIFT 8
+#define IMX6_ANALOG_TEMPMON_TEMPSENSE0_TEMP_CNT_MASK \
+ (0xfff << IMX6_ANALOG_TEMPMON_TEMPSENSE0_TEMP_CNT_SHIFT)
+#define IMX6_ANALOG_TEMPMON_TEMPSENSE0_VALID 0x4
+#define IMX6_ANALOG_TEMPMON_TEMPSENSE0_MEASURE 0x2
+#define IMX6_ANALOG_TEMPMON_TEMPSENSE0_POWER_DOWN 0x1
+
+#define IMX6_ANALOG_TEMPMON_TEMPSENSE1 0x190
+#define IMX6_ANALOG_TEMPMON_TEMPSENSE1_SET 0x194
+#define IMX6_ANALOG_TEMPMON_TEMPSENSE1_CLR 0x198
+#define IMX6_ANALOG_TEMPMON_TEMPSENSE1_TOG 0x19C
+
+#define IMX6_ANALOG_USB1_VBUS_DETECT 0x1A0
+#define IMX6_ANALOG_USB1_VBUS_DETECT_SET 0x1A4
+#define IMX6_ANALOG_USB1_VBUS_DETECT_CLR 0x1A8
+#define IMX6_ANALOG_USB1_VBUS_DETECT_TOG 0x1AC
+#define IMX6_ANALOG_USB1_CHRG_DETECT 0x1B0
+#define IMX6_ANALOG_USB1_CHRG_DETECT_SET 0x1B4
+#define IMX6_ANALOG_USB1_CHRG_DETECT_CLR 0x1B8
+#define IMX6_ANALOG_USB1_CHRG_DETECT_TOG 0x1BC
+#define IMX6_ANALOG_USB_CHRG_DETECT_N_ENABLE (1 << 20) /* EN_B */
+#define IMX6_ANALOG_USB_CHRG_DETECT_N_CHK_CHRG (1 << 19) /* CHK_CHRG_B */
+#define IMX6_ANALOG_USB_CHRG_DETECT_CHK_CONTACT (1 << 18)
+#define IMX6_ANALOG_USB1_VBUS_DETECT_STAT 0x1C0
+#define IMX6_ANALOG_USB1_CHRG_DETECT_STAT 0x1D0
+#define IMX6_ANALOG_USB1_MISC 0x1F0
+#define IMX6_ANALOG_USB1_MISC_SET 0x1F4
+#define IMX6_ANALOG_USB1_MISC_CLR 0x1F8
+#define IMX6_ANALOG_USB1_MISC_TOG 0x1FC
+#define IMX6_ANALOG_USB2_VBUS_DETECT 0x200
+#define IMX6_ANALOG_USB2_VBUS_DETECT_SET 0x204
+#define IMX6_ANALOG_USB2_VBUS_DETECT_CLR 0x208
+#define IMX6_ANALOG_USB2_VBUS_DETECT_TOG 0x20C
+#define IMX6_ANALOG_USB2_CHRG_DETECT 0x210
+#define IMX6_ANALOG_USB2_CHRG_DETECT_SET 0x214
+#define IMX6_ANALOG_USB2_CHRG_DETECT_CLR 0x218
+#define IMX6_ANALOG_USB2_CHRG_DETECT_TOG 0x21C
+#define IMX6_ANALOG_USB2_VBUS_DETECT_STAT 0x220
+#define IMX6_ANALOG_USB2_CHRG_DETECT_STAT 0x230
+#define IMX6_ANALOG_USB2_MISC 0x250
+#define IMX6_ANALOG_USB2_MISC_SET 0x254
+#define IMX6_ANALOG_USB2_MISC_CLR 0x258
+#define IMX6_ANALOG_USB2_MISC_TOG 0x25C
+#define IMX6_ANALOG_DIGPROG 0x260
+#define IMX6_ANALOG_DIGPROG_SL 0x280
+#define IMX6_ANALOG_DIGPROG_SOCTYPE_SHIFT 16
+#define IMX6_ANALOG_DIGPROG_SOCTYPE_MASK \
+ (0xff << IMX6_ANALOG_DIGPROG_SOCTYPE_SHIFT)
+
+#endif
diff --git a/sys/arm/freescale/imx/imx6_anatopvar.h b/sys/arm/freescale/imx/imx6_anatopvar.h
new file mode 100644
index 000000000000..7378bf245b54
--- /dev/null
+++ b/sys/arm/freescale/imx/imx6_anatopvar.h
@@ -0,0 +1,49 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Ian Lepore <ian@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef IMX6_ANATOPVAR_H
+#define IMX6_ANATOPVAR_H
+
+/*
+ * All registers controlling various analog aspects of the SoC (such as PLLs or
+ * voltage regulators or USB VBUS detection) are gathered together under the
+ * anatop device (because of newbus hierarchical resource management), but other
+ * drivers such as CMM or USBPHY need access to these registers. These
+ * functions let them have at the hardware directly. No effort is made by these
+ * functions to mediate concurrent access.
+ */
+uint32_t imx6_anatop_read_4(bus_size_t _offset);
+void imx6_anatop_write_4(bus_size_t _offset, uint32_t _value);
+
+uint32_t imx6_get_cpu_clock(void);
+
+uint32_t pll4_configure_output(uint32_t mfi, uint32_t mfn, uint32_t mfd);
+
+#endif
diff --git a/sys/arm/freescale/imx/imx6_audmux.c b/sys/arm/freescale/imx/imx6_audmux.c
new file mode 100644
index 000000000000..61ec0b6da510
--- /dev/null
+++ b/sys/arm/freescale/imx/imx6_audmux.c
@@ -0,0 +1,157 @@
+/*-
+ * Copyright (c) 2015 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * i.MX6 Digital Audio Multiplexer (AUDMUX)
+ * Chapter 16, i.MX 6Dual/6Quad Applications Processor Reference Manual,
+ * Rev. 1, 04/2013
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/endian.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#define READ4(_sc, _reg) \
+ bus_space_read_4(_sc->bst, _sc->bsh, _reg)
+#define WRITE4(_sc, _reg, _val) \
+ bus_space_write_4(_sc->bst, _sc->bsh, _reg, _val)
+
+#define AUDMUX_PTCR(n) (0x8 * (n - 1)) /* Port Timing Control Register */
+#define PTCR_TFS_DIR (1 << 31) /* Transmit Frame Sync Direction Control */
+#define PTCR_TFSEL_S 27 /* Transmit Frame Sync Select */
+#define PTCR_TFSEL_M 0xf
+#define PTCR_TCLKDIR (1 << 26) /* Transmit Clock Direction Control */
+#define PTCR_TCSEL_S 22 /* Transmit Clock Select. */
+#define PTCR_TCSEL_M 0xf
+#define PTCR_RFS_DIR (1 << 21) /* Receive Frame Sync Direction Control */
+#define PTCR_SYN (1 << 11)
+#define AUDMUX_PDCR(n) (0x8 * (n - 1) + 0x4) /* Port Data Control Reg */
+#define PDCR_RXDSEL_S 13 /* Receive Data Select */
+#define PDCR_RXDSEL_M 0x3
+#define PDCR_RXDSEL_PORT(n) (n - 1)
+
+struct audmux_softc {
+ struct resource *res[1];
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+ void *ih;
+};
+
+static struct resource_spec audmux_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+static int
+audmux_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "fsl,imx6q-audmux"))
+ return (ENXIO);
+
+ device_set_desc(dev, "i.MX6 Digital Audio Multiplexer");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+audmux_configure(struct audmux_softc *sc,
+ int ssi_port, int audmux_port)
+{
+ uint32_t reg;
+
+ /* Direction: output */
+ reg = (PTCR_TFS_DIR | PTCR_TCLKDIR | PTCR_SYN);
+ WRITE4(sc, AUDMUX_PTCR(audmux_port), reg);
+
+ /* Select source */
+ reg = (PDCR_RXDSEL_PORT(ssi_port) << PDCR_RXDSEL_S);
+ WRITE4(sc, AUDMUX_PDCR(audmux_port), reg);
+
+ return (0);
+}
+
+static int
+audmux_attach(device_t dev)
+{
+ struct audmux_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (bus_alloc_resources(dev, audmux_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ /* Memory interface */
+ sc->bst = rman_get_bustag(sc->res[0]);
+ sc->bsh = rman_get_bushandle(sc->res[0]);
+
+ /*
+ * Direct SSI1 output to AUDMUX5 pins.
+ * TODO: dehardcore this.
+ */
+ audmux_configure(sc, 1, 5);
+
+ return (0);
+};
+
+static device_method_t audmux_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, audmux_probe),
+ DEVMETHOD(device_attach, audmux_attach),
+ { 0, 0 }
+};
+
+static driver_t audmux_driver = {
+ "audmux",
+ audmux_methods,
+ sizeof(struct audmux_softc),
+};
+
+static devclass_t audmux_devclass;
+
+DRIVER_MODULE(audmux, simplebus, audmux_driver, audmux_devclass, 0, 0);
diff --git a/sys/arm/freescale/imx/imx6_ccm.c b/sys/arm/freescale/imx/imx6_ccm.c
new file mode 100644
index 000000000000..5bdb71caa4bd
--- /dev/null
+++ b/sys/arm/freescale/imx/imx6_ccm.c
@@ -0,0 +1,520 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Ian Lepore <ian@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Clocks and power control driver for Freescale i.MX6 family of SoCs.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+
+#include <arm/freescale/imx/imx6_anatopreg.h>
+#include <arm/freescale/imx/imx6_anatopvar.h>
+#include <arm/freescale/imx/imx6_ccmreg.h>
+#include <arm/freescale/imx/imx_machdep.h>
+#include <arm/freescale/imx/imx_ccmvar.h>
+
+#ifndef CCGR_CLK_MODE_ALWAYS
+#define CCGR_CLK_MODE_OFF 0
+#define CCGR_CLK_MODE_RUNMODE 1
+#define CCGR_CLK_MODE_ALWAYS 3
+#endif
+
+struct ccm_softc {
+ device_t dev;
+ struct resource *mem_res;
+};
+
+static struct ccm_softc *ccm_sc;
+
+static inline uint32_t
+RD4(struct ccm_softc *sc, bus_size_t off)
+{
+
+ return (bus_read_4(sc->mem_res, off));
+}
+
+static inline void
+WR4(struct ccm_softc *sc, bus_size_t off, uint32_t val)
+{
+
+ bus_write_4(sc->mem_res, off, val);
+}
+
+/*
+ * Until we have a fully functional ccm driver which implements the fdt_clock
+ * interface, use the age-old workaround of unconditionally enabling the clocks
+ * for devices we might need to use. The SoC defaults to most clocks enabled,
+ * but the rom boot code and u-boot disable a few of them. We turn on only
+ * what's needed to run the chip plus devices we have drivers for, and turn off
+ * devices we don't yet have drivers for. (Note that USB is not turned on here
+ * because that is one we do when the driver asks for it.)
+ */
+static void
+ccm_init_gates(struct ccm_softc *sc)
+{
+ uint32_t reg;
+
+ /* ahpbdma, aipstz 1 & 2 buses */
+ reg = CCGR0_AIPS_TZ1 | CCGR0_AIPS_TZ2 | CCGR0_ABPHDMA;
+ WR4(sc, CCM_CCGR0, reg);
+
+ /* enet, epit, gpt, spi */
+ reg = CCGR1_ENET | CCGR1_EPIT1 | CCGR1_GPT | CCGR1_ECSPI1 |
+ CCGR1_ECSPI2 | CCGR1_ECSPI3 | CCGR1_ECSPI4 | CCGR1_ECSPI5;
+ WR4(sc, CCM_CCGR1, reg);
+
+ /* ipmux & ipsync (bridges), iomux, i2c */
+ reg = CCGR2_I2C1 | CCGR2_I2C2 | CCGR2_I2C3 | CCGR2_IIM |
+ CCGR2_IOMUX_IPT | CCGR2_IPMUX1 | CCGR2_IPMUX2 | CCGR2_IPMUX3 |
+ CCGR2_IPSYNC_IP2APB_TZASC1 | CCGR2_IPSYNC_IP2APB_TZASC2 |
+ CCGR2_IPSYNC_VDOA;
+ WR4(sc, CCM_CCGR2, reg);
+
+ /* DDR memory controller */
+ reg = CCGR3_OCRAM | CCGR3_MMDC_CORE_IPG |
+ CCGR3_MMDC_CORE_ACLK_FAST | CCGR3_CG11 | CCGR3_CG13;
+ WR4(sc, CCM_CCGR3, reg);
+
+ /* pl301 bus crossbar */
+ reg = CCGR4_PL301_MX6QFAST1_S133 |
+ CCGR4_PL301_MX6QPER1_BCH | CCGR4_PL301_MX6QPER2_MAIN;
+ WR4(sc, CCM_CCGR4, reg);
+
+ /* uarts, ssi, sdma */
+ reg = CCGR5_SDMA | CCGR5_SSI1 | CCGR5_SSI2 | CCGR5_SSI3 |
+ CCGR5_UART | CCGR5_UART_SERIAL;
+ WR4(sc, CCM_CCGR5, reg);
+
+ /* usdhc 1-4, usboh3 */
+ reg = CCGR6_USBOH3 | CCGR6_USDHC1 | CCGR6_USDHC2 |
+ CCGR6_USDHC3 | CCGR6_USDHC4;
+ WR4(sc, CCM_CCGR6, reg);
+}
+
+static int
+ccm_detach(device_t dev)
+{
+ struct ccm_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (sc->mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+
+ return (0);
+}
+
+static int
+ccm_attach(device_t dev)
+{
+ struct ccm_softc *sc;
+ int err, rid;
+ uint32_t reg;
+
+ sc = device_get_softc(dev);
+ err = 0;
+
+ /* Allocate bus_space resources. */
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "Cannot allocate memory resources\n");
+ err = ENXIO;
+ goto out;
+ }
+
+ ccm_sc = sc;
+
+ /*
+ * Configure the Low Power Mode setting to leave the ARM core power on
+ * when a WFI instruction is executed. This lets the MPCore timers and
+ * GIC continue to run, which is helpful when the only thing that can
+ * wake you up is an MPCore Private Timer interrupt delivered via GIC.
+ *
+ * XXX Based on the docs, setting CCM_CGPR_INT_MEM_CLK_LPM shouldn't be
+ * required when the LPM bits are set to LPM_RUN. But experimentally
+ * I've experienced a fairly rare lockup when not setting it. I was
+ * unable to prove conclusively that the lockup was related to power
+ * management or that this definitively fixes it. Revisit this.
+ */
+ reg = RD4(sc, CCM_CGPR);
+ reg |= CCM_CGPR_INT_MEM_CLK_LPM;
+ WR4(sc, CCM_CGPR, reg);
+ reg = RD4(sc, CCM_CLPCR);
+ reg = (reg & ~CCM_CLPCR_LPM_MASK) | CCM_CLPCR_LPM_RUN;
+ WR4(sc, CCM_CLPCR, reg);
+
+ ccm_init_gates(sc);
+
+ err = 0;
+
+out:
+
+ if (err != 0)
+ ccm_detach(dev);
+
+ return (err);
+}
+
+static int
+ccm_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_is_compatible(dev, "fsl,imx6q-ccm") == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Freescale i.MX6 Clock Control Module");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+void
+imx_ccm_ssi_configure(device_t _ssidev)
+{
+ struct ccm_softc *sc;
+ uint32_t reg;
+
+ sc = ccm_sc;
+
+ /*
+ * Select PLL4 (Audio PLL) clock multiplexer as source.
+ * PLL output frequency = Fref * (DIV_SELECT + NUM/DENOM).
+ */
+
+ reg = RD4(sc, CCM_CSCMR1);
+ reg &= ~(SSI_CLK_SEL_M << SSI1_CLK_SEL_S);
+ reg |= (SSI_CLK_SEL_PLL4 << SSI1_CLK_SEL_S);
+ reg &= ~(SSI_CLK_SEL_M << SSI2_CLK_SEL_S);
+ reg |= (SSI_CLK_SEL_PLL4 << SSI2_CLK_SEL_S);
+ reg &= ~(SSI_CLK_SEL_M << SSI3_CLK_SEL_S);
+ reg |= (SSI_CLK_SEL_PLL4 << SSI3_CLK_SEL_S);
+ WR4(sc, CCM_CSCMR1, reg);
+
+ /*
+ * Ensure we have set hardware-default values
+ * for pre and post dividers.
+ */
+
+ /* SSI1 and SSI3 */
+ reg = RD4(sc, CCM_CS1CDR);
+ /* Divide by 2 */
+ reg &= ~(SSI_CLK_PODF_MASK << SSI1_CLK_PODF_SHIFT);
+ reg &= ~(SSI_CLK_PODF_MASK << SSI3_CLK_PODF_SHIFT);
+ reg |= (0x1 << SSI1_CLK_PODF_SHIFT);
+ reg |= (0x1 << SSI3_CLK_PODF_SHIFT);
+ /* Divide by 4 */
+ reg &= ~(SSI_CLK_PRED_MASK << SSI1_CLK_PRED_SHIFT);
+ reg &= ~(SSI_CLK_PRED_MASK << SSI3_CLK_PRED_SHIFT);
+ reg |= (0x3 << SSI1_CLK_PRED_SHIFT);
+ reg |= (0x3 << SSI3_CLK_PRED_SHIFT);
+ WR4(sc, CCM_CS1CDR, reg);
+
+ /* SSI2 */
+ reg = RD4(sc, CCM_CS2CDR);
+ /* Divide by 2 */
+ reg &= ~(SSI_CLK_PODF_MASK << SSI2_CLK_PODF_SHIFT);
+ reg |= (0x1 << SSI2_CLK_PODF_SHIFT);
+ /* Divide by 4 */
+ reg &= ~(SSI_CLK_PRED_MASK << SSI2_CLK_PRED_SHIFT);
+ reg |= (0x3 << SSI2_CLK_PRED_SHIFT);
+ WR4(sc, CCM_CS2CDR, reg);
+}
+
+void
+imx_ccm_usb_enable(device_t _usbdev)
+{
+
+ /*
+ * For imx6, the USBOH3 clock gate is bits 0-1 of CCGR6, so no need for
+ * shifting and masking here, just set the low-order two bits to ALWAYS.
+ */
+ WR4(ccm_sc, CCM_CCGR6, RD4(ccm_sc, CCM_CCGR6) | CCGR_CLK_MODE_ALWAYS);
+}
+
+void
+imx_ccm_usbphy_enable(device_t _phydev)
+{
+ /*
+ * XXX Which unit?
+ * Right now it's not clear how to figure from fdt data which phy unit
+ * we're supposed to operate on. Until this is worked out, just enable
+ * both PHYs.
+ */
+#if 0
+ int phy_num, regoff;
+
+ phy_num = 0; /* XXX */
+
+ switch (phy_num) {
+ case 0:
+ regoff = 0;
+ break;
+ case 1:
+ regoff = 0x10;
+ break;
+ default:
+ device_printf(ccm_sc->dev, "Bad PHY number %u,\n",
+ phy_num);
+ return;
+ }
+
+ imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_USB1 + regoff,
+ IMX6_ANALOG_CCM_PLL_USB_ENABLE |
+ IMX6_ANALOG_CCM_PLL_USB_POWER |
+ IMX6_ANALOG_CCM_PLL_USB_EN_USB_CLKS);
+#else
+ imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_USB1 + 0,
+ IMX6_ANALOG_CCM_PLL_USB_ENABLE |
+ IMX6_ANALOG_CCM_PLL_USB_POWER |
+ IMX6_ANALOG_CCM_PLL_USB_EN_USB_CLKS);
+
+ imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_USB1 + 0x10,
+ IMX6_ANALOG_CCM_PLL_USB_ENABLE |
+ IMX6_ANALOG_CCM_PLL_USB_POWER |
+ IMX6_ANALOG_CCM_PLL_USB_EN_USB_CLKS);
+#endif
+}
+
+int
+imx6_ccm_sata_enable(void)
+{
+ uint32_t v;
+ int timeout;
+
+ /* Un-gate the sata controller. */
+ WR4(ccm_sc, CCM_CCGR5, RD4(ccm_sc, CCM_CCGR5) | CCGR5_SATA);
+
+ /* Power up the PLL that feeds ENET/SATA/PCI phys, wait for lock. */
+ v = RD4(ccm_sc, CCM_ANALOG_PLL_ENET);
+ v &= ~CCM_ANALOG_PLL_ENET_POWERDOWN;
+ WR4(ccm_sc, CCM_ANALOG_PLL_ENET, v);
+
+ for (timeout = 100000; timeout > 0; timeout--) {
+ if (RD4(ccm_sc, CCM_ANALOG_PLL_ENET) &
+ CCM_ANALOG_PLL_ENET_LOCK) {
+ break;
+ }
+ }
+ if (timeout <= 0) {
+ return ETIMEDOUT;
+ }
+
+ /* Enable the PLL, and enable its 100mhz output. */
+ v |= CCM_ANALOG_PLL_ENET_ENABLE;
+ v &= ~CCM_ANALOG_PLL_ENET_BYPASS;
+ WR4(ccm_sc, CCM_ANALOG_PLL_ENET, v);
+
+ v |= CCM_ANALOG_PLL_ENET_ENABLE_100M;
+ WR4(ccm_sc, CCM_ANALOG_PLL_ENET, v);
+
+ return 0;
+}
+
+uint32_t
+imx_ccm_ecspi_hz(void)
+{
+
+ return (60000000);
+}
+
+uint32_t
+imx_ccm_ipg_hz(void)
+{
+
+ return (66000000);
+}
+
+uint32_t
+imx_ccm_perclk_hz(void)
+{
+
+ return (66000000);
+}
+
+uint32_t
+imx_ccm_sdhci_hz(void)
+{
+
+ return (200000000);
+}
+
+uint32_t
+imx_ccm_uart_hz(void)
+{
+
+ return (80000000);
+}
+
+uint32_t
+imx_ccm_ahb_hz(void)
+{
+ return (132000000);
+}
+
+int
+imx_ccm_pll_video_enable(void)
+{
+ uint32_t reg;
+ int timeout;
+
+ /* Power down PLL */
+ reg = RD4(ccm_sc, CCM_ANALOG_PLL_VIDEO);
+ reg &= ~CCM_ANALOG_PLL_VIDEO_POWERDOWN;
+ WR4(ccm_sc, CCM_ANALOG_PLL_VIDEO, reg);
+
+ /*
+ * Fvideo = Fref * (37 + 11/12) / 2
+ * Fref = 24MHz, Fvideo = 455MHz
+ */
+ reg &= ~CCM_ANALOG_PLL_VIDEO_POST_DIV_SELECT_MASK;
+ reg |= CCM_ANALOG_PLL_VIDEO_POST_DIV_2;
+ reg &= ~CCM_ANALOG_PLL_VIDEO_DIV_SELECT_MASK;
+ reg |= 37 << CCM_ANALOG_PLL_VIDEO_DIV_SELECT_SHIFT;
+ WR4(ccm_sc, CCM_ANALOG_PLL_VIDEO, reg);
+
+ WR4(ccm_sc, CCM_ANALOG_PLL_VIDEO_NUM, 11);
+ WR4(ccm_sc, CCM_ANALOG_PLL_VIDEO_DENOM, 12);
+
+ /* Power up and wait for PLL lock down */
+ reg = RD4(ccm_sc, CCM_ANALOG_PLL_VIDEO);
+ reg &= ~CCM_ANALOG_PLL_VIDEO_POWERDOWN;
+ WR4(ccm_sc, CCM_ANALOG_PLL_VIDEO, reg);
+
+ for (timeout = 100000; timeout > 0; timeout--) {
+ if (RD4(ccm_sc, CCM_ANALOG_PLL_VIDEO) &
+ CCM_ANALOG_PLL_VIDEO_LOCK) {
+ break;
+ }
+ }
+ if (timeout <= 0) {
+ return ETIMEDOUT;
+ }
+
+ /* Enable the PLL */
+ reg |= CCM_ANALOG_PLL_VIDEO_ENABLE;
+ reg &= ~CCM_ANALOG_PLL_VIDEO_BYPASS;
+ WR4(ccm_sc, CCM_ANALOG_PLL_VIDEO, reg);
+
+ return (0);
+}
+
+void
+imx_ccm_ipu_enable(int ipu)
+{
+ struct ccm_softc *sc;
+ uint32_t reg;
+
+ sc = ccm_sc;
+ reg = RD4(sc, CCM_CCGR3);
+ if (ipu == 1)
+ reg |= CCGR3_IPU1_IPU | CCGR3_IPU1_DI0;
+ else
+ reg |= CCGR3_IPU2_IPU | CCGR3_IPU2_DI0;
+ WR4(sc, CCM_CCGR3, reg);
+
+ /* Set IPU1_DI0 clock to source from PLL5 and divide it by 3 */
+ reg = RD4(sc, CCM_CHSCCDR);
+ reg &= ~(CHSCCDR_IPU1_DI0_PRE_CLK_SEL_MASK |
+ CHSCCDR_IPU1_DI0_PODF_MASK | CHSCCDR_IPU1_DI0_CLK_SEL_MASK);
+ reg |= (CHSCCDR_PODF_DIVIDE_BY_3 << CHSCCDR_IPU1_DI0_PODF_SHIFT);
+ reg |= (CHSCCDR_IPU_PRE_CLK_PLL5 << CHSCCDR_IPU1_DI0_PRE_CLK_SEL_SHIFT);
+ WR4(sc, CCM_CHSCCDR, reg);
+
+ reg |= (CHSCCDR_CLK_SEL_PREMUXED << CHSCCDR_IPU1_DI0_CLK_SEL_SHIFT);
+ WR4(sc, CCM_CHSCCDR, reg);
+}
+
+uint32_t
+imx_ccm_ipu_hz(void)
+{
+
+ return (455000000 / 3);
+}
+
+void
+imx_ccm_hdmi_enable(void)
+{
+ struct ccm_softc *sc;
+ uint32_t reg;
+
+ sc = ccm_sc;
+ reg = RD4(sc, CCM_CCGR2);
+ reg |= CCGR2_HDMI_TX | CCGR2_HDMI_TX_ISFR;
+ WR4(sc, CCM_CCGR2, reg);
+}
+
+uint32_t
+imx_ccm_get_cacrr(void)
+{
+
+ return (RD4(ccm_sc, CCM_CACCR));
+}
+
+void
+imx_ccm_set_cacrr(uint32_t divisor)
+{
+
+ WR4(ccm_sc, CCM_CACCR, divisor);
+}
+
+static device_method_t ccm_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ccm_probe),
+ DEVMETHOD(device_attach, ccm_attach),
+ DEVMETHOD(device_detach, ccm_detach),
+
+ DEVMETHOD_END
+};
+
+static driver_t ccm_driver = {
+ "ccm",
+ ccm_methods,
+ sizeof(struct ccm_softc)
+};
+
+static devclass_t ccm_devclass;
+
+EARLY_DRIVER_MODULE(ccm, simplebus, ccm_driver, ccm_devclass, 0, 0,
+ BUS_PASS_CPU + BUS_PASS_ORDER_EARLY);
diff --git a/sys/arm/freescale/imx/imx6_ccmreg.h b/sys/arm/freescale/imx/imx6_ccmreg.h
new file mode 100644
index 000000000000..dfa7aad45cd7
--- /dev/null
+++ b/sys/arm/freescale/imx/imx6_ccmreg.h
@@ -0,0 +1,165 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Ian Lepore <ian@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef IMX6_CCMREG_H
+#define IMX6_CCMREG_H
+
+#define CCM_CACCR 0x010
+#define CCM_CBCDR 0x014
+#define CBCDR_MMDC_CH1_AXI_PODF_SHIFT 3
+#define CBCDR_MMDC_CH1_AXI_PODF_MASK (7 << 3)
+#define CCM_CSCMR1 0x01C
+#define SSI1_CLK_SEL_S 10
+#define SSI2_CLK_SEL_S 12
+#define SSI3_CLK_SEL_S 14
+#define SSI_CLK_SEL_M 0x3
+#define SSI_CLK_SEL_508_PFD 0
+#define SSI_CLK_SEL_454_PFD 1
+#define SSI_CLK_SEL_PLL4 2
+#define CCM_CSCMR2 0x020
+#define CSCMR2_LDB_DI0_IPU_DIV_SHIFT 10
+#define CCM_CS1CDR 0x028
+#define SSI1_CLK_PODF_SHIFT 0
+#define SSI1_CLK_PRED_SHIFT 6
+#define SSI3_CLK_PODF_SHIFT 16
+#define SSI3_CLK_PRED_SHIFT 22
+#define SSI_CLK_PODF_MASK 0x3f
+#define SSI_CLK_PRED_MASK 0x7
+#define CCM_CS2CDR 0x02C
+#define SSI2_CLK_PODF_SHIFT 0
+#define SSI2_CLK_PRED_SHIFT 6
+#define LDB_DI0_CLK_SEL_SHIFT 9
+#define LDB_DI0_CLK_SEL_MASK (3 << LDB_DI0_CLK_SEL_SHIFT)
+#define CCM_CHSCCDR 0x034
+#define CHSCCDR_IPU1_DI0_PRE_CLK_SEL_MASK (0x7 << 6)
+#define CHSCCDR_IPU1_DI0_PRE_CLK_SEL_SHIFT 6
+#define CHSCCDR_IPU1_DI0_PODF_MASK (0x7 << 3)
+#define CHSCCDR_IPU1_DI0_PODF_SHIFT 3
+#define CHSCCDR_IPU1_DI0_CLK_SEL_MASK (0x7)
+#define CHSCCDR_IPU1_DI0_CLK_SEL_SHIFT 0
+#define CHSCCDR_CLK_SEL_PREMUXED 0
+#define CHSCCDR_CLK_SEL_LDB_DI0 3
+#define CHSCCDR_PODF_DIVIDE_BY_3 2
+#define CHSCCDR_PODF_DIVIDE_BY_1 0
+#define CHSCCDR_IPU_PRE_CLK_540M_PFD 5
+#define CHSCCDR_IPU_PRE_CLK_PLL5 2
+#define CCM_CSCDR2 0x038
+#define CCM_CLPCR 0x054
+#define CCM_CLPCR_LPM_MASK 0x03
+#define CCM_CLPCR_LPM_RUN 0x00
+#define CCM_CLPCR_LPM_WAIT 0x01
+#define CCM_CLPCR_LPM_STOP 0x02
+#define CCM_CGPR 0x064
+#define CCM_CGPR_INT_MEM_CLK_LPM (1 << 17)
+#define CCM_CCGR0 0x068
+#define CCGR0_AIPS_TZ1 (0x3 << 0)
+#define CCGR0_AIPS_TZ2 (0x3 << 2)
+#define CCGR0_ABPHDMA (0x3 << 4)
+#define CCM_CCGR1 0x06C
+#define CCGR1_ECSPI1 (0x3 << 0)
+#define CCGR1_ECSPI2 (0x3 << 2)
+#define CCGR1_ECSPI3 (0x3 << 4)
+#define CCGR1_ECSPI4 (0x3 << 6)
+#define CCGR1_ECSPI5 (0x3 << 8)
+#define CCGR1_ENET (0x3 << 10)
+#define CCGR1_EPIT1 (0x3 << 12)
+#define CCGR1_EPIT2 (0x3 << 14)
+#define CCGR1_ESAI (0x3 << 16)
+#define CCGR1_GPT (0x3 << 20)
+#define CCGR1_GPT_SERIAL (0x3 << 22)
+#define CCM_CCGR2 0x070
+#define CCGR2_HDMI_TX (0x3 << 0)
+#define CCGR2_HDMI_TX_ISFR (0x3 << 4)
+#define CCGR2_I2C1 (0x3 << 6)
+#define CCGR2_I2C2 (0x3 << 8)
+#define CCGR2_I2C3 (0x3 << 10)
+#define CCGR2_IIM (0x3 << 12)
+#define CCGR2_IOMUX_IPT (0x3 << 14)
+#define CCGR2_IPMUX1 (0x3 << 16)
+#define CCGR2_IPMUX2 (0x3 << 18)
+#define CCGR2_IPMUX3 (0x3 << 20)
+#define CCGR2_IPSYNC_IP2APB_TZASC1 (0x3 << 22)
+#define CCGR2_IPSYNC_IP2APB_TZASC2 (0x3 << 24)
+#define CCGR2_IPSYNC_VDOA (0x3 << 26)
+#define CCM_CCGR3 0x074
+#define CCGR3_IPU1_IPU (0x3 << 0)
+#define CCGR3_IPU1_DI0 (0x3 << 2)
+#define CCGR3_IPU1_DI1 (0x3 << 4)
+#define CCGR3_IPU2_IPU (0x3 << 6)
+#define CCGR3_IPU2_DI0 (0x3 << 8)
+#define CCGR3_IPU2_DI1 (0x3 << 10)
+#define CCGR3_LDB_DI0 (0x3 << 12)
+#define CCGR3_LDB_DI1 (0x3 << 14)
+#define CCGR3_MMDC_CORE_ACLK_FAST (0x3 << 20)
+#define CCGR3_CG11 (0x3 << 22)
+#define CCGR3_MMDC_CORE_IPG (0x3 << 24)
+#define CCGR3_CG13 (0x3 << 26)
+#define CCGR3_OCRAM (0x3 << 28)
+#define CCM_CCGR4 0x078
+#define CCGR4_PL301_MX6QFAST1_S133 (0x3 << 8)
+#define CCGR4_PL301_MX6QPER1_BCH (0x3 << 12)
+#define CCGR4_PL301_MX6QPER2_MAIN (0x3 << 14)
+#define CCM_CCGR5 0x07C
+#define CCGR5_SATA (0x3 << 4)
+#define CCGR5_SDMA (0x3 << 6)
+#define CCGR5_SSI1 (0x3 << 18)
+#define CCGR5_SSI2 (0x3 << 20)
+#define CCGR5_SSI3 (0x3 << 22)
+#define CCGR5_UART (0x3 << 24)
+#define CCGR5_UART_SERIAL (0x3 << 26)
+#define CCM_CCGR6 0x080
+#define CCGR6_USBOH3 (0x3 << 0)
+#define CCGR6_USDHC1 (0x3 << 2)
+#define CCGR6_USDHC2 (0x3 << 4)
+#define CCGR6_USDHC3 (0x3 << 6)
+#define CCGR6_USDHC4 (0x3 << 8)
+#define CCM_CMEOR 0x088
+
+#define CCM_ANALOG_PLL_VIDEO 0x000040a0
+#define CCM_ANALOG_PLL_VIDEO_LOCK (1u << 31)
+#define CCM_ANALOG_PLL_VIDEO_BYPASS (1u << 16)
+#define CCM_ANALOG_PLL_VIDEO_ENABLE (1u << 13)
+#define CCM_ANALOG_PLL_VIDEO_POWERDOWN (1u << 12)
+#define CCM_ANALOG_PLL_VIDEO_POST_DIV_SELECT_MASK (3u << 19)
+#define CCM_ANALOG_PLL_VIDEO_POST_DIV_2 (1u << 19)
+#define CCM_ANALOG_PLL_VIDEO_DIV_SELECT_MASK (0x7f << 0)
+#define CCM_ANALOG_PLL_VIDEO_DIV_SELECT_SHIFT 0
+
+#define CCM_ANALOG_PLL_VIDEO_NUM 0x000040b0
+#define CCM_ANALOG_PLL_VIDEO_DENOM 0x000040c0
+
+#define CCM_ANALOG_PLL_ENET 0x000040e0
+#define CCM_ANALOG_PLL_ENET_LOCK (1u << 31)
+#define CCM_ANALOG_PLL_ENET_ENABLE_100M (1u << 20) /* SATA */
+#define CCM_ANALOG_PLL_ENET_BYPASS (1u << 16)
+#define CCM_ANALOG_PLL_ENET_ENABLE (1u << 13) /* Ether */
+#define CCM_ANALOG_PLL_ENET_POWERDOWN (1u << 12)
+
+#endif
diff --git a/sys/arm/freescale/imx/imx6_hdmi.c b/sys/arm/freescale/imx/imx6_hdmi.c
new file mode 100644
index 000000000000..cd89710be897
--- /dev/null
+++ b/sys/arm/freescale/imx/imx6_hdmi.c
@@ -0,0 +1,218 @@
+/*-
+ * Copyright (c) 2015 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * HDMI core module
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/eventhandler.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+
+#include <dev/videomode/videomode.h>
+#include <dev/videomode/edidvar.h>
+
+#include <arm/freescale/imx/imx_ccmvar.h>
+#include <arm/freescale/imx/imx_iomuxvar.h>
+#include <arm/freescale/imx/imx_iomuxreg.h>
+
+#include <dev/hdmi/dwc_hdmi.h>
+
+#include "hdmi_if.h"
+
+struct imx_hdmi_softc {
+ struct dwc_hdmi_softc base;
+ phandle_t i2c_xref;
+ eventhandler_tag eh_tag;
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"fsl,imx6dl-hdmi", 1},
+ {"fsl,imx6q-hdmi", 1},
+ {NULL, 0}
+};
+
+static device_t
+imx_hdmi_get_i2c_dev(device_t dev)
+{
+ struct imx_hdmi_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (sc->i2c_xref == 0)
+ return (NULL);
+
+ return (OF_device_from_xref(sc->i2c_xref));
+}
+
+/*
+ * Deferred HDMI init. dwc_hdmi_init() does i2c transfers for DDC/EDID. The imx
+ * i2c devices also use a config_intrhook function to finish their init, because
+ * they require interrupts to perform transfers. There is no way to control
+ * whether the i2c or our hdmi intrhook function runs first. If we go first we
+ * have to continue waiting until after the i2c driver is ready to do transfers
+ * and has registered its phandle.
+ *
+ * This function is used as both a config_intrhook function and after that as an
+ * eventhandler callback function (if necessary), to see if our i2c device is
+ * ready yet. When it is, continue with hdmi init. When first called as an
+ * intrhook function the i2c devices might be ready, in which case we never
+ * register as an eventhandler at all. Otherwise we register to see newbus
+ * attach events, and as each device attaches we check to see whether it was the
+ * i2c device we care about. Once we have our i2c device we unregister from
+ * seeing further attach events.
+ */
+static void
+imx_hdmi_init(void *dev)
+{
+ struct imx_hdmi_softc *sc;
+
+ sc = device_get_softc((device_t)dev);
+
+ if (OF_device_from_xref(sc->i2c_xref) != NULL) {
+ if (sc->eh_tag != NULL) {
+ EVENTHANDLER_DEREGISTER_NOWAIT(device_attach,
+ sc->eh_tag);
+ }
+ dwc_hdmi_init(dev);
+ return;
+ }
+
+ if (bootverbose)
+ device_printf((device_t)dev, "Waiting for DDC i2c device\n");
+
+ if (sc->eh_tag == NULL) {
+ sc->eh_tag = EVENTHANDLER_REGISTER(device_attach,
+ imx_hdmi_init, dev, EVENTHANDLER_PRI_ANY);
+ }
+}
+
+static int
+imx_hdmi_detach(device_t dev)
+{
+ struct imx_hdmi_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (sc->base.sc_mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY,
+ sc->base.sc_mem_rid, sc->base.sc_mem_res);
+
+ return (0);
+}
+
+static int
+imx_hdmi_attach(device_t dev)
+{
+ struct imx_hdmi_softc *sc;
+ int err;
+ uint32_t gpr3;
+ phandle_t node, i2c_xref;
+
+ sc = device_get_softc(dev);
+ sc->base.sc_dev = dev;
+ sc->base.sc_get_i2c_dev = imx_hdmi_get_i2c_dev;
+ err = 0;
+
+ /* Allocate memory resources. */
+ sc->base.sc_mem_rid = 0;
+ sc->base.sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &sc->base.sc_mem_rid, RF_ACTIVE);
+ if (sc->base.sc_mem_res == NULL) {
+ device_printf(dev, "Cannot allocate memory resources\n");
+ err = ENXIO;
+ goto out;
+ }
+
+ node = ofw_bus_get_node(dev);
+ if (OF_getencprop(node, "ddc-i2c-bus", &i2c_xref, sizeof(i2c_xref)) == -1)
+ sc->i2c_xref = 0;
+ else
+ sc->i2c_xref = i2c_xref;
+
+ imx_ccm_hdmi_enable();
+
+ gpr3 = imx_iomux_gpr_get(IOMUXC_GPR3);
+ gpr3 &= ~(IOMUXC_GPR3_HDMI_MASK);
+ gpr3 |= IOMUXC_GPR3_HDMI_IPU1_DI0;
+ imx_iomux_gpr_set(IOMUXC_GPR3, gpr3);
+
+ /* Further HDMI init requires interrupts for i2c transfers. */
+ config_intrhook_oneshot(imx_hdmi_init, dev);
+ return (0);
+
+out:
+ imx_hdmi_detach(dev);
+
+ return (err);
+}
+
+static int
+imx_hdmi_probe(device_t dev)
+{
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Freescale i.MX6 HDMI core");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static device_method_t imx_hdmi_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, imx_hdmi_probe),
+ DEVMETHOD(device_attach, imx_hdmi_attach),
+ DEVMETHOD(device_detach, imx_hdmi_detach),
+
+ /* HDMI methods */
+ DEVMETHOD(hdmi_get_edid, dwc_hdmi_get_edid),
+ DEVMETHOD(hdmi_set_videomode, dwc_hdmi_set_videomode),
+
+ DEVMETHOD_END
+};
+
+static driver_t imx_hdmi_driver = {
+ "hdmi",
+ imx_hdmi_methods,
+ sizeof(struct imx_hdmi_softc)
+};
+
+static devclass_t imx_hdmi_devclass;
+
+DRIVER_MODULE(hdmi, simplebus, imx_hdmi_driver, imx_hdmi_devclass, 0, 0);
diff --git a/sys/arm/freescale/imx/imx6_ipu.c b/sys/arm/freescale/imx/imx6_ipu.c
new file mode 100644
index 000000000000..417f8f64a28c
--- /dev/null
+++ b/sys/arm/freescale/imx/imx6_ipu.c
@@ -0,0 +1,1272 @@
+/*-
+ * Copyright 2015 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/clock.h>
+#include <sys/eventhandler.h>
+#include <sys/time.h>
+#include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+#include <sys/fbio.h>
+#include <sys/consio.h>
+
+#include <machine/bus.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/fb/fbreg.h>
+#include <dev/vt/vt.h>
+
+#include <dev/videomode/videomode.h>
+#include <dev/videomode/edidvar.h>
+
+#include <arm/freescale/imx/imx6_src.h>
+#include <arm/freescale/imx/imx_ccmvar.h>
+
+#include "fb_if.h"
+#include "hdmi_if.h"
+
+static int have_ipu = 0;
+
+#define MODE_HBP(mode) ((mode)->htotal - (mode)->hsync_end)
+#define MODE_HFP(mode) ((mode)->hsync_start - (mode)->hdisplay)
+#define MODE_HSW(mode) ((mode)->hsync_end - (mode)->hsync_start)
+#define MODE_VBP(mode) ((mode)->vtotal - (mode)->vsync_end)
+#define MODE_VFP(mode) ((mode)->vsync_start - (mode)->vdisplay)
+#define MODE_VSW(mode) ((mode)->vsync_end - (mode)->vsync_start)
+
+#define MODE_BPP 16
+#define MODE_PIXEL_CLOCK_INVERT 1
+
+#define DMA_CHANNEL 23
+#define DC_CHAN5 5
+#define DI_PORT 0
+
+#define IPU_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define IPU_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#define IPU_LOCK_INIT(_sc) mtx_init(&(_sc)->sc_mtx, \
+ device_get_nameunit(_sc->sc_dev), "ipu", MTX_DEF)
+#define IPU_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx)
+
+#define IPU_READ4(_sc, reg) bus_read_4((_sc)->sc_mem_res, (reg))
+#define IPU_WRITE4(_sc, reg, value) \
+ bus_write_4((_sc)->sc_mem_res, (reg), (value))
+
+#define CPMEM_BASE 0x300000
+#define DC_TEMPL_BASE 0x380000
+
+/* Microcode */
+/* Word 1 */
+#define TEMPLATE_SYNC(v) ((v) << 0)
+#define TEMPLATE_GLUELOGIC(v) ((v) << 4)
+#define TEMPLATE_MAPPING(v) ((v) << 15)
+#define TEMPLATE_WAVEFORM(v) ((v) << 11)
+#define GLUELOGIC_KEEP_ASSERTED (1 << 3)
+#define GLUELOGIC_KEEP_NEGATED (1 << 2)
+/* Word 2 */
+#define TEMPLATE_OPCODE(v) ((v) << 4)
+#define OPCODE_WROD 0x18
+#define TEMPLATE_STOP (1 << 9)
+
+#define IPU_CONF 0x200000
+#define IPU_CONF_DMFC_EN (1 << 10)
+#define IPU_CONF_DC_EN (1 << 9)
+#define IPU_CONF_DI1_EN (1 << 7)
+#define IPU_CONF_DI0_EN (1 << 6)
+#define IPU_CONF_DP_EN (1 << 5)
+#define IPU_DISP_GEN 0x2000C4
+#define DISP_GEN_DI1_CNTR_RELEASE (1 << 25)
+#define DISP_GEN_DI0_CNTR_RELEASE (1 << 24)
+#define DISP_GEN_MCU_MAX_BURST_STOP (1 << 22)
+#define DISP_GEN_MCU_T_SHIFT 18
+#define IPU_MEM_RST 0x2000DC
+#define IPU_MEM_RST_START (1 << 31)
+#define IPU_MEM_RST_ALL 0x807FFFFF
+#define IPU_CH_DB_MODE_SEL_0 0x200150
+#define IPU_CH_DB_MODE_SEL_1 0x200154
+#define IPU_CUR_BUF_0 0x20023C
+#define IPU_CUR_BUF_1 0x200240
+
+#define IPU_IDMAC_CH_EN_1 0x208004
+#define IPU_IDMAC_CH_EN_2 0x208008
+#define IPU_IDMAC_CH_PRI_1 0x208014
+#define IPU_IDMAC_CH_PRI_2 0x208018
+
+#define IPU_DI0_GENERAL 0x240000
+#define DI_CLOCK_EXTERNAL (1 << 20)
+#define DI_GENERAL_POL_CLK (1 << 17)
+#define DI_GENERAL_POLARITY_3 (1 << 2)
+#define DI_GENERAL_POLARITY_2 (1 << 1)
+#define IPU_DI0_BS_CLKGEN0 0x240004
+#define DI_BS_CLKGEN0(_int, _frac) (((_int) << 4) | (_frac))
+#define IPU_DI0_BS_CLKGEN1 0x240008
+#define DI_BS_CLKGEN1_DOWN(_int, _frac) ((((_int) << 1) | (_frac)) << 16)
+#define IPU_DI0_SW_GEN0_1 0x24000C
+#define DI_RUN_VALUE_M1(v) ((v) << 19)
+#define DI_RUN_RESOLUTION(v) ((v) << 16)
+#define DI_OFFSET_VALUE(v) ((v) << 3)
+#define IPU_DI0_SW_GEN1_1 0x240030
+#define DI_CNT_POLARITY_GEN_EN(v) ((v) << 29)
+#define DI_CNT_AUTO_RELOAD (1 << 28)
+#define DI_CNT_CLR_SEL(v) ((v) << 25)
+#define DI_CNT_DOWN(v) ((v) << 16)
+#define DI_CNT_POLARITY_TRIGGER_SEL(v) ((v) << 12)
+#define DI_CNT_POLARITY_CLR_SEL(v) ((v) << 9)
+#define IPU_DI0_SYNC_AS_GEN 0x240054
+#define SYNC_AS_GEN_VSYNC_SEL(v) ((v) << 13)
+#define SYNC_AS_GEN_SYNC_START(v) ((v) << 0)
+#define IPU_DI0_DW_GEN_0 0x240058
+#define DW_GEN_DI_ACCESS_SIZE(v) ((v) << 24)
+#define DW_GEN_DI_COMPONENT_SIZE(v) ((v) << 16)
+#define DW_GEN_DI_SET_MASK 3
+#define DW_GEN_DI_PIN_15_SET(v) ((v) << 8)
+#define IPU_DI0_DW_SET3_0 0x240118
+#define DW_SET_DATA_CNT_DOWN(v) ((v) << 16)
+#define DW_SET_DATA_CNT_UP(v) ((v) << 0)
+#define IPU_DI0_STP_REP 0x240148
+#define IPU_DI0_POL 0x240164
+#define DI_POL_DRDY_POLARITY_15 (1 << 4)
+#define IPU_DI0_SCR_CONF 0x240170
+
+#define IPU_DI1_GENERAL 0x248000
+#define IPU_DI1_BS_CLKGEN0 0x248004
+#define IPU_DI1_BS_CLKGEN1 0x248008
+#define IPU_DI1_SW_GEN0_1 0x24800C
+#define IPU_DI1_SW_GEN1_1 0x248030
+#define IPU_DI1_SYNC_AS_GEN 0x248054
+#define IPU_DI1_DW_GEN_0 0x248058
+#define IPU_DI1_POL 0x248164
+#define IPU_DI1_DW_SET3_0 0x248118
+#define IPU_DI1_STP_REP 0x248148
+#define IPU_DI1_SCR_CONF 0x248170
+#define DMFC_RD_CHAN 0x260000
+#define DMFC_WR_CHAN 0x260004
+#define DMFC_WR_CHAN_BURST_SIZE_32 (0 << 6)
+#define DMFC_WR_CHAN_BURST_SIZE_16 (1 << 6)
+#define DMFC_WR_CHAN_BURST_SIZE_8 (2 << 6)
+#define DMFC_WR_CHAN_BURST_SIZE_4 (3 << 6)
+#define DMFC_WR_CHAN_BURST_SIZE_4 (3 << 6)
+#define DMFC_WR_CHAN_FIFO_SIZE_128 (2 << 3)
+#define DMFC_WR_CHAN_DEF 0x260008
+#define DMFC_WR_CHAN_DEF_WM_CLR_2C(v) ((v) << 29)
+#define DMFC_WR_CHAN_DEF_WM_CLR_1C(v) ((v) << 21)
+#define DMFC_WR_CHAN_DEF_WM_CLR_2(v) ((v) << 13)
+#define DMFC_WR_CHAN_DEF_WM_CLR_1(v) ((v) << 5)
+#define DMFC_WR_CHAN_DEF_WM_SET_1(v) ((v) << 2)
+#define DMFC_WR_CHAN_DEF_WM_EN_1 (1 << 1)
+#define DMFC_DP_CHAN 0x26000C
+#define DMFC_DP_CHAN_BURST_SIZE_8 2
+#define DMFC_DP_CHAN_FIFO_SIZE_256 1
+#define DMFC_DP_CHAN_FIFO_SIZE_128 2
+#define DMFC_DP_CHAN_BURST_SIZE_5F(v) ((v) << 14)
+#define DMFC_DP_CHAN_FIFO_SIZE_5F(v) ((v) << 11)
+#define DMFC_DP_CHAN_ST_ADDR_SIZE_5F(v) ((v) << 8)
+#define DMFC_DP_CHAN_BURST_SIZE_5B(v) ((v) << 6)
+#define DMFC_DP_CHAN_FIFO_SIZE_5B(v) ((v) << 3)
+#define DMFC_DP_CHAN_ST_ADDR_SIZE_5B(v) ((v) << 0)
+#define DMFC_DP_CHAN_DEF 0x260010
+#define DMFC_DP_CHAN_DEF_WM_CLR_6F(v) ((v) << 29)
+#define DMFC_DP_CHAN_DEF_WM_CLR_6B(v) ((v) << 21)
+#define DMFC_DP_CHAN_DEF_WM_CLR_5F(v) ((v) << 13)
+#define DMFC_DP_CHAN_DEF_WM_SET_5F(v) ((v) << 10)
+#define DMFC_DP_CHAN_DEF_WM_EN_5F (1 << 9)
+#define DMFC_DP_CHAN_DEF_WM_CLR_5B(v) ((v) << 5)
+#define DMFC_DP_CHAN_DEF_WM_SET_5B(v) ((v) << 2)
+#define DMFC_DP_CHAN_DEF_WM_EN_5B (1 << 1)
+#define DMFC_GENERAL_1 0x260014
+#define DMFC_GENERAL_1_WAIT4EOT_5B (1 << 20)
+#define DMFC_IC_CTRL 0x26001C
+#define DMFC_IC_CTRL_DISABLED 0x2
+
+#define DC_WRITE_CH_CONF_1 0x0025801C
+#define WRITE_CH_CONF_PROG_CHAN_TYP_MASK (7 << 5)
+#define WRITE_CH_CONF_PROG_CHAN_NORMAL (4 << 5)
+#define DC_WRITE_CH_ADDR_1 0x00258020
+#define DC_WRITE_CH_CONF_5 0x0025805C
+#define WRITE_CH_CONF_PROG_DISP_ID(v) ((v) << 3)
+#define WRITE_CH_CONF_PROG_DI_ID(v) ((v) << 2)
+#define WRITE_CH_CONF_PROG_W_SIZE(v) (v)
+#define DC_WRITE_CH_ADDR_5 0x00258060
+#define DC_RL0_CH_5 0x00258064
+#define DC_GEN 0x002580D4
+#define DC_GEN_SYNC_PRIORITY (1 << 7)
+#define DC_GEN_ASYNC (0 << 1)
+#define DC_GEN_SYNC (2 << 1)
+#define DC_DISP_CONF2(di) (0x002580E8 + (di) * 4)
+#define DC_MAP_CONF_0 0x00258108
+#define DC_MAP_CONF_15 0x00258144
+#define DC_MAP_CONF_VAL(map) (DC_MAP_CONF_15 + ((map) / 2) * sizeof(uint32_t))
+#define MAP_CONF_VAL_MASK 0xffff
+#define DC_MAP_CONF_PTR(ptr) (DC_MAP_CONF_0 + ((ptr) / 2) * sizeof(uint32_t))
+#define MAP_CONF_PTR_MASK 0x1f
+
+#define DI_COUNTER_INT_HSYNC 1
+#define DI_COUNTER_HSYNC 2
+#define DI_COUNTER_VSYNC 3
+#define DI_COUNTER_AD_0 4
+#define DI_COUNTER_AD_1 5
+
+#define DI_SYNC_NONE 0
+#define DI_SYNC_CLK 1
+#define DI_SYNC_COUNTER(c) ((c) + 1)
+
+struct ipu_cpmem_word {
+ uint32_t data[5];
+ uint32_t padding[3];
+};
+
+struct ipu_cpmem_ch_param {
+ struct ipu_cpmem_word word[2];
+};
+
+#define CH_PARAM_RESET(param) memset(param, 0, sizeof(*param))
+#define IPU_READ_CH_PARAM(_sc, ch, param) bus_read_region_4( \
+ (_sc)->sc_mem_res, CPMEM_BASE + ch * (sizeof(*param)),\
+ (uint32_t*)param, sizeof(*param) / 4)
+#define IPU_WRITE_CH_PARAM(_sc, ch, param) bus_write_region_4( \
+ (_sc)->sc_mem_res, CPMEM_BASE + ch * (sizeof(*param)),\
+ (uint32_t*)param, sizeof(*param) / 4)
+
+#define CH_PARAM_SET_FW(param, v) ipu_ch_param_set_value((param), \
+ 0, 125, 13, (v))
+#define CH_PARAM_SET_FH(param, v) ipu_ch_param_set_value((param), \
+ 0, 138, 12, (v))
+#define CH_PARAM_SET_SLY(param, v) ipu_ch_param_set_value((param), \
+ 1, 102, 14, (v))
+#define CH_PARAM_SET_EBA0(param, v) ipu_ch_param_set_value((param), \
+ 1, 0, 29, (v))
+#define CH_PARAM_SET_EBA1(param, v) ipu_ch_param_set_value((param), \
+ 1, 29, 29, (v))
+#define CH_PARAM_SET_BPP(param, v) ipu_ch_param_set_value((param), \
+ 0, 107, 3, (v))
+#define CH_PARAM_SET_PFS(param, v) ipu_ch_param_set_value((param), \
+ 1, 85, 4, (v))
+#define CH_PARAM_SET_NPB(param, v) ipu_ch_param_set_value((param), \
+ 1, 78, 7, (v))
+#define CH_PARAM_SET_UBO(param, v) ipu_ch_param_set_value((param), \
+ 0, 46, 22, (v))
+#define CH_PARAM_SET_VBO(param, v) ipu_ch_param_set_value((param), \
+ 0, 68, 22, (v))
+
+#define CH_PARAM_SET_RED_WIDTH(param, v) ipu_ch_param_set_value((param), \
+ 1, 116, 3, (v))
+#define CH_PARAM_SET_RED_OFFSET(param, v) ipu_ch_param_set_value((param), \
+ 1, 128, 5, (v))
+
+#define CH_PARAM_SET_GREEN_WIDTH(param, v) ipu_ch_param_set_value((param), \
+ 1, 119, 3, (v))
+#define CH_PARAM_SET_GREEN_OFFSET(param, v) ipu_ch_param_set_value((param), \
+ 1, 133, 5, (v))
+
+#define CH_PARAM_SET_BLUE_WIDTH(param, v) ipu_ch_param_set_value((param), \
+ 1, 122, 3, (v))
+#define CH_PARAM_SET_BLUE_OFFSET(param, v) ipu_ch_param_set_value((param), \
+ 1, 138, 5, (v))
+
+#define CH_PARAM_SET_ALPHA_WIDTH(param, v) ipu_ch_param_set_value((param), \
+ 1, 125, 3, (v))
+#define CH_PARAM_SET_ALPHA_OFFSET(param, v) ipu_ch_param_set_value((param), \
+ 1, 143, 5, (v))
+
+#define CH_PARAM_GET_FW(param) ipu_ch_param_get_value((param), \
+ 0, 125, 13)
+#define CH_PARAM_GET_FH(param) ipu_ch_param_get_value((param), \
+ 0, 138, 12)
+#define CH_PARAM_GET_SLY(param) ipu_ch_param_get_value((param), \
+ 1, 102, 14)
+#define CH_PARAM_GET_EBA0(param) ipu_ch_param_get_value((param), \
+ 1, 0, 29)
+#define CH_PARAM_GET_EBA1(param) ipu_ch_param_get_value((param), \
+ 1, 29, 29)
+#define CH_PARAM_GET_BPP(param) ipu_ch_param_get_value((param), \
+ 0, 107, 3)
+#define CH_PARAM_GET_PFS(param) ipu_ch_param_get_value((param), \
+ 1, 85, 4)
+#define CH_PARAM_GET_NPB(param) ipu_ch_param_get_value((param), \
+ 1, 78, 7)
+#define CH_PARAM_GET_UBO(param) ipu_ch_param_get_value((param), \
+ 0, 46, 22)
+#define CH_PARAM_GET_VBO(param) ipu_ch_param_get_value((param), \
+ 0, 68, 22)
+
+#define CH_PARAM_GET_RED_WIDTH(param) ipu_ch_param_get_value((param), \
+ 1, 116, 3)
+#define CH_PARAM_GET_RED_OFFSET(param) ipu_ch_param_get_value((param), \
+ 1, 128, 5)
+
+#define CH_PARAM_GET_GREEN_WIDTH(param) ipu_ch_param_get_value((param), \
+ 1, 119, 3)
+#define CH_PARAM_GET_GREEN_OFFSET(param) ipu_ch_param_get_value((param), \
+ 1, 133, 5)
+
+#define CH_PARAM_GET_BLUE_WIDTH(param) ipu_ch_param_get_value((param), \
+ 1, 122, 3)
+#define CH_PARAM_GET_BLUE_OFFSET(param) ipu_ch_param_get_value((param), \
+ 1, 138, 5)
+
+#define CH_PARAM_GET_ALPHA_WIDTH(param) ipu_ch_param_get_value((param), \
+ 1, 125, 3)
+#define CH_PARAM_GET_ALPHA_OFFSET(param) ipu_ch_param_get_value((param), \
+ 1, 143, 5)
+
+#define IPU_PIX_FORMAT_BPP_32 0
+#define IPU_PIX_FORMAT_BPP_24 1
+#define IPU_PIX_FORMAT_BPP_18 2
+#define IPU_PIX_FORMAT_BPP_16 3
+#define IPU_PIX_FORMAT_BPP_12 4
+#define IPU_PIX_FORMAT_BPP_8 5
+#define IPU_PIX_FORMAT_BPP_
+
+#define IPU_PIX_FORMAT_RGB 7
+
+enum dc_event_t {
+ DC_EVENT_NF = 0,
+ DC_EVENT_NL,
+ DC_EVENT_EOF,
+ DC_EVENT_NFIELD,
+ DC_EVENT_EOL,
+ DC_EVENT_EOFIELD,
+ DC_EVENT_NEW_ADDR,
+ DC_EVENT_NEW_CHAN,
+ DC_EVENT_NEW_DATA
+};
+
+struct ipu_softc {
+ device_t sc_dev;
+ struct resource *sc_mem_res;
+ int sc_mem_rid;
+ struct resource *sc_irq_res;
+ int sc_irq_rid;
+ void *sc_intr_hl;
+ struct mtx sc_mtx;
+ struct fb_info sc_fb_info;
+ const struct videomode *sc_mode;
+
+ /* Framebuffer */
+ bus_dma_tag_t sc_dma_tag;
+ bus_dmamap_t sc_dma_map;
+ size_t sc_fb_size;
+ bus_addr_t sc_fb_phys;
+ uint8_t *sc_fb_base;
+
+ /* HDMI */
+ eventhandler_tag sc_hdmi_evh;
+};
+
+static void
+ipu_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err)
+{
+ bus_addr_t *addr;
+
+ if (err)
+ return;
+
+ addr = (bus_addr_t*)arg;
+ *addr = segs[0].ds_addr;
+}
+
+static void
+ipu_ch_param_set_value(struct ipu_cpmem_ch_param *param,
+ int word, unsigned int offset, int len, uint32_t value)
+{
+ uint32_t datapos, bitpos, mask;
+ uint32_t data, data2;
+
+ KASSERT((len <= 32), ("%s: field len is more than 32", __func__));
+
+ datapos = offset / 32;
+ bitpos = offset % 32;
+
+ mask = (1 << len) - 1;
+ data = param->word[word].data[datapos];
+ data &= ~(mask << bitpos);
+ data |= (value << bitpos);
+ param->word[word].data[datapos] = data;
+
+ if ((bitpos + len) > 32) {
+ len = bitpos + len - 32;
+ mask = (1UL << len) - 1;
+ data2 = param->word[word].data[datapos + 1];
+ data2 &= mask;
+ data2 |= (value >> (32 - bitpos));
+ param->word[word].data[datapos + 1] = data2;
+ }
+}
+
+#ifdef DEBUG
+static uint32_t
+ipu_ch_param_get_value(struct ipu_cpmem_ch_param *param,
+ int word, unsigned int offset, int len)
+{
+ uint32_t datapos, bitpos, mask;
+ uint32_t data, data2;
+
+ KASSERT((len <= 32), ("%s: field len is more than 32", __func__));
+
+ datapos = offset / 32;
+ bitpos = offset % 32;
+ mask = (1UL << len) - 1;
+ data = param->word[word].data[datapos];
+ data = data >> bitpos;
+ data &= mask;
+ if ((bitpos + len) > 32) {
+ len = bitpos + len - 32;
+ mask = (1UL << len) - 1;
+ data2 = param->word[word].data[datapos + 1];
+ data2 &= mask;
+ data |= (data2 << (32 - bitpos));
+ }
+
+ return (data);
+}
+
+static void
+ipu_print_channel(struct ipu_cpmem_ch_param *param)
+{
+ int offset0[] = {0, 10, 19, 32, 44, 45, 46, 68, 90, 94, 95, 113, 114, 117, 119, 120, 121, 122, 123, 124, 125, 138, 150, 151, -1};
+ int offset1[] = {0, 29, 58, 78, 85, 89, 90, 93, 95, 102, 116, 119, 122, 125, 128, 133, 138, 143, 148, 149, 150, -1};
+ printf("WORD0: %08x %08x %08x %08x %08x\n",
+ param->word[0].data[0], param->word[0].data[1],
+ param->word[0].data[2], param->word[0].data[3],
+ param->word[0].data[4]);
+ printf("WORD1: %08x %08x %08x %08x %08x\n",
+ param->word[1].data[0], param->word[1].data[1],
+ param->word[1].data[2], param->word[1].data[3],
+ param->word[1].data[4]);
+
+ for (int i = 0; offset0[i + 1] != -1; i++) {
+ int len = offset0[i + 1] - offset0[i];
+ printf("W0[%d:%d] = %d\n", offset0[i],
+ offset0[i] + len - 1,
+ ipu_ch_param_get_value(param, 0, offset0[i], len)
+ );
+ }
+
+ for (int i = 0; offset1[i + 1] != -1; i++) {
+ int len = offset1[i + 1] - offset1[i];
+ printf("W1[%d:%d] = %d\n", offset1[i],
+ offset1[i] + len - 1,
+ ipu_ch_param_get_value(param, 1, offset1[i], len)
+ );
+ }
+
+ printf("FW: %d\n", CH_PARAM_GET_FW(param));
+ printf("FH: %d\n", CH_PARAM_GET_FH(param));
+ printf("SLY: %d\n", CH_PARAM_GET_SLY(param));
+ printf("EBA0: 0x%08x\n", CH_PARAM_GET_EBA0(param));
+ printf("EBA1: 0x%08x\n", CH_PARAM_GET_EBA1(param));
+ printf("BPP: %d\n", CH_PARAM_GET_BPP(param));
+ printf("PFS: %d\n", CH_PARAM_GET_PFS(param));
+ printf("NPB: %d\n", CH_PARAM_GET_NPB(param));
+ printf("UBO: %d\n", CH_PARAM_GET_UBO(param));
+ printf("VBO: %d\n", CH_PARAM_GET_VBO(param));
+ printf("RED: %d bits @%d\n", CH_PARAM_GET_RED_WIDTH(param) + 1,
+ CH_PARAM_GET_RED_OFFSET(param));
+ printf("GREEN: %d bits @%d\n", CH_PARAM_GET_GREEN_WIDTH(param) + 1,
+ CH_PARAM_GET_GREEN_OFFSET(param));
+ printf("BLUE: %d bits @%d\n", CH_PARAM_GET_BLUE_WIDTH(param) + 1,
+ CH_PARAM_GET_BLUE_OFFSET(param));
+ printf("ALPHA: %d bits @%d\n", CH_PARAM_GET_ALPHA_WIDTH(param) + 1,
+ CH_PARAM_GET_ALPHA_OFFSET(param));
+}
+#endif
+
+static void
+ipu_di_enable(struct ipu_softc *sc, int di)
+{
+ uint32_t flag, reg;
+
+ flag = di ? DISP_GEN_DI1_CNTR_RELEASE : DISP_GEN_DI0_CNTR_RELEASE;
+ reg = IPU_READ4(sc, IPU_DISP_GEN);
+ reg |= flag;
+ IPU_WRITE4(sc, IPU_DISP_GEN, reg);
+}
+
+static void
+ipu_config_wave_gen_0(struct ipu_softc *sc, int di,
+ int wave_gen, int run_value, int run_res,
+ int offset_value, int offset_res)
+{
+ uint32_t addr, reg;
+
+ addr = (di ? IPU_DI1_SW_GEN0_1 : IPU_DI0_SW_GEN0_1)
+ + (wave_gen - 1) * sizeof(uint32_t);
+ reg = DI_RUN_VALUE_M1(run_value) |
+ DI_RUN_RESOLUTION(run_res) |
+ DI_OFFSET_VALUE(offset_value) | offset_res;
+ IPU_WRITE4(sc, addr, reg);
+}
+
+static void
+ipu_config_wave_gen_1(struct ipu_softc *sc, int di, int wave_gen,
+ int repeat_count, int cnt_clr_src,
+ int cnt_polarity_gen_en,
+ int cnt_polarity_clr_src,
+ int cnt_polarity_trigger_src,
+ int cnt_up, int cnt_down)
+{
+ uint32_t addr, reg;
+
+ addr = (di ? IPU_DI1_SW_GEN1_1 : IPU_DI0_SW_GEN1_1)
+ + (wave_gen - 1) * sizeof(uint32_t);
+ reg = DI_CNT_POLARITY_GEN_EN(cnt_polarity_gen_en) |
+ DI_CNT_CLR_SEL(cnt_clr_src) |
+ DI_CNT_POLARITY_TRIGGER_SEL(cnt_polarity_trigger_src) |
+ DI_CNT_POLARITY_CLR_SEL(cnt_polarity_clr_src);
+ reg |= DI_CNT_DOWN(cnt_down) | cnt_up;
+ if (repeat_count == 0)
+ reg |= DI_CNT_AUTO_RELOAD;
+ IPU_WRITE4(sc, addr, reg);
+
+ addr = (di ? IPU_DI1_STP_REP : IPU_DI0_STP_REP)
+ + (wave_gen - 1) / 2 * sizeof(uint32_t);
+ reg = IPU_READ4(sc, addr);
+ if (wave_gen % 2) {
+ reg &= ~(0xffff);
+ reg |= repeat_count;
+ }
+ else {
+ reg &= ~(0xffff << 16);
+ reg |= (repeat_count << 16);
+ }
+ IPU_WRITE4(sc, addr, reg);
+}
+
+static void
+ipu_reset_wave_gen(struct ipu_softc *sc, int di,
+ int wave_gen)
+{
+ uint32_t addr, reg;
+
+ addr = (di ? IPU_DI1_SW_GEN0_1 : IPU_DI0_SW_GEN0_1)
+ + (wave_gen - 1) * sizeof(uint32_t);
+ IPU_WRITE4(sc, addr, 0);
+
+ addr = (di ? IPU_DI1_SW_GEN1_1 : IPU_DI0_SW_GEN1_1)
+ + (wave_gen - 1) * sizeof(uint32_t);
+ IPU_WRITE4(sc, addr, 0);
+
+ addr = (di ? IPU_DI1_STP_REP : IPU_DI0_STP_REP)
+ + (wave_gen - 1) / 2 * sizeof(uint32_t);
+ reg = IPU_READ4(sc, addr);
+ if (wave_gen % 2)
+ reg &= ~(0xffff);
+ else
+ reg &= ~(0xffff << 16);
+ IPU_WRITE4(sc, addr, reg);
+}
+
+static void
+ipu_init_microcode_template(struct ipu_softc *sc, int di, int map)
+{
+ uint32_t addr;
+ uint32_t w1, w2;
+ int i, word;
+ int glue;
+
+ word = di ? 2 : 5;
+
+ for (i = 0; i < 3; i++) {
+ if (i == 0)
+ glue = GLUELOGIC_KEEP_ASSERTED;
+ else if (i == 1)
+ glue = GLUELOGIC_KEEP_NEGATED;
+ else if (i == 2)
+ glue = 0;
+
+ w1 = TEMPLATE_SYNC(5) |
+ TEMPLATE_GLUELOGIC(glue) |
+ TEMPLATE_WAVEFORM(1) | /* wave unit 0 */
+ TEMPLATE_MAPPING(map + 1);
+ /* operand is zero */
+
+ /* Write data to DI and Hold data in register */
+ w2 = TEMPLATE_OPCODE(OPCODE_WROD) |
+ TEMPLATE_STOP;
+
+ addr = DC_TEMPL_BASE + (word + i) * 2 * sizeof(uint32_t);
+ IPU_WRITE4(sc, addr, w1);
+ IPU_WRITE4(sc, addr + sizeof(uint32_t), w2);
+ }
+}
+
+static uint32_t
+ipu_calc_divisor(uint32_t reference, uint32_t freq)
+{
+ uint32_t div, i;
+ uint32_t delta, min_delta;
+
+ min_delta = freq;
+ div = 255;
+
+ for (i = 1; i < 255; i++) {
+ delta = abs(reference/i - freq);
+ if (delta < min_delta) {
+ div = i;
+ min_delta = delta;
+ }
+ }
+
+ return (div);
+}
+
+static void
+ipu_config_timing(struct ipu_softc *sc, int di)
+{
+ uint32_t div;
+ uint32_t di_scr_conf;
+ uint32_t gen_offset, gen;
+ uint32_t as_gen_offset, as_gen;
+ uint32_t dw_gen_offset, dw_gen;
+ uint32_t dw_set_offset, dw_set;
+ uint32_t bs_clkgen_offset;
+ int map;
+ uint32_t freq;
+
+ freq = sc->sc_mode->dot_clock * 1000;
+
+ div = ipu_calc_divisor(imx_ccm_ipu_hz(), freq);
+ map = 0;
+
+ bs_clkgen_offset = di ? IPU_DI1_BS_CLKGEN0 : IPU_DI0_BS_CLKGEN0;
+ IPU_WRITE4(sc, bs_clkgen_offset, DI_BS_CLKGEN0(div, 0));
+ /* half of the divider */
+ IPU_WRITE4(sc, bs_clkgen_offset + 4, DI_BS_CLKGEN1_DOWN(div / 2, div % 2));
+
+ /* Setup wave generator */
+ dw_gen_offset = di ? IPU_DI1_DW_GEN_0 : IPU_DI0_DW_GEN_0;
+ dw_gen = DW_GEN_DI_ACCESS_SIZE(div - 1) | DW_GEN_DI_COMPONENT_SIZE(div - 1);
+ dw_gen &= ~DW_GEN_DI_PIN_15_SET(DW_GEN_DI_SET_MASK);
+ dw_gen |= DW_GEN_DI_PIN_15_SET(3); /* set 3*/
+ IPU_WRITE4(sc, dw_gen_offset, dw_gen);
+
+ dw_set_offset = di ? IPU_DI1_DW_SET3_0 : IPU_DI0_DW_SET3_0;
+ dw_set = DW_SET_DATA_CNT_DOWN(div * 2) | DW_SET_DATA_CNT_UP(0);
+ IPU_WRITE4(sc, dw_set_offset, dw_set);
+
+ /* DI_COUNTER_INT_HSYNC */
+ ipu_config_wave_gen_0(sc, di, DI_COUNTER_INT_HSYNC,
+ sc->sc_mode->htotal - 1, DI_SYNC_CLK, 0, DI_SYNC_NONE);
+ ipu_config_wave_gen_1(sc, di, DI_COUNTER_INT_HSYNC,
+ 0, DI_SYNC_NONE, 0, DI_SYNC_NONE, DI_SYNC_NONE, 0, 0);
+
+ /* DI_COUNTER_HSYNC */
+ ipu_config_wave_gen_0(sc, di, DI_COUNTER_HSYNC,
+ sc->sc_mode->htotal - 1, DI_SYNC_CLK, 0, DI_SYNC_CLK);
+ ipu_config_wave_gen_1(sc, di, DI_COUNTER_HSYNC,
+ 0, DI_SYNC_NONE, 1, DI_SYNC_NONE, DI_SYNC_CLK,
+ 0, MODE_HSW(sc->sc_mode) * 2);
+
+ /* DI_COUNTER_VSYNC */
+ ipu_config_wave_gen_0(sc, di, DI_COUNTER_VSYNC,
+ sc->sc_mode->vtotal - 1, DI_SYNC_COUNTER(DI_COUNTER_INT_HSYNC),
+ 0, DI_SYNC_NONE);
+ ipu_config_wave_gen_1(sc, di, DI_COUNTER_VSYNC,
+ 0, DI_SYNC_NONE, 1, DI_SYNC_NONE,
+ DI_SYNC_COUNTER(DI_COUNTER_INT_HSYNC),
+ 0, MODE_VSW(sc->sc_mode) * 2);
+
+ di_scr_conf = di ? IPU_DI1_SCR_CONF : IPU_DI0_SCR_CONF;
+ IPU_WRITE4(sc, di_scr_conf, sc->sc_mode->vtotal - 1);
+
+ /* TODO: update DI_SCR_CONF */
+
+ /* Active Data 0 */
+ ipu_config_wave_gen_0(sc, di, DI_COUNTER_AD_0,
+ 0, DI_SYNC_COUNTER(DI_COUNTER_HSYNC),
+ MODE_VSW(sc->sc_mode) + MODE_VFP(sc->sc_mode), DI_SYNC_COUNTER(DI_COUNTER_HSYNC));
+ ipu_config_wave_gen_1(sc, di, DI_COUNTER_AD_0,
+ sc->sc_mode->vdisplay, DI_SYNC_COUNTER(DI_COUNTER_VSYNC),
+ 0, DI_SYNC_NONE, DI_SYNC_NONE, 0, 0);
+
+ ipu_config_wave_gen_0(sc, di, DI_COUNTER_AD_1,
+ 0, DI_SYNC_CLK, MODE_HSW(sc->sc_mode) + MODE_HFP(sc->sc_mode), DI_SYNC_CLK);
+ ipu_config_wave_gen_1(sc, di, DI_COUNTER_AD_1,
+ sc->sc_mode->hdisplay, DI_SYNC_COUNTER(DI_COUNTER_AD_0),
+ 0, DI_SYNC_NONE, DI_SYNC_NONE, 0, 0);
+
+ ipu_reset_wave_gen(sc, di, 6);
+ ipu_reset_wave_gen(sc, di, 7);
+ ipu_reset_wave_gen(sc, di, 8);
+ ipu_reset_wave_gen(sc, di, 9);
+
+ ipu_init_microcode_template(sc, di, map);
+
+ gen_offset = di ? IPU_DI1_GENERAL : IPU_DI0_GENERAL;
+ gen = IPU_READ4(sc, gen_offset);
+
+ if (sc->sc_mode->flags & VID_NHSYNC)
+ gen &= ~DI_GENERAL_POLARITY_2;
+ else /* active high */
+ gen |= DI_GENERAL_POLARITY_2;
+
+ if (sc->sc_mode->flags & VID_NVSYNC)
+ gen &= ~DI_GENERAL_POLARITY_3;
+ else /* active high */
+ gen |= DI_GENERAL_POLARITY_3;
+
+ if (MODE_PIXEL_CLOCK_INVERT)
+ gen &= ~DI_GENERAL_POL_CLK;
+ else
+ gen |= DI_GENERAL_POL_CLK;
+
+ /* Use LDB clock to drive pixel clock */
+ gen |= DI_CLOCK_EXTERNAL;
+
+ IPU_WRITE4(sc, gen_offset, gen);
+
+ as_gen_offset = di ? IPU_DI1_SYNC_AS_GEN : IPU_DI0_SYNC_AS_GEN;
+ as_gen = SYNC_AS_GEN_VSYNC_SEL(DI_COUNTER_VSYNC - 1) |
+ SYNC_AS_GEN_SYNC_START(2);
+ IPU_WRITE4(sc, as_gen_offset, as_gen);
+
+ IPU_WRITE4(sc, (di ? IPU_DI1_POL : IPU_DI0_POL), DI_POL_DRDY_POLARITY_15);
+
+ IPU_WRITE4(sc, DC_DISP_CONF2(di), sc->sc_mode->hdisplay);
+}
+
+static void
+ipu_dc_enable(struct ipu_softc *sc)
+{
+ uint32_t conf;
+
+ /* channel 1 uses DI1 */
+ IPU_WRITE4(sc, DC_WRITE_CH_CONF_1, WRITE_CH_CONF_PROG_DI_ID(1));
+
+ conf = IPU_READ4(sc, DC_WRITE_CH_CONF_5);
+ conf &= ~WRITE_CH_CONF_PROG_CHAN_TYP_MASK;
+ conf |= WRITE_CH_CONF_PROG_CHAN_NORMAL;
+ IPU_WRITE4(sc, DC_WRITE_CH_CONF_5, conf);
+}
+
+static void
+ipu_dc_link_event(struct ipu_softc *sc, int event, int addr, int priority)
+{
+ uint32_t reg;
+ int offset;
+ int shift;
+
+ if (event % 2)
+ shift = 16;
+ else
+ shift = 0;
+
+ offset = DC_RL0_CH_5 + (event / 2) * sizeof(uint32_t);
+
+ reg = IPU_READ4(sc, offset);
+ reg &= ~(0xFFFF << shift);
+ reg |= ((addr << 8) | priority) << shift;
+ IPU_WRITE4(sc, offset, reg);
+}
+
+static void
+ipu_dc_setup_map(struct ipu_softc *sc, int map,
+ int byte, int offset, int mask)
+{
+ uint32_t reg, shift, ptr;
+
+ ptr = map * 3 + byte;
+
+ reg = IPU_READ4(sc, DC_MAP_CONF_VAL(ptr));
+ if (ptr & 1)
+ shift = 16;
+ else
+ shift = 0;
+ reg &= ~(0xffff << shift);
+ reg |= ((offset << 8) | mask) << shift;
+ IPU_WRITE4(sc, DC_MAP_CONF_VAL(ptr), reg);
+
+ reg = IPU_READ4(sc, DC_MAP_CONF_PTR(map));
+ if (map & 1)
+ shift = 16 + 5 * byte;
+ else
+ shift = 5 * byte;
+ reg &= ~(MAP_CONF_PTR_MASK << shift);
+ reg |= (ptr) << shift;
+ IPU_WRITE4(sc, DC_MAP_CONF_PTR(map), reg);
+}
+
+static void
+ipu_dc_reset_map(struct ipu_softc *sc, int map)
+{
+ uint32_t reg, shift;
+
+ reg = IPU_READ4(sc, DC_MAP_CONF_VAL(map));
+ if (map & 1)
+ shift = 16;
+ else
+ shift = 0;
+ reg &= ~(MAP_CONF_VAL_MASK << shift);
+ IPU_WRITE4(sc, DC_MAP_CONF_VAL(map), reg);
+}
+
+static void
+ipu_dc_init(struct ipu_softc *sc, int di_port)
+{
+ int addr;
+ uint32_t conf;
+
+ if (di_port)
+ addr = 2;
+ else
+ addr = 5;
+
+ ipu_dc_link_event(sc, DC_EVENT_NL, addr, 3);
+ ipu_dc_link_event(sc, DC_EVENT_EOL, addr + 1, 2);
+ ipu_dc_link_event(sc, DC_EVENT_NEW_DATA, addr + 2, 1);
+ ipu_dc_link_event(sc, DC_EVENT_NF, 0, 0);
+ ipu_dc_link_event(sc, DC_EVENT_NFIELD, 0, 0);
+ ipu_dc_link_event(sc, DC_EVENT_EOF, 0, 0);
+ ipu_dc_link_event(sc, DC_EVENT_EOFIELD, 0, 0);
+ ipu_dc_link_event(sc, DC_EVENT_NEW_CHAN, 0, 0);
+ ipu_dc_link_event(sc, DC_EVENT_NEW_ADDR, 0, 0);
+
+ conf = WRITE_CH_CONF_PROG_W_SIZE(0x02) |
+ WRITE_CH_CONF_PROG_DISP_ID(DI_PORT) |
+ WRITE_CH_CONF_PROG_DI_ID(DI_PORT);
+
+ IPU_WRITE4(sc, DC_WRITE_CH_CONF_5, conf);
+ IPU_WRITE4(sc, DC_WRITE_CH_ADDR_5, 0x00000000);
+ IPU_WRITE4(sc, DC_GEN, DC_GEN_SYNC_PRIORITY | DC_GEN_SYNC); /* High priority, sync */
+}
+
+static void
+ipu_init_buffer(struct ipu_softc *sc)
+{
+ struct ipu_cpmem_ch_param param;
+ uint32_t stride;
+ uint32_t reg, db_mode_sel, cur_buf;
+
+ stride = sc->sc_mode->hdisplay * MODE_BPP / 8;
+
+ /* init channel parameters */
+ CH_PARAM_RESET(&param);
+ /* XXX: interlaced modes are not supported yet */
+ CH_PARAM_SET_FW(&param, sc->sc_mode->hdisplay - 1);
+ CH_PARAM_SET_FH(&param, sc->sc_mode->vdisplay - 1);
+ CH_PARAM_SET_SLY(&param, stride - 1);
+
+ CH_PARAM_SET_EBA0(&param, (sc->sc_fb_phys >> 3));
+ CH_PARAM_SET_EBA1(&param, (sc->sc_fb_phys >> 3));
+
+ CH_PARAM_SET_BPP(&param, IPU_PIX_FORMAT_BPP_16);
+ CH_PARAM_SET_PFS(&param, IPU_PIX_FORMAT_RGB);
+ /* 16 pixels per burst access */
+ CH_PARAM_SET_NPB(&param, 16 - 1);
+
+ CH_PARAM_SET_RED_OFFSET(&param, 0);
+ CH_PARAM_SET_RED_WIDTH(&param, 5 - 1);
+ CH_PARAM_SET_GREEN_OFFSET(&param, 5);
+ CH_PARAM_SET_GREEN_WIDTH(&param, 6 - 1);
+ CH_PARAM_SET_BLUE_OFFSET(&param, 11);
+ CH_PARAM_SET_BLUE_WIDTH(&param, 5 - 1);
+ CH_PARAM_SET_ALPHA_OFFSET(&param, 16);
+ CH_PARAM_SET_ALPHA_WIDTH(&param, 8 - 1);
+
+ CH_PARAM_SET_UBO(&param, 0);
+ CH_PARAM_SET_VBO(&param, 0);
+
+ IPU_WRITE_CH_PARAM(sc, DMA_CHANNEL, &param);
+#ifdef DEBUG
+ ipu_print_channel(&param);
+#endif
+
+ /* init DMFC */
+ IPU_WRITE4(sc, DMFC_IC_CTRL, DMFC_IC_CTRL_DISABLED);
+ /* High resolution DP */
+ IPU_WRITE4(sc, DMFC_WR_CHAN, DMFC_WR_CHAN_BURST_SIZE_8 |
+ DMFC_WR_CHAN_FIFO_SIZE_128);
+ IPU_WRITE4(sc, DMFC_WR_CHAN_DEF, DMFC_WR_CHAN_DEF_WM_CLR_2C(1) |
+ DMFC_WR_CHAN_DEF_WM_CLR_1C(1) |
+ DMFC_WR_CHAN_DEF_WM_CLR_2(1) |
+ DMFC_WR_CHAN_DEF_WM_CLR_1(7) |
+ DMFC_WR_CHAN_DEF_WM_SET_1(5) |
+ DMFC_WR_CHAN_DEF_WM_EN_1);
+
+ IPU_WRITE4(sc, DMFC_DP_CHAN,
+ DMFC_DP_CHAN_BURST_SIZE_5F(DMFC_DP_CHAN_BURST_SIZE_8) |
+ DMFC_DP_CHAN_FIFO_SIZE_5F(DMFC_DP_CHAN_FIFO_SIZE_128) |
+ DMFC_DP_CHAN_ST_ADDR_SIZE_5F(6) /* segment 6 */ |
+ DMFC_DP_CHAN_BURST_SIZE_5B(DMFC_DP_CHAN_BURST_SIZE_8) |
+ DMFC_DP_CHAN_FIFO_SIZE_5B(DMFC_DP_CHAN_FIFO_SIZE_256) |
+ DMFC_DP_CHAN_ST_ADDR_SIZE_5B(2) /* segment 2 */);
+
+ IPU_WRITE4(sc, DMFC_DP_CHAN_DEF, DMFC_DP_CHAN_DEF_WM_CLR_6F(1) |
+ DMFC_DP_CHAN_DEF_WM_CLR_6B(1) |
+ DMFC_DP_CHAN_DEF_WM_CLR_5F(7) |
+ DMFC_DP_CHAN_DEF_WM_SET_5F(5) |
+ DMFC_DP_CHAN_DEF_WM_EN_5F |
+ DMFC_DP_CHAN_DEF_WM_CLR_5B(7) |
+ DMFC_DP_CHAN_DEF_WM_SET_5B(5) |
+ DMFC_DP_CHAN_DEF_WM_EN_5B);
+
+ reg = IPU_READ4(sc, DMFC_GENERAL_1);
+ reg &= ~(DMFC_GENERAL_1_WAIT4EOT_5B);
+ IPU_WRITE4(sc, DMFC_GENERAL_1, reg);
+
+ /* XXX: set priority? */
+
+ /* Set single buffer mode */
+ if (DMA_CHANNEL < 32) {
+ db_mode_sel = IPU_CH_DB_MODE_SEL_0;
+ cur_buf = IPU_CUR_BUF_0;
+ } else {
+ db_mode_sel = IPU_CH_DB_MODE_SEL_1;
+ cur_buf = IPU_CUR_BUF_1;
+ }
+
+ reg = IPU_READ4(sc, db_mode_sel);
+ reg |= (1UL << (DMA_CHANNEL & 0x1f));
+ IPU_WRITE4(sc, db_mode_sel, reg);
+
+ IPU_WRITE4(sc, cur_buf, (1UL << (DMA_CHANNEL & 0x1f)));
+}
+
+static int
+ipu_init(struct ipu_softc *sc)
+{
+ uint32_t reg, off;
+ int i, err;
+ size_t dma_size;
+
+ IPU_WRITE4(sc, IPU_CONF, DI_PORT ? IPU_CONF_DI1_EN : IPU_CONF_DI0_EN);
+
+ IPU_WRITE4(sc, IPU_MEM_RST, IPU_MEM_RST_ALL);
+ i = 1000;
+ while (i-- > 0) {
+ if (!(IPU_READ4(sc, IPU_MEM_RST) & IPU_MEM_RST_START))
+ break;
+ DELAY(1);
+ }
+
+ if (i <= 0) {
+ err = ETIMEDOUT;
+ device_printf(sc->sc_dev, "timeout while resetting memory\n");
+ goto fail;
+ }
+
+ ipu_dc_reset_map(sc, 0);
+ ipu_dc_setup_map(sc, 0, 0, 7, 0xff);
+ ipu_dc_setup_map(sc, 0, 1, 15, 0xff);
+ ipu_dc_setup_map(sc, 0, 2, 23, 0xff);
+
+ dma_size = round_page(sc->sc_mode->hdisplay * sc->sc_mode->vdisplay * (MODE_BPP / 8));
+
+ /*
+ * Now allocate framebuffer memory
+ */
+ err = bus_dma_tag_create(
+ bus_get_dma_tag(sc->sc_dev),
+ 4, 0, /* alignment, boundary */
+ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ dma_size, 1, /* maxsize, nsegments */
+ dma_size, 0, /* maxsegsize, flags */
+ NULL, NULL, /* lockfunc, lockarg */
+ &sc->sc_dma_tag);
+ if (err)
+ goto fail;
+
+ err = bus_dmamem_alloc(sc->sc_dma_tag, (void **)&sc->sc_fb_base,
+ BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->sc_dma_map);
+
+ if (err) {
+ device_printf(sc->sc_dev, "cannot allocate framebuffer\n");
+ goto fail;
+ }
+
+ err = bus_dmamap_load(sc->sc_dma_tag, sc->sc_dma_map, sc->sc_fb_base,
+ dma_size, ipu_dmamap_cb, &sc->sc_fb_phys, BUS_DMA_NOWAIT);
+
+ if (err) {
+ device_printf(sc->sc_dev, "cannot load DMA map\n");
+ goto fail;
+ }
+
+ /* Calculate actual FB Size */
+ sc->sc_fb_size = sc->sc_mode->hdisplay * sc->sc_mode->vdisplay * MODE_BPP / 8;
+
+ ipu_dc_init(sc, DI_PORT);
+ reg = IPU_READ4(sc, IPU_CONF);
+ reg |= IPU_CONF_DMFC_EN | IPU_CONF_DC_EN | IPU_CONF_DP_EN;
+ IPU_WRITE4(sc, IPU_CONF, reg);
+
+ ipu_config_timing(sc, DI_PORT);
+ ipu_init_buffer(sc);
+ ipu_di_enable(sc, DI_PORT);
+
+ /* Enable DMA channel */
+ off = (DMA_CHANNEL > 31) ? IPU_IDMAC_CH_EN_2 : IPU_IDMAC_CH_EN_1;
+ reg = IPU_READ4(sc, off);
+ reg |= (1 << (DMA_CHANNEL & 0x1f));
+ IPU_WRITE4(sc, off, reg);
+
+ ipu_dc_enable(sc);
+
+ sc->sc_fb_info.fb_name = device_get_nameunit(sc->sc_dev);
+ sc->sc_fb_info.fb_vbase = (intptr_t)sc->sc_fb_base;
+ sc->sc_fb_info.fb_pbase = sc->sc_fb_phys;
+ sc->sc_fb_info.fb_size = sc->sc_fb_size;
+ sc->sc_fb_info.fb_bpp = sc->sc_fb_info.fb_depth = MODE_BPP;
+ sc->sc_fb_info.fb_stride = sc->sc_mode->hdisplay * MODE_BPP / 8;
+ sc->sc_fb_info.fb_width = sc->sc_mode->hdisplay;
+ sc->sc_fb_info.fb_height = sc->sc_mode->vdisplay;
+
+ device_t fbd = device_add_child(sc->sc_dev, "fbd",
+ device_get_unit(sc->sc_dev));
+ if (fbd == NULL) {
+ device_printf(sc->sc_dev, "Failed to add fbd child\n");
+ goto fail;
+ }
+ if (device_probe_and_attach(fbd) != 0) {
+ device_printf(sc->sc_dev, "Failed to attach fbd device\n");
+ goto fail;
+ }
+
+ return (0);
+fail:
+
+ return (err);
+}
+
+static int
+ipu_mode_is_valid(const struct videomode *mode)
+{
+ if ((mode->dot_clock < 13500) || (mode->dot_clock > 216000))
+ return (0);
+
+ return (1);
+}
+
+static const struct videomode *
+ipu_pick_mode(struct edid_info *ei)
+{
+ const struct videomode *videomode;
+ const struct videomode *m;
+ int n;
+
+ videomode = NULL;
+
+ /*
+ * Pick a mode.
+ */
+ if (ei->edid_preferred_mode != NULL) {
+ if (ipu_mode_is_valid(ei->edid_preferred_mode))
+ videomode = ei->edid_preferred_mode;
+ }
+
+ if (videomode == NULL) {
+ m = ei->edid_modes;
+
+ sort_modes(ei->edid_modes,
+ &ei->edid_preferred_mode,
+ ei->edid_nmodes);
+ for (n = 0; n < ei->edid_nmodes; n++)
+ if (ipu_mode_is_valid(&m[n])) {
+ videomode = &m[n];
+ break;
+ }
+ }
+
+ return videomode;
+}
+
+static void
+ipu_hdmi_event(void *arg, device_t hdmi_dev)
+{
+ struct ipu_softc *sc;
+ uint8_t *edid;
+ uint32_t edid_len;
+ struct edid_info ei;
+ const struct videomode *videomode;
+
+ sc = arg;
+
+ edid = NULL;
+ edid_len = 0;
+ if (HDMI_GET_EDID(hdmi_dev, &edid, &edid_len) != 0) {
+ device_printf(sc->sc_dev, "failed to get EDID info from HDMI framer\n");
+ }
+
+ videomode = NULL;
+
+ if ( edid && (edid_parse(edid, &ei) == 0)) {
+ if (bootverbose)
+ edid_print(&ei);
+ videomode = ipu_pick_mode(&ei);
+ } else
+ device_printf(sc->sc_dev, "failed to parse EDID\n");
+
+ /* Use standard VGA as fallback */
+ if (videomode == NULL)
+ videomode = pick_mode_by_ref(640, 480, 60);
+
+ if (videomode == NULL) {
+ device_printf(sc->sc_dev, "failed to find usable videomode\n");
+ return;
+ }
+
+ sc->sc_mode = videomode;
+
+ if (bootverbose)
+ device_printf(sc->sc_dev, "detected videomode: %dx%d\n",
+ videomode->hdisplay, videomode->vdisplay);
+
+ ipu_init(sc);
+
+ HDMI_SET_VIDEOMODE(hdmi_dev, sc->sc_mode);
+}
+
+static int
+ipu_probe(device_t dev)
+{
+
+ if (have_ipu)
+ return (ENXIO);
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "fsl,imx6q-ipu"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Freescale IPU");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ipu_attach(device_t dev)
+{
+ struct ipu_softc *sc;
+
+ if (have_ipu)
+ return (ENXIO);
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+
+ sc->sc_mem_rid = 0;
+ sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &sc->sc_mem_rid, RF_ACTIVE);
+ if (!sc->sc_mem_res) {
+ device_printf(dev, "cannot allocate memory window\n");
+ return (ENXIO);
+ }
+
+ sc->sc_irq_rid = 0;
+ sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
+ &sc->sc_irq_rid, RF_ACTIVE);
+ if (!sc->sc_irq_res) {
+ bus_release_resource(dev, SYS_RES_MEMORY,
+ sc->sc_mem_rid, sc->sc_mem_res);
+ device_printf(dev, "cannot allocate interrupt\n");
+ return (ENXIO);
+ }
+
+ /* Enable IPU1 */
+ if (imx_ccm_pll_video_enable() != 0) {
+ bus_release_resource(dev, SYS_RES_MEMORY,
+ sc->sc_mem_rid, sc->sc_mem_res);
+ bus_release_resource(dev, SYS_RES_IRQ,
+ sc->sc_irq_rid, sc->sc_irq_res);
+ device_printf(dev, "failed to set up video PLL\n");
+ return (ENXIO);
+ }
+
+ imx_ccm_ipu_enable(1);
+
+ if (src_reset_ipu() != 0) {
+ bus_release_resource(dev, SYS_RES_MEMORY,
+ sc->sc_mem_rid, sc->sc_mem_res);
+ bus_release_resource(dev, SYS_RES_IRQ,
+ sc->sc_irq_rid, sc->sc_irq_res);
+ device_printf(dev, "failed to reset IPU\n");
+ return (ENXIO);
+ }
+
+ IPU_LOCK_INIT(sc);
+
+ sc->sc_hdmi_evh = EVENTHANDLER_REGISTER(hdmi_event,
+ ipu_hdmi_event, sc, 0);
+
+ have_ipu = 1;
+
+ return (0);
+}
+
+static int
+ipu_detach(device_t dev)
+{
+ /* Do not let unload driver */
+ return (EBUSY);
+}
+
+static struct fb_info *
+ipu_fb_getinfo(device_t dev)
+{
+ struct ipu_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ return (&sc->sc_fb_info);
+}
+
+static device_method_t ipu_methods[] = {
+ DEVMETHOD(device_probe, ipu_probe),
+ DEVMETHOD(device_attach, ipu_attach),
+ DEVMETHOD(device_detach, ipu_detach),
+
+ /* Framebuffer service methods */
+ DEVMETHOD(fb_getinfo, ipu_fb_getinfo),
+
+ DEVMETHOD_END
+};
+
+static driver_t ipu_driver = {
+ "fb",
+ ipu_methods,
+ sizeof(struct ipu_softc),
+};
+
+static devclass_t ipu_devclass;
+
+DRIVER_MODULE(ipu, simplebus, ipu_driver, ipu_devclass, 0, 0);
+MODULE_VERSION(ipu, 1);
+MODULE_DEPEND(ipu, simplebus, 1, 1, 1);
diff --git a/sys/arm/freescale/imx/imx6_machdep.c b/sys/arm/freescale/imx/imx6_machdep.c
new file mode 100644
index 000000000000..d08baa8af188
--- /dev/null
+++ b/sys/arm/freescale/imx/imx6_machdep.c
@@ -0,0 +1,396 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Ian Lepore <ian@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "opt_platform.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/reboot.h>
+#include <sys/devmap.h>
+
+#include <vm/vm.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+#include <machine/machdep.h>
+#include <machine/platformvar.h>
+
+#include <arm/arm/mpcore_timervar.h>
+#include <arm/freescale/imx/imx6_anatopreg.h>
+#include <arm/freescale/imx/imx6_anatopvar.h>
+#include <arm/freescale/imx/imx_machdep.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/openfirm.h>
+
+#include <arm/freescale/imx/imx6_machdep.h>
+
+#include "platform_if.h"
+#include "platform_pl310_if.h"
+
+static platform_attach_t imx6_attach;
+static platform_devmap_init_t imx6_devmap_init;
+static platform_late_init_t imx6_late_init;
+static platform_cpu_reset_t imx6_cpu_reset;
+
+/*
+ * Fix FDT data related to interrupts.
+ *
+ * Driven by the needs of linux and its drivers (as always), the published FDT
+ * data for imx6 now sets the interrupt parent for most devices to the GPC
+ * interrupt controller, which is for use when the chip is in deep-sleep mode.
+ * We don't support deep sleep or have a GPC-PIC driver; we need all interrupts
+ * to be handled by the GIC.
+ *
+ * Luckily, the change to the FDT data was to assign the GPC as the interrupt
+ * parent for the soc node and letting that get inherited by all other devices
+ * (except a few that directly name GIC as their interrupt parent). So we can
+ * set the world right by just changing the interrupt-parent property of the soc
+ * node to refer to GIC instead of GPC. This will get us by until we write our
+ * own GPC driver (or until linux changes its mind and the FDT data again).
+ *
+ * 2020/11/25: The tempmon and pmu nodes are siblings (not children) of the soc
+ * node, so for them to use interrupts we need to apply the same fix as we do
+ * for the soc node.
+ *
+ * We validate that we have data that looks like we expect before changing it:
+ * - SOC node exists and has GPC as its interrupt parent.
+ * - GPC node exists and has GIC as its interrupt parent.
+ * - GIC node exists and is its own interrupt parent or has no parent.
+ *
+ * This applies to all models of imx6. Luckily all of them have the devices
+ * involved at the same addresses on the same buses, so we don't need any
+ * per-soc logic. We handle this at platform attach time rather than via the
+ * fdt_fixup_table, because the latter requires matching on the FDT "model"
+ * property, and this applies to all boards including those not yet invented.
+ *
+ * This just in: as of the import of dts files from linux 4.15 on 2018-02-10,
+ * they appear to have applied a new style rule to the dts which forbids leading
+ * zeroes in the @address qualifiers on node names. Since we have to find those
+ * nodes by string matching we now have to search for both flavors of each node
+ * name involved.
+ */
+
+static void
+fix_node_iparent(const char* nodepath, phandle_t gpcxref, phandle_t gicxref)
+{
+ static const char *propname = "interrupt-parent";
+ phandle_t node, iparent;
+
+ if ((node = OF_finddevice(nodepath)) == -1)
+ return;
+ if (OF_getencprop(node, propname, &iparent, sizeof(iparent)) <= 0)
+ return;
+ if (iparent != gpcxref)
+ return;
+
+ OF_setprop(node, propname, &gicxref, sizeof(gicxref));
+}
+
+static void
+fix_fdt_interrupt_data(void)
+{
+ phandle_t gicipar, gicnode, gicxref;
+ phandle_t gpcipar, gpcnode, gpcxref;
+ int result;
+
+ /* GIC node may be child of soc node, or appear directly at root. */
+ gicnode = OF_finddevice("/soc/interrupt-controller@00a01000");
+ if (gicnode == -1)
+ gicnode = OF_finddevice("/soc/interrupt-controller@a01000");
+ if (gicnode == -1) {
+ gicnode = OF_finddevice("/interrupt-controller@00a01000");
+ if (gicnode == -1)
+ gicnode = OF_finddevice("/interrupt-controller@a01000");
+ if (gicnode == -1)
+ return;
+ }
+ gicxref = OF_xref_from_node(gicnode);
+
+ /* If gic node has no parent, pretend it is its own parent. */
+ result = OF_getencprop(gicnode, "interrupt-parent", &gicipar,
+ sizeof(gicipar));
+ if (result <= 0)
+ gicipar = gicxref;
+
+ gpcnode = OF_finddevice("/soc/aips-bus@02000000/gpc@020dc000");
+ if (gpcnode == -1)
+ gpcnode = OF_finddevice("/soc/aips-bus@2000000/gpc@20dc000");
+ if (gpcnode == -1)
+ gpcnode = OF_finddevice("/soc/bus@2000000/gpc@20dc000");
+ if (gpcnode == -1)
+ return;
+ result = OF_getencprop(gpcnode, "interrupt-parent", &gpcipar,
+ sizeof(gpcipar));
+ if (result <= 0)
+ return;
+ gpcxref = OF_xref_from_node(gpcnode);
+
+ if (gpcipar != gicxref || gicipar != gicxref)
+ return;
+
+ gicxref = cpu_to_fdt32(gicxref);
+ fix_node_iparent("/soc", gpcxref, gicxref);
+ fix_node_iparent("/pmu", gpcxref, gicxref);
+ fix_node_iparent("/tempmon", gpcxref, gicxref);
+}
+
+static void
+fix_fdt_iomuxc_data(void)
+{
+ phandle_t node;
+
+ /*
+ * The linux dts defines two nodes with the same mmio address range,
+ * iomuxc-gpr and the regular iomuxc. The -grp node is a simple_mfd and
+ * a syscon, but it only has access to a small subset of the iomuxc
+ * registers, so it can't serve as the accessor for the iomuxc driver's
+ * register IO. But right now, the simple_mfd driver attaches first,
+ * preventing the real iomuxc driver from allocating its mmio register
+ * range because it partially overlaps with the -gpr range.
+ *
+ * For now, by far the easiest thing to do to keep imx6 working is to
+ * just disable the iomuxc-gpr node because we don't have a driver for
+ * it anyway, we just need to prevent attachment of simple_mfd.
+ *
+ * If we ever write a -gpr driver, this code should probably switch to
+ * modifying the reg property so that the range covers all the iomuxc
+ * regs, then the -gpr driver can be a regular syscon driver that iomuxc
+ * uses for register access.
+ */
+ node = OF_finddevice("/soc/aips-bus@2000000/iomuxc-gpr@20e0000");
+ if (node == -1)
+ node = OF_finddevice("/soc/bus@2000000/iomuxc-gpr@20e0000");
+ if (node != -1)
+ OF_setprop(node, "status", "disabled", sizeof("disabled"));
+}
+
+static int
+imx6_attach(platform_t plat)
+{
+
+ /* Fix soc interrupt-parent property. */
+ fix_fdt_interrupt_data();
+
+ /* Fix iomuxc-gpr and iomuxc nodes both using the same mmio range. */
+ fix_fdt_iomuxc_data();
+
+ /* Inform the MPCore timer driver that its clock is variable. */
+ arm_tmr_change_frequency(ARM_TMR_FREQUENCY_VARIES);
+
+ return (0);
+}
+
+static void
+imx6_late_init(platform_t plat)
+{
+ const uint32_t IMX6_WDOG_SR_PHYS = 0x020bc004;
+
+ imx_wdog_init_last_reset(IMX6_WDOG_SR_PHYS);
+}
+
+/*
+ * Set up static device mappings.
+ *
+ * This attempts to cover the most-used devices with 1MB section mappings, which
+ * is good for performance (uses fewer TLB entries for device access).
+ *
+ * ARMMP covers the interrupt controller, MPCore timers, global timer, and the
+ * L2 cache controller. Most of the 1MB range is unused reserved space.
+ *
+ * AIPS1/AIPS2 cover most of the on-chip devices such as uart, spi, i2c, etc.
+ *
+ * Notably not mapped right now are HDMI, GPU, and other devices below ARMMP in
+ * the memory map. When we get support for graphics it might make sense to
+ * static map some of that area. Be careful with other things in that area such
+ * as OCRAM that probably shouldn't be mapped as VM_MEMATTR_DEVICE memory.
+ */
+static int
+imx6_devmap_init(platform_t plat)
+{
+ const uint32_t IMX6_ARMMP_PHYS = 0x00a00000;
+ const uint32_t IMX6_ARMMP_SIZE = 0x00100000;
+ const uint32_t IMX6_AIPS1_PHYS = 0x02000000;
+ const uint32_t IMX6_AIPS1_SIZE = 0x00100000;
+ const uint32_t IMX6_AIPS2_PHYS = 0x02100000;
+ const uint32_t IMX6_AIPS2_SIZE = 0x00100000;
+
+ devmap_add_entry(IMX6_ARMMP_PHYS, IMX6_ARMMP_SIZE);
+ devmap_add_entry(IMX6_AIPS1_PHYS, IMX6_AIPS1_SIZE);
+ devmap_add_entry(IMX6_AIPS2_PHYS, IMX6_AIPS2_SIZE);
+
+ return (0);
+}
+
+static void
+imx6_cpu_reset(platform_t plat)
+{
+ const uint32_t IMX6_WDOG_CR_PHYS = 0x020bc000;
+
+ imx_wdog_cpu_reset(IMX6_WDOG_CR_PHYS);
+}
+
+/*
+ * Determine what flavor of imx6 we're running on.
+ *
+ * This code is based on the way u-boot does it. Information found on the web
+ * indicates that Freescale themselves were the original source of this logic,
+ * including the strange check for number of CPUs in the SCU configuration
+ * register, which is apparently needed on some revisions of the SOLO.
+ *
+ * According to the documentation, there is such a thing as an i.MX6 Dual
+ * (non-lite flavor). However, Freescale doesn't seem to have assigned it a
+ * number or provided any logic to handle it in their detection code.
+ *
+ * Note that the ANALOG_DIGPROG and SCU configuration registers are not
+ * documented in the chip reference manual. (SCU configuration is mentioned,
+ * but not mapped out in detail.) I think the bottom two bits of the scu config
+ * register may be ncpu-1.
+ *
+ * This hasn't been tested yet on a dual[-lite].
+ *
+ * On a solo:
+ * digprog = 0x00610001
+ * hwsoc = 0x00000062
+ * scu config = 0x00000500
+ * On a quad:
+ * digprog = 0x00630002
+ * hwsoc = 0x00000063
+ * scu config = 0x00005503
+ */
+u_int
+imx_soc_type(void)
+{
+ uint32_t digprog, hwsoc;
+ uint32_t *pcr;
+ static u_int soctype;
+ const vm_offset_t SCU_CONFIG_PHYSADDR = 0x00a00004;
+#define HWSOC_MX6SL 0x60
+#define HWSOC_MX6DL 0x61
+#define HWSOC_MX6SOLO 0x62
+#define HWSOC_MX6Q 0x63
+#define HWSOC_MX6UL 0x64
+
+ if (soctype != 0)
+ return (soctype);
+
+ digprog = imx6_anatop_read_4(IMX6_ANALOG_DIGPROG_SL);
+ hwsoc = (digprog >> IMX6_ANALOG_DIGPROG_SOCTYPE_SHIFT) &
+ IMX6_ANALOG_DIGPROG_SOCTYPE_MASK;
+
+ if (hwsoc != HWSOC_MX6SL) {
+ digprog = imx6_anatop_read_4(IMX6_ANALOG_DIGPROG);
+ hwsoc = (digprog & IMX6_ANALOG_DIGPROG_SOCTYPE_MASK) >>
+ IMX6_ANALOG_DIGPROG_SOCTYPE_SHIFT;
+ /*printf("digprog = 0x%08x\n", digprog);*/
+ if (hwsoc == HWSOC_MX6DL) {
+ pcr = devmap_ptov(SCU_CONFIG_PHYSADDR, 4);
+ if (pcr != NULL) {
+ /*printf("scu config = 0x%08x\n", *pcr);*/
+ if ((*pcr & 0x03) == 0) {
+ hwsoc = HWSOC_MX6SOLO;
+ }
+ }
+ }
+ }
+ /* printf("hwsoc 0x%08x\n", hwsoc); */
+
+ switch (hwsoc) {
+ case HWSOC_MX6SL:
+ soctype = IMXSOC_6SL;
+ break;
+ case HWSOC_MX6SOLO:
+ soctype = IMXSOC_6S;
+ break;
+ case HWSOC_MX6DL:
+ soctype = IMXSOC_6DL;
+ break;
+ case HWSOC_MX6Q :
+ soctype = IMXSOC_6Q;
+ break;
+ case HWSOC_MX6UL:
+ soctype = IMXSOC_6UL;
+ break;
+ default:
+ printf("imx_soc_type: Don't understand hwsoc 0x%02x, "
+ "digprog 0x%08x; assuming IMXSOC_6Q\n", hwsoc, digprog);
+ soctype = IMXSOC_6Q;
+ break;
+ }
+
+ return (soctype);
+}
+
+/*
+ * Early putc routine for EARLY_PRINTF support. To use, add to kernel config:
+ * option SOCDEV_PA=0x02000000
+ * option SOCDEV_VA=0x02000000
+ * option EARLY_PRINTF
+ * Resist the temptation to change the #if 0 to #ifdef EARLY_PRINTF here. It
+ * makes sense now, but if multiple SOCs do that it will make early_putc another
+ * duplicate symbol to be eliminated on the path to a generic kernel.
+ */
+#if 0
+static void
+imx6_early_putc(int c)
+{
+ volatile uint32_t * UART_STAT_REG = (uint32_t *)0x02020098;
+ volatile uint32_t * UART_TX_REG = (uint32_t *)0x02020040;
+ const uint32_t UART_TXRDY = (1 << 3);
+
+ while ((*UART_STAT_REG & UART_TXRDY) == 0)
+ continue;
+ *UART_TX_REG = c;
+}
+early_putc_t *early_putc = imx6_early_putc;
+#endif
+
+static platform_method_t imx6_methods[] = {
+ PLATFORMMETHOD(platform_attach, imx6_attach),
+ PLATFORMMETHOD(platform_devmap_init, imx6_devmap_init),
+ PLATFORMMETHOD(platform_late_init, imx6_late_init),
+ PLATFORMMETHOD(platform_cpu_reset, imx6_cpu_reset),
+
+#ifdef SMP
+ PLATFORMMETHOD(platform_mp_start_ap, imx6_mp_start_ap),
+ PLATFORMMETHOD(platform_mp_setmaxid, imx6_mp_setmaxid),
+#endif
+
+ PLATFORMMETHOD(platform_pl310_init, imx6_pl310_init),
+
+ PLATFORMMETHOD_END,
+};
+
+FDT_PLATFORM_DEF2(imx6, imx6s, "i.MX6 Solo", 0, "fsl,imx6s", 80);
+FDT_PLATFORM_DEF2(imx6, imx6d, "i.MX6 Dual", 0, "fsl,imx6dl", 80);
+FDT_PLATFORM_DEF2(imx6, imx6q, "i.MX6 Quad", 0, "fsl,imx6q", 80);
+FDT_PLATFORM_DEF2(imx6, imx6ul, "i.MX6 UltraLite", 0, "fsl,imx6ul", 67);
diff --git a/sys/arm/freescale/imx/imx6_machdep.h b/sys/arm/freescale/imx/imx6_machdep.h
new file mode 100644
index 000000000000..4db1bb354530
--- /dev/null
+++ b/sys/arm/freescale/imx/imx6_machdep.h
@@ -0,0 +1,38 @@
+/*-
+ * Copyright (c) 2017 Andrew Turner
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef IMX6_MACHDEP_H
+#define IMX6_MACHDEP_H
+
+struct pl310_softc;
+
+void imx6_mp_start_ap(platform_t);
+void imx6_mp_setmaxid(platform_t);
+void imx6_pl310_init(platform_t, struct pl310_softc *);
+
+#endif /* IMX6_MACHDEP_H */
diff --git a/sys/arm/freescale/imx/imx6_mp.c b/sys/arm/freescale/imx/imx6_mp.c
new file mode 100644
index 000000000000..ea7c8cc3a293
--- /dev/null
+++ b/sys/arm/freescale/imx/imx6_mp.c
@@ -0,0 +1,163 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2014 Juergen Weiss <weiss@uni-mainz.de>
+ * Copyright (c) 2014 Ian Lepore <ian@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/smp.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/cpu.h>
+#include <machine/smp.h>
+#include <machine/fdt.h>
+#include <machine/intr.h>
+#include <machine/platform.h>
+#include <machine/platformvar.h>
+
+#include <arm/freescale/imx/imx6_machdep.h>
+
+#define SCU_PHYSBASE 0x00a00000
+#define SCU_SIZE 0x00001000
+
+#define SCU_CONTROL_REG 0x00
+#define SCU_CONTROL_ENABLE (1 << 0)
+#define SCU_CONFIG_REG 0x04
+#define SCU_CONFIG_REG_NCPU_MASK 0x03
+#define SCU_CPUPOWER_REG 0x08
+#define SCU_INV_TAGS_REG 0x0c
+#define SCU_DIAG_CONTROL 0x30
+#define SCU_DIAG_DISABLE_MIGBIT (1 << 0)
+#define SCU_FILTER_START_REG 0x40
+#define SCU_FILTER_END_REG 0x44
+#define SCU_SECURE_ACCESS_REG 0x50
+#define SCU_NONSECURE_ACCESS_REG 0x54
+
+#define SRC_PHYSBASE 0x020d8000
+#define SRC_SIZE 0x4000
+#define SRC_CONTROL_REG 0x00
+#define SRC_CONTROL_C1ENA_SHIFT 22 /* Bit for Core 1 enable */
+#define SRC_CONTROL_C1RST_SHIFT 14 /* Bit for Core 1 reset */
+#define SRC_GPR0_C1FUNC 0x20 /* Register for Core 1 entry func */
+#define SRC_GPR1_C1ARG 0x24 /* Register for Core 1 entry arg */
+
+void
+imx6_mp_setmaxid(platform_t plat)
+{
+ bus_space_handle_t scu;
+ int hwcpu, ncpu;
+ uint32_t val;
+
+ /* If we've already set the global vars don't bother to do it again. */
+ if (mp_ncpus != 0)
+ return;
+
+ if (bus_space_map(fdtbus_bs_tag, SCU_PHYSBASE, SCU_SIZE, 0, &scu) != 0)
+ panic("Couldn't map the SCU\n");
+ val = bus_space_read_4(fdtbus_bs_tag, scu, SCU_CONFIG_REG);
+ hwcpu = (val & SCU_CONFIG_REG_NCPU_MASK) + 1;
+ bus_space_unmap(fdtbus_bs_tag, scu, SCU_SIZE);
+
+ ncpu = hwcpu;
+ TUNABLE_INT_FETCH("hw.ncpu", &ncpu);
+ if (ncpu < 1 || ncpu > hwcpu)
+ ncpu = hwcpu;
+
+ mp_ncpus = ncpu;
+ mp_maxid = ncpu - 1;
+}
+
+void
+imx6_mp_start_ap(platform_t plat)
+{
+ bus_space_handle_t scu;
+ bus_space_handle_t src;
+
+ uint32_t val;
+ int i;
+
+ if (bus_space_map(fdtbus_bs_tag, SCU_PHYSBASE, SCU_SIZE, 0, &scu) != 0)
+ panic("Couldn't map the SCU\n");
+ if (bus_space_map(fdtbus_bs_tag, SRC_PHYSBASE, SRC_SIZE, 0, &src) != 0)
+ panic("Couldn't map the system reset controller (SRC)\n");
+
+ /*
+ * Invalidate SCU cache tags. The 0x0000ffff constant invalidates all
+ * ways on all cores 0-3. Per the ARM docs, it's harmless to write to
+ * the bits for cores that are not present.
+ */
+ bus_space_write_4(fdtbus_bs_tag, scu, SCU_INV_TAGS_REG, 0x0000ffff);
+
+ /*
+ * Erratum ARM/MP: 764369 (problems with cache maintenance).
+ * Setting the "disable-migratory bit" in the undocumented SCU
+ * Diagnostic Control Register helps work around the problem.
+ */
+ val = bus_space_read_4(fdtbus_bs_tag, scu, SCU_DIAG_CONTROL);
+ bus_space_write_4(fdtbus_bs_tag, scu, SCU_DIAG_CONTROL,
+ val | SCU_DIAG_DISABLE_MIGBIT);
+
+ /*
+ * Enable the SCU, then clean the cache on this core. After these two
+ * operations the cache tag ram in the SCU is coherent with the contents
+ * of the cache on this core. The other cores aren't running yet so
+ * their caches can't contain valid data yet, but we've initialized
+ * their SCU tag ram above, so they will be coherent from startup.
+ */
+ val = bus_space_read_4(fdtbus_bs_tag, scu, SCU_CONTROL_REG);
+ bus_space_write_4(fdtbus_bs_tag, scu, SCU_CONTROL_REG,
+ val | SCU_CONTROL_ENABLE);
+ dcache_wbinv_poc_all();
+
+ /*
+ * For each AP core, set the entry point address and argument registers,
+ * and set the core-enable and core-reset bits in the control register.
+ */
+ val = bus_space_read_4(fdtbus_bs_tag, src, SRC_CONTROL_REG);
+ for (i=1; i < mp_ncpus; i++) {
+ bus_space_write_4(fdtbus_bs_tag, src, SRC_GPR0_C1FUNC + 8*i,
+ pmap_kextract((vm_offset_t)mpentry));
+ bus_space_write_4(fdtbus_bs_tag, src, SRC_GPR1_C1ARG + 8*i, 0);
+
+ val |= ((1 << (SRC_CONTROL_C1ENA_SHIFT - 1 + i )) |
+ ( 1 << (SRC_CONTROL_C1RST_SHIFT - 1 + i)));
+ }
+ bus_space_write_4(fdtbus_bs_tag, src, SRC_CONTROL_REG, val);
+
+ dsb();
+ sev();
+
+ bus_space_unmap(fdtbus_bs_tag, scu, SCU_SIZE);
+ bus_space_unmap(fdtbus_bs_tag, src, SRC_SIZE);
+}
diff --git a/sys/arm/freescale/imx/imx6_pl310.c b/sys/arm/freescale/imx/imx6_pl310.c
new file mode 100644
index 000000000000..8bf832e916f0
--- /dev/null
+++ b/sys/arm/freescale/imx/imx6_pl310.c
@@ -0,0 +1,67 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Olivier Houchard.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * The machine-dependent part of the arm/pl310 driver for imx6 SoCs.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <machine/bus.h>
+#include <machine/pl310.h>
+#include <machine/platformvar.h>
+
+#include <arm/freescale/imx/imx6_machdep.h>
+
+#include "platform_pl310_if.h"
+
+void
+imx6_pl310_init(platform_t plat, struct pl310_softc *sc)
+{
+ uint32_t reg;
+
+ /*
+ * Enable power saving modes:
+ * - Dynamic Gating stops the clock when the controller is idle.
+ * - Standby stops the clock when the cores are in WFI mode.
+ */
+ reg = pl310_read4(sc, PL310_POWER_CTRL);
+ reg |= POWER_CTRL_ENABLE_GATING | POWER_CTRL_ENABLE_STANDBY;
+ pl310_write4(sc, PL310_POWER_CTRL, reg);
+
+ pl310_set_ram_latency(sc, PL310_TAG_RAM_CTRL, 4, 2, 3);
+ pl310_set_ram_latency(sc, PL310_DATA_RAM_CTRL, 4, 2, 3);
+}
diff --git a/sys/arm/freescale/imx/imx6_sdma.c b/sys/arm/freescale/imx/imx6_sdma.c
new file mode 100644
index 000000000000..88e07fa3418e
--- /dev/null
+++ b/sys/arm/freescale/imx/imx6_sdma.c
@@ -0,0 +1,524 @@
+/*-
+ * Copyright (c) 2015 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * i.MX6 Smart Direct Memory Access Controller (sDMA)
+ * Chapter 41, i.MX 6Dual/6Quad Applications Processor Reference Manual,
+ * Rev. 1, 04/2013
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/endian.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/firmware.h>
+
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_kern.h>
+#include <vm/pmap.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <arm/freescale/imx/imx6_sdma.h>
+
+#define MAX_BD (PAGE_SIZE / sizeof(struct sdma_buffer_descriptor))
+
+#define READ4(_sc, _reg) \
+ bus_space_read_4(_sc->bst, _sc->bsh, _reg)
+#define WRITE4(_sc, _reg, _val) \
+ bus_space_write_4(_sc->bst, _sc->bsh, _reg, _val)
+
+struct sdma_softc *sdma_sc;
+
+static struct resource_spec sdma_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+/*
+ * This will get set to true if we can't load firmware while attaching, to
+ * prevent multiple attempts to re-attach the device on each bus pass.
+ */
+static bool firmware_unavailable;
+
+static void
+sdma_intr(void *arg)
+{
+ struct sdma_buffer_descriptor *bd;
+ struct sdma_channel *channel;
+ struct sdma_conf *conf;
+ struct sdma_softc *sc;
+ int pending;
+ int i;
+ int j;
+
+ sc = arg;
+
+ pending = READ4(sc, SDMAARM_INTR);
+
+ /* Ack intr */
+ WRITE4(sc, SDMAARM_INTR, pending);
+
+ for (i = 0; i < SDMA_N_CHANNELS; i++) {
+ if ((pending & (1 << i)) == 0)
+ continue;
+ channel = &sc->channel[i];
+ conf = channel->conf;
+ if (!conf)
+ continue;
+ for (j = 0; j < conf->num_bd; j++) {
+ bd = &channel->bd[j];
+ bd->mode.status |= BD_DONE;
+ if (bd->mode.status & BD_RROR)
+ printf("sDMA error\n");
+ }
+
+ conf->ih(conf->ih_user, 1);
+
+ WRITE4(sc, SDMAARM_HSTART, (1 << i));
+ }
+}
+
+static int
+sdma_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev) || firmware_unavailable)
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "fsl,imx6q-sdma"))
+ return (ENXIO);
+
+ device_set_desc(dev, "i.MX6 Smart Direct Memory Access Controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+int
+sdma_start(int chn)
+{
+ struct sdma_softc *sc;
+
+ sc = sdma_sc;
+
+ WRITE4(sc, SDMAARM_HSTART, (1 << chn));
+
+ return (0);
+}
+
+int
+sdma_stop(int chn)
+{
+ struct sdma_softc *sc;
+
+ sc = sdma_sc;
+
+ WRITE4(sc, SDMAARM_STOP_STAT, (1 << chn));
+
+ return (0);
+}
+
+int
+sdma_alloc(void)
+{
+ struct sdma_channel *channel;
+ struct sdma_softc *sc;
+ int found;
+ int chn;
+ int i;
+
+ sc = sdma_sc;
+ found = 0;
+
+ /* Channel 0 can't be used */
+ for (i = 1; i < SDMA_N_CHANNELS; i++) {
+ channel = &sc->channel[i];
+ if (channel->in_use == 0) {
+ channel->in_use = 1;
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ return (-1);
+
+ chn = i;
+
+ /* Allocate area for buffer descriptors */
+ channel->bd = (void *)kmem_alloc_contig(PAGE_SIZE, M_ZERO, 0, ~0,
+ PAGE_SIZE, 0, VM_MEMATTR_UNCACHEABLE);
+
+ return (chn);
+}
+
+int
+sdma_free(int chn)
+{
+ struct sdma_channel *channel;
+ struct sdma_softc *sc;
+
+ sc = sdma_sc;
+
+ channel = &sc->channel[chn];
+ channel->in_use = 0;
+
+ kmem_free((vm_offset_t)channel->bd, PAGE_SIZE);
+
+ return (0);
+}
+
+static int
+sdma_overrides(struct sdma_softc *sc, int chn,
+ int evt, int host, int dsp)
+{
+ int reg;
+
+ /* Ignore sDMA requests */
+ reg = READ4(sc, SDMAARM_EVTOVR);
+ if (evt)
+ reg |= (1 << chn);
+ else
+ reg &= ~(1 << chn);
+ WRITE4(sc, SDMAARM_EVTOVR, reg);
+
+ /* Ignore enable bit (HE) */
+ reg = READ4(sc, SDMAARM_HOSTOVR);
+ if (host)
+ reg |= (1 << chn);
+ else
+ reg &= ~(1 << chn);
+ WRITE4(sc, SDMAARM_HOSTOVR, reg);
+
+ /* Prevent sDMA channel from starting */
+ reg = READ4(sc, SDMAARM_DSPOVR);
+ if (!dsp)
+ reg |= (1 << chn);
+ else
+ reg &= ~(1 << chn);
+ WRITE4(sc, SDMAARM_DSPOVR, reg);
+
+ return (0);
+}
+
+int
+sdma_configure(int chn, struct sdma_conf *conf)
+{
+ struct sdma_buffer_descriptor *bd0;
+ struct sdma_buffer_descriptor *bd;
+ struct sdma_context_data *context;
+ struct sdma_channel *channel;
+ struct sdma_softc *sc;
+#if 0
+ int timeout;
+ int ret;
+#endif
+ int i;
+
+ sc = sdma_sc;
+
+ channel = &sc->channel[chn];
+ channel->conf = conf;
+
+ /* Ensure operation has stopped */
+ sdma_stop(chn);
+
+ /* Set priority and enable the channel */
+ WRITE4(sc, SDMAARM_SDMA_CHNPRI(chn), 1);
+ WRITE4(sc, SDMAARM_CHNENBL(conf->event), (1 << chn));
+
+ sdma_overrides(sc, chn, 0, 0, 0);
+
+ if (conf->num_bd > MAX_BD) {
+ device_printf(sc->dev, "Error: too much buffer"
+ " descriptors requested\n");
+ return (-1);
+ }
+
+ for (i = 0; i < conf->num_bd; i++) {
+ bd = &channel->bd[i];
+ bd->mode.command = conf->command;
+ bd->mode.status = BD_DONE | BD_EXTD | BD_CONT | BD_INTR;
+ if (i == (conf->num_bd - 1))
+ bd->mode.status |= BD_WRAP;
+ bd->mode.count = conf->period;
+ bd->buffer_addr = conf->saddr + (conf->period * i);
+ bd->ext_buffer_addr = 0;
+ }
+
+ sc->ccb[chn].base_bd_ptr = vtophys(channel->bd);
+ sc->ccb[chn].current_bd_ptr = vtophys(channel->bd);
+
+ /*
+ * Load context.
+ *
+ * i.MX6 Reference Manual: Appendix A SDMA Scripts
+ * A.3.1.7.1 (mcu_2_app)
+ */
+
+ /*
+ * TODO: allow using other scripts
+ */
+ context = sc->context;
+ memset(context, 0, sizeof(*context));
+ context->channel_state.pc = sc->fw_scripts->mcu_2_app_addr;
+
+ /*
+ * Tx FIFO 0 address (r6)
+ * Event_mask (r1)
+ * Event2_mask (r0)
+ * Watermark level (r7)
+ */
+
+ if (conf->event > 32) {
+ context->gReg[0] = (1 << (conf->event % 32));
+ context->gReg[1] = 0;
+ } else {
+ context->gReg[0] = 0;
+ context->gReg[1] = (1 << conf->event);
+ }
+
+ context->gReg[6] = conf->daddr;
+ context->gReg[7] = conf->word_length;
+
+ bd0 = sc->bd0;
+ bd0->mode.command = C0_SETDM;
+ bd0->mode.status = BD_DONE | BD_INTR | BD_WRAP | BD_EXTD;
+ bd0->mode.count = sizeof(*context) / 4;
+ bd0->buffer_addr = sc->context_phys;
+ bd0->ext_buffer_addr = 2048 + (sizeof(*context) / 4) * chn;
+
+ WRITE4(sc, SDMAARM_HSTART, 1);
+
+#if 0
+ /* Debug purposes */
+
+ timeout = 1000;
+ while (!(ret = READ4(sc, SDMAARM_INTR) & 1)) {
+ if (timeout-- <= 0)
+ break;
+ DELAY(10);
+ };
+
+ if (!ret) {
+ device_printf(sc->dev, "Failed to load context.\n");
+ return (-1);
+ }
+
+ WRITE4(sc, SDMAARM_INTR, ret);
+
+ device_printf(sc->dev, "Context loaded successfully.\n");
+#endif
+
+ return (0);
+}
+
+static int
+load_firmware(struct sdma_softc *sc)
+{
+ const struct sdma_firmware_header *header;
+ const struct firmware *fp;
+
+ fp = firmware_get("sdma-imx6q");
+ if (fp == NULL) {
+ device_printf(sc->dev, "Can't get firmware.\n");
+ return (-1);
+ }
+
+ header = fp->data;
+ if (header->magic != FW_HEADER_MAGIC) {
+ device_printf(sc->dev, "Can't use firmware.\n");
+ return (-1);
+ }
+
+ sc->fw_header = header;
+ sc->fw_scripts = (const void *)((const char *)header +
+ header->script_addrs_start);
+
+ return (0);
+}
+
+static int
+boot_firmware(struct sdma_softc *sc)
+{
+ struct sdma_buffer_descriptor *bd0;
+ const uint32_t *ram_code;
+ int timeout;
+ int ret;
+ int chn;
+ int sz;
+ int i;
+
+ ram_code = (const void *)((const char *)sc->fw_header +
+ sc->fw_header->ram_code_start);
+
+ /* Make sure SDMA has not started yet */
+ WRITE4(sc, SDMAARM_MC0PTR, 0);
+
+ sz = SDMA_N_CHANNELS * sizeof(struct sdma_channel_control) + \
+ sizeof(struct sdma_context_data);
+ sc->ccb = (void *)kmem_alloc_contig(sz, M_ZERO, 0, ~0, PAGE_SIZE, 0,
+ VM_MEMATTR_UNCACHEABLE);
+ sc->ccb_phys = vtophys(sc->ccb);
+
+ sc->context = (void *)((char *)sc->ccb + \
+ SDMA_N_CHANNELS * sizeof(struct sdma_channel_control));
+ sc->context_phys = vtophys(sc->context);
+
+ /* Disable all the channels */
+ for (i = 0; i < SDMA_N_EVENTS; i++)
+ WRITE4(sc, SDMAARM_CHNENBL(i), 0);
+
+ /* All channels have priority 0 */
+ for (i = 0; i < SDMA_N_CHANNELS; i++)
+ WRITE4(sc, SDMAARM_SDMA_CHNPRI(i), 0);
+
+ /* Channel 0 is used for booting firmware */
+ chn = 0;
+
+ sc->bd0 = (void *)kmem_alloc_contig(PAGE_SIZE, M_ZERO, 0, ~0, PAGE_SIZE,
+ 0, VM_MEMATTR_UNCACHEABLE);
+ bd0 = sc->bd0;
+ sc->ccb[chn].base_bd_ptr = vtophys(bd0);
+ sc->ccb[chn].current_bd_ptr = vtophys(bd0);
+
+ WRITE4(sc, SDMAARM_SDMA_CHNPRI(chn), 1);
+
+ sdma_overrides(sc, chn, 1, 0, 0);
+
+ /* XXX: not sure what is that */
+ WRITE4(sc, SDMAARM_CHN0ADDR, 0x4050);
+
+ WRITE4(sc, SDMAARM_CONFIG, 0);
+ WRITE4(sc, SDMAARM_MC0PTR, sc->ccb_phys);
+ WRITE4(sc, SDMAARM_CONFIG, CONFIG_CSM);
+ WRITE4(sc, SDMAARM_SDMA_CHNPRI(chn), 1);
+
+ bd0->mode.command = C0_SETPM;
+ bd0->mode.status = BD_DONE | BD_INTR | BD_WRAP | BD_EXTD;
+ bd0->mode.count = sc->fw_header->ram_code_size / 2;
+ bd0->buffer_addr = vtophys(ram_code);
+ bd0->ext_buffer_addr = sc->fw_scripts->ram_code_start_addr;
+
+ WRITE4(sc, SDMAARM_HSTART, 1);
+
+ timeout = 100;
+ while (!(ret = READ4(sc, SDMAARM_INTR) & 1)) {
+ if (timeout-- <= 0)
+ break;
+ DELAY(10);
+ }
+
+ if (ret == 0) {
+ device_printf(sc->dev, "SDMA failed to boot\n");
+ return (-1);
+ }
+
+ WRITE4(sc, SDMAARM_INTR, ret);
+
+#if 0
+ device_printf(sc->dev, "SDMA booted successfully.\n");
+#endif
+
+ /* Debug is disabled */
+ WRITE4(sc, SDMAARM_ONCE_ENB, 0);
+
+ return (0);
+}
+
+static int
+sdma_attach(device_t dev)
+{
+ struct sdma_softc *sc;
+ int err;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ if (load_firmware(sc) == -1) {
+ firmware_unavailable = true;
+ return (ENXIO);
+ }
+
+ if (bus_alloc_resources(dev, sdma_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ /* Memory interface */
+ sc->bst = rman_get_bustag(sc->res[0]);
+ sc->bsh = rman_get_bushandle(sc->res[0]);
+
+ sdma_sc = sc;
+
+ /* Setup interrupt handler */
+ err = bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE,
+ NULL, sdma_intr, sc, &sc->ih);
+ if (err) {
+ device_printf(dev, "Unable to alloc interrupt resource.\n");
+ return (ENXIO);
+ }
+
+ if (boot_firmware(sc) == -1)
+ return (ENXIO);
+
+ return (0);
+};
+
+static device_method_t sdma_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, sdma_probe),
+ DEVMETHOD(device_attach, sdma_attach),
+ { 0, 0 }
+};
+
+static driver_t sdma_driver = {
+ "sdma",
+ sdma_methods,
+ sizeof(struct sdma_softc),
+};
+
+static devclass_t sdma_devclass;
+
+/* We want to attach after all interrupt controllers, before anything else. */
+EARLY_DRIVER_MODULE(sdma, simplebus, sdma_driver, sdma_devclass, 0, 0,
+ BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LAST);
diff --git a/sys/arm/freescale/imx/imx6_sdma.h b/sys/arm/freescale/imx/imx6_sdma.h
new file mode 100644
index 000000000000..4cd4cebf9435
--- /dev/null
+++ b/sys/arm/freescale/imx/imx6_sdma.h
@@ -0,0 +1,245 @@
+/*-
+ * Copyright (c) 2015 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define SDMAARM_MC0PTR 0x00 /* ARM platform Channel 0 Pointer */
+#define SDMAARM_INTR 0x04 /* Channel Interrupts */
+#define SDMAARM_STOP_STAT 0x08 /* Channel Stop/Channel Status */
+#define SDMAARM_HSTART 0x0C /* Channel Start */
+#define SDMAARM_EVTOVR 0x10 /* Channel Event Override */
+#define SDMAARM_DSPOVR 0x14 /* Channel BP Override */
+#define SDMAARM_HOSTOVR 0x18 /* Channel ARM platform Override */
+#define SDMAARM_EVTPEND 0x1C /* Channel Event Pending */
+#define SDMAARM_RESET 0x24 /* Reset Register */
+#define SDMAARM_EVTERR 0x28 /* DMA Request Error Register */
+#define SDMAARM_INTRMASK 0x2C /* Channel ARM platform Interrupt Mask */
+#define SDMAARM_PSW 0x30 /* Schedule Status */
+#define SDMAARM_EVTERRDBG 0x34 /* DMA Request Error Register */
+#define SDMAARM_CONFIG 0x38 /* Configuration Register */
+#define CONFIG_CSM 0x3
+#define SDMAARM_SDMA_LOCK 0x3C /* SDMA LOCK */
+#define SDMAARM_ONCE_ENB 0x40 /* OnCE Enable */
+#define SDMAARM_ONCE_DATA 0x44 /* OnCE Data Register */
+#define SDMAARM_ONCE_INSTR 0x48 /* OnCE Instruction Register */
+#define SDMAARM_ONCE_STAT 0x4C /* OnCE Status Register */
+#define SDMAARM_ONCE_CMD 0x50 /* OnCE Command Register */
+#define SDMAARM_ILLINSTADDR 0x58 /* Illegal Instruction Trap Address */
+#define SDMAARM_CHN0ADDR 0x5C /* Channel 0 Boot Address */
+#define SDMAARM_EVT_MIRROR 0x60 /* DMA Requests */
+#define SDMAARM_EVT_MIRROR2 0x64 /* DMA Requests 2 */
+#define SDMAARM_XTRIG_CONF1 0x70 /* Cross-Trigger Events Configuration Register 1 */
+#define SDMAARM_XTRIG_CONF2 0x74 /* Cross-Trigger Events Configuration Register 2 */
+#define SDMAARM_SDMA_CHNPRI(n) (0x100 + 0x4 * n) /* Channel Priority Registers */
+#define SDMAARM_CHNENBL(n) (0x200 + 0x4 * n) /* Channel Enable RAM */
+
+/* SDMA Event Mappings */
+#define SSI1_RX_1 35
+#define SSI1_TX_1 36
+#define SSI1_RX_0 37
+#define SSI1_TX_0 38
+#define SSI2_RX_1 39
+#define SSI2_TX_1 40
+#define SSI2_RX_0 41
+#define SSI2_TX_0 42
+#define SSI3_RX_1 43
+#define SSI3_TX_1 44
+#define SSI3_RX_0 45
+#define SSI3_TX_0 46
+
+#define C0_ADDR 0x01
+#define C0_LOAD 0x02
+#define C0_DUMP 0x03
+#define C0_SETCTX 0x07
+#define C0_GETCTX 0x03
+#define C0_SETDM 0x01
+#define C0_SETPM 0x04
+#define C0_GETDM 0x02
+#define C0_GETPM 0x08
+
+#define BD_DONE 0x01
+#define BD_WRAP 0x02
+#define BD_CONT 0x04
+#define BD_INTR 0x08
+#define BD_RROR 0x10
+#define BD_LAST 0x20
+#define BD_EXTD 0x80
+
+/* sDMA data transfer length */
+#define CMD_4BYTES 0
+#define CMD_3BYTES 3
+#define CMD_2BYTES 2
+#define CMD_1BYTES 1
+
+struct sdma_firmware_header {
+ uint32_t magic;
+ uint32_t version_major;
+ uint32_t version_minor;
+ uint32_t script_addrs_start;
+ uint32_t num_script_addrs;
+ uint32_t ram_code_start;
+ uint32_t ram_code_size;
+};
+
+struct sdma_mode_count {
+ uint16_t count;
+ uint8_t status;
+ uint8_t command;
+};
+
+struct sdma_buffer_descriptor {
+ struct sdma_mode_count mode;
+ uint32_t buffer_addr;
+ uint32_t ext_buffer_addr;
+} __packed;
+
+struct sdma_channel_control {
+ uint32_t current_bd_ptr;
+ uint32_t base_bd_ptr;
+ uint32_t unused[2];
+} __packed;
+
+struct sdma_state_registers {
+ uint32_t pc :14;
+ uint32_t unused1: 1;
+ uint32_t t : 1;
+ uint32_t rpc :14;
+ uint32_t unused0: 1;
+ uint32_t sf : 1;
+ uint32_t spc :14;
+ uint32_t unused2: 1;
+ uint32_t df : 1;
+ uint32_t epc :14;
+ uint32_t lm : 2;
+} __packed;
+
+struct sdma_context_data {
+ struct sdma_state_registers channel_state;
+ uint32_t gReg[8];
+ uint32_t mda;
+ uint32_t msa;
+ uint32_t ms;
+ uint32_t md;
+ uint32_t pda;
+ uint32_t psa;
+ uint32_t ps;
+ uint32_t pd;
+ uint32_t ca;
+ uint32_t cs;
+ uint32_t dda;
+ uint32_t dsa;
+ uint32_t ds;
+ uint32_t dd;
+ uint32_t unused[8];
+} __packed;
+
+/* SDMA firmware script pointers */
+struct sdma_script_start_addrs {
+ int32_t ap_2_ap_addr;
+ int32_t ap_2_bp_addr;
+ int32_t ap_2_ap_fixed_addr;
+ int32_t bp_2_ap_addr;
+ int32_t loopback_on_dsp_side_addr;
+ int32_t mcu_interrupt_only_addr;
+ int32_t firi_2_per_addr;
+ int32_t firi_2_mcu_addr;
+ int32_t per_2_firi_addr;
+ int32_t mcu_2_firi_addr;
+ int32_t uart_2_per_addr;
+ int32_t uart_2_mcu_addr;
+ int32_t per_2_app_addr;
+ int32_t mcu_2_app_addr;
+ int32_t per_2_per_addr;
+ int32_t uartsh_2_per_addr;
+ int32_t uartsh_2_mcu_addr;
+ int32_t per_2_shp_addr;
+ int32_t mcu_2_shp_addr;
+ int32_t ata_2_mcu_addr;
+ int32_t mcu_2_ata_addr;
+ int32_t app_2_per_addr;
+ int32_t app_2_mcu_addr;
+ int32_t shp_2_per_addr;
+ int32_t shp_2_mcu_addr;
+ int32_t mshc_2_mcu_addr;
+ int32_t mcu_2_mshc_addr;
+ int32_t spdif_2_mcu_addr;
+ int32_t mcu_2_spdif_addr;
+ int32_t asrc_2_mcu_addr;
+ int32_t ext_mem_2_ipu_addr;
+ int32_t descrambler_addr;
+ int32_t dptc_dvfs_addr;
+ int32_t utra_addr;
+ int32_t ram_code_start_addr;
+ int32_t mcu_2_ssish_addr;
+ int32_t ssish_2_mcu_addr;
+ int32_t hdmi_dma_addr;
+};
+
+#define SDMA_N_CHANNELS 32
+#define SDMA_N_EVENTS 48
+#define FW_HEADER_MAGIC 0x414d4453
+
+struct sdma_channel {
+ struct sdma_conf *conf;
+ struct sdma_buffer_descriptor *bd;
+ uint8_t in_use;
+};
+
+struct sdma_softc {
+ struct resource *res[2];
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+ device_t dev;
+ void *ih;
+ struct sdma_channel_control *ccb;
+ struct sdma_buffer_descriptor *bd0;
+ struct sdma_context_data *context;
+ struct sdma_channel channel[SDMA_N_CHANNELS];
+ uint32_t num_bd;
+ uint32_t ccb_phys;
+ uint32_t context_phys;
+ const struct sdma_firmware_header *fw_header;
+ const struct sdma_script_start_addrs *fw_scripts;
+};
+
+struct sdma_conf {
+ bus_addr_t saddr;
+ bus_addr_t daddr;
+ uint32_t word_length;
+ uint32_t nbits;
+ uint32_t command;
+ uint32_t num_bd;
+ uint32_t event;
+ uint32_t period;
+ uint32_t (*ih)(void *, int);
+ void *ih_user;
+};
+
+int sdma_configure(int, struct sdma_conf *);
+int sdma_start(int);
+int sdma_stop(int);
+int sdma_alloc(void);
+int sdma_free(int);
diff --git a/sys/arm/freescale/imx/imx6_snvs.c b/sys/arm/freescale/imx/imx6_snvs.c
new file mode 100644
index 000000000000..138abb883b7e
--- /dev/null
+++ b/sys/arm/freescale/imx/imx6_snvs.c
@@ -0,0 +1,235 @@
+/*-
+ * Copyright (c) 2017 Ian Lepore <ian@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Driver for imx6 Secure Non-Volatile Storage system, which really means "all
+ * the stuff that's powered by a battery when main power is off". This includes
+ * realtime clock, tamper monitor, and power-management functions. Currently
+ * this driver provides only realtime clock support.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/clock.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <machine/bus.h>
+
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "clock_if.h"
+#include "syscon_if.h"
+
+#define SNVS_LPCR 0x38 /* Control register */
+#define LPCR_LPCALB_VAL_SHIFT 10 /* Calibration shift */
+#define LPCR_LPCALB_VAL_MASK 0x1f /* Calibration mask */
+#define LPCR_LPCALB_EN (1u << 8) /* Calibration enable */
+#define LPCR_SRTC_ENV (1u << 0) /* RTC enabled/valid */
+
+#define SNVS_LPSRTCMR 0x50 /* Counter MSB */
+#define SNVS_LPSRTCLR 0x54 /* Counter LSB */
+
+#define RTC_RESOLUTION_US (1000000 / 32768) /* 32khz clock */
+
+/*
+ * The RTC is a 47-bit counter clocked at 32KHz and organized as a 32.15
+ * fixed-point binary value. Shifting by SBT_LSB bits translates between
+ * counter and sbintime values.
+ */
+#define RTC_BITS 47
+#define SBT_BITS 64
+#define SBT_LSB (SBT_BITS - RTC_BITS)
+
+struct snvs_softc {
+ device_t dev;
+ struct syscon *syscon;
+ uint32_t lpcr;
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"fsl,sec-v4.0-mon-rtc-lp", true},
+ {NULL, false}
+};
+
+static inline uint32_t
+RD4(struct snvs_softc *sc, bus_size_t offset)
+{
+
+ return (SYSCON_READ_4(sc->syscon, offset));
+}
+
+static inline void
+WR4(struct snvs_softc *sc, bus_size_t offset, uint32_t value)
+{
+
+ SYSCON_WRITE_4(sc->syscon, offset, value);
+}
+
+static void
+snvs_rtc_enable(struct snvs_softc *sc, bool enable)
+{
+ uint32_t enbit;
+
+ if (enable)
+ sc->lpcr |= LPCR_SRTC_ENV;
+ else
+ sc->lpcr &= ~LPCR_SRTC_ENV;
+ WR4(sc, SNVS_LPCR, sc->lpcr);
+
+ /* Wait for the hardware to achieve the requested state. */
+ enbit = sc->lpcr & LPCR_SRTC_ENV;
+ while ((RD4(sc, SNVS_LPCR) & LPCR_SRTC_ENV) != enbit)
+ continue;
+}
+
+static int
+snvs_gettime(device_t dev, struct timespec *ts)
+{
+ struct snvs_softc *sc;
+ sbintime_t counter1, counter2;
+
+ sc = device_get_softc(dev);
+
+ /* If the clock is not enabled and valid, we can't help. */
+ if (!(RD4(sc, SNVS_LPCR) & LPCR_SRTC_ENV)) {
+ return (EINVAL);
+ }
+
+ /*
+ * The counter is clocked asynchronously to cpu accesses; read and
+ * assemble the pieces of the counter until we get the same value twice.
+ * The counter is 47 bits, organized as a 32.15 binary fixed-point
+ * value. If we shift it up to the high order part of a 64-bit word it
+ * turns into an sbintime.
+ */
+ do {
+ counter1 = (uint64_t)RD4(sc, SNVS_LPSRTCMR) << (SBT_LSB + 32);
+ counter1 |= (uint64_t)RD4(sc, SNVS_LPSRTCLR) << (SBT_LSB);
+ counter2 = (uint64_t)RD4(sc, SNVS_LPSRTCMR) << (SBT_LSB + 32);
+ counter2 |= (uint64_t)RD4(sc, SNVS_LPSRTCLR) << (SBT_LSB);
+ } while (counter1 != counter2);
+
+ *ts = sbttots(counter1);
+
+ clock_dbgprint_ts(sc->dev, CLOCK_DBG_READ, ts);
+
+ return (0);
+}
+
+static int
+snvs_settime(device_t dev, struct timespec *ts)
+{
+ struct snvs_softc *sc;
+ sbintime_t sbt;
+
+ sc = device_get_softc(dev);
+
+ /*
+ * The hardware format is the same as sbt (with fewer fractional bits),
+ * so first convert the time to sbt. It takes two clock cycles for the
+ * counter to start after setting the enable bit, so add two SBT_LSBs to
+ * what we're about to set.
+ */
+ sbt = tstosbt(*ts);
+ sbt += 2 << SBT_LSB;
+ snvs_rtc_enable(sc, false);
+ WR4(sc, SNVS_LPSRTCMR, (uint32_t)(sbt >> (SBT_LSB + 32)));
+ WR4(sc, SNVS_LPSRTCLR, (uint32_t)(sbt >> (SBT_LSB)));
+ snvs_rtc_enable(sc, true);
+
+ clock_dbgprint_ts(sc->dev, CLOCK_DBG_WRITE, ts);
+
+ return (0);
+}
+
+static int
+snvs_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+ return (ENXIO);
+
+ device_set_desc(dev, "i.MX6 SNVS RTC");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+snvs_attach(device_t dev)
+{
+ struct snvs_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ if (syscon_get_handle_default(sc->dev, &sc->syscon) != 0) {
+ device_printf(sc->dev, "Cannot get syscon handle\n");
+ return (ENXIO);
+ }
+
+ clock_register(sc->dev, RTC_RESOLUTION_US);
+
+ return (0);
+}
+
+static int
+snvs_detach(device_t dev)
+{
+ struct snvs_softc *sc;
+
+ sc = device_get_softc(dev);
+ clock_unregister(sc->dev);
+ return (0);
+}
+
+static device_method_t snvs_methods[] = {
+ DEVMETHOD(device_probe, snvs_probe),
+ DEVMETHOD(device_attach, snvs_attach),
+ DEVMETHOD(device_detach, snvs_detach),
+
+ /* clock_if methods */
+ DEVMETHOD(clock_gettime, snvs_gettime),
+ DEVMETHOD(clock_settime, snvs_settime),
+
+ DEVMETHOD_END
+};
+
+static driver_t snvs_driver = {
+ "snvs",
+ snvs_methods,
+ sizeof(struct snvs_softc),
+};
+
+static devclass_t snvs_devclass;
+
+DRIVER_MODULE(snvs, simplebus, snvs_driver, snvs_devclass, 0, 0);
+SIMPLEBUS_PNP_INFO(compat_data);
diff --git a/sys/arm/freescale/imx/imx6_src.c b/sys/arm/freescale/imx/imx6_src.c
new file mode 100644
index 000000000000..325c8befabc3
--- /dev/null
+++ b/sys/arm/freescale/imx/imx6_src.c
@@ -0,0 +1,173 @@
+/*-
+ * Copyright (c) 2015 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * System Reset Control for iMX6
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+
+#include <arm/freescale/imx/imx6_src.h>
+
+#define SRC_SCR 0
+#define SW_IPU1_RST (1 << 3)
+
+struct src_softc {
+ device_t dev;
+ struct resource *mem_res;
+};
+
+static struct src_softc *src_sc;
+
+static inline uint32_t
+RD4(struct src_softc *sc, bus_size_t off)
+{
+
+ return (bus_read_4(sc->mem_res, off));
+}
+
+static inline void
+WR4(struct src_softc *sc, bus_size_t off, uint32_t val)
+{
+
+ bus_write_4(sc->mem_res, off, val);
+}
+
+int
+src_reset_ipu(void)
+{
+ uint32_t reg;
+ int timeout = 10000;
+
+ if (src_sc == NULL)
+ return (-1);
+
+ reg = RD4(src_sc, SRC_SCR);
+ reg |= SW_IPU1_RST;
+ WR4(src_sc, SRC_SCR, reg);
+
+ while (timeout-- > 0) {
+ reg = RD4(src_sc, SRC_SCR);
+ if (reg & SW_IPU1_RST)
+ DELAY(1);
+ else
+ break;
+ }
+
+ if (timeout < 0)
+ return (-1);
+ else
+ return (0);
+}
+
+static int
+src_detach(device_t dev)
+{
+ struct src_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (sc->mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+
+ return (0);
+}
+
+static int
+src_attach(device_t dev)
+{
+ struct src_softc *sc;
+ int err, rid;
+
+ sc = device_get_softc(dev);
+ err = 0;
+
+ /* Allocate bus_space resources. */
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "Cannot allocate memory resources\n");
+ err = ENXIO;
+ goto out;
+ }
+
+ src_sc = sc;
+
+ err = 0;
+
+out:
+
+ if (err != 0)
+ src_detach(dev);
+
+ return (err);
+}
+
+static int
+src_probe(device_t dev)
+{
+
+ if ((ofw_bus_is_compatible(dev, "fsl,imx6q-src") == 0) &&
+ (ofw_bus_is_compatible(dev, "fsl,imx6-src") == 0))
+ return (ENXIO);
+
+ device_set_desc(dev, "Freescale i.MX6 System Reset Controller");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static device_method_t src_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, src_probe),
+ DEVMETHOD(device_attach, src_attach),
+ DEVMETHOD(device_detach, src_detach),
+
+ DEVMETHOD_END
+};
+
+static driver_t src_driver = {
+ "src",
+ src_methods,
+ sizeof(struct src_softc)
+};
+
+static devclass_t src_devclass;
+
+DRIVER_MODULE(src, simplebus, src_driver, src_devclass, 0, 0);
diff --git a/sys/arm/freescale/imx/imx6_src.h b/sys/arm/freescale/imx/imx6_src.h
new file mode 100644
index 000000000000..c973c6182951
--- /dev/null
+++ b/sys/arm/freescale/imx/imx6_src.h
@@ -0,0 +1,34 @@
+/*-
+ * Copyright (c) 2015 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __IMX6_SRC_H__
+#define __IMX6_SRC_H__
+
+int src_reset_ipu(void);
+
+#endif /* __IMX6_SRC_H__ */
diff --git a/sys/arm/freescale/imx/imx6_ssi.c b/sys/arm/freescale/imx/imx6_ssi.c
new file mode 100644
index 000000000000..2330978849e9
--- /dev/null
+++ b/sys/arm/freescale/imx/imx6_ssi.c
@@ -0,0 +1,861 @@
+/*-
+ * Copyright (c) 2015 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * i.MX6 Synchronous Serial Interface (SSI)
+ *
+ * Chapter 61, i.MX 6Dual/6Quad Applications Processor Reference Manual,
+ * Rev. 1, 04/2013
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+
+#include <dev/sound/pcm/sound.h>
+#include <dev/sound/chip.h>
+#include <mixer_if.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <arm/freescale/imx/imx6_sdma.h>
+#include <arm/freescale/imx/imx6_anatopvar.h>
+#include <arm/freescale/imx/imx_ccmvar.h>
+
+#define READ4(_sc, _reg) \
+ bus_space_read_4(_sc->bst, _sc->bsh, _reg)
+#define WRITE4(_sc, _reg, _val) \
+ bus_space_write_4(_sc->bst, _sc->bsh, _reg, _val)
+
+#define SSI_NCHANNELS 1
+#define DMAS_TOTAL 8
+
+/* i.MX6 SSI registers */
+
+#define SSI_STX0 0x00 /* Transmit Data Register n */
+#define SSI_STX1 0x04 /* Transmit Data Register n */
+#define SSI_SRX0 0x08 /* Receive Data Register n */
+#define SSI_SRX1 0x0C /* Receive Data Register n */
+#define SSI_SCR 0x10 /* Control Register */
+#define SCR_I2S_MODE_S 5 /* I2S Mode Select. */
+#define SCR_I2S_MODE_M 0x3
+#define SCR_SYN (1 << 4)
+#define SCR_NET (1 << 3) /* Network mode */
+#define SCR_RE (1 << 2) /* Receive Enable. */
+#define SCR_TE (1 << 1) /* Transmit Enable. */
+#define SCR_SSIEN (1 << 0) /* SSI Enable */
+#define SSI_SISR 0x14 /* Interrupt Status Register */
+#define SSI_SIER 0x18 /* Interrupt Enable Register */
+#define SIER_RDMAE (1 << 22) /* Receive DMA Enable. */
+#define SIER_RIE (1 << 21) /* Receive Interrupt Enable. */
+#define SIER_TDMAE (1 << 20) /* Transmit DMA Enable. */
+#define SIER_TIE (1 << 19) /* Transmit Interrupt Enable. */
+#define SIER_TDE0IE (1 << 12) /* Transmit Data Register Empty 0. */
+#define SIER_TUE0IE (1 << 8) /* Transmitter Underrun Error 0. */
+#define SIER_TFE0IE (1 << 0) /* Transmit FIFO Empty 0 IE. */
+#define SSI_STCR 0x1C /* Transmit Configuration Register */
+#define STCR_TXBIT0 (1 << 9) /* Transmit Bit 0 shift MSB/LSB */
+#define STCR_TFEN1 (1 << 8) /* Transmit FIFO Enable 1. */
+#define STCR_TFEN0 (1 << 7) /* Transmit FIFO Enable 0. */
+#define STCR_TFDIR (1 << 6) /* Transmit Frame Direction. */
+#define STCR_TXDIR (1 << 5) /* Transmit Clock Direction. */
+#define STCR_TSHFD (1 << 4) /* Transmit Shift Direction. */
+#define STCR_TSCKP (1 << 3) /* Transmit Clock Polarity. */
+#define STCR_TFSI (1 << 2) /* Transmit Frame Sync Invert. */
+#define STCR_TFSL (1 << 1) /* Transmit Frame Sync Length. */
+#define STCR_TEFS (1 << 0) /* Transmit Early Frame Sync. */
+#define SSI_SRCR 0x20 /* Receive Configuration Register */
+#define SSI_STCCR 0x24 /* Transmit Clock Control Register */
+#define STCCR_DIV2 (1 << 18) /* Divide By 2. */
+#define STCCR_PSR (1 << 17) /* Divide clock by 8. */
+#define WL3_WL0_S 13
+#define WL3_WL0_M 0xf
+#define DC4_DC0_S 8
+#define DC4_DC0_M 0x1f
+#define PM7_PM0_S 0
+#define PM7_PM0_M 0xff
+#define SSI_SRCCR 0x28 /* Receive Clock Control Register */
+#define SSI_SFCSR 0x2C /* FIFO Control/Status Register */
+#define SFCSR_RFWM1_S 20 /* Receive FIFO Empty WaterMark 1 */
+#define SFCSR_RFWM1_M 0xf
+#define SFCSR_TFWM1_S 16 /* Transmit FIFO Empty WaterMark 1 */
+#define SFCSR_TFWM1_M 0xf
+#define SFCSR_RFWM0_S 4 /* Receive FIFO Empty WaterMark 0 */
+#define SFCSR_RFWM0_M 0xf
+#define SFCSR_TFWM0_S 0 /* Transmit FIFO Empty WaterMark 0 */
+#define SFCSR_TFWM0_M 0xf
+#define SSI_SACNT 0x38 /* AC97 Control Register */
+#define SSI_SACADD 0x3C /* AC97 Command Address Register */
+#define SSI_SACDAT 0x40 /* AC97 Command Data Register */
+#define SSI_SATAG 0x44 /* AC97 Tag Register */
+#define SSI_STMSK 0x48 /* Transmit Time Slot Mask Register */
+#define SSI_SRMSK 0x4C /* Receive Time Slot Mask Register */
+#define SSI_SACCST 0x50 /* AC97 Channel Status Register */
+#define SSI_SACCEN 0x54 /* AC97 Channel Enable Register */
+#define SSI_SACCDIS 0x58 /* AC97 Channel Disable Register */
+
+static MALLOC_DEFINE(M_SSI, "ssi", "ssi audio");
+
+uint32_t ssi_dma_intr(void *arg, int chn);
+
+struct ssi_rate {
+ uint32_t speed;
+ uint32_t mfi; /* PLL4 Multiplication Factor Integer */
+ uint32_t mfn; /* PLL4 Multiplication Factor Numerator */
+ uint32_t mfd; /* PLL4 Multiplication Factor Denominator */
+ /* More dividers to configure can be added here */
+};
+
+static struct ssi_rate rate_map[] = {
+ { 192000, 49, 152, 1000 }, /* PLL4 49.152 Mhz */
+ /* TODO: add more frequences */
+ { 0, 0 },
+};
+
+/*
+ * i.MX6 example bit clock formula
+ *
+ * BCLK = 2 channels * 192000 hz * 24 bit = 9216000 hz =
+ * (24000000 * (49 + 152/1000.0) / 4 / 4 / 2 / 2 / 2 / 1 / 1)
+ * ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
+ * | | | | | | | | | | |
+ * Fref ------/ | | | | | | | | | |
+ * PLL4 div select -/ | | | | | | | | |
+ * PLL4 num --------------/ | | | | | | | |
+ * PLL4 denom -------------------/ | | | | | | |
+ * PLL4 post div ---------------------/ | | | | | |
+ * CCM ssi pre div (CCM_CS1CDR) ----------/ | | | | |
+ * CCM ssi post div (CCM_CS1CDR) -------------/ | | | |
+ * SSI PM7_PM0_S ---------------------------------/ | | |
+ * SSI Fixed divider ---------------------------------/ | |
+ * SSI DIV2 ----------------------------------------------/ |
+ * SSI PSR (prescaler /1 or /8) ------------------------------/
+ *
+ * MCLK (Master clock) depends on DAC, usually BCLK * 4
+ */
+
+struct sc_info {
+ struct resource *res[2];
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+ device_t dev;
+ struct mtx *lock;
+ void *ih;
+ int pos;
+ int dma_size;
+ bus_dma_tag_t dma_tag;
+ bus_dmamap_t dma_map;
+ bus_addr_t buf_base_phys;
+ uint32_t *buf_base;
+ struct sdma_conf *conf;
+ struct ssi_rate *sr;
+ struct sdma_softc *sdma_sc;
+ uint32_t sdma_ev_rx;
+ uint32_t sdma_ev_tx;
+ int sdma_channel;
+};
+
+/* Channel registers */
+struct sc_chinfo {
+ struct snd_dbuf *buffer;
+ struct pcm_channel *channel;
+ struct sc_pcminfo *parent;
+
+ /* Channel information */
+ uint32_t dir;
+ uint32_t format;
+
+ /* Flags */
+ uint32_t run;
+};
+
+/* PCM device private data */
+struct sc_pcminfo {
+ device_t dev;
+ uint32_t (*ih)(struct sc_pcminfo *scp);
+ uint32_t chnum;
+ struct sc_chinfo chan[SSI_NCHANNELS];
+ struct sc_info *sc;
+};
+
+static struct resource_spec ssi_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+static int setup_dma(struct sc_pcminfo *scp);
+static void setup_ssi(struct sc_info *);
+static void ssi_configure_clock(struct sc_info *);
+
+/*
+ * Mixer interface.
+ */
+
+static int
+ssimixer_init(struct snd_mixer *m)
+{
+ struct sc_pcminfo *scp;
+ struct sc_info *sc;
+ int mask;
+
+ scp = mix_getdevinfo(m);
+ sc = scp->sc;
+
+ if (sc == NULL)
+ return -1;
+
+ mask = SOUND_MASK_PCM;
+ mask |= SOUND_MASK_VOLUME;
+
+ snd_mtxlock(sc->lock);
+ pcm_setflags(scp->dev, pcm_getflags(scp->dev) | SD_F_SOFTPCMVOL);
+ mix_setdevs(m, mask);
+ snd_mtxunlock(sc->lock);
+
+ return (0);
+}
+
+static int
+ssimixer_set(struct snd_mixer *m, unsigned dev,
+ unsigned left, unsigned right)
+{
+ struct sc_pcminfo *scp;
+
+ scp = mix_getdevinfo(m);
+
+ /* Here we can configure hardware volume on our DAC */
+
+#if 1
+ device_printf(scp->dev, "ssimixer_set() %d %d\n",
+ left, right);
+#endif
+
+ return (0);
+}
+
+static kobj_method_t ssimixer_methods[] = {
+ KOBJMETHOD(mixer_init, ssimixer_init),
+ KOBJMETHOD(mixer_set, ssimixer_set),
+ KOBJMETHOD_END
+};
+MIXER_DECLARE(ssimixer);
+
+/*
+ * Channel interface.
+ */
+
+static void *
+ssichan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
+ struct pcm_channel *c, int dir)
+{
+ struct sc_pcminfo *scp;
+ struct sc_chinfo *ch;
+ struct sc_info *sc;
+
+ scp = (struct sc_pcminfo *)devinfo;
+ sc = scp->sc;
+
+ snd_mtxlock(sc->lock);
+ ch = &scp->chan[0];
+ ch->dir = dir;
+ ch->run = 0;
+ ch->buffer = b;
+ ch->channel = c;
+ ch->parent = scp;
+ snd_mtxunlock(sc->lock);
+
+ if (sndbuf_setup(ch->buffer, sc->buf_base, sc->dma_size) != 0) {
+ device_printf(scp->dev, "Can't setup sndbuf.\n");
+ return NULL;
+ }
+
+ return ch;
+}
+
+static int
+ssichan_free(kobj_t obj, void *data)
+{
+ struct sc_chinfo *ch = data;
+ struct sc_pcminfo *scp = ch->parent;
+ struct sc_info *sc = scp->sc;
+
+#if 0
+ device_printf(scp->dev, "ssichan_free()\n");
+#endif
+
+ snd_mtxlock(sc->lock);
+ /* TODO: free channel buffer */
+ snd_mtxunlock(sc->lock);
+
+ return (0);
+}
+
+static int
+ssichan_setformat(kobj_t obj, void *data, uint32_t format)
+{
+ struct sc_chinfo *ch = data;
+
+ ch->format = format;
+
+ return (0);
+}
+
+static uint32_t
+ssichan_setspeed(kobj_t obj, void *data, uint32_t speed)
+{
+ struct sc_pcminfo *scp;
+ struct sc_chinfo *ch;
+ struct ssi_rate *sr;
+ struct sc_info *sc;
+ int threshold;
+ int i;
+
+ ch = data;
+ scp = ch->parent;
+ sc = scp->sc;
+
+ sr = NULL;
+
+ /* First look for equal frequency. */
+ for (i = 0; rate_map[i].speed != 0; i++) {
+ if (rate_map[i].speed == speed)
+ sr = &rate_map[i];
+ }
+
+ /* If no match, just find nearest. */
+ if (sr == NULL) {
+ for (i = 0; rate_map[i].speed != 0; i++) {
+ sr = &rate_map[i];
+ threshold = sr->speed + ((rate_map[i + 1].speed != 0) ?
+ ((rate_map[i + 1].speed - sr->speed) >> 1) : 0);
+ if (speed < threshold)
+ break;
+ }
+ }
+
+ sc->sr = sr;
+
+ ssi_configure_clock(sc);
+
+ return (sr->speed);
+}
+
+static void
+ssi_configure_clock(struct sc_info *sc)
+{
+ struct ssi_rate *sr;
+
+ sr = sc->sr;
+
+ pll4_configure_output(sr->mfi, sr->mfn, sr->mfd);
+
+ /* Configure other dividers here, if any */
+}
+
+static uint32_t
+ssichan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
+{
+ struct sc_chinfo *ch = data;
+ struct sc_pcminfo *scp = ch->parent;
+ struct sc_info *sc = scp->sc;
+
+ sndbuf_resize(ch->buffer, sc->dma_size / blocksize, blocksize);
+
+ setup_dma(scp);
+
+ return (sndbuf_getblksz(ch->buffer));
+}
+
+uint32_t
+ssi_dma_intr(void *arg, int chn)
+{
+ struct sc_pcminfo *scp;
+ struct sdma_conf *conf;
+ struct sc_chinfo *ch;
+ struct sc_info *sc;
+ int bufsize;
+
+ scp = arg;
+ ch = &scp->chan[0];
+ sc = scp->sc;
+ conf = sc->conf;
+
+ bufsize = sndbuf_getsize(ch->buffer);
+
+ sc->pos += conf->period;
+ if (sc->pos >= bufsize)
+ sc->pos -= bufsize;
+
+ if (ch->run)
+ chn_intr(ch->channel);
+
+ return (0);
+}
+
+static int
+find_sdma_controller(struct sc_info *sc)
+{
+ struct sdma_softc *sdma_sc;
+ phandle_t node, sdma_node;
+ device_t sdma_dev;
+ pcell_t dts_value[DMAS_TOTAL];
+ int len;
+
+ if ((node = ofw_bus_get_node(sc->dev)) == -1)
+ return (ENXIO);
+
+ if ((len = OF_getproplen(node, "dmas")) <= 0)
+ return (ENXIO);
+
+ if (len != sizeof(dts_value)) {
+ device_printf(sc->dev,
+ "\"dmas\" property length is invalid: %d (expected %d)",
+ len, sizeof(dts_value));
+ return (ENXIO);
+ }
+
+ OF_getencprop(node, "dmas", dts_value, sizeof(dts_value));
+
+ sc->sdma_ev_rx = dts_value[1];
+ sc->sdma_ev_tx = dts_value[5];
+
+ sdma_node = OF_node_from_xref(dts_value[0]);
+
+ sdma_sc = NULL;
+
+ sdma_dev = devclass_get_device(devclass_find("sdma"), 0);
+ if (sdma_dev)
+ sdma_sc = device_get_softc(sdma_dev);
+
+ if (sdma_sc == NULL) {
+ device_printf(sc->dev, "No sDMA found. Can't operate\n");
+ return (ENXIO);
+ }
+
+ sc->sdma_sc = sdma_sc;
+
+ return (0);
+};
+
+static int
+setup_dma(struct sc_pcminfo *scp)
+{
+ struct sdma_conf *conf;
+ struct sc_chinfo *ch;
+ struct sc_info *sc;
+ int fmt;
+
+ ch = &scp->chan[0];
+ sc = scp->sc;
+ conf = sc->conf;
+
+ conf->ih = ssi_dma_intr;
+ conf->ih_user = scp;
+ conf->saddr = sc->buf_base_phys;
+ conf->daddr = rman_get_start(sc->res[0]) + SSI_STX0;
+ conf->event = sc->sdma_ev_tx; /* SDMA TX event */
+ conf->period = sndbuf_getblksz(ch->buffer);
+ conf->num_bd = sndbuf_getblkcnt(ch->buffer);
+
+ /*
+ * Word Length
+ * Can be 32, 24, 16 or 8 for sDMA.
+ *
+ * SSI supports 24 at max.
+ */
+
+ fmt = sndbuf_getfmt(ch->buffer);
+
+ if (fmt & AFMT_16BIT) {
+ conf->word_length = 16;
+ conf->command = CMD_2BYTES;
+ } else if (fmt & AFMT_24BIT) {
+ conf->word_length = 24;
+ conf->command = CMD_3BYTES;
+ } else {
+ device_printf(sc->dev, "Unknown format\n");
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+ssi_start(struct sc_pcminfo *scp)
+{
+ struct sc_info *sc;
+ int reg;
+
+ sc = scp->sc;
+
+ if (sdma_configure(sc->sdma_channel, sc->conf) != 0) {
+ device_printf(sc->dev, "Can't configure sDMA\n");
+ return (-1);
+ }
+
+ /* Enable DMA interrupt */
+ reg = (SIER_TDMAE);
+ WRITE4(sc, SSI_SIER, reg);
+
+ sdma_start(sc->sdma_channel);
+
+ return (0);
+}
+
+static int
+ssi_stop(struct sc_pcminfo *scp)
+{
+ struct sc_info *sc;
+ int reg;
+
+ sc = scp->sc;
+
+ reg = READ4(sc, SSI_SIER);
+ reg &= ~(SIER_TDMAE);
+ WRITE4(sc, SSI_SIER, reg);
+
+ sdma_stop(sc->sdma_channel);
+
+ bzero(sc->buf_base, sc->dma_size);
+
+ return (0);
+}
+
+static int
+ssichan_trigger(kobj_t obj, void *data, int go)
+{
+ struct sc_pcminfo *scp;
+ struct sc_chinfo *ch;
+ struct sc_info *sc;
+
+ ch = data;
+ scp = ch->parent;
+ sc = scp->sc;
+
+ snd_mtxlock(sc->lock);
+
+ switch (go) {
+ case PCMTRIG_START:
+#if 0
+ device_printf(scp->dev, "trigger start\n");
+#endif
+ ch->run = 1;
+
+ ssi_start(scp);
+
+ break;
+
+ case PCMTRIG_STOP:
+ case PCMTRIG_ABORT:
+#if 0
+ device_printf(scp->dev, "trigger stop or abort\n");
+#endif
+ ch->run = 0;
+
+ ssi_stop(scp);
+
+ break;
+ }
+
+ snd_mtxunlock(sc->lock);
+
+ return (0);
+}
+
+static uint32_t
+ssichan_getptr(kobj_t obj, void *data)
+{
+ struct sc_pcminfo *scp;
+ struct sc_chinfo *ch;
+ struct sc_info *sc;
+
+ ch = data;
+ scp = ch->parent;
+ sc = scp->sc;
+
+ return (sc->pos);
+}
+
+static uint32_t ssi_pfmt[] = {
+ SND_FORMAT(AFMT_S24_LE, 2, 0),
+ 0
+};
+
+static struct pcmchan_caps ssi_pcaps = {44100, 192000, ssi_pfmt, 0};
+
+static struct pcmchan_caps *
+ssichan_getcaps(kobj_t obj, void *data)
+{
+
+ return (&ssi_pcaps);
+}
+
+static kobj_method_t ssichan_methods[] = {
+ KOBJMETHOD(channel_init, ssichan_init),
+ KOBJMETHOD(channel_free, ssichan_free),
+ KOBJMETHOD(channel_setformat, ssichan_setformat),
+ KOBJMETHOD(channel_setspeed, ssichan_setspeed),
+ KOBJMETHOD(channel_setblocksize, ssichan_setblocksize),
+ KOBJMETHOD(channel_trigger, ssichan_trigger),
+ KOBJMETHOD(channel_getptr, ssichan_getptr),
+ KOBJMETHOD(channel_getcaps, ssichan_getcaps),
+ KOBJMETHOD_END
+};
+CHANNEL_DECLARE(ssichan);
+
+static int
+ssi_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "fsl,imx6q-ssi"))
+ return (ENXIO);
+
+ device_set_desc(dev, "i.MX6 Synchronous Serial Interface (SSI)");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static void
+ssi_intr(void *arg)
+{
+ struct sc_pcminfo *scp;
+ struct sc_chinfo *ch;
+ struct sc_info *sc;
+
+ scp = arg;
+ sc = scp->sc;
+ ch = &scp->chan[0];
+
+ /* We don't use SSI interrupt */
+#if 0
+ device_printf(sc->dev, "SSI Intr 0x%08x\n",
+ READ4(sc, SSI_SISR));
+#endif
+}
+
+static void
+setup_ssi(struct sc_info *sc)
+{
+ int reg;
+
+ reg = READ4(sc, SSI_STCCR);
+ reg &= ~(WL3_WL0_M << WL3_WL0_S);
+ reg |= (0xb << WL3_WL0_S); /* 24 bit */
+ reg &= ~(DC4_DC0_M << DC4_DC0_S);
+ reg |= (1 << DC4_DC0_S); /* 2 words per frame */
+ reg &= ~(STCCR_DIV2); /* Divide by 1 */
+ reg &= ~(STCCR_PSR); /* Divide by 1 */
+ reg &= ~(PM7_PM0_M << PM7_PM0_S);
+ reg |= (1 << PM7_PM0_S); /* Divide by 2 */
+ WRITE4(sc, SSI_STCCR, reg);
+
+ reg = READ4(sc, SSI_SFCSR);
+ reg &= ~(SFCSR_TFWM0_M << SFCSR_TFWM0_S);
+ reg |= (8 << SFCSR_TFWM0_S); /* empty slots */
+ WRITE4(sc, SSI_SFCSR, reg);
+
+ reg = READ4(sc, SSI_STCR);
+ reg |= (STCR_TFEN0);
+ reg &= ~(STCR_TFEN1);
+ reg &= ~(STCR_TSHFD); /* MSB */
+ reg |= (STCR_TXBIT0);
+ reg |= (STCR_TXDIR | STCR_TFDIR);
+ reg |= (STCR_TSCKP); /* falling edge */
+ reg |= (STCR_TFSI);
+ reg &= ~(STCR_TFSI); /* active high frame sync */
+ reg &= ~(STCR_TFSL);
+ reg |= STCR_TEFS;
+ WRITE4(sc, SSI_STCR, reg);
+
+ reg = READ4(sc, SSI_SCR);
+ reg &= ~(SCR_I2S_MODE_M << SCR_I2S_MODE_S); /* Not master */
+ reg |= (SCR_SSIEN | SCR_TE);
+ reg |= (SCR_NET);
+ reg |= (SCR_SYN);
+ WRITE4(sc, SSI_SCR, reg);
+}
+
+static void
+ssi_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err)
+{
+ bus_addr_t *addr;
+
+ if (err)
+ return;
+
+ addr = (bus_addr_t*)arg;
+ *addr = segs[0].ds_addr;
+}
+
+static int
+ssi_attach(device_t dev)
+{
+ char status[SND_STATUSLEN];
+ struct sc_pcminfo *scp;
+ struct sc_info *sc;
+ int err;
+
+ sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO);
+ sc->dev = dev;
+ sc->sr = &rate_map[0];
+ sc->pos = 0;
+ sc->conf = malloc(sizeof(struct sdma_conf), M_DEVBUF, M_WAITOK | M_ZERO);
+
+ sc->lock = snd_mtxcreate(device_get_nameunit(dev), "ssi softc");
+ if (sc->lock == NULL) {
+ device_printf(dev, "Can't create mtx\n");
+ return (ENXIO);
+ }
+
+ if (bus_alloc_resources(dev, ssi_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ /* Memory interface */
+ sc->bst = rman_get_bustag(sc->res[0]);
+ sc->bsh = rman_get_bushandle(sc->res[0]);
+
+ /* SDMA */
+ if (find_sdma_controller(sc)) {
+ device_printf(dev, "could not find active SDMA\n");
+ return (ENXIO);
+ }
+
+ /* Setup PCM */
+ scp = malloc(sizeof(struct sc_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO);
+ scp->sc = sc;
+ scp->dev = dev;
+
+ /*
+ * Maximum possible DMA buffer.
+ * Will be used partially to match 24 bit word.
+ */
+ sc->dma_size = 131072;
+
+ /*
+ * Must use dma_size boundary as modulo feature required.
+ * Modulo feature allows setup circular buffer.
+ */
+
+ err = bus_dma_tag_create(
+ bus_get_dma_tag(sc->dev),
+ 4, sc->dma_size, /* alignment, boundary */
+ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ sc->dma_size, 1, /* maxsize, nsegments */
+ sc->dma_size, 0, /* maxsegsize, flags */
+ NULL, NULL, /* lockfunc, lockarg */
+ &sc->dma_tag);
+
+ err = bus_dmamem_alloc(sc->dma_tag, (void **)&sc->buf_base,
+ BUS_DMA_NOWAIT | BUS_DMA_COHERENT, &sc->dma_map);
+ if (err) {
+ device_printf(dev, "cannot allocate framebuffer\n");
+ return (ENXIO);
+ }
+
+ err = bus_dmamap_load(sc->dma_tag, sc->dma_map, sc->buf_base,
+ sc->dma_size, ssi_dmamap_cb, &sc->buf_base_phys, BUS_DMA_NOWAIT);
+ if (err) {
+ device_printf(dev, "cannot load DMA map\n");
+ return (ENXIO);
+ }
+
+ bzero(sc->buf_base, sc->dma_size);
+
+ /* Setup interrupt handler */
+ err = bus_setup_intr(dev, sc->res[1], INTR_MPSAFE | INTR_TYPE_AV,
+ NULL, ssi_intr, scp, &sc->ih);
+ if (err) {
+ device_printf(dev, "Unable to alloc interrupt resource.\n");
+ return (ENXIO);
+ }
+
+ pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE);
+
+ err = pcm_register(dev, scp, 1, 0);
+ if (err) {
+ device_printf(dev, "Can't register pcm.\n");
+ return (ENXIO);
+ }
+
+ scp->chnum = 0;
+ pcm_addchan(dev, PCMDIR_PLAY, &ssichan_class, scp);
+ scp->chnum++;
+
+ snprintf(status, SND_STATUSLEN, "at simplebus");
+ pcm_setstatus(dev, status);
+
+ mixer_init(dev, &ssimixer_class, scp);
+ setup_ssi(sc);
+
+ imx_ccm_ssi_configure(dev);
+
+ sc->sdma_channel = sdma_alloc();
+ if (sc->sdma_channel < 0) {
+ device_printf(sc->dev, "Can't get sDMA channel\n");
+ return (1);
+ }
+
+ return (0);
+}
+
+static device_method_t ssi_pcm_methods[] = {
+ DEVMETHOD(device_probe, ssi_probe),
+ DEVMETHOD(device_attach, ssi_attach),
+ { 0, 0 }
+};
+
+static driver_t ssi_pcm_driver = {
+ "pcm",
+ ssi_pcm_methods,
+ PCM_SOFTC_SIZE,
+};
+
+DRIVER_MODULE(ssi, simplebus, ssi_pcm_driver, pcm_devclass, 0, 0);
+MODULE_DEPEND(ssi, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
+MODULE_DEPEND(ssi, sdma, 0, 0, 0);
+MODULE_VERSION(ssi, 1);
diff --git a/sys/arm/freescale/imx/imx6_usbphy.c b/sys/arm/freescale/imx/imx6_usbphy.c
new file mode 100644
index 000000000000..56d3cce907a6
--- /dev/null
+++ b/sys/arm/freescale/imx/imx6_usbphy.c
@@ -0,0 +1,209 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Ian Lepore <ian@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * USBPHY driver for Freescale i.MX6 family of SoCs.
+ */
+
+#include "opt_bus.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+
+#include <arm/freescale/imx/imx_ccmvar.h>
+#include <arm/freescale/imx/imx6_anatopreg.h>
+#include <arm/freescale/imx/imx6_anatopvar.h>
+
+/*
+ * Hardware register defines.
+ */
+#define PWD_REG 0x0000
+#define CTRL_STATUS_REG 0x0030
+#define CTRL_SET_REG 0x0034
+#define CTRL_CLR_REG 0x0038
+#define CTRL_TOGGLE_REG 0x003c
+#define CTRL_SFTRST (1U << 31)
+#define CTRL_CLKGATE (1 << 30)
+#define CTRL_ENUTMILEVEL3 (1 << 15)
+#define CTRL_ENUTMILEVEL2 (1 << 14)
+
+struct usbphy_softc {
+ device_t dev;
+ struct resource *mem_res;
+ u_int phy_num;
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"fsl,imx6q-usbphy", true},
+ {"fsl,imx6ul-usbphy", true},
+ {NULL, false}
+};
+
+static int
+usbphy_detach(device_t dev)
+{
+ struct usbphy_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (sc->mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+
+ return (0);
+}
+
+static int
+usbphy_attach(device_t dev)
+{
+ struct usbphy_softc *sc;
+ int err, regoff, rid;
+
+ sc = device_get_softc(dev);
+ err = 0;
+
+ /* Allocate bus_space resources. */
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "Cannot allocate memory resources\n");
+ err = ENXIO;
+ goto out;
+ }
+
+ /*
+ * XXX Totally lame way to get the unit number (but not quite as lame as
+ * adding an ad-hoc property to the fdt data). This works as long as
+ * this driver is used for imx6 only.
+ */
+ const uint32_t PWD_PHY1_REG_PHYSADDR = 0x020c9000;
+ if (BUS_SPACE_PHYSADDR(sc->mem_res, 0) == PWD_PHY1_REG_PHYSADDR) {
+ sc->phy_num = 0;
+ regoff = 0;
+ } else {
+ sc->phy_num = 1;
+ regoff = 0x60;
+ }
+
+ /*
+ * Based on a note in the u-boot source code, disable charger detection
+ * to avoid degrading the differential signaling on the DP line. Note
+ * that this disables (by design) both charger detection and contact
+ * detection, because of the screwball mix of active-high and active-low
+ * bits in this register.
+ */
+ imx6_anatop_write_4(IMX6_ANALOG_USB1_CHRG_DETECT + regoff,
+ IMX6_ANALOG_USB_CHRG_DETECT_N_ENABLE |
+ IMX6_ANALOG_USB_CHRG_DETECT_N_CHK_CHRG);
+
+ imx6_anatop_write_4(IMX6_ANALOG_USB1_CHRG_DETECT + regoff,
+ IMX6_ANALOG_USB_CHRG_DETECT_N_ENABLE |
+ IMX6_ANALOG_USB_CHRG_DETECT_N_CHK_CHRG);
+
+ /* XXX Configure the overcurrent detection here. */
+
+ /*
+ * Turn on the phy clocks.
+ */
+ imx_ccm_usbphy_enable(dev);
+
+ /*
+ * Set the software reset bit, then clear both it and the clock gate bit
+ * to bring the device out of reset with the clock running.
+ */
+ bus_write_4(sc->mem_res, CTRL_SET_REG, CTRL_SFTRST);
+ bus_write_4(sc->mem_res, CTRL_CLR_REG, CTRL_SFTRST | CTRL_CLKGATE);
+
+ /* Set UTMI+ level 2+3 bits to enable low and full speed devices. */
+ bus_write_4(sc->mem_res, CTRL_SET_REG,
+ CTRL_ENUTMILEVEL2 | CTRL_ENUTMILEVEL3);
+
+ /* Power up: clear all bits in the powerdown register. */
+ bus_write_4(sc->mem_res, PWD_REG, 0);
+
+ err = 0;
+
+out:
+
+ if (err != 0)
+ usbphy_detach(dev);
+
+ return (err);
+}
+
+static int
+usbphy_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+ return (ENXIO);
+
+ device_set_desc(dev, "Freescale i.MX6 USB PHY");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static device_method_t usbphy_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, usbphy_probe),
+ DEVMETHOD(device_attach, usbphy_attach),
+ DEVMETHOD(device_detach, usbphy_detach),
+
+ DEVMETHOD_END
+};
+
+static driver_t usbphy_driver = {
+ "usbphy",
+ usbphy_methods,
+ sizeof(struct usbphy_softc)
+};
+
+static devclass_t usbphy_devclass;
+
+/*
+ * This driver needs to start before the ehci driver, but later than the usual
+ * "special" drivers like clocks and cpu. Ehci starts at DEFAULT so SUPPORTDEV
+ * is where this driver fits most.
+ */
+EARLY_DRIVER_MODULE(usbphy, simplebus, usbphy_driver, usbphy_devclass, 0, 0,
+ BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE);
diff --git a/sys/arm/freescale/imx/imx_ccmvar.h b/sys/arm/freescale/imx/imx_ccmvar.h
new file mode 100644
index 000000000000..f15c61bd96e7
--- /dev/null
+++ b/sys/arm/freescale/imx/imx_ccmvar.h
@@ -0,0 +1,66 @@
+/*-
+ * Copyright (c) 2014 Ian Lepore <ian@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef IMX_CCMVAR_H
+#define IMX_CCMVAR_H
+
+/*
+ * We need a clock management system that works across unrelated SoCs and
+ * devices. For now, to keep imx development moving, define some barebones
+ * functionality that can be shared within the imx family by having each SoC
+ * implement functions with a common name.
+ *
+ * The usb enable functions are best-effort. They turn on the usb otg, host,
+ * and phy clocks in a SoC-specific manner, but it may take a lot more than that
+ * to make usb work on a given board. In particular, it can require specific
+ * pinmux setup of gpio pins connected to external phy parts, voltage regulators
+ * and overcurrent detectors, and so on. On such boards, u-boot or other early
+ * board setup code has to handle those things.
+ */
+
+uint32_t imx_ccm_ecspi_hz(void);
+uint32_t imx_ccm_ipg_hz(void);
+uint32_t imx_ccm_perclk_hz(void);
+uint32_t imx_ccm_sdhci_hz(void);
+uint32_t imx_ccm_uart_hz(void);
+uint32_t imx_ccm_ahb_hz(void);
+uint32_t imx_ccm_ipu_hz(void);
+
+void imx_ccm_usb_enable(device_t _usbdev);
+void imx_ccm_usbphy_enable(device_t _phydev);
+void imx_ccm_ssi_configure(device_t _ssidev);
+int imx_ccm_pll_video_enable(void);
+void imx_ccm_hdmi_enable(void);
+void imx_ccm_ipu_enable(int ipu);
+int imx6_ccm_sata_enable(void);
+
+/* Routines to get and set the arm clock root divisor register. */
+uint32_t imx_ccm_get_cacrr(void);
+void imx_ccm_set_cacrr(uint32_t _divisor);
+
+#endif
diff --git a/sys/arm/freescale/imx/imx_console.c b/sys/arm/freescale/imx/imx_console.c
new file mode 100644
index 000000000000..d5ade2decd7b
--- /dev/null
+++ b/sys/arm/freescale/imx/imx_console.c
@@ -0,0 +1,176 @@
+/*-
+ * Copyright (c) 2012, 2013 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Oleksandr Rybalko under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* Simple UART console driver for Freescale i.MX515 */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/cons.h>
+#include <sys/consio.h>
+#include <sys/kernel.h>
+
+/* Allow it to be predefined, to be able to use another UART for console */
+#ifndef IMX_UART_BASE
+#define IMX_UART_BASE 0xe3fbc000 /* imx51 UART1 */
+#endif
+
+#define IMX_RXD 0x00
+#define IMX_TXD 0x40
+
+#define IMX_UFCR 0x90
+#define IMX_USR1 0x94
+#define IMX_USR1_TRDY (1 << 13)
+
+#define IMX_USR2 0x98
+#define IMX_USR2_RDR (1 << 0)
+#define IMX_USR2_TXFE (1 << 14)
+#define IMX_USR2_TXDC (1 << 3)
+
+#define IMX_UTS 0xb4
+#define IMX_UTS_TXFULL (1 << 4)
+
+/*
+ * The base address of the uart registers.
+ *
+ * This is global so that it can be changed on the fly from the outside. For
+ * example, set imx_uart_base=physaddr and then call cninit() as the first two
+ * lines of initarm() and enjoy printf() availability through the tricky bits of
+ * startup. After initarm() switches from physical to virtual addressing, just
+ * set imx_uart_base=virtaddr and printf keeps working.
+ */
+uint32_t imx_uart_base = IMX_UART_BASE;
+
+/*
+ * uart related funcs
+ */
+static uint32_t
+ub_getreg(uint32_t off)
+{
+
+ return *((volatile uint32_t *)(imx_uart_base + off));
+}
+
+static void
+ub_setreg(uint32_t off, uint32_t val)
+{
+
+ *((volatile uint32_t *)(imx_uart_base + off)) = val;
+}
+
+static int
+ub_tstc(void)
+{
+
+ return ((ub_getreg(IMX_USR2) & IMX_USR2_RDR) ? 1 : 0);
+}
+
+static int
+ub_getc(void)
+{
+
+ while (!ub_tstc());
+ __asm __volatile("nop");
+
+ return (ub_getreg(IMX_RXD) & 0xff);
+}
+
+static void
+ub_putc(unsigned char c)
+{
+
+ if (c == '\n')
+ ub_putc('\r');
+
+ while (ub_getreg(IMX_UTS) & IMX_UTS_TXFULL)
+ __asm __volatile("nop");
+
+ ub_setreg(IMX_TXD, c);
+}
+
+static cn_probe_t uart_cnprobe;
+static cn_init_t uart_cninit;
+static cn_term_t uart_cnterm;
+static cn_getc_t uart_cngetc;
+static cn_putc_t uart_cnputc;
+static cn_grab_t uart_cngrab;
+static cn_ungrab_t uart_cnungrab;
+
+static void
+uart_cngrab(struct consdev *cp)
+{
+
+}
+
+static void
+uart_cnungrab(struct consdev *cp)
+{
+
+}
+
+static void
+uart_cnprobe(struct consdev *cp)
+{
+
+ sprintf(cp->cn_name, "uart");
+ cp->cn_pri = CN_NORMAL;
+}
+
+static void
+uart_cninit(struct consdev *cp)
+{
+
+ /* Init fifo trigger levels to 32 bytes, refclock div to 2. */
+ ub_setreg(IMX_UFCR, 0x00004210);
+}
+
+static void
+uart_cnputc(struct consdev *cp, int c)
+{
+
+ ub_putc(c);
+}
+
+static int
+uart_cngetc(struct consdev * cp)
+{
+
+ return ub_getc();
+}
+
+static void
+uart_cnterm(struct consdev * cp)
+{
+
+}
+
+CONSOLE_DRIVER(uart);
diff --git a/sys/arm/freescale/imx/imx_epit.c b/sys/arm/freescale/imx/imx_epit.c
new file mode 100644
index 000000000000..1295bc75c783
--- /dev/null
+++ b/sys/arm/freescale/imx/imx_epit.c
@@ -0,0 +1,491 @@
+/*-
+ * Copyright (c) 2017 Ian Lepore <ian@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Driver for imx Enhanced Programmable Interval Timer, a simple free-running
+ * counter device that can be used as the system timecounter. On imx5 a second
+ * instance of the device is used as the system eventtimer.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+#include <machine/machdep.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/freescale/imx/imx_ccmvar.h>
+#include <arm/freescale/imx/imx_machdep.h>
+
+#define EPIT_CR 0x00 /* Control register */
+#define EPIT_CR_CLKSRC_SHIFT 24
+#define EPIT_CR_CLKSRC_OFF 0
+#define EPIT_CR_CLKSRC_IPG 1
+#define EPIT_CR_CLKSRC_HFCLK 2
+#define EPIT_CR_CLKSRC_LFCLK 3
+#define EPIT_CR_STOPEN (1u << 21)
+#define EPIT_CR_WAITEN (1u << 19)
+#define EPIT_CR_DBGEN (1u << 18)
+#define EPIT_CR_IOVW (1u << 17)
+#define EPIT_CR_SWR (1u << 16)
+#define EPIT_CR_RLD (1u << 3)
+#define EPIT_CR_OCIEN (1u << 2)
+#define EPIT_CR_ENMOD (1u << 1)
+#define EPIT_CR_EN (1u << 0)
+
+#define EPIT_SR 0x04 /* Status register */
+#define EPIT_SR_OCIF (1u << 0)
+
+#define EPIT_LR 0x08 /* Load register */
+#define EPIT_CMPR 0x0c /* Compare register */
+#define EPIT_CNR 0x10 /* Counter register */
+
+/*
+ * Define event timer limits.
+ *
+ * In theory our minimum period is 1 tick, because to setup a oneshot we don't
+ * need a read-modify-write sequence to calculate and set a compare register
+ * value while the counter is running. In practice the waveform diagrams in the
+ * manual make it appear that a setting of 1 might cause it to miss the event,
+ * so I'm setting the lower limit to 2 ticks.
+ */
+#define ET_MIN_TICKS 2
+#define ET_MAX_TICKS 0xfffffffe
+
+static u_int epit_tc_get_timecount(struct timecounter *tc);
+
+struct epit_softc {
+ device_t dev;
+ struct resource * memres;
+ struct resource * intres;
+ void * inthandle;
+ uint32_t clkfreq;
+ uint32_t ctlreg;
+ uint32_t period;
+ struct timecounter tc;
+ struct eventtimer et;
+ bool oneshot;
+};
+
+/*
+ * Probe data. For some reason, the standard linux dts files don't have
+ * compatible properties on the epit devices (other properties are missing too,
+ * like clocks, but we don't care as much about that). So our probe routine
+ * uses the name of the node (must contain "epit") and the address of the
+ * registers as identifying marks.
+ */
+static const uint32_t imx51_epit_ioaddr[2] = {0x73fac000, 0x73fb0000};
+static const uint32_t imx53_epit_ioaddr[2] = {0x53fac000, 0x53fb0000};
+static const uint32_t imx6_epit_ioaddr[2] = {0x020d0000, 0x020d4000};
+
+/* ocd_data is number of units to instantiate on the platform */
+static struct ofw_compat_data compat_data[] = {
+ {"fsl,imx6ul-epit", 1},
+ {"fsl,imx6sx-epit", 1},
+ {"fsl,imx6q-epit", 1},
+ {"fsl,imx6dl-epit", 1},
+ {"fsl,imx53-epit", 2},
+ {"fsl,imx51-epit", 2},
+ {"fsl,imx31-epit", 2},
+ {"fsl,imx27-epit", 2},
+ {"fsl,imx25-epit", 2},
+ {NULL, 0}
+};
+
+static inline uint32_t
+RD4(struct epit_softc *sc, bus_size_t offset)
+{
+
+ return (bus_read_4(sc->memres, offset));
+}
+
+static inline void
+WR4(struct epit_softc *sc, bus_size_t offset, uint32_t value)
+{
+
+ bus_write_4(sc->memres, offset, value);
+}
+
+static inline void
+WR4B(struct epit_softc *sc, bus_size_t offset, uint32_t value)
+{
+
+ bus_write_4(sc->memres, offset, value);
+ bus_barrier(sc->memres, offset, 4, BUS_SPACE_BARRIER_WRITE);
+}
+
+static u_int
+epit_read_counter(struct epit_softc *sc)
+{
+
+ /*
+ * Hardware is a downcounter, adjust to look like it counts up for use
+ * with timecounter and DELAY.
+ */
+ return (0xffffffff - RD4(sc, EPIT_CNR));
+}
+
+static void
+epit_do_delay(int usec, void *arg)
+{
+ struct epit_softc *sc = arg;
+ uint64_t curcnt, endcnt, startcnt, ticks;
+
+ /*
+ * Calculate the tick count with 64-bit values so that it works for any
+ * clock frequency. Loop until the hardware count reaches start+ticks.
+ * If the 32-bit hardware count rolls over while we're looping, just
+ * manually do a carry into the high bits after each read; don't worry
+ * that doing this on each loop iteration is inefficient -- we're trying
+ * to waste time here.
+ */
+ ticks = 1 + ((uint64_t)usec * sc->clkfreq) / 1000000;
+ curcnt = startcnt = epit_read_counter(sc);
+ endcnt = startcnt + ticks;
+ while (curcnt < endcnt) {
+ curcnt = epit_read_counter(sc);
+ if (curcnt < startcnt)
+ curcnt += 1ULL << 32;
+ }
+}
+
+static u_int
+epit_tc_get_timecount(struct timecounter *tc)
+{
+
+ return (epit_read_counter(tc->tc_priv));
+}
+
+static int
+epit_tc_attach(struct epit_softc *sc)
+{
+
+ /* When the counter hits zero, reload with 0xffffffff. Start it. */
+ WR4(sc, EPIT_LR, 0xffffffff);
+ WR4(sc, EPIT_CR, sc->ctlreg | EPIT_CR_EN);
+
+ /* Register as a timecounter. */
+ sc->tc.tc_name = "EPIT";
+ sc->tc.tc_quality = 1000;
+ sc->tc.tc_frequency = sc->clkfreq;
+ sc->tc.tc_counter_mask = 0xffffffff;
+ sc->tc.tc_get_timecount = epit_tc_get_timecount;
+ sc->tc.tc_priv = sc;
+ tc_init(&sc->tc);
+
+ /* We are the DELAY() implementation. */
+ arm_set_delay(epit_do_delay, sc);
+
+ return (0);
+}
+
+static int
+epit_et_start(struct eventtimer *et, sbintime_t first, sbintime_t period)
+{
+ struct epit_softc *sc;
+ uint32_t ticks;
+
+ sc = (struct epit_softc *)et->et_priv;
+
+ /*
+ * Disable the timer and clear any pending status. The timer may be
+ * running or may have just expired if we're called to reschedule the
+ * next event before the previous event time arrives.
+ */
+ WR4(sc, EPIT_CR, sc->ctlreg);
+ WR4(sc, EPIT_SR, EPIT_SR_OCIF);
+ if (period != 0) {
+ sc->oneshot = false;
+ ticks = ((uint32_t)et->et_frequency * period) >> 32;
+ } else if (first != 0) {
+ sc->oneshot = true;
+ ticks = ((uint32_t)et->et_frequency * first) >> 32;
+ } else {
+ return (EINVAL);
+ }
+
+ /* Set the countdown load register and start the timer. */
+ WR4(sc, EPIT_LR, ticks);
+ WR4B(sc, EPIT_CR, sc->ctlreg | EPIT_CR_EN);
+
+ return (0);
+}
+
+static int
+epit_et_stop(struct eventtimer *et)
+{
+ struct epit_softc *sc;
+
+ sc = (struct epit_softc *)et->et_priv;
+
+ /* Disable the timer and clear any pending status. */
+ WR4(sc, EPIT_CR, sc->ctlreg);
+ WR4B(sc, EPIT_SR, EPIT_SR_OCIF);
+
+ return (0);
+}
+
+static int
+epit_intr(void *arg)
+{
+ struct epit_softc *sc;
+ uint32_t status;
+
+ sc = arg;
+
+ /*
+ * Disable a one-shot timer until a new event is scheduled so that the
+ * counter doesn't wrap and fire again. Do this before clearing the
+ * status since a short period would make it fire again really soon.
+ *
+ * Clear interrupt status before invoking event callbacks. The callback
+ * often sets up a new one-shot timer event and if the interval is short
+ * enough it can fire before we get out of this function. If we cleared
+ * at the bottom we'd miss the interrupt and hang until the clock wraps.
+ */
+ if (sc->oneshot)
+ WR4(sc, EPIT_CR, sc->ctlreg);
+
+ status = RD4(sc, EPIT_SR);
+ WR4B(sc, EPIT_SR, status);
+
+ if ((status & EPIT_SR_OCIF) == 0)
+ return (FILTER_STRAY);
+
+ if (sc->et.et_active)
+ sc->et.et_event_cb(&sc->et, sc->et.et_arg);
+
+ return (FILTER_HANDLED);
+}
+
+static int
+epit_et_attach(struct epit_softc *sc)
+{
+ int err, rid;
+
+ rid = 0;
+ sc->intres = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (sc->intres == NULL) {
+ device_printf(sc->dev, "could not allocate interrupt\n");
+ return (ENXIO);
+ }
+
+ err = bus_setup_intr(sc->dev, sc->intres, INTR_TYPE_CLK | INTR_MPSAFE,
+ epit_intr, NULL, sc, &sc->inthandle);
+ if (err != 0) {
+ device_printf(sc->dev, "unable to setup the irq handler\n");
+ return (err);
+ }
+
+ /* To be an eventtimer, we need interrupts enabled. */
+ sc->ctlreg |= EPIT_CR_OCIEN;
+
+ /* Register as an eventtimer. */
+ sc->et.et_name = "EPIT";
+ sc->et.et_flags = ET_FLAGS_ONESHOT | ET_FLAGS_PERIODIC;
+ sc->et.et_quality = 1000;
+ sc->et.et_frequency = sc->clkfreq;
+ sc->et.et_min_period = ((uint64_t)ET_MIN_TICKS << 32) / sc->clkfreq;
+ sc->et.et_max_period = ((uint64_t)ET_MAX_TICKS << 32) / sc->clkfreq;
+ sc->et.et_start = epit_et_start;
+ sc->et.et_stop = epit_et_stop;
+ sc->et.et_priv = sc;
+ et_register(&sc->et);
+
+ return (0);
+}
+
+static int
+epit_probe(device_t dev)
+{
+ struct resource *memres;
+ rman_res_t ioaddr;
+ int num_units, rid, unit;
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ /*
+ * The FDT data for imx5 and imx6 EPIT hardware is missing or broken,
+ * but it may get fixed some day, so first just do a normal check. We
+ * return success if the compatible string matches and we haven't
+ * already instantiated the number of units needed on this platform.
+ */
+ unit = device_get_unit(dev);
+ num_units = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+ if (unit < num_units) {
+ device_set_desc(dev, "i.MX EPIT timer");
+ return (BUS_PROBE_DEFAULT);
+ }
+
+ /*
+ * No compat string match, but for imx6 all the data we need is in the
+ * node except the compat string, so do our own compatibility check
+ * using the device name of the node and the register block address.
+ */
+ if (strstr(ofw_bus_get_name(dev), "epit") == NULL)
+ return (ENXIO);
+
+ rid = 0;
+ memres = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_UNMAPPED);
+ if (memres == NULL)
+ return (ENXIO);
+ ioaddr = rman_get_start(memres);
+ bus_free_resource(dev, SYS_RES_MEMORY, memres);
+
+ if (imx_soc_family() == 6) {
+ if (unit > 0)
+ return (ENXIO);
+ if (ioaddr != imx6_epit_ioaddr[unit])
+ return (ENXIO);
+ } else {
+ if (unit > 1)
+ return (ENXIO);
+ switch (imx_soc_type()) {
+ case IMXSOC_51:
+ if (ioaddr != imx51_epit_ioaddr[unit])
+ return (ENXIO);
+ break;
+ case IMXSOC_53:
+ if (ioaddr != imx53_epit_ioaddr[unit])
+ return (ENXIO);
+ break;
+ default:
+ return (ENXIO);
+ }
+ /*
+ * XXX Right now we have no way to handle the fact that the
+ * entire EPIT node is missing, which means no interrupt data.
+ */
+ return (ENXIO);
+ }
+
+ device_set_desc(dev, "i.MX EPIT timer");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+epit_attach(device_t dev)
+{
+ struct epit_softc *sc;
+ int err, rid;
+ uint32_t clksrc;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ rid = 0;
+ sc->memres = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->memres == NULL) {
+ device_printf(sc->dev, "could not allocate registers\n");
+ return (ENXIO);
+ }
+
+ /*
+ * For now, use ipg (66 MHz). Some day we should get this from fdt.
+ */
+ clksrc = EPIT_CR_CLKSRC_IPG;
+
+ switch (clksrc) {
+ default:
+ device_printf(dev,
+ "Unsupported clock source '%d', using IPG\n", clksrc);
+ /* FALLTHROUGH */
+ case EPIT_CR_CLKSRC_IPG:
+ sc->clkfreq = imx_ccm_ipg_hz();
+ break;
+ case EPIT_CR_CLKSRC_HFCLK:
+ sc->clkfreq = imx_ccm_perclk_hz();
+ break;
+ case EPIT_CR_CLKSRC_LFCLK:
+ sc->clkfreq = 32768;
+ break;
+ }
+
+ /*
+ * Init: stop operations and clear all options, then set up options and
+ * clock source, then do a soft-reset and wait for it to complete.
+ */
+ WR4(sc, EPIT_CR, 0);
+
+ sc->ctlreg =
+ (clksrc << EPIT_CR_CLKSRC_SHIFT) | /* Use selected clock */
+ EPIT_CR_ENMOD | /* Reload counter on enable */
+ EPIT_CR_RLD | /* Reload counter from LR */
+ EPIT_CR_STOPEN | /* Run in STOP mode */
+ EPIT_CR_WAITEN | /* Run in WAIT mode */
+ EPIT_CR_DBGEN; /* Run in DEBUG mode */
+
+ WR4B(sc, EPIT_CR, sc->ctlreg | EPIT_CR_SWR);
+ while (RD4(sc, EPIT_CR) & EPIT_CR_SWR)
+ continue;
+
+ /*
+ * Unit 0 is the timecounter, 1 (if instantiated) is the eventtimer.
+ */
+ if (device_get_unit(sc->dev) == 0)
+ err = epit_tc_attach(sc);
+ else
+ err = epit_et_attach(sc);
+
+ return (err);
+}
+
+static device_method_t epit_methods[] = {
+ DEVMETHOD(device_probe, epit_probe),
+ DEVMETHOD(device_attach, epit_attach),
+
+ DEVMETHOD_END
+};
+
+static driver_t epit_driver = {
+ "imx_epit",
+ epit_methods,
+ sizeof(struct epit_softc),
+};
+
+static devclass_t epit_devclass;
+
+EARLY_DRIVER_MODULE(imx_epit, simplebus, epit_driver, epit_devclass, 0,
+ 0, BUS_PASS_TIMER);
diff --git a/sys/arm/freescale/imx/imx_gpio.c b/sys/arm/freescale/imx/imx_gpio.c
new file mode 100644
index 000000000000..8324d8daa30c
--- /dev/null
+++ b/sys/arm/freescale/imx/imx_gpio.c
@@ -0,0 +1,959 @@
+/*-
+ * Copyright (c) 2012, 2013 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Oleksandr Rybalko under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Freescale i.MX515 GPIO driver.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/gpio.h>
+#include <sys/proc.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+#include <machine/resource.h>
+
+#include <dev/gpio/gpiobusvar.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#if defined(EXT_RESOURCES) && defined(__aarch64__)
+#define IMX_ENABLE_CLOCKS
+#endif
+
+#ifdef IMX_ENABLE_CLOCKS
+#include <dev/extres/clk/clk.h>
+#endif
+
+#include "gpio_if.h"
+
+#ifdef INTRNG
+#include "pic_if.h"
+#endif
+
+#define WRITE4(_sc, _r, _v) \
+ bus_space_write_4((_sc)->sc_iot, (_sc)->sc_ioh, (_r), (_v))
+#define READ4(_sc, _r) \
+ bus_space_read_4((_sc)->sc_iot, (_sc)->sc_ioh, (_r))
+#define SET4(_sc, _r, _m) \
+ WRITE4((_sc), (_r), READ4((_sc), (_r)) | (_m))
+#define CLEAR4(_sc, _r, _m) \
+ WRITE4((_sc), (_r), READ4((_sc), (_r)) & ~(_m))
+
+/* Registers definition for Freescale i.MX515 GPIO controller */
+
+#define IMX_GPIO_DR_REG 0x000 /* Pin Data */
+#define IMX_GPIO_OE_REG 0x004 /* Set Pin Output */
+#define IMX_GPIO_PSR_REG 0x008 /* Pad Status */
+#define IMX_GPIO_ICR1_REG 0x00C /* Interrupt Configuration */
+#define IMX_GPIO_ICR2_REG 0x010 /* Interrupt Configuration */
+#define GPIO_ICR_COND_LOW 0
+#define GPIO_ICR_COND_HIGH 1
+#define GPIO_ICR_COND_RISE 2
+#define GPIO_ICR_COND_FALL 3
+#define GPIO_ICR_COND_MASK 0x3
+#define IMX_GPIO_IMR_REG 0x014 /* Interrupt Mask Register */
+#define IMX_GPIO_ISR_REG 0x018 /* Interrupt Status Register */
+#define IMX_GPIO_EDGE_REG 0x01C /* Edge Detect Register */
+
+#ifdef INTRNG
+#define DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \
+ GPIO_INTR_LEVEL_LOW | GPIO_INTR_LEVEL_HIGH | GPIO_INTR_EDGE_RISING | \
+ GPIO_INTR_EDGE_FALLING | GPIO_INTR_EDGE_BOTH)
+#else
+#define DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)
+#endif
+
+#define NGPIO 32
+
+#ifdef INTRNG
+struct gpio_irqsrc {
+ struct intr_irqsrc gi_isrc;
+ u_int gi_irq;
+ uint32_t gi_mode;
+};
+#endif
+
+struct imx51_gpio_softc {
+ device_t dev;
+ device_t sc_busdev;
+ struct mtx sc_mtx;
+ struct resource *sc_res[3]; /* 1 x mem, 2 x IRQ */
+ void *gpio_ih[2];
+ bus_space_tag_t sc_iot;
+ bus_space_handle_t sc_ioh;
+ int gpio_npins;
+ struct gpio_pin gpio_pins[NGPIO];
+#ifdef INTRNG
+ struct gpio_irqsrc gpio_pic_irqsrc[NGPIO];
+#endif
+#ifdef IMX_ENABLE_CLOCKS
+ clk_t clk;
+#endif
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"fsl,imx8mq-gpio", 1},
+ {"fsl,imx6q-gpio", 1},
+ {"fsl,imx53-gpio", 1},
+ {"fsl,imx51-gpio", 1},
+ {NULL, 0}
+};
+
+static struct resource_spec imx_gpio_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 1, RF_ACTIVE },
+ { -1, 0 }
+};
+#define FIRST_IRQRES 1
+#define NUM_IRQRES 2
+
+/*
+ * Helpers
+ */
+static void imx51_gpio_pin_configure(struct imx51_gpio_softc *,
+ struct gpio_pin *, uint32_t);
+
+/*
+ * Driver stuff
+ */
+static int imx51_gpio_probe(device_t);
+static int imx51_gpio_attach(device_t);
+static int imx51_gpio_detach(device_t);
+
+/*
+ * GPIO interface
+ */
+static device_t imx51_gpio_get_bus(device_t);
+static int imx51_gpio_pin_max(device_t, int *);
+static int imx51_gpio_pin_getcaps(device_t, uint32_t, uint32_t *);
+static int imx51_gpio_pin_getflags(device_t, uint32_t, uint32_t *);
+static int imx51_gpio_pin_getname(device_t, uint32_t, char *);
+static int imx51_gpio_pin_setflags(device_t, uint32_t, uint32_t);
+static int imx51_gpio_pin_set(device_t, uint32_t, unsigned int);
+static int imx51_gpio_pin_get(device_t, uint32_t, unsigned int *);
+static int imx51_gpio_pin_toggle(device_t, uint32_t pin);
+
+#ifdef INTRNG
+static int
+gpio_pic_map_fdt(struct imx51_gpio_softc *sc, struct intr_map_data_fdt *daf,
+ u_int *irqp, uint32_t *modep)
+{
+ u_int irq;
+ uint32_t mode;
+
+ /*
+ * From devicetree/bindings/gpio/fsl-imx-gpio.txt:
+ * #interrupt-cells: 2. The first cell is the GPIO number. The second
+ * cell bits[3:0] is used to specify trigger type and level flags:
+ * 1 = low-to-high edge triggered.
+ * 2 = high-to-low edge triggered.
+ * 4 = active high level-sensitive.
+ * 8 = active low level-sensitive.
+ * We can do any single one of these modes, and also edge low+high
+ * (i.e., trigger on both edges); other combinations are not supported.
+ */
+
+ if (daf->ncells != 2) {
+ device_printf(sc->dev, "Invalid #interrupt-cells\n");
+ return (EINVAL);
+ }
+
+ irq = daf->cells[0];
+ if (irq >= sc->gpio_npins) {
+ device_printf(sc->dev, "Invalid interrupt number %u\n", irq);
+ return (EINVAL);
+ }
+ switch (daf->cells[1]) {
+ case 1:
+ mode = GPIO_INTR_EDGE_RISING;
+ break;
+ case 2:
+ mode = GPIO_INTR_EDGE_FALLING;
+ break;
+ case 3:
+ mode = GPIO_INTR_EDGE_BOTH;
+ break;
+ case 4:
+ mode = GPIO_INTR_LEVEL_HIGH;
+ break;
+ case 8:
+ mode = GPIO_INTR_LEVEL_LOW;
+ break;
+ default:
+ device_printf(sc->dev, "Unsupported interrupt mode 0x%2x\n",
+ daf->cells[1]);
+ return (ENOTSUP);
+ }
+ *irqp = irq;
+ if (modep != NULL)
+ *modep = mode;
+ return (0);
+}
+
+static int
+gpio_pic_map_gpio(struct imx51_gpio_softc *sc, struct intr_map_data_gpio *dag,
+ u_int *irqp, uint32_t *modep)
+{
+ u_int irq;
+
+ irq = dag->gpio_pin_num;
+ if (irq >= sc->gpio_npins) {
+ device_printf(sc->dev, "Invalid interrupt number %u\n", irq);
+ return (EINVAL);
+ }
+
+ switch (dag->gpio_intr_mode) {
+ case GPIO_INTR_LEVEL_LOW:
+ case GPIO_INTR_LEVEL_HIGH:
+ case GPIO_INTR_EDGE_RISING:
+ case GPIO_INTR_EDGE_FALLING:
+ case GPIO_INTR_EDGE_BOTH:
+ break;
+ default:
+ device_printf(sc->dev, "Unsupported interrupt mode 0x%8x\n",
+ dag->gpio_intr_mode);
+ return (EINVAL);
+ }
+
+ *irqp = irq;
+ if (modep != NULL)
+ *modep = dag->gpio_intr_mode;
+ return (0);
+}
+
+static int
+gpio_pic_map(struct imx51_gpio_softc *sc, struct intr_map_data *data,
+ u_int *irqp, uint32_t *modep)
+{
+
+ switch (data->type) {
+ case INTR_MAP_DATA_FDT:
+ return (gpio_pic_map_fdt(sc, (struct intr_map_data_fdt *)data,
+ irqp, modep));
+ case INTR_MAP_DATA_GPIO:
+ return (gpio_pic_map_gpio(sc, (struct intr_map_data_gpio *)data,
+ irqp, modep));
+ default:
+ return (ENOTSUP);
+ }
+}
+
+static int
+gpio_pic_map_intr(device_t dev, struct intr_map_data *data,
+ struct intr_irqsrc **isrcp)
+{
+ int error;
+ u_int irq;
+ struct imx51_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+ error = gpio_pic_map(sc, data, &irq, NULL);
+ if (error == 0)
+ *isrcp = &sc->gpio_pic_irqsrc[irq].gi_isrc;
+ return (error);
+}
+
+static int
+gpio_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ struct imx51_gpio_softc *sc;
+ struct gpio_irqsrc *gi;
+
+ sc = device_get_softc(dev);
+ if (isrc->isrc_handlers == 0) {
+ gi = (struct gpio_irqsrc *)isrc;
+ gi->gi_mode = GPIO_INTR_CONFORM;
+
+ // XXX Not sure this is necessary
+ mtx_lock_spin(&sc->sc_mtx);
+ CLEAR4(sc, IMX_GPIO_IMR_REG, (1U << gi->gi_irq));
+ WRITE4(sc, IMX_GPIO_ISR_REG, (1U << gi->gi_irq));
+ mtx_unlock_spin(&sc->sc_mtx);
+ }
+ return (0);
+}
+
+static int
+gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ struct imx51_gpio_softc *sc;
+ struct gpio_irqsrc *gi;
+ int error;
+ u_int icfg, irq, reg, shift, wrk;
+ uint32_t mode;
+
+ if (data == NULL)
+ return (ENOTSUP);
+
+ sc = device_get_softc(dev);
+ gi = (struct gpio_irqsrc *)isrc;
+
+ /* Get config for interrupt. */
+ error = gpio_pic_map(sc, data, &irq, &mode);
+ if (error != 0)
+ return (error);
+ if (gi->gi_irq != irq)
+ return (EINVAL);
+
+ /* Compare config if this is not first setup. */
+ if (isrc->isrc_handlers != 0)
+ return (gi->gi_mode == mode ? 0 : EINVAL);
+ gi->gi_mode = mode;
+
+ /*
+ * To interrupt on both edges we have to use the EDGE register. The
+ * manual says it only exists for backwards compatibilty with older imx
+ * chips, but it's also the only way to configure interrupting on both
+ * edges. If the EDGE bit is on, the corresponding ICRn bit is ignored.
+ */
+ mtx_lock_spin(&sc->sc_mtx);
+ if (mode == GPIO_INTR_EDGE_BOTH) {
+ SET4(sc, IMX_GPIO_EDGE_REG, (1u << irq));
+ } else {
+ CLEAR4(sc, IMX_GPIO_EDGE_REG, (1u << irq));
+ switch (mode) {
+ default:
+ /* silence warnings; default can't actually happen. */
+ /* FALLTHROUGH */
+ case GPIO_INTR_LEVEL_LOW:
+ icfg = GPIO_ICR_COND_LOW;
+ break;
+ case GPIO_INTR_LEVEL_HIGH:
+ icfg = GPIO_ICR_COND_HIGH;
+ break;
+ case GPIO_INTR_EDGE_RISING:
+ icfg = GPIO_ICR_COND_RISE;
+ break;
+ case GPIO_INTR_EDGE_FALLING:
+ icfg = GPIO_ICR_COND_FALL;
+ break;
+ }
+ if (irq < 16) {
+ reg = IMX_GPIO_ICR1_REG;
+ shift = 2 * irq;
+ } else {
+ reg = IMX_GPIO_ICR2_REG;
+ shift = 2 * (irq - 16);
+ }
+ wrk = READ4(sc, reg);
+ wrk &= ~(GPIO_ICR_COND_MASK << shift);
+ wrk |= icfg << shift;
+ WRITE4(sc, reg, wrk);
+ }
+ WRITE4(sc, IMX_GPIO_ISR_REG, (1u << irq));
+ SET4(sc, IMX_GPIO_IMR_REG, (1u << irq));
+ mtx_unlock_spin(&sc->sc_mtx);
+
+ return (0);
+}
+
+/*
+ * this is mask_intr
+ */
+static void
+gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct imx51_gpio_softc *sc;
+ u_int irq;
+
+ sc = device_get_softc(dev);
+ irq = ((struct gpio_irqsrc *)isrc)->gi_irq;
+
+ mtx_lock_spin(&sc->sc_mtx);
+ CLEAR4(sc, IMX_GPIO_IMR_REG, (1U << irq));
+ mtx_unlock_spin(&sc->sc_mtx);
+}
+
+/*
+ * this is unmask_intr
+ */
+static void
+gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct imx51_gpio_softc *sc;
+ u_int irq;
+
+ sc = device_get_softc(dev);
+ irq = ((struct gpio_irqsrc *)isrc)->gi_irq;
+
+ mtx_lock_spin(&sc->sc_mtx);
+ SET4(sc, IMX_GPIO_IMR_REG, (1U << irq));
+ mtx_unlock_spin(&sc->sc_mtx);
+}
+
+static void
+gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct imx51_gpio_softc *sc;
+ u_int irq;
+
+ sc = device_get_softc(dev);
+ irq = ((struct gpio_irqsrc *)isrc)->gi_irq;
+
+ arm_irq_memory_barrier(0);
+ /* EOI. W1C reg so no r-m-w, no locking needed. */
+ WRITE4(sc, IMX_GPIO_ISR_REG, (1U << irq));
+}
+
+static void
+gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct imx51_gpio_softc *sc;
+ u_int irq;
+
+ sc = device_get_softc(dev);
+ irq = ((struct gpio_irqsrc *)isrc)->gi_irq;
+
+ arm_irq_memory_barrier(0);
+ /* EOI. W1C reg so no r-m-w, no locking needed. */
+ WRITE4(sc, IMX_GPIO_ISR_REG, (1U << irq));
+ gpio_pic_enable_intr(dev, isrc);
+}
+
+static void
+gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+ gpio_pic_disable_intr(dev, isrc);
+}
+
+static int
+gpio_pic_filter(void *arg)
+{
+ struct imx51_gpio_softc *sc;
+ struct intr_irqsrc *isrc;
+ uint32_t i, interrupts;
+
+ sc = arg;
+ mtx_lock_spin(&sc->sc_mtx);
+ interrupts = READ4(sc, IMX_GPIO_ISR_REG) & READ4(sc, IMX_GPIO_IMR_REG);
+ mtx_unlock_spin(&sc->sc_mtx);
+
+ for (i = 0; interrupts != 0; i++, interrupts >>= 1) {
+ if ((interrupts & 0x1) == 0)
+ continue;
+ isrc = &sc->gpio_pic_irqsrc[i].gi_isrc;
+ if (intr_isrc_dispatch(isrc, curthread->td_intr_frame) != 0) {
+ gpio_pic_disable_intr(sc->dev, isrc);
+ gpio_pic_post_filter(sc->dev, isrc);
+ device_printf(sc->dev, "Stray irq %u disabled\n", i);
+ }
+ }
+
+ return (FILTER_HANDLED);
+}
+
+/*
+ * Initialize our isrcs and register them with intrng.
+ */
+static int
+gpio_pic_register_isrcs(struct imx51_gpio_softc *sc)
+{
+ int error;
+ uint32_t irq;
+ const char *name;
+
+ name = device_get_nameunit(sc->dev);
+ for (irq = 0; irq < NGPIO; irq++) {
+ sc->gpio_pic_irqsrc[irq].gi_irq = irq;
+ sc->gpio_pic_irqsrc[irq].gi_mode = GPIO_INTR_CONFORM;
+
+ error = intr_isrc_register(&sc->gpio_pic_irqsrc[irq].gi_isrc,
+ sc->dev, 0, "%s,%u", name, irq);
+ if (error != 0) {
+ /* XXX call intr_isrc_deregister() */
+ device_printf(sc->dev, "%s failed", __func__);
+ return (error);
+ }
+ }
+ return (0);
+}
+#endif
+
+/*
+ *
+ */
+static void
+imx51_gpio_pin_configure(struct imx51_gpio_softc *sc, struct gpio_pin *pin,
+ unsigned int flags)
+{
+ u_int newflags, pad;
+
+ mtx_lock_spin(&sc->sc_mtx);
+
+ /*
+ * Manage input/output; other flags not supported yet (maybe not ever,
+ * since we have no connection to the pad config registers from here).
+ *
+ * When setting a pin to output, honor the PRESET_[LOW,HIGH] flags if
+ * present. Otherwise, for glitchless transistions on pins with pulls,
+ * read the current state of the pad and preset the DR register to drive
+ * the current value onto the pin before enabling the pin for output.
+ *
+ * Note that changes to pin->gp_flags must be acccumulated in newflags
+ * and stored with a single writeback to gp_flags at the end, to enable
+ * unlocked reads of that value elsewhere. This is only about unlocked
+ * access to gp_flags from elsewhere; we still use locking in this
+ * function to protect r-m-w access to the hardware registers.
+ */
+ if (flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) {
+ newflags = pin->gp_flags & ~(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT);
+ if (flags & GPIO_PIN_OUTPUT) {
+ if (flags & GPIO_PIN_PRESET_LOW) {
+ pad = 0;
+ } else if (flags & GPIO_PIN_PRESET_HIGH) {
+ pad = 1;
+ } else {
+ if (flags & GPIO_PIN_OPENDRAIN)
+ pad = READ4(sc, IMX_GPIO_PSR_REG);
+ else
+ pad = READ4(sc, IMX_GPIO_DR_REG);
+ pad = (pad >> pin->gp_pin) & 1;
+ }
+ newflags |= GPIO_PIN_OUTPUT;
+ SET4(sc, IMX_GPIO_DR_REG, (pad << pin->gp_pin));
+ SET4(sc, IMX_GPIO_OE_REG, (1U << pin->gp_pin));
+ } else {
+ newflags |= GPIO_PIN_INPUT;
+ CLEAR4(sc, IMX_GPIO_OE_REG, (1U << pin->gp_pin));
+ }
+ pin->gp_flags = newflags;
+ }
+
+ mtx_unlock_spin(&sc->sc_mtx);
+}
+
+static device_t
+imx51_gpio_get_bus(device_t dev)
+{
+ struct imx51_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ return (sc->sc_busdev);
+}
+
+static int
+imx51_gpio_pin_max(device_t dev, int *maxpin)
+{
+ struct imx51_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+ *maxpin = sc->gpio_npins - 1;
+
+ return (0);
+}
+
+static int
+imx51_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
+{
+ struct imx51_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (pin >= sc->gpio_npins)
+ return (EINVAL);
+
+ *caps = sc->gpio_pins[pin].gp_caps;
+
+ return (0);
+}
+
+static int
+imx51_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
+{
+ struct imx51_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (pin >= sc->gpio_npins)
+ return (EINVAL);
+
+ *flags = sc->gpio_pins[pin].gp_flags;
+
+ return (0);
+}
+
+static int
+imx51_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
+{
+ struct imx51_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->gpio_npins)
+ return (EINVAL);
+
+ mtx_lock_spin(&sc->sc_mtx);
+ memcpy(name, sc->gpio_pins[pin].gp_name, GPIOMAXNAME);
+ mtx_unlock_spin(&sc->sc_mtx);
+
+ return (0);
+}
+
+static int
+imx51_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
+{
+ struct imx51_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (pin >= sc->gpio_npins)
+ return (EINVAL);
+
+ imx51_gpio_pin_configure(sc, &sc->gpio_pins[pin], flags);
+
+ return (0);
+}
+
+static int
+imx51_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
+{
+ struct imx51_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (pin >= sc->gpio_npins)
+ return (EINVAL);
+
+ mtx_lock_spin(&sc->sc_mtx);
+ if (value)
+ SET4(sc, IMX_GPIO_DR_REG, (1U << pin));
+ else
+ CLEAR4(sc, IMX_GPIO_DR_REG, (1U << pin));
+ mtx_unlock_spin(&sc->sc_mtx);
+
+ return (0);
+}
+
+static int
+imx51_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
+{
+ struct imx51_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (pin >= sc->gpio_npins)
+ return (EINVAL);
+
+ /*
+ * Normally a pin set for output can be read by reading the DR reg which
+ * indicates what value is being driven to that pin. The exception is
+ * pins configured for open-drain mode, in which case we have to read
+ * the pad status register in case the pin is being driven externally.
+ * Doing so requires that the SION bit be configured in pinmux, which
+ * isn't the case for most normal gpio pins, so only try to read via PSR
+ * if the OPENDRAIN flag is set, and it's the user's job to correctly
+ * configure SION along with open-drain output mode for those pins.
+ */
+ if (sc->gpio_pins[pin].gp_flags & GPIO_PIN_OPENDRAIN)
+ *val = (READ4(sc, IMX_GPIO_PSR_REG) >> pin) & 1;
+ else
+ *val = (READ4(sc, IMX_GPIO_DR_REG) >> pin) & 1;
+
+ return (0);
+}
+
+static int
+imx51_gpio_pin_toggle(device_t dev, uint32_t pin)
+{
+ struct imx51_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (pin >= sc->gpio_npins)
+ return (EINVAL);
+
+ mtx_lock_spin(&sc->sc_mtx);
+ WRITE4(sc, IMX_GPIO_DR_REG,
+ (READ4(sc, IMX_GPIO_DR_REG) ^ (1U << pin)));
+ mtx_unlock_spin(&sc->sc_mtx);
+
+ return (0);
+}
+
+static int
+imx51_gpio_pin_access_32(device_t dev, uint32_t first_pin, uint32_t clear_pins,
+ uint32_t change_pins, uint32_t *orig_pins)
+{
+ struct imx51_gpio_softc *sc;
+
+ if (first_pin != 0)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+
+ if (orig_pins != NULL)
+ *orig_pins = READ4(sc, IMX_GPIO_DR_REG);
+
+ if ((clear_pins | change_pins) != 0) {
+ mtx_lock_spin(&sc->sc_mtx);
+ WRITE4(sc, IMX_GPIO_DR_REG,
+ (READ4(sc, IMX_GPIO_DR_REG) & ~clear_pins) ^ change_pins);
+ mtx_unlock_spin(&sc->sc_mtx);
+ }
+
+ return (0);
+}
+
+static int
+imx51_gpio_pin_config_32(device_t dev, uint32_t first_pin, uint32_t num_pins,
+ uint32_t *pin_flags)
+{
+ struct imx51_gpio_softc *sc;
+ u_int i;
+ uint32_t bit, drclr, drset, flags, oeclr, oeset, pads;
+
+ sc = device_get_softc(dev);
+
+ if (first_pin != 0 || num_pins > sc->gpio_npins)
+ return (EINVAL);
+
+ drclr = drset = oeclr = oeset = 0;
+ pads = READ4(sc, IMX_GPIO_DR_REG);
+
+ for (i = 0; i < num_pins; ++i) {
+ bit = 1u << i;
+ flags = pin_flags[i];
+ if (flags & GPIO_PIN_INPUT) {
+ oeclr |= bit;
+ } else if (flags & GPIO_PIN_OUTPUT) {
+ oeset |= bit;
+ if (flags & GPIO_PIN_PRESET_LOW)
+ drclr |= bit;
+ else if (flags & GPIO_PIN_PRESET_HIGH)
+ drset |= bit;
+ else /* Drive whatever it's now pulled to. */
+ drset |= pads & bit;
+ }
+ }
+
+ mtx_lock_spin(&sc->sc_mtx);
+ WRITE4(sc, IMX_GPIO_DR_REG,
+ (READ4(sc, IMX_GPIO_DR_REG) & ~drclr) | drset);
+ WRITE4(sc, IMX_GPIO_OE_REG,
+ (READ4(sc, IMX_GPIO_OE_REG) & ~oeclr) | oeset);
+ mtx_unlock_spin(&sc->sc_mtx);
+
+ return (0);
+}
+
+static int
+imx51_gpio_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) {
+ device_set_desc(dev, "Freescale i.MX GPIO Controller");
+ return (BUS_PROBE_DEFAULT);
+ }
+
+ return (ENXIO);
+}
+
+static int
+imx51_gpio_attach(device_t dev)
+{
+ struct imx51_gpio_softc *sc;
+ int i, irq, unit;
+#ifdef IMX_ENABLE_CLOCKS
+ int err;
+#endif
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ sc->gpio_npins = NGPIO;
+
+ mtx_init(&sc->sc_mtx, device_get_nameunit(sc->dev), NULL, MTX_SPIN);
+
+#ifdef IMX_ENABLE_CLOCKS
+ if (clk_get_by_ofw_index(sc->dev, 0, 0, &sc->clk) != 0) {
+ device_printf(dev, "could not get clock");
+ return (ENOENT);
+ }
+
+ err = clk_enable(sc->clk);
+ if (err != 0) {
+ device_printf(sc->dev, "could not enable ipg clock\n");
+ return (err);
+ }
+#endif
+
+ if (bus_alloc_resources(dev, imx_gpio_spec, sc->sc_res)) {
+ device_printf(dev, "could not allocate resources\n");
+ bus_release_resources(dev, imx_gpio_spec, sc->sc_res);
+ mtx_destroy(&sc->sc_mtx);
+ return (ENXIO);
+ }
+
+ sc->sc_iot = rman_get_bustag(sc->sc_res[0]);
+ sc->sc_ioh = rman_get_bushandle(sc->sc_res[0]);
+ /*
+ * Mask off all interrupts in hardware, then set up interrupt handling.
+ */
+ WRITE4(sc, IMX_GPIO_IMR_REG, 0);
+ for (irq = 0; irq < 2; irq++) {
+#ifdef INTRNG
+ if ((bus_setup_intr(dev, sc->sc_res[1 + irq], INTR_TYPE_CLK,
+ gpio_pic_filter, NULL, sc, &sc->gpio_ih[irq]))) {
+ device_printf(dev,
+ "WARNING: unable to register interrupt handler\n");
+ imx51_gpio_detach(dev);
+ return (ENXIO);
+ }
+#endif
+ }
+
+ unit = device_get_unit(dev);
+ for (i = 0; i < sc->gpio_npins; i++) {
+ sc->gpio_pins[i].gp_pin = i;
+ sc->gpio_pins[i].gp_caps = DEFAULT_CAPS;
+ sc->gpio_pins[i].gp_flags =
+ (READ4(sc, IMX_GPIO_OE_REG) & (1U << i)) ? GPIO_PIN_OUTPUT :
+ GPIO_PIN_INPUT;
+ snprintf(sc->gpio_pins[i].gp_name, GPIOMAXNAME,
+ "GPIO%d_IO%02d", unit + 1, i);
+ }
+
+#ifdef INTRNG
+ gpio_pic_register_isrcs(sc);
+ intr_pic_register(dev, OF_xref_from_node(ofw_bus_get_node(dev)));
+#endif
+ sc->sc_busdev = gpiobus_attach_bus(dev);
+
+ if (sc->sc_busdev == NULL) {
+ imx51_gpio_detach(dev);
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static int
+imx51_gpio_detach(device_t dev)
+{
+ int irq;
+ struct imx51_gpio_softc *sc;
+#ifdef IMX_ENABLE_CLOCKS
+ int error;
+#endif
+
+ sc = device_get_softc(dev);
+
+#ifdef IMX_ENABLE_CLOCKS
+ error = clk_disable(sc->clk);
+ if (error != 0) {
+ device_printf(sc->dev, "could not disable ipg clock\n");
+ return (error);
+ }
+#endif
+
+ gpiobus_detach_bus(dev);
+ for (irq = 0; irq < NUM_IRQRES; irq++) {
+ if (sc->gpio_ih[irq])
+ bus_teardown_intr(dev, sc->sc_res[irq + FIRST_IRQRES],
+ sc->gpio_ih[irq]);
+ }
+ bus_release_resources(dev, imx_gpio_spec, sc->sc_res);
+ mtx_destroy(&sc->sc_mtx);
+
+ return(0);
+}
+
+static phandle_t
+imx51_gpio_get_node(device_t bus, device_t dev)
+{
+ /*
+ * Share controller node with gpiobus device
+ */
+ return ofw_bus_get_node(bus);
+}
+
+static device_method_t imx51_gpio_methods[] = {
+ DEVMETHOD(device_probe, imx51_gpio_probe),
+ DEVMETHOD(device_attach, imx51_gpio_attach),
+ DEVMETHOD(device_detach, imx51_gpio_detach),
+
+#ifdef INTRNG
+ /* Interrupt controller interface */
+ DEVMETHOD(pic_disable_intr, gpio_pic_disable_intr),
+ DEVMETHOD(pic_enable_intr, gpio_pic_enable_intr),
+ DEVMETHOD(pic_map_intr, gpio_pic_map_intr),
+ DEVMETHOD(pic_setup_intr, gpio_pic_setup_intr),
+ DEVMETHOD(pic_teardown_intr, gpio_pic_teardown_intr),
+ DEVMETHOD(pic_post_filter, gpio_pic_post_filter),
+ DEVMETHOD(pic_post_ithread, gpio_pic_post_ithread),
+ DEVMETHOD(pic_pre_ithread, gpio_pic_pre_ithread),
+#endif
+
+ /* OFW methods */
+ DEVMETHOD(ofw_bus_get_node, imx51_gpio_get_node),
+
+ /* GPIO protocol */
+ DEVMETHOD(gpio_get_bus, imx51_gpio_get_bus),
+ DEVMETHOD(gpio_pin_max, imx51_gpio_pin_max),
+ DEVMETHOD(gpio_pin_getname, imx51_gpio_pin_getname),
+ DEVMETHOD(gpio_pin_getflags, imx51_gpio_pin_getflags),
+ DEVMETHOD(gpio_pin_getcaps, imx51_gpio_pin_getcaps),
+ DEVMETHOD(gpio_pin_setflags, imx51_gpio_pin_setflags),
+ DEVMETHOD(gpio_pin_get, imx51_gpio_pin_get),
+ DEVMETHOD(gpio_pin_set, imx51_gpio_pin_set),
+ DEVMETHOD(gpio_pin_toggle, imx51_gpio_pin_toggle),
+ DEVMETHOD(gpio_pin_access_32, imx51_gpio_pin_access_32),
+ DEVMETHOD(gpio_pin_config_32, imx51_gpio_pin_config_32),
+ {0, 0},
+};
+
+static driver_t imx51_gpio_driver = {
+ "gpio",
+ imx51_gpio_methods,
+ sizeof(struct imx51_gpio_softc),
+};
+static devclass_t imx51_gpio_devclass;
+
+EARLY_DRIVER_MODULE(imx51_gpio, simplebus, imx51_gpio_driver,
+ imx51_gpio_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);
diff --git a/sys/arm/freescale/imx/imx_gpt.c b/sys/arm/freescale/imx/imx_gpt.c
new file mode 100644
index 000000000000..6149ec6c6f04
--- /dev/null
+++ b/sys/arm/freescale/imx/imx_gpt.c
@@ -0,0 +1,425 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012, 2013 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Oleksandr Rybalko under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <machine/bus.h>
+#include <machine/intr.h>
+#include <machine/machdep.h> /* For arm_set_delay */
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/freescale/imx/imx_ccmvar.h>
+#include <arm/freescale/imx/imx_gptreg.h>
+
+#define WRITE4(_sc, _r, _v) \
+ bus_space_write_4((_sc)->sc_iot, (_sc)->sc_ioh, (_r), (_v))
+#define READ4(_sc, _r) \
+ bus_space_read_4((_sc)->sc_iot, (_sc)->sc_ioh, (_r))
+#define SET4(_sc, _r, _m) \
+ WRITE4((_sc), (_r), READ4((_sc), (_r)) | (_m))
+#define CLEAR4(_sc, _r, _m) \
+ WRITE4((_sc), (_r), READ4((_sc), (_r)) & ~(_m))
+
+static u_int imx_gpt_get_timecount(struct timecounter *);
+static int imx_gpt_timer_start(struct eventtimer *, sbintime_t,
+ sbintime_t);
+static int imx_gpt_timer_stop(struct eventtimer *);
+
+static void imx_gpt_do_delay(int, void *);
+
+static int imx_gpt_intr(void *);
+static int imx_gpt_probe(device_t);
+static int imx_gpt_attach(device_t);
+
+static struct timecounter imx_gpt_timecounter = {
+ .tc_name = "iMXGPT",
+ .tc_get_timecount = imx_gpt_get_timecount,
+ .tc_counter_mask = ~0u,
+ .tc_frequency = 0,
+ .tc_quality = 1000,
+};
+
+struct imx_gpt_softc {
+ device_t sc_dev;
+ struct resource * res[2];
+ bus_space_tag_t sc_iot;
+ bus_space_handle_t sc_ioh;
+ void * sc_ih; /* interrupt handler */
+ uint32_t sc_period;
+ uint32_t sc_clksrc;
+ uint32_t clkfreq;
+ uint32_t ir_reg;
+ struct eventtimer et;
+};
+
+/* Try to divide down an available fast clock to this frequency. */
+#define TARGET_FREQUENCY 1000000000
+
+static struct resource_spec imx_gpt_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"fsl,imx6dl-gpt", 1},
+ {"fsl,imx6q-gpt", 1},
+ {"fsl,imx6ul-gpt", 1},
+ {"fsl,imx53-gpt", 1},
+ {"fsl,imx51-gpt", 1},
+ {"fsl,imx31-gpt", 1},
+ {"fsl,imx27-gpt", 1},
+ {"fsl,imx25-gpt", 1},
+ {NULL, 0}
+};
+
+static int
+imx_gpt_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ /*
+ * We only support a single unit, because the only thing this driver
+ * does with the complex timer hardware is supply the system
+ * timecounter and eventtimer. There is nothing useful we can do with
+ * the additional device instances that exist in some chips.
+ */
+ if (device_get_unit(dev) > 0)
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) {
+ device_set_desc(dev, "Freescale i.MX GPT timer");
+ return (BUS_PROBE_DEFAULT);
+ }
+
+ return (ENXIO);
+}
+
+static int
+imx_gpt_attach(device_t dev)
+{
+ struct imx_gpt_softc *sc;
+ int ctlreg, err;
+ uint32_t basefreq, prescale, setup_ticks, t1, t2;
+
+ sc = device_get_softc(dev);
+
+ if (bus_alloc_resources(dev, imx_gpt_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ sc->sc_dev = dev;
+ sc->sc_iot = rman_get_bustag(sc->res[0]);
+ sc->sc_ioh = rman_get_bushandle(sc->res[0]);
+
+ /*
+ * For now, just automatically choose a good clock for the hardware
+ * we're running on. Eventually we could allow selection from the fdt;
+ * the code in this driver will cope with any clock frequency.
+ */
+ sc->sc_clksrc = GPT_CR_CLKSRC_IPG;
+
+ ctlreg = 0;
+
+ switch (sc->sc_clksrc) {
+ case GPT_CR_CLKSRC_32K:
+ basefreq = 32768;
+ break;
+ case GPT_CR_CLKSRC_IPG:
+ basefreq = imx_ccm_ipg_hz();
+ break;
+ case GPT_CR_CLKSRC_IPG_HIGH:
+ basefreq = imx_ccm_ipg_hz() * 2;
+ break;
+ case GPT_CR_CLKSRC_24M:
+ ctlreg |= GPT_CR_24MEN;
+ basefreq = 24000000;
+ break;
+ case GPT_CR_CLKSRC_NONE:/* Can't run without a clock. */
+ case GPT_CR_CLKSRC_EXT: /* No way to get the freq of an ext clock. */
+ default:
+ device_printf(dev, "Unsupported clock source '%d'\n",
+ sc->sc_clksrc);
+ return (EINVAL);
+ }
+
+ /*
+ * The following setup sequence is from the I.MX6 reference manual,
+ * "Selecting the clock source". First, disable the clock and
+ * interrupts. This also clears input and output mode bits and in
+ * general completes several of the early steps in the procedure.
+ */
+ WRITE4(sc, IMX_GPT_CR, 0);
+ WRITE4(sc, IMX_GPT_IR, 0);
+
+ /* Choose the clock and the power-saving behaviors. */
+ ctlreg |=
+ sc->sc_clksrc | /* Use selected clock */
+ GPT_CR_FRR | /* Just count (FreeRunner mode) */
+ GPT_CR_STOPEN | /* Run in STOP mode */
+ GPT_CR_DOZEEN | /* Run in DOZE mode */
+ GPT_CR_WAITEN | /* Run in WAIT mode */
+ GPT_CR_DBGEN; /* Run in DEBUG mode */
+ WRITE4(sc, IMX_GPT_CR, ctlreg);
+
+ /*
+ * The datasheet says to do the software reset after choosing the clock
+ * source. It says nothing about needing to wait for the reset to
+ * complete, but the register description does document the fact that
+ * the reset isn't complete until the SWR bit reads 0, so let's be safe.
+ * The reset also clears all registers except for a few of the bits in
+ * CR, but we'll rewrite all the CR bits when we start the counter.
+ */
+ WRITE4(sc, IMX_GPT_CR, ctlreg | GPT_CR_SWR);
+ while (READ4(sc, IMX_GPT_CR) & GPT_CR_SWR)
+ continue;
+
+ /* Set a prescaler value that gets us near the target frequency. */
+ if (basefreq < TARGET_FREQUENCY) {
+ prescale = 0;
+ sc->clkfreq = basefreq;
+ } else {
+ prescale = basefreq / TARGET_FREQUENCY;
+ sc->clkfreq = basefreq / prescale;
+ prescale -= 1; /* 1..n range is 0..n-1 in hardware. */
+ }
+ WRITE4(sc, IMX_GPT_PR, prescale);
+
+ /* Clear the status register. */
+ WRITE4(sc, IMX_GPT_SR, GPT_IR_ALL);
+
+ /* Start the counter. */
+ WRITE4(sc, IMX_GPT_CR, ctlreg | GPT_CR_EN);
+
+ if (bootverbose)
+ device_printf(dev, "Running on %dKHz clock, base freq %uHz CR=0x%08x, PR=0x%08x\n",
+ sc->clkfreq / 1000, basefreq, READ4(sc, IMX_GPT_CR), READ4(sc, IMX_GPT_PR));
+
+ /* Setup the timer interrupt. */
+ err = bus_setup_intr(dev, sc->res[1], INTR_TYPE_CLK, imx_gpt_intr,
+ NULL, sc, &sc->sc_ih);
+ if (err != 0) {
+ bus_release_resources(dev, imx_gpt_spec, sc->res);
+ device_printf(dev, "Unable to setup the clock irq handler, "
+ "err = %d\n", err);
+ return (ENXIO);
+ }
+
+ /*
+ * Measure how many clock ticks it takes to setup a one-shot event (it's
+ * longer than you might think, due to wait states in accessing gpt
+ * registers). Scale up the result by a factor of 1.5 to be safe,
+ * and use that to set the minimum eventtimer period we can schedule. In
+ * the real world, the value works out to about 750ns on imx5 hardware.
+ */
+ t1 = READ4(sc, IMX_GPT_CNT);
+ WRITE4(sc, IMX_GPT_OCR3, 0);
+ t2 = READ4(sc, IMX_GPT_CNT);
+ setup_ticks = ((t2 - t1 + 1) * 3) / 2;
+
+ /* Register as an eventtimer. */
+ sc->et.et_name = "iMXGPT";
+ sc->et.et_flags = ET_FLAGS_ONESHOT | ET_FLAGS_PERIODIC;
+ sc->et.et_quality = 800;
+ sc->et.et_frequency = sc->clkfreq;
+ sc->et.et_min_period = ((uint64_t)setup_ticks << 32) / sc->clkfreq;
+ sc->et.et_max_period = ((uint64_t)0xfffffffe << 32) / sc->clkfreq;
+ sc->et.et_start = imx_gpt_timer_start;
+ sc->et.et_stop = imx_gpt_timer_stop;
+ sc->et.et_priv = sc;
+ et_register(&sc->et);
+
+ /* Register as a timecounter. */
+ imx_gpt_timecounter.tc_frequency = sc->clkfreq;
+ imx_gpt_timecounter.tc_priv = sc;
+ tc_init(&imx_gpt_timecounter);
+
+ /* If this is the first unit, store the softc for use in DELAY. */
+ if (device_get_unit(dev) == 0) {
+ arm_set_delay(imx_gpt_do_delay, sc);
+ }
+
+ return (0);
+}
+
+static int
+imx_gpt_timer_start(struct eventtimer *et, sbintime_t first, sbintime_t period)
+{
+ struct imx_gpt_softc *sc;
+ uint32_t ticks;
+
+ sc = (struct imx_gpt_softc *)et->et_priv;
+
+ if (period != 0) {
+ sc->sc_period = ((uint32_t)et->et_frequency * period) >> 32;
+ /* Set expected value */
+ WRITE4(sc, IMX_GPT_OCR2, READ4(sc, IMX_GPT_CNT) + sc->sc_period);
+ /* Enable compare register 2 Interrupt */
+ sc->ir_reg |= GPT_IR_OF2;
+ WRITE4(sc, IMX_GPT_IR, sc->ir_reg);
+ return (0);
+ } else if (first != 0) {
+ /* Enable compare register 3 interrupt if not already on. */
+ if ((sc->ir_reg & GPT_IR_OF3) == 0) {
+ sc->ir_reg |= GPT_IR_OF3;
+ WRITE4(sc, IMX_GPT_IR, sc->ir_reg);
+ }
+ ticks = ((uint32_t)et->et_frequency * first) >> 32;
+ /* Do not disturb, otherwise event will be lost */
+ spinlock_enter();
+ /* Set expected value */
+ WRITE4(sc, IMX_GPT_OCR3, READ4(sc, IMX_GPT_CNT) + ticks);
+ /* Now everybody can relax */
+ spinlock_exit();
+ return (0);
+ }
+
+ return (EINVAL);
+}
+
+static int
+imx_gpt_timer_stop(struct eventtimer *et)
+{
+ struct imx_gpt_softc *sc;
+
+ sc = (struct imx_gpt_softc *)et->et_priv;
+
+ /* Disable interrupts and clear any pending status. */
+ sc->ir_reg &= ~(GPT_IR_OF2 | GPT_IR_OF3);
+ WRITE4(sc, IMX_GPT_IR, sc->ir_reg);
+ WRITE4(sc, IMX_GPT_SR, GPT_IR_OF2 | GPT_IR_OF3);
+ sc->sc_period = 0;
+
+ return (0);
+}
+
+static int
+imx_gpt_intr(void *arg)
+{
+ struct imx_gpt_softc *sc;
+ uint32_t status;
+
+ sc = (struct imx_gpt_softc *)arg;
+
+ status = READ4(sc, IMX_GPT_SR);
+
+ /*
+ * Clear interrupt status before invoking event callbacks. The callback
+ * often sets up a new one-shot timer event and if the interval is short
+ * enough it can fire before we get out of this function. If we cleared
+ * at the bottom we'd miss the interrupt and hang until the clock wraps.
+ */
+ WRITE4(sc, IMX_GPT_SR, status);
+
+ /* Handle one-shot timer events. */
+ if (status & GPT_IR_OF3) {
+ if (sc->et.et_active) {
+ sc->et.et_event_cb(&sc->et, sc->et.et_arg);
+ }
+ }
+
+ /* Handle periodic timer events. */
+ if (status & GPT_IR_OF2) {
+ if (sc->et.et_active)
+ sc->et.et_event_cb(&sc->et, sc->et.et_arg);
+ if (sc->sc_period != 0)
+ WRITE4(sc, IMX_GPT_OCR2, READ4(sc, IMX_GPT_CNT) +
+ sc->sc_period);
+ }
+
+ return (FILTER_HANDLED);
+}
+
+static u_int
+imx_gpt_get_timecount(struct timecounter *tc)
+{
+ struct imx_gpt_softc *sc;
+
+ sc = tc->tc_priv;
+ return (READ4(sc, IMX_GPT_CNT));
+}
+
+static device_method_t imx_gpt_methods[] = {
+ DEVMETHOD(device_probe, imx_gpt_probe),
+ DEVMETHOD(device_attach, imx_gpt_attach),
+
+ DEVMETHOD_END
+};
+
+static driver_t imx_gpt_driver = {
+ "imx_gpt",
+ imx_gpt_methods,
+ sizeof(struct imx_gpt_softc),
+};
+
+static devclass_t imx_gpt_devclass;
+
+EARLY_DRIVER_MODULE(imx_gpt, simplebus, imx_gpt_driver, imx_gpt_devclass, 0,
+ 0, BUS_PASS_TIMER);
+
+static void
+imx_gpt_do_delay(int usec, void *arg)
+{
+ struct imx_gpt_softc *sc = arg;
+ uint64_t curcnt, endcnt, startcnt, ticks;
+
+ /*
+ * Calculate the tick count with 64-bit values so that it works for any
+ * clock frequency. Loop until the hardware count reaches start+ticks.
+ * If the 32-bit hardware count rolls over while we're looping, just
+ * manually do a carry into the high bits after each read; don't worry
+ * that doing this on each loop iteration is inefficient -- we're trying
+ * to waste time here.
+ */
+ ticks = 1 + ((uint64_t)usec * sc->clkfreq) / 1000000;
+ curcnt = startcnt = READ4(sc, IMX_GPT_CNT);
+ endcnt = startcnt + ticks;
+ while (curcnt < endcnt) {
+ curcnt = READ4(sc, IMX_GPT_CNT);
+ if (curcnt < startcnt)
+ curcnt += 1ULL << 32;
+ }
+}
diff --git a/sys/arm/freescale/imx/imx_gptreg.h b/sys/arm/freescale/imx/imx_gptreg.h
new file mode 100644
index 000000000000..e1c13b7d0d6a
--- /dev/null
+++ b/sys/arm/freescale/imx/imx_gptreg.h
@@ -0,0 +1,103 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012, 2013 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Oleksandr Rybalko under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/* Registers definition for Freescale i.MX515 Generic Periodic Timer */
+
+#define IMX_GPT_CR 0x0000 /* Control Register R/W */
+#define GPT_CR_FO3 (1U << 31)
+#define GPT_CR_FO2 (1 << 30)
+#define GPT_CR_FO1 (1 << 29)
+#define GPT_CR_OM3_SHIFT 26
+#define GPT_CR_OM3_MASK 0x1c000000
+#define GPT_CR_OM2_SHIFT 23
+#define GPT_CR_OM2_MASK 0x03800000
+#define GPT_CR_OM1_SHIFT 20
+#define GPT_CR_OM1_MASK 0x00700000
+#define GPT_CR_OMX_NONE 0
+#define GPT_CR_OMX_TOGGLE 1
+#define GPT_CR_OMX_CLEAR 2
+#define GPT_CR_OMX_SET 3
+#define GPT_CR_OMX_PULSE 4 /* Run CLKSRC on output pin */
+#define GPT_CR_IM2_SHIFT 18
+#define GPT_CR_IM2_MASK 0x000c0000
+#define GPT_CR_IM1_SHIFT 16
+#define GPT_CR_IM1_MASK 0x00030000
+#define GPT_CR_IMX_NONE 0
+#define GPT_CR_IMX_REDGE 1
+#define GPT_CR_IMX_FEDGE 2
+#define GPT_CR_IMX_BOTH 3
+#define GPT_CR_SWR (1 << 15)
+#define GPT_CR_24MEN (1 << 10)
+#define GPT_CR_FRR (1 << 9)
+#define GPT_CR_CLKSRC_NONE (0 << 6)
+#define GPT_CR_CLKSRC_IPG (1 << 6)
+#define GPT_CR_CLKSRC_IPG_HIGH (2 << 6)
+#define GPT_CR_CLKSRC_EXT (3 << 6)
+#define GPT_CR_CLKSRC_32K (4 << 6)
+#define GPT_CR_CLKSRC_24M (5 << 6)
+#define GPT_CR_STOPEN (1 << 5)
+#define GPT_CR_DOZEEN (1 << 4)
+#define GPT_CR_WAITEN (1 << 3)
+#define GPT_CR_DBGEN (1 << 2)
+#define GPT_CR_ENMOD (1 << 1)
+#define GPT_CR_EN (1 << 0)
+
+#define IMX_GPT_PR 0x0004 /* Prescaler Register R/W */
+#define GPT_PR_VALUE_SHIFT 0
+#define GPT_PR_VALUE_MASK 0x00000fff
+#define GPT_PR_VALUE_SHIFT_24M 12
+#define GPT_PR_VALUE_MASK_24M 0x0000f000
+
+/* Same map for SR and IR */
+#define IMX_GPT_SR 0x0008 /* Status Register R/W */
+#define IMX_GPT_IR 0x000c /* Interrupt Register R/W */
+#define GPT_IR_ROV (1 << 5)
+#define GPT_IR_IF2 (1 << 4)
+#define GPT_IR_IF1 (1 << 3)
+#define GPT_IR_OF3 (1 << 2)
+#define GPT_IR_OF2 (1 << 1)
+#define GPT_IR_OF1 (1 << 0)
+#define GPT_IR_ALL \
+ (GPT_IR_ROV | \
+ GPT_IR_IF2 | \
+ GPT_IR_IF1 | \
+ GPT_IR_OF3 | \
+ GPT_IR_OF2 | \
+ GPT_IR_OF1)
+
+#define IMX_GPT_OCR1 0x0010 /* Output Compare Register 1 R/W */
+#define IMX_GPT_OCR2 0x0014 /* Output Compare Register 2 R/W */
+#define IMX_GPT_OCR3 0x0018 /* Output Compare Register 3 R/W */
+#define IMX_GPT_ICR1 0x001c /* Input capture Register 1 RO */
+#define IMX_GPT_ICR2 0x0020 /* Input capture Register 2 RO */
+#define IMX_GPT_CNT 0x0024 /* Counter Register RO */
diff --git a/sys/arm/freescale/imx/imx_i2c.c b/sys/arm/freescale/imx/imx_i2c.c
new file mode 100644
index 000000000000..6f1d69a00a05
--- /dev/null
+++ b/sys/arm/freescale/imx/imx_i2c.c
@@ -0,0 +1,732 @@
+/*-
+ * Copyright (C) 2008-2009 Semihalf, Michal Hajduk
+ * Copyright (c) 2012, 2013 The FreeBSD Foundation
+ * Copyright (c) 2015 Ian Lepore <ian@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Portions of this software were developed by Oleksandr Rybalko
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * I2C driver for Freescale i.MX hardware.
+ *
+ * Note that the hardware is capable of running as both a master and a slave.
+ * This driver currently implements only master-mode operations.
+ *
+ * This driver supports multi-master i2c buses, by detecting bus arbitration
+ * loss and returning IIC_EBUSBSY status. Notably, it does not do any kind of
+ * retries if some other master jumps onto the bus and interrupts one of our
+ * transfer cycles resulting in arbitration loss in mid-transfer. The caller
+ * must handle retries in a way that makes sense for the slave being addressed.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/gpio.h>
+#include <sys/kernel.h>
+#include <sys/limits.h>
+#include <sys/module.h>
+#include <sys/resource.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <arm/freescale/imx/imx_ccmvar.h>
+
+#include <dev/iicbus/iiconf.h>
+#include <dev/iicbus/iicbus.h>
+#include <dev/iicbus/iic_recover_bus.h>
+#include "iicbus_if.h"
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/fdt/fdt_pinctrl.h>
+#include <dev/gpio/gpiobusvar.h>
+
+#if defined(EXT_RESOURCES) && defined(__aarch64__)
+#define IMX_ENABLE_CLOCKS
+#endif
+
+#ifdef IMX_ENABLE_CLOCKS
+#include <dev/extres/clk/clk.h>
+#endif
+
+#define I2C_ADDR_REG 0x00 /* I2C slave address register */
+#define I2C_FDR_REG 0x04 /* I2C frequency divider register */
+#define I2C_CONTROL_REG 0x08 /* I2C control register */
+#define I2C_STATUS_REG 0x0C /* I2C status register */
+#define I2C_DATA_REG 0x10 /* I2C data register */
+#define I2C_DFSRR_REG 0x14 /* I2C Digital Filter Sampling rate */
+
+#define I2CCR_MEN (1 << 7) /* Module enable */
+#define I2CCR_MSTA (1 << 5) /* Master/slave mode */
+#define I2CCR_MTX (1 << 4) /* Transmit/receive mode */
+#define I2CCR_TXAK (1 << 3) /* Transfer acknowledge */
+#define I2CCR_RSTA (1 << 2) /* Repeated START */
+
+#define I2CSR_MCF (1 << 7) /* Data transfer */
+#define I2CSR_MASS (1 << 6) /* Addressed as a slave */
+#define I2CSR_MBB (1 << 5) /* Bus busy */
+#define I2CSR_MAL (1 << 4) /* Arbitration lost */
+#define I2CSR_SRW (1 << 2) /* Slave read/write */
+#define I2CSR_MIF (1 << 1) /* Module interrupt */
+#define I2CSR_RXAK (1 << 0) /* Received acknowledge */
+
+#define I2C_BAUD_RATE_FAST 0x31
+#define I2C_BAUD_RATE_DEF 0x3F
+#define I2C_DFSSR_DIV 0x10
+
+/*
+ * A table of available divisors and the associated coded values to put in the
+ * FDR register to achieve that divisor.. There is no algorithmic relationship I
+ * can see between divisors and the codes that go into the register. The table
+ * begins and ends with entries that handle insane configuration values.
+ */
+struct clkdiv {
+ u_int divisor;
+ u_int regcode;
+};
+static struct clkdiv clkdiv_table[] = {
+ { 0, 0x20 }, { 22, 0x20 }, { 24, 0x21 }, { 26, 0x22 },
+ { 28, 0x23 }, { 30, 0x00 }, { 32, 0x24 }, { 36, 0x25 },
+ { 40, 0x26 }, { 42, 0x03 }, { 44, 0x27 }, { 48, 0x28 },
+ { 52, 0x05 }, { 56, 0x29 }, { 60, 0x06 }, { 64, 0x2a },
+ { 72, 0x2b }, { 80, 0x2c }, { 88, 0x09 }, { 96, 0x2d },
+ { 104, 0x0a }, { 112, 0x2e }, { 128, 0x2f }, { 144, 0x0c },
+ { 160, 0x30 }, { 192, 0x31 }, { 224, 0x32 }, { 240, 0x0f },
+ { 256, 0x33 }, { 288, 0x10 }, { 320, 0x34 }, { 384, 0x35 },
+ { 448, 0x36 }, { 480, 0x13 }, { 512, 0x37 }, { 576, 0x14 },
+ { 640, 0x38 }, { 768, 0x39 }, { 896, 0x3a }, { 960, 0x17 },
+ { 1024, 0x3b }, { 1152, 0x18 }, { 1280, 0x3c }, { 1536, 0x3d },
+ { 1792, 0x3e }, { 1920, 0x1b }, { 2048, 0x3f }, { 2304, 0x1c },
+ { 2560, 0x1d }, { 3072, 0x1e }, { 3840, 0x1f }, {UINT_MAX, 0x1f}
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"fsl,imx21-i2c", 1},
+ {"fsl,imx6q-i2c", 1},
+ {"fsl,imx-i2c", 1},
+ {NULL, 0}
+};
+
+struct i2c_softc {
+ device_t dev;
+ device_t iicbus;
+ struct resource *res;
+ int rid;
+ sbintime_t byte_time_sbt;
+ int rb_pinctl_idx;
+ gpio_pin_t rb_sclpin;
+ gpio_pin_t rb_sdapin;
+ u_int debug;
+ u_int slave;
+#ifdef IMX_ENABLE_CLOCKS
+ clk_t ipgclk;
+#endif
+};
+
+#define DEVICE_DEBUGF(sc, lvl, fmt, args...) \
+ if ((lvl) <= (sc)->debug) \
+ device_printf((sc)->dev, fmt, ##args)
+
+#define DEBUGF(sc, lvl, fmt, args...) \
+ if ((lvl) <= (sc)->debug) \
+ printf(fmt, ##args)
+
+static phandle_t i2c_get_node(device_t, device_t);
+static int i2c_probe(device_t);
+static int i2c_attach(device_t);
+static int i2c_detach(device_t);
+
+static int i2c_repeated_start(device_t, u_char, int);
+static int i2c_start(device_t, u_char, int);
+static int i2c_stop(device_t);
+static int i2c_reset(device_t, u_char, u_char, u_char *);
+static int i2c_read(device_t, char *, int, int *, int, int);
+static int i2c_write(device_t, const char *, int, int *, int);
+
+static device_method_t i2c_methods[] = {
+ DEVMETHOD(device_probe, i2c_probe),
+ DEVMETHOD(device_attach, i2c_attach),
+ DEVMETHOD(device_detach, i2c_detach),
+
+ /* OFW methods */
+ DEVMETHOD(ofw_bus_get_node, i2c_get_node),
+
+ DEVMETHOD(iicbus_callback, iicbus_null_callback),
+ DEVMETHOD(iicbus_repeated_start, i2c_repeated_start),
+ DEVMETHOD(iicbus_start, i2c_start),
+ DEVMETHOD(iicbus_stop, i2c_stop),
+ DEVMETHOD(iicbus_reset, i2c_reset),
+ DEVMETHOD(iicbus_read, i2c_read),
+ DEVMETHOD(iicbus_write, i2c_write),
+ DEVMETHOD(iicbus_transfer, iicbus_transfer_gen),
+
+ DEVMETHOD_END
+};
+
+static driver_t i2c_driver = {
+ "imx_i2c",
+ i2c_methods,
+ sizeof(struct i2c_softc),
+};
+static devclass_t i2c_devclass;
+
+DRIVER_MODULE(imx_i2c, simplebus, i2c_driver, i2c_devclass, 0, 0);
+DRIVER_MODULE(ofw_iicbus, imx_i2c, ofw_iicbus_driver, ofw_iicbus_devclass, 0, 0);
+MODULE_DEPEND(imx_i2c, iicbus, 1, 1, 1);
+SIMPLEBUS_PNP_INFO(compat_data);
+
+static phandle_t
+i2c_get_node(device_t bus, device_t dev)
+{
+ /*
+ * Share controller node with iicbus device
+ */
+ return ofw_bus_get_node(bus);
+}
+
+static __inline void
+i2c_write_reg(struct i2c_softc *sc, bus_size_t off, uint8_t val)
+{
+
+ bus_write_1(sc->res, off, val);
+}
+
+static __inline uint8_t
+i2c_read_reg(struct i2c_softc *sc, bus_size_t off)
+{
+
+ return (bus_read_1(sc->res, off));
+}
+
+static __inline void
+i2c_flag_set(struct i2c_softc *sc, bus_size_t off, uint8_t mask)
+{
+ uint8_t status;
+
+ status = i2c_read_reg(sc, off);
+ status |= mask;
+ i2c_write_reg(sc, off, status);
+}
+
+/* Wait for bus to become busy or not-busy. */
+static int
+wait_for_busbusy(struct i2c_softc *sc, int wantbusy)
+{
+ int retry, srb;
+
+ retry = 1000;
+ while (retry --) {
+ srb = i2c_read_reg(sc, I2C_STATUS_REG) & I2CSR_MBB;
+ if ((srb && wantbusy) || (!srb && !wantbusy))
+ return (IIC_NOERR);
+ DELAY(1);
+ }
+ return (IIC_ETIMEOUT);
+}
+
+/* Wait for transfer to complete, optionally check RXAK. */
+static int
+wait_for_xfer(struct i2c_softc *sc, int checkack)
+{
+ int retry, sr;
+
+ /*
+ * Sleep for about the time it takes to transfer a byte (with precision
+ * set to tolerate 5% oversleep). We calculate the approximate byte
+ * transfer time when we set the bus speed divisor. Slaves are allowed
+ * to do clock-stretching so the actual transfer time can be larger, but
+ * this gets the bulk of the waiting out of the way without tying up the
+ * processor the whole time.
+ */
+ pause_sbt("imxi2c", sc->byte_time_sbt, sc->byte_time_sbt / 20, 0);
+
+ retry = 10000;
+ while (retry --) {
+ sr = i2c_read_reg(sc, I2C_STATUS_REG);
+ if (sr & I2CSR_MIF) {
+ if (sr & I2CSR_MAL)
+ return (IIC_EBUSERR);
+ else if (checkack && (sr & I2CSR_RXAK))
+ return (IIC_ENOACK);
+ else
+ return (IIC_NOERR);
+ }
+ DELAY(1);
+ }
+ return (IIC_ETIMEOUT);
+}
+
+/*
+ * Implement the error handling shown in the state diagram of the imx6 reference
+ * manual. If there was an error, then:
+ * - Clear master mode (MSTA and MTX).
+ * - Wait for the bus to become free or for a timeout to happen.
+ * - Disable the controller.
+ */
+static int
+i2c_error_handler(struct i2c_softc *sc, int error)
+{
+
+ if (error != 0) {
+ i2c_write_reg(sc, I2C_STATUS_REG, 0);
+ i2c_write_reg(sc, I2C_CONTROL_REG, I2CCR_MEN);
+ wait_for_busbusy(sc, false);
+ i2c_write_reg(sc, I2C_CONTROL_REG, 0);
+ }
+ return (error);
+}
+
+static int
+i2c_recover_getsda(void *ctx)
+{
+ bool active;
+
+ gpio_pin_is_active(((struct i2c_softc *)ctx)->rb_sdapin, &active);
+ return (active);
+}
+
+static void
+i2c_recover_setsda(void *ctx, int value)
+{
+
+ gpio_pin_set_active(((struct i2c_softc *)ctx)->rb_sdapin, value);
+}
+
+static int
+i2c_recover_getscl(void *ctx)
+{
+ bool active;
+
+ gpio_pin_is_active(((struct i2c_softc *)ctx)->rb_sclpin, &active);
+ return (active);
+
+}
+
+static void
+i2c_recover_setscl(void *ctx, int value)
+{
+
+ gpio_pin_set_active(((struct i2c_softc *)ctx)->rb_sclpin, value);
+}
+
+static int
+i2c_recover_bus(struct i2c_softc *sc)
+{
+ struct iicrb_pin_access pins;
+ int err;
+
+ /*
+ * If we have gpio pinmux config, reconfigure the pins to gpio mode,
+ * invoke iic_recover_bus which checks for a hung bus and bitbangs a
+ * recovery sequence if necessary, then configure the pins back to i2c
+ * mode (idx 0).
+ */
+ if (sc->rb_pinctl_idx == 0)
+ return (0);
+
+ fdt_pinctrl_configure(sc->dev, sc->rb_pinctl_idx);
+
+ pins.ctx = sc;
+ pins.getsda = i2c_recover_getsda;
+ pins.setsda = i2c_recover_setsda;
+ pins.getscl = i2c_recover_getscl;
+ pins.setscl = i2c_recover_setscl;
+ err = iic_recover_bus(&pins);
+
+ fdt_pinctrl_configure(sc->dev, 0);
+
+ return (err);
+}
+
+static int
+i2c_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Freescale i.MX I2C");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+i2c_attach(device_t dev)
+{
+ char wrkstr[16];
+ struct i2c_softc *sc;
+ phandle_t node;
+ int err, cfgidx;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ sc->rid = 0;
+
+#ifdef IMX_ENABLE_CLOCKS
+ if (clk_get_by_ofw_index(sc->dev, 0, 0, &sc->ipgclk) != 0) {
+ device_printf(dev, "could not get ipg clock");
+ return (ENOENT);
+ }
+
+ err = clk_enable(sc->ipgclk);
+ if (err != 0) {
+ device_printf(sc->dev, "could not enable ipg clock\n");
+ return (err);
+ }
+#endif
+
+ sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->rid,
+ RF_ACTIVE);
+ if (sc->res == NULL) {
+ device_printf(dev, "could not allocate resources");
+ return (ENXIO);
+ }
+
+ sc->iicbus = device_add_child(dev, "iicbus", -1);
+ if (sc->iicbus == NULL) {
+ device_printf(dev, "could not add iicbus child");
+ return (ENXIO);
+ }
+
+ /* Set up debug-enable sysctl. */
+ SYSCTL_ADD_INT(device_get_sysctl_ctx(sc->dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)),
+ OID_AUTO, "debug", CTLFLAG_RWTUN, &sc->debug, 0,
+ "Enable debug; 1=reads/writes, 2=add starts/stops");
+
+ /*
+ * Set up for bus recovery using gpio pins, if the pinctrl and gpio
+ * properties are present. This is optional. If all the config data is
+ * not in place, we just don't do gpio bitbang bus recovery.
+ */
+ node = ofw_bus_get_node(sc->dev);
+
+ err = gpio_pin_get_by_ofw_property(dev, node, "scl-gpios",
+ &sc->rb_sclpin);
+ if (err != 0)
+ goto no_recovery;
+ err = gpio_pin_get_by_ofw_property(dev, node, "sda-gpios",
+ &sc->rb_sdapin);
+ if (err != 0)
+ goto no_recovery;
+
+ /*
+ * Preset the gpio pins to output high (idle bus state). The signal
+ * won't actually appear on the pins until the bus recovery code changes
+ * the pinmux config from i2c to gpio.
+ */
+ gpio_pin_setflags(sc->rb_sclpin, GPIO_PIN_OUTPUT);
+ gpio_pin_setflags(sc->rb_sdapin, GPIO_PIN_OUTPUT);
+ gpio_pin_set_active(sc->rb_sclpin, true);
+ gpio_pin_set_active(sc->rb_sdapin, true);
+
+ /*
+ * Obtain the index of pinctrl node for bus recovery using gpio pins,
+ * then confirm that pinctrl properties exist for that index and for the
+ * default pinctrl-0. If sc->rb_pinctl_idx is non-zero, the reset code
+ * will also do a bus recovery, so setting this value must be last.
+ */
+ err = ofw_bus_find_string_index(node, "pinctrl-names", "gpio", &cfgidx);
+ if (err == 0) {
+ snprintf(wrkstr, sizeof(wrkstr), "pinctrl-%d", cfgidx);
+ if (OF_hasprop(node, "pinctrl-0") && OF_hasprop(node, wrkstr))
+ sc->rb_pinctl_idx = cfgidx;
+ }
+
+no_recovery:
+
+ /* We don't do a hardware reset here because iicbus_attach() does it. */
+
+ /* Probe and attach the iicbus when interrupts are available. */
+ return (bus_delayed_attach_children(dev));
+}
+
+static int
+i2c_detach(device_t dev)
+{
+ struct i2c_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+
+#ifdef IMX_ENABLE_CLOCKS
+ error = clk_disable(sc->ipgclk);
+ if (error != 0) {
+ device_printf(sc->dev, "could not disable ipg clock\n");
+ return (error);
+ }
+#endif
+
+ if ((error = bus_generic_detach(sc->dev)) != 0) {
+ device_printf(sc->dev, "cannot detach child devices\n");
+ return (error);
+ }
+
+ if (sc->iicbus != NULL)
+ device_delete_child(dev, sc->iicbus);
+
+ /* Release bus-recover pins; gpio_pin_release() handles NULL args. */
+ gpio_pin_release(sc->rb_sclpin);
+ gpio_pin_release(sc->rb_sdapin);
+
+ if (sc->res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->res);
+
+ return (0);
+}
+
+static int
+i2c_repeated_start(device_t dev, u_char slave, int timeout)
+{
+ struct i2c_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ if ((i2c_read_reg(sc, I2C_STATUS_REG) & I2CSR_MBB) == 0) {
+ return (IIC_EBUSERR);
+ }
+
+ /*
+ * Set repeated start condition, delay (per reference manual, min 156nS)
+ * before writing slave address, wait for ack after write.
+ */
+ i2c_flag_set(sc, I2C_CONTROL_REG, I2CCR_RSTA);
+ DELAY(1);
+ i2c_write_reg(sc, I2C_STATUS_REG, 0x0);
+ i2c_write_reg(sc, I2C_DATA_REG, slave);
+ sc->slave = slave;
+ DEVICE_DEBUGF(sc, 2, "rstart 0x%02x\n", sc->slave);
+ error = wait_for_xfer(sc, true);
+ return (i2c_error_handler(sc, error));
+}
+
+static int
+i2c_start_ll(device_t dev, u_char slave, int timeout)
+{
+ struct i2c_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ i2c_write_reg(sc, I2C_CONTROL_REG, I2CCR_MEN);
+ DELAY(10); /* Delay for controller to sample bus state. */
+ if (i2c_read_reg(sc, I2C_STATUS_REG) & I2CSR_MBB) {
+ return (i2c_error_handler(sc, IIC_EBUSERR));
+ }
+ i2c_write_reg(sc, I2C_CONTROL_REG, I2CCR_MEN | I2CCR_MSTA | I2CCR_MTX);
+ if ((error = wait_for_busbusy(sc, true)) != IIC_NOERR)
+ return (i2c_error_handler(sc, error));
+ i2c_write_reg(sc, I2C_STATUS_REG, 0);
+ i2c_write_reg(sc, I2C_DATA_REG, slave);
+ sc->slave = slave;
+ DEVICE_DEBUGF(sc, 2, "start 0x%02x\n", sc->slave);
+ error = wait_for_xfer(sc, true);
+ return (i2c_error_handler(sc, error));
+}
+
+static int
+i2c_start(device_t dev, u_char slave, int timeout)
+{
+ struct i2c_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ /*
+ * Invoke the low-level code to put the bus into master mode and address
+ * the given slave. If that fails, idle the controller and attempt a
+ * bus recovery, and then try again one time. Signaling a start and
+ * addressing the slave is the only operation that a low-level driver
+ * can safely retry without any help from the upper layers that know
+ * more about the slave device.
+ */
+ if ((error = i2c_start_ll(dev, slave, timeout)) != 0) {
+ i2c_write_reg(sc, I2C_CONTROL_REG, 0x0);
+ if ((error = i2c_recover_bus(sc)) != 0)
+ return (error);
+ error = i2c_start_ll(dev, slave, timeout);
+ }
+ return (error);
+}
+
+static int
+i2c_stop(device_t dev)
+{
+ struct i2c_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ i2c_write_reg(sc, I2C_CONTROL_REG, I2CCR_MEN);
+ wait_for_busbusy(sc, false);
+ i2c_write_reg(sc, I2C_CONTROL_REG, 0);
+ DEVICE_DEBUGF(sc, 2, "stop 0x%02x\n", sc->slave);
+ return (IIC_NOERR);
+}
+
+static int
+i2c_reset(device_t dev, u_char speed, u_char addr, u_char *oldadr)
+{
+ struct i2c_softc *sc;
+ u_int busfreq, div, i, ipgfreq;
+#ifdef IMX_ENABLE_CLOCKS
+ int err;
+ uint64_t freq;
+#endif
+
+ sc = device_get_softc(dev);
+
+ DEVICE_DEBUGF(sc, 1, "reset\n");
+
+ /*
+ * Look up the divisor that gives the nearest speed that doesn't exceed
+ * the configured value for the bus.
+ */
+#ifdef IMX_ENABLE_CLOCKS
+ err = clk_get_freq(sc->ipgclk, &freq);
+ if (err != 0) {
+ device_printf(sc->dev, "cannot get frequency\n");
+ return (err);
+ }
+ ipgfreq = (int32_t)freq;
+#else
+ ipgfreq = imx_ccm_ipg_hz();
+#endif
+ busfreq = IICBUS_GET_FREQUENCY(sc->iicbus, speed);
+ div = howmany(ipgfreq, busfreq);
+ for (i = 0; i < nitems(clkdiv_table); i++) {
+ if (clkdiv_table[i].divisor >= div)
+ break;
+ }
+
+ /*
+ * Calculate roughly how long it will take to transfer a byte (which
+ * requires 9 clock cycles) at the new bus speed. This value is used to
+ * pause() while waiting for transfer-complete. With a 66MHz IPG clock
+ * and the actual i2c bus speeds that leads to, for nominal 100KHz and
+ * 400KHz bus speeds the transfer times are roughly 104uS and 22uS.
+ */
+ busfreq = ipgfreq / clkdiv_table[i].divisor;
+ sc->byte_time_sbt = SBT_1US * (9000000 / busfreq);
+
+ /*
+ * Disable the controller (do the reset), and set the new clock divisor.
+ */
+ i2c_write_reg(sc, I2C_STATUS_REG, 0x0);
+ i2c_write_reg(sc, I2C_CONTROL_REG, 0x0);
+ i2c_write_reg(sc, I2C_FDR_REG, (uint8_t)clkdiv_table[i].regcode);
+
+ /*
+ * Now that the controller is idle, perform bus recovery. If the bus
+ * isn't hung, this a fairly fast no-op.
+ */
+ return (i2c_recover_bus(sc));
+}
+
+static int
+i2c_read(device_t dev, char *buf, int len, int *read, int last, int delay)
+{
+ struct i2c_softc *sc;
+ int error, reg;
+
+ sc = device_get_softc(dev);
+ *read = 0;
+
+ DEVICE_DEBUGF(sc, 1, "read 0x%02x len %d: ", sc->slave, len);
+ if (len) {
+ if (len == 1)
+ i2c_write_reg(sc, I2C_CONTROL_REG, I2CCR_MEN |
+ I2CCR_MSTA | I2CCR_TXAK);
+ else
+ i2c_write_reg(sc, I2C_CONTROL_REG, I2CCR_MEN |
+ I2CCR_MSTA);
+ /* Dummy read to prime the receiver. */
+ i2c_write_reg(sc, I2C_STATUS_REG, 0x0);
+ i2c_read_reg(sc, I2C_DATA_REG);
+ }
+
+ error = 0;
+ *read = 0;
+ while (*read < len) {
+ if ((error = wait_for_xfer(sc, false)) != IIC_NOERR)
+ break;
+ i2c_write_reg(sc, I2C_STATUS_REG, 0x0);
+ if (last) {
+ if (*read == len - 2) {
+ /* NO ACK on last byte */
+ i2c_write_reg(sc, I2C_CONTROL_REG, I2CCR_MEN |
+ I2CCR_MSTA | I2CCR_TXAK);
+ } else if (*read == len - 1) {
+ /* Transfer done, signal stop. */
+ i2c_write_reg(sc, I2C_CONTROL_REG, I2CCR_MEN |
+ I2CCR_TXAK);
+ wait_for_busbusy(sc, false);
+ }
+ }
+ reg = i2c_read_reg(sc, I2C_DATA_REG);
+ DEBUGF(sc, 1, "0x%02x ", reg);
+ *buf++ = reg;
+ (*read)++;
+ }
+ DEBUGF(sc, 1, "\n");
+
+ return (i2c_error_handler(sc, error));
+}
+
+static int
+i2c_write(device_t dev, const char *buf, int len, int *sent, int timeout)
+{
+ struct i2c_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ error = 0;
+ *sent = 0;
+ DEVICE_DEBUGF(sc, 1, "write 0x%02x len %d: ", sc->slave, len);
+ while (*sent < len) {
+ DEBUGF(sc, 1, "0x%02x ", *buf);
+ i2c_write_reg(sc, I2C_STATUS_REG, 0x0);
+ i2c_write_reg(sc, I2C_DATA_REG, *buf++);
+ if ((error = wait_for_xfer(sc, true)) != IIC_NOERR)
+ break;
+ (*sent)++;
+ }
+ DEBUGF(sc, 1, "\n");
+ return (i2c_error_handler(sc, error));
+}
diff --git a/sys/arm/freescale/imx/imx_iomux.c b/sys/arm/freescale/imx/imx_iomux.c
new file mode 100644
index 000000000000..f8c7d8e785a6
--- /dev/null
+++ b/sys/arm/freescale/imx/imx_iomux.c
@@ -0,0 +1,330 @@
+/*-
+ * Copyright (c) 2014 Ian Lepore
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Pin mux and pad control driver for imx5 and imx6.
+ *
+ * This driver implements the fdt_pinctrl interface for configuring the gpio and
+ * peripheral pins based on fdt configuration data.
+ *
+ * When the driver attaches, it walks the entire fdt tree and automatically
+ * configures the pins for each device which has a pinctrl-0 property and whose
+ * status is "okay". In addition it implements the fdt_pinctrl_configure()
+ * method which any other driver can call at any time to reconfigure its pins.
+ *
+ * The nature of the fsl,pins property in fdt data makes this driver's job very
+ * easy. Instead of representing each pin and pad configuration using symbolic
+ * properties such as pullup-enable="true" and so on, the data simply contains
+ * the addresses of the registers that control the pins, and the raw values to
+ * store in those registers.
+ *
+ * The imx5 and imx6 SoCs also have a small number of "general purpose
+ * registers" in the iomuxc device which are used to control an assortment
+ * of completely unrelated aspects of SoC behavior. This driver provides other
+ * drivers with direct access to those registers via simple accessor functions.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/fdt/fdt_pinctrl.h>
+
+#include <arm/freescale/imx/imx_iomuxvar.h>
+#include <arm/freescale/imx/imx_machdep.h>
+
+struct iomux_softc {
+ device_t dev;
+ struct resource *mem_res;
+ u_int last_gpregaddr;
+};
+
+static struct iomux_softc *iomux_sc;
+
+static struct ofw_compat_data compat_data[] = {
+ {"fsl,imx8mq-iomuxc", true},
+ {"fsl,imx6dl-iomuxc", true},
+ {"fsl,imx6q-iomuxc", true},
+ {"fsl,imx6sl-iomuxc", true},
+ {"fsl,imx6ul-iomuxc", true},
+ {"fsl,imx6sx-iomuxc", true},
+ {"fsl,imx53-iomuxc", true},
+ {"fsl,imx51-iomuxc", true},
+ {NULL, false},
+};
+
+/*
+ * Each tuple in an fsl,pins property contains these fields.
+ */
+struct pincfg {
+ uint32_t mux_reg;
+ uint32_t padconf_reg;
+ uint32_t input_reg;
+ uint32_t mux_val;
+ uint32_t input_val;
+ uint32_t padconf_val;
+};
+
+#define PADCONF_NONE (1U << 31) /* Do not configure pad. */
+#define PADCONF_SION (1U << 30) /* Force SION bit in mux register. */
+#define PADMUX_SION (1U << 4) /* The SION bit in the mux register. */
+
+static inline uint32_t
+RD4(struct iomux_softc *sc, bus_size_t off)
+{
+
+ return (bus_read_4(sc->mem_res, off));
+}
+
+static inline void
+WR4(struct iomux_softc *sc, bus_size_t off, uint32_t val)
+{
+
+ bus_write_4(sc->mem_res, off, val);
+}
+
+static void
+iomux_configure_input(struct iomux_softc *sc, uint32_t reg, uint32_t val)
+{
+ u_int select, mask, shift, width;
+
+ /* If register and value are zero, there is nothing to configure. */
+ if (reg == 0 && val == 0)
+ return;
+
+ /*
+ * If the config value has 0xff in the high byte it is encoded:
+ * 31 23 15 7 0
+ * | 0xff | shift | width | select |
+ * We need to mask out the old select value and OR in the new, using a
+ * mask of the given width and shifting the values up by shift.
+ */
+ if ((val & 0xff000000) == 0xff000000) {
+ select = val & 0x000000ff;
+ width = (val & 0x0000ff00) >> 8;
+ shift = (val & 0x00ff0000) >> 16;
+ mask = ((1u << width) - 1) << shift;
+ val = (RD4(sc, reg) & ~mask) | (select << shift);
+ }
+ WR4(sc, reg, val);
+}
+
+static int
+iomux_configure_pins(device_t dev, phandle_t cfgxref)
+{
+ struct iomux_softc *sc;
+ struct pincfg *cfgtuples, *cfg;
+ phandle_t cfgnode;
+ int i, ntuples;
+ uint32_t sion;
+
+ sc = device_get_softc(dev);
+ cfgnode = OF_node_from_xref(cfgxref);
+ ntuples = OF_getencprop_alloc_multi(cfgnode, "fsl,pins",
+ sizeof(*cfgtuples), (void **)&cfgtuples);
+ if (ntuples < 0)
+ return (ENOENT);
+ if (ntuples == 0)
+ return (0); /* Empty property is not an error. */
+ for (i = 0, cfg = cfgtuples; i < ntuples; i++, cfg++) {
+ sion = (cfg->padconf_val & PADCONF_SION) ? PADMUX_SION : 0;
+ WR4(sc, cfg->mux_reg, cfg->mux_val | sion);
+ iomux_configure_input(sc, cfg->input_reg, cfg->input_val);
+ if ((cfg->padconf_val & PADCONF_NONE) == 0)
+ WR4(sc, cfg->padconf_reg, cfg->padconf_val);
+ if (bootverbose) {
+ char name[32];
+ OF_getprop(cfgnode, "name", &name, sizeof(name));
+ printf("%16s: muxreg 0x%04x muxval 0x%02x "
+ "inpreg 0x%04x inpval 0x%02x "
+ "padreg 0x%04x padval 0x%08x\n",
+ name, cfg->mux_reg, cfg->mux_val | sion,
+ cfg->input_reg, cfg->input_val,
+ cfg->padconf_reg, cfg->padconf_val);
+ }
+ }
+ OF_prop_free(cfgtuples);
+ return (0);
+}
+
+static int
+iomux_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+ return (ENXIO);
+
+ device_set_desc(dev, "Freescale i.MX pin configuration");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+iomux_detach(device_t dev)
+{
+
+ /* This device is always present. */
+ return (EBUSY);
+}
+
+static int
+iomux_attach(device_t dev)
+{
+ struct iomux_softc * sc;
+ int rid;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ switch (imx_soc_type()) {
+ case IMXSOC_51:
+ sc->last_gpregaddr = 1 * sizeof(uint32_t);
+ break;
+ case IMXSOC_53:
+ sc->last_gpregaddr = 2 * sizeof(uint32_t);
+ break;
+ case IMXSOC_6DL:
+ case IMXSOC_6S:
+ case IMXSOC_6SL:
+ case IMXSOC_6Q:
+ sc->last_gpregaddr = 13 * sizeof(uint32_t);
+ break;
+ case IMXSOC_6UL:
+ sc->last_gpregaddr = 14 * sizeof(uint32_t);
+ break;
+ default:
+ device_printf(dev, "Unknown SoC type\n");
+ return (ENXIO);
+ }
+
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "Cannot allocate memory resources\n");
+ return (ENXIO);
+ }
+
+ iomux_sc = sc;
+
+ /*
+ * Register as a pinctrl device, and call the convenience function that
+ * walks the entire device tree invoking FDT_PINCTRL_CONFIGURE() on any
+ * pinctrl-0 property cells whose xref phandle refers to a configuration
+ * that is a child node of our node in the tree.
+ *
+ * The pinctrl bindings documentation specifically mentions that the
+ * pinctrl device itself may have a pinctrl-0 property which contains
+ * static configuration to be applied at device init time. The tree
+ * walk will automatically handle this for us when it passes through our
+ * node in the tree.
+ */
+ fdt_pinctrl_register(dev, "fsl,pins");
+ fdt_pinctrl_configure_tree(dev);
+
+ return (0);
+}
+
+uint32_t
+imx_iomux_gpr_get(u_int regaddr)
+{
+ struct iomux_softc * sc;
+
+ sc = iomux_sc;
+ KASSERT(sc != NULL, ("%s called before attach", __FUNCTION__));
+ KASSERT(regaddr >= 0 && regaddr <= sc->last_gpregaddr,
+ ("%s bad regaddr %u, max %u", __FUNCTION__, regaddr,
+ sc->last_gpregaddr));
+
+ return (RD4(iomux_sc, regaddr));
+}
+
+void
+imx_iomux_gpr_set(u_int regaddr, uint32_t val)
+{
+ struct iomux_softc * sc;
+
+ sc = iomux_sc;
+ KASSERT(sc != NULL, ("%s called before attach", __FUNCTION__));
+ KASSERT(regaddr >= 0 && regaddr <= sc->last_gpregaddr,
+ ("%s bad regaddr %u, max %u", __FUNCTION__, regaddr,
+ sc->last_gpregaddr));
+
+ WR4(iomux_sc, regaddr, val);
+}
+
+void
+imx_iomux_gpr_set_masked(u_int regaddr, uint32_t clrbits, uint32_t setbits)
+{
+ struct iomux_softc * sc;
+ uint32_t val;
+
+ sc = iomux_sc;
+ KASSERT(sc != NULL, ("%s called before attach", __FUNCTION__));
+ KASSERT(regaddr >= 0 && regaddr <= sc->last_gpregaddr,
+ ("%s bad regaddr %u, max %u", __FUNCTION__, regaddr,
+ sc->last_gpregaddr));
+
+ val = RD4(iomux_sc, regaddr * 4);
+ val = (val & ~clrbits) | setbits;
+ WR4(iomux_sc, regaddr, val);
+}
+
+static device_method_t imx_iomux_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, iomux_probe),
+ DEVMETHOD(device_attach, iomux_attach),
+ DEVMETHOD(device_detach, iomux_detach),
+
+ /* fdt_pinctrl interface */
+ DEVMETHOD(fdt_pinctrl_configure,iomux_configure_pins),
+
+ DEVMETHOD_END
+};
+
+static driver_t imx_iomux_driver = {
+ "imx_iomux",
+ imx_iomux_methods,
+ sizeof(struct iomux_softc),
+};
+
+static devclass_t imx_iomux_devclass;
+
+EARLY_DRIVER_MODULE(imx_iomux, simplebus, imx_iomux_driver,
+ imx_iomux_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_LATE);
diff --git a/sys/arm/freescale/imx/imx_iomuxreg.h b/sys/arm/freescale/imx/imx_iomuxreg.h
new file mode 100644
index 000000000000..c102ef4b4189
--- /dev/null
+++ b/sys/arm/freescale/imx/imx_iomuxreg.h
@@ -0,0 +1,61 @@
+/*-
+ * Copyright (c) 2015 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef IMX_IOMUXREG_H
+#define IMX_IOMUXREG_H
+
+#define IMX_IOMUXREG_LOWEST_SET_BIT(__mask) ((((__mask) - 1) & (__mask)) ^ (__mask))
+#define IMX_IOMUXREG_SHIFTIN(__x, __mask) ((__x) * IMX_IOMUXREG_LOWEST_SET_BIT(__mask))
+
+#define IMX_IOMUXREG_BIT(n) (1 << (n))
+#define IMX_IOMUXREG_BITS(__m, __n) \
+ ((IMX_IOMUXREG_BIT(MAX((__m), (__n)) + 1) - 1) ^ (IMX_IOMUXREG_BIT(MIN((__m), (__n))) - 1))
+
+#define IOMUXC_GPR0 0x00
+#define IOMUXC_GPR1 0x04
+#define IOMUXC_GPR2 0x08
+#define IOMUXC_GPR3 0x0C
+#define IOMUXC_GPR3_HDMI_MASK (3 << 2)
+#define IOMUXC_GPR3_HDMI_IPU1_DI0 (0 << 2)
+#define IOMUXC_GPR3_HDMI_IPU1_DI1 (1 << 2)
+#define IOMUXC_GPR3_HDMI_IPU2_DI0 (2 << 2)
+#define IOMUXC_GPR3_HDMI_IPU2_DI1 (3 << 2)
+
+#define IOMUX_GPR13 0x34
+#define IOMUX_GPR13_SATA_PHY_8(n) IMX_IOMUXREG_SHIFTIN(n, IMX_IOMUXREG_BITS(26, 24))
+#define IOMUX_GPR13_SATA_PHY_7(n) IMX_IOMUXREG_SHIFTIN(n, IMX_IOMUXREG_BITS(23, 19))
+#define IOMUX_GPR13_SATA_PHY_6(n) IMX_IOMUXREG_SHIFTIN(n, IMX_IOMUXREG_BITS(18, 16))
+#define IOMUX_GPR13_SATA_SPEED(n) IMX_IOMUXREG_SHIFTIN(n, (1 << 15))
+#define IOMUX_GPR13_SATA_PHY_5(n) IMX_IOMUXREG_SHIFTIN(n, (1 << 14))
+#define IOMUX_GPR13_SATA_PHY_4(n) IMX_IOMUXREG_SHIFTIN(n, IMX_IOMUXREG_BITS(13, 11))
+#define IOMUX_GPR13_SATA_PHY_3(n) IMX_IOMUXREG_SHIFTIN(n, IMX_IOMUXREG_BITS(10, 7))
+#define IOMUX_GPR13_SATA_PHY_2(n) IMX_IOMUXREG_SHIFTIN(n, IMX_IOMUXREG_BITS(6, 2))
+#define IOMUX_GPR13_SATA_PHY_1(n) IMX_IOMUXREG_SHIFTIN(n, (1 << 1))
+#define IOMUX_GPR13_SATA_PHY_0(n) IMX_IOMUXREG_SHIFTIN(n, (1 << 0))
+
+#endif
diff --git a/sys/arm/freescale/imx/imx_iomuxvar.h b/sys/arm/freescale/imx/imx_iomuxvar.h
new file mode 100644
index 000000000000..40fe7ef52e06
--- /dev/null
+++ b/sys/arm/freescale/imx/imx_iomuxvar.h
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 2014 Ian Lepore <ian@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef IMX_IOMUXVAR_H
+#define IMX_IOMUXVAR_H
+
+/*
+ * IOMUX interface functions
+ */
+void iomux_set_function(u_int pin, u_int fn);
+void iomux_set_pad(u_int pin, u_int cfg);
+u_int iomux_get_pad_config(u_int pin);
+
+/*
+ * The IOMUX Controller device has a small set of "general purpose registers"
+ * which control various aspects of SoC operation that really have nothing to do
+ * with IO pin assignments or pad control. These functions let other soc level
+ * code manipulate these values.
+ */
+uint32_t imx_iomux_gpr_get(u_int regaddr);
+void imx_iomux_gpr_set(u_int regaddr, uint32_t val);
+void imx_iomux_gpr_set_masked(u_int regaddr, uint32_t clrbits, uint32_t setbits);
+
+#endif
diff --git a/sys/arm/freescale/imx/imx_machdep.c b/sys/arm/freescale/imx/imx_machdep.c
new file mode 100644
index 000000000000..d920c41171b7
--- /dev/null
+++ b/sys/arm/freescale/imx/imx_machdep.c
@@ -0,0 +1,132 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Ian Lepore <ian@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "opt_platform.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/reboot.h>
+#include <sys/devmap.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/armreg.h>
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/machdep.h>
+
+#include <arm/freescale/imx/imx_machdep.h>
+#include <arm/freescale/imx/imx_wdogreg.h>
+
+SYSCTL_NODE(_hw, OID_AUTO, imx, CTLFLAG_RW | CTLFLAG_MPSAFE, NULL,
+ "i.MX container");
+
+static int last_reset_status;
+SYSCTL_UINT(_hw_imx, OID_AUTO, last_reset_status, CTLFLAG_RD,
+ &last_reset_status, 0, "Last reset status register");
+
+SYSCTL_STRING(_hw_imx, OID_AUTO, last_reset_reason, CTLFLAG_RD,
+ "unknown", 0, "Last reset reason");
+
+/*
+ * This code which manipulates the watchdog hardware is here to implement
+ * cpu_reset() because the watchdog is the only way for software to reset the
+ * chip. Why here and not in imx_wdog.c? Because there's no requirement that
+ * the watchdog driver be compiled in, but it's nice to be able to reboot even
+ * if it's not.
+ */
+void
+imx_wdog_cpu_reset(vm_offset_t wdcr_physaddr)
+{
+ volatile uint16_t cr, *pcr;
+
+ if ((pcr = devmap_ptov(wdcr_physaddr, sizeof(*pcr))) == NULL) {
+ printf("imx_wdog_cpu_reset(): "
+ "cannot find control register... locking up now.");
+ for (;;)
+ cpu_spinwait();
+ }
+ cr = *pcr;
+
+ /*
+ * If the watchdog hardware has been set up to trigger an external reset
+ * signal on watchdog timeout, then we do software-requested rebooting
+ * the same way, by asserting the external reset signal.
+ *
+ * Asserting external reset is supposed to result in some external
+ * component asserting the POR pin on the SoC, possibly after adjusting
+ * and stabilizing system voltages, or taking other system-wide reset
+ * actions. Just in case there is some kind of misconfiguration, we
+ * hang out and do nothing for a full second, then continue on into
+ * the code to assert a software reset as well.
+ */
+ if (cr & WDOG_CR_WDT) {
+ cr &= ~WDOG_CR_WDA; /* Assert active-low ext reset bit. */
+ *pcr = cr;
+ DELAY(1000000);
+ printf("imx_wdog_cpu_reset(): "
+ "External reset failed, trying internal cpu-reset\n");
+ DELAY(10000); /* Time for printf to appear */
+ }
+
+ /*
+ * Imx6 erratum ERR004346 says the SRS bit has to be cleared twice
+ * within the same cycle of the 32khz clock to reliably trigger the
+ * reset. Writing it 3 times in a row ensures at least 2 of the writes
+ * happen in the same 32k clock cycle.
+ */
+ cr &= ~WDOG_CR_SRS; /* Assert active-low software reset bit. */
+ *pcr = cr;
+ *pcr = cr;
+ *pcr = cr;
+
+ /* Reset happens on the next tick of the 32khz clock, wait for it. */
+ for (;;)
+ cpu_spinwait();
+}
+
+void
+imx_wdog_init_last_reset(vm_offset_t wdsr_phys)
+{
+ volatile uint16_t * psr;
+
+ if ((psr = devmap_ptov(wdsr_phys, sizeof(*psr))) == NULL)
+ return;
+ last_reset_status = *psr;
+ if (last_reset_status & WDOG_RSR_SFTW) {
+ sysctl___hw_imx_last_reset_reason.oid_arg1 = "SoftwareReset";
+ } else if (last_reset_status & WDOG_RSR_TOUT) {
+ sysctl___hw_imx_last_reset_reason.oid_arg1 = "WatchdogTimeout";
+ } else if (last_reset_status & WDOG_RSR_POR) {
+ sysctl___hw_imx_last_reset_reason.oid_arg1 = "PowerOnReset";
+ }
+}
diff --git a/sys/arm/freescale/imx/imx_machdep.h b/sys/arm/freescale/imx/imx_machdep.h
new file mode 100644
index 000000000000..a030a0791af6
--- /dev/null
+++ b/sys/arm/freescale/imx/imx_machdep.h
@@ -0,0 +1,71 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Ian Lepore <ian@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef IMX_MACHDEP_H
+#define IMX_MACHDEP_H
+
+#include <sys/types.h>
+#include <sys/sysctl.h>
+
+SYSCTL_DECL(_hw_imx);
+
+/* Common functions, implemented in imx_machdep.c. */
+
+void imx_wdog_cpu_reset(vm_offset_t _wdcr_phys) __attribute__((__noreturn__));
+void imx_wdog_init_last_reset(vm_offset_t _wdsr_phys);
+
+/* From here down, routines are implemented in imxNN_machdep.c. */
+
+/*
+ * SoC identity.
+ * According to the documentation, there is such a thing as an i.MX6 Dual
+ * (non-lite flavor). However, Freescale doesn't seem to have assigned it a
+ * number in their code for determining the SoC type in u-boot.
+ *
+ * To-do: put silicon revision numbers into the low-order bits somewhere.
+ */
+#define IMXSOC_51 0x51000000
+#define IMXSOC_53 0x53000000
+#define IMXSOC_6SL 0x60000000
+#define IMXSOC_6DL 0x61000000
+#define IMXSOC_6S 0x62000000
+#define IMXSOC_6Q 0x63000000
+#define IMXSOC_6UL 0x64000000
+#define IMXSOC_FAMSHIFT 28
+
+u_int imx_soc_type(void);
+
+static inline u_int
+imx_soc_family(void)
+{
+ return (imx_soc_type() >> IMXSOC_FAMSHIFT);
+}
+
+#endif
diff --git a/sys/arm/freescale/imx/imx_nop_usbphy.c b/sys/arm/freescale/imx/imx_nop_usbphy.c
new file mode 100644
index 000000000000..200511b2c273
--- /dev/null
+++ b/sys/arm/freescale/imx/imx_nop_usbphy.c
@@ -0,0 +1,122 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Ian Lepore <ian@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * USBPHY "no-op" driver for Freescale family of SoCs. This driver is used on
+ * SoCs which have usbphy hardware whose clocks need to be enabled, but no other
+ * action has to be taken to make the hardware work.
+ */
+
+#include "opt_bus.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+
+#include <arm/freescale/imx/imx_ccmvar.h>
+
+/*
+ * Table of supported FDT compat strings.
+ */
+static struct ofw_compat_data compat_data[] = {
+ {"nop-usbphy", true},
+ {"usb-nop-xceiv", true},
+ {NULL, false},
+};
+
+struct usbphy_softc {
+ device_t dev;
+ u_int phy_num;
+};
+
+static int
+usbphy_detach(device_t dev)
+{
+
+ return (0);
+}
+
+static int
+usbphy_attach(device_t dev)
+{
+ struct usbphy_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ /*
+ * Turn on the phy clocks.
+ */
+ imx_ccm_usbphy_enable(dev);
+
+ return (0);
+}
+
+static int
+usbphy_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) {
+ device_set_desc(dev, "Freescale USB PHY");
+ return (BUS_PROBE_DEFAULT);
+ }
+
+ return (ENXIO);
+}
+
+static device_method_t usbphy_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, usbphy_probe),
+ DEVMETHOD(device_attach, usbphy_attach),
+ DEVMETHOD(device_detach, usbphy_detach),
+
+ DEVMETHOD_END
+};
+
+static driver_t usbphy_driver = {
+ "usbphy",
+ usbphy_methods,
+ sizeof(struct usbphy_softc)
+};
+
+static devclass_t usbphy_devclass;
+
+DRIVER_MODULE(usbphy, simplebus, usbphy_driver, usbphy_devclass, 0, 0);
diff --git a/sys/arm/freescale/imx/imx_spi.c b/sys/arm/freescale/imx/imx_spi.c
new file mode 100644
index 000000000000..6624955ff124
--- /dev/null
+++ b/sys/arm/freescale/imx/imx_spi.c
@@ -0,0 +1,611 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2018 Ian Lepore <ian@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Driver for imx Enhanced Configurable SPI; master-mode only.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/gpio.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <arm/freescale/imx/imx_ccmvar.h>
+
+#include <dev/gpio/gpiobusvar.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/spibus/spi.h>
+#include <dev/spibus/spibusvar.h>
+
+#include "spibus_if.h"
+
+#define ECSPI_RXDATA 0x00
+#define ECSPI_TXDATA 0x04
+#define ECSPI_CTLREG 0x08
+#define CTLREG_BLEN_SHIFT 20
+#define CTLREG_BLEN_MASK 0x0fff
+#define CTLREG_CSEL_SHIFT 18
+#define CTLREG_CSEL_MASK 0x03
+#define CTLREG_DRCTL_SHIFT 16
+#define CTLREG_DRCTL_MASK 0x03
+#define CTLREG_PREDIV_SHIFT 12
+#define CTLREG_PREDIV_MASK 0x0f
+#define CTLREG_POSTDIV_SHIFT 8
+#define CTLREG_POSTDIV_MASK 0x0f
+#define CTLREG_CMODE_SHIFT 4
+#define CTLREG_CMODE_MASK 0x0f
+#define CTLREG_CMODES_MASTER (CTLREG_CMODE_MASK << CTLREG_CMODE_SHIFT)
+#define CTLREG_SMC (1u << 3)
+#define CTLREG_XCH (1u << 2)
+#define CTLREG_HT (1u << 1)
+#define CTLREG_EN (1u << 0)
+#define ECSPI_CFGREG 0x0c
+#define CFGREG_HTLEN_SHIFT 24
+#define CFGREG_SCLKCTL_SHIFT 20
+#define CFGREG_DATACTL_SHIFT 16
+#define CFGREG_SSPOL_SHIFT 12
+#define CFGREG_SSCTL_SHIFT 8
+#define CFGREG_SCLKPOL_SHIFT 4
+#define CFGREG_SCLKPHA_SHIFT 0
+#define CFGREG_MASK 0x0f /* all CFGREG fields are 4 bits */
+#define ECSPI_INTREG 0x10
+#define INTREG_TCEN (1u << 7)
+#define INTREG_ROEN (1u << 6)
+#define INTREG_RFEN (1u << 5)
+#define INTREG_RDREN (1u << 4)
+#define INTREG_RREN (1u << 3)
+#define INTREG_TFEN (1u << 2)
+#define INTREG_TDREN (1u << 1)
+#define INTREG_TEEN (1u << 0)
+#define ECSPI_DMAREG 0x14
+#define DMA_RX_THRESH_SHIFT 16
+#define DMA_RX_THRESH_MASK 0x3f
+#define DMA_TX_THRESH_SHIFT 0
+#define DMA_TX_THRESH_MASK 0x3f
+#define ECSPI_STATREG 0x18
+#define SREG_TC (1u << 7)
+#define SREG_RO (1u << 6)
+#define SREG_RF (1u << 5)
+#define SREG_RDR (1u << 4)
+#define SREG_RR (1u << 3)
+#define SREG_TF (1u << 2)
+#define SREG_TDR (1u << 1)
+#define SREG_TE (1u << 0)
+#define ECSPI_PERIODREG 0x1c
+#define ECSPI_TESTREG 0x20
+
+#define CS_MAX 4 /* Max number of chip selects. */
+#define CS_MASK 0x03 /* Mask flag bits out of chipsel. */
+
+#define FIFO_SIZE 64
+#define FIFO_RXTHRESH 32
+#define FIFO_TXTHRESH 32
+
+struct spi_softc {
+ device_t dev;
+ device_t spibus;
+ struct mtx mtx;
+ struct resource *memres;
+ struct resource *intres;
+ void *inthandle;
+ gpio_pin_t cspins[CS_MAX];
+ u_int debug;
+ u_int basefreq;
+ uint32_t ctlreg;
+ uint32_t intreg;
+ uint32_t fifocnt;
+ uint8_t *rxbuf;
+ uint32_t rxidx;
+ uint32_t rxlen;
+ uint8_t *txbuf;
+ uint32_t txidx;
+ uint32_t txlen;
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"fsl,imx51-ecspi", true},
+ {"fsl,imx53-ecspi", true},
+ {"fsl,imx6dl-ecspi", true},
+ {"fsl,imx6q-ecspi", true},
+ {"fsl,imx6sx-ecspi", true},
+ {"fsl,imx6ul-ecspi", true},
+ {NULL, false}
+};
+
+static inline uint32_t
+RD4(struct spi_softc *sc, bus_size_t offset)
+{
+
+ return (bus_read_4(sc->memres, offset));
+}
+
+static inline void
+WR4(struct spi_softc *sc, bus_size_t offset, uint32_t value)
+{
+
+ bus_write_4(sc->memres, offset, value);
+}
+
+static u_int
+spi_calc_clockdiv(struct spi_softc *sc, u_int busfreq)
+{
+ u_int post, pre;
+
+ /* Returning 0 effectively sets both dividers to 1. */
+ if (sc->basefreq <= busfreq)
+ return (0);
+
+ /*
+ * Brute-force this; all real-world bus speeds are going to be found on
+ * the 1st or 2nd time through this loop.
+ */
+ for (post = 0; post < 16; ++post) {
+ pre = ((sc->basefreq >> post) / busfreq) - 1;
+ if (pre < 16)
+ break;
+ }
+ if (post == 16) {
+ /* The lowest we can go is ~115 Hz. */
+ pre = 15;
+ post = 15;
+ }
+
+ if (sc->debug >= 2) {
+ device_printf(sc->dev,
+ "base %u bus %u; pre %u, post %u; actual busfreq %u\n",
+ sc->basefreq, busfreq, pre, post,
+ (sc->basefreq / (pre + 1)) / (1 << post));
+ }
+
+ return (pre << CTLREG_PREDIV_SHIFT) | (post << CTLREG_POSTDIV_SHIFT);
+}
+
+static void
+spi_set_chipsel(struct spi_softc *sc, u_int cs, bool active)
+{
+ bool pinactive;
+
+ /*
+ * This is kinda crazy... the gpio pins for chipsel are defined as
+ * active-high in the dts, but are supposed to be treated as active-low
+ * by this driver. So to turn on chipsel we have to invert the value
+ * passed to gpio_pin_set_active(). Then, to make it more fun, any
+ * slave can say its chipsel is active-high, so if that option is
+ * on, we have to invert the value again.
+ */
+ pinactive = !active ^ (bool)(cs & SPIBUS_CS_HIGH);
+
+ if (sc->debug >= 2) {
+ device_printf(sc->dev, "chipsel %u changed to %u\n",
+ (cs & ~SPIBUS_CS_HIGH), pinactive);
+ }
+
+ /*
+ * Change the pin, then do a dummy read of its current state to ensure
+ * that the state change reaches the hardware before proceeding.
+ */
+ gpio_pin_set_active(sc->cspins[cs & ~SPIBUS_CS_HIGH], pinactive);
+ gpio_pin_is_active(sc->cspins[cs & ~SPIBUS_CS_HIGH], &pinactive);
+}
+
+static void
+spi_hw_setup(struct spi_softc *sc, u_int cs, u_int mode, u_int freq)
+{
+ uint32_t reg;
+
+ /*
+ * Set up control register, and write it first to bring the device out
+ * of reset.
+ */
+ sc->ctlreg = CTLREG_EN | CTLREG_CMODES_MASTER | CTLREG_SMC;
+ sc->ctlreg |= spi_calc_clockdiv(sc, freq);
+ sc->ctlreg |= 7 << CTLREG_BLEN_SHIFT; /* XXX byte at a time */
+ WR4(sc, ECSPI_CTLREG, sc->ctlreg);
+
+ /*
+ * Set up the config register. Note that we do all transfers with the
+ * SPI hardware's chip-select set to zero. The actual chip select is
+ * handled with a gpio pin.
+ */
+ reg = 0;
+ if (cs & SPIBUS_CS_HIGH)
+ reg |= 1u << CFGREG_SSPOL_SHIFT;
+ if (mode & SPIBUS_MODE_CPHA)
+ reg |= 1u << CFGREG_SCLKPHA_SHIFT;
+ if (mode & SPIBUS_MODE_CPOL) {
+ reg |= 1u << CFGREG_SCLKPOL_SHIFT;
+ reg |= 1u << CFGREG_SCLKCTL_SHIFT;
+ }
+ WR4(sc, ECSPI_CFGREG, reg);
+
+ /*
+ * Set up the rx/tx FIFO interrupt thresholds.
+ */
+ reg = (FIFO_RXTHRESH << DMA_RX_THRESH_SHIFT);
+ reg |= (FIFO_TXTHRESH << DMA_TX_THRESH_SHIFT);
+ WR4(sc, ECSPI_DMAREG, reg);
+
+ /*
+ * Do a dummy read, to make sure the preceding writes reach the spi
+ * hardware before we assert any gpio chip select.
+ */
+ (void)RD4(sc, ECSPI_CFGREG);
+}
+
+static void
+spi_empty_rxfifo(struct spi_softc *sc)
+{
+
+ while (sc->rxidx < sc->rxlen && (RD4(sc, ECSPI_STATREG) & SREG_RR)) {
+ sc->rxbuf[sc->rxidx++] = (uint8_t)RD4(sc, ECSPI_RXDATA);
+ --sc->fifocnt;
+ }
+}
+
+static void
+spi_fill_txfifo(struct spi_softc *sc)
+{
+
+ while (sc->txidx < sc->txlen && sc->fifocnt < FIFO_SIZE) {
+ WR4(sc, ECSPI_TXDATA, sc->txbuf[sc->txidx++]);
+ ++sc->fifocnt;
+ }
+
+ /*
+ * If we're out of data, disable tx data ready (threshold) interrupts,
+ * and enable tx fifo empty interrupts.
+ */
+ if (sc->txidx == sc->txlen)
+ sc->intreg = (sc->intreg & ~INTREG_TDREN) | INTREG_TEEN;
+}
+
+static void
+spi_intr(void *arg)
+{
+ struct spi_softc *sc = arg;
+ uint32_t intreg, status;
+
+ mtx_lock(&sc->mtx);
+
+ sc = arg;
+ intreg = sc->intreg;
+ status = RD4(sc, ECSPI_STATREG);
+ WR4(sc, ECSPI_STATREG, status); /* Clear w1c bits. */
+
+ /*
+ * If we get an overflow error, just signal that the transfer is done
+ * and wakeup the waiting thread, which will see that txidx != txlen and
+ * return an IO error to the caller.
+ */
+ if (__predict_false(status & SREG_RO)) {
+ if (sc->debug || bootverbose) {
+ device_printf(sc->dev, "rxoverflow rxidx %u txidx %u\n",
+ sc->rxidx, sc->txidx);
+ }
+ sc->intreg = 0;
+ wakeup(sc);
+ mtx_unlock(&sc->mtx);
+ return;
+ }
+
+ if (status & SREG_RR)
+ spi_empty_rxfifo(sc);
+
+ if (status & SREG_TDR)
+ spi_fill_txfifo(sc);
+
+ /*
+ * If we're out of bytes to send...
+ * - If Transfer Complete is set (shift register is empty) and we've
+ * received everything we expect, we're all done.
+ * - Else if Tx Fifo Empty is set, we need to stop waiting for that and
+ * switch to waiting for Transfer Complete (wait for shift register
+ * to empty out), and also for Receive Ready (last of incoming data).
+ */
+ if (sc->txidx == sc->txlen) {
+ if ((status & SREG_TC) && sc->fifocnt == 0) {
+ sc->intreg = 0;
+ wakeup(sc);
+ } else if (status & SREG_TE) {
+ sc->intreg &= ~(sc->intreg & ~INTREG_TEEN);
+ sc->intreg |= INTREG_TCEN | INTREG_RREN;
+ }
+ }
+
+ /*
+ * If interrupt flags changed, write the new flags to the hardware and
+ * do a dummy readback to ensure the changes reach the hardware before
+ * we exit the isr.
+ */
+ if (sc->intreg != intreg) {
+ WR4(sc, ECSPI_INTREG, sc->intreg);
+ (void)RD4(sc, ECSPI_INTREG);
+ }
+
+ if (sc->debug >= 3) {
+ device_printf(sc->dev,
+ "spi_intr, sreg 0x%08x intreg was 0x%08x now 0x%08x\n",
+ status, intreg, sc->intreg);
+ }
+
+ mtx_unlock(&sc->mtx);
+}
+
+static int
+spi_xfer_buf(struct spi_softc *sc, void *rxbuf, void *txbuf, uint32_t len)
+{
+ int err;
+
+ if (sc->debug >= 1) {
+ device_printf(sc->dev,
+ "spi_xfer_buf, rxbuf %p txbuf %p len %u\n",
+ rxbuf, txbuf, len);
+ }
+
+ if (len == 0)
+ return (0);
+
+ sc->rxbuf = rxbuf;
+ sc->rxlen = len;
+ sc->rxidx = 0;
+ sc->txbuf = txbuf;
+ sc->txlen = len;
+ sc->txidx = 0;
+ sc->intreg = INTREG_RDREN | INTREG_TDREN;
+ spi_fill_txfifo(sc);
+
+ /* Enable interrupts last; spi_fill_txfifo() can change sc->intreg */
+ WR4(sc, ECSPI_INTREG, sc->intreg);
+
+ err = 0;
+ while (err == 0 && sc->intreg != 0)
+ err = msleep(sc, &sc->mtx, 0, "imxspi", 10 * hz);
+
+ if (sc->rxidx != sc->rxlen || sc->txidx != sc->txlen)
+ err = EIO;
+
+ return (err);
+}
+
+static int
+spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
+{
+ struct spi_softc *sc = device_get_softc(dev);
+ uint32_t cs, mode, clock;
+ int err;
+
+ spibus_get_cs(child, &cs);
+ spibus_get_clock(child, &clock);
+ spibus_get_mode(child, &mode);
+
+ if (cs > CS_MAX || sc->cspins[cs] == NULL) {
+ if (sc->debug || bootverbose)
+ device_printf(sc->dev, "Invalid chip select %u\n", cs);
+ return (EINVAL);
+ }
+
+ mtx_lock(&sc->mtx);
+ device_busy(sc->dev);
+
+ if (sc->debug >= 1) {
+ device_printf(sc->dev,
+ "spi_transfer, cs 0x%x clock %u mode %u\n",
+ cs, clock, mode);
+ }
+
+ /* Set up the hardware and select the device. */
+ spi_hw_setup(sc, cs, mode, clock);
+ spi_set_chipsel(sc, cs, true);
+
+ /* Transfer command then data bytes. */
+ err = 0;
+ if (cmd->tx_cmd_sz > 0)
+ err = spi_xfer_buf(sc, cmd->rx_cmd, cmd->tx_cmd,
+ cmd->tx_cmd_sz);
+ if (cmd->tx_data_sz > 0 && err == 0)
+ err = spi_xfer_buf(sc, cmd->rx_data, cmd->tx_data,
+ cmd->tx_data_sz);
+
+ /* Deselect the device, turn off (and reset) hardware. */
+ spi_set_chipsel(sc, cs, false);
+ WR4(sc, ECSPI_CTLREG, 0);
+
+ device_unbusy(sc->dev);
+ mtx_unlock(&sc->mtx);
+
+ return (err);
+}
+
+static phandle_t
+spi_get_node(device_t bus, device_t dev)
+{
+
+ /*
+ * Share our controller node with our spibus child; it instantiates
+ * devices by walking the children contained within our node.
+ */
+ return ofw_bus_get_node(bus);
+}
+
+static int
+spi_detach(device_t dev)
+{
+ struct spi_softc *sc = device_get_softc(dev);
+ int error, idx;
+
+ if ((error = bus_generic_detach(sc->dev)) != 0)
+ return (error);
+
+ if (sc->spibus != NULL)
+ device_delete_child(dev, sc->spibus);
+
+ for (idx = 0; idx < nitems(sc->cspins); ++idx) {
+ if (sc->cspins[idx] != NULL)
+ gpio_pin_release(sc->cspins[idx]);
+ }
+
+ if (sc->inthandle != NULL)
+ bus_teardown_intr(sc->dev, sc->intres, sc->inthandle);
+ if (sc->intres != NULL)
+ bus_release_resource(sc->dev, SYS_RES_IRQ, 0, sc->intres);
+ if (sc->memres != NULL)
+ bus_release_resource(sc->dev, SYS_RES_MEMORY, 0, sc->memres);
+
+ mtx_destroy(&sc->mtx);
+
+ return (0);
+}
+
+static int
+spi_attach(device_t dev)
+{
+ struct spi_softc *sc = device_get_softc(dev);
+ phandle_t node;
+ int err, idx, rid;
+
+ sc->dev = dev;
+ sc->basefreq = imx_ccm_ecspi_hz();
+
+ mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ /* Set up debug-enable sysctl. */
+ SYSCTL_ADD_INT(device_get_sysctl_ctx(sc->dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)),
+ OID_AUTO, "debug", CTLFLAG_RWTUN, &sc->debug, 0,
+ "Enable debug, higher values = more info");
+
+ /* Allocate mmio register access resources. */
+ rid = 0;
+ sc->memres = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->memres == NULL) {
+ device_printf(sc->dev, "could not allocate registers\n");
+ spi_detach(sc->dev);
+ return (ENXIO);
+ }
+
+ /* Allocate interrupt resources and set up handler. */
+ rid = 0;
+ sc->intres = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (sc->intres == NULL) {
+ device_printf(sc->dev, "could not allocate interrupt\n");
+ device_detach(sc->dev);
+ return (ENXIO);
+ }
+ err = bus_setup_intr(sc->dev, sc->intres, INTR_TYPE_MISC | INTR_MPSAFE,
+ NULL, spi_intr, sc, &sc->inthandle);
+ if (err != 0) {
+ device_printf(sc->dev, "could not setup interrupt handler");
+ device_detach(sc->dev);
+ return (ENXIO);
+ }
+
+ /* Allocate gpio pins for configured chip selects. */
+ node = ofw_bus_get_node(sc->dev);
+ for (idx = 0; idx < nitems(sc->cspins); ++idx) {
+ err = gpio_pin_get_by_ofw_propidx(sc->dev, node, "cs-gpios",
+ idx, &sc->cspins[idx]);
+ if (err == 0) {
+ gpio_pin_setflags(sc->cspins[idx], GPIO_PIN_OUTPUT);
+ } else if (sc->debug >= 2) {
+ device_printf(sc->dev,
+ "cannot configure gpio for chip select %u\n", idx);
+ }
+ }
+
+ /*
+ * Hardware init: put all channels into Master mode, turn off the enable
+ * bit (gates off clocks); we only enable the hardware while xfers run.
+ */
+ WR4(sc, ECSPI_CTLREG, CTLREG_CMODES_MASTER);
+
+ /*
+ * Add the spibus driver as a child, and setup a one-shot intrhook to
+ * attach it after interrupts are working. It will attach actual SPI
+ * devices as its children, and those devices may need to do IO during
+ * their attach. We can't do IO until timers and interrupts are working.
+ */
+ sc->spibus = device_add_child(dev, "spibus", -1);
+ return (bus_delayed_attach_children(dev));
+}
+
+static int
+spi_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+ return (ENXIO);
+
+ device_set_desc(dev, "i.MX ECSPI Master");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static device_method_t spi_methods[] = {
+ DEVMETHOD(device_probe, spi_probe),
+ DEVMETHOD(device_attach, spi_attach),
+ DEVMETHOD(device_detach, spi_detach),
+
+ /* spibus_if */
+ DEVMETHOD(spibus_transfer, spi_transfer),
+
+ /* ofw_bus_if */
+ DEVMETHOD(ofw_bus_get_node, spi_get_node),
+
+ DEVMETHOD_END
+};
+
+static driver_t spi_driver = {
+ "imx_spi",
+ spi_methods,
+ sizeof(struct spi_softc),
+};
+
+static devclass_t spi_devclass;
+
+DRIVER_MODULE(imx_spi, simplebus, spi_driver, spi_devclass, 0, 0);
+DRIVER_MODULE(ofw_spibus, imx_spi, ofw_spibus_driver, ofw_spibus_devclass, 0, 0);
+MODULE_DEPEND(imx_spi, ofw_spibus, 1, 1, 1);
+SIMPLEBUS_PNP_INFO(compat_data);
diff --git a/sys/arm/freescale/imx/imx_wdog.c b/sys/arm/freescale/imx/imx_wdog.c
new file mode 100644
index 000000000000..df2ae510e5fb
--- /dev/null
+++ b/sys/arm/freescale/imx/imx_wdog.c
@@ -0,0 +1,264 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012, 2013 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Oleksandr Rybalko under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/eventhandler.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/watchdog.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/freescale/imx/imx_machdep.h>
+#include <arm/freescale/imx/imx_wdogreg.h>
+
+struct imx_wdog_softc {
+ struct mtx sc_mtx;
+ device_t sc_dev;
+ struct resource *sc_res[2];
+ void *sc_ih;
+ uint32_t sc_timeout;
+ bool sc_pde_enabled;
+};
+
+static struct resource_spec imx_wdog_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE },
+ RESOURCE_SPEC_END
+};
+
+#define MEMRES 0
+#define IRQRES 1
+
+static struct ofw_compat_data compat_data[] = {
+ {"fsl,imx6sx-wdt", 1},
+ {"fsl,imx6sl-wdt", 1},
+ {"fsl,imx6q-wdt", 1},
+ {"fsl,imx53-wdt", 1},
+ {"fsl,imx51-wdt", 1},
+ {"fsl,imx50-wdt", 1},
+ {"fsl,imx35-wdt", 1},
+ {"fsl,imx27-wdt", 1},
+ {"fsl,imx25-wdt", 1},
+ {"fsl,imx21-wdt", 1},
+ {NULL, 0}
+};
+
+static inline uint16_t
+RD2(struct imx_wdog_softc *sc, bus_size_t offs)
+{
+
+ return (bus_read_2(sc->sc_res[MEMRES], offs));
+}
+
+static inline void
+WR2(struct imx_wdog_softc *sc, bus_size_t offs, uint16_t val)
+{
+
+ bus_write_2(sc->sc_res[MEMRES], offs, val);
+}
+
+static int
+imx_wdog_enable(struct imx_wdog_softc *sc, u_int timeout)
+{
+ uint16_t reg;
+
+ if (timeout < 1 || timeout > 128)
+ return (EINVAL);
+
+ mtx_lock(&sc->sc_mtx);
+ if (timeout != sc->sc_timeout) {
+ sc->sc_timeout = timeout;
+ reg = RD2(sc, WDOG_CR_REG);
+ reg &= ~WDOG_CR_WT_MASK;
+ reg |= ((2 * timeout - 1) << WDOG_CR_WT_SHIFT);
+ WR2(sc, WDOG_CR_REG, reg | WDOG_CR_WDE);
+ }
+ /* Refresh counter */
+ WR2(sc, WDOG_SR_REG, WDOG_SR_STEP1);
+ WR2(sc, WDOG_SR_REG, WDOG_SR_STEP2);
+ /* Watchdog active, can disable rom-boot watchdog. */
+ if (sc->sc_pde_enabled) {
+ sc->sc_pde_enabled = false;
+ reg = RD2(sc, WDOG_MCR_REG);
+ WR2(sc, WDOG_MCR_REG, reg & ~WDOG_MCR_PDE);
+ }
+ mtx_unlock(&sc->sc_mtx);
+
+ return (0);
+}
+
+static void
+imx_watchdog(void *arg, u_int cmd, int *error)
+{
+ struct imx_wdog_softc *sc;
+ u_int timeout;
+
+ sc = arg;
+ if (cmd == 0) {
+ if (bootverbose)
+ device_printf(sc->sc_dev, "Can not be disabled.\n");
+ *error = EOPNOTSUPP;
+ } else {
+ timeout = (u_int)((1ULL << (cmd & WD_INTERVAL)) / 1000000000U);
+ if (imx_wdog_enable(sc, timeout) == 0)
+ *error = 0;
+ }
+}
+
+static int
+imx_wdog_intr(void *arg)
+{
+ struct imx_wdog_softc *sc = arg;
+
+ /*
+ * When configured for external reset, the actual reset is supposed to
+ * happen when some external device responds to the assertion of the
+ * WDOG_B signal by asserting the POR signal to the chip. This
+ * interrupt handler is a backstop mechanism; it is set up to fire
+ * simultaneously with WDOG_B, and if the external reset happens we'll
+ * never actually make it to here. If we do make it here, just trigger
+ * a software reset. That code will see that external reset is
+ * configured, and it will wait for 1 second for it to take effect, then
+ * it will do a software reset as a fallback.
+ */
+ imx_wdog_cpu_reset(BUS_SPACE_PHYSADDR(sc->sc_res[MEMRES], WDOG_CR_REG));
+
+ return (FILTER_HANDLED); /* unreached */
+}
+
+static int
+imx_wdog_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Freescale i.MX Watchdog");
+ return (0);
+}
+
+static int
+imx_wdog_attach(device_t dev)
+{
+ struct imx_wdog_softc *sc;
+ pcell_t timeout;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+
+ if (bus_alloc_resources(dev, imx_wdog_spec, sc->sc_res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), "imx_wdt", MTX_DEF);
+
+ /*
+ * If we're configured to assert an external reset signal, set up the
+ * hardware to do so, and install an interrupt handler whose only
+ * purpose is to backstop the external reset. Don't worry if the
+ * interrupt setup fails, since it's only a backstop measure.
+ */
+ if (ofw_bus_has_prop(sc->sc_dev, "fsl,ext-reset-output")) {
+ WR2(sc, WDOG_CR_REG, WDOG_CR_WDT | RD2(sc, WDOG_CR_REG));
+ bus_setup_intr(sc->sc_dev, sc->sc_res[IRQRES],
+ INTR_TYPE_MISC | INTR_MPSAFE, imx_wdog_intr, NULL, sc,
+ &sc->sc_ih);
+ WR2(sc, WDOG_ICR_REG, WDOG_ICR_WIE); /* Enable, count is 0. */
+ }
+
+ /*
+ * Note whether the rom-boot so-called "power-down" watchdog is active,
+ * so we can disable it when the regular watchdog is first enabled.
+ */
+ if (RD2(sc, WDOG_MCR_REG) & WDOG_MCR_PDE)
+ sc->sc_pde_enabled = true;
+
+ EVENTHANDLER_REGISTER(watchdog_list, imx_watchdog, sc, 0);
+
+ /* If there is a timeout-sec property, activate the watchdog. */
+ if (OF_getencprop(ofw_bus_get_node(sc->sc_dev), "timeout-sec",
+ &timeout, sizeof(timeout)) == sizeof(timeout)) {
+ if (timeout < 1 || timeout > 128) {
+ device_printf(sc->sc_dev, "ERROR: bad timeout-sec "
+ "property value %u, using 128\n", timeout);
+ timeout = 128;
+ }
+ imx_wdog_enable(sc, timeout);
+ device_printf(sc->sc_dev, "watchdog enabled using "
+ "timeout-sec property value %u\n", timeout);
+ }
+
+ /*
+ * The watchdog hardware cannot be disabled, so there's little point in
+ * coding up a detach() routine to carefully tear everything down, just
+ * make the device busy so that detach can't happen.
+ */
+ device_busy(sc->sc_dev);
+ return (0);
+}
+
+static device_method_t imx_wdog_methods[] = {
+ DEVMETHOD(device_probe, imx_wdog_probe),
+ DEVMETHOD(device_attach, imx_wdog_attach),
+ DEVMETHOD_END
+};
+
+static driver_t imx_wdog_driver = {
+ "imx_wdog",
+ imx_wdog_methods,
+ sizeof(struct imx_wdog_softc),
+};
+
+static devclass_t imx_wdog_devclass;
+
+EARLY_DRIVER_MODULE(imx_wdog, simplebus, imx_wdog_driver,
+ imx_wdog_devclass, 0, 0, BUS_PASS_TIMER);
+SIMPLEBUS_PNP_INFO(compat_data);
diff --git a/sys/arm/freescale/imx/imx_wdogreg.h b/sys/arm/freescale/imx/imx_wdogreg.h
new file mode 100644
index 000000000000..d192a28bbdc5
--- /dev/null
+++ b/sys/arm/freescale/imx/imx_wdogreg.h
@@ -0,0 +1,63 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012, 2013 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Oleksandr Rybalko under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define WDOG_CLK_FREQ 32768
+
+#define WDOG_CR_REG 0x00 /* Control Register */
+#define WDOG_CR_WT_MASK 0xff00 /* Count; 0.5 sec units */
+#define WDOG_CR_WT_SHIFT 8
+#define WDOG_CR_WDW (1u << 7) /* Suspend when in WAIT mode */
+#define WDOG_CR_WDA (1u << 5) /* Don't assert ext reset */
+#define WDOG_CR_SRS (1u << 4) /* Don't assert soft reset */
+#define WDOG_CR_WDT (1u << 3) /* Assert ext reset on timeout */
+#define WDOG_CR_WDE (1u << 2) /* Watchdog Enable */
+#define WDOG_CR_WDBG (1u << 1) /* Suspend when DBG mode */
+#define WDOG_CR_WDZST (1u << 0) /* Suspend when LP mode */
+
+#define WDOG_SR_REG 0x02 /* Service Register */
+#define WDOG_SR_STEP1 0x5555
+#define WDOG_SR_STEP2 0xaaaa
+
+#define WDOG_RSR_REG 0x04 /* Reset Status Register */
+#define WDOG_RSR_POR (1u << 4) /* Due to Power-On Reset */
+#define WDOG_RSR_TOUT (1u << 1) /* Due WDog timeout reset */
+#define WDOG_RSR_SFTW (1u << 0) /* Due Soft reset */
+
+#define WDOG_ICR_REG 0x06 /* Interrupt Control Register */
+#define WDOG_ICR_WIE (1u << 15) /* Enable Interrupt */
+#define WDOG_ICR_WTIS (1u << 14) /* Interrupt has occurred */
+#define WDOG_ICR_WTCT_MASK 0x00ff /* Interrupt lead time in 0.5s */
+#define WDOG_ICR_WTCT_SHIFT 0 /* units before reset occurs */
+
+#define WDOG_MCR_REG 0x08 /* Miscellaneous Control Register */
+#define WDOG_MCR_PDE (1u << 0) /* Power-down enable */
diff --git a/sys/arm/freescale/imx/std.imx51 b/sys/arm/freescale/imx/std.imx51
new file mode 100644
index 000000000000..092ec7e47384
--- /dev/null
+++ b/sys/arm/freescale/imx/std.imx51
@@ -0,0 +1,6 @@
+# $FreeBSD$
+machine arm armv7
+cpu CPU_CORTEXA
+makeoptions CONF_CFLAGS="-march=armv7a"
+
+files "../freescale/imx/files.imx5"
diff --git a/sys/arm/freescale/imx/std.imx53 b/sys/arm/freescale/imx/std.imx53
new file mode 100644
index 000000000000..092ec7e47384
--- /dev/null
+++ b/sys/arm/freescale/imx/std.imx53
@@ -0,0 +1,6 @@
+# $FreeBSD$
+machine arm armv7
+cpu CPU_CORTEXA
+makeoptions CONF_CFLAGS="-march=armv7a"
+
+files "../freescale/imx/files.imx5"
diff --git a/sys/arm/freescale/imx/std.imx6 b/sys/arm/freescale/imx/std.imx6
new file mode 100644
index 000000000000..99b6528a8922
--- /dev/null
+++ b/sys/arm/freescale/imx/std.imx6
@@ -0,0 +1,7 @@
+# $FreeBSD$
+machine arm armv7
+cpu CPU_CORTEXA
+makeoptions CONF_CFLAGS="-march=armv7a"
+
+files "../freescale/imx/files.imx6"
+
diff --git a/sys/arm/freescale/imx/tzic.c b/sys/arm/freescale/imx/tzic.c
new file mode 100644
index 000000000000..6f3aca14da84
--- /dev/null
+++ b/sys/arm/freescale/imx/tzic.c
@@ -0,0 +1,312 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012, 2013 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Oleksandr Rybalko under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/proc.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/freescale/imx/imx51_tzicreg.h>
+
+#include "pic_if.h"
+
+#define TZIC_NIRQS 128
+
+struct tzic_irqsrc {
+ struct intr_irqsrc isrc;
+ u_int irq;
+};
+
+struct tzic_softc {
+ device_t dev;
+ struct resource *tzicregs;
+ struct tzic_irqsrc isrcs[TZIC_NIRQS];
+};
+
+static struct tzic_softc *tzic_sc;
+
+static inline uint32_t
+tzic_read_4(struct tzic_softc *sc, int reg)
+{
+
+ return (bus_read_4(sc->tzicregs, reg));
+}
+
+static inline void
+tzic_write_4(struct tzic_softc *sc, int reg, uint32_t val)
+{
+
+ bus_write_4(sc->tzicregs, reg, val);
+}
+
+static inline void
+tzic_irq_eoi(struct tzic_softc *sc)
+{
+
+ tzic_write_4(sc, TZIC_PRIOMASK, 0xff);
+}
+
+static inline void
+tzic_irq_mask(struct tzic_softc *sc, u_int irq)
+{
+
+ tzic_write_4(sc, TZIC_ENCLEAR(irq >> 5), (1u << (irq & 0x1f)));
+}
+
+static inline void
+tzic_irq_unmask(struct tzic_softc *sc, u_int irq)
+{
+
+ tzic_write_4(sc, TZIC_ENSET(irq >> 5), (1u << (irq & 0x1f)));
+}
+
+static int
+tzic_intr(void *arg)
+{
+ struct tzic_softc *sc = arg;
+ int b, i, irq;
+ uint32_t pending;
+
+ /* Get active interrupt */
+ for (i = 0; i < TZIC_NIRQS / 32; ++i) {
+ pending = tzic_read_4(sc, TZIC_PND(i));
+ if ((b = 31 - __builtin_clz(pending)) < 0)
+ continue;
+ irq = i * 32 + b;
+ tzic_write_4(sc, TZIC_PRIOMASK, 0);
+ if (intr_isrc_dispatch(&sc->isrcs[irq].isrc,
+ curthread->td_intr_frame) != 0) {
+ tzic_irq_mask(sc, irq);
+ tzic_irq_eoi(sc);
+ arm_irq_memory_barrier(irq);
+ if (bootverbose) {
+ device_printf(sc->dev,
+ "Stray irq %u disabled\n", irq);
+ }
+ }
+ return (FILTER_HANDLED);
+ }
+
+ if (bootverbose)
+ device_printf(sc->dev, "Spurious interrupt detected\n");
+
+ return (FILTER_HANDLED);
+}
+
+static void
+tzic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ u_int irq = ((struct tzic_irqsrc *)isrc)->irq;
+ struct tzic_softc *sc = device_get_softc(dev);
+
+ arm_irq_memory_barrier(irq);
+ tzic_irq_unmask(sc, irq);
+}
+
+static void
+tzic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ u_int irq = ((struct tzic_irqsrc *)isrc)->irq;
+ struct tzic_softc *sc = device_get_softc(dev);
+
+ tzic_irq_mask(sc, irq);
+}
+
+static int
+tzic_map_intr(device_t dev, struct intr_map_data *data,
+ struct intr_irqsrc **isrcp)
+{
+ struct intr_map_data_fdt *daf;
+ struct tzic_softc *sc;
+
+ if (data->type != INTR_MAP_DATA_FDT)
+ return (ENOTSUP);
+
+ daf = (struct intr_map_data_fdt *)data;
+ if (daf->ncells != 1 || daf->cells[0] >= TZIC_NIRQS)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+ *isrcp = &sc->isrcs[daf->cells[0]].isrc;
+
+ return (0);
+}
+
+static void
+tzic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct tzic_softc *sc = device_get_softc(dev);
+
+ tzic_irq_mask(sc, ((struct tzic_irqsrc *)isrc)->irq);
+ tzic_irq_eoi(sc);
+}
+
+static void
+tzic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+
+ tzic_enable_intr(dev, isrc);
+}
+
+static void
+tzic_post_filter(device_t dev, struct intr_irqsrc *isrc)
+{
+
+ tzic_irq_eoi(device_get_softc(dev));
+}
+
+static int
+tzic_pic_attach(struct tzic_softc *sc)
+{
+ struct intr_pic *pic;
+ const char *name;
+ intptr_t xref;
+ int error;
+ u_int irq;
+
+ name = device_get_nameunit(sc->dev);
+ for (irq = 0; irq < TZIC_NIRQS; irq++) {
+ sc->isrcs[irq].irq = irq;
+ error = intr_isrc_register(&sc->isrcs[irq].isrc,
+ sc->dev, 0, "%s,%u", name, irq);
+ if (error != 0)
+ return (error);
+ }
+
+ xref = OF_xref_from_node(ofw_bus_get_node(sc->dev));
+ pic = intr_pic_register(sc->dev, xref);
+ if (pic == NULL)
+ return (ENXIO);
+
+ return (intr_pic_claim_root(sc->dev, xref, tzic_intr, sc, 0));
+}
+
+static int
+tzic_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_is_compatible(dev, "fsl,tzic")) {
+ device_set_desc(dev, "TrustZone Interrupt Controller");
+ return (BUS_PROBE_DEFAULT);
+ }
+ return (ENXIO);
+}
+
+static int
+tzic_attach(device_t dev)
+{
+ struct tzic_softc *sc = device_get_softc(dev);
+ int i;
+
+ if (tzic_sc)
+ return (ENXIO);
+ tzic_sc = sc;
+ sc->dev = dev;
+
+ i = 0;
+ sc->tzicregs = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &i,
+ RF_ACTIVE);
+ if (sc->tzicregs == NULL) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ /* route all interrupts to IRQ. secure interrupts are for FIQ */
+ for (i = 0; i < 4; i++)
+ tzic_write_4(sc, TZIC_INTSEC(i), 0xffffffff);
+
+ /* disable all interrupts */
+ for (i = 0; i < 4; i++)
+ tzic_write_4(sc, TZIC_ENCLEAR(i), 0xffffffff);
+
+ /* Set all interrupts to priority 0 (max). */
+ for (i = 0; i < 128 / 4; ++i)
+ tzic_write_4(sc, TZIC_PRIORITY(i), 0);
+
+ /*
+ * Set priority mask to lowest (unmasked) prio, set synchronizer to
+ * low-latency mode (as opposed to low-power), enable the controller.
+ */
+ tzic_write_4(sc, TZIC_PRIOMASK, 0xff);
+ tzic_write_4(sc, TZIC_SYNCCTRL, 0);
+ tzic_write_4(sc, TZIC_INTCNTL, INTCNTL_NSEN_MASK|INTCNTL_NSEN|INTCNTL_EN);
+
+ /* Register as a root pic. */
+ if (tzic_pic_attach(sc) != 0) {
+ device_printf(dev, "could not attach PIC\n");
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static device_method_t tzic_methods[] = {
+ DEVMETHOD(device_probe, tzic_probe),
+ DEVMETHOD(device_attach, tzic_attach),
+
+ DEVMETHOD(pic_disable_intr, tzic_disable_intr),
+ DEVMETHOD(pic_enable_intr, tzic_enable_intr),
+ DEVMETHOD(pic_map_intr, tzic_map_intr),
+ DEVMETHOD(pic_post_filter, tzic_post_filter),
+ DEVMETHOD(pic_post_ithread, tzic_post_ithread),
+ DEVMETHOD(pic_pre_ithread, tzic_pre_ithread),
+
+ DEVMETHOD_END
+};
+
+static driver_t tzic_driver = {
+ "tzic",
+ tzic_methods,
+ sizeof(struct tzic_softc),
+};
+
+static devclass_t tzic_devclass;
+
+EARLY_DRIVER_MODULE(tzic, ofwbus, tzic_driver, tzic_devclass, 0, 0,
+ BUS_PASS_INTERRUPT);
diff --git a/sys/arm/freescale/vybrid/files.vybrid b/sys/arm/freescale/vybrid/files.vybrid
new file mode 100644
index 000000000000..9922481a239a
--- /dev/null
+++ b/sys/arm/freescale/vybrid/files.vybrid
@@ -0,0 +1,22 @@
+# $FreeBSD$
+
+arm/freescale/vybrid/vf_machdep.c standard
+arm/freescale/vybrid/vf_ccm.c standard
+arm/freescale/vybrid/vf_anadig.c standard
+arm/freescale/vybrid/vf_iomuxc.c standard
+arm/freescale/vybrid/vf_mscm.c standard
+arm/freescale/vybrid/vf_src.c standard
+arm/freescale/vybrid/vf_edma.c standard
+arm/freescale/vybrid/vf_dmamux.c standard
+arm/freescale/vybrid/vf_port.c standard
+arm/freescale/vybrid/vf_adc.c standard
+arm/freescale/vybrid/vf_i2c.c optional iicbus
+arm/freescale/vybrid/vf_tcon.c optional vt
+arm/freescale/vybrid/vf_dcu4.c optional vt
+arm/freescale/vybrid/vf_nfc.c optional nand
+arm/freescale/vybrid/vf_ehci.c optional ehci
+arm/freescale/vybrid/vf_gpio.c optional gpio
+arm/freescale/vybrid/vf_uart.c optional uart
+arm/freescale/vybrid/vf_sai.c optional sound
+arm/freescale/vybrid/vf_spi.c optional vf_spi
+dev/ffec/if_ffec.c optional ffec
diff --git a/sys/arm/freescale/vybrid/std.vybrid b/sys/arm/freescale/vybrid/std.vybrid
new file mode 100644
index 000000000000..d92a520898b7
--- /dev/null
+++ b/sys/arm/freescale/vybrid/std.vybrid
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+cpu CPU_CORTEXA
+machine arm armv7
+makeoptions CONF_CFLAGS="-march=armv7a"
+
+files "../freescale/vybrid/files.vybrid"
diff --git a/sys/arm/freescale/vybrid/vf_adc.c b/sys/arm/freescale/vybrid/vf_adc.c
new file mode 100644
index 000000000000..7a506b89300e
--- /dev/null
+++ b/sys/arm/freescale/vybrid/vf_adc.c
@@ -0,0 +1,243 @@
+/*-
+ * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Vybrid Family 12-bit Analog to Digital Converter (ADC)
+ * Chapter 37, Vybrid Reference Manual, Rev. 5, 07/2013
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <arm/freescale/vybrid/vf_common.h>
+#include <arm/freescale/vybrid/vf_adc.h>
+
+#define ADC_HC0 0x00 /* Ctrl reg for hardware triggers */
+#define ADC_HC1 0x04 /* Ctrl reg for hardware triggers */
+#define HC_AIEN (1 << 7) /* Conversion Complete Int Control */
+#define HC_ADCH_M 0x1f /* Input Channel Select Mask */
+#define HC_ADCH_S 0 /* Input Channel Select Shift */
+#define ADC_HS 0x08 /* Status register for HW triggers */
+#define HS_COCO0 (1 << 0) /* Conversion Complete Flag */
+#define HS_COCO1 (1 << 1) /* Conversion Complete Flag */
+#define ADC_R0 0x0C /* Data result reg for HW triggers */
+#define ADC_R1 0x10 /* Data result reg for HW triggers */
+#define ADC_CFG 0x14 /* Configuration register */
+#define CFG_OVWREN (1 << 16) /* Data Overwrite Enable */
+#define CFG_AVGS_M 0x3 /* Hardware Average select Mask */
+#define CFG_AVGS_S 14 /* Hardware Average select Shift */
+#define CFG_ADTRG (1 << 13) /* Conversion Trigger Select */
+#define CFG_REFSEL_M 0x3 /* Voltage Reference Select Mask */
+#define CFG_REFSEL_S 11 /* Voltage Reference Select Shift */
+#define CFG_ADHSC (1 << 10) /* High Speed Configuration */
+#define CFG_ADSTS_M 0x3 /* Defines the sample time duration */
+#define CFG_ADSTS_S 8 /* Defines the sample time duration */
+#define CFG_ADLPC (1 << 7) /* Low-Power Configuration */
+#define CFG_ADIV_M 0x3 /* Clock Divide Select */
+#define CFG_ADIV_S 5 /* Clock Divide Select */
+#define CFG_ADLSMP (1 << 4) /* Long Sample Time Configuration */
+#define CFG_MODE_M 0x3 /* Conversion Mode Selection Mask */
+#define CFG_MODE_S 2 /* Conversion Mode Selection Shift */
+#define CFG_MODE_12 0x2 /* 12-bit mode */
+#define CFG_ADICLK_M 0x3 /* Input Clock Select Mask */
+#define CFG_ADICLK_S 0 /* Input Clock Select Shift */
+#define ADC_GC 0x18 /* General control register */
+#define GC_CAL (1 << 7) /* Calibration */
+#define GC_ADCO (1 << 6) /* Continuous Conversion Enable */
+#define GC_AVGE (1 << 5) /* Hardware average enable */
+#define GC_ACFE (1 << 4) /* Compare Function Enable */
+#define GC_ACFGT (1 << 3) /* Compare Function Greater Than En */
+#define GC_ACREN (1 << 2) /* Compare Function Range En */
+#define GC_DMAEN (1 << 1) /* DMA Enable */
+#define GC_ADACKEN (1 << 0) /* Asynchronous clock output enable */
+#define ADC_GS 0x1C /* General status register */
+#define GS_AWKST (1 << 2) /* Asynchronous wakeup int status */
+#define GS_CALF (1 << 1) /* Calibration Failed Flag */
+#define GS_ADACT (1 << 0) /* Conversion Active */
+#define ADC_CV 0x20 /* Compare value register */
+#define CV_CV2_M 0xfff /* Compare Value 2 Mask */
+#define CV_CV2_S 16 /* Compare Value 2 Shift */
+#define CV_CV1_M 0xfff /* Compare Value 1 Mask */
+#define CV_CV1_S 0 /* Compare Value 1 Shift */
+#define ADC_OFS 0x24 /* Offset correction value register */
+#define OFS_SIGN 12 /* Sign bit */
+#define OFS_M 0xfff /* Offset value Mask */
+#define OFS_S 0 /* Offset value Shift */
+#define ADC_CAL 0x28 /* Calibration value register */
+#define CAL_CODE_M 0xf /* Calibration Result Value Mask */
+#define CAL_CODE_S 0 /* Calibration Result Value Shift */
+#define ADC_PCTL 0x30 /* Pin control register */
+
+struct adc_softc {
+ struct resource *res[2];
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+ void *ih;
+};
+
+struct adc_softc *adc_sc;
+
+static struct resource_spec adc_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+static int
+adc_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "fsl,mvf600-adc"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Vybrid Family "
+ "12-bit Analog to Digital Converter");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static void
+adc_intr(void *arg)
+{
+ struct adc_softc *sc;
+
+ sc = arg;
+
+ /* Conversation complete */
+}
+
+uint32_t
+adc_read(void)
+{
+ struct adc_softc *sc;
+
+ sc = adc_sc;
+ if (sc == NULL)
+ return (0);
+
+ return (READ4(sc, ADC_R0));
+}
+
+uint32_t
+adc_enable(int channel)
+{
+ struct adc_softc *sc;
+ int reg;
+
+ sc = adc_sc;
+ if (sc == NULL)
+ return (1);
+
+ reg = READ4(sc, ADC_HC0);
+ reg &= ~(HC_ADCH_M << HC_ADCH_S);
+ reg |= (channel << HC_ADCH_S);
+ WRITE4(sc, ADC_HC0, reg);
+
+ return (0);
+}
+
+static int
+adc_attach(device_t dev)
+{
+ struct adc_softc *sc;
+ int err;
+ int reg;
+
+ sc = device_get_softc(dev);
+
+ if (bus_alloc_resources(dev, adc_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ /* Memory interface */
+ sc->bst = rman_get_bustag(sc->res[0]);
+ sc->bsh = rman_get_bushandle(sc->res[0]);
+
+ adc_sc = sc;
+
+ /* Setup interrupt handler */
+ err = bus_setup_intr(dev, sc->res[1], INTR_TYPE_BIO | INTR_MPSAFE,
+ NULL, adc_intr, sc, &sc->ih);
+ if (err) {
+ device_printf(dev, "Unable to alloc interrupt resource.\n");
+ return (ENXIO);
+ }
+
+ /* Configure 12-bit mode */
+ reg = READ4(sc, ADC_CFG);
+ reg &= ~(CFG_MODE_M << CFG_MODE_S);
+ reg |= (CFG_MODE_12 << CFG_MODE_S); /* 12bit */
+ WRITE4(sc, ADC_CFG, reg);
+
+ /* Configure for continuous conversion */
+ reg = READ4(sc, ADC_GC);
+ reg |= (GC_ADCO | GC_AVGE);
+ WRITE4(sc, ADC_GC, reg);
+
+ /* Disable interrupts */
+ reg = READ4(sc, ADC_HC0);
+ reg &= HC_AIEN;
+ WRITE4(sc, ADC_HC0, reg);
+
+ return (0);
+}
+
+static device_method_t adc_methods[] = {
+ DEVMETHOD(device_probe, adc_probe),
+ DEVMETHOD(device_attach, adc_attach),
+ { 0, 0 }
+};
+
+static driver_t adc_driver = {
+ "adc",
+ adc_methods,
+ sizeof(struct adc_softc),
+};
+
+static devclass_t adc_devclass;
+
+DRIVER_MODULE(adc, simplebus, adc_driver, adc_devclass, 0, 0);
diff --git a/sys/arm/freescale/vybrid/vf_adc.h b/sys/arm/freescale/vybrid/vf_adc.h
new file mode 100644
index 000000000000..efb231f70a8f
--- /dev/null
+++ b/sys/arm/freescale/vybrid/vf_adc.h
@@ -0,0 +1,30 @@
+/*-
+ * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+uint32_t adc_read(void);
+uint32_t adc_enable(int);
diff --git a/sys/arm/freescale/vybrid/vf_anadig.c b/sys/arm/freescale/vybrid/vf_anadig.c
new file mode 100644
index 000000000000..a8f944baf5fe
--- /dev/null
+++ b/sys/arm/freescale/vybrid/vf_anadig.c
@@ -0,0 +1,246 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013-2014 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Vybrid Family Analog components control digital interface (ANADIG)
+ * Chapter 11, Vybrid Reference Manual, Rev. 5, 07/2013
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <arm/freescale/vybrid/vf_common.h>
+
+#define ANADIG_PLL3_CTRL 0x010 /* PLL3 Control */
+#define ANADIG_PLL7_CTRL 0x020 /* PLL7 Control */
+#define ANADIG_PLL2_CTRL 0x030 /* PLL2 Control */
+#define ANADIG_PLL2_SS 0x040 /* PLL2 Spread Spectrum */
+#define ANADIG_PLL2_NUM 0x050 /* PLL2 Numerator */
+#define ANADIG_PLL2_DENOM 0x060 /* PLL2 Denominator */
+#define ANADIG_PLL4_CTRL 0x070 /* PLL4 Control */
+#define ANADIG_PLL4_NUM 0x080 /* PLL4 Numerator */
+#define ANADIG_PLL4_DENOM 0x090 /* PLL4 Denominator */
+#define ANADIG_PLL6_CTRL 0x0A0 /* PLL6 Control */
+#define ANADIG_PLL6_NUM 0x0B0 /* PLL6 Numerator */
+#define ANADIG_PLL6_DENOM 0x0C0 /* PLL6 Denominator */
+#define ANADIG_PLL5_CTRL 0x0E0 /* PLL5 Control */
+#define ANADIG_PLL3_PFD 0x0F0 /* PLL3 PFD */
+#define ANADIG_PLL2_PFD 0x100 /* PLL2 PFD */
+#define ANADIG_REG_1P1 0x110 /* Regulator 1P1 */
+#define ANADIG_REG_3P0 0x120 /* Regulator 3P0 */
+#define ANADIG_REG_2P5 0x130 /* Regulator 2P5 */
+#define ANADIG_ANA_MISC0 0x150 /* Analog Miscellaneous */
+#define ANADIG_ANA_MISC1 0x160 /* Analog Miscellaneous */
+#define ANADIG_ANADIG_DIGPROG 0x260 /* Digital Program */
+#define ANADIG_PLL1_CTRL 0x270 /* PLL1 Control */
+#define ANADIG_PLL1_SS 0x280 /* PLL1 Spread Spectrum */
+#define ANADIG_PLL1_NUM 0x290 /* PLL1 Numerator */
+#define ANADIG_PLL1_DENOM 0x2A0 /* PLL1 Denominator */
+#define ANADIG_PLL1_PFD 0x2B0 /* PLL1_PFD */
+#define ANADIG_PLL_LOCK 0x2C0 /* PLL Lock */
+
+#define USB_VBUS_DETECT(n) (0x1A0 + 0x60 * n)
+#define USB_CHRG_DETECT(n) (0x1B0 + 0x60 * n)
+#define USB_VBUS_DETECT_STATUS(n) (0x1C0 + 0x60 * n)
+#define USB_CHRG_DETECT_STATUS(n) (0x1D0 + 0x60 * n)
+#define USB_LOOPBACK(n) (0x1E0 + 0x60 * n)
+#define USB_MISC(n) (0x1F0 + 0x60 * n)
+
+#define ANADIG_PLL_LOCKED (1U << 31)
+#define ENABLE_LINREG (1 << 0)
+#define EN_CLK_TO_UTMI (1 << 30)
+
+#define CTRL_BYPASS (1 << 16)
+#define CTRL_PWR (1 << 12)
+#define CTRL_PLL_EN (1 << 13)
+#define EN_USB_CLKS (1 << 6)
+
+#define PLL4_CTRL_DIV_SEL_S 0
+#define PLL4_CTRL_DIV_SEL_M 0x7f
+
+struct anadig_softc {
+ struct resource *res[1];
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+};
+
+struct anadig_softc *anadig_sc;
+
+static struct resource_spec anadig_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+static int
+anadig_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "fsl,mvf600-anadig"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Vybrid Family ANADIG Unit");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+enable_pll(struct anadig_softc *sc, int pll_ctrl)
+{
+ int reg;
+
+ reg = READ4(sc, pll_ctrl);
+ reg &= ~(CTRL_BYPASS | CTRL_PWR);
+ if (pll_ctrl == ANADIG_PLL3_CTRL || pll_ctrl == ANADIG_PLL7_CTRL) {
+ /* It is USB PLL. Power bit logic is reversed */
+ reg |= (CTRL_PWR | EN_USB_CLKS);
+ }
+ WRITE4(sc, pll_ctrl, reg);
+
+ /* Wait for PLL lock */
+ while (!(READ4(sc, pll_ctrl) & ANADIG_PLL_LOCKED))
+ ;
+
+ reg = READ4(sc, pll_ctrl);
+ reg |= (CTRL_PLL_EN);
+ WRITE4(sc, pll_ctrl, reg);
+
+ return (0);
+}
+
+uint32_t
+pll4_configure_output(uint32_t mfi, uint32_t mfn, uint32_t mfd)
+{
+ struct anadig_softc *sc;
+ int reg;
+
+ sc = anadig_sc;
+
+ /*
+ * PLLout = Fsys * (MFI+(MFN/MFD))
+ */
+
+ reg = READ4(sc, ANADIG_PLL4_CTRL);
+ reg &= ~(PLL4_CTRL_DIV_SEL_M << PLL4_CTRL_DIV_SEL_S);
+ reg |= (mfi << PLL4_CTRL_DIV_SEL_S);
+ WRITE4(sc, ANADIG_PLL4_CTRL, reg);
+ WRITE4(sc, ANADIG_PLL4_NUM, mfn);
+ WRITE4(sc, ANADIG_PLL4_DENOM, mfd);
+
+ return (0);
+}
+
+static int
+anadig_attach(device_t dev)
+{
+ struct anadig_softc *sc;
+ int reg;
+
+ sc = device_get_softc(dev);
+
+ if (bus_alloc_resources(dev, anadig_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ /* Memory interface */
+ sc->bst = rman_get_bustag(sc->res[0]);
+ sc->bsh = rman_get_bushandle(sc->res[0]);
+
+ anadig_sc = sc;
+
+ /* Enable USB PLLs */
+ enable_pll(sc, ANADIG_PLL3_CTRL);
+ enable_pll(sc, ANADIG_PLL7_CTRL);
+
+ /* Enable other PLLs */
+ enable_pll(sc, ANADIG_PLL1_CTRL);
+ enable_pll(sc, ANADIG_PLL2_CTRL);
+ enable_pll(sc, ANADIG_PLL4_CTRL);
+ enable_pll(sc, ANADIG_PLL5_CTRL);
+ enable_pll(sc, ANADIG_PLL6_CTRL);
+
+ /* Enable USB voltage regulator */
+ reg = READ4(sc, ANADIG_REG_3P0);
+ reg |= (ENABLE_LINREG);
+ WRITE4(sc, ANADIG_REG_3P0, reg);
+
+ /* Give clocks to USB */
+ reg = READ4(sc, USB_MISC(0));
+ reg |= (EN_CLK_TO_UTMI);
+ WRITE4(sc, USB_MISC(0), reg);
+
+ reg = READ4(sc, USB_MISC(1));
+ reg |= (EN_CLK_TO_UTMI);
+ WRITE4(sc, USB_MISC(1), reg);
+
+#if 0
+ printf("USB_ANALOG_USB_MISC(0) == 0x%08x\n",
+ READ4(sc, USB_ANALOG_USB_MISC(0)));
+ printf("USB_ANALOG_USB_MISC(1) == 0x%08x\n",
+ READ4(sc, USB_ANALOG_USB_MISC(1)));
+#endif
+
+ return (0);
+}
+
+static device_method_t anadig_methods[] = {
+ DEVMETHOD(device_probe, anadig_probe),
+ DEVMETHOD(device_attach, anadig_attach),
+ { 0, 0 }
+};
+
+static driver_t anadig_driver = {
+ "anadig",
+ anadig_methods,
+ sizeof(struct anadig_softc),
+};
+
+static devclass_t anadig_devclass;
+
+DRIVER_MODULE(anadig, simplebus, anadig_driver, anadig_devclass, 0, 0);
diff --git a/sys/arm/freescale/vybrid/vf_ccm.c b/sys/arm/freescale/vybrid/vf_ccm.c
new file mode 100644
index 000000000000..bfb957338af7
--- /dev/null
+++ b/sys/arm/freescale/vybrid/vf_ccm.c
@@ -0,0 +1,501 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013-2014 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Vybrid Family Clock Controller Module (CCM)
+ * Chapter 10, Vybrid Reference Manual, Rev. 5, 07/2013
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <arm/freescale/vybrid/vf_common.h>
+
+#define CCM_CCR 0x00 /* Control Register */
+#define CCM_CSR 0x04 /* Status Register */
+#define CCM_CCSR 0x08 /* Clock Switcher Register */
+#define CCM_CACRR 0x0C /* ARM Clock Root Register */
+#define CCM_CSCMR1 0x10 /* Serial Clock Multiplexer Register 1 */
+#define CCM_CSCDR1 0x14 /* Serial Clock Divider Register 1 */
+#define CCM_CSCDR2 0x18 /* Serial Clock Divider Register 2 */
+#define CCM_CSCDR3 0x1C /* Serial Clock Divider Register 3 */
+#define CCM_CSCMR2 0x20 /* Serial Clock Multiplexer Register 2 */
+#define CCM_CTOR 0x28 /* Testing Observability Register */
+#define CCM_CLPCR 0x2C /* Low Power Control Register */
+#define CCM_CISR 0x30 /* Interrupt Status Register */
+#define CCM_CIMR 0x34 /* Interrupt Mask Register */
+#define CCM_CCOSR 0x38 /* Clock Output Source Register */
+#define CCM_CGPR 0x3C /* General Purpose Register */
+
+#define CCM_CCGRN 12
+#define CCM_CCGR(n) (0x40 + (n * 0x04)) /* Clock Gating Register */
+#define CCM_CMEOR(n) (0x70 + (n * 0x70)) /* Module Enable Override */
+#define CCM_CCPGR(n) (0x90 + (n * 0x04)) /* Platform Clock Gating */
+
+#define CCM_CPPDSR 0x88 /* PLL PFD Disable Status Register */
+#define CCM_CCOWR 0x8C /* CORE Wakeup Register */
+
+#define PLL3_PFD4_EN (1U << 31)
+#define PLL3_PFD3_EN (1 << 30)
+#define PLL3_PFD2_EN (1 << 29)
+#define PLL3_PFD1_EN (1 << 28)
+#define PLL2_PFD4_EN (1 << 15)
+#define PLL2_PFD3_EN (1 << 14)
+#define PLL2_PFD2_EN (1 << 13)
+#define PLL2_PFD1_EN (1 << 12)
+#define PLL1_PFD4_EN (1 << 11)
+#define PLL1_PFD3_EN (1 << 10)
+#define PLL1_PFD2_EN (1 << 9)
+#define PLL1_PFD1_EN (1 << 8)
+
+/* CCM_CCR */
+#define FIRC_EN (1 << 16)
+#define FXOSC_EN (1 << 12)
+#define FXOSC_RDY (1 << 5)
+
+/* CCM_CSCDR1 */
+#define ENET_TS_EN (1 << 23)
+#define RMII_CLK_EN (1 << 24)
+#define SAI3_EN (1 << 19)
+
+/* CCM_CSCDR2 */
+#define ESAI_EN (1 << 30)
+#define ESDHC1_EN (1 << 29)
+#define ESDHC0_EN (1 << 28)
+#define NFC_EN (1 << 9)
+#define ESDHC1_DIV_S 20
+#define ESDHC1_DIV_M 0xf
+#define ESDHC0_DIV_S 16
+#define ESDHC0_DIV_M 0xf
+
+/* CCM_CSCDR3 */
+#define DCU0_EN (1 << 19)
+
+#define QSPI1_EN (1 << 12)
+#define QSPI1_DIV (1 << 11)
+#define QSPI1_X2_DIV (1 << 10)
+#define QSPI1_X4_DIV_M 0x3
+#define QSPI1_X4_DIV_S 8
+
+#define QSPI0_EN (1 << 4)
+#define QSPI0_DIV (1 << 3)
+#define QSPI0_X2_DIV (1 << 2)
+#define QSPI0_X4_DIV_M 0x3
+#define QSPI0_X4_DIV_S 0
+
+#define SAI3_DIV_SHIFT 12
+#define SAI3_DIV_MASK 0xf
+#define ESAI_DIV_SHIFT 24
+#define ESAI_DIV_MASK 0xf
+
+#define PLL4_CLK_DIV_SHIFT 6
+#define PLL4_CLK_DIV_MASK 0x7
+
+#define IPG_CLK_DIV_SHIFT 11
+#define IPG_CLK_DIV_MASK 0x3
+
+#define ESAI_CLK_SEL_SHIFT 20
+#define ESAI_CLK_SEL_MASK 0x3
+
+#define SAI3_CLK_SEL_SHIFT 6
+#define SAI3_CLK_SEL_MASK 0x3
+
+#define CKO1_EN (1 << 10)
+#define CKO1_DIV_MASK 0xf
+#define CKO1_DIV_SHIFT 6
+#define CKO1_SEL_MASK 0x3f
+#define CKO1_SEL_SHIFT 0
+#define CKO1_PLL4_MAIN 0x6
+#define CKO1_PLL4_DIVD 0x7
+
+struct clk {
+ uint32_t reg;
+ uint32_t enable_reg;
+ uint32_t div_mask;
+ uint32_t div_shift;
+ uint32_t div_val;
+ uint32_t sel_reg;
+ uint32_t sel_mask;
+ uint32_t sel_shift;
+ uint32_t sel_val;
+};
+
+static struct clk ipg_clk = {
+ .reg = CCM_CACRR,
+ .enable_reg = 0,
+ .div_mask = IPG_CLK_DIV_MASK,
+ .div_shift = IPG_CLK_DIV_SHIFT,
+ .div_val = 1, /* Divide by 2 */
+ .sel_reg = 0,
+ .sel_mask = 0,
+ .sel_shift = 0,
+ .sel_val = 0,
+};
+
+/*
+ PLL4 clock divider (before switching the clocks should be gated)
+ 000 Divide by 1 (only if PLL frequency less than or equal to 650 MHz)
+ 001 Divide by 4
+ 010 Divide by 6
+ 011 Divide by 8
+ 100 Divide by 10
+ 101 Divide by 12
+ 110 Divide by 14
+ 111 Divide by 16
+*/
+
+static struct clk pll4_clk = {
+ .reg = CCM_CACRR,
+ .enable_reg = 0,
+ .div_mask = PLL4_CLK_DIV_MASK,
+ .div_shift = PLL4_CLK_DIV_SHIFT,
+ .div_val = 5, /* Divide by 12 */
+ .sel_reg = 0,
+ .sel_mask = 0,
+ .sel_shift = 0,
+ .sel_val = 0,
+};
+
+static struct clk sai3_clk = {
+ .reg = CCM_CSCDR1,
+ .enable_reg = SAI3_EN,
+ .div_mask = SAI3_DIV_MASK,
+ .div_shift = SAI3_DIV_SHIFT,
+ .div_val = 1,
+ .sel_reg = CCM_CSCMR1,
+ .sel_mask = SAI3_CLK_SEL_MASK,
+ .sel_shift = SAI3_CLK_SEL_SHIFT,
+ .sel_val = 0x3, /* Divided PLL4 main clock */
+};
+
+static struct clk cko1_clk = {
+ .reg = CCM_CCOSR,
+ .enable_reg = CKO1_EN,
+ .div_mask = CKO1_DIV_MASK,
+ .div_shift = CKO1_DIV_SHIFT,
+ .div_val = 1,
+ .sel_reg = CCM_CCOSR,
+ .sel_mask = CKO1_SEL_MASK,
+ .sel_shift = CKO1_SEL_SHIFT,
+ .sel_val = CKO1_PLL4_DIVD,
+};
+
+static struct clk esdhc0_clk = {
+ .reg = CCM_CSCDR2,
+ .enable_reg = ESDHC0_EN,
+ .div_mask = ESDHC0_DIV_M,
+ .div_shift = ESDHC0_DIV_S,
+ .div_val = 0x9,
+ .sel_reg = 0,
+ .sel_mask = 0,
+ .sel_shift = 0,
+ .sel_val = 0,
+};
+
+static struct clk esdhc1_clk = {
+ .reg = CCM_CSCDR2,
+ .enable_reg = ESDHC1_EN,
+ .div_mask = ESDHC1_DIV_M,
+ .div_shift = ESDHC1_DIV_S,
+ .div_val = 0x9,
+ .sel_reg = 0,
+ .sel_mask = 0,
+ .sel_shift = 0,
+ .sel_val = 0,
+};
+
+static struct clk qspi0_clk = {
+ .reg = CCM_CSCDR3,
+ .enable_reg = QSPI0_EN,
+ .div_mask = 0,
+ .div_shift = 0,
+ .div_val = 0,
+ .sel_reg = 0,
+ .sel_mask = 0,
+ .sel_shift = 0,
+ .sel_val = 0,
+};
+
+static struct clk dcu0_clk = {
+ .reg = CCM_CSCDR3,
+ .enable_reg = DCU0_EN,
+ .div_mask = 0x7,
+ .div_shift = 16, /* DCU0_DIV */
+ .div_val = 0, /* divide by 1 */
+ .sel_reg = 0,
+ .sel_mask = 0,
+ .sel_shift = 0,
+ .sel_val = 0,
+};
+
+static struct clk enet_clk = {
+ .reg = CCM_CSCDR1,
+ .enable_reg = (ENET_TS_EN | RMII_CLK_EN),
+ .div_mask = 0,
+ .div_shift = 0,
+ .div_val = 0,
+ .sel_reg = 0,
+ .sel_mask = 0,
+ .sel_shift = 0,
+ .sel_val = 0,
+};
+
+static struct clk nand_clk = {
+ .reg = CCM_CSCDR2,
+ .enable_reg = NFC_EN,
+ .div_mask = 0,
+ .div_shift = 0,
+ .div_val = 0,
+ .sel_reg = 0,
+ .sel_mask = 0,
+ .sel_shift = 0,
+ .sel_val = 0,
+};
+
+/*
+ Divider to generate ESAI clock
+ 0000 Divide by 1
+ 0001 Divide by 2
+ ... ...
+ 1111 Divide by 16
+*/
+
+static struct clk esai_clk = {
+ .reg = CCM_CSCDR2,
+ .enable_reg = ESAI_EN,
+ .div_mask = ESAI_DIV_MASK,
+ .div_shift = ESAI_DIV_SHIFT,
+ .div_val = 3, /* Divide by 4 */
+ .sel_reg = CCM_CSCMR1,
+ .sel_mask = ESAI_CLK_SEL_MASK,
+ .sel_shift = ESAI_CLK_SEL_SHIFT,
+ .sel_val = 0x3, /* Divided PLL4 main clock */
+};
+
+struct clock_entry {
+ char *name;
+ struct clk *clk;
+};
+
+static struct clock_entry clock_map[] = {
+ {"ipg", &ipg_clk},
+ {"pll4", &pll4_clk},
+ {"sai3", &sai3_clk},
+ {"cko1", &cko1_clk},
+ {"esdhc0", &esdhc0_clk},
+ {"esdhc1", &esdhc1_clk},
+ {"qspi0", &qspi0_clk},
+ {"dcu0", &dcu0_clk},
+ {"enet", &enet_clk},
+ {"nand", &nand_clk},
+ {"esai", &esai_clk},
+ {NULL, NULL}
+};
+
+struct ccm_softc {
+ struct resource *res[1];
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+ device_t dev;
+};
+
+static struct resource_spec ccm_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+static int
+ccm_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "fsl,mvf600-ccm"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Vybrid Family CCM Unit");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+set_clock(struct ccm_softc *sc, char *name)
+{
+ struct clk *clk;
+ int reg;
+ int i;
+
+ for (i = 0; clock_map[i].name != NULL; i++) {
+ if (strcmp(clock_map[i].name, name) == 0) {
+#if 0
+ device_printf(sc->dev, "Configuring %s clk\n", name);
+#endif
+ clk = clock_map[i].clk;
+ if (clk->sel_reg != 0) {
+ reg = READ4(sc, clk->sel_reg);
+ reg &= ~(clk->sel_mask << clk->sel_shift);
+ reg |= (clk->sel_val << clk->sel_shift);
+ WRITE4(sc, clk->sel_reg, reg);
+ }
+
+ reg = READ4(sc, clk->reg);
+ reg |= clk->enable_reg;
+ reg &= ~(clk->div_mask << clk->div_shift);
+ reg |= (clk->div_val << clk->div_shift);
+ WRITE4(sc, clk->reg, reg);
+ }
+ }
+
+ return (0);
+}
+
+static int
+ccm_fdt_set(struct ccm_softc *sc)
+{
+ phandle_t child, parent, root;
+ int len;
+ char *fdt_config, *name;
+
+ root = OF_finddevice("/");
+ len = 0;
+ parent = root;
+
+ /* Find 'clock_names' prop in the tree */
+ for (child = OF_child(parent); child != 0; child = OF_peer(child)) {
+ /* Find a 'leaf'. Start the search from this node. */
+ while (OF_child(child)) {
+ parent = child;
+ child = OF_child(child);
+ }
+
+ if (!ofw_bus_node_status_okay(child))
+ continue;
+
+ if ((len = OF_getproplen(child, "clock_names")) > 0) {
+ len = OF_getproplen(child, "clock_names");
+ OF_getprop_alloc(child, "clock_names",
+ (void **)&fdt_config);
+
+ while (len > 0) {
+ name = fdt_config;
+ fdt_config += strlen(name) + 1;
+ len -= strlen(name) + 1;
+ set_clock(sc, name);
+ }
+ }
+
+ if (OF_peer(child) == 0) {
+ /* No more siblings. */
+ child = parent;
+ parent = OF_parent(child);
+ }
+ }
+
+ return (0);
+}
+
+static int
+ccm_attach(device_t dev)
+{
+ struct ccm_softc *sc;
+ int reg;
+ int i;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ if (bus_alloc_resources(dev, ccm_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ /* Memory interface */
+ sc->bst = rman_get_bustag(sc->res[0]);
+ sc->bsh = rman_get_bushandle(sc->res[0]);
+
+ /* Enable oscillator */
+ reg = READ4(sc, CCM_CCR);
+ reg |= (FIRC_EN | FXOSC_EN);
+ WRITE4(sc, CCM_CCR, reg);
+
+ /* Wait 10 times */
+ for (i = 0; i < 10; i++) {
+ if (READ4(sc, CCM_CSR) & FXOSC_RDY) {
+ device_printf(sc->dev, "On board oscillator is ready.\n");
+ break;
+ }
+
+ cpufunc_nullop();
+ }
+
+ /* Clock is on during all modes, except stop mode. */
+ for (i = 0; i < CCM_CCGRN; i++) {
+ WRITE4(sc, CCM_CCGR(i), 0xffffffff);
+ }
+
+ /* Take and apply FDT clocks */
+ ccm_fdt_set(sc);
+
+ return (0);
+}
+
+static device_method_t ccm_methods[] = {
+ DEVMETHOD(device_probe, ccm_probe),
+ DEVMETHOD(device_attach, ccm_attach),
+ { 0, 0 }
+};
+
+static driver_t ccm_driver = {
+ "ccm",
+ ccm_methods,
+ sizeof(struct ccm_softc),
+};
+
+static devclass_t ccm_devclass;
+
+DRIVER_MODULE(ccm, simplebus, ccm_driver, ccm_devclass, 0, 0);
diff --git a/sys/arm/freescale/vybrid/vf_common.h b/sys/arm/freescale/vybrid/vf_common.h
new file mode 100644
index 000000000000..ab3c57fd3c81
--- /dev/null
+++ b/sys/arm/freescale/vybrid/vf_common.h
@@ -0,0 +1,45 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013-2014 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define READ4(_sc, _reg) \
+ bus_space_read_4(_sc->bst, _sc->bsh, _reg)
+#define WRITE4(_sc, _reg, _val) \
+ bus_space_write_4(_sc->bst, _sc->bsh, _reg, _val)
+#define READ2(_sc, _reg) \
+ bus_space_read_2(_sc->bst, _sc->bsh, _reg)
+#define WRITE2(_sc, _reg, _val) \
+ bus_space_write_2(_sc->bst, _sc->bsh, _reg, _val)
+#define READ1(_sc, _reg) \
+ bus_space_read_1(_sc->bst, _sc->bsh, _reg)
+#define WRITE1(_sc, _reg, _val) \
+ bus_space_write_1(_sc->bst, _sc->bsh, _reg, _val)
+
+uint32_t pll4_configure_output(uint32_t mfi, uint32_t mfn, uint32_t mfd);
+uint32_t tcon_bypass(void);
diff --git a/sys/arm/freescale/vybrid/vf_dcu4.c b/sys/arm/freescale/vybrid/vf_dcu4.c
new file mode 100644
index 000000000000..9e455d6027c6
--- /dev/null
+++ b/sys/arm/freescale/vybrid/vf_dcu4.c
@@ -0,0 +1,471 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Vybrid Family Display Control Unit (DCU4)
+ * Chapter 55, Vybrid Reference Manual, Rev. 5, 07/2013
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+#include <sys/fbio.h>
+#include <sys/consio.h>
+#include <sys/eventhandler.h>
+#include <sys/gpio.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/vt/vt.h>
+#include <dev/vt/colors/vt_termcolors.h>
+
+#include "gpio_if.h"
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include "fb_if.h"
+
+#include <arm/freescale/vybrid/vf_common.h>
+
+#define DCU_CTRLDESCCURSOR1 0x000 /* Control Descriptor Cursor 1 */
+#define DCU_CTRLDESCCURSOR2 0x004 /* Control Descriptor Cursor 2 */
+#define DCU_CTRLDESCCURSOR3 0x008 /* Control Descriptor Cursor 3 */
+#define DCU_CTRLDESCCURSOR4 0x00C /* Control Descriptor Cursor 4 */
+#define DCU_DCU_MODE 0x010 /* DCU4 Mode */
+#define DCU_MODE_M 0x3
+#define DCU_MODE_S 0
+#define DCU_MODE_NORMAL 0x1
+#define DCU_MODE_TEST 0x2
+#define DCU_MODE_COLBAR 0x3
+#define RASTER_EN (1 << 14) /* Raster scan of pixel data */
+#define PDI_EN (1 << 13)
+#define PDI_DE_MODE (1 << 11)
+#define PDI_MODE_M 2
+#define DCU_BGND 0x014 /* Background */
+#define DCU_DISP_SIZE 0x018 /* Display Size */
+#define DELTA_M 0x7ff
+#define DELTA_Y_S 16
+#define DELTA_X_S 0
+#define DCU_HSYN_PARA 0x01C /* Horizontal Sync Parameter */
+#define BP_H_SHIFT 22
+#define PW_H_SHIFT 11
+#define FP_H_SHIFT 0
+#define DCU_VSYN_PARA 0x020 /* Vertical Sync Parameter */
+#define BP_V_SHIFT 22
+#define PW_V_SHIFT 11
+#define FP_V_SHIFT 0
+#define DCU_SYNPOL 0x024 /* Synchronize Polarity */
+#define INV_HS (1 << 0)
+#define INV_VS (1 << 1)
+#define INV_PDI_VS (1 << 8) /* Polarity of PDI input VSYNC. */
+#define INV_PDI_HS (1 << 9) /* Polarity of PDI input HSYNC. */
+#define INV_PDI_DE (1 << 10) /* Polarity of PDI input DE. */
+#define DCU_THRESHOLD 0x028 /* Threshold */
+#define LS_BF_VS_SHIFT 16
+#define OUT_BUF_HIGH_SHIFT 8
+#define OUT_BUF_LOW_SHIFT 0
+#define DCU_INT_STATUS 0x02C /* Interrupt Status */
+#define DCU_INT_MASK 0x030 /* Interrupt Mask */
+#define DCU_COLBAR_1 0x034 /* COLBAR_1 */
+#define DCU_COLBAR_2 0x038 /* COLBAR_2 */
+#define DCU_COLBAR_3 0x03C /* COLBAR_3 */
+#define DCU_COLBAR_4 0x040 /* COLBAR_4 */
+#define DCU_COLBAR_5 0x044 /* COLBAR_5 */
+#define DCU_COLBAR_6 0x048 /* COLBAR_6 */
+#define DCU_COLBAR_7 0x04C /* COLBAR_7 */
+#define DCU_COLBAR_8 0x050 /* COLBAR_8 */
+#define DCU_DIV_RATIO 0x054 /* Divide Ratio */
+#define DCU_SIGN_CALC_1 0x058 /* Sign Calculation 1 */
+#define DCU_SIGN_CALC_2 0x05C /* Sign Calculation 2 */
+#define DCU_CRC_VAL 0x060 /* CRC Value */
+#define DCU_PDI_STATUS 0x064 /* PDI Status */
+#define DCU_PDI_STA_MSK 0x068 /* PDI Status Mask */
+#define DCU_PARR_ERR_STATUS1 0x06C /* Parameter Error Status 1 */
+#define DCU_PARR_ERR_STATUS2 0x070 /* Parameter Error Status 2 */
+#define DCU_PARR_ERR_STATUS3 0x07C /* Parameter Error Status 3 */
+#define DCU_MASK_PARR_ERR_ST1 0x080 /* Mask Parameter Error Status 1 */
+#define DCU_MASK_PARR_ERR_ST2 0x084 /* Mask Parameter Error Status 2 */
+#define DCU_MASK_PARR_ERR_ST3 0x090 /* Mask Parameter Error Status 3 */
+#define DCU_THRESHOLD_INP_BUF_1 0x094 /* Threshold Input 1 */
+#define DCU_THRESHOLD_INP_BUF_2 0x098 /* Threshold Input 2 */
+#define DCU_THRESHOLD_INP_BUF_3 0x09C /* Threshold Input 3 */
+#define DCU_LUMA_COMP 0x0A0 /* LUMA Component */
+#define DCU_CHROMA_RED 0x0A4 /* Red Chroma Components */
+#define DCU_CHROMA_GREEN 0x0A8 /* Green Chroma Components */
+#define DCU_CHROMA_BLUE 0x0AC /* Blue Chroma Components */
+#define DCU_CRC_POS 0x0B0 /* CRC Position */
+#define DCU_LYR_INTPOL_EN 0x0B4 /* Layer Interpolation Enable */
+#define DCU_LYR_LUMA_COMP 0x0B8 /* Layer Luminance Component */
+#define DCU_LYR_CHRM_RED 0x0BC /* Layer Chroma Red */
+#define DCU_LYR_CHRM_GRN 0x0C0 /* Layer Chroma Green */
+#define DCU_LYR_CHRM_BLUE 0x0C4 /* Layer Chroma Blue */
+#define DCU_COMP_IMSIZE 0x0C8 /* Compression Image Size */
+#define DCU_UPDATE_MODE 0x0CC /* Update Mode */
+#define READREG (1 << 30)
+#define MODE (1 << 31)
+#define DCU_UNDERRUN 0x0D0 /* Underrun */
+#define DCU_GLBL_PROTECT 0x100 /* Global Protection */
+#define DCU_SFT_LCK_BIT_L0 0x104 /* Soft Lock Bit Layer 0 */
+#define DCU_SFT_LCK_BIT_L1 0x108 /* Soft Lock Bit Layer 1 */
+#define DCU_SFT_LCK_DISP_SIZE 0x10C /* Soft Lock Display Size */
+#define DCU_SFT_LCK_HS_VS_PARA 0x110 /* Soft Lock Hsync/Vsync Parameter */
+#define DCU_SFT_LCK_POL 0x114 /* Soft Lock POL */
+#define DCU_SFT_LCK_L0_TRANSP 0x118 /* Soft Lock L0 Transparency */
+#define DCU_SFT_LCK_L1_TRANSP 0x11C /* Soft Lock L1 Transparency */
+
+/* Control Descriptor */
+#define DCU_CTRLDESCL(n, m) 0x200 + (0x40 * n) + 0x4 * (m - 1)
+#define DCU_CTRLDESCLn_1(n) DCU_CTRLDESCL(n, 1)
+#define DCU_CTRLDESCLn_2(n) DCU_CTRLDESCL(n, 2)
+#define DCU_CTRLDESCLn_3(n) DCU_CTRLDESCL(n, 3)
+#define TRANS_SHIFT 20
+#define DCU_CTRLDESCLn_4(n) DCU_CTRLDESCL(n, 4)
+#define BPP_MASK 0xf /* Bit per pixel Mask */
+#define BPP_SHIFT 16 /* Bit per pixel Shift */
+#define BPP24 0x5
+#define EN_LAYER (1 << 31) /* Enable the layer */
+#define DCU_CTRLDESCLn_5(n) DCU_CTRLDESCL(n, 5)
+#define DCU_CTRLDESCLn_6(n) DCU_CTRLDESCL(n, 6)
+#define DCU_CTRLDESCLn_7(n) DCU_CTRLDESCL(n, 7)
+#define DCU_CTRLDESCLn_8(n) DCU_CTRLDESCL(n, 8)
+#define DCU_CTRLDESCLn_9(n) DCU_CTRLDESCL(n, 9)
+
+#define NUM_LAYERS 64
+
+struct panel_info {
+ uint32_t width;
+ uint32_t height;
+ uint32_t h_back_porch;
+ uint32_t h_pulse_width;
+ uint32_t h_front_porch;
+ uint32_t v_back_porch;
+ uint32_t v_pulse_width;
+ uint32_t v_front_porch;
+ uint32_t clk_div;
+ uint32_t backlight_pin;
+};
+
+struct dcu_softc {
+ struct resource *res[2];
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+ void *ih;
+ device_t dev;
+ device_t sc_fbd; /* fbd child */
+ struct fb_info sc_info;
+ struct panel_info *panel;
+};
+
+static struct resource_spec dcu_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+static int
+dcu_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "fsl,mvf600-dcu4"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Vybrid Family Display Control Unit (DCU4)");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static void
+dcu_intr(void *arg)
+{
+ struct dcu_softc *sc;
+ int reg;
+
+ sc = arg;
+
+ /* Ack interrupts */
+ reg = READ4(sc, DCU_INT_STATUS);
+ WRITE4(sc, DCU_INT_STATUS, reg);
+
+ /* TODO interrupt handler */
+}
+
+static int
+get_panel_info(struct dcu_softc *sc, struct panel_info *panel)
+{
+ phandle_t node;
+ pcell_t dts_value[3];
+ int len;
+
+ if ((node = ofw_bus_get_node(sc->dev)) == -1)
+ return (ENXIO);
+
+ /* panel size */
+ if ((len = OF_getproplen(node, "panel-size")) <= 0)
+ return (ENXIO);
+ OF_getencprop(node, "panel-size", dts_value, len);
+ panel->width = dts_value[0];
+ panel->height = dts_value[1];
+
+ /* hsync */
+ if ((len = OF_getproplen(node, "panel-hsync")) <= 0)
+ return (ENXIO);
+ OF_getencprop(node, "panel-hsync", dts_value, len);
+ panel->h_back_porch = dts_value[0];
+ panel->h_pulse_width = dts_value[1];
+ panel->h_front_porch = dts_value[2];
+
+ /* vsync */
+ if ((len = OF_getproplen(node, "panel-vsync")) <= 0)
+ return (ENXIO);
+ OF_getencprop(node, "panel-vsync", dts_value, len);
+ panel->v_back_porch = dts_value[0];
+ panel->v_pulse_width = dts_value[1];
+ panel->v_front_porch = dts_value[2];
+
+ /* clk divider */
+ if ((len = OF_getproplen(node, "panel-clk-div")) <= 0)
+ return (ENXIO);
+ OF_getencprop(node, "panel-clk-div", dts_value, len);
+ panel->clk_div = dts_value[0];
+
+ /* backlight pin */
+ if ((len = OF_getproplen(node, "panel-backlight-pin")) <= 0)
+ return (ENXIO);
+ OF_getencprop(node, "panel-backlight-pin", dts_value, len);
+ panel->backlight_pin = dts_value[0];
+
+ return (0);
+}
+
+static int
+dcu_init(struct dcu_softc *sc)
+{
+ struct panel_info *panel;
+ int reg;
+ int i;
+
+ panel = sc->panel;
+
+ /* Configure DCU */
+ reg = ((sc->sc_info.fb_height) << DELTA_Y_S);
+ reg |= (sc->sc_info.fb_width / 16);
+ WRITE4(sc, DCU_DISP_SIZE, reg);
+
+ reg = (panel->h_back_porch << BP_H_SHIFT);
+ reg |= (panel->h_pulse_width << PW_H_SHIFT);
+ reg |= (panel->h_front_porch << FP_H_SHIFT);
+ WRITE4(sc, DCU_HSYN_PARA, reg);
+
+ reg = (panel->v_back_porch << BP_V_SHIFT);
+ reg |= (panel->v_pulse_width << PW_V_SHIFT);
+ reg |= (panel->v_front_porch << FP_V_SHIFT);
+ WRITE4(sc, DCU_VSYN_PARA, reg);
+
+ WRITE4(sc, DCU_BGND, 0);
+ WRITE4(sc, DCU_DIV_RATIO, panel->clk_div);
+
+ reg = (INV_VS | INV_HS);
+ WRITE4(sc, DCU_SYNPOL, reg);
+
+ /* TODO: export to panel info */
+ reg = (0x3 << LS_BF_VS_SHIFT);
+ reg |= (0x78 << OUT_BUF_HIGH_SHIFT);
+ reg |= (0 << OUT_BUF_LOW_SHIFT);
+ WRITE4(sc, DCU_THRESHOLD, reg);
+
+ /* Mask all the interrupts */
+ WRITE4(sc, DCU_INT_MASK, 0xffffffff);
+
+ /* Reset all layers */
+ for (i = 0; i < NUM_LAYERS; i++) {
+ WRITE4(sc, DCU_CTRLDESCLn_1(i), 0x0);
+ WRITE4(sc, DCU_CTRLDESCLn_2(i), 0x0);
+ WRITE4(sc, DCU_CTRLDESCLn_3(i), 0x0);
+ WRITE4(sc, DCU_CTRLDESCLn_4(i), 0x0);
+ WRITE4(sc, DCU_CTRLDESCLn_5(i), 0x0);
+ WRITE4(sc, DCU_CTRLDESCLn_6(i), 0x0);
+ WRITE4(sc, DCU_CTRLDESCLn_7(i), 0x0);
+ WRITE4(sc, DCU_CTRLDESCLn_8(i), 0x0);
+ WRITE4(sc, DCU_CTRLDESCLn_9(i), 0x0);
+ }
+
+ /* Setup first layer */
+ reg = (sc->sc_info.fb_width | (sc->sc_info.fb_height << 16));
+ WRITE4(sc, DCU_CTRLDESCLn_1(0), reg);
+ WRITE4(sc, DCU_CTRLDESCLn_2(0), 0x0);
+ WRITE4(sc, DCU_CTRLDESCLn_3(0), sc->sc_info.fb_pbase);
+ reg = (BPP24 << BPP_SHIFT);
+ reg |= EN_LAYER;
+ reg |= (0xFF << TRANS_SHIFT); /* completely opaque */
+ WRITE4(sc, DCU_CTRLDESCLn_4(0), reg);
+ WRITE4(sc, DCU_CTRLDESCLn_5(0), 0xffffff);
+ WRITE4(sc, DCU_CTRLDESCLn_6(0), 0x0);
+ WRITE4(sc, DCU_CTRLDESCLn_7(0), 0x0);
+ WRITE4(sc, DCU_CTRLDESCLn_8(0), 0x0);
+ WRITE4(sc, DCU_CTRLDESCLn_9(0), 0x0);
+
+ /* Enable DCU in normal mode */
+ reg = READ4(sc, DCU_DCU_MODE);
+ reg &= ~(DCU_MODE_M << DCU_MODE_S);
+ reg |= (DCU_MODE_NORMAL << DCU_MODE_S);
+ reg |= (RASTER_EN);
+ WRITE4(sc, DCU_DCU_MODE, reg);
+ WRITE4(sc, DCU_UPDATE_MODE, READREG);
+
+ return (0);
+}
+
+static int
+dcu_attach(device_t dev)
+{
+ struct panel_info panel;
+ struct dcu_softc *sc;
+ device_t gpio_dev;
+ int err;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ if (bus_alloc_resources(dev, dcu_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ /* Memory interface */
+ sc->bst = rman_get_bustag(sc->res[0]);
+ sc->bsh = rman_get_bushandle(sc->res[0]);
+
+ /* Setup interrupt handler */
+ err = bus_setup_intr(dev, sc->res[1], INTR_TYPE_BIO | INTR_MPSAFE,
+ NULL, dcu_intr, sc, &sc->ih);
+ if (err) {
+ device_printf(dev, "Unable to alloc interrupt resource.\n");
+ return (ENXIO);
+ }
+
+ if (get_panel_info(sc, &panel)) {
+ device_printf(dev, "Can't get panel info\n");
+ return (ENXIO);
+ }
+
+ sc->panel = &panel;
+
+ /* Bypass timing control (used for raw lcd panels) */
+ tcon_bypass();
+
+ /* Get the GPIO device, we need this to give power to USB */
+ gpio_dev = devclass_get_device(devclass_find("gpio"), 0);
+ if (gpio_dev == NULL) {
+ device_printf(sc->dev, "Error: failed to get the GPIO dev\n");
+ return (1);
+ }
+
+ /* Turn on backlight */
+ /* TODO: Use FlexTimer/PWM */
+ GPIO_PIN_SETFLAGS(gpio_dev, panel.backlight_pin, GPIO_PIN_OUTPUT);
+ GPIO_PIN_SET(gpio_dev, panel.backlight_pin, GPIO_PIN_HIGH);
+
+ sc->sc_info.fb_width = panel.width;
+ sc->sc_info.fb_height = panel.height;
+ sc->sc_info.fb_stride = sc->sc_info.fb_width * 3;
+ sc->sc_info.fb_bpp = sc->sc_info.fb_depth = 24;
+ sc->sc_info.fb_size = sc->sc_info.fb_height * sc->sc_info.fb_stride;
+ sc->sc_info.fb_vbase = (intptr_t)contigmalloc(sc->sc_info.fb_size,
+ M_DEVBUF, M_ZERO, 0, ~0, PAGE_SIZE, 0);
+ sc->sc_info.fb_pbase = (intptr_t)vtophys(sc->sc_info.fb_vbase);
+
+#if 0
+ printf("%dx%d [%d]\n", sc->sc_info.fb_width, sc->sc_info.fb_height,
+ sc->sc_info.fb_stride);
+ printf("pbase == 0x%08x\n", sc->sc_info.fb_pbase);
+#endif
+
+ memset((int8_t *)sc->sc_info.fb_vbase, 0x0, sc->sc_info.fb_size);
+
+ dcu_init(sc);
+
+ sc->sc_info.fb_name = device_get_nameunit(dev);
+
+ /* Ask newbus to attach framebuffer device to me. */
+ sc->sc_fbd = device_add_child(dev, "fbd", device_get_unit(dev));
+ if (sc->sc_fbd == NULL)
+ device_printf(dev, "Can't attach fbd device\n");
+
+ if (device_probe_and_attach(sc->sc_fbd) != 0) {
+ device_printf(sc->dev, "Failed to attach fbd device\n");
+ }
+
+ return (0);
+}
+
+static struct fb_info *
+dcu4_fb_getinfo(device_t dev)
+{
+ struct dcu_softc *sc = device_get_softc(dev);
+
+ return (&sc->sc_info);
+}
+
+static device_method_t dcu_methods[] = {
+ DEVMETHOD(device_probe, dcu_probe),
+ DEVMETHOD(device_attach, dcu_attach),
+
+ /* Framebuffer service methods */
+ DEVMETHOD(fb_getinfo, dcu4_fb_getinfo),
+ { 0, 0 }
+};
+
+static driver_t dcu_driver = {
+ "fb",
+ dcu_methods,
+ sizeof(struct dcu_softc),
+};
+
+static devclass_t dcu_devclass;
+
+DRIVER_MODULE(fb, simplebus, dcu_driver, dcu_devclass, 0, 0);
diff --git a/sys/arm/freescale/vybrid/vf_dmamux.c b/sys/arm/freescale/vybrid/vf_dmamux.c
new file mode 100644
index 000000000000..978bab5fc1c7
--- /dev/null
+++ b/sys/arm/freescale/vybrid/vf_dmamux.c
@@ -0,0 +1,155 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Vybrid Family Direct Memory Access Multiplexer (DMAMUX)
+ * Chapter 22, Vybrid Reference Manual, Rev. 5, 07/2013
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <arm/freescale/vybrid/vf_common.h>
+#include <arm/freescale/vybrid/vf_dmamux.h>
+
+#define DMAMUX_CHCFG(n) (0x1 * n) /* Channels 0-15 Cfg Reg */
+#define CHCFG_ENBL (1 << 7) /* Channel Enable */
+#define CHCFG_TRIG (1 << 6) /* Channel Trigger Enable */
+#define CHCFG_SOURCE_MASK 0x3f /* Channel Source (Slot) */
+#define CHCFG_SOURCE_SHIFT 0
+
+struct dmamux_softc {
+ struct resource *res[4];
+ bus_space_tag_t bst[4];
+ bus_space_handle_t bsh[4];
+};
+
+struct dmamux_softc *dmamux_sc;
+
+static struct resource_spec dmamux_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* DMAMUX0 */
+ { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* DMAMUX1 */
+ { SYS_RES_MEMORY, 2, RF_ACTIVE }, /* DMAMUX2 */
+ { SYS_RES_MEMORY, 3, RF_ACTIVE }, /* DMAMUX3 */
+ { -1, 0 }
+};
+
+static int
+dmamux_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "fsl,mvf600-dmamux"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Vybrid Family Direct Memory Access Multiplexer");
+ return (BUS_PROBE_DEFAULT);
+}
+
+int
+dmamux_configure(int mux, int source, int channel, int enable)
+{
+ struct dmamux_softc *sc;
+ int reg;
+
+ sc = dmamux_sc;
+
+ MUX_WRITE1(sc, mux, DMAMUX_CHCFG(channel), 0x0);
+
+ reg = 0;
+ if (enable)
+ reg |= (CHCFG_ENBL);
+
+ reg &= ~(CHCFG_SOURCE_MASK << CHCFG_SOURCE_SHIFT);
+ reg |= (source << CHCFG_SOURCE_SHIFT);
+
+ MUX_WRITE1(sc, mux, DMAMUX_CHCFG(channel), reg);
+
+ return (0);
+}
+
+static int
+dmamux_attach(device_t dev)
+{
+ struct dmamux_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+
+ if (bus_alloc_resources(dev, dmamux_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ /* Memory interface */
+ for (i = 0; i < 4; i++) {
+ sc->bst[i] = rman_get_bustag(sc->res[i]);
+ sc->bsh[i] = rman_get_bushandle(sc->res[i]);
+ }
+
+ dmamux_sc = sc;
+
+ return (0);
+}
+
+static device_method_t dmamux_methods[] = {
+ DEVMETHOD(device_probe, dmamux_probe),
+ DEVMETHOD(device_attach, dmamux_attach),
+ { 0, 0 }
+};
+
+static driver_t dmamux_driver = {
+ "dmamux",
+ dmamux_methods,
+ sizeof(struct dmamux_softc),
+};
+
+static devclass_t dmamux_devclass;
+
+DRIVER_MODULE(dmamux, simplebus, dmamux_driver, dmamux_devclass, 0, 0);
diff --git a/sys/arm/freescale/vybrid/vf_dmamux.h b/sys/arm/freescale/vybrid/vf_dmamux.h
new file mode 100644
index 000000000000..8b3a2b959b42
--- /dev/null
+++ b/sys/arm/freescale/vybrid/vf_dmamux.h
@@ -0,0 +1,50 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+int dmamux_configure(int mux, int source, int channel, int enable);
+
+enum mux_num {
+ MUX0,
+ MUX1,
+ MUX2,
+ MUX3,
+};
+
+enum mux_grp {
+ MUXGRP0, /* MUX[0,3] */
+ MUXGRP1, /* MUX[1,2] */
+};
+
+/* DMAMUX */
+#define MUX_READ1(_sc, _mux, _reg) \
+ bus_space_read_1(_sc->bst[_mux], _sc->bsh[_mux], _reg)
+
+#define MUX_WRITE1(_sc, _mux, _reg, _val) \
+ bus_space_write_1(_sc->bst[_mux], _sc->bsh[_mux], _reg, _val)
diff --git a/sys/arm/freescale/vybrid/vf_edma.c b/sys/arm/freescale/vybrid/vf_edma.c
new file mode 100644
index 000000000000..23ce8cfd2c8d
--- /dev/null
+++ b/sys/arm/freescale/vybrid/vf_edma.c
@@ -0,0 +1,338 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Vybrid Family Enhanced Direct Memory Access Controller (eDMA)
+ * Chapter 21, Vybrid Reference Manual, Rev. 5, 07/2013
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <arm/freescale/vybrid/vf_edma.h>
+#include <arm/freescale/vybrid/vf_dmamux.h>
+#include <arm/freescale/vybrid/vf_common.h>
+
+struct edma_channel {
+ uint32_t enabled;
+ uint32_t mux_num;
+ uint32_t mux_src;
+ uint32_t mux_chn;
+ uint32_t (*ih) (void *, int);
+ void *ih_user;
+};
+
+static struct edma_channel edma_map[EDMA_NUM_CHANNELS];
+
+static struct resource_spec edma_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* TCD */
+ { SYS_RES_IRQ, 0, RF_ACTIVE }, /* Transfer complete */
+ { SYS_RES_IRQ, 1, RF_ACTIVE }, /* Error Interrupt */
+ { -1, 0 }
+};
+
+static int
+edma_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "fsl,mvf600-edma"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Vybrid Family eDMA Controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static void
+edma_transfer_complete_intr(void *arg)
+{
+ struct edma_channel *ch;
+ struct edma_softc *sc;
+ int interrupts;
+ int i;
+
+ sc = arg;
+
+ interrupts = READ4(sc, DMA_INT);
+ WRITE1(sc, DMA_CINT, CINT_CAIR);
+
+ for (i = 0; i < EDMA_NUM_CHANNELS; i++) {
+ if (interrupts & (0x1 << i)) {
+ ch = &edma_map[i];
+ if (ch->enabled == 1) {
+ if (ch->ih != NULL) {
+ ch->ih(ch->ih_user, i);
+ }
+ }
+ }
+ }
+}
+
+static void
+edma_err_intr(void *arg)
+{
+ struct edma_softc *sc;
+ int reg;
+
+ sc = arg;
+
+ reg = READ4(sc, DMA_ERR);
+
+#if 0
+ device_printf(sc->dev, "DMA_ERR 0x%08x, ES 0x%08x\n",
+ reg, READ4(sc, DMA_ES));
+#endif
+
+ WRITE1(sc, DMA_CERR, CERR_CAEI);
+}
+
+static int
+channel_free(struct edma_softc *sc, int chnum)
+{
+ struct edma_channel *ch;
+
+ ch = &edma_map[chnum];
+ ch->enabled = 0;
+
+ dmamux_configure(ch->mux_num, ch->mux_src, ch->mux_chn, 0);
+
+ return (0);
+}
+
+static int
+channel_configure(struct edma_softc *sc, int mux_grp, int mux_src)
+{
+ struct edma_channel *ch;
+ int channel_first;
+ int mux_num;
+ int chnum;
+ int i;
+
+ if ((sc->device_id == 0 && mux_grp == 1) || \
+ (sc->device_id == 1 && mux_grp == 0)) {
+ channel_first = NCHAN_PER_MUX;
+ mux_num = (sc->device_id * 2) + 1;
+ } else {
+ channel_first = 0;
+ mux_num = sc->device_id * 2;
+ }
+
+ /* Take first unused eDMA channel */
+ ch = NULL;
+ for (i = channel_first; i < (channel_first + NCHAN_PER_MUX); i++) {
+ ch = &edma_map[i];
+ if (ch->enabled == 0) {
+ break;
+ }
+ ch = NULL;
+ }
+
+ if (ch == NULL) {
+ /* Can't find free channel */
+ return (-1);
+ }
+
+ chnum = i;
+
+ ch->enabled = 1;
+ ch->mux_num = mux_num;
+ ch->mux_src = mux_src;
+ ch->mux_chn = (chnum - channel_first); /* 0 to 15 */
+
+ dmamux_configure(ch->mux_num, ch->mux_src, ch->mux_chn, 1);
+
+ return (chnum);
+}
+
+static int
+dma_stop(struct edma_softc *sc, int chnum)
+{
+ int reg;
+
+ reg = READ4(sc, DMA_ERQ);
+ reg &= ~(0x1 << chnum);
+ WRITE4(sc, DMA_ERQ, reg);
+
+ return (0);
+}
+
+static int
+dma_setup(struct edma_softc *sc, struct tcd_conf *tcd)
+{
+ struct edma_channel *ch;
+ int chnum;
+ int reg;
+
+ chnum = tcd->channel;
+
+ ch = &edma_map[chnum];
+ ch->ih = tcd->ih;
+ ch->ih_user = tcd->ih_user;
+
+ TCD_WRITE4(sc, DMA_TCDn_SADDR(chnum), tcd->saddr);
+ TCD_WRITE4(sc, DMA_TCDn_DADDR(chnum), tcd->daddr);
+
+ reg = (tcd->smod << TCD_ATTR_SMOD_SHIFT);
+ reg |= (tcd->dmod << TCD_ATTR_DMOD_SHIFT);
+ reg |= (tcd->ssize << TCD_ATTR_SSIZE_SHIFT);
+ reg |= (tcd->dsize << TCD_ATTR_DSIZE_SHIFT);
+ TCD_WRITE2(sc, DMA_TCDn_ATTR(chnum), reg);
+
+ TCD_WRITE2(sc, DMA_TCDn_SOFF(chnum), tcd->soff);
+ TCD_WRITE2(sc, DMA_TCDn_DOFF(chnum), tcd->doff);
+ TCD_WRITE4(sc, DMA_TCDn_SLAST(chnum), tcd->slast);
+ TCD_WRITE4(sc, DMA_TCDn_DLASTSGA(chnum), tcd->dlast_sga);
+ TCD_WRITE4(sc, DMA_TCDn_NBYTES_MLOFFYES(chnum), tcd->nbytes);
+
+ reg = tcd->nmajor; /* Current Major Iteration Count */
+ TCD_WRITE2(sc, DMA_TCDn_CITER_ELINKNO(chnum), reg);
+ TCD_WRITE2(sc, DMA_TCDn_BITER_ELINKNO(chnum), reg);
+
+ reg = (TCD_CSR_INTMAJOR);
+ if(tcd->majorelink == 1) {
+ reg |= TCD_CSR_MAJORELINK;
+ reg |= (tcd->majorelinkch << TCD_CSR_MAJORELINKCH_SHIFT);
+ }
+ TCD_WRITE2(sc, DMA_TCDn_CSR(chnum), reg);
+
+ /* Enable requests */
+ reg = READ4(sc, DMA_ERQ);
+ reg |= (0x1 << chnum);
+ WRITE4(sc, DMA_ERQ, reg);
+
+ /* Enable error interrupts */
+ reg = READ4(sc, DMA_EEI);
+ reg |= (0x1 << chnum);
+ WRITE4(sc, DMA_EEI, reg);
+
+ return (0);
+}
+
+static int
+dma_request(struct edma_softc *sc, int chnum)
+{
+ int reg;
+
+ /* Start */
+ reg = TCD_READ2(sc, DMA_TCDn_CSR(chnum));
+ reg |= TCD_CSR_START;
+ TCD_WRITE2(sc, DMA_TCDn_CSR(chnum), reg);
+
+ return (0);
+}
+
+static int
+edma_attach(device_t dev)
+{
+ struct edma_softc *sc;
+ phandle_t node;
+ int dts_value;
+ int len;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ if ((node = ofw_bus_get_node(sc->dev)) == -1)
+ return (ENXIO);
+
+ if ((len = OF_getproplen(node, "device-id")) <= 0)
+ return (ENXIO);
+
+ OF_getencprop(node, "device-id", &dts_value, len);
+ sc->device_id = dts_value;
+
+ sc->dma_stop = dma_stop;
+ sc->dma_setup = dma_setup;
+ sc->dma_request = dma_request;
+ sc->channel_configure = channel_configure;
+ sc->channel_free = channel_free;
+
+ if (bus_alloc_resources(dev, edma_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ /* Memory interface */
+ sc->bst = rman_get_bustag(sc->res[0]);
+ sc->bsh = rman_get_bushandle(sc->res[0]);
+ sc->bst_tcd = rman_get_bustag(sc->res[1]);
+ sc->bsh_tcd = rman_get_bushandle(sc->res[1]);
+
+ /* Setup interrupt handlers */
+ if (bus_setup_intr(dev, sc->res[2], INTR_TYPE_BIO | INTR_MPSAFE,
+ NULL, edma_transfer_complete_intr, sc, &sc->tc_ih)) {
+ device_printf(dev, "Unable to alloc DMA intr resource.\n");
+ return (ENXIO);
+ }
+
+ if (bus_setup_intr(dev, sc->res[3], INTR_TYPE_BIO | INTR_MPSAFE,
+ NULL, edma_err_intr, sc, &sc->err_ih)) {
+ device_printf(dev, "Unable to alloc DMA Err intr resource.\n");
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static device_method_t edma_methods[] = {
+ DEVMETHOD(device_probe, edma_probe),
+ DEVMETHOD(device_attach, edma_attach),
+ { 0, 0 }
+};
+
+static driver_t edma_driver = {
+ "edma",
+ edma_methods,
+ sizeof(struct edma_softc),
+};
+
+static devclass_t edma_devclass;
+
+DRIVER_MODULE(edma, simplebus, edma_driver, edma_devclass, 0, 0);
diff --git a/sys/arm/freescale/vybrid/vf_edma.h b/sys/arm/freescale/vybrid/vf_edma.h
new file mode 100644
index 000000000000..72968e7ce79e
--- /dev/null
+++ b/sys/arm/freescale/vybrid/vf_edma.h
@@ -0,0 +1,188 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define DMA_CR 0x000 /* Control */
+#define DMA_ES 0x004 /* Error Status */
+#define DMA_ERQ 0x00C /* Enable Request */
+#define DMA_EEI 0x014 /* Enable Error Interrupt */
+#define DMA_CEEI 0x018 /* Clear Enable Error Interrupt */
+#define DMA_SEEI 0x019 /* Set Enable Error Interrupt */
+#define DMA_CERQ 0x01A /* Clear Enable Request */
+#define DMA_SERQ 0x01B /* Set Enable Request */
+#define DMA_CDNE 0x01C /* Clear DONE Status Bit */
+#define DMA_SSRT 0x01D /* Set START Bit */
+#define DMA_CERR 0x01E /* Clear Error */
+#define CERR_CAEI (1 << 6) /* Clear All Error Indicators */
+#define DMA_CINT 0x01F /* Clear Interrupt Request */
+#define CINT_CAIR (1 << 6) /* Clear All Interrupt Requests */
+#define DMA_INT 0x024 /* Interrupt Request */
+#define DMA_ERR 0x02C /* Error */
+#define DMA_HRS 0x034 /* Hardware Request Status */
+#define DMA_EARS 0x044 /* Enable Asynchronous Request in Stop */
+#define DMA_DCHPRI3 0x100 /* Channel n Priority */
+#define DMA_DCHPRI2 0x101 /* Channel n Priority */
+#define DMA_DCHPRI1 0x102 /* Channel n Priority */
+#define DMA_DCHPRI0 0x103 /* Channel n Priority */
+#define DMA_DCHPRI7 0x104 /* Channel n Priority */
+#define DMA_DCHPRI6 0x105 /* Channel n Priority */
+#define DMA_DCHPRI5 0x106 /* Channel n Priority */
+#define DMA_DCHPRI4 0x107 /* Channel n Priority */
+#define DMA_DCHPRI11 0x108 /* Channel n Priority */
+#define DMA_DCHPRI10 0x109 /* Channel n Priority */
+#define DMA_DCHPRI9 0x10A /* Channel n Priority */
+#define DMA_DCHPRI8 0x10B /* Channel n Priority */
+#define DMA_DCHPRI15 0x10C /* Channel n Priority */
+#define DMA_DCHPRI14 0x10D /* Channel n Priority */
+#define DMA_DCHPRI13 0x10E /* Channel n Priority */
+#define DMA_DCHPRI12 0x10F /* Channel n Priority */
+#define DMA_DCHPRI19 0x110 /* Channel n Priority */
+#define DMA_DCHPRI18 0x111 /* Channel n Priority */
+#define DMA_DCHPRI17 0x112 /* Channel n Priority */
+#define DMA_DCHPRI16 0x113 /* Channel n Priority */
+#define DMA_DCHPRI23 0x114 /* Channel n Priority */
+#define DMA_DCHPRI22 0x115 /* Channel n Priority */
+#define DMA_DCHPRI21 0x116 /* Channel n Priority */
+#define DMA_DCHPRI20 0x117 /* Channel n Priority */
+#define DMA_DCHPRI27 0x118 /* Channel n Priority */
+#define DMA_DCHPRI26 0x119 /* Channel n Priority */
+#define DMA_DCHPRI25 0x11A /* Channel n Priority */
+#define DMA_DCHPRI24 0x11B /* Channel n Priority */
+#define DMA_DCHPRI31 0x11C /* Channel n Priority */
+#define DMA_DCHPRI30 0x11D /* Channel n Priority */
+#define DMA_DCHPRI29 0x11E /* Channel n Priority */
+#define DMA_DCHPRI28 0x11F /* Channel n Priority */
+
+#define DMA_TCDn_SADDR(n) (0x00 + 0x20 * n) /* Source Address */
+#define DMA_TCDn_SOFF(n) (0x04 + 0x20 * n) /* Signed Source Address Offset */
+#define DMA_TCDn_ATTR(n) (0x06 + 0x20 * n) /* Transfer Attributes */
+#define DMA_TCDn_NBYTES_MLNO(n) (0x08 + 0x20 * n) /* Minor Byte Count */
+#define DMA_TCDn_NBYTES_MLOFFNO(n) (0x08 + 0x20 * n) /* Signed Minor Loop Offset */
+#define DMA_TCDn_NBYTES_MLOFFYES(n) (0x08 + 0x20 * n) /* Signed Minor Loop Offset */
+#define DMA_TCDn_SLAST(n) (0x0C + 0x20 * n) /* Last Source Address Adjustment */
+#define DMA_TCDn_DADDR(n) (0x10 + 0x20 * n) /* Destination Address */
+#define DMA_TCDn_DOFF(n) (0x14 + 0x20 * n) /* Signed Destination Address Offset */
+#define DMA_TCDn_CITER_ELINKYES(n) (0x16 + 0x20 * n) /* Current Minor Loop Link, Major Loop Count */
+#define DMA_TCDn_CITER_ELINKNO(n) (0x16 + 0x20 * n)
+#define DMA_TCDn_DLASTSGA(n) (0x18 + 0x20 * n) /* Last Dst Addr Adjustment/Scatter Gather Address */
+#define DMA_TCDn_CSR(n) (0x1C + 0x20 * n) /* Control and Status */
+#define DMA_TCDn_BITER_ELINKYES(n) (0x1E + 0x20 * n) /* Beginning Minor Loop Link, Major Loop Count */
+#define DMA_TCDn_BITER_ELINKNO(n) (0x1E + 0x20 * n) /* Beginning Minor Loop Link, Major Loop Count */
+
+#define TCD_CSR_START (1 << 0)
+#define TCD_CSR_INTMAJOR (1 << 1)
+#define TCD_CSR_INTHALF (1 << 2)
+#define TCD_CSR_DREQ (1 << 3)
+#define TCD_CSR_ESG (1 << 4)
+#define TCD_CSR_MAJORELINK (1 << 5)
+#define TCD_CSR_ACTIVE (1 << 6)
+#define TCD_CSR_DONE (1 << 7)
+#define TCD_CSR_MAJORELINKCH_SHIFT 8
+
+#define TCD_ATTR_SMOD_SHIFT 11 /* Source Address Modulo */
+#define TCD_ATTR_SSIZE_SHIFT 8 /* Source Data Transfer Size */
+#define TCD_ATTR_DMOD_SHIFT 3 /* Dst Address Modulo */
+#define TCD_ATTR_DSIZE_SHIFT 0 /* Dst Data Transfer Size */
+
+#define TCD_READ4(_sc, _reg) \
+ bus_space_read_4(_sc->bst_tcd, _sc->bsh_tcd, _reg)
+#define TCD_WRITE4(_sc, _reg, _val) \
+ bus_space_write_4(_sc->bst_tcd, _sc->bsh_tcd, _reg, _val)
+#define TCD_READ2(_sc, _reg) \
+ bus_space_read_2(_sc->bst_tcd, _sc->bsh_tcd, _reg)
+#define TCD_WRITE2(_sc, _reg, _val) \
+ bus_space_write_2(_sc->bst_tcd, _sc->bsh_tcd, _reg, _val)
+#define TCD_READ1(_sc, _reg) \
+ bus_space_read_1(_sc->bst_tcd, _sc->bsh_tcd, _reg)
+#define TCD_WRITE1(_sc, _reg, _val) \
+ bus_space_write_1(_sc->bst_tcd, _sc->bsh_tcd, _reg, _val)
+
+#define EDMA_NUM_DEVICES 2
+#define EDMA_NUM_CHANNELS 32
+#define NCHAN_PER_MUX 16
+
+struct tcd_conf {
+ bus_addr_t saddr;
+ bus_addr_t daddr;
+ uint32_t nbytes;
+ uint32_t nmajor;
+ uint32_t majorelink;
+ uint32_t majorelinkch;
+ uint32_t esg;
+ uint32_t smod;
+ uint32_t dmod;
+ uint32_t soff;
+ uint32_t doff;
+ uint32_t ssize;
+ uint32_t dsize;
+ uint32_t slast;
+ uint32_t dlast_sga;
+ uint32_t channel;
+ uint32_t (*ih)(void *, int);
+ void *ih_user;
+};
+
+/*
+ * TCD struct is described at
+ * Vybrid Reference Manual, Rev. 5, 07/2013
+ *
+ * Should be used for Scatter/Gathering feature.
+ */
+
+struct TCD {
+ uint32_t saddr;
+ uint16_t attr;
+ uint16_t soff;
+ uint32_t nbytes;
+ uint32_t slast;
+ uint32_t daddr;
+ uint16_t citer;
+ uint16_t doff;
+ uint32_t dlast_sga;
+ uint16_t biter;
+ uint16_t csr;
+} __packed;
+
+struct edma_softc {
+ device_t dev;
+ struct resource *res[4];
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+ bus_space_tag_t bst_tcd;
+ bus_space_handle_t bsh_tcd;
+ void *tc_ih;
+ void *err_ih;
+ uint32_t device_id;
+
+ int (*channel_configure) (struct edma_softc *, int, int);
+ int (*channel_free) (struct edma_softc *, int);
+ int (*dma_request) (struct edma_softc *, int);
+ int (*dma_setup) (struct edma_softc *, struct tcd_conf *);
+ int (*dma_stop) (struct edma_softc *, int);
+};
diff --git a/sys/arm/freescale/vybrid/vf_ehci.c b/sys/arm/freescale/vybrid/vf_ehci.c
new file mode 100644
index 000000000000..8c1ddfc7d79e
--- /dev/null
+++ b/sys/arm/freescale/vybrid/vf_ehci.c
@@ -0,0 +1,428 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Vybrid Family Universal Serial Bus (USB) Controller
+ * Chapter 44-45, Vybrid Reference Manual, Rev. 5, 07/2013
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_bus.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/condvar.h>
+#include <sys/rman.h>
+#include <sys/gpio.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/controller/ehci.h>
+#include <dev/usb/controller/ehcireg.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include "gpio_if.h"
+#include "opt_platform.h"
+
+#define ENUTMILEVEL3 (1 << 15)
+#define ENUTMILEVEL2 (1 << 14)
+
+#define GPIO_USB_PWR 134
+
+#define USB_ID 0x000 /* Identification register */
+#define USB_HWGENERAL 0x004 /* Hardware General */
+#define USB_HWHOST 0x008 /* Host Hardware Parameters */
+#define USB_HWDEVICE 0x00C /* Device Hardware Parameters */
+#define USB_HWTXBUF 0x010 /* TX Buffer Hardware Parameters */
+#define USB_HWRXBUF 0x014 /* RX Buffer Hardware Parameters */
+#define USB_HCSPARAMS 0x104 /* Host Controller Structural Parameters */
+
+#define USBPHY_PWD 0x00 /* PHY Power-Down Register */
+#define USBPHY_PWD_SET 0x04 /* PHY Power-Down Register */
+#define USBPHY_PWD_CLR 0x08 /* PHY Power-Down Register */
+#define USBPHY_PWD_TOG 0x0C /* PHY Power-Down Register */
+#define USBPHY_TX 0x10 /* PHY Transmitter Control Register */
+#define USBPHY_RX 0x20 /* PHY Receiver Control Register */
+#define USBPHY_RX_SET 0x24 /* PHY Receiver Control Register */
+#define USBPHY_RX_CLR 0x28 /* PHY Receiver Control Register */
+#define USBPHY_RX_TOG 0x2C /* PHY Receiver Control Register */
+#define USBPHY_CTRL 0x30 /* PHY General Control Register */
+#define USBPHY_CTRL_SET 0x34 /* PHY General Control Register */
+#define USBPHY_CTRL_CLR 0x38 /* PHY General Control Register */
+#define USBPHY_CTRL_TOG 0x3C /* PHY General Control Register */
+#define USBPHY_STATUS 0x40 /* PHY Status Register */
+#define USBPHY_DEBUG 0x50 /* PHY Debug Register */
+#define USBPHY_DEBUG_SET 0x54 /* PHY Debug Register */
+#define USBPHY_DEBUG_CLR 0x58 /* PHY Debug Register */
+#define USBPHY_DEBUG_TOG 0x5C /* PHY Debug Register */
+#define USBPHY_DEBUG0_STATUS 0x60 /* UTMI Debug Status Register 0 */
+#define USBPHY_DEBUG1 0x70 /* UTMI Debug Status Register 1 */
+#define USBPHY_DEBUG1_SET 0x74 /* UTMI Debug Status Register 1 */
+#define USBPHY_DEBUG1_CLR 0x78 /* UTMI Debug Status Register 1 */
+#define USBPHY_DEBUG1_TOG 0x7C /* UTMI Debug Status Register 1 */
+#define USBPHY_VERSION 0x80 /* UTMI RTL Version */
+#define USBPHY_IP 0x90 /* PHY IP Block Register */
+#define USBPHY_IP_SET 0x94 /* PHY IP Block Register */
+#define USBPHY_IP_CLR 0x98 /* PHY IP Block Register */
+#define USBPHY_IP_TOG 0x9C /* PHY IP Block Register */
+
+#define USBPHY_CTRL_SFTRST (1U << 31)
+#define USBPHY_CTRL_CLKGATE (1 << 30)
+#define USBPHY_DEBUG_CLKGATE (1 << 30)
+
+#define PHY_READ4(_sc, _reg) \
+ bus_space_read_4(_sc->bst_phy, _sc->bsh_phy, _reg)
+#define PHY_WRITE4(_sc, _reg, _val) \
+ bus_space_write_4(_sc->bst_phy, _sc->bsh_phy, _reg, _val)
+
+#define USBC_READ4(_sc, _reg) \
+ bus_space_read_4(_sc->bst_usbc, _sc->bsh_usbc, _reg)
+#define USBC_WRITE4(_sc, _reg, _val) \
+ bus_space_write_4(_sc->bst_usbc, _sc->bsh_usbc, _reg, _val)
+
+/* Forward declarations */
+static int vybrid_ehci_attach(device_t dev);
+static int vybrid_ehci_detach(device_t dev);
+static int vybrid_ehci_probe(device_t dev);
+
+struct vybrid_ehci_softc {
+ ehci_softc_t base;
+ device_t dev;
+ struct resource *res[6];
+ bus_space_tag_t bst_phy;
+ bus_space_handle_t bsh_phy;
+ bus_space_tag_t bst_usbc;
+ bus_space_handle_t bsh_usbc;
+};
+
+static struct resource_spec vybrid_ehci_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_MEMORY, 1, RF_ACTIVE },
+ { SYS_RES_MEMORY, 2, RF_ACTIVE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+static device_method_t ehci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, vybrid_ehci_probe),
+ DEVMETHOD(device_attach, vybrid_ehci_attach),
+ DEVMETHOD(device_detach, vybrid_ehci_detach),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ { 0, 0 }
+};
+
+/* kobj_class definition */
+static driver_t ehci_driver = {
+ "ehci",
+ ehci_methods,
+ sizeof(ehci_softc_t)
+};
+
+static devclass_t ehci_devclass;
+
+DRIVER_MODULE(vybrid_ehci, simplebus, ehci_driver, ehci_devclass, 0, 0);
+MODULE_DEPEND(vybrid_ehci, usb, 1, 1, 1);
+
+static void
+vybrid_ehci_post_reset(struct ehci_softc *ehci_softc)
+{
+ uint32_t usbmode;
+
+ /* Force HOST mode */
+ usbmode = EOREAD4(ehci_softc, EHCI_USBMODE_NOLPM);
+ usbmode &= ~EHCI_UM_CM;
+ usbmode |= EHCI_UM_CM_HOST;
+ EOWRITE4(ehci_softc, EHCI_USBMODE_NOLPM, usbmode);
+}
+
+/*
+ * Public methods
+ */
+static int
+vybrid_ehci_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_is_compatible(dev, "fsl,mvf600-usb-ehci") == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Vybrid Family integrated USB controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+phy_init(struct vybrid_ehci_softc *esc)
+{
+ device_t sc_gpio_dev;
+ int reg;
+
+ /* Reset phy */
+ reg = PHY_READ4(esc, USBPHY_CTRL);
+ reg |= (USBPHY_CTRL_SFTRST);
+ PHY_WRITE4(esc, USBPHY_CTRL, reg);
+
+ /* Minimum reset time */
+ DELAY(10000);
+
+ reg &= ~(USBPHY_CTRL_SFTRST | USBPHY_CTRL_CLKGATE);
+ PHY_WRITE4(esc, USBPHY_CTRL, reg);
+
+ reg = (ENUTMILEVEL2 | ENUTMILEVEL3);
+ PHY_WRITE4(esc, USBPHY_CTRL_SET, reg);
+
+ /* Get the GPIO device, we need this to give power to USB */
+ sc_gpio_dev = devclass_get_device(devclass_find("gpio"), 0);
+ if (sc_gpio_dev == NULL) {
+ device_printf(esc->dev, "Error: failed to get the GPIO dev\n");
+ return (1);
+ }
+
+ /* Give power to USB */
+ GPIO_PIN_SETFLAGS(sc_gpio_dev, GPIO_USB_PWR, GPIO_PIN_OUTPUT);
+ GPIO_PIN_SET(sc_gpio_dev, GPIO_USB_PWR, GPIO_PIN_HIGH);
+
+ /* Power up PHY */
+ PHY_WRITE4(esc, USBPHY_PWD, 0x00);
+
+ /* Ungate clocks */
+ reg = PHY_READ4(esc, USBPHY_DEBUG);
+ reg &= ~(USBPHY_DEBUG_CLKGATE);
+ PHY_WRITE4(esc, USBPHY_DEBUG, reg);
+
+#if 0
+ printf("USBPHY_CTRL == 0x%08x\n",
+ PHY_READ4(esc, USBPHY_CTRL));
+ printf("USBPHY_IP == 0x%08x\n",
+ PHY_READ4(esc, USBPHY_IP));
+ printf("USBPHY_STATUS == 0x%08x\n",
+ PHY_READ4(esc, USBPHY_STATUS));
+ printf("USBPHY_DEBUG == 0x%08x\n",
+ PHY_READ4(esc, USBPHY_DEBUG));
+ printf("USBPHY_DEBUG0_STATUS == 0x%08x\n",
+ PHY_READ4(esc, USBPHY_DEBUG0_STATUS));
+ printf("USBPHY_DEBUG1 == 0x%08x\n",
+ PHY_READ4(esc, USBPHY_DEBUG1));
+#endif
+
+ return (0);
+}
+
+static int
+vybrid_ehci_attach(device_t dev)
+{
+ struct vybrid_ehci_softc *esc;
+ ehci_softc_t *sc;
+ bus_space_handle_t bsh;
+ int err;
+ int reg;
+
+ esc = device_get_softc(dev);
+ esc->dev = dev;
+
+ sc = &esc->base;
+ sc->sc_bus.parent = dev;
+ sc->sc_bus.devices = sc->sc_devices;
+ sc->sc_bus.devices_max = EHCI_MAX_DEVICES;
+ sc->sc_bus.dma_bits = 32;
+
+ if (bus_alloc_resources(dev, vybrid_ehci_spec, esc->res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ /* EHCI registers */
+ sc->sc_io_tag = rman_get_bustag(esc->res[0]);
+ bsh = rman_get_bushandle(esc->res[0]);
+ sc->sc_io_size = rman_get_size(esc->res[0]);
+
+ esc->bst_usbc = rman_get_bustag(esc->res[1]);
+ esc->bsh_usbc = rman_get_bushandle(esc->res[1]);
+
+ esc->bst_phy = rman_get_bustag(esc->res[2]);
+ esc->bsh_phy = rman_get_bushandle(esc->res[2]);
+
+ /* get all DMA memory */
+ if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(dev),
+ &ehci_iterate_hw_softc))
+ return (ENXIO);
+
+#if 0
+ printf("USBx_HCSPARAMS is 0x%08x\n",
+ bus_space_read_4(sc->sc_io_tag, bsh, USB_HCSPARAMS));
+ printf("USB_ID == 0x%08x\n",
+ bus_space_read_4(sc->sc_io_tag, bsh, USB_ID));
+ printf("USB_HWGENERAL == 0x%08x\n",
+ bus_space_read_4(sc->sc_io_tag, bsh, USB_HWGENERAL));
+ printf("USB_HWHOST == 0x%08x\n",
+ bus_space_read_4(sc->sc_io_tag, bsh, USB_HWHOST));
+ printf("USB_HWDEVICE == 0x%08x\n",
+ bus_space_read_4(sc->sc_io_tag, bsh, USB_HWDEVICE));
+ printf("USB_HWTXBUF == 0x%08x\n",
+ bus_space_read_4(sc->sc_io_tag, bsh, USB_HWTXBUF));
+ printf("USB_HWRXBUF == 0x%08x\n",
+ bus_space_read_4(sc->sc_io_tag, bsh, USB_HWRXBUF));
+#endif
+
+ if (phy_init(esc)) {
+ device_printf(dev, "Could not setup PHY\n");
+ return (1);
+ }
+
+ /*
+ * Set handle to USB related registers subregion used by
+ * generic EHCI driver.
+ */
+ err = bus_space_subregion(sc->sc_io_tag, bsh, 0x100,
+ sc->sc_io_size, &sc->sc_io_hdl);
+ if (err != 0)
+ return (ENXIO);
+
+ /* Setup interrupt handler */
+ err = bus_setup_intr(dev, esc->res[3], INTR_TYPE_BIO | INTR_MPSAFE,
+ NULL, (driver_intr_t *)ehci_interrupt, sc,
+ &sc->sc_intr_hdl);
+ if (err) {
+ device_printf(dev, "Could not setup irq, "
+ "%d\n", err);
+ return (1);
+ }
+
+ /* Add USB device */
+ sc->sc_bus.bdev = device_add_child(dev, "usbus", -1);
+ if (!sc->sc_bus.bdev) {
+ device_printf(dev, "Could not add USB device\n");
+ err = bus_teardown_intr(dev, esc->res[5],
+ sc->sc_intr_hdl);
+ if (err)
+ device_printf(dev, "Could not tear down irq,"
+ " %d\n", err);
+ return (1);
+ }
+ device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
+
+ strlcpy(sc->sc_vendor, "Freescale", sizeof(sc->sc_vendor));
+
+ /* Set host mode */
+ reg = bus_space_read_4(sc->sc_io_tag, sc->sc_io_hdl, 0xA8);
+ reg |= 0x3;
+ bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl, 0xA8, reg);
+
+ /* Set flags and callbacks*/
+ sc->sc_flags |= EHCI_SCFLG_TT | EHCI_SCFLG_NORESTERM;
+ sc->sc_vendor_post_reset = vybrid_ehci_post_reset;
+ sc->sc_vendor_get_port_speed = ehci_get_port_speed_portsc;
+
+ err = ehci_init(sc);
+ if (!err) {
+ sc->sc_flags |= EHCI_SCFLG_DONEINIT;
+ err = device_probe_and_attach(sc->sc_bus.bdev);
+ } else {
+ device_printf(dev, "USB init failed err=%d\n", err);
+
+ device_delete_child(dev, sc->sc_bus.bdev);
+ sc->sc_bus.bdev = NULL;
+
+ err = bus_teardown_intr(dev, esc->res[5],
+ sc->sc_intr_hdl);
+ if (err)
+ device_printf(dev, "Could not tear down irq,"
+ " %d\n", err);
+ return (1);
+ }
+ return (0);
+}
+
+static int
+vybrid_ehci_detach(device_t dev)
+{
+ struct vybrid_ehci_softc *esc;
+ ehci_softc_t *sc;
+ int err;
+
+ esc = device_get_softc(dev);
+ sc = &esc->base;
+
+ /* First detach all children; we can't detach if that fails. */
+ if ((err = device_delete_children(dev)) != 0)
+ return (err);
+
+ /*
+ * only call ehci_detach() after ehci_init()
+ */
+ if (sc->sc_flags & EHCI_SCFLG_DONEINIT) {
+ ehci_detach(sc);
+ sc->sc_flags &= ~EHCI_SCFLG_DONEINIT;
+ }
+
+ /*
+ * Disable interrupts that might have been switched on in
+ * ehci_init.
+ */
+ if (sc->sc_io_tag && sc->sc_io_hdl)
+ bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl,
+ EHCI_USBINTR, 0);
+
+ if (esc->res[5] && sc->sc_intr_hdl) {
+ err = bus_teardown_intr(dev, esc->res[5],
+ sc->sc_intr_hdl);
+ if (err) {
+ device_printf(dev, "Could not tear down irq,"
+ " %d\n", err);
+ return (err);
+ }
+ sc->sc_intr_hdl = NULL;
+ }
+
+ usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc);
+
+ bus_release_resources(dev, vybrid_ehci_spec, esc->res);
+
+ return (0);
+}
diff --git a/sys/arm/freescale/vybrid/vf_gpio.c b/sys/arm/freescale/vybrid/vf_gpio.c
new file mode 100644
index 000000000000..cfa40693dfbe
--- /dev/null
+++ b/sys/arm/freescale/vybrid/vf_gpio.c
@@ -0,0 +1,386 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Vybrid Family General-Purpose Input/Output (GPIO)
+ * Chapter 7, Vybrid Reference Manual, Rev. 5, 07/2013
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+#include <sys/mutex.h>
+#include <sys/gpio.h>
+
+#include <dev/gpio/gpiobusvar.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include "gpio_if.h"
+
+#include <arm/freescale/vybrid/vf_common.h>
+#include <arm/freescale/vybrid/vf_port.h>
+
+#define GPIO_PDOR(n) (0x00 + 0x40 * (n >> 5))
+#define GPIO_PSOR(n) (0x04 + 0x40 * (n >> 5))
+#define GPIO_PCOR(n) (0x08 + 0x40 * (n >> 5))
+#define GPIO_PTOR(n) (0x0C + 0x40 * (n >> 5))
+#define GPIO_PDIR(n) (0x10 + 0x40 * (n >> 5))
+
+#define GPIO_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define GPIO_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+
+#define DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)
+
+/*
+ * GPIO interface
+ */
+static device_t vf_gpio_get_bus(device_t);
+static int vf_gpio_pin_max(device_t, int *);
+static int vf_gpio_pin_getcaps(device_t, uint32_t, uint32_t *);
+static int vf_gpio_pin_getname(device_t, uint32_t, char *);
+static int vf_gpio_pin_getflags(device_t, uint32_t, uint32_t *);
+static int vf_gpio_pin_setflags(device_t, uint32_t, uint32_t);
+static int vf_gpio_pin_set(device_t, uint32_t, unsigned int);
+static int vf_gpio_pin_get(device_t, uint32_t, unsigned int *);
+static int vf_gpio_pin_toggle(device_t, uint32_t pin);
+
+struct vf_gpio_softc {
+ struct resource *res[1];
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+
+ device_t sc_busdev;
+ struct mtx sc_mtx;
+ int gpio_npins;
+ struct gpio_pin gpio_pins[NGPIO];
+};
+
+struct vf_gpio_softc *gpio_sc;
+
+static struct resource_spec vf_gpio_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+static int
+vf_gpio_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "fsl,mvf600-gpio"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Vybrid Family GPIO Unit");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+vf_gpio_attach(device_t dev)
+{
+ struct vf_gpio_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ if (bus_alloc_resources(dev, vf_gpio_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources\n");
+ mtx_destroy(&sc->sc_mtx);
+ return (ENXIO);
+ }
+
+ /* Memory interface */
+ sc->bst = rman_get_bustag(sc->res[0]);
+ sc->bsh = rman_get_bushandle(sc->res[0]);
+
+ gpio_sc = sc;
+
+ sc->gpio_npins = NGPIO;
+
+ for (i = 0; i < sc->gpio_npins; i++) {
+ sc->gpio_pins[i].gp_pin = i;
+ sc->gpio_pins[i].gp_caps = DEFAULT_CAPS;
+ sc->gpio_pins[i].gp_flags =
+ (READ4(sc, GPIO_PDOR(i)) & (1 << (i % 32))) ?
+ GPIO_PIN_OUTPUT: GPIO_PIN_INPUT;
+ snprintf(sc->gpio_pins[i].gp_name, GPIOMAXNAME,
+ "vf_gpio%d.%d", device_get_unit(dev), i);
+ }
+
+ sc->sc_busdev = gpiobus_attach_bus(dev);
+ if (sc->sc_busdev == NULL) {
+ bus_release_resources(dev, vf_gpio_spec, sc->res);
+ mtx_destroy(&sc->sc_mtx);
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static device_t
+vf_gpio_get_bus(device_t dev)
+{
+ struct vf_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ return (sc->sc_busdev);
+}
+
+static int
+vf_gpio_pin_max(device_t dev, int *maxpin)
+{
+
+ *maxpin = NGPIO - 1;
+ return (0);
+}
+
+static int
+vf_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
+{
+ struct vf_gpio_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+ for (i = 0; i < sc->gpio_npins; i++) {
+ if (sc->gpio_pins[i].gp_pin == pin)
+ break;
+ }
+
+ if (i >= sc->gpio_npins)
+ return (EINVAL);
+
+ GPIO_LOCK(sc);
+ memcpy(name, sc->gpio_pins[i].gp_name, GPIOMAXNAME);
+ GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+vf_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
+{
+ struct vf_gpio_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+ for (i = 0; i < sc->gpio_npins; i++) {
+ if (sc->gpio_pins[i].gp_pin == pin)
+ break;
+ }
+
+ if (i >= sc->gpio_npins)
+ return (EINVAL);
+
+ GPIO_LOCK(sc);
+ *caps = sc->gpio_pins[i].gp_caps;
+ GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+vf_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
+{
+ struct vf_gpio_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+ for (i = 0; i < sc->gpio_npins; i++) {
+ if (sc->gpio_pins[i].gp_pin == pin)
+ break;
+ }
+
+ if (i >= sc->gpio_npins)
+ return (EINVAL);
+
+ GPIO_LOCK(sc);
+ *flags = sc->gpio_pins[i].gp_flags;
+ GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+vf_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
+{
+ struct vf_gpio_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+ for (i = 0; i < sc->gpio_npins; i++) {
+ if (sc->gpio_pins[i].gp_pin == pin)
+ break;
+ }
+
+ if (i >= sc->gpio_npins)
+ return (EINVAL);
+
+ GPIO_LOCK(sc);
+ *val = (READ4(sc, GPIO_PDIR(i)) & (1 << (i % 32))) ? 1 : 0;
+ GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+vf_gpio_pin_toggle(device_t dev, uint32_t pin)
+{
+ struct vf_gpio_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+ for (i = 0; i < sc->gpio_npins; i++) {
+ if (sc->gpio_pins[i].gp_pin == pin)
+ break;
+ }
+
+ if (i >= sc->gpio_npins)
+ return (EINVAL);
+
+ GPIO_LOCK(sc);
+ WRITE4(sc, GPIO_PTOR(i), (1 << (i % 32)));
+ GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static void
+vf_gpio_pin_configure(struct vf_gpio_softc *sc, struct gpio_pin *pin,
+ unsigned int flags)
+{
+
+ GPIO_LOCK(sc);
+
+ /*
+ * Manage input/output
+ */
+ if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) {
+ pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT);
+ if (flags & GPIO_PIN_OUTPUT) {
+ pin->gp_flags |= GPIO_PIN_OUTPUT;
+
+ } else {
+ pin->gp_flags |= GPIO_PIN_INPUT;
+ WRITE4(sc, GPIO_PCOR(pin->gp_pin),
+ (1 << (pin->gp_pin % 32)));
+ }
+ }
+
+ GPIO_UNLOCK(sc);
+}
+
+static int
+vf_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
+{
+ struct vf_gpio_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+ for (i = 0; i < sc->gpio_npins; i++) {
+ if (sc->gpio_pins[i].gp_pin == pin)
+ break;
+ }
+
+ if (i >= sc->gpio_npins)
+ return (EINVAL);
+
+ vf_gpio_pin_configure(sc, &sc->gpio_pins[i], flags);
+
+ return (0);
+}
+
+static int
+vf_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
+{
+ struct vf_gpio_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+ for (i = 0; i < sc->gpio_npins; i++) {
+ if (sc->gpio_pins[i].gp_pin == pin)
+ break;
+ }
+
+ if (i >= sc->gpio_npins)
+ return (EINVAL);
+
+ GPIO_LOCK(sc);
+ if (value)
+ WRITE4(sc, GPIO_PSOR(i), (1 << (i % 32)));
+ else
+ WRITE4(sc, GPIO_PCOR(i), (1 << (i % 32)));
+ GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static device_method_t vf_gpio_methods[] = {
+ DEVMETHOD(device_probe, vf_gpio_probe),
+ DEVMETHOD(device_attach, vf_gpio_attach),
+
+ /* GPIO protocol */
+ DEVMETHOD(gpio_get_bus, vf_gpio_get_bus),
+ DEVMETHOD(gpio_pin_max, vf_gpio_pin_max),
+ DEVMETHOD(gpio_pin_getname, vf_gpio_pin_getname),
+ DEVMETHOD(gpio_pin_getcaps, vf_gpio_pin_getcaps),
+ DEVMETHOD(gpio_pin_getflags, vf_gpio_pin_getflags),
+ DEVMETHOD(gpio_pin_get, vf_gpio_pin_get),
+ DEVMETHOD(gpio_pin_toggle, vf_gpio_pin_toggle),
+ DEVMETHOD(gpio_pin_setflags, vf_gpio_pin_setflags),
+ DEVMETHOD(gpio_pin_set, vf_gpio_pin_set),
+ { 0, 0 }
+};
+
+static driver_t vf_gpio_driver = {
+ "gpio",
+ vf_gpio_methods,
+ sizeof(struct vf_gpio_softc),
+};
+
+static devclass_t vf_gpio_devclass;
+
+DRIVER_MODULE(vf_gpio, simplebus, vf_gpio_driver, vf_gpio_devclass, 0, 0);
diff --git a/sys/arm/freescale/vybrid/vf_i2c.c b/sys/arm/freescale/vybrid/vf_i2c.c
new file mode 100644
index 000000000000..a2636dee0b03
--- /dev/null
+++ b/sys/arm/freescale/vybrid/vf_i2c.c
@@ -0,0 +1,633 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Vybrid Family Inter-Integrated Circuit (I2C)
+ * Chapter 48, Vybrid Reference Manual, Rev. 5, 07/2013
+ */
+
+/*
+ * This driver is based on the I2C driver for i.MX
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+
+#include <dev/iicbus/iiconf.h>
+#include <dev/iicbus/iicbus.h>
+
+#include "iicbus_if.h"
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#ifdef EXT_RESOURCES
+#include <dev/extres/clk/clk.h>
+#endif
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <arm/freescale/vybrid/vf_common.h>
+
+#define I2C_IBAD 0x0 /* I2C Bus Address Register */
+#define I2C_IBFD 0x1 /* I2C Bus Frequency Divider Register */
+#define I2C_IBCR 0x2 /* I2C Bus Control Register */
+#define IBCR_MDIS (1 << 7) /* Module disable. */
+#define IBCR_IBIE (1 << 6) /* I-Bus Interrupt Enable. */
+#define IBCR_MSSL (1 << 5) /* Master/Slave mode select. */
+#define IBCR_TXRX (1 << 4) /* Transmit/Receive mode select. */
+#define IBCR_NOACK (1 << 3) /* Data Acknowledge disable. */
+#define IBCR_RSTA (1 << 2) /* Repeat Start. */
+#define IBCR_DMAEN (1 << 1) /* DMA Enable. */
+#define I2C_IBSR 0x3 /* I2C Bus Status Register */
+#define IBSR_TCF (1 << 7) /* Transfer complete. */
+#define IBSR_IAAS (1 << 6) /* Addressed as a slave. */
+#define IBSR_IBB (1 << 5) /* Bus busy. */
+#define IBSR_IBAL (1 << 4) /* Arbitration Lost. */
+#define IBSR_SRW (1 << 2) /* Slave Read/Write. */
+#define IBSR_IBIF (1 << 1) /* I-Bus Interrupt Flag. */
+#define IBSR_RXAK (1 << 0) /* Received Acknowledge. */
+#define I2C_IBDR 0x4 /* I2C Bus Data I/O Register */
+#define I2C_IBIC 0x5 /* I2C Bus Interrupt Config Register */
+#define IBIC_BIIE (1 << 7) /* Bus Idle Interrupt Enable bit. */
+#define I2C_IBDBG 0x6 /* I2C Bus Debug Register */
+
+#ifdef DEBUG
+#define vf_i2c_dbg(_sc, fmt, args...) \
+ device_printf((_sc)->dev, fmt, ##args)
+#else
+#define vf_i2c_dbg(_sc, fmt, args...)
+#endif
+
+#define HW_UNKNOWN 0x00
+#define HW_MVF600 0x01
+#define HW_VF610 0x02
+
+static int i2c_repeated_start(device_t, u_char, int);
+static int i2c_start(device_t, u_char, int);
+static int i2c_stop(device_t);
+static int i2c_reset(device_t, u_char, u_char, u_char *);
+static int i2c_read(device_t, char *, int, int *, int, int);
+static int i2c_write(device_t, const char *, int, int *, int);
+static phandle_t i2c_get_node(device_t, device_t);
+
+struct i2c_div_type {
+ uint32_t reg_val;
+ uint32_t div;
+};
+
+struct i2c_softc {
+ struct resource *res[2];
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+#ifdef EXT_RESOURCES
+ clk_t clock;
+ uint32_t freq;
+#endif
+ device_t dev;
+ device_t iicbus;
+ struct mtx mutex;
+ uintptr_t hwtype;
+};
+
+static struct resource_spec i2c_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+#ifdef EXT_RESOURCES
+static struct i2c_div_type vf610_div_table[] = {
+ { 0x00, 20 }, { 0x01, 22 }, { 0x02, 24 }, { 0x03, 26 },
+ { 0x04, 28 }, { 0x05, 30 }, { 0x09, 32 }, { 0x06, 34 },
+ { 0x0A, 36 }, { 0x0B, 40 }, { 0x0C, 44 }, { 0x0D, 48 },
+ { 0x0E, 56 }, { 0x12, 64 }, { 0x13, 72 }, { 0x14, 80 },
+ { 0x15, 88 }, { 0x19, 96 }, { 0x16, 104 }, { 0x1A, 112 },
+ { 0x17, 128 }, { 0x1D, 160 }, { 0x1E, 192 }, { 0x22, 224 },
+ { 0x1F, 240 }, { 0x23, 256 }, { 0x24, 288 }, { 0x25, 320 },
+ { 0x26, 384 }, { 0x2A, 448 }, { 0x27, 480 }, { 0x2B, 512 },
+ { 0x2C, 576 }, { 0x2D, 640 }, { 0x2E, 768 }, { 0x32, 896 },
+ { 0x2F, 960 }, { 0x33, 1024 }, { 0x34, 1152 }, { 0x35, 1280 },
+ { 0x36, 1536 }, { 0x3A, 1792 }, { 0x37, 1920 }, { 0x3B, 2048 },
+ { 0x3C, 2304 }, { 0x3D, 2560 }, { 0x3E, 3072 }, { 0x3F, 3840 },
+ { 0x3F, 3840 }, { 0x7B, 4096 }, { 0x7D, 5120 }, { 0x7E, 6144 },
+};
+#endif
+
+static const struct ofw_compat_data i2c_compat_data[] = {
+ {"fsl,mvf600-i2c", HW_MVF600},
+ {"fsl,vf610-i2c", HW_VF610},
+ {NULL, HW_UNKNOWN}
+};
+
+static int
+i2c_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_search_compatible(dev, i2c_compat_data)->ocd_data)
+ return (ENXIO);
+
+ device_set_desc(dev, "Vybrid Family Inter-Integrated Circuit (I2C)");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+i2c_attach(device_t dev)
+{
+ struct i2c_softc *sc;
+#ifdef EXT_RESOURCES
+ phandle_t node;
+#endif
+ int error;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ sc->hwtype = ofw_bus_search_compatible(dev, i2c_compat_data)->ocd_data;
+#ifdef EXT_RESOURCES
+ node = ofw_bus_get_node(dev);
+
+ error = clk_get_by_ofw_index(dev, node, 0, &sc->clock);
+ if (error != 0) {
+ sc->freq = 0;
+ device_printf(dev, "Parent clock not found.\n");
+ } else {
+ if (OF_hasprop(node, "clock-frequency"))
+ OF_getencprop(node, "clock-frequency", &sc->freq,
+ sizeof(sc->freq));
+ else
+ sc->freq = 100000;
+ }
+#endif
+
+ mtx_init(&sc->mutex, device_get_nameunit(dev), "I2C", MTX_DEF);
+
+ error = bus_alloc_resources(dev, i2c_spec, sc->res);
+ if (error != 0) {
+ mtx_destroy(&sc->mutex);
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ /* Memory interface */
+ sc->bst = rman_get_bustag(sc->res[0]);
+ sc->bsh = rman_get_bushandle(sc->res[0]);
+
+ WRITE1(sc, I2C_IBIC, IBIC_BIIE);
+
+ sc->iicbus = device_add_child(dev, "iicbus", -1);
+ if (sc->iicbus == NULL) {
+ device_printf(dev, "could not add iicbus child");
+ mtx_destroy(&sc->mutex);
+ bus_release_resources(dev, i2c_spec, sc->res);
+ return (ENXIO);
+ }
+
+ bus_generic_attach(dev);
+
+ return (0);
+}
+
+static int
+i2c_detach(device_t dev)
+{
+ struct i2c_softc *sc;
+ int error = 0;
+
+ sc = device_get_softc(dev);
+
+ error = bus_generic_detach(dev);
+ if (error != 0) {
+ device_printf(dev, "cannot detach child devices.\n");
+ return (error);
+ }
+
+ error = device_delete_child(dev, sc->iicbus);
+ if (error != 0) {
+ device_printf(dev, "could not delete iicbus child.\n");
+ return (error);
+ }
+
+ bus_release_resources(dev, i2c_spec, sc->res);
+
+ mtx_destroy(&sc->mutex);
+
+ return (0);
+}
+
+/* Wait for transfer interrupt flag */
+static int
+wait_for_iif(struct i2c_softc *sc)
+{
+ int retry;
+
+ retry = 1000;
+ while (retry --) {
+ if (READ1(sc, I2C_IBSR) & IBSR_IBIF) {
+ WRITE1(sc, I2C_IBSR, IBSR_IBIF);
+ return (IIC_NOERR);
+ }
+ DELAY(10);
+ }
+
+ return (IIC_ETIMEOUT);
+}
+
+/* Wait for free bus */
+static int
+wait_for_nibb(struct i2c_softc *sc)
+{
+ int retry;
+
+ retry = 1000;
+ while (retry --) {
+ if ((READ1(sc, I2C_IBSR) & IBSR_IBB) == 0)
+ return (IIC_NOERR);
+ DELAY(10);
+ }
+
+ return (IIC_ETIMEOUT);
+}
+
+/* Wait for transfer complete+interrupt flag */
+static int
+wait_for_icf(struct i2c_softc *sc)
+{
+ int retry;
+
+ retry = 1000;
+ while (retry --) {
+ if (READ1(sc, I2C_IBSR) & IBSR_TCF) {
+ if (READ1(sc, I2C_IBSR) & IBSR_IBIF) {
+ WRITE1(sc, I2C_IBSR, IBSR_IBIF);
+ return (IIC_NOERR);
+ }
+ }
+ DELAY(10);
+ }
+
+ return (IIC_ETIMEOUT);
+}
+/* Get ACK bit from last write */
+static bool
+tx_acked(struct i2c_softc *sc)
+{
+
+ return (READ1(sc, I2C_IBSR) & IBSR_RXAK) ? false : true;
+
+}
+
+static int
+i2c_repeated_start(device_t dev, u_char slave, int timeout)
+{
+ struct i2c_softc *sc;
+ int error;
+ int reg;
+
+ sc = device_get_softc(dev);
+
+ vf_i2c_dbg(sc, "i2c repeated start\n");
+
+ mtx_lock(&sc->mutex);
+
+ WRITE1(sc, I2C_IBAD, slave);
+
+ if ((READ1(sc, I2C_IBSR) & IBSR_IBB) == 0) {
+ mtx_unlock(&sc->mutex);
+ return (IIC_EBUSERR);
+ }
+
+ /* Set repeated start condition */
+ DELAY(10);
+
+ reg = READ1(sc, I2C_IBCR);
+ reg |= (IBCR_RSTA | IBCR_IBIE);
+ WRITE1(sc, I2C_IBCR, reg);
+
+ DELAY(10);
+
+ /* Write target address - LSB is R/W bit */
+ WRITE1(sc, I2C_IBDR, slave);
+
+ error = wait_for_iif(sc);
+
+ if (!tx_acked(sc)) {
+ vf_i2c_dbg(sc,
+ "cant i2c start: missing ACK after slave addres\n");
+ return (IIC_ENOACK);
+ }
+
+ mtx_unlock(&sc->mutex);
+
+ if (error != 0)
+ return (error);
+
+ return (IIC_NOERR);
+}
+
+static int
+i2c_start(device_t dev, u_char slave, int timeout)
+{
+ struct i2c_softc *sc;
+ int error;
+ int reg;
+
+ sc = device_get_softc(dev);
+
+ vf_i2c_dbg(sc, "i2c start\n");
+
+ mtx_lock(&sc->mutex);
+
+ WRITE1(sc, I2C_IBAD, slave);
+
+ if (READ1(sc, I2C_IBSR) & IBSR_IBB) {
+ mtx_unlock(&sc->mutex);
+ vf_i2c_dbg(sc, "cant i2c start: IIC_EBUSBSY\n");
+ return (IIC_EBUSERR);
+ }
+
+ /* Set start condition */
+ reg = (IBCR_MSSL | IBCR_NOACK | IBCR_IBIE);
+ WRITE1(sc, I2C_IBCR, reg);
+
+ DELAY(100);
+
+ reg |= (IBCR_TXRX);
+ WRITE1(sc, I2C_IBCR, reg);
+
+ /* Write target address - LSB is R/W bit */
+ WRITE1(sc, I2C_IBDR, slave);
+
+ error = wait_for_iif(sc);
+ if (error != 0) {
+ mtx_unlock(&sc->mutex);
+ vf_i2c_dbg(sc, "cant i2c start: iif error\n");
+ return (error);
+ }
+ mtx_unlock(&sc->mutex);
+
+ if (!tx_acked(sc)) {
+ vf_i2c_dbg(sc,
+ "cant i2c start: missing QACK after slave addres\n");
+ return (IIC_ENOACK);
+ }
+
+ return (IIC_NOERR);
+}
+
+static int
+i2c_stop(device_t dev)
+{
+ struct i2c_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ vf_i2c_dbg(sc, "i2c stop\n");
+
+ mtx_lock(&sc->mutex);
+
+ WRITE1(sc, I2C_IBCR, IBCR_NOACK | IBCR_IBIE);
+
+ DELAY(100);
+
+ /* Reset controller if bus still busy after STOP */
+ if (wait_for_nibb(sc) == IIC_ETIMEOUT) {
+ WRITE1(sc, I2C_IBCR, IBCR_MDIS);
+ DELAY(1000);
+ WRITE1(sc, I2C_IBCR, IBCR_NOACK);
+ }
+ mtx_unlock(&sc->mutex);
+
+ return (IIC_NOERR);
+}
+
+static uint32_t
+i2c_get_div_val(device_t dev)
+{
+ struct i2c_softc *sc;
+#ifdef EXT_RESOURCES
+ uint64_t clk_freq;
+ int error, i;
+
+ sc = device_get_softc(dev);
+
+ if (sc->hwtype == HW_MVF600)
+ return 20;
+
+ if (sc->freq == 0)
+ return vf610_div_table[nitems(vf610_div_table) - 1].reg_val;
+
+ error = clk_get_freq(sc->clock, &clk_freq);
+ if (error != 0) {
+ device_printf(dev, "Could not get parent clock frequency. "
+ "Using default divider.\n");
+ return vf610_div_table[nitems(vf610_div_table) - 1].reg_val;
+ }
+
+ for (i = 0; i < nitems(vf610_div_table) - 1; i++)
+ if ((clk_freq / vf610_div_table[i].div) <= sc->freq)
+ break;
+
+ return vf610_div_table[i].reg_val;
+#else
+ sc = device_get_softc(dev);
+
+ if (sc->hwtype == HW_VF610)
+ return 0x3F;
+ else
+ return 20;
+#endif
+}
+
+static int
+i2c_reset(device_t dev, u_char speed, u_char addr, u_char *oldadr)
+{
+ struct i2c_softc *sc;
+ uint32_t div;
+
+ sc = device_get_softc(dev);
+ div = i2c_get_div_val(dev);
+ vf_i2c_dbg(sc, "Div val: %02x\n", div);
+
+ vf_i2c_dbg(sc, "i2c reset\n");
+
+ switch (speed) {
+ case IIC_FAST:
+ case IIC_SLOW:
+ case IIC_UNKNOWN:
+ case IIC_FASTEST:
+ default:
+ break;
+ }
+
+ mtx_lock(&sc->mutex);
+ WRITE1(sc, I2C_IBCR, IBCR_MDIS);
+
+ DELAY(1000);
+
+ WRITE1(sc, I2C_IBFD, div);
+ WRITE1(sc, I2C_IBCR, 0x0); /* Enable i2c */
+
+ DELAY(1000);
+
+ mtx_unlock(&sc->mutex);
+
+ return (IIC_NOERR);
+}
+
+static int
+i2c_read(device_t dev, char *buf, int len, int *read, int last, int delay)
+{
+ struct i2c_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ vf_i2c_dbg(sc, "i2c read\n");
+
+ *read = 0;
+
+ mtx_lock(&sc->mutex);
+
+ if (len) {
+ if (len == 1)
+ WRITE1(sc, I2C_IBCR, IBCR_IBIE | IBCR_MSSL | \
+ IBCR_NOACK);
+ else
+ WRITE1(sc, I2C_IBCR, IBCR_IBIE | IBCR_MSSL);
+
+ /* dummy read */
+ READ1(sc, I2C_IBDR);
+ DELAY(1000);
+ }
+
+ while (*read < len) {
+ error = wait_for_icf(sc);
+ if (error != 0) {
+ mtx_unlock(&sc->mutex);
+ return (error);
+ }
+
+ if ((*read == len - 2) && last) {
+ /* NO ACK on last byte */
+ WRITE1(sc, I2C_IBCR, IBCR_IBIE | IBCR_MSSL | \
+ IBCR_NOACK);
+ }
+
+ if ((*read == len - 1) && last) {
+ /* Transfer done, remove master bit */
+ WRITE1(sc, I2C_IBCR, IBCR_IBIE | IBCR_NOACK);
+ }
+
+ *buf++ = READ1(sc, I2C_IBDR);
+ (*read)++;
+ }
+ mtx_unlock(&sc->mutex);
+
+ return (IIC_NOERR);
+}
+
+static int
+i2c_write(device_t dev, const char *buf, int len, int *sent, int timeout)
+{
+ struct i2c_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ vf_i2c_dbg(sc, "i2c write\n");
+
+ *sent = 0;
+
+ mtx_lock(&sc->mutex);
+ while (*sent < len) {
+ WRITE1(sc, I2C_IBDR, *buf++);
+
+ error = wait_for_iif(sc);
+ if (error != 0) {
+ mtx_unlock(&sc->mutex);
+ return (error);
+ }
+
+ if (!tx_acked(sc) && (*sent = (len - 2)) ){
+ mtx_unlock(&sc->mutex);
+ vf_i2c_dbg(sc, "no ACK on %d write\n", *sent);
+ return (IIC_ENOACK);
+ }
+
+ (*sent)++;
+ }
+ mtx_unlock(&sc->mutex);
+ return (IIC_NOERR);
+}
+
+static phandle_t
+i2c_get_node(device_t bus, device_t dev)
+{
+
+ return ofw_bus_get_node(bus);
+}
+
+static device_method_t i2c_methods[] = {
+ DEVMETHOD(device_probe, i2c_probe),
+ DEVMETHOD(device_attach, i2c_attach),
+ DEVMETHOD(device_detach, i2c_detach),
+
+ DEVMETHOD(ofw_bus_get_node, i2c_get_node),
+
+ DEVMETHOD(iicbus_callback, iicbus_null_callback),
+ DEVMETHOD(iicbus_repeated_start, i2c_repeated_start),
+ DEVMETHOD(iicbus_start, i2c_start),
+ DEVMETHOD(iicbus_stop, i2c_stop),
+ DEVMETHOD(iicbus_reset, i2c_reset),
+ DEVMETHOD(iicbus_read, i2c_read),
+ DEVMETHOD(iicbus_write, i2c_write),
+ DEVMETHOD(iicbus_transfer, iicbus_transfer_gen),
+ { 0, 0 }
+};
+
+static devclass_t i2c_devclass;
+static DEFINE_CLASS_0(i2c, i2c_driver, i2c_methods, sizeof(struct i2c_softc));
+DRIVER_MODULE(vybrid_i2c, simplebus, i2c_driver, i2c_devclass, 0, 0);
+DRIVER_MODULE(iicbus, i2c, iicbus_driver, iicbus_devclass, 0, 0);
+DRIVER_MODULE(ofw_iicbus, i2c, ofw_iicbus_driver, ofw_iicbus_devclass, 0, 0);
diff --git a/sys/arm/freescale/vybrid/vf_iomuxc.c b/sys/arm/freescale/vybrid/vf_iomuxc.c
new file mode 100644
index 000000000000..e46dd31c2efb
--- /dev/null
+++ b/sys/arm/freescale/vybrid/vf_iomuxc.c
@@ -0,0 +1,212 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013-2014 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Vybrid Family Input/Output Multiplexer Controller (IOMUXC)
+ * Chapter 5, Vybrid Reference Manual, Rev. 5, 07/2013
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <arm/freescale/vybrid/vf_iomuxc.h>
+#include <arm/freescale/vybrid/vf_common.h>
+
+#define MUX_MODE_MASK 7
+#define MUX_MODE_SHIFT 20
+#define MUX_MODE_GPIO 0
+#define MUX_MODE_VBUS_EN_OTG 2
+
+#define IBE (1 << 0) /* Input Buffer Enable Field */
+#define OBE (1 << 1) /* Output Buffer Enable Field. */
+#define PUE (1 << 2) /* Pull / Keep Select Field. */
+#define PKE (1 << 3) /* Pull / Keep Enable Field. */
+#define HYS (1 << 9) /* Hysteresis Enable Field */
+#define ODE (1 << 10) /* Open Drain Enable Field. */
+#define SRE (1 << 11) /* Slew Rate Field. */
+
+#define SPEED_SHIFT 12
+#define SPEED_MASK 0x3
+#define SPEED_LOW 0 /* 50 MHz */
+#define SPEED_MEDIUM 0x1 /* 100 MHz */
+#define SPEED_HIGH 0x3 /* 200 MHz */
+
+#define PUS_SHIFT 4 /* Pull Up / Down Config Field Shift */
+#define PUS_MASK 0x3
+#define PUS_100_KOHM_PULL_DOWN 0
+#define PUS_47_KOHM_PULL_UP 0x1
+#define PUS_100_KOHM_PULL_UP 0x2
+#define PUS_22_KOHM_PULL_UP 0x3
+
+#define DSE_SHIFT 6 /* Drive Strength Field Shift */
+#define DSE_MASK 0x7
+#define DSE_DISABLED 0 /* Output driver disabled */
+#define DSE_150_OHM 0x1
+#define DSE_75_OHM 0x2
+#define DSE_50_OHM 0x3
+#define DSE_37_OHM 0x4
+#define DSE_30_OHM 0x5
+#define DSE_25_OHM 0x6
+#define DSE_20_OHM 0x7
+
+#define MAX_MUX_LEN 1024
+
+struct iomuxc_softc {
+ struct resource *tmr_res[1];
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+ device_t dev;
+};
+
+static struct resource_spec iomuxc_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+static int
+iomuxc_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "fsl,mvf600-iomuxc"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Vybrid Family IOMUXC Unit");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+pinmux_set(struct iomuxc_softc *sc)
+{
+ phandle_t child, parent, root;
+ pcell_t iomux_config[MAX_MUX_LEN];
+ int len;
+ int values;
+ int pin;
+ int pin_cfg;
+ int i;
+
+ root = OF_finddevice("/");
+ len = 0;
+ parent = root;
+
+ /* Find 'iomux_config' prop in the nodes */
+ for (child = OF_child(parent); child != 0; child = OF_peer(child)) {
+ /* Find a 'leaf'. Start the search from this node. */
+ while (OF_child(child)) {
+ parent = child;
+ child = OF_child(child);
+ }
+
+ if (!ofw_bus_node_status_okay(child))
+ continue;
+
+ if ((len = OF_getproplen(child, "iomux_config")) > 0) {
+ OF_getencprop(child, "iomux_config", iomux_config, len);
+
+ values = len / (sizeof(uint32_t));
+ for (i = 0; i < values; i += 2) {
+ pin = iomux_config[i];
+ pin_cfg = iomux_config[i+1];
+#if 0
+ device_printf(sc->dev, "Set pin %d to 0x%08x\n",
+ pin, pin_cfg);
+#endif
+ WRITE4(sc, IOMUXC(pin), pin_cfg);
+ }
+ }
+
+ if (OF_peer(child) == 0) {
+ /* No more siblings. */
+ child = parent;
+ parent = OF_parent(child);
+ }
+ }
+
+ return (0);
+}
+
+static int
+iomuxc_attach(device_t dev)
+{
+ struct iomuxc_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ if (bus_alloc_resources(dev, iomuxc_spec, sc->tmr_res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ /* Memory interface */
+ sc->bst = rman_get_bustag(sc->tmr_res[0]);
+ sc->bsh = rman_get_bushandle(sc->tmr_res[0]);
+
+ pinmux_set(sc);
+
+ return (0);
+}
+
+static device_method_t iomuxc_methods[] = {
+ DEVMETHOD(device_probe, iomuxc_probe),
+ DEVMETHOD(device_attach, iomuxc_attach),
+ { 0, 0 }
+};
+
+static driver_t iomuxc_driver = {
+ "iomuxc",
+ iomuxc_methods,
+ sizeof(struct iomuxc_softc),
+};
+
+static devclass_t iomuxc_devclass;
+
+DRIVER_MODULE(iomuxc, simplebus, iomuxc_driver, iomuxc_devclass, 0, 0);
diff --git a/sys/arm/freescale/vybrid/vf_iomuxc.h b/sys/arm/freescale/vybrid/vf_iomuxc.h
new file mode 100644
index 000000000000..3dc1bcdcbe2f
--- /dev/null
+++ b/sys/arm/freescale/vybrid/vf_iomuxc.h
@@ -0,0 +1,167 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define IOMUXC(n) (n * 0x04)
+#define IOMUXCN 135
+#define IOMUXC_PTA6 0x000 /* Software MUX Pad Control Register 0 */
+#define IOMUXC_PTA8 0x004 /* Software MUX Pad Control Register 1 */
+#define IOMUXC_PTA9 0x008 /* Software MUX Pad Control Register 2 */
+#define IOMUXC_PTA10 0x00C /* Software MUX Pad Control Register 3 */
+#define IOMUXC_PTA11 0x010 /* Software MUX Pad Control Register 4 */
+#define IOMUXC_PTA12 0x014 /* Software MUX Pad Control Register 5 */
+#define IOMUXC_PTA16 0x018 /* Software MUX Pad Control Register 6 */
+#define IOMUXC_PTA17 0x01C /* Software MUX Pad Control Register 7 */
+#define IOMUXC_PTA18 0x020 /* Software MUX Pad Control Register 8 */
+#define IOMUXC_PTA19 0x024 /* Software MUX Pad Control Register 9 */
+#define IOMUXC_PTA20 0x028 /* Software MUX Pad Control Register 10 */
+#define IOMUXC_PTA21 0x02C /* Software MUX Pad Control Register 11 */
+#define IOMUXC_PTA22 0x030 /* Software MUX Pad Control Register 12 */
+#define IOMUXC_PTA23 0x034 /* Software MUX Pad Control Register 13 */
+#define IOMUXC_PTA24 0x038 /* Software MUX Pad Control Register 14 */
+#define IOMUXC_PTA25 0x03C /* Software MUX Pad Control Register 15 */
+#define IOMUXC_PTA26 0x040 /* Software MUX Pad Control Register 16 */
+#define IOMUXC_PTA27 0x044 /* Software MUX Pad Control Register 17 */
+#define IOMUXC_PTA28 0x048 /* Software MUX Pad Control Register 18 */
+#define IOMUXC_PTA29 0x04C /* Software MUX Pad Control Register 19 */
+#define IOMUXC_PTA30 0x050 /* Software MUX Pad Control Register 20 */
+#define IOMUXC_PTA31 0x054 /* Software MUX Pad Control Register 21 */
+#define IOMUXC_PTB0 0x058 /* Software MUX Pad Control Register 22 */
+#define IOMUXC_PTB1 0x05C /* Software MUX Pad Control Register 23 */
+#define IOMUXC_PTB2 0x060 /* Software MUX Pad Control Register 24 */
+#define IOMUXC_PTB3 0x064 /* Software MUX Pad Control Register 25 */
+#define IOMUXC_PTB4 0x068 /* Software MUX Pad Control Register 26 */
+#define IOMUXC_PTB5 0x06C /* Software MUX Pad Control Register 27 */
+#define IOMUXC_PTB6 0x070 /* Software MUX Pad Control Register 28 */
+#define IOMUXC_PTB7 0x074 /* Software MUX Pad Control Register 29 */
+#define IOMUXC_PTB8 0x078 /* Software MUX Pad Control Register 30 */
+#define IOMUXC_PTB9 0x07C /* Software MUX Pad Control Register 31 */
+#define IOMUXC_PTB10 0x080 /* Software MUX Pad Control Register 32 */
+#define IOMUXC_PTB11 0x084 /* Software MUX Pad Control Register 33 */
+#define IOMUXC_PTB12 0x088 /* Software MUX Pad Control Register 34 */
+#define IOMUXC_PTB13 0x08C /* Software MUX Pad Control Register 35 */
+#define IOMUXC_PTB14 0x090 /* Software MUX Pad Control Register 36 */
+#define IOMUXC_PTB15 0x094 /* Software MUX Pad Control Register 37 */
+#define IOMUXC_PTB16 0x098 /* Software MUX Pad Control Register 38 */
+#define IOMUXC_PTB17 0x09C /* Software MUX Pad Control Register 39 */
+#define IOMUXC_PTB18 0x0A0 /* Software MUX Pad Control Register 40 */
+#define IOMUXC_PTB19 0x0A4 /* Software MUX Pad Control Register 41 */
+#define IOMUXC_PTB20 0x0A8 /* Software MUX Pad Control Register 42 */
+#define IOMUXC_PTB21 0x0AC /* Software MUX Pad Control Register 43 */
+#define IOMUXC_PTB22 0x0B0 /* Software MUX Pad Control Register 44 */
+#define IOMUXC_PTC0 0x0B4 /* Software MUX Pad Control Register 45 */
+#define IOMUXC_PTC1 0x0B8 /* Software MUX Pad Control Register 46 */
+#define IOMUXC_PTC2 0x0BC /* Software MUX Pad Control Register 47 */
+#define IOMUXC_PTC3 0x0C0 /* Software MUX Pad Control Register 48 */
+#define IOMUXC_PTC4 0x0C4 /* Software MUX Pad Control Register 49 */
+#define IOMUXC_PTC5 0x0C8 /* Software MUX Pad Control Register 50 */
+#define IOMUXC_PTC6 0x0CC /* Software MUX Pad Control Register 51 */
+#define IOMUXC_PTC7 0x0D0 /* Software MUX Pad Control Register 52 */
+#define IOMUXC_PTC8 0x0D4 /* Software MUX Pad Control Register 53 */
+#define IOMUXC_PTC9 0x0D8 /* Software MUX Pad Control Register 54 */
+#define IOMUXC_PTC10 0x0DC /* Software MUX Pad Control Register 55 */
+#define IOMUXC_PTC11 0x0E0 /* Software MUX Pad Control Register 56 */
+#define IOMUXC_PTC12 0x0E4 /* Software MUX Pad Control Register 57 */
+#define IOMUXC_PTC13 0x0E8 /* Software MUX Pad Control Register 58 */
+#define IOMUXC_PTC14 0x0EC /* Software MUX Pad Control Register 59 */
+#define IOMUXC_PTC15 0x0F0 /* Software MUX Pad Control Register 60 */
+#define IOMUXC_PTC16 0x0F4 /* Software MUX Pad Control Register 61 */
+#define IOMUXC_PTC17 0x0F8 /* Software MUX Pad Control Register 62 */
+#define IOMUXC_PTD31 0x0FC /* Software MUX Pad Control Register 63 */
+#define IOMUXC_PTD30 0x100 /* Software MUX Pad Control Register 64 */
+#define IOMUXC_PTD29 0x104 /* Software MUX Pad Control Register 65 */
+#define IOMUXC_PTD28 0x108 /* Software MUX Pad Control Register 66 */
+#define IOMUXC_PTD27 0x10C /* Software MUX Pad Control Register 67 */
+#define IOMUXC_PTD26 0x110 /* Software MUX Pad Control Register 68 */
+#define IOMUXC_PTD25 0x114 /* Software MUX Pad Control Register 69 */
+#define IOMUXC_PTD24 0x118 /* Software MUX Pad Control Register 70 */
+#define IOMUXC_PTD23 0x11C /* Software MUX Pad Control Register 71 */
+#define IOMUXC_PTD22 0x120 /* Software MUX Pad Control Register 72 */
+#define IOMUXC_PTD21 0x124 /* Software MUX Pad Control Register 73 */
+#define IOMUXC_PTD20 0x128 /* Software MUX Pad Control Register 74 */
+#define IOMUXC_PTD19 0x12C /* Software MUX Pad Control Register 75 */
+#define IOMUXC_PTD18 0x130 /* Software MUX Pad Control Register 76 */
+#define IOMUXC_PTD17 0x134 /* Software MUX Pad Control Register 77 */
+#define IOMUXC_PTD16 0x138 /* Software MUX Pad Control Register 78 */
+#define IOMUXC_PTD0 0x13C /* Software MUX Pad Control Register 79 */
+#define IOMUXC_PTD1 0x140 /* Software MUX Pad Control Register 80 */
+#define IOMUXC_PTD2 0x144 /* Software MUX Pad Control Register 81 */
+#define IOMUXC_PTD3 0x148 /* Software MUX Pad Control Register 82 */
+#define IOMUXC_PTD4 0x14C /* Software MUX Pad Control Register 83 */
+#define IOMUXC_PTD5 0x150 /* Software MUX Pad Control Register 84 */
+#define IOMUXC_PTD6 0x154 /* Software MUX Pad Control Register 85 */
+#define IOMUXC_PTD7 0x158 /* Software MUX Pad Control Register 86 */
+#define IOMUXC_PTD8 0x15C /* Software MUX Pad Control Register 87 */
+#define IOMUXC_PTD9 0x160 /* Software MUX Pad Control Register 88 */
+#define IOMUXC_PTD10 0x164 /* Software MUX Pad Control Register 89 */
+#define IOMUXC_PTD11 0x168 /* Software MUX Pad Control Register 90 */
+#define IOMUXC_PTD12 0x16C /* Software MUX Pad Control Register 91 */
+#define IOMUXC_PTD13 0x170 /* Software MUX Pad Control Register 92 */
+#define IOMUXC_PTB23 0x174 /* Software MUX Pad Control Register 93 */
+#define IOMUXC_PTB24 0x178 /* Software MUX Pad Control Register 94 */
+#define IOMUXC_PTB25 0x17C /* Software MUX Pad Control Register 95 */
+#define IOMUXC_PTB26 0x180 /* Software MUX Pad Control Register 96 */
+#define IOMUXC_PTB27 0x184 /* Software MUX Pad Control Register 97 */
+#define IOMUXC_PTB28 0x188 /* Software MUX Pad Control Register 98 */
+#define IOMUXC_PTC26 0x18C /* Software MUX Pad Control Register 99 */
+#define IOMUXC_PTC27 0x190 /* Software MUX Pad Control Register 100 */
+#define IOMUXC_PTC28 0x194 /* Software MUX Pad Control Register 101 */
+#define IOMUXC_PTC29 0x198 /* Software MUX Pad Control Register 102 */
+#define IOMUXC_PTC30 0x19C /* Software MUX Pad Control Register 103 */
+#define IOMUXC_PTC31 0x1A0 /* Software MUX Pad Control Register 104 */
+#define IOMUXC_PTE0 0x1A4 /* Software MUX Pad Control Register 105 */
+#define IOMUXC_PTE1 0x1A8 /* Software MUX Pad Control Register 106 */
+#define IOMUXC_PTE2 0x1AC /* Software MUX Pad Control Register 107 */
+#define IOMUXC_PTE3 0x1B0 /* Software MUX Pad Control Register 108 */
+#define IOMUXC_PTE4 0x1B4 /* Software MUX Pad Control Register 109 */
+#define IOMUXC_PTE5 0x1B8 /* Software MUX Pad Control Register 110 */
+#define IOMUXC_PTE6 0x1BC /* Software MUX Pad Control Register 111 */
+#define IOMUXC_PTE7 0x1C0 /* Software MUX Pad Control Register 112 */
+#define IOMUXC_PTE8 0x1C4 /* Software MUX Pad Control Register 113 */
+#define IOMUXC_PTE9 0x1C8 /* Software MUX Pad Control Register 114 */
+#define IOMUXC_PTE10 0x1CC /* Software MUX Pad Control Register 115 */
+#define IOMUXC_PTE11 0x1D0 /* Software MUX Pad Control Register 116 */
+#define IOMUXC_PTE12 0x1D4 /* Software MUX Pad Control Register 117 */
+#define IOMUXC_PTE13 0x1D8 /* Software MUX Pad Control Register 118 */
+#define IOMUXC_PTE14 0x1DC /* Software MUX Pad Control Register 119 */
+#define IOMUXC_PTE15 0x1E0 /* Software MUX Pad Control Register 120 */
+#define IOMUXC_PTE16 0x1E4 /* Software MUX Pad Control Register 121 */
+#define IOMUXC_PTE17 0x1E8 /* Software MUX Pad Control Register 122 */
+#define IOMUXC_PTE18 0x1EC /* Software MUX Pad Control Register 123 */
+#define IOMUXC_PTE19 0x1F0 /* Software MUX Pad Control Register 124 */
+#define IOMUXC_PTE20 0x1F4 /* Software MUX Pad Control Register 125 */
+#define IOMUXC_PTE21 0x1F8 /* Software MUX Pad Control Register 126 */
+#define IOMUXC_PTE22 0x1FC /* Software MUX Pad Control Register 127 */
+#define IOMUXC_PTE23 0x200 /* Software MUX Pad Control Register 128 */
+#define IOMUXC_PTE24 0x204 /* Software MUX Pad Control Register 129 */
+#define IOMUXC_PTE25 0x208 /* Software MUX Pad Control Register 130 */
+#define IOMUXC_PTE26 0x20C /* Software MUX Pad Control Register 131 */
+#define IOMUXC_PTE27 0x210 /* Software MUX Pad Control Register 132 */
+#define IOMUXC_PTE28 0x214 /* Software MUX Pad Control Register 133 */
+#define IOMUXC_PTA7 0x218 /* Software MUX Pad Control Register 134 */
diff --git a/sys/arm/freescale/vybrid/vf_machdep.c b/sys/arm/freescale/vybrid/vf_machdep.c
new file mode 100644
index 000000000000..6c5b7bcf3755
--- /dev/null
+++ b/sys/arm/freescale/vybrid/vf_machdep.c
@@ -0,0 +1,88 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "opt_platform.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/devmap.h>
+
+#include <vm/vm.h>
+
+#include <dev/ofw/openfirm.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+#include <machine/platform.h>
+#include <machine/platformvar.h>
+
+#include <arm/freescale/vybrid/vf_src.h>
+
+#include "platform_if.h"
+
+static int
+vf_devmap_init(platform_t plat)
+{
+
+ devmap_add_entry(0x40000000, 0x100000);
+
+ return (0);
+}
+
+static void
+vf_cpu_reset(platform_t plat)
+{
+ phandle_t src;
+ uint32_t paddr;
+ bus_addr_t vaddr;
+
+ if (src_swreset() == 0)
+ goto end;
+
+ src = OF_finddevice("src");
+ if ((src != -1) && (OF_getencprop(src, "reg", &paddr, sizeof(paddr))) > 0) {
+ if (bus_space_map(fdtbus_bs_tag, paddr, 0x10, 0, &vaddr) == 0) {
+ bus_space_write_4(fdtbus_bs_tag, vaddr, 0x00, SW_RST);
+ }
+ }
+
+end:
+ while (1);
+}
+
+static platform_method_t vf_methods[] = {
+ PLATFORMMETHOD(platform_devmap_init, vf_devmap_init),
+ PLATFORMMETHOD(platform_cpu_reset, vf_cpu_reset),
+
+ PLATFORMMETHOD_END,
+};
+
+FDT_PLATFORM_DEF(vf, "vybrid", 0, "freescale,vybrid", 200);
diff --git a/sys/arm/freescale/vybrid/vf_mscm.c b/sys/arm/freescale/vybrid/vf_mscm.c
new file mode 100644
index 000000000000..aa29175468af
--- /dev/null
+++ b/sys/arm/freescale/vybrid/vf_mscm.c
@@ -0,0 +1,126 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Vybrid Family Miscellaneous System Control Module (MSCM)
+ * Chapter 66, Vybrid Reference Manual, Rev. 5, 07/2013
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <arm/freescale/vybrid/vf_common.h>
+
+#define VF_NINT 112 /* Total number of interrupts */
+
+/* Int Router Shared Peripheral Routing Control */
+#define MSCM_IRSPRC(n) (0x880 + 2 * n)
+
+struct mscm_softc {
+ struct resource *res[1];
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+};
+
+static struct resource_spec mscm_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+static int
+mscm_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "fsl,mvf600-mscm"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Vybrid Family Miscellaneous System Control Module");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+mscm_attach(device_t dev)
+{
+ struct mscm_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+
+ if (bus_alloc_resources(dev, mscm_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ /* Memory interface */
+ sc->bst = rman_get_bustag(sc->res[0]);
+ sc->bsh = rman_get_bushandle(sc->res[0]);
+
+ /* Route all the interrupts to CP0 */
+ for (i = 0; i < VF_NINT; i++)
+ WRITE2(sc, MSCM_IRSPRC(i), 1);
+
+ return (0);
+}
+
+static device_method_t mscm_methods[] = {
+ DEVMETHOD(device_probe, mscm_probe),
+ DEVMETHOD(device_attach, mscm_attach),
+ { 0, 0 }
+};
+
+static driver_t mscm_driver = {
+ "mscm",
+ mscm_methods,
+ sizeof(struct mscm_softc),
+};
+
+static devclass_t mscm_devclass;
+
+DRIVER_MODULE(mscm, simplebus, mscm_driver, mscm_devclass, 0, 0);
diff --git a/sys/arm/freescale/vybrid/vf_port.c b/sys/arm/freescale/vybrid/vf_port.c
new file mode 100644
index 000000000000..d04cf4a08b37
--- /dev/null
+++ b/sys/arm/freescale/vybrid/vf_port.c
@@ -0,0 +1,249 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Vybrid Family Port control and interrupts (PORT)
+ * Chapter 6, Vybrid Reference Manual, Rev. 5, 07/2013
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <arm/freescale/vybrid/vf_port.h>
+#include <arm/freescale/vybrid/vf_common.h>
+
+/* Pin Control Register */
+#define PORT_PCR(n) (0x1000 * (n >> 5) + 0x4 * (n % 32))
+#define PCR_IRQC_S 16
+#define PCR_IRQC_M 0xF
+#define PCR_DMA_RE 0x1
+#define PCR_DMA_FE 0x2
+#define PCR_DMA_EE 0x3
+#define PCR_INT_LZ 0x8
+#define PCR_INT_RE 0x9
+#define PCR_INT_FE 0xA
+#define PCR_INT_EE 0xB
+#define PCR_INT_LO 0xC
+#define PCR_ISF (1 << 24)
+#define PORT0_ISFR 0xA0 /* Interrupt Status Flag Register */
+#define PORT0_DFER 0xC0 /* Digital Filter Enable Register */
+#define PORT0_DFCR 0xC4 /* Digital Filter Clock Register */
+#define PORT0_DFWR 0xC8 /* Digital Filter Width Register */
+
+struct port_event {
+ uint32_t enabled;
+ uint32_t mux_num;
+ uint32_t mux_src;
+ uint32_t mux_chn;
+ void (*ih) (void *);
+ void *ih_user;
+ enum ev_type pevt;
+};
+
+static struct port_event event_map[NGPIO];
+
+struct port_softc {
+ struct resource *res[6];
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+ void *gpio_ih[NGPIO];
+};
+
+struct port_softc *port_sc;
+
+static struct resource_spec port_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 1, RF_ACTIVE },
+ { SYS_RES_IRQ, 2, RF_ACTIVE },
+ { SYS_RES_IRQ, 3, RF_ACTIVE },
+ { SYS_RES_IRQ, 4, RF_ACTIVE },
+ { -1, 0 }
+};
+
+static int
+port_intr(void *arg)
+{
+ struct port_event *pev;
+ struct port_softc *sc;
+ int reg;
+ int i;
+
+ sc = arg;
+
+ for (i = 0; i < NGPIO; i++) {
+ reg = READ4(sc, PORT_PCR(i));
+ if (reg & PCR_ISF) {
+ /* Clear interrupt */
+ WRITE4(sc, PORT_PCR(i), reg);
+
+ /* Handle event */
+ pev = &event_map[i];
+ if (pev->enabled == 1) {
+ if (pev->ih != NULL) {
+ pev->ih(pev->ih_user);
+ }
+ }
+ }
+ }
+
+ return (FILTER_HANDLED);
+}
+
+int
+port_setup(int pnum, enum ev_type pevt, void (*ih)(void *), void *ih_user)
+{
+ struct port_event *pev;
+ struct port_softc *sc;
+ int reg;
+ int val;
+
+ sc = port_sc;
+
+ switch (pevt) {
+ case DMA_RISING_EDGE:
+ val = PCR_DMA_RE;
+ break;
+ case DMA_FALLING_EDGE:
+ val = PCR_DMA_FE;
+ break;
+ case DMA_EITHER_EDGE:
+ val = PCR_DMA_EE;
+ break;
+ case INT_LOGIC_ZERO:
+ val = PCR_INT_LZ;
+ break;
+ case INT_RISING_EDGE:
+ val = PCR_INT_RE;
+ break;
+ case INT_FALLING_EDGE:
+ val = PCR_INT_FE;
+ break;
+ case INT_EITHER_EDGE:
+ val = PCR_INT_EE;
+ break;
+ case INT_LOGIC_ONE:
+ val = PCR_INT_LO;
+ break;
+ default:
+ return (-1);
+ }
+
+ reg = READ4(sc, PORT_PCR(pnum));
+ reg &= ~(PCR_IRQC_M << PCR_IRQC_S);
+ reg |= (val << PCR_IRQC_S);
+ WRITE4(sc, PORT_PCR(pnum), reg);
+
+ pev = &event_map[pnum];
+ pev->ih = ih;
+ pev->ih_user = ih_user;
+ pev->pevt = pevt;
+ pev->enabled = 1;
+
+ return (0);
+}
+
+static int
+port_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "fsl,mvf600-port"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Vybrid Family Port control and interrupts");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+port_attach(device_t dev)
+{
+ struct port_softc *sc;
+ int irq;
+
+ sc = device_get_softc(dev);
+
+ if (bus_alloc_resources(dev, port_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ /* Memory interface */
+ sc->bst = rman_get_bustag(sc->res[0]);
+ sc->bsh = rman_get_bushandle(sc->res[0]);
+
+ port_sc = sc;
+
+ for (irq = 0; irq < NPORTS; irq ++) {
+ if ((bus_setup_intr(dev, sc->res[1 + irq], INTR_TYPE_MISC,
+ port_intr, NULL, sc, &sc->gpio_ih[irq]))) {
+ device_printf(dev,
+ "ERROR: Unable to register interrupt handler\n");
+ return (ENXIO);
+ }
+ }
+
+ return (0);
+}
+
+static device_method_t port_methods[] = {
+ DEVMETHOD(device_probe, port_probe),
+ DEVMETHOD(device_attach, port_attach),
+ { 0, 0 }
+};
+
+static driver_t port_driver = {
+ "port",
+ port_methods,
+ sizeof(struct port_softc),
+};
+
+static devclass_t port_devclass;
+
+DRIVER_MODULE(port, simplebus, port_driver, port_devclass, 0, 0);
diff --git a/sys/arm/freescale/vybrid/vf_port.h b/sys/arm/freescale/vybrid/vf_port.h
new file mode 100644
index 000000000000..e046a8fcba83
--- /dev/null
+++ b/sys/arm/freescale/vybrid/vf_port.h
@@ -0,0 +1,45 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define NPORTS 5
+#define NGPIO (NPORTS * 32)
+
+enum ev_type {
+ DMA_RISING_EDGE,
+ DMA_FALLING_EDGE,
+ DMA_EITHER_EDGE,
+ INT_LOGIC_ZERO,
+ INT_RISING_EDGE,
+ INT_FALLING_EDGE,
+ INT_EITHER_EDGE,
+ INT_LOGIC_ONE,
+};
+
+int port_setup(int, enum ev_type, void (*ih)(void *), void *ih_user);
diff --git a/sys/arm/freescale/vybrid/vf_sai.c b/sys/arm/freescale/vybrid/vf_sai.c
new file mode 100644
index 000000000000..26ba34df5009
--- /dev/null
+++ b/sys/arm/freescale/vybrid/vf_sai.c
@@ -0,0 +1,804 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Vybrid Family Synchronous Audio Interface (SAI)
+ * Chapter 51, Vybrid Reference Manual, Rev. 5, 07/2013
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+
+#include <dev/sound/pcm/sound.h>
+#include <dev/sound/chip.h>
+#include <mixer_if.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <arm/freescale/vybrid/vf_common.h>
+#include <arm/freescale/vybrid/vf_dmamux.h>
+#include <arm/freescale/vybrid/vf_edma.h>
+
+#define I2S_TCSR 0x00 /* SAI Transmit Control */
+#define I2S_TCR1 0x04 /* SAI Transmit Configuration 1 */
+#define I2S_TCR2 0x08 /* SAI Transmit Configuration 2 */
+#define I2S_TCR3 0x0C /* SAI Transmit Configuration 3 */
+#define I2S_TCR4 0x10 /* SAI Transmit Configuration 4 */
+#define I2S_TCR5 0x14 /* SAI Transmit Configuration 5 */
+#define I2S_TDR0 0x20 /* SAI Transmit Data */
+#define I2S_TFR0 0x40 /* SAI Transmit FIFO */
+#define I2S_TMR 0x60 /* SAI Transmit Mask */
+#define I2S_RCSR 0x80 /* SAI Receive Control */
+#define I2S_RCR1 0x84 /* SAI Receive Configuration 1 */
+#define I2S_RCR2 0x88 /* SAI Receive Configuration 2 */
+#define I2S_RCR3 0x8C /* SAI Receive Configuration 3 */
+#define I2S_RCR4 0x90 /* SAI Receive Configuration 4 */
+#define I2S_RCR5 0x94 /* SAI Receive Configuration 5 */
+#define I2S_RDR0 0xA0 /* SAI Receive Data */
+#define I2S_RFR0 0xC0 /* SAI Receive FIFO */
+#define I2S_RMR 0xE0 /* SAI Receive Mask */
+
+#define TCR1_TFW_M 0x1f /* Transmit FIFO Watermark Mask */
+#define TCR1_TFW_S 0 /* Transmit FIFO Watermark Shift */
+#define TCR2_MSEL_M 0x3 /* MCLK Select Mask*/
+#define TCR2_MSEL_S 26 /* MCLK Select Shift*/
+#define TCR2_BCP (1 << 25) /* Bit Clock Polarity */
+#define TCR2_BCD (1 << 24) /* Bit Clock Direction */
+#define TCR3_TCE (1 << 16) /* Transmit Channel Enable */
+#define TCR4_FRSZ_M 0x1f /* Frame size Mask */
+#define TCR4_FRSZ_S 16 /* Frame size Shift */
+#define TCR4_SYWD_M 0x1f /* Sync Width Mask */
+#define TCR4_SYWD_S 8 /* Sync Width Shift */
+#define TCR4_MF (1 << 4) /* MSB First */
+#define TCR4_FSE (1 << 3) /* Frame Sync Early */
+#define TCR4_FSP (1 << 1) /* Frame Sync Polarity Low */
+#define TCR4_FSD (1 << 0) /* Frame Sync Direction Master */
+#define TCR5_FBT_M 0x1f /* First Bit Shifted */
+#define TCR5_FBT_S 8 /* First Bit Shifted */
+#define TCR5_W0W_M 0x1f /* Word 0 Width */
+#define TCR5_W0W_S 16 /* Word 0 Width */
+#define TCR5_WNW_M 0x1f /* Word N Width */
+#define TCR5_WNW_S 24 /* Word N Width */
+#define TCSR_TE (1 << 31) /* Transmitter Enable */
+#define TCSR_BCE (1 << 28) /* Bit Clock Enable */
+#define TCSR_FRDE (1 << 0) /* FIFO Request DMA Enable */
+
+#define SAI_NCHANNELS 1
+
+static MALLOC_DEFINE(M_SAI, "sai", "sai audio");
+
+struct sai_rate {
+ uint32_t speed;
+ uint32_t div; /* Bit Clock Divide. Division value is (div + 1) * 2. */
+ uint32_t mfi; /* PLL4 Multiplication Factor Integer */
+ uint32_t mfn; /* PLL4 Multiplication Factor Numerator */
+ uint32_t mfd; /* PLL4 Multiplication Factor Denominator */
+};
+
+/*
+ * Bit clock divider formula
+ * (div + 1) * 2 = MCLK/(nch * LRCLK * bits/1000000),
+ * where:
+ * MCLK - master clock
+ * nch - number of channels
+ * LRCLK - left right clock
+ * e.g. (div + 1) * 2 = 16.9344/(2 * 44100 * 24/1000000)
+ *
+ * Example for 96khz, 24bit, 18.432 Mhz mclk (192fs)
+ * { 96000, 1, 18, 40176000, 93000000 },
+ */
+
+static struct sai_rate rate_map[] = {
+ { 44100, 7, 33, 80798400, 93000000 }, /* 33.8688 Mhz */
+ { 96000, 3, 36, 80352000, 93000000 }, /* 36.864 Mhz */
+ { 192000, 1, 36, 80352000, 93000000 }, /* 36.864 Mhz */
+ { 0, 0 },
+};
+
+struct sc_info {
+ struct resource *res[2];
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+ device_t dev;
+ struct mtx *lock;
+ uint32_t speed;
+ uint32_t period;
+ void *ih;
+ int pos;
+ int dma_size;
+ bus_dma_tag_t dma_tag;
+ bus_dmamap_t dma_map;
+ bus_addr_t buf_base_phys;
+ uint32_t *buf_base;
+ struct tcd_conf *tcd;
+ struct sai_rate *sr;
+ struct edma_softc *edma_sc;
+ int edma_chnum;
+};
+
+/* Channel registers */
+struct sc_chinfo {
+ struct snd_dbuf *buffer;
+ struct pcm_channel *channel;
+ struct sc_pcminfo *parent;
+
+ /* Channel information */
+ uint32_t dir;
+ uint32_t format;
+
+ /* Flags */
+ uint32_t run;
+};
+
+/* PCM device private data */
+struct sc_pcminfo {
+ device_t dev;
+ uint32_t (*ih) (struct sc_pcminfo *scp);
+ uint32_t chnum;
+ struct sc_chinfo chan[SAI_NCHANNELS];
+ struct sc_info *sc;
+};
+
+static struct resource_spec sai_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+static int setup_dma(struct sc_pcminfo *scp);
+static void setup_sai(struct sc_info *);
+static void sai_configure_clock(struct sc_info *);
+
+/*
+ * Mixer interface.
+ */
+
+static int
+saimixer_init(struct snd_mixer *m)
+{
+ struct sc_pcminfo *scp;
+ struct sc_info *sc;
+ int mask;
+
+ scp = mix_getdevinfo(m);
+ sc = scp->sc;
+
+ if (sc == NULL)
+ return -1;
+
+ mask = SOUND_MASK_PCM;
+
+ snd_mtxlock(sc->lock);
+ pcm_setflags(scp->dev, pcm_getflags(scp->dev) | SD_F_SOFTPCMVOL);
+ mix_setdevs(m, mask);
+ snd_mtxunlock(sc->lock);
+
+ return (0);
+}
+
+static int
+saimixer_set(struct snd_mixer *m, unsigned dev,
+ unsigned left, unsigned right)
+{
+ struct sc_pcminfo *scp;
+
+ scp = mix_getdevinfo(m);
+
+#if 0
+ device_printf(scp->dev, "saimixer_set() %d %d\n",
+ left, right);
+#endif
+
+ return (0);
+}
+
+static kobj_method_t saimixer_methods[] = {
+ KOBJMETHOD(mixer_init, saimixer_init),
+ KOBJMETHOD(mixer_set, saimixer_set),
+ KOBJMETHOD_END
+};
+MIXER_DECLARE(saimixer);
+
+/*
+ * Channel interface.
+ */
+
+static void *
+saichan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
+ struct pcm_channel *c, int dir)
+{
+ struct sc_pcminfo *scp;
+ struct sc_chinfo *ch;
+ struct sc_info *sc;
+
+ scp = (struct sc_pcminfo *)devinfo;
+ sc = scp->sc;
+
+ snd_mtxlock(sc->lock);
+ ch = &scp->chan[0];
+ ch->dir = dir;
+ ch->run = 0;
+ ch->buffer = b;
+ ch->channel = c;
+ ch->parent = scp;
+ snd_mtxunlock(sc->lock);
+
+ if (sndbuf_setup(ch->buffer, sc->buf_base, sc->dma_size) != 0) {
+ device_printf(scp->dev, "Can't setup sndbuf.\n");
+ return NULL;
+ }
+
+ return ch;
+}
+
+static int
+saichan_free(kobj_t obj, void *data)
+{
+ struct sc_chinfo *ch = data;
+ struct sc_pcminfo *scp = ch->parent;
+ struct sc_info *sc = scp->sc;
+
+#if 0
+ device_printf(scp->dev, "saichan_free()\n");
+#endif
+
+ snd_mtxlock(sc->lock);
+ /* TODO: free channel buffer */
+ snd_mtxunlock(sc->lock);
+
+ return (0);
+}
+
+static int
+saichan_setformat(kobj_t obj, void *data, uint32_t format)
+{
+ struct sc_chinfo *ch = data;
+
+ ch->format = format;
+
+ return (0);
+}
+
+static uint32_t
+saichan_setspeed(kobj_t obj, void *data, uint32_t speed)
+{
+ struct sc_pcminfo *scp;
+ struct sc_chinfo *ch;
+ struct sai_rate *sr;
+ struct sc_info *sc;
+ int threshold;
+ int i;
+
+ ch = data;
+ scp = ch->parent;
+ sc = scp->sc;
+
+ sr = NULL;
+
+ /* First look for equal frequency. */
+ for (i = 0; rate_map[i].speed != 0; i++) {
+ if (rate_map[i].speed == speed)
+ sr = &rate_map[i];
+ }
+
+ /* If no match, just find nearest. */
+ if (sr == NULL) {
+ for (i = 0; rate_map[i].speed != 0; i++) {
+ sr = &rate_map[i];
+ threshold = sr->speed + ((rate_map[i + 1].speed != 0) ?
+ ((rate_map[i + 1].speed - sr->speed) >> 1) : 0);
+ if (speed < threshold)
+ break;
+ }
+ }
+
+ sc->sr = sr;
+
+ sai_configure_clock(sc);
+
+ return (sr->speed);
+}
+
+static void
+sai_configure_clock(struct sc_info *sc)
+{
+ struct sai_rate *sr;
+ int reg;
+
+ sr = sc->sr;
+
+ /*
+ * Manual says that TCR/RCR registers must not be
+ * altered when TCSR[TE] is set.
+ * We ignore it since we have problem sometimes
+ * after re-enabling transmitter (DMA goes stall).
+ */
+
+ reg = READ4(sc, I2S_TCR2);
+ reg &= ~(0xff << 0);
+ reg |= (sr->div << 0);
+ WRITE4(sc, I2S_TCR2, reg);
+
+ pll4_configure_output(sr->mfi, sr->mfn, sr->mfd);
+}
+
+static uint32_t
+saichan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
+{
+ struct sc_chinfo *ch = data;
+ struct sc_pcminfo *scp = ch->parent;
+ struct sc_info *sc = scp->sc;
+
+ sndbuf_resize(ch->buffer, sc->dma_size / blocksize, blocksize);
+
+ sc->period = sndbuf_getblksz(ch->buffer);
+ return (sc->period);
+}
+
+uint32_t sai_dma_intr(void *arg, int chn);
+uint32_t
+sai_dma_intr(void *arg, int chn)
+{
+ struct sc_pcminfo *scp;
+ struct sc_chinfo *ch;
+ struct sc_info *sc;
+ struct tcd_conf *tcd;
+
+ scp = arg;
+ ch = &scp->chan[0];
+
+ sc = scp->sc;
+ tcd = sc->tcd;
+
+ sc->pos += (tcd->nbytes * tcd->nmajor);
+ if (sc->pos >= sc->dma_size)
+ sc->pos -= sc->dma_size;
+
+ if (ch->run)
+ chn_intr(ch->channel);
+
+ return (0);
+}
+
+static int
+find_edma_controller(struct sc_info *sc)
+{
+ struct edma_softc *edma_sc;
+ phandle_t node, edma_node;
+ int edma_src_transmit;
+ int edma_mux_group;
+ int edma_device_id;
+ device_t edma_dev;
+ int dts_value;
+ int len;
+ int i;
+
+ if ((node = ofw_bus_get_node(sc->dev)) == -1)
+ return (ENXIO);
+
+ if ((len = OF_getproplen(node, "edma-controller")) <= 0)
+ return (ENXIO);
+ if ((len = OF_getproplen(node, "edma-src-transmit")) <= 0)
+ return (ENXIO);
+ if ((len = OF_getproplen(node, "edma-mux-group")) <= 0)
+ return (ENXIO);
+
+ OF_getencprop(node, "edma-src-transmit", &dts_value, len);
+ edma_src_transmit = dts_value;
+ OF_getencprop(node, "edma-mux-group", &dts_value, len);
+ edma_mux_group = dts_value;
+ OF_getencprop(node, "edma-controller", &dts_value, len);
+ edma_node = OF_node_from_xref(dts_value);
+
+ if ((len = OF_getproplen(edma_node, "device-id")) <= 0) {
+ return (ENXIO);
+ }
+
+ OF_getencprop(edma_node, "device-id", &dts_value, len);
+ edma_device_id = dts_value;
+
+ edma_sc = NULL;
+
+ for (i = 0; i < EDMA_NUM_DEVICES; i++) {
+ edma_dev = devclass_get_device(devclass_find("edma"), i);
+ if (edma_dev) {
+ edma_sc = device_get_softc(edma_dev);
+ if (edma_sc->device_id == edma_device_id) {
+ /* found */
+ break;
+ }
+
+ edma_sc = NULL;
+ }
+ }
+
+ if (edma_sc == NULL) {
+ device_printf(sc->dev, "no eDMA. can't operate\n");
+ return (ENXIO);
+ }
+
+ sc->edma_sc = edma_sc;
+
+ sc->edma_chnum = edma_sc->channel_configure(edma_sc, edma_mux_group,
+ edma_src_transmit);
+ if (sc->edma_chnum < 0) {
+ /* cant setup eDMA */
+ return (ENXIO);
+ }
+
+ return (0);
+};
+
+static int
+setup_dma(struct sc_pcminfo *scp)
+{
+ struct tcd_conf *tcd;
+ struct sc_info *sc;
+
+ sc = scp->sc;
+
+ tcd = malloc(sizeof(struct tcd_conf), M_DEVBUF, M_WAITOK | M_ZERO);
+ tcd->channel = sc->edma_chnum;
+ tcd->ih = sai_dma_intr;
+ tcd->ih_user = scp;
+ tcd->saddr = sc->buf_base_phys;
+ tcd->daddr = rman_get_start(sc->res[0]) + I2S_TDR0;
+
+ /*
+ * Bytes to transfer per each minor loop.
+ * Hardware FIFO buffer size is 32x32bits.
+ */
+ tcd->nbytes = 64;
+
+ tcd->nmajor = 512;
+ tcd->smod = 17; /* dma_size range */
+ tcd->dmod = 0;
+ tcd->esg = 0;
+ tcd->soff = 0x4;
+ tcd->doff = 0;
+ tcd->ssize = 0x2;
+ tcd->dsize = 0x2;
+ tcd->slast = 0;
+ tcd->dlast_sga = 0;
+
+ sc->tcd = tcd;
+
+ sc->edma_sc->dma_setup(sc->edma_sc, sc->tcd);
+
+ return (0);
+}
+
+static int
+saichan_trigger(kobj_t obj, void *data, int go)
+{
+ struct sc_chinfo *ch = data;
+ struct sc_pcminfo *scp = ch->parent;
+ struct sc_info *sc = scp->sc;
+
+ snd_mtxlock(sc->lock);
+
+ switch (go) {
+ case PCMTRIG_START:
+#if 0
+ device_printf(scp->dev, "trigger start\n");
+#endif
+ ch->run = 1;
+ break;
+
+ case PCMTRIG_STOP:
+ case PCMTRIG_ABORT:
+#if 0
+ device_printf(scp->dev, "trigger stop or abort\n");
+#endif
+ ch->run = 0;
+ break;
+ }
+
+ snd_mtxunlock(sc->lock);
+
+ return (0);
+}
+
+static uint32_t
+saichan_getptr(kobj_t obj, void *data)
+{
+ struct sc_pcminfo *scp;
+ struct sc_chinfo *ch;
+ struct sc_info *sc;
+
+ ch = data;
+ scp = ch->parent;
+ sc = scp->sc;
+
+ return (sc->pos);
+}
+
+static uint32_t sai_pfmt[] = {
+ /*
+ * eDMA doesn't allow 24-bit coping,
+ * so we use 32.
+ */
+ SND_FORMAT(AFMT_S32_LE, 2, 0),
+ 0
+};
+
+static struct pcmchan_caps sai_pcaps = {44100, 192000, sai_pfmt, 0};
+
+static struct pcmchan_caps *
+saichan_getcaps(kobj_t obj, void *data)
+{
+
+ return (&sai_pcaps);
+}
+
+static kobj_method_t saichan_methods[] = {
+ KOBJMETHOD(channel_init, saichan_init),
+ KOBJMETHOD(channel_free, saichan_free),
+ KOBJMETHOD(channel_setformat, saichan_setformat),
+ KOBJMETHOD(channel_setspeed, saichan_setspeed),
+ KOBJMETHOD(channel_setblocksize, saichan_setblocksize),
+ KOBJMETHOD(channel_trigger, saichan_trigger),
+ KOBJMETHOD(channel_getptr, saichan_getptr),
+ KOBJMETHOD(channel_getcaps, saichan_getcaps),
+ KOBJMETHOD_END
+};
+CHANNEL_DECLARE(saichan);
+
+static int
+sai_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "fsl,mvf600-sai"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Vybrid Family Synchronous Audio Interface");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static void
+sai_intr(void *arg)
+{
+ struct sc_pcminfo *scp;
+ struct sc_info *sc;
+
+ scp = arg;
+ sc = scp->sc;
+
+ device_printf(sc->dev, "Error I2S_TCSR == 0x%08x\n",
+ READ4(sc, I2S_TCSR));
+}
+
+static void
+setup_sai(struct sc_info *sc)
+{
+ int reg;
+
+ /*
+ * TCR/RCR registers must not be altered when TCSR[TE] is set.
+ */
+
+ reg = READ4(sc, I2S_TCSR);
+ reg &= ~(TCSR_BCE | TCSR_TE | TCSR_FRDE);
+ WRITE4(sc, I2S_TCSR, reg);
+
+ reg = READ4(sc, I2S_TCR3);
+ reg &= ~(TCR3_TCE);
+ WRITE4(sc, I2S_TCR3, reg);
+
+ reg = (64 << TCR1_TFW_S);
+ WRITE4(sc, I2S_TCR1, reg);
+
+ reg = READ4(sc, I2S_TCR2);
+ reg &= ~(TCR2_MSEL_M << TCR2_MSEL_S);
+ reg |= (1 << TCR2_MSEL_S);
+ reg |= (TCR2_BCP | TCR2_BCD);
+ WRITE4(sc, I2S_TCR2, reg);
+
+ sai_configure_clock(sc);
+
+ reg = READ4(sc, I2S_TCR3);
+ reg |= (TCR3_TCE);
+ WRITE4(sc, I2S_TCR3, reg);
+
+ /* Configure to 32-bit I2S mode */
+ reg = READ4(sc, I2S_TCR4);
+ reg &= ~(TCR4_FRSZ_M << TCR4_FRSZ_S);
+ reg |= (1 << TCR4_FRSZ_S); /* 2 words per frame */
+ reg &= ~(TCR4_SYWD_M << TCR4_SYWD_S);
+ reg |= (23 << TCR4_SYWD_S);
+ reg |= (TCR4_MF | TCR4_FSE | TCR4_FSP | TCR4_FSD);
+ WRITE4(sc, I2S_TCR4, reg);
+
+ reg = READ4(sc, I2S_TCR5);
+ reg &= ~(TCR5_W0W_M << TCR5_W0W_S);
+ reg |= (23 << TCR5_W0W_S);
+ reg &= ~(TCR5_WNW_M << TCR5_WNW_S);
+ reg |= (23 << TCR5_WNW_S);
+ reg &= ~(TCR5_FBT_M << TCR5_FBT_S);
+ reg |= (31 << TCR5_FBT_S);
+ WRITE4(sc, I2S_TCR5, reg);
+
+ /* Enable transmitter */
+ reg = READ4(sc, I2S_TCSR);
+ reg |= (TCSR_BCE | TCSR_TE | TCSR_FRDE);
+ reg |= (1 << 10); /* FEIE */
+ WRITE4(sc, I2S_TCSR, reg);
+}
+
+static void
+sai_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err)
+{
+ bus_addr_t *addr;
+
+ if (err)
+ return;
+
+ addr = (bus_addr_t*)arg;
+ *addr = segs[0].ds_addr;
+}
+
+static int
+sai_attach(device_t dev)
+{
+ char status[SND_STATUSLEN];
+ struct sc_pcminfo *scp;
+ struct sc_info *sc;
+ int err;
+
+ sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO);
+ sc->dev = dev;
+ sc->sr = &rate_map[0];
+ sc->pos = 0;
+
+ sc->lock = snd_mtxcreate(device_get_nameunit(dev), "sai softc");
+ if (sc->lock == NULL) {
+ device_printf(dev, "Cant create mtx\n");
+ return (ENXIO);
+ }
+
+ if (bus_alloc_resources(dev, sai_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ /* Memory interface */
+ sc->bst = rman_get_bustag(sc->res[0]);
+ sc->bsh = rman_get_bushandle(sc->res[0]);
+
+ /* eDMA */
+ if (find_edma_controller(sc)) {
+ device_printf(dev, "could not find active eDMA\n");
+ return (ENXIO);
+ }
+
+ /* Setup PCM */
+ scp = malloc(sizeof(struct sc_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO);
+ scp->sc = sc;
+ scp->dev = dev;
+
+ /* DMA */
+ sc->dma_size = 131072;
+
+ /*
+ * Must use dma_size boundary as modulo feature required.
+ * Modulo feature allows setup circular buffer.
+ */
+
+ err = bus_dma_tag_create(
+ bus_get_dma_tag(sc->dev),
+ 4, sc->dma_size, /* alignment, boundary */
+ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ sc->dma_size, 1, /* maxsize, nsegments */
+ sc->dma_size, 0, /* maxsegsize, flags */
+ NULL, NULL, /* lockfunc, lockarg */
+ &sc->dma_tag);
+
+ err = bus_dmamem_alloc(sc->dma_tag, (void **)&sc->buf_base,
+ BUS_DMA_NOWAIT | BUS_DMA_COHERENT, &sc->dma_map);
+ if (err) {
+ device_printf(dev, "cannot allocate framebuffer\n");
+ return (ENXIO);
+ }
+
+ err = bus_dmamap_load(sc->dma_tag, sc->dma_map, sc->buf_base,
+ sc->dma_size, sai_dmamap_cb, &sc->buf_base_phys, BUS_DMA_NOWAIT);
+ if (err) {
+ device_printf(dev, "cannot load DMA map\n");
+ return (ENXIO);
+ }
+
+ bzero(sc->buf_base, sc->dma_size);
+
+ /* Setup interrupt handler */
+ err = bus_setup_intr(dev, sc->res[1], INTR_MPSAFE | INTR_TYPE_AV,
+ NULL, sai_intr, scp, &sc->ih);
+ if (err) {
+ device_printf(dev, "Unable to alloc interrupt resource.\n");
+ return (ENXIO);
+ }
+
+ pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE);
+
+ err = pcm_register(dev, scp, 1, 0);
+ if (err) {
+ device_printf(dev, "Can't register pcm.\n");
+ return (ENXIO);
+ }
+
+ scp->chnum = 0;
+ pcm_addchan(dev, PCMDIR_PLAY, &saichan_class, scp);
+ scp->chnum++;
+
+ snprintf(status, SND_STATUSLEN, "at simplebus");
+ pcm_setstatus(dev, status);
+
+ mixer_init(dev, &saimixer_class, scp);
+
+ setup_dma(scp);
+ setup_sai(sc);
+
+ return (0);
+}
+
+static device_method_t sai_pcm_methods[] = {
+ DEVMETHOD(device_probe, sai_probe),
+ DEVMETHOD(device_attach, sai_attach),
+ { 0, 0 }
+};
+
+static driver_t sai_pcm_driver = {
+ "pcm",
+ sai_pcm_methods,
+ PCM_SOFTC_SIZE,
+};
+
+DRIVER_MODULE(sai, simplebus, sai_pcm_driver, pcm_devclass, 0, 0);
+MODULE_DEPEND(sai, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
+MODULE_VERSION(sai, 1);
diff --git a/sys/arm/freescale/vybrid/vf_spi.c b/sys/arm/freescale/vybrid/vf_spi.c
new file mode 100644
index 000000000000..a264b5d2c965
--- /dev/null
+++ b/sys/arm/freescale/vybrid/vf_spi.c
@@ -0,0 +1,293 @@
+/*-
+ * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Vybrid Family Serial Peripheral Interface (SPI)
+ * Chapter 47, Vybrid Reference Manual, Rev. 5, 07/2013
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+
+#include <dev/spibus/spi.h>
+#include <dev/spibus/spibusvar.h>
+
+#include "spibus_if.h"
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <arm/freescale/vybrid/vf_common.h>
+
+#define SPI_FIFO_SIZE 4
+
+#define SPI_MCR 0x00 /* Module Configuration */
+#define MCR_MSTR (1 << 31) /* Master/Slave Mode Select */
+#define MCR_CONT_SCKE (1 << 30) /* Continuous SCK Enable */
+#define MCR_FRZ (1 << 27) /* Freeze */
+#define MCR_PCSIS_S 16 /* Peripheral Chip Select */
+#define MCR_PCSIS_M 0x3f
+#define MCR_MDIS (1 << 14) /* Module Disable */
+#define MCR_CLR_TXF (1 << 11) /* Clear TX FIFO */
+#define MCR_CLR_RXF (1 << 10) /* Clear RX FIFO */
+#define MCR_HALT (1 << 0) /* Starts and stops SPI transfers */
+#define SPI_TCR 0x08 /* Transfer Count */
+#define SPI_CTAR0 0x0C /* Clock and Transfer Attributes */
+#define SPI_CTAR0_SLAVE 0x0C /* Clock and Transfer Attributes */
+#define SPI_CTAR1 0x10 /* Clock and Transfer Attributes */
+#define SPI_CTAR2 0x14 /* Clock and Transfer Attributes */
+#define SPI_CTAR3 0x18 /* Clock and Transfer Attributes */
+#define CTAR_FMSZ_M 0xf
+#define CTAR_FMSZ_S 27 /* Frame Size */
+#define CTAR_FMSZ_8 0x7 /* 8 bits */
+#define CTAR_CPOL (1 << 26) /* Clock Polarity */
+#define CTAR_CPHA (1 << 25) /* Clock Phase */
+#define CTAR_LSBFE (1 << 24) /* Less significant bit first */
+#define CTAR_PCSSCK_M 0x3
+#define CTAR_PCSSCK_S 22 /* PCS to SCK Delay Prescaler */
+#define CTAR_PBR_M 0x3
+#define CTAR_PBR_S 16 /* Baud Rate Prescaler */
+#define CTAR_PBR_7 0x3 /* Divide by 7 */
+#define CTAR_CSSCK_M 0xf
+#define CTAR_CSSCK_S 12 /* PCS to SCK Delay Scaler */
+#define CTAR_BR_M 0xf
+#define CTAR_BR_S 0 /* Baud Rate Scaler */
+#define SPI_SR 0x2C /* Status Register */
+#define SR_TCF (1 << 31) /* Transfer Complete Flag */
+#define SR_EOQF (1 << 28) /* End of Queue Flag */
+#define SR_TFFF (1 << 25) /* Transmit FIFO Fill Flag */
+#define SR_RFDF (1 << 17) /* Receive FIFO Drain Flag */
+#define SPI_RSER 0x30 /* DMA/Interrupt Select */
+#define RSER_EOQF_RE (1 << 28) /* Finished Request Enable */
+#define SPI_PUSHR 0x34 /* PUSH TX FIFO In Master Mode */
+#define PUSHR_CONT (1 << 31) /* Continuous Peripheral CS */
+#define PUSHR_EOQ (1 << 27) /* End Of Queue */
+#define PUSHR_CTCNT (1 << 26) /* Clear Transfer Counter */
+#define PUSHR_PCS_M 0x3f
+#define PUSHR_PCS_S 16 /* Select PCS signals */
+
+#define SPI_PUSHR_SLAVE 0x34 /* PUSH TX FIFO Register In Slave Mode */
+#define SPI_POPR 0x38 /* POP RX FIFO Register */
+#define SPI_TXFR0 0x3C /* Transmit FIFO Registers */
+#define SPI_TXFR1 0x40
+#define SPI_TXFR2 0x44
+#define SPI_TXFR3 0x48
+#define SPI_RXFR0 0x7C /* Receive FIFO Registers */
+#define SPI_RXFR1 0x80
+#define SPI_RXFR2 0x84
+#define SPI_RXFR3 0x88
+
+struct spi_softc {
+ struct resource *res[2];
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+ void *ih;
+};
+
+static struct resource_spec spi_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+static int
+spi_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "fsl,mvf600-spi"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Vybrid Family Serial Peripheral Interface");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+spi_attach(device_t dev)
+{
+ struct spi_softc *sc;
+ uint32_t reg;
+
+ sc = device_get_softc(dev);
+
+ if (bus_alloc_resources(dev, spi_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ /* Memory interface */
+ sc->bst = rman_get_bustag(sc->res[0]);
+ sc->bsh = rman_get_bushandle(sc->res[0]);
+
+ reg = READ4(sc, SPI_MCR);
+ reg |= MCR_MSTR;
+ reg &= ~(MCR_CONT_SCKE | MCR_MDIS | MCR_FRZ);
+ reg &= ~(MCR_PCSIS_M << MCR_PCSIS_S);
+ reg |= (MCR_PCSIS_M << MCR_PCSIS_S); /* PCS Active low */
+ reg |= (MCR_CLR_TXF | MCR_CLR_RXF);
+ WRITE4(sc, SPI_MCR, reg);
+
+ reg = READ4(sc, SPI_RSER);
+ reg |= RSER_EOQF_RE;
+ WRITE4(sc, SPI_RSER, reg);
+
+ reg = READ4(sc, SPI_MCR);
+ reg &= ~MCR_HALT;
+ WRITE4(sc, SPI_MCR, reg);
+
+ reg = READ4(sc, SPI_CTAR0);
+ reg &= ~(CTAR_FMSZ_M << CTAR_FMSZ_S);
+ reg |= (CTAR_FMSZ_8 << CTAR_FMSZ_S);
+ /*
+ * TODO: calculate BR
+ * SCK baud rate = ( fsys / PBR ) * (1 + DBR) / BR
+ *
+ * reg &= ~(CTAR_BR_M << CTAR_BR_S);
+ */
+ reg &= ~CTAR_CPOL; /* Polarity */
+ reg |= CTAR_CPHA;
+ /*
+ * Set LSB (Less significant bit first)
+ * must be used for some applications, e.g. some LCDs
+ */
+ reg |= CTAR_LSBFE;
+ WRITE4(sc, SPI_CTAR0, reg);
+
+ reg = READ4(sc, SPI_CTAR0);
+ reg &= ~(CTAR_PBR_M << CTAR_PBR_S);
+ reg |= (CTAR_PBR_7 << CTAR_PBR_S);
+ WRITE4(sc, SPI_CTAR0, reg);
+
+ device_add_child(dev, "spibus", 0);
+ return (bus_generic_attach(dev));
+}
+
+static int
+spi_txrx(struct spi_softc *sc, uint8_t *out_buf,
+ uint8_t *in_buf, int bufsz, int cs)
+{
+ uint32_t reg, wreg;
+ uint32_t txcnt;
+ uint32_t i;
+
+ txcnt = 0;
+
+ for (i = 0; i < bufsz; i++) {
+ txcnt++;
+ wreg = out_buf[i];
+ wreg |= PUSHR_CONT;
+ wreg |= (cs << PUSHR_PCS_S);
+ if (i == 0)
+ wreg |= PUSHR_CTCNT;
+ if (i == (bufsz - 1) || txcnt == SPI_FIFO_SIZE)
+ wreg |= PUSHR_EOQ;
+ WRITE4(sc, SPI_PUSHR, wreg);
+
+ if (i == (bufsz - 1) || txcnt == SPI_FIFO_SIZE) {
+ txcnt = 0;
+
+ /* Wait last entry in a queue to be transmitted */
+ while((READ4(sc, SPI_SR) & SR_EOQF) == 0)
+ continue;
+
+ reg = READ4(sc, SPI_SR);
+ reg |= (SR_TCF | SR_EOQF);
+ WRITE4(sc, SPI_SR, reg);
+ }
+
+ /* Wait until RX FIFO is empty */
+ while((READ4(sc, SPI_SR) & SR_RFDF) == 0)
+ continue;
+
+ in_buf[i] = READ1(sc, SPI_POPR);
+ }
+
+ return (0);
+}
+
+static int
+spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
+{
+ struct spi_softc *sc;
+ uint32_t cs;
+
+ sc = device_get_softc(dev);
+
+ KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz,
+ ("%s: TX/RX command sizes should be equal", __func__));
+ KASSERT(cmd->tx_data_sz == cmd->rx_data_sz,
+ ("%s: TX/RX data sizes should be equal", __func__));
+
+ /* get the proper chip select */
+ spibus_get_cs(child, &cs);
+
+ cs &= ~SPIBUS_CS_HIGH;
+
+ /* Command */
+ spi_txrx(sc, cmd->tx_cmd, cmd->rx_cmd, cmd->tx_cmd_sz, cs);
+
+ /* Data */
+ spi_txrx(sc, cmd->tx_data, cmd->rx_data, cmd->tx_data_sz, cs);
+
+ return (0);
+}
+
+static device_method_t spi_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, spi_probe),
+ DEVMETHOD(device_attach, spi_attach),
+ /* SPI interface */
+ DEVMETHOD(spibus_transfer, spi_transfer),
+ { 0, 0 }
+};
+
+static driver_t spi_driver = {
+ "spi",
+ spi_methods,
+ sizeof(struct spi_softc),
+};
+
+static devclass_t spi_devclass;
+
+DRIVER_MODULE(spi, simplebus, spi_driver, spi_devclass, 0, 0);
diff --git a/sys/arm/freescale/vybrid/vf_src.c b/sys/arm/freescale/vybrid/vf_src.c
new file mode 100644
index 000000000000..bd1467819e2c
--- /dev/null
+++ b/sys/arm/freescale/vybrid/vf_src.c
@@ -0,0 +1,150 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Vybrid Family System Reset Controller (SRC)
+ * Chapter 18, Vybrid Reference Manual, Rev. 5, 07/2013
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <arm/freescale/vybrid/vf_src.h>
+#include <arm/freescale/vybrid/vf_common.h>
+
+#define SRC_SCR 0x00 /* SRC Control Register */
+#define SRC_SBMR1 0x04 /* SRC Boot Mode Register 1 */
+#define SRC_SRSR 0x08 /* SRC Status Register */
+#define SRC_SECR 0x0C /* SRC_SECR */
+#define SRC_SICR 0x14 /* SRC Reset Interrupt Configuration Register */
+#define SRC_SIMR 0x18 /* SRC Interrupt Masking Register */
+#define SRC_SBMR2 0x1C /* SRC Boot Mode Register 2 */
+#define SRC_GPR0 0x20 /* General Purpose Register */
+#define SRC_GPR1 0x24 /* General Purpose Register */
+#define SRC_GPR2 0x28 /* General Purpose Register */
+#define SRC_GPR3 0x2C /* General Purpose Register */
+#define SRC_GPR4 0x30 /* General Purpose Register */
+#define SRC_MISC0 0x4C /* MISC0 */
+#define SRC_MISC1 0x50 /* MISC1 */
+#define SRC_MISC2 0x54 /* MISC2 */
+#define SRC_MISC3 0x58 /* MISC3 */
+
+struct src_softc {
+ struct resource *res[1];
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+};
+
+struct src_softc *src_sc;
+
+static struct resource_spec src_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+int
+src_swreset(void)
+{
+
+ if (src_sc == NULL)
+ return (1);
+
+ WRITE4(src_sc, SRC_SCR, SW_RST);
+
+ return (0);
+}
+
+static int
+src_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "fsl,mvf600-src"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Vybrid Family System Reset Controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+src_attach(device_t dev)
+{
+ struct src_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (bus_alloc_resources(dev, src_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ /* Memory interface */
+ sc->bst = rman_get_bustag(sc->res[0]);
+ sc->bsh = rman_get_bushandle(sc->res[0]);
+
+ src_sc = sc;
+
+ return (0);
+}
+
+static device_method_t src_methods[] = {
+ DEVMETHOD(device_probe, src_probe),
+ DEVMETHOD(device_attach, src_attach),
+ { 0, 0 }
+};
+
+static driver_t src_driver = {
+ "src",
+ src_methods,
+ sizeof(struct src_softc),
+};
+
+static devclass_t src_devclass;
+
+DRIVER_MODULE(src, simplebus, src_driver, src_devclass, 0, 0);
diff --git a/sys/arm/freescale/vybrid/vf_src.h b/sys/arm/freescale/vybrid/vf_src.h
new file mode 100644
index 000000000000..09bcfb2b4d06
--- /dev/null
+++ b/sys/arm/freescale/vybrid/vf_src.h
@@ -0,0 +1,32 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define SW_RST (1 << 12) /* Software reset */
+int src_swreset(void);
diff --git a/sys/arm/freescale/vybrid/vf_tcon.c b/sys/arm/freescale/vybrid/vf_tcon.c
new file mode 100644
index 000000000000..475f86271c45
--- /dev/null
+++ b/sys/arm/freescale/vybrid/vf_tcon.c
@@ -0,0 +1,138 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Vybrid Family Timing Controller (TCON)
+ * Chapter 58, Vybrid Reference Manual, Rev. 5, 07/2013
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <arm/freescale/vybrid/vf_common.h>
+
+#define TCON0_CTRL1 0x00
+#define TCON_BYPASS (1 << 29)
+
+struct tcon_softc {
+ struct resource *res[1];
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+};
+
+struct tcon_softc *tcon_sc;
+
+static struct resource_spec tcon_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+uint32_t
+tcon_bypass(void)
+{
+ struct tcon_softc *sc;
+
+ if (tcon_sc == NULL)
+ return (1);
+
+ sc = tcon_sc;
+
+ WRITE4(tcon_sc, TCON0_CTRL1, TCON_BYPASS);
+
+ return (0);
+}
+
+static int
+tcon_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "fsl,mvf600-tcon"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Vybrid Family Timing Controller (TCON)");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+tcon_attach(device_t dev)
+{
+ struct tcon_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (bus_alloc_resources(dev, tcon_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ /* Memory interface */
+ sc->bst = rman_get_bustag(sc->res[0]);
+ sc->bsh = rman_get_bushandle(sc->res[0]);
+
+ tcon_sc = sc;
+
+ return (0);
+}
+
+static device_method_t tcon_methods[] = {
+ DEVMETHOD(device_probe, tcon_probe),
+ DEVMETHOD(device_attach, tcon_attach),
+ { 0, 0 }
+};
+
+static driver_t tcon_driver = {
+ "tcon",
+ tcon_methods,
+ sizeof(struct tcon_softc),
+};
+
+static devclass_t tcon_devclass;
+
+DRIVER_MODULE(tcon, simplebus, tcon_driver, tcon_devclass, 0, 0);
diff --git a/sys/arm/freescale/vybrid/vf_uart.c b/sys/arm/freescale/vybrid/vf_uart.c
new file mode 100644
index 000000000000..50a0f9104e28
--- /dev/null
+++ b/sys/arm/freescale/vybrid/vf_uart.c
@@ -0,0 +1,518 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Vybrid Family Universal Asynchronous Receiver/Transmitter
+ * Chapter 49, Vybrid Reference Manual, Rev. 5, 07/2013
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_ddb.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kdb.h>
+#include <machine/bus.h>
+
+#include <dev/uart/uart.h>
+#include <dev/uart/uart_cpu.h>
+#include <dev/uart/uart_cpu_fdt.h>
+#include <dev/uart/uart_bus.h>
+
+#include "uart_if.h"
+
+#define UART_BDH 0x00 /* Baud Rate Registers: High */
+#define UART_BDL 0x01 /* Baud Rate Registers: Low */
+#define UART_C1 0x02 /* Control Register 1 */
+#define UART_C2 0x03 /* Control Register 2 */
+#define UART_S1 0x04 /* Status Register 1 */
+#define UART_S2 0x05 /* Status Register 2 */
+#define UART_C3 0x06 /* Control Register 3 */
+#define UART_D 0x07 /* Data Register */
+#define UART_MA1 0x08 /* Match Address Registers 1 */
+#define UART_MA2 0x09 /* Match Address Registers 2 */
+#define UART_C4 0x0A /* Control Register 4 */
+#define UART_C5 0x0B /* Control Register 5 */
+#define UART_ED 0x0C /* Extended Data Register */
+#define UART_MODEM 0x0D /* Modem Register */
+#define UART_IR 0x0E /* Infrared Register */
+#define UART_PFIFO 0x10 /* FIFO Parameters */
+#define UART_CFIFO 0x11 /* FIFO Control Register */
+#define UART_SFIFO 0x12 /* FIFO Status Register */
+#define UART_TWFIFO 0x13 /* FIFO Transmit Watermark */
+#define UART_TCFIFO 0x14 /* FIFO Transmit Count */
+#define UART_RWFIFO 0x15 /* FIFO Receive Watermark */
+#define UART_RCFIFO 0x16 /* FIFO Receive Count */
+#define UART_C7816 0x18 /* 7816 Control Register */
+#define UART_IE7816 0x19 /* 7816 Interrupt Enable Register */
+#define UART_IS7816 0x1A /* 7816 Interrupt Status Register */
+#define UART_WP7816T0 0x1B /* 7816 Wait Parameter Register */
+#define UART_WP7816T1 0x1B /* 7816 Wait Parameter Register */
+#define UART_WN7816 0x1C /* 7816 Wait N Register */
+#define UART_WF7816 0x1D /* 7816 Wait FD Register */
+#define UART_ET7816 0x1E /* 7816 Error Threshold Register */
+#define UART_TL7816 0x1F /* 7816 Transmit Length Register */
+#define UART_C6 0x21 /* CEA709.1-B Control Register 6 */
+#define UART_PCTH 0x22 /* CEA709.1-B Packet Cycle Time Counter High */
+#define UART_PCTL 0x23 /* CEA709.1-B Packet Cycle Time Counter Low */
+#define UART_B1T 0x24 /* CEA709.1-B Beta1 Timer */
+#define UART_SDTH 0x25 /* CEA709.1-B Secondary Delay Timer High */
+#define UART_SDTL 0x26 /* CEA709.1-B Secondary Delay Timer Low */
+#define UART_PRE 0x27 /* CEA709.1-B Preamble */
+#define UART_TPL 0x28 /* CEA709.1-B Transmit Packet Length */
+#define UART_IE 0x29 /* CEA709.1-B Interrupt Enable Register */
+#define UART_WB 0x2A /* CEA709.1-B WBASE */
+#define UART_S3 0x2B /* CEA709.1-B Status Register */
+#define UART_S4 0x2C /* CEA709.1-B Status Register */
+#define UART_RPL 0x2D /* CEA709.1-B Received Packet Length */
+#define UART_RPREL 0x2E /* CEA709.1-B Received Preamble Length */
+#define UART_CPW 0x2F /* CEA709.1-B Collision Pulse Width */
+#define UART_RIDT 0x30 /* CEA709.1-B Receive Indeterminate Time */
+#define UART_TIDT 0x31 /* CEA709.1-B Transmit Indeterminate Time */
+
+#define UART_C2_TE (1 << 3) /* Transmitter Enable */
+#define UART_C2_TIE (1 << 7) /* Transmitter Interrupt Enable */
+#define UART_C2_RE (1 << 2) /* Receiver Enable */
+#define UART_C2_RIE (1 << 5) /* Receiver Interrupt Enable */
+#define UART_S1_TDRE (1 << 7) /* Transmit Data Register Empty Flag */
+#define UART_S1_RDRF (1 << 5) /* Receive Data Register Full Flag */
+#define UART_S2_LBKDIF (1 << 7) /* LIN Break Detect Interrupt Flag */
+
+#define UART_C4_BRFA 0x1f /* Baud Rate Fine Adjust */
+#define UART_BDH_SBR 0x1f /* UART Baud Rate Bits */
+
+/*
+ * Low-level UART interface.
+ */
+static int vf_uart_probe(struct uart_bas *bas);
+static void vf_uart_init(struct uart_bas *bas, int, int, int, int);
+static void vf_uart_term(struct uart_bas *bas);
+static void vf_uart_putc(struct uart_bas *bas, int);
+static int vf_uart_rxready(struct uart_bas *bas);
+static int vf_uart_getc(struct uart_bas *bas, struct mtx *);
+
+void uart_reinit(struct uart_softc *,int,int);
+
+static struct uart_ops uart_vybrid_ops = {
+ .probe = vf_uart_probe,
+ .init = vf_uart_init,
+ .term = vf_uart_term,
+ .putc = vf_uart_putc,
+ .rxready = vf_uart_rxready,
+ .getc = vf_uart_getc,
+};
+
+static int
+vf_uart_probe(struct uart_bas *bas)
+{
+
+ return (0);
+}
+
+static void
+vf_uart_init(struct uart_bas *bas, int baudrate, int databits,
+ int stopbits, int parity)
+{
+
+}
+
+static void
+vf_uart_term(struct uart_bas *bas)
+{
+
+}
+
+static void
+vf_uart_putc(struct uart_bas *bas, int c)
+{
+
+ while (!(uart_getreg(bas, UART_S1) & UART_S1_TDRE))
+ ;
+
+ uart_setreg(bas, UART_D, c);
+}
+
+static int
+vf_uart_rxready(struct uart_bas *bas)
+{
+ int usr1;
+
+ usr1 = uart_getreg(bas, UART_S1);
+ if (usr1 & UART_S1_RDRF) {
+ return (1);
+ }
+
+ return (0);
+}
+
+static int
+vf_uart_getc(struct uart_bas *bas, struct mtx *hwmtx)
+{
+ int c;
+
+ uart_lock(hwmtx);
+
+ while (!(uart_getreg(bas, UART_S1) & UART_S1_RDRF))
+ ;
+
+ c = uart_getreg(bas, UART_D);
+ uart_unlock(hwmtx);
+
+ return (c & 0xff);
+}
+
+/*
+ * High-level UART interface.
+ */
+struct vf_uart_softc {
+ struct uart_softc base;
+};
+
+void
+uart_reinit(struct uart_softc *sc, int clkspeed, int baud)
+{
+ struct uart_bas *bas;
+ int sbr;
+ int brfa;
+ int reg;
+
+ bas = &sc->sc_bas;
+ if (!bas) {
+ printf("Error: can't reconfigure bas\n");
+ return;
+ }
+
+ uart_setreg(bas, UART_MODEM, 0x00);
+
+ /*
+ * Disable transmitter and receiver
+ * for a while.
+ */
+ reg = uart_getreg(bas, UART_C2);
+ reg &= ~(UART_C2_RE | UART_C2_TE);
+ uart_setreg(bas, UART_C2, 0x00);
+
+ uart_setreg(bas, UART_C1, 0x00);
+
+ sbr = (uint16_t) (clkspeed / (baud * 16));
+ brfa = (clkspeed / baud) - (sbr * 16);
+
+ reg = uart_getreg(bas, UART_BDH);
+ reg &= ~UART_BDH_SBR;
+ reg |= ((sbr & 0x1f00) >> 8);
+ uart_setreg(bas, UART_BDH, reg);
+
+ reg = sbr & 0x00ff;
+ uart_setreg(bas, UART_BDL, reg);
+
+ reg = uart_getreg(bas, UART_C4);
+ reg &= ~UART_C4_BRFA;
+ reg |= (brfa & UART_C4_BRFA);
+ uart_setreg(bas, UART_C4, reg);
+
+ reg = uart_getreg(bas, UART_C2);
+ reg |= (UART_C2_RE | UART_C2_TE);
+ uart_setreg(bas, UART_C2, reg);
+
+}
+
+static int vf_uart_bus_attach(struct uart_softc *);
+static int vf_uart_bus_detach(struct uart_softc *);
+static int vf_uart_bus_flush(struct uart_softc *, int);
+static int vf_uart_bus_getsig(struct uart_softc *);
+static int vf_uart_bus_ioctl(struct uart_softc *, int, intptr_t);
+static int vf_uart_bus_ipend(struct uart_softc *);
+static int vf_uart_bus_param(struct uart_softc *, int, int, int, int);
+static int vf_uart_bus_probe(struct uart_softc *);
+static int vf_uart_bus_receive(struct uart_softc *);
+static int vf_uart_bus_setsig(struct uart_softc *, int);
+static int vf_uart_bus_transmit(struct uart_softc *);
+
+static kobj_method_t vf_uart_methods[] = {
+ KOBJMETHOD(uart_attach, vf_uart_bus_attach),
+ KOBJMETHOD(uart_detach, vf_uart_bus_detach),
+ KOBJMETHOD(uart_flush, vf_uart_bus_flush),
+ KOBJMETHOD(uart_getsig, vf_uart_bus_getsig),
+ KOBJMETHOD(uart_ioctl, vf_uart_bus_ioctl),
+ KOBJMETHOD(uart_ipend, vf_uart_bus_ipend),
+ KOBJMETHOD(uart_param, vf_uart_bus_param),
+ KOBJMETHOD(uart_probe, vf_uart_bus_probe),
+ KOBJMETHOD(uart_receive, vf_uart_bus_receive),
+ KOBJMETHOD(uart_setsig, vf_uart_bus_setsig),
+ KOBJMETHOD(uart_transmit, vf_uart_bus_transmit),
+ { 0, 0 }
+};
+
+static struct uart_class uart_vybrid_class = {
+ "vybrid",
+ vf_uart_methods,
+ sizeof(struct vf_uart_softc),
+ .uc_ops = &uart_vybrid_ops,
+ .uc_range = 0x100,
+ .uc_rclk = 24000000, /* TODO: get value from CCM */
+ .uc_rshift = 0
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"fsl,mvf600-uart", (uintptr_t)&uart_vybrid_class},
+ {NULL, (uintptr_t)NULL},
+};
+UART_FDT_CLASS_AND_DEVICE(compat_data);
+
+static int
+vf_uart_bus_attach(struct uart_softc *sc)
+{
+ struct uart_bas *bas;
+ int reg;
+
+ bas = &sc->sc_bas;
+
+ sc->sc_hwiflow = 0;
+ sc->sc_hwoflow = 0;
+
+ uart_reinit(sc, 66000000, 115200);
+
+ reg = uart_getreg(bas, UART_C2);
+ if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE) {
+ reg &= ~UART_C2_RIE;
+ } else {
+ reg |= UART_C2_RIE;
+ }
+ uart_setreg(bas, UART_C2, reg);
+
+ return (0);
+}
+
+static int
+vf_uart_bus_detach(struct uart_softc *sc)
+{
+
+ /* TODO */
+ return (0);
+}
+
+static int
+vf_uart_bus_flush(struct uart_softc *sc, int what)
+{
+
+ /* TODO */
+ return (0);
+}
+
+static int
+vf_uart_bus_getsig(struct uart_softc *sc)
+{
+
+ /* TODO */
+ return (0);
+}
+
+static int
+vf_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
+{
+ struct uart_bas *bas;
+ int error;
+
+ bas = &sc->sc_bas;
+ error = 0;
+ uart_lock(sc->sc_hwmtx);
+ switch (request) {
+ case UART_IOCTL_BREAK:
+ /* TODO */
+ break;
+ case UART_IOCTL_BAUD:
+ /* TODO */
+ *(int*)data = 115200;
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ uart_unlock(sc->sc_hwmtx);
+
+ return (error);
+}
+
+static int
+vf_uart_bus_ipend(struct uart_softc *sc)
+{
+ struct uart_bas *bas;
+ int ipend;
+ uint32_t usr1, usr2;
+ int reg;
+ int sfifo;
+
+ bas = &sc->sc_bas;
+ ipend = 0;
+
+ uart_lock(sc->sc_hwmtx);
+
+ usr1 = uart_getreg(bas, UART_S1);
+ usr2 = uart_getreg(bas, UART_S2);
+ sfifo = uart_getreg(bas, UART_SFIFO);
+
+ /* ack usr2 */
+ uart_setreg(bas, UART_S2, usr2);
+
+ if (usr1 & UART_S1_TDRE) {
+ reg = uart_getreg(bas, UART_C2);
+ reg &= ~(UART_C2_TIE);
+ uart_setreg(bas, UART_C2, reg);
+
+ if (sc->sc_txbusy != 0) {
+ ipend |= SER_INT_TXIDLE;
+ }
+ }
+
+ if (usr1 & UART_S1_RDRF) {
+ reg = uart_getreg(bas, UART_C2);
+ reg &= ~(UART_C2_RIE);
+ uart_setreg(bas, UART_C2, reg);
+
+ ipend |= SER_INT_RXREADY;
+ }
+
+ if (usr2 & UART_S2_LBKDIF) {
+ ipend |= SER_INT_BREAK;
+ }
+
+ uart_unlock(sc->sc_hwmtx);
+
+ return (ipend);
+}
+
+static int
+vf_uart_bus_param(struct uart_softc *sc, int baudrate, int databits,
+ int stopbits, int parity)
+{
+
+ uart_lock(sc->sc_hwmtx);
+ vf_uart_init(&sc->sc_bas, baudrate, databits, stopbits, parity);
+ uart_unlock(sc->sc_hwmtx);
+
+ return (0);
+}
+
+static int
+vf_uart_bus_probe(struct uart_softc *sc)
+{
+ int error;
+
+ error = vf_uart_probe(&sc->sc_bas);
+ if (error)
+ return (error);
+
+ sc->sc_rxfifosz = 1;
+ sc->sc_txfifosz = 1;
+
+ device_set_desc(sc->sc_dev, "Vybrid Family UART");
+ return (0);
+}
+
+static int
+vf_uart_bus_receive(struct uart_softc *sc)
+{
+ struct uart_bas *bas;
+ int reg;
+ int c;
+
+ bas = &sc->sc_bas;
+ uart_lock(sc->sc_hwmtx);
+
+ /* Read FIFO */
+ while (uart_getreg(bas, UART_S1) & UART_S1_RDRF) {
+ if (uart_rx_full(sc)) {
+ /* No space left in input buffer */
+ sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN;
+ break;
+ }
+
+ c = uart_getreg(bas, UART_D);
+ uart_rx_put(sc, c);
+ }
+
+ /* Reenable Data Ready interrupt */
+ reg = uart_getreg(bas, UART_C2);
+ reg |= (UART_C2_RIE);
+ uart_setreg(bas, UART_C2, reg);
+
+ uart_unlock(sc->sc_hwmtx);
+ return (0);
+}
+
+static int
+vf_uart_bus_setsig(struct uart_softc *sc, int sig)
+{
+ struct uart_bas *bas;
+ int reg;
+
+ /* TODO: implement (?) */
+
+ /* XXX workaround to have working console on mount prompt */
+ /* Enable RX interrupt */
+ bas = &sc->sc_bas;
+ if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE) {
+ reg = uart_getreg(bas, UART_C2);
+ reg |= (UART_C2_RIE);
+ uart_setreg(bas, UART_C2, reg);
+ }
+
+ return (0);
+}
+
+static int
+vf_uart_bus_transmit(struct uart_softc *sc)
+{
+ struct uart_bas *bas = &sc->sc_bas;
+ int i;
+ int reg;
+
+ bas = &sc->sc_bas;
+ uart_lock(sc->sc_hwmtx);
+
+ /* Fill TX FIFO */
+ for (i = 0; i < sc->sc_txdatasz; i++) {
+ uart_setreg(bas, UART_D, sc->sc_txbuf[i] & 0xff);
+ uart_barrier(&sc->sc_bas);
+ }
+
+ sc->sc_txbusy = 1;
+
+ /* Call me when ready */
+ reg = uart_getreg(bas, UART_C2);
+ reg |= (UART_C2_TIE);
+ uart_setreg(bas, UART_C2, reg);
+
+ uart_unlock(sc->sc_hwmtx);
+
+ return (0);
+}
diff --git a/sys/arm/include/_align.h b/sys/arm/include/_align.h
new file mode 100644
index 000000000000..a8c6df4304f8
--- /dev/null
+++ b/sys/arm/include/_align.h
@@ -0,0 +1,54 @@
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 2001 David E. O'Brien
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)param.h 5.8 (Berkeley) 6/28/91
+ * $FreeBSD$
+ */
+
+#ifndef _ARM_INCLUDE__ALIGN_H_
+#define _ARM_INCLUDE__ALIGN_H_
+
+/*
+ * Round p (pointer or byte index) up to the hardware-required alignment which
+ * is sufficient for any data type, pointer or numeric. The resulting type
+ * is equivelent to arm's uintptr_t (but is purposely spelled "unsigned" here).
+ */
+#define _ALIGNBYTES (sizeof(int) - 1)
+#define _ALIGN(p) (((unsigned)(p) + _ALIGNBYTES) & ~_ALIGNBYTES)
+
+#endif /* !_ARM_INCLUDE__ALIGN_H_ */
diff --git a/sys/arm/include/_bus.h b/sys/arm/include/_bus.h
new file mode 100644
index 000000000000..803dda2e2763
--- /dev/null
+++ b/sys/arm/include/_bus.h
@@ -0,0 +1,47 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2005 M. Warner Losh <imp@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef ARM_INCLUDE__BUS_H
+#define ARM_INCLUDE__BUS_H
+
+/*
+ * Addresses (in bus space).
+ */
+typedef u_long bus_addr_t;
+typedef u_long bus_size_t;
+
+/*
+ * Access methods for bus space.
+ */
+typedef struct bus_space *bus_space_tag_t;
+typedef u_long bus_space_handle_t;
+
+#endif /* ARM_INCLUDE__BUS_H */
diff --git a/sys/arm/include/_inttypes.h b/sys/arm/include/_inttypes.h
new file mode 100644
index 000000000000..d8c1ce8a6cb9
--- /dev/null
+++ b/sys/arm/include/_inttypes.h
@@ -0,0 +1,215 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-NetBSD
+ *
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Klaus Klein.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * From: $NetBSD: int_fmtio.h,v 1.2 2001/04/26 16:25:21 kleink Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_INTTYPES_H_
+#define _MACHINE_INTTYPES_H_
+
+/*
+ * Macros for format specifiers.
+ */
+
+/* fprintf(3) macros for signed integers. */
+
+#define PRId8 "d" /* int8_t */
+#define PRId16 "d" /* int16_t */
+#define PRId32 "d" /* int32_t */
+#define PRId64 "lld" /* int64_t */
+#define PRIdLEAST8 "d" /* int_least8_t */
+#define PRIdLEAST16 "d" /* int_least16_t */
+#define PRIdLEAST32 "d" /* int_least32_t */
+#define PRIdLEAST64 "lld" /* int_least64_t */
+#define PRIdFAST8 "d" /* int_fast8_t */
+#define PRIdFAST16 "d" /* int_fast16_t */
+#define PRIdFAST32 "d" /* int_fast32_t */
+#define PRIdFAST64 "lld" /* int_fast64_t */
+#define PRIdMAX "jd" /* intmax_t */
+#define PRIdPTR "d" /* intptr_t */
+
+#define PRIi8 "i" /* int8_t */
+#define PRIi16 "i" /* int16_t */
+#define PRIi32 "i" /* int32_t */
+#define PRIi64 "lli" /* int64_t */
+#define PRIiLEAST8 "i" /* int_least8_t */
+#define PRIiLEAST16 "i" /* int_least16_t */
+#define PRIiLEAST32 "i" /* int_least32_t */
+#define PRIiLEAST64 "lli" /* int_least64_t */
+#define PRIiFAST8 "i" /* int_fast8_t */
+#define PRIiFAST16 "i" /* int_fast16_t */
+#define PRIiFAST32 "i" /* int_fast32_t */
+#define PRIiFAST64 "lli" /* int_fast64_t */
+#define PRIiMAX "ji" /* intmax_t */
+#define PRIiPTR "i" /* intptr_t */
+
+/* fprintf(3) macros for unsigned integers. */
+
+#define PRIo8 "o" /* uint8_t */
+#define PRIo16 "o" /* uint16_t */
+#define PRIo32 "o" /* uint32_t */
+#define PRIo64 "llo" /* uint64_t */
+#define PRIoLEAST8 "o" /* uint_least8_t */
+#define PRIoLEAST16 "o" /* uint_least16_t */
+#define PRIoLEAST32 "o" /* uint_least32_t */
+#define PRIoLEAST64 "llo" /* uint_least64_t */
+#define PRIoFAST8 "o" /* uint_fast8_t */
+#define PRIoFAST16 "o" /* uint_fast16_t */
+#define PRIoFAST32 "o" /* uint_fast32_t */
+#define PRIoFAST64 "llo" /* uint_fast64_t */
+#define PRIoMAX "jo" /* uintmax_t */
+#define PRIoPTR "o" /* uintptr_t */
+
+#define PRIu8 "u" /* uint8_t */
+#define PRIu16 "u" /* uint16_t */
+#define PRIu32 "u" /* uint32_t */
+#define PRIu64 "llu" /* uint64_t */
+#define PRIuLEAST8 "u" /* uint_least8_t */
+#define PRIuLEAST16 "u" /* uint_least16_t */
+#define PRIuLEAST32 "u" /* uint_least32_t */
+#define PRIuLEAST64 "llu" /* uint_least64_t */
+#define PRIuFAST8 "u" /* uint_fast8_t */
+#define PRIuFAST16 "u" /* uint_fast16_t */
+#define PRIuFAST32 "u" /* uint_fast32_t */
+#define PRIuFAST64 "llu" /* uint_fast64_t */
+#define PRIuMAX "ju" /* uintmax_t */
+#define PRIuPTR "u" /* uintptr_t */
+
+#define PRIx8 "x" /* uint8_t */
+#define PRIx16 "x" /* uint16_t */
+#define PRIx32 "x" /* uint32_t */
+#define PRIx64 "llx" /* uint64_t */
+#define PRIxLEAST8 "x" /* uint_least8_t */
+#define PRIxLEAST16 "x" /* uint_least16_t */
+#define PRIxLEAST32 "x" /* uint_least32_t */
+#define PRIxLEAST64 "llx" /* uint_least64_t */
+#define PRIxFAST8 "x" /* uint_fast8_t */
+#define PRIxFAST16 "x" /* uint_fast16_t */
+#define PRIxFAST32 "x" /* uint_fast32_t */
+#define PRIxFAST64 "llx" /* uint_fast64_t */
+#define PRIxMAX "jx" /* uintmax_t */
+#define PRIxPTR "x" /* uintptr_t */
+
+#define PRIX8 "X" /* uint8_t */
+#define PRIX16 "X" /* uint16_t */
+#define PRIX32 "X" /* uint32_t */
+#define PRIX64 "llX" /* uint64_t */
+#define PRIXLEAST8 "X" /* uint_least8_t */
+#define PRIXLEAST16 "X" /* uint_least16_t */
+#define PRIXLEAST32 "X" /* uint_least32_t */
+#define PRIXLEAST64 "llX" /* uint_least64_t */
+#define PRIXFAST8 "X" /* uint_fast8_t */
+#define PRIXFAST16 "X" /* uint_fast16_t */
+#define PRIXFAST32 "X" /* uint_fast32_t */
+#define PRIXFAST64 "llX" /* uint_fast64_t */
+#define PRIXMAX "jX" /* uintmax_t */
+#define PRIXPTR "X" /* uintptr_t */
+
+/* fscanf(3) macros for signed integers. */
+
+#define SCNd8 "hhd" /* int8_t */
+#define SCNd16 "hd" /* int16_t */
+#define SCNd32 "d" /* int32_t */
+#define SCNd64 "lld" /* int64_t */
+#define SCNdLEAST8 "hhd" /* int_least8_t */
+#define SCNdLEAST16 "hd" /* int_least16_t */
+#define SCNdLEAST32 "d" /* int_least32_t */
+#define SCNdLEAST64 "lld" /* int_least64_t */
+#define SCNdFAST8 "d" /* int_fast8_t */
+#define SCNdFAST16 "d" /* int_fast16_t */
+#define SCNdFAST32 "d" /* int_fast32_t */
+#define SCNdFAST64 "lld" /* int_fast64_t */
+#define SCNdMAX "jd" /* intmax_t */
+#define SCNdPTR "d" /* intptr_t */
+
+#define SCNi8 "hhi" /* int8_t */
+#define SCNi16 "hi" /* int16_t */
+#define SCNi32 "i" /* int32_t */
+#define SCNi64 "lli" /* int64_t */
+#define SCNiLEAST8 "hhi" /* int_least8_t */
+#define SCNiLEAST16 "hi" /* int_least16_t */
+#define SCNiLEAST32 "i" /* int_least32_t */
+#define SCNiLEAST64 "lli" /* int_least64_t */
+#define SCNiFAST8 "i" /* int_fast8_t */
+#define SCNiFAST16 "i" /* int_fast16_t */
+#define SCNiFAST32 "i" /* int_fast32_t */
+#define SCNiFAST64 "lli" /* int_fast64_t */
+#define SCNiMAX "ji" /* intmax_t */
+#define SCNiPTR "i" /* intptr_t */
+
+/* fscanf(3) macros for unsigned integers. */
+
+#define SCNo8 "hho" /* uint8_t */
+#define SCNo16 "ho" /* uint16_t */
+#define SCNo32 "o" /* uint32_t */
+#define SCNo64 "llo" /* uint64_t */
+#define SCNoLEAST8 "hho" /* uint_least8_t */
+#define SCNoLEAST16 "ho" /* uint_least16_t */
+#define SCNoLEAST32 "o" /* uint_least32_t */
+#define SCNoLEAST64 "llo" /* uint_least64_t */
+#define SCNoFAST8 "o" /* uint_fast8_t */
+#define SCNoFAST16 "o" /* uint_fast16_t */
+#define SCNoFAST32 "o" /* uint_fast32_t */
+#define SCNoFAST64 "llo" /* uint_fast64_t */
+#define SCNoMAX "jo" /* uintmax_t */
+#define SCNoPTR "o" /* uintptr_t */
+
+#define SCNu8 "hhu" /* uint8_t */
+#define SCNu16 "hu" /* uint16_t */
+#define SCNu32 "u" /* uint32_t */
+#define SCNu64 "llu" /* uint64_t */
+#define SCNuLEAST8 "hhu" /* uint_least8_t */
+#define SCNuLEAST16 "hu" /* uint_least16_t */
+#define SCNuLEAST32 "u" /* uint_least32_t */
+#define SCNuLEAST64 "llu" /* uint_least64_t */
+#define SCNuFAST8 "u" /* uint_fast8_t */
+#define SCNuFAST16 "u" /* uint_fast16_t */
+#define SCNuFAST32 "u" /* uint_fast32_t */
+#define SCNuFAST64 "llu" /* uint_fast64_t */
+#define SCNuMAX "ju" /* uintmax_t */
+#define SCNuPTR "u" /* uintptr_t */
+
+#define SCNx8 "hhx" /* uint8_t */
+#define SCNx16 "hx" /* uint16_t */
+#define SCNx32 "x" /* uint32_t */
+#define SCNx64 "llx" /* uint64_t */
+#define SCNxLEAST8 "hhx" /* uint_least8_t */
+#define SCNxLEAST16 "hx" /* uint_least16_t */
+#define SCNxLEAST32 "x" /* uint_least32_t */
+#define SCNxLEAST64 "llx" /* uint_least64_t */
+#define SCNxFAST8 "x" /* uint_fast8_t */
+#define SCNxFAST16 "x" /* uint_fast16_t */
+#define SCNxFAST32 "x" /* uint_fast32_t */
+#define SCNxFAST64 "llx" /* uint_fast64_t */
+#define SCNxMAX "jx" /* uintmax_t */
+#define SCNxPTR "x" /* uintptr_t */
+
+#endif /* !_MACHINE_INTTYPES_H_ */
diff --git a/sys/arm/include/_limits.h b/sys/arm/include/_limits.h
new file mode 100644
index 000000000000..fe816e50f9cb
--- /dev/null
+++ b/sys/arm/include/_limits.h
@@ -0,0 +1,89 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)limits.h 8.3 (Berkeley) 1/4/94
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE__LIMITS_H_
+#define _MACHINE__LIMITS_H_
+
+/*
+ * According to ANSI (section 2.2.4.2), the values below must be usable by
+ * #if preprocessing directives. Additionally, the expression must have the
+ * same type as would an expression that is an object of the corresponding
+ * type converted according to the integral promotions. The subtraction for
+ * INT_MIN, etc., is so the value is not unsigned; e.g., 0x80000000 is an
+ * unsigned int for 32-bit two's complement ANSI compilers (section 3.1.3.2).
+ */
+
+#define __CHAR_BIT 8 /* number of bits in a char */
+
+#define __SCHAR_MAX 0x7f /* max value for a signed char */
+#define __SCHAR_MIN (-0x7f - 1) /* min value for a signed char */
+
+#define __UCHAR_MAX 0xff /* max value for an unsigned char */
+
+#define __USHRT_MAX 0xffff /* max value for an unsigned short */
+#define __SHRT_MAX 0x7fff /* max value for a short */
+#define __SHRT_MIN (-0x7fff - 1) /* min value for a short */
+
+#define __UINT_MAX 0xffffffff /* max value for an unsigned int */
+#define __INT_MAX 0x7fffffff /* max value for an int */
+#define __INT_MIN (-0x7fffffff - 1) /* min value for an int */
+
+#define __ULONG_MAX 0xffffffffUL /* max value for an unsigned long */
+#define __LONG_MAX 0x7fffffffL /* max value for a long */
+#define __LONG_MIN (-0x7fffffffL - 1) /* min value for a long */
+
+ /* max value for an unsigned long long */
+#define __ULLONG_MAX 0xffffffffffffffffULL
+#define __LLONG_MAX 0x7fffffffffffffffLL /* max value for a long long */
+#define __LLONG_MIN (-0x7fffffffffffffffLL - 1) /* min for a long long */
+
+#define __SSIZE_MAX __INT_MAX /* max value for a ssize_t */
+
+#define __SIZE_T_MAX __UINT_MAX /* max value for a size_t */
+
+#define __OFF_MAX __LLONG_MAX /* max value for a off_t */
+#define __OFF_MIN __LLONG_MIN /* min value for a off_t */
+
+/* Quads and long longs are the same size. Ensure they stay in sync. */
+#define __UQUAD_MAX __ULLONG_MAX /* max value for a uquad_t */
+#define __QUAD_MAX __LLONG_MAX /* max value for a quad_t */
+#define __QUAD_MIN __LLONG_MIN /* min value for a quad_t */
+
+#define __LONG_BIT 32
+#define __WORD_BIT 32
+
+/* Minimum signal stack size. */
+#define __MINSIGSTKSZ (1024 * 4)
+
+#endif /* !_MACHINE__LIMITS_H_ */
diff --git a/sys/arm/include/_stdint.h b/sys/arm/include/_stdint.h
new file mode 100644
index 000000000000..2032b7368143
--- /dev/null
+++ b/sys/arm/include/_stdint.h
@@ -0,0 +1,160 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-NetBSD
+ *
+ * Copyright (c) 2001, 2002 Mike Barcroft <mike@FreeBSD.org>
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Klaus Klein.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE__STDINT_H_
+#define _MACHINE__STDINT_H_
+
+#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS)
+
+#define INT8_C(c) (c)
+#define INT16_C(c) (c)
+#define INT32_C(c) (c)
+#define INT64_C(c) (c ## LL)
+
+#define UINT8_C(c) (c)
+#define UINT16_C(c) (c)
+#define UINT32_C(c) (c ## U)
+#define UINT64_C(c) (c ## ULL)
+
+#define INTMAX_C(c) INT64_C(c)
+#define UINTMAX_C(c) UINT64_C(c)
+
+#endif /* !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) */
+
+#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS)
+
+/*
+ * ISO/IEC 9899:1999
+ * 7.18.2.1 Limits of exact-width integer types
+ */
+/* Minimum values of exact-width signed integer types. */
+#define INT8_MIN (-0x7f-1)
+#define INT16_MIN (-0x7fff-1)
+#define INT32_MIN (-0x7fffffff-1)
+#define INT64_MIN (-0x7fffffffffffffffLL-1)
+
+/* Maximum values of exact-width signed integer types. */
+#define INT8_MAX 0x7f
+#define INT16_MAX 0x7fff
+#define INT32_MAX 0x7fffffff
+#define INT64_MAX 0x7fffffffffffffffLL
+
+/* Maximum values of exact-width unsigned integer types. */
+#define UINT8_MAX 0xff
+#define UINT16_MAX 0xffff
+#define UINT32_MAX 0xffffffffU
+#define UINT64_MAX 0xffffffffffffffffULL
+
+/*
+ * ISO/IEC 9899:1999
+ * 7.18.2.2 Limits of minimum-width integer types
+ */
+/* Minimum values of minimum-width signed integer types. */
+#define INT_LEAST8_MIN INT8_MIN
+#define INT_LEAST16_MIN INT16_MIN
+#define INT_LEAST32_MIN INT32_MIN
+#define INT_LEAST64_MIN INT64_MIN
+
+/* Maximum values of minimum-width signed integer types. */
+#define INT_LEAST8_MAX INT8_MAX
+#define INT_LEAST16_MAX INT16_MAX
+#define INT_LEAST32_MAX INT32_MAX
+#define INT_LEAST64_MAX INT64_MAX
+
+/* Maximum values of minimum-width unsigned integer types. */
+#define UINT_LEAST8_MAX UINT8_MAX
+#define UINT_LEAST16_MAX UINT16_MAX
+#define UINT_LEAST32_MAX UINT32_MAX
+#define UINT_LEAST64_MAX UINT64_MAX
+
+/*
+ * ISO/IEC 9899:1999
+ * 7.18.2.3 Limits of fastest minimum-width integer types
+ */
+/* Minimum values of fastest minimum-width signed integer types. */
+#define INT_FAST8_MIN INT32_MIN
+#define INT_FAST16_MIN INT32_MIN
+#define INT_FAST32_MIN INT32_MIN
+#define INT_FAST64_MIN INT64_MIN
+
+/* Maximum values of fastest minimum-width signed integer types. */
+#define INT_FAST8_MAX INT32_MAX
+#define INT_FAST16_MAX INT32_MAX
+#define INT_FAST32_MAX INT32_MAX
+#define INT_FAST64_MAX INT64_MAX
+
+/* Maximum values of fastest minimum-width unsigned integer types. */
+#define UINT_FAST8_MAX UINT32_MAX
+#define UINT_FAST16_MAX UINT32_MAX
+#define UINT_FAST32_MAX UINT32_MAX
+#define UINT_FAST64_MAX UINT64_MAX
+
+/*
+ * ISO/IEC 9899:1999
+ * 7.18.2.4 Limits of integer types capable of holding object pointers
+ */
+#define INTPTR_MIN INT32_MIN
+#define INTPTR_MAX INT32_MAX
+#define UINTPTR_MAX UINT32_MAX
+
+/*
+ * ISO/IEC 9899:1999
+ * 7.18.2.5 Limits of greatest-width integer types
+ */
+#define INTMAX_MIN INT64_MIN
+#define INTMAX_MAX INT64_MAX
+#define UINTMAX_MAX UINT64_MAX
+
+/*
+ * ISO/IEC 9899:1999
+ * 7.18.3 Limits of other integer types
+ */
+/* Limits of ptrdiff_t. */
+#define PTRDIFF_MIN INT32_MIN
+#define PTRDIFF_MAX INT32_MAX
+
+/* Limits of sig_atomic_t. */
+#define SIG_ATOMIC_MIN INT32_MIN
+#define SIG_ATOMIC_MAX INT32_MAX
+
+/* Limit of size_t. */
+#define SIZE_MAX UINT32_MAX
+
+/* Limits of wint_t. */
+#define WINT_MIN INT32_MIN
+#define WINT_MAX INT32_MAX
+
+#endif /* !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) */
+
+#endif /* !_MACHINE__STDINT_H_ */
diff --git a/sys/arm/include/_types.h b/sys/arm/include/_types.h
new file mode 100644
index 000000000000..14ab76c5fbde
--- /dev/null
+++ b/sys/arm/include/_types.h
@@ -0,0 +1,114 @@
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 2002 Mike Barcroft <mike@FreeBSD.org>
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * From: @(#)ansi.h 8.2 (Berkeley) 1/4/94
+ * From: @(#)types.h 8.3 (Berkeley) 1/5/94
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE__TYPES_H_
+#define _MACHINE__TYPES_H_
+
+#ifndef _SYS_CDEFS_H_
+#error this file needs sys/cdefs.h as a prerequisite
+#endif
+
+/*
+ * Basic types upon which most other types are built.
+ */
+typedef signed char __int8_t;
+typedef unsigned char __uint8_t;
+typedef short __int16_t;
+typedef unsigned short __uint16_t;
+typedef int __int32_t;
+typedef unsigned int __uint32_t;
+#ifndef lint
+__extension__
+#endif
+/* LONGLONG */
+typedef long long __int64_t;
+#ifndef lint
+__extension__
+#endif
+/* LONGLONG */
+typedef unsigned long long __uint64_t;
+
+/*
+ * Standard type definitions.
+ */
+typedef __uint32_t __clock_t; /* clock()... */
+typedef __int32_t __critical_t;
+#ifndef _STANDALONE
+typedef double __double_t;
+typedef float __float_t;
+#endif
+typedef __int32_t __intfptr_t;
+typedef __int64_t __intmax_t;
+typedef __int32_t __intptr_t;
+typedef __int32_t __int_fast8_t;
+typedef __int32_t __int_fast16_t;
+typedef __int32_t __int_fast32_t;
+typedef __int64_t __int_fast64_t;
+typedef __int8_t __int_least8_t;
+typedef __int16_t __int_least16_t;
+typedef __int32_t __int_least32_t;
+typedef __int64_t __int_least64_t;
+typedef __int32_t __ptrdiff_t; /* ptr1 - ptr2 */
+typedef __int32_t __register_t;
+typedef __int32_t __segsz_t; /* segment size (in pages) */
+typedef __uint32_t __size_t; /* sizeof() */
+typedef __int32_t __ssize_t; /* byte count or error */
+typedef __int64_t __time_t; /* time()... */
+typedef __uint32_t __uintfptr_t;
+typedef __uint64_t __uintmax_t;
+typedef __uint32_t __uintptr_t;
+typedef __uint32_t __uint_fast8_t;
+typedef __uint32_t __uint_fast16_t;
+typedef __uint32_t __uint_fast32_t;
+typedef __uint64_t __uint_fast64_t;
+typedef __uint8_t __uint_least8_t;
+typedef __uint16_t __uint_least16_t;
+typedef __uint32_t __uint_least32_t;
+typedef __uint64_t __uint_least64_t;
+typedef __uint32_t __u_register_t;
+typedef __uint32_t __vm_offset_t;
+typedef __uint32_t __vm_paddr_t;
+typedef __uint32_t __vm_size_t;
+
+typedef unsigned int ___wchar_t;
+#define __WCHAR_MIN 0 /* min value for a wchar_t */
+#define __WCHAR_MAX __UINT_MAX /* max value for a wchar_t */
+
+#endif /* !_MACHINE__TYPES_H_ */
diff --git a/sys/arm/include/acle-compat.h b/sys/arm/include/acle-compat.h
new file mode 100644
index 000000000000..93e965a04795
--- /dev/null
+++ b/sys/arm/include/acle-compat.h
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2014 ARM Ltd
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the company may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ARM LTD ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL ARM LTD BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __ARM_ARCH
+
+/* ACLE standardises a set of pre-defines that describe the ARM architecture.
+ These were mostly implemented in GCC around GCC-4.8; older versions
+ have no, or only partial support. To provide a level of backwards
+ compatibility we try to work out what the definitions should be, given
+ the older pre-defines that GCC did produce. This isn't complete, but
+ it should be enough for use by routines that depend on this header. */
+
+/* No need to handle ARMv8, GCC had ACLE support before that. */
+
+#define __ARM_ACLE 101
+
+# ifdef __ARM_ARCH_7__
+/* The common subset of ARMv7 in all profiles. */
+# define __ARM_ARCH 7
+# define __ARM_ARCH_ISA_THUMB 2
+# define __ARM_FEATURE_CLZ
+# define __ARM_FEATURE_LDREX 7
+# define __ARM_FEATURE_UNALIGNED
+# endif
+
+# if defined (__ARM_ARCH_7A__) || defined (__ARM_ARCH_7R__)
+# define __ARM_ARCH 7
+# define __ARM_ARCH_ISA_THUMB 2
+# define __ARM_ARCH_ISA_ARM
+# define __ARM_FEATURE_CLZ
+# define __ARM_FEATURE_SIMD32
+# define __ARM_FEATURE_DSP
+# define __ARM_FEATURE_QBIT
+# define __ARM_FEATURE_SAT
+# define __ARM_FEATURE_LDREX 15
+# define __ARM_FEATURE_UNALIGNED
+# ifdef __ARM_ARCH_7A__
+# define __ARM_ARCH_PROFILE 'A'
+# else
+# define __ARM_ARCH_PROFILE 'R'
+# endif
+# endif
+
+# ifdef __ARM_ARCH_7EM__
+# define __ARM_ARCH 7
+# define __ARM_ARCH_ISA_THUMB 2
+# define __ARM_FEATURE_CLZ
+# define __ARM_FEATURE_SIMD32
+# define __ARM_FEATURE_DSP
+# define __ARM_FEATURE_QBIT
+# define __ARM_FEATURE_SAT
+# define __ARM_FEATURE_LDREX 7
+# define __ARM_FEATURE_UNALIGNED
+# define __ARM_ARCH_PROFILE 'M'
+# endif
+
+# ifdef __ARM_ARCH_7M__
+# define __ARM_ARCH 7
+# define __ARM_ARCH_ISA_THUMB 2
+# define __ARM_FEATURE_CLZ
+# define __ARM_FEATURE_QBIT
+# define __ARM_FEATURE_SAT
+# define __ARM_FEATURE_LDREX 7
+# define __ARM_FEATURE_UNALIGNED
+# define __ARM_ARCH_PROFILE 'M'
+# endif
+
+# ifdef __ARM_ARCH_6T2__
+# define __ARM_ARCH 6
+# define __ARM_ARCH_ISA_THUMB 2
+# define __ARM_ARCH_ISA_ARM
+# define __ARM_FEATURE_CLZ
+# define __ARM_FEATURE_SIMD32
+# define __ARM_FEATURE_DSP
+# define __ARM_FEATURE_QBIT
+# define __ARM_FEATURE_SAT
+# define __ARM_FEATURE_LDREX 4
+# define __ARM_FEATURE_UNALIGNED
+# endif
+
+# ifdef __ARM_ARCH_6M__
+# define __ARM_ARCH 6
+# define __ARM_ARCH_ISA_THUMB 1
+# define __ARM_ARCH_PROFILE 'M'
+# endif
+
+# if defined (__ARM_ARCH_6__) || defined (__ARM_ARCH_6J__) \
+ || defined (__ARM_ARCH_6K__) || defined (__ARM_ARCH_6Z__) \
+ || defined (__ARM_ARCH_6ZK__)
+# define __ARM_ARCH 6
+# define __ARM_ARCH_ISA_THUMB 1
+# define __ARM_ARCH_ISA_ARM
+# define __ARM_FEATURE_CLZ
+# define __ARM_FEATURE_SIMD32
+# define __ARM_FEATURE_DSP
+# define __ARM_FEATURE_QBIT
+# define __ARM_FEATURE_SAT
+# define __ARM_FEATURE_UNALIGNED
+# ifndef __thumb__
+# if defined (__ARM_ARCH_6K__) || defined (__ARM_ARCH_6ZK__)
+# define __ARM_FEATURE_LDREX 15
+# else
+# define __ARM_FEATURE_LDREX 4
+# endif
+# endif
+# endif
+
+# if defined (__ARM_ARCH_5TE__) || defined (__ARM_ARCH_5E__)
+# define __ARM_ARCH 5
+# define __ARM_ARCH_ISA_ARM
+# ifdef __ARM_ARCH_5TE__
+# define __ARM_ARCH_ISA_THUMB 1
+# endif
+# define __ARM_FEATURE_CLZ
+# define __ARM_FEATURE_DSP
+# endif
+
+# if defined (__ARM_ARCH_5T__) || defined (__ARM_ARCH_5__)
+# define __ARM_ARCH 5
+# define __ARM_ARCH_ISA_ARM
+# ifdef __ARM_ARCH_5TE__
+# define __ARM_ARCH_ISA_THUMB 1
+# endif
+# define __ARM_FEATURE_CLZ
+# endif
+
+# ifdef __ARM_ARCH_4T__
+# define __ARM_ARCH 4
+# define __ARM_ARCH_ISA_ARM
+# define __ARM_ARCH_ISA_THUMB 1
+# endif
+
+# ifdef __ARM_ARCH_4__
+# define __ARM_ARCH 4
+# define __ARM_ARCH_ISA_ARM
+# endif
+
+# if defined (__ARM_ARCH_3__) || defined (__ARM_ARCH_3M__)
+# define __ARM_ARCH 3
+# define __ARM_ARCH_ISA_ARM
+# endif
+
+# ifdef __ARM_ARCH_2__
+# define __ARM_ARCH 2
+# define __ARM_ARCH_ISA_ARM
+# endif
+
+# ifdef __ARMEB__
+# define __ARM_BIG_ENDIAN
+# endif
+
+/* If we still don't know what the target architecture is, then we're
+ probably not using GCC. */
+# ifndef __ARM_ARCH
+# error Unable to determine architecture version.
+# endif
+
+#endif /* __ARM_ARCH */
diff --git a/sys/arm/include/armreg.h b/sys/arm/include/armreg.h
new file mode 100644
index 000000000000..02fad3a7fd59
--- /dev/null
+++ b/sys/arm/include/armreg.h
@@ -0,0 +1,458 @@
+/* $NetBSD: armreg.h,v 1.37 2007/01/06 00:50:54 christos Exp $ */
+
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 1998, 2001 Ben Harris
+ * Copyright (c) 1994-1996 Mark Brinicombe.
+ * Copyright (c) 1994 Brini.
+ * All rights reserved.
+ *
+ * This code is derived from software written for Brini by Mark Brinicombe
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Brini.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef MACHINE_ARMREG_H
+#define MACHINE_ARMREG_H
+
+#ifndef _SYS_CDEFS_H_
+#error Please include sys/cdefs.h before including machine/armreg.h
+#endif
+
+#define INSN_SIZE 4
+#define INSN_COND_MASK 0xf0000000 /* Condition mask */
+#define PSR_MODE 0x0000001f /* mode mask */
+#define PSR_USR32_MODE 0x00000010
+#define PSR_FIQ32_MODE 0x00000011
+#define PSR_IRQ32_MODE 0x00000012
+#define PSR_SVC32_MODE 0x00000013
+#define PSR_MON32_MODE 0x00000016
+#define PSR_ABT32_MODE 0x00000017
+#define PSR_HYP32_MODE 0x0000001a
+#define PSR_UND32_MODE 0x0000001b
+#define PSR_SYS32_MODE 0x0000001f
+#define PSR_32_MODE 0x00000010
+#define PSR_T 0x00000020 /* Instruction set bit */
+#define PSR_F 0x00000040 /* FIQ disable bit */
+#define PSR_I 0x00000080 /* IRQ disable bit */
+#define PSR_A 0x00000100 /* Imprecise abort bit */
+#define PSR_E 0x00000200 /* Data endianess bit */
+#define PSR_GE 0x000f0000 /* Greater than or equal to bits */
+#define PSR_J 0x01000000 /* Java bit */
+#define PSR_Q 0x08000000 /* Sticky overflow bit */
+#define PSR_V 0x10000000 /* Overflow bit */
+#define PSR_C 0x20000000 /* Carry bit */
+#define PSR_Z 0x40000000 /* Zero bit */
+#define PSR_N 0x80000000 /* Negative bit */
+#define PSR_FLAGS 0xf0000000 /* Flags mask. */
+
+/* The high-order byte is always the implementor */
+#define CPU_ID_IMPLEMENTOR_MASK 0xff000000
+#define CPU_ID_ARM_LTD 0x41000000 /* 'A' */
+#define CPU_ID_DEC 0x44000000 /* 'D' */
+#define CPU_ID_MOTOROLA 0x4D000000 /* 'M' */
+#define CPU_ID_QUALCOM 0x51000000 /* 'Q' */
+#define CPU_ID_TI 0x54000000 /* 'T' */
+#define CPU_ID_MARVELL 0x56000000 /* 'V' */
+#define CPU_ID_INTEL 0x69000000 /* 'i' */
+#define CPU_ID_FARADAY 0x66000000 /* 'f' */
+
+#define CPU_ID_VARIANT_SHIFT 20
+#define CPU_ID_VARIANT_MASK 0x00f00000
+
+/* How to decide what format the CPUID is in. */
+#define CPU_ID_ISOLD(x) (((x) & 0x0000f000) == 0x00000000)
+#define CPU_ID_IS7(x) (((x) & 0x0000f000) == 0x00007000)
+#define CPU_ID_ISNEW(x) (!CPU_ID_ISOLD(x) && !CPU_ID_IS7(x))
+
+/* On recent ARMs this byte holds the architecture and variant (sub-model) */
+#define CPU_ID_ARCH_MASK 0x000f0000
+#define CPU_ID_ARCH_V3 0x00000000
+#define CPU_ID_ARCH_V4 0x00010000
+#define CPU_ID_ARCH_V4T 0x00020000
+#define CPU_ID_ARCH_V5 0x00030000
+#define CPU_ID_ARCH_V5T 0x00040000
+#define CPU_ID_ARCH_V5TE 0x00050000
+#define CPU_ID_ARCH_V5TEJ 0x00060000
+#define CPU_ID_ARCH_V6 0x00070000
+#define CPU_ID_CPUID_SCHEME 0x000f0000
+
+/* Next three nybbles are part number */
+#define CPU_ID_PARTNO_MASK 0x0000fff0
+
+/* Intel XScale has sub fields in part number */
+#define CPU_ID_XSCALE_COREGEN_MASK 0x0000e000 /* core generation */
+#define CPU_ID_XSCALE_COREREV_MASK 0x00001c00 /* core revision */
+#define CPU_ID_XSCALE_PRODUCT_MASK 0x000003f0 /* product number */
+
+/* And finally, the revision number. */
+#define CPU_ID_REVISION_MASK 0x0000000f
+
+/* Individual CPUs are probably best IDed by everything but the revision. */
+#define CPU_ID_CPU_MASK 0xfffffff0
+
+/* ARM9 and later CPUs */
+#define CPU_ID_ARM920T 0x41129200
+#define CPU_ID_ARM920T_ALT 0x41009200
+#define CPU_ID_ARM922T 0x41029220
+#define CPU_ID_ARM926EJS 0x41069260
+#define CPU_ID_ARM940T 0x41029400 /* XXX no MMU */
+#define CPU_ID_ARM946ES 0x41049460 /* XXX no MMU */
+#define CPU_ID_ARM966ES 0x41049660 /* XXX no MMU */
+#define CPU_ID_ARM966ESR1 0x41059660 /* XXX no MMU */
+#define CPU_ID_ARM1020E 0x4115a200 /* (AKA arm10 rev 1) */
+#define CPU_ID_ARM1022ES 0x4105a220
+#define CPU_ID_ARM1026EJS 0x4106a260
+#define CPU_ID_ARM1136JS 0x4107b360
+#define CPU_ID_ARM1136JSR1 0x4117b360
+#define CPU_ID_ARM1176JZS 0x410fb760
+
+/* CPUs that follow the CPUID scheme */
+#define CPU_ID_SCHEME_MASK \
+ (CPU_ID_IMPLEMENTOR_MASK | CPU_ID_ARCH_MASK | CPU_ID_PARTNO_MASK)
+
+#define CPU_ID_CORTEXA5 (CPU_ID_ARM_LTD | CPU_ID_CPUID_SCHEME | 0xc050)
+#define CPU_ID_CORTEXA7 (CPU_ID_ARM_LTD | CPU_ID_CPUID_SCHEME | 0xc070)
+#define CPU_ID_CORTEXA8 (CPU_ID_ARM_LTD | CPU_ID_CPUID_SCHEME | 0xc080)
+#define CPU_ID_CORTEXA8R1 (CPU_ID_CORTEXA8 | (1 << CPU_ID_VARIANT_SHIFT))
+#define CPU_ID_CORTEXA8R2 (CPU_ID_CORTEXA8 | (2 << CPU_ID_VARIANT_SHIFT))
+#define CPU_ID_CORTEXA8R3 (CPU_ID_CORTEXA8 | (3 << CPU_ID_VARIANT_SHIFT))
+#define CPU_ID_CORTEXA9 (CPU_ID_ARM_LTD | CPU_ID_CPUID_SCHEME | 0xc090)
+#define CPU_ID_CORTEXA9R1 (CPU_ID_CORTEXA9 | (1 << CPU_ID_VARIANT_SHIFT))
+#define CPU_ID_CORTEXA9R2 (CPU_ID_CORTEXA9 | (2 << CPU_ID_VARIANT_SHIFT))
+#define CPU_ID_CORTEXA9R3 (CPU_ID_CORTEXA9 | (3 << CPU_ID_VARIANT_SHIFT))
+#define CPU_ID_CORTEXA9R4 (CPU_ID_CORTEXA9 | (4 << CPU_ID_VARIANT_SHIFT))
+/* XXX: Cortex-A12 is the old name for this part, it has been renamed the A17 */
+#define CPU_ID_CORTEXA12 (CPU_ID_ARM_LTD | CPU_ID_CPUID_SCHEME | 0xc0d0)
+#define CPU_ID_CORTEXA12R0 (CPU_ID_CORTEXA12 | (0 << CPU_ID_VARIANT_SHIFT))
+#define CPU_ID_CORTEXA15 (CPU_ID_ARM_LTD | CPU_ID_CPUID_SCHEME | 0xc0f0)
+#define CPU_ID_CORTEXA15R0 (CPU_ID_CORTEXA15 | (0 << CPU_ID_VARIANT_SHIFT))
+#define CPU_ID_CORTEXA15R1 (CPU_ID_CORTEXA15 | (1 << CPU_ID_VARIANT_SHIFT))
+#define CPU_ID_CORTEXA15R2 (CPU_ID_CORTEXA15 | (2 << CPU_ID_VARIANT_SHIFT))
+#define CPU_ID_CORTEXA15R3 (CPU_ID_CORTEXA15 | (3 << CPU_ID_VARIANT_SHIFT))
+#define CPU_ID_CORTEXA53 (CPU_ID_ARM_LTD | CPU_ID_CPUID_SCHEME | 0xd030)
+#define CPU_ID_CORTEXA57 (CPU_ID_ARM_LTD | CPU_ID_CPUID_SCHEME | 0xd070)
+#define CPU_ID_CORTEXA72 (CPU_ID_ARM_LTD | CPU_ID_CPUID_SCHEME | 0xd080)
+
+#define CPU_ID_KRAIT300 (CPU_ID_QUALCOM | CPU_ID_CPUID_SCHEME | 0x06f0)
+/* Snapdragon S4 Pro/APQ8064 */
+#define CPU_ID_KRAIT300R0 (CPU_ID_KRAIT300 | (0 << CPU_ID_VARIANT_SHIFT))
+#define CPU_ID_KRAIT300R1 (CPU_ID_KRAIT300 | (1 << CPU_ID_VARIANT_SHIFT))
+
+#define CPU_ID_TI925T 0x54029250
+#define CPU_ID_MV88FR131 0x56251310 /* Marvell Feroceon 88FR131 Core */
+#define CPU_ID_MV88FR331 0x56153310 /* Marvell Feroceon 88FR331 Core */
+#define CPU_ID_MV88FR571_VD 0x56155710 /* Marvell Feroceon 88FR571-VD Core (ID from datasheet) */
+
+/*
+ * LokiPlus core has also ID set to 0x41159260 and this define cause execution of unsupported
+ * L2-cache instructions so need to disable it. 0x41159260 is a generic ARM926E-S ID.
+ */
+#ifdef SOC_MV_LOKIPLUS
+#define CPU_ID_MV88FR571_41 0x00000000
+#else
+#define CPU_ID_MV88FR571_41 0x41159260 /* Marvell Feroceon 88FR571-VD Core (actual ID from CPU reg) */
+#endif
+
+#define CPU_ID_MV88SV581X_V7 0x561F5810 /* Marvell Sheeva 88SV581x v7 Core */
+#define CPU_ID_MV88SV584X_V7 0x562F5840 /* Marvell Sheeva 88SV584x v7 Core */
+/* Marvell's CPUIDs with ARM ID in implementor field */
+#define CPU_ID_ARM_88SV581X_V7 0x413FC080 /* Marvell Sheeva 88SV581x v7 Core */
+
+#define CPU_ID_FA526 0x66015260
+#define CPU_ID_FA626TE 0x66056260
+#define CPU_ID_80200 0x69052000
+#define CPU_ID_PXA250 0x69052100 /* sans core revision */
+#define CPU_ID_PXA210 0x69052120
+#define CPU_ID_PXA250A 0x69052100 /* 1st version Core */
+#define CPU_ID_PXA210A 0x69052120 /* 1st version Core */
+#define CPU_ID_PXA250B 0x69052900 /* 3rd version Core */
+#define CPU_ID_PXA210B 0x69052920 /* 3rd version Core */
+#define CPU_ID_PXA250C 0x69052d00 /* 4th version Core */
+#define CPU_ID_PXA210C 0x69052d20 /* 4th version Core */
+#define CPU_ID_PXA27X 0x69054110
+#define CPU_ID_80321_400 0x69052420
+#define CPU_ID_80321_600 0x69052430
+#define CPU_ID_80321_400_B0 0x69052c20
+#define CPU_ID_80321_600_B0 0x69052c30
+#define CPU_ID_80219_400 0x69052e20 /* A0 stepping/revision. */
+#define CPU_ID_80219_600 0x69052e30 /* A0 stepping/revision. */
+#define CPU_ID_81342 0x69056810
+#define CPU_ID_IXP425 0x690541c0
+#define CPU_ID_IXP425_533 0x690541c0
+#define CPU_ID_IXP425_400 0x690541d0
+#define CPU_ID_IXP425_266 0x690541f0
+#define CPU_ID_IXP435 0x69054040
+#define CPU_ID_IXP465 0x69054200
+
+/* CPUID registers */
+#define ARM_PFR0_ARM_ISA_MASK 0x0000000f
+
+#define ARM_PFR0_THUMB_MASK 0x000000f0
+#define ARM_PFR0_THUMB 0x10
+#define ARM_PFR0_THUMB2 0x30
+
+#define ARM_PFR0_JAZELLE_MASK 0x00000f00
+#define ARM_PFR0_THUMBEE_MASK 0x0000f000
+
+#define ARM_PFR1_ARMV4_MASK 0x0000000f
+#define ARM_PFR1_SEC_EXT_MASK 0x000000f0
+#define ARM_PFR1_MICROCTRL_MASK 0x00000f00
+
+/*
+ * Post-ARM3 CP15 registers:
+ *
+ * 1 Control register
+ *
+ * 2 Translation Table Base
+ *
+ * 3 Domain Access Control
+ *
+ * 4 Reserved
+ *
+ * 5 Fault Status
+ *
+ * 6 Fault Address
+ *
+ * 7 Cache/write-buffer Control
+ *
+ * 8 TLB Control
+ *
+ * 9 Cache Lockdown
+ *
+ * 10 TLB Lockdown
+ *
+ * 11 Reserved
+ *
+ * 12 Reserved
+ *
+ * 13 Process ID (for FCSE)
+ *
+ * 14 Reserved
+ *
+ * 15 Implementation Dependent
+ */
+
+/* Some of the definitions below need cleaning up for V3/V4 architectures */
+
+/* CPU control register (CP15 register 1) */
+#define CPU_CONTROL_MMU_ENABLE 0x00000001 /* M: MMU/Protection unit enable */
+#define CPU_CONTROL_AFLT_ENABLE 0x00000002 /* A: Alignment fault enable */
+#define CPU_CONTROL_DC_ENABLE 0x00000004 /* C: IDC/DC enable */
+#define CPU_CONTROL_WBUF_ENABLE 0x00000008 /* W: Write buffer enable */
+#define CPU_CONTROL_32BP_ENABLE 0x00000010 /* P: 32-bit exception handlers */
+#define CPU_CONTROL_32BD_ENABLE 0x00000020 /* D: 32-bit addressing */
+#define CPU_CONTROL_LABT_ENABLE 0x00000040 /* L: Late abort enable */
+#define CPU_CONTROL_BEND_ENABLE 0x00000080 /* B: Big-endian mode */
+#define CPU_CONTROL_SYST_ENABLE 0x00000100 /* S: System protection bit */
+#define CPU_CONTROL_ROM_ENABLE 0x00000200 /* R: ROM protection bit */
+#define CPU_CONTROL_CPCLK 0x00000400 /* F: Implementation defined */
+#define CPU_CONTROL_SW_ENABLE 0x00000400 /* SW: SWP instruction enable */
+#define CPU_CONTROL_BPRD_ENABLE 0x00000800 /* Z: Branch prediction enable */
+#define CPU_CONTROL_IC_ENABLE 0x00001000 /* I: IC enable */
+#define CPU_CONTROL_VECRELOC 0x00002000 /* V: Vector relocation */
+#define CPU_CONTROL_ROUNDROBIN 0x00004000 /* RR: Predictable replacement */
+#define CPU_CONTROL_V4COMPAT 0x00008000 /* L4: ARMv4 compat LDR R15 etc */
+#define CPU_CONTROL_HAF_ENABLE 0x00020000 /* HA: Hardware Access Flag Enable */
+#define CPU_CONTROL_FI_ENABLE 0x00200000 /* FI: Low interrupt latency */
+#define CPU_CONTROL_UNAL_ENABLE 0x00400000 /* U: unaligned data access */
+#define CPU_CONTROL_V6_EXTPAGE 0x00800000 /* XP: ARMv6 extended page tables */
+#define CPU_CONTROL_V_ENABLE 0x01000000 /* VE: Interrupt vectors enable */
+#define CPU_CONTROL_EX_BEND 0x02000000 /* EE: exception endianness */
+#define CPU_CONTROL_L2_ENABLE 0x04000000 /* L2 Cache enabled */
+#define CPU_CONTROL_NMFI 0x08000000 /* NMFI: Non maskable FIQ */
+#define CPU_CONTROL_TR_ENABLE 0x10000000 /* TRE: TEX Remap*/
+#define CPU_CONTROL_AF_ENABLE 0x20000000 /* AFE: Access Flag enable */
+#define CPU_CONTROL_TE_ENABLE 0x40000000 /* TE: Thumb Exception enable */
+
+#define CPU_CONTROL_IDC_ENABLE CPU_CONTROL_DC_ENABLE
+
+/* ARM11x6 Auxiliary Control Register (CP15 register 1, opcode2 1) */
+#define ARM11X6_AUXCTL_RS 0x00000001 /* return stack */
+#define ARM11X6_AUXCTL_DB 0x00000002 /* dynamic branch prediction */
+#define ARM11X6_AUXCTL_SB 0x00000004 /* static branch prediction */
+#define ARM11X6_AUXCTL_TR 0x00000008 /* MicroTLB replacement strat. */
+#define ARM11X6_AUXCTL_EX 0x00000010 /* exclusive L1/L2 cache */
+#define ARM11X6_AUXCTL_RA 0x00000020 /* clean entire cache disable */
+#define ARM11X6_AUXCTL_RV 0x00000040 /* block transfer cache disable */
+#define ARM11X6_AUXCTL_CZ 0x00000080 /* restrict cache size */
+
+/* ARM1136 Auxiliary Control Register (CP15 register 1, opcode2 1) */
+#define ARM1136_AUXCTL_PFI 0x80000000 /* PFI: partial FI mode. */
+ /* This is an undocumented flag
+ * used to work around a cache bug
+ * in r0 steppings. See errata
+ * 364296.
+ */
+/* ARM1176 Auxiliary Control Register (CP15 register 1, opcode2 1) */
+#define ARM1176_AUXCTL_PHD 0x10000000 /* inst. prefetch halting disable */
+#define ARM1176_AUXCTL_BFD 0x20000000 /* branch folding disable */
+#define ARM1176_AUXCTL_FSD 0x40000000 /* force speculative ops disable */
+#define ARM1176_AUXCTL_FIO 0x80000000 /* low intr latency override */
+
+/* XScale Auxillary Control Register (CP15 register 1, opcode2 1) */
+#define XSCALE_AUXCTL_K 0x00000001 /* dis. write buffer coalescing */
+#define XSCALE_AUXCTL_P 0x00000002 /* ECC protect page table access */
+/* Note: XSCale core 3 uses those for LLR DCcahce attributes */
+#define XSCALE_AUXCTL_MD_WB_RA 0x00000000 /* mini-D$ wb, read-allocate */
+#define XSCALE_AUXCTL_MD_WB_RWA 0x00000010 /* mini-D$ wb, read/write-allocate */
+#define XSCALE_AUXCTL_MD_WT 0x00000020 /* mini-D$ wt, read-allocate */
+#define XSCALE_AUXCTL_MD_MASK 0x00000030
+
+/* Xscale Core 3 only */
+#define XSCALE_AUXCTL_LLR 0x00000400 /* Enable L2 for LLR Cache */
+
+/* Marvell Extra Features Register (CP15 register 1, opcode2 0) */
+#define MV_DC_REPLACE_LOCK 0x80000000 /* Replace DCache Lock */
+#define MV_DC_STREAM_ENABLE 0x20000000 /* DCache Streaming Switch */
+#define MV_WA_ENABLE 0x10000000 /* Enable Write Allocate */
+#define MV_L2_PREFETCH_DISABLE 0x01000000 /* L2 Cache Prefetch Disable */
+#define MV_L2_INV_EVICT_ERR 0x00800000 /* L2 Invalidates Uncorrectable Error Line Eviction */
+#define MV_L2_ENABLE 0x00400000 /* L2 Cache enable */
+#define MV_IC_REPLACE_LOCK 0x00080000 /* Replace ICache Lock */
+#define MV_BGH_ENABLE 0x00040000 /* Branch Global History Register Enable */
+#define MV_BTB_DISABLE 0x00020000 /* Branch Target Buffer Disable */
+#define MV_L1_PARERR_ENABLE 0x00010000 /* L1 Parity Error Enable */
+
+/* Cache type register definitions */
+#define CPU_CT_ISIZE(x) ((x) & 0xfff) /* I$ info */
+#define CPU_CT_DSIZE(x) (((x) >> 12) & 0xfff) /* D$ info */
+#define CPU_CT_S (1U << 24) /* split cache */
+#define CPU_CT_CTYPE(x) (((x) >> 25) & 0xf) /* cache type */
+#define CPU_CT_FORMAT(x) ((x) >> 29)
+/* Cache type register definitions for ARM v7 */
+#define CPU_CT_IMINLINE(x) ((x) & 0xf) /* I$ min line size */
+#define CPU_CT_DMINLINE(x) (((x) >> 16) & 0xf) /* D$ min line size */
+
+#define CPU_CT_CTYPE_WT 0 /* write-through */
+#define CPU_CT_CTYPE_WB1 1 /* write-back, clean w/ read */
+#define CPU_CT_CTYPE_WB2 2 /* w/b, clean w/ cp15,7 */
+#define CPU_CT_CTYPE_WB6 6 /* w/b, cp15,7, lockdown fmt A */
+#define CPU_CT_CTYPE_WB7 7 /* w/b, cp15,7, lockdown fmt B */
+
+#define CPU_CT_xSIZE_LEN(x) ((x) & 0x3) /* line size */
+#define CPU_CT_xSIZE_M (1U << 2) /* multiplier */
+#define CPU_CT_xSIZE_ASSOC(x) (((x) >> 3) & 0x7) /* associativity */
+#define CPU_CT_xSIZE_SIZE(x) (((x) >> 6) & 0x7) /* size */
+
+#define CPU_CT_ARMV7 0x4
+/* ARM v7 Cache type definitions */
+#define CPUV7_CT_CTYPE_WT (1U << 31)
+#define CPUV7_CT_CTYPE_WB (1 << 30)
+#define CPUV7_CT_CTYPE_RA (1 << 29)
+#define CPUV7_CT_CTYPE_WA (1 << 28)
+
+#define CPUV7_CT_xSIZE_LEN(x) ((x) & 0x7) /* line size */
+#define CPUV7_CT_xSIZE_ASSOC(x) (((x) >> 3) & 0x3ff) /* associativity */
+#define CPUV7_CT_xSIZE_SET(x) (((x) >> 13) & 0x7fff) /* num sets */
+
+#define CPUV7_L2CTLR_NPROC_SHIFT 24
+#define CPUV7_L2CTLR_NPROC(r) ((((r) >> CPUV7_L2CTLR_NPROC_SHIFT) & 3) + 1)
+
+#define CPU_CLIDR_CTYPE(reg,x) (((reg) >> ((x) * 3)) & 0x7)
+#define CPU_CLIDR_LOUIS(reg) (((reg) >> 21) & 0x7)
+#define CPU_CLIDR_LOC(reg) (((reg) >> 24) & 0x7)
+#define CPU_CLIDR_LOUU(reg) (((reg) >> 27) & 0x7)
+
+#define CACHE_ICACHE 1
+#define CACHE_DCACHE 2
+#define CACHE_SEP_CACHE 3
+#define CACHE_UNI_CACHE 4
+
+/* Fault status register definitions */
+#define FAULT_USER 0x10
+
+#define FAULT_ALIGN 0x001 /* Alignment Fault */
+#define FAULT_DEBUG 0x002 /* Debug Event */
+#define FAULT_ACCESS_L1 0x003 /* Access Bit (L1) */
+#define FAULT_ICACHE 0x004 /* Instruction cache maintenance */
+#define FAULT_TRAN_L1 0x005 /* Translation Fault (L1) */
+#define FAULT_ACCESS_L2 0x006 /* Access Bit (L2) */
+#define FAULT_TRAN_L2 0x007 /* Translation Fault (L2) */
+#define FAULT_EA_PREC 0x008 /* External Abort */
+#define FAULT_DOMAIN_L1 0x009 /* Domain Fault (L1) */
+#define FAULT_DOMAIN_L2 0x00B /* Domain Fault (L2) */
+#define FAULT_EA_TRAN_L1 0x00C /* External Translation Abort (L1) */
+#define FAULT_PERM_L1 0x00D /* Permission Fault (L1) */
+#define FAULT_EA_TRAN_L2 0x00E /* External Translation Abort (L2) */
+#define FAULT_PERM_L2 0x00F /* Permission Fault (L2) */
+#define FAULT_TLB_CONFLICT 0x010 /* TLB Conflict Abort */
+#define FAULT_EA_IMPREC 0x016 /* Asynchronous External Abort */
+#define FAULT_PE_IMPREC 0x018 /* Asynchronous Parity Error */
+#define FAULT_PARITY 0x019 /* Parity Error */
+#define FAULT_PE_TRAN_L1 0x01C /* Parity Error on Translation (L1) */
+#define FAULT_PE_TRAN_L2 0x01E /* Parity Error on Translation (L2) */
+
+#define FSR_TO_FAULT(fsr) (((fsr) & 0xF) | \
+ ((((fsr) & (1 << 10)) >> (10 - 4))))
+#define FSR_LPAE (1 << 9) /* LPAE indicator */
+#define FSR_WNR (1 << 11) /* Write-not-Read access */
+#define FSR_EXT (1 << 12) /* DECERR/SLVERR for external*/
+#define FSR_CM (1 << 13) /* Cache maintenance fault */
+
+/*
+ * Address of the vector page, low and high versions.
+ */
+#ifndef __ASSEMBLER__
+#define ARM_VECTORS_LOW 0x00000000U
+#define ARM_VECTORS_HIGH 0xffff0000U
+#else
+#define ARM_VECTORS_LOW 0
+#define ARM_VECTORS_HIGH 0xffff0000
+#endif
+
+/*
+ * ARM Instructions
+ *
+ * 3 3 2 2 2
+ * 1 0 9 8 7 0
+ * +-------+-------------------------------------------------------+
+ * | cond | instruction dependant |
+ * |c c c c| |
+ * +-------+-------------------------------------------------------+
+ */
+
+#define INSN_SIZE 4 /* Always 4 bytes */
+#define INSN_COND_MASK 0xf0000000 /* Condition mask */
+#define INSN_COND_AL 0xe0000000 /* Always condition */
+
+/* ARM register defines */
+#define ARM_REG_SIZE 4
+#define ARM_REG_NUM_PC 15
+#define ARM_REG_NUM_LR 14
+#define ARM_REG_NUM_SP 13
+
+#define THUMB_INSN_SIZE 2 /* Some are 4 bytes. */
+
+/* ARM Hypervisor Related Defines */
+#define ARM_CP15_HDCR_HPMN 0x0000001f
+
+#endif /* !MACHINE_ARMREG_H */
diff --git a/sys/arm/include/asm.h b/sys/arm/include/asm.h
new file mode 100644
index 000000000000..1974908b65c1
--- /dev/null
+++ b/sys/arm/include/asm.h
@@ -0,0 +1,261 @@
+/* $NetBSD: asm.h,v 1.5 2003/08/07 16:26:53 agc Exp $ */
+
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)asm.h 5.5 (Berkeley) 5/7/91
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_ASM_H_
+#define _MACHINE_ASM_H_
+#include <sys/cdefs.h>
+#include <machine/sysreg.h>
+
+#define _C_LABEL(x) x
+#define _ASM_LABEL(x) x
+
+#ifndef _ALIGN_TEXT
+# define _ALIGN_TEXT .align 2
+#endif
+
+#ifndef _STANDALONE
+#define STOP_UNWINDING .cantunwind
+#define _FNSTART .fnstart
+#define _FNEND .fnend
+#define _SAVE(...) .save __VA_ARGS__
+#else
+#define STOP_UNWINDING
+#define _FNSTART
+#define _FNEND
+#define _SAVE(...)
+#endif
+
+/*
+ * gas/arm uses @ as a single comment character and thus cannot be used here.
+ * It recognises the # instead of an @ symbol in .type directives.
+ */
+#define _ASM_TYPE_FUNCTION #function
+#define _ASM_TYPE_OBJECT #object
+
+/* XXX Is this still the right prologue for profiling? */
+#ifdef GPROF
+#define _PROF_PROLOGUE \
+ mov ip, lr; \
+ bl __mcount
+#else
+#define _PROF_PROLOGUE
+#endif
+
+/*
+ * EENTRY()/EEND() mark "extra" entry/exit points from a function.
+ * LEENTRY()/LEEND() are the same for local symbols.
+ * The unwind info cannot handle the concept of a nested function, or a function
+ * with multiple .fnstart directives, but some of our assembler code is written
+ * with multiple labels to allow entry at several points. The EENTRY() macro
+ * defines such an extra entry point without a new .fnstart, so that it's
+ * basically just a label that you can jump to. The EEND() macro does nothing
+ * at all, except document the exit point associated with the same-named entry.
+ */
+#define GLOBAL(x) .global x
+
+#ifdef __thumb__
+#define _FUNC_MODE .code 16; .thumb_func
+#else
+#define _FUNC_MODE .code 32
+#endif
+
+#define _LEENTRY(x) .type x,_ASM_TYPE_FUNCTION; _FUNC_MODE; x:
+#define _LEEND(x) /* nothing */
+#define _EENTRY(x) GLOBAL(x); _LEENTRY(x)
+#define _EEND(x) _LEEND(x)
+
+#define _LENTRY(x) .text; _ALIGN_TEXT; _LEENTRY(x); _FNSTART
+#define _LEND(x) .size x, . - x; _FNEND
+#define _ENTRY(x) .text; _ALIGN_TEXT; _EENTRY(x); _FNSTART
+#define _END(x) _LEND(x)
+
+#define ENTRY(y) _ENTRY(_C_LABEL(y)); _PROF_PROLOGUE
+#define EENTRY(y) _EENTRY(_C_LABEL(y));
+#define ENTRY_NP(y) _ENTRY(_C_LABEL(y))
+#define EENTRY_NP(y) _EENTRY(_C_LABEL(y))
+#define END(y) _END(_C_LABEL(y))
+#define EEND(y) _EEND(_C_LABEL(y))
+#define ASENTRY(y) _ENTRY(_ASM_LABEL(y)); _PROF_PROLOGUE
+#define ASLENTRY(y) _LENTRY(_ASM_LABEL(y)); _PROF_PROLOGUE
+#define ASEENTRY(y) _EENTRY(_ASM_LABEL(y));
+#define ASLEENTRY(y) _LEENTRY(_ASM_LABEL(y));
+#define ASENTRY_NP(y) _ENTRY(_ASM_LABEL(y))
+#define ASLENTRY_NP(y) _LENTRY(_ASM_LABEL(y))
+#define ASEENTRY_NP(y) _EENTRY(_ASM_LABEL(y))
+#define ASLEENTRY_NP(y) _LEENTRY(_ASM_LABEL(y))
+#define ASEND(y) _END(_ASM_LABEL(y))
+#define ASLEND(y) _LEND(_ASM_LABEL(y))
+#define ASEEND(y) _EEND(_ASM_LABEL(y))
+#define ASLEEND(y) _LEEND(_ASM_LABEL(y))
+
+#define ASMSTR .asciz
+
+#if defined(PIC)
+#define PLT_SYM(x) PIC_SYM(x, PLT)
+#define GOT_SYM(x) PIC_SYM(x, GOT)
+#define GOT_GET(x,got,sym) \
+ ldr x, sym; \
+ ldr x, [x, got]
+#define GOT_INIT(got,gotsym,pclabel) \
+ ldr got, gotsym; \
+ pclabel: add got, pc
+#ifdef __thumb__
+#define GOT_INITSYM(gotsym,pclabel) \
+ .align 2; \
+ gotsym: .word _C_LABEL(_GLOBAL_OFFSET_TABLE_) - (pclabel+4)
+#else
+#define GOT_INITSYM(gotsym,pclabel) \
+ .align 2; \
+ gotsym: .word _C_LABEL(_GLOBAL_OFFSET_TABLE_) - (pclabel+8)
+#endif
+
+#ifdef __STDC__
+#define PIC_SYM(x,y) x ## ( ## y ## )
+#else
+#define PIC_SYM(x,y) x/**/(/**/y/**/)
+#endif
+
+#else
+#define PLT_SYM(x) x
+#define GOT_SYM(x) x
+#define GOT_GET(x,got,sym) \
+ ldr x, sym;
+#define GOT_INIT(got,gotsym,pclabel)
+#define GOT_INITSYM(gotsym,pclabel)
+#define PIC_SYM(x,y) x
+#endif /* PIC */
+
+#undef __FBSDID
+#if !defined(lint) && !defined(STRIP_FBSDID)
+#define __FBSDID(s) .ident s
+#else
+#define __FBSDID(s) /* nothing */
+#endif
+
+#define WEAK_ALIAS(alias,sym) \
+ .weak alias; \
+ alias = sym
+
+#ifdef __STDC__
+#define WARN_REFERENCES(sym,msg) \
+ .stabs msg ## ,30,0,0,0 ; \
+ .stabs __STRING(_C_LABEL(sym)) ## ,1,0,0,0
+#else
+#define WARN_REFERENCES(sym,msg) \
+ .stabs msg,30,0,0,0 ; \
+ .stabs __STRING(sym),1,0,0,0
+#endif /* __STDC__ */
+
+/* Exactly one of the __ARM_ARCH_*__ macros will be defined by the compiler. */
+/* The _ARM_ARCH_* macros are deprecated and will be removed soon. */
+/* This should be moved into another header so it can be used in
+ * both asm and C code. machine/asm.h cannot be included in C code. */
+#if defined (__ARM_ARCH_7__) || defined (__ARM_ARCH_7A__)
+#define _ARM_ARCH_7
+#define _HAVE_ARMv7_INSTRUCTIONS 1
+#endif
+
+#if defined (_HAVE_ARMv7_INSTRUCTIONS) || defined (__ARM_ARCH_6__) || \
+ defined (__ARM_ARCH_6J__) || defined (__ARM_ARCH_6K__) || \
+ defined (__ARM_ARCH_6KZ__) || \
+ defined (__ARM_ARCH_6Z__) || defined (__ARM_ARCH_6ZK__)
+#define _ARM_ARCH_6
+#define _HAVE_ARMv6_INSTRUCTIONS 1
+#endif
+
+#if defined (_HAVE_ARMv6_INSTRUCTIONS) || defined (__ARM_ARCH_5TE__) || \
+ defined (__ARM_ARCH_5TEJ__) || defined (__ARM_ARCH_5E__)
+#define _ARM_ARCH_5E
+#define _HAVE_ARMv5E_INSTRUCTIONS 1
+#endif
+
+#if defined (_HAVE_ARMv5E_INSTRUCTIONS) || defined (__ARM_ARCH_5__) || \
+ defined (__ARM_ARCH_5T__)
+#define _ARM_ARCH_5
+#define _HAVE_ARMv5_INSTRUCTIONS 1
+#endif
+
+#if defined (_HAVE_ARMv5_INSTRUCTIONS) || defined (__ARM_ARCH_4T__)
+#define _ARM_ARCH_4T
+#define _HAVE_ARMv4T_INSTRUCTIONS 1
+#endif
+
+/* FreeBSD requires ARMv4, so this is always set. */
+#define _HAVE_ARMv4_INSTRUCTIONS 1
+
+#if defined (_HAVE_ARMv4T_INSTRUCTIONS)
+# define RET bx lr
+# define RETeq bxeq lr
+# define RETne bxne lr
+# define RETc(c) bx##c lr
+#else
+# define RET mov pc, lr
+# define RETeq moveq pc, lr
+# define RETne movne pc, lr
+# define RETc(c) mov##c pc, lr
+#endif
+
+#if __ARM_ARCH >= 7
+#define ISB isb
+#define DSB dsb
+#define DMB dmb
+#define WFI wfi
+
+#if defined(__ARM_ARCH_7VE__) || defined(__clang__)
+#define MSR_ELR_HYP(regnum) msr elr_hyp, lr
+#define ERET eret
+#else
+#define MSR_ELR_HYP(regnum) .word (0xe12ef300 | regnum)
+#define ERET .word 0xe160006e
+#endif
+
+#elif __ARM_ARCH == 6
+#define ISB mcr CP15_CP15ISB
+#define DSB mcr CP15_CP15DSB
+#define DMB mcr CP15_CP15DMB
+#define WFI mcr CP15_CP15WFI
+#else
+#define ISB mcr CP15_CP15ISB
+#define DSB mcr CP15_CP15DSB /* DSB and DMB are the */
+#define DMB mcr CP15_CP15DSB /* same prior to v6.*/
+/* No form of WFI available on v4, define nothing to get an error on use. */
+#endif
+
+#endif /* !_MACHINE_ASM_H_ */
diff --git a/sys/arm/include/asmacros.h b/sys/arm/include/asmacros.h
new file mode 100644
index 000000000000..a41af82febba
--- /dev/null
+++ b/sys/arm/include/asmacros.h
@@ -0,0 +1,59 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Olivier Houchard <cognet@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_ASMACROS_H_
+#define _MACHINE_ASMACROS_H_
+
+#include <machine/asm.h>
+
+#ifdef _KERNEL
+
+#ifdef LOCORE
+
+#define GET_CURTHREAD_PTR(tmp) \
+ mrc p15, 0, tmp, c13, c0, 4
+
+#define ELFNOTE(section, type, vendor, desctype, descdata...) \
+ .pushsection section ; \
+ .balign 4 ; \
+ .long 2f - 1f /* namesz */ ; \
+ .long 4f - 3f /* descsz */ ; \
+ .long type /* type */ ; \
+ 1: .asciz vendor /* vendor name */ ; \
+ 2: .balign 4 ; \
+ 3: desctype descdata /* node */ ; \
+ 4: .balign 4 ; \
+ .popsection
+
+#endif /* LOCORE */
+
+#endif /* _KERNEL */
+
+#endif /* !_MACHINE_ASMACROS_H_ */
diff --git a/sys/arm/include/atags.h b/sys/arm/include/atags.h
new file mode 100644
index 000000000000..baf39b5c0c69
--- /dev/null
+++ b/sys/arm/include/atags.h
@@ -0,0 +1,130 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 M. Warner Losh <imp@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __MACHINE_ATAGS_H__
+#define __MACHINE_ATAGS_H__
+
+/*
+ * Linux boot ABI compatable ATAG definitions. All these structures
+ * assume tight packing, but since they are all uint32_t's, I've not
+ * bothered to do the usual alignment dance.
+ */
+
+#define LBABI_MAX_COMMAND_LINE 1024
+
+struct arm_lbabi_header
+{
+ uint32_t size; /* Size of this node, including header */
+ uint32_t tag; /* Node type */
+};
+
+#define ATAG_NONE 0x00000000 /* End of atags list */
+#define ATAG_CORE 0x54410001 /* List must start with ATAG_CORE */
+#define ATAG_MEM 0x54410002 /* Multiple ATAG_MEM nodes possible */
+#define ATAG_VIDEOTEXT 0x54410003 /* Video parameters */
+#define ATAG_RAMDISK 0x54410004 /* Describes the ramdisk parameters */
+#define ATAG_INITRD 0x54410005 /* Deprecated ramdisk -- used va not pa */
+#define ATAG_INITRD2 0x54420005 /* compressed ramdisk image */
+#define ATAG_SERIAL 0x54410006 /* 64-bits of serial number */
+#define ATAG_REVISION 0x54410007 /* Board revision */
+#define ATAG_VIDEOLFB 0x54410008 /* vesafb framebuffer */
+#define ATAG_CMDLINE 0x54410009 /* Command line */
+
+/*
+ * ATAG_CORE data
+ */
+struct arm_lbabi_core
+{
+ uint32_t flags; /* bit 0 == read-only */
+ uint32_t pagesize;
+ uint32_t rootdev;
+};
+
+/*
+ * ATAG_MEM data -- Can be more than one to describe different
+ * banks.
+ */
+struct arm_lbabi_mem32
+{
+ uint32_t size;
+ uint32_t start; /* start of physical memory */
+};
+
+/*
+ * ATAG_INITRD2 - Compressed ramdisk image details
+ */
+struct arm_lbabi_initrd
+{
+ uint32_t start; /* pa of start */
+ uint32_t size; /* How big the ram disk is */
+};
+
+/*
+ * ATAG_SERIAL - serial number
+ */
+struct arm_lbabi_serial_number
+{
+ uint32_t low;
+ uint32_t high;
+};
+
+/*
+ * ATAG_REVISION - board revision
+ */
+struct arm_lbabi_revision
+{
+ uint32_t rev;
+};
+
+/*
+ * ATAG_CMDLINE - Command line from uboot
+ */
+struct arm_lbabi_command_line
+{
+ char command[1]; /* Minimum command length */
+};
+
+struct arm_lbabi_tag
+{
+ struct arm_lbabi_header tag_hdr;
+ union {
+ struct arm_lbabi_core tag_core;
+ struct arm_lbabi_mem32 tag_mem;
+ struct arm_lbabi_initrd tag_initrd;
+ struct arm_lbabi_serial_number tag_sn;
+ struct arm_lbabi_revision tag_rev;
+ struct arm_lbabi_command_line tag_cmd;
+ } u;
+};
+
+#define ATAG_TAG(a) (a)->tag_hdr.tag
+#define ATAG_SIZE(a) ((a)->tag_hdr.size * sizeof(uint32_t))
+#define ATAG_NEXT(a) (struct arm_lbabi_tag *)((char *)(a) + ATAG_SIZE(a))
+
+#endif /* __MACHINE_ATAGS_H__ */
diff --git a/sys/arm/include/atomic-v6.h b/sys/arm/include/atomic-v6.h
new file mode 100644
index 000000000000..b81ad6447ef7
--- /dev/null
+++ b/sys/arm/include/atomic-v6.h
@@ -0,0 +1,1050 @@
+/* $NetBSD: atomic.h,v 1.1 2002/10/19 12:22:34 bsh Exp $ */
+
+/*-
+ * Copyright (C) 2003-2004 Olivier Houchard
+ * Copyright (C) 1994-1997 Mark Brinicombe
+ * Copyright (C) 1994 Brini
+ * All rights reserved.
+ *
+ * This code is derived from software written for Brini by Mark Brinicombe
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Brini.
+ * 4. The name of Brini may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BRINI BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_ATOMIC_V6_H_
+#define _MACHINE_ATOMIC_V6_H_
+
+#ifndef _MACHINE_ATOMIC_H_
+#error Do not include this file directly, use <machine/atomic.h>
+#endif
+
+#if __ARM_ARCH >= 7
+#define isb() __asm __volatile("isb" : : : "memory")
+#define dsb() __asm __volatile("dsb" : : : "memory")
+#define dmb() __asm __volatile("dmb" : : : "memory")
+#else
+#define isb() __asm __volatile("mcr p15, 0, %0, c7, c5, 4" : : "r" (0) : "memory")
+#define dsb() __asm __volatile("mcr p15, 0, %0, c7, c10, 4" : : "r" (0) : "memory")
+#define dmb() __asm __volatile("mcr p15, 0, %0, c7, c10, 5" : : "r" (0) : "memory")
+#endif
+
+#define mb() dmb()
+#define wmb() dmb()
+#define rmb() dmb()
+
+#define ARM_HAVE_ATOMIC64
+
+#define ATOMIC_ACQ_REL_LONG(NAME) \
+static __inline void \
+atomic_##NAME##_acq_long(__volatile u_long *p, u_long v) \
+{ \
+ atomic_##NAME##_long(p, v); \
+ dmb(); \
+} \
+ \
+static __inline void \
+atomic_##NAME##_rel_long(__volatile u_long *p, u_long v) \
+{ \
+ dmb(); \
+ atomic_##NAME##_long(p, v); \
+}
+
+#define ATOMIC_ACQ_REL(NAME, WIDTH) \
+static __inline void \
+atomic_##NAME##_acq_##WIDTH(__volatile uint##WIDTH##_t *p, uint##WIDTH##_t v)\
+{ \
+ atomic_##NAME##_##WIDTH(p, v); \
+ dmb(); \
+} \
+ \
+static __inline void \
+atomic_##NAME##_rel_##WIDTH(__volatile uint##WIDTH##_t *p, uint##WIDTH##_t v)\
+{ \
+ dmb(); \
+ atomic_##NAME##_##WIDTH(p, v); \
+}
+
+static __inline void
+atomic_add_32(volatile uint32_t *p, uint32_t val)
+{
+ uint32_t tmp = 0, tmp2 = 0;
+
+ __asm __volatile(
+ "1: ldrex %0, [%2] \n"
+ " add %0, %0, %3 \n"
+ " strex %1, %0, [%2] \n"
+ " cmp %1, #0 \n"
+ " it ne \n"
+ " bne 1b \n"
+ : "=&r" (tmp), "+r" (tmp2)
+ ,"+r" (p), "+r" (val) : : "cc", "memory");
+}
+
+static __inline void
+atomic_add_64(volatile uint64_t *p, uint64_t val)
+{
+ uint64_t tmp;
+ uint32_t exflag;
+
+ __asm __volatile(
+ "1: \n"
+ " ldrexd %Q[tmp], %R[tmp], [%[ptr]] \n"
+ " adds %Q[tmp], %Q[val] \n"
+ " adc %R[tmp], %R[tmp], %R[val] \n"
+ " strexd %[exf], %Q[tmp], %R[tmp], [%[ptr]] \n"
+ " teq %[exf], #0 \n"
+ " it ne \n"
+ " bne 1b \n"
+ : [exf] "=&r" (exflag),
+ [tmp] "=&r" (tmp)
+ : [ptr] "r" (p),
+ [val] "r" (val)
+ : "cc", "memory");
+}
+
+static __inline void
+atomic_add_long(volatile u_long *p, u_long val)
+{
+
+ atomic_add_32((volatile uint32_t *)p, val);
+}
+
+ATOMIC_ACQ_REL(add, 32)
+ATOMIC_ACQ_REL(add, 64)
+ATOMIC_ACQ_REL_LONG(add)
+
+static __inline void
+atomic_clear_32(volatile uint32_t *address, uint32_t setmask)
+{
+ uint32_t tmp = 0, tmp2 = 0;
+
+ __asm __volatile(
+ "1: ldrex %0, [%2] \n"
+ " bic %0, %0, %3 \n"
+ " strex %1, %0, [%2] \n"
+ " cmp %1, #0 \n"
+ " it ne \n"
+ " bne 1b \n"
+ : "=&r" (tmp), "+r" (tmp2), "+r" (address), "+r" (setmask)
+ : : "cc", "memory");
+}
+
+static __inline void
+atomic_clear_64(volatile uint64_t *p, uint64_t val)
+{
+ uint64_t tmp;
+ uint32_t exflag;
+
+ __asm __volatile(
+ "1: \n"
+ " ldrexd %Q[tmp], %R[tmp], [%[ptr]] \n"
+ " bic %Q[tmp], %Q[val] \n"
+ " bic %R[tmp], %R[val] \n"
+ " strexd %[exf], %Q[tmp], %R[tmp], [%[ptr]] \n"
+ " teq %[exf], #0 \n"
+ " it ne \n"
+ " bne 1b \n"
+ : [exf] "=&r" (exflag),
+ [tmp] "=&r" (tmp)
+ : [ptr] "r" (p),
+ [val] "r" (val)
+ : "cc", "memory");
+}
+
+static __inline void
+atomic_clear_long(volatile u_long *address, u_long setmask)
+{
+
+ atomic_clear_32((volatile uint32_t *)address, setmask);
+}
+
+ATOMIC_ACQ_REL(clear, 32)
+ATOMIC_ACQ_REL(clear, 64)
+ATOMIC_ACQ_REL_LONG(clear)
+
+#define ATOMIC_FCMPSET_CODE(RET, TYPE, SUF) \
+ { \
+ TYPE tmp; \
+ \
+ __asm __volatile( \
+ "1: ldrex" SUF " %[tmp], [%[ptr]] \n" \
+ " ldr" SUF " %[ret], [%[oldv]] \n" \
+ " teq %[tmp], %[ret] \n" \
+ " ittee ne \n" \
+ " str" SUF "ne %[tmp], [%[oldv]] \n" \
+ " movne %[ret], #0 \n" \
+ " strex" SUF "eq %[ret], %[newv], [%[ptr]] \n" \
+ " eorseq %[ret], #1 \n" \
+ " beq 1b \n" \
+ : [ret] "=&r" (RET), \
+ [tmp] "=&r" (tmp) \
+ : [ptr] "r" (_ptr), \
+ [oldv] "r" (_old), \
+ [newv] "r" (_new) \
+ : "cc", "memory"); \
+ }
+
+#define ATOMIC_FCMPSET_CODE64(RET) \
+ { \
+ uint64_t cmp, tmp; \
+ \
+ __asm __volatile( \
+ "1: ldrexd %Q[tmp], %R[tmp], [%[ptr]] \n" \
+ " ldrd %Q[cmp], %R[cmp], [%[oldv]] \n" \
+ " teq %Q[tmp], %Q[cmp] \n" \
+ " it eq \n" \
+ " teqeq %R[tmp], %R[cmp] \n" \
+ " ittee ne \n" \
+ " movne %[ret], #0 \n" \
+ " strdne %[cmp], [%[oldv]] \n" \
+ " strexdeq %[ret], %Q[newv], %R[newv], [%[ptr]] \n" \
+ " eorseq %[ret], #1 \n" \
+ " beq 1b \n" \
+ : [ret] "=&r" (RET), \
+ [cmp] "=&r" (cmp), \
+ [tmp] "=&r" (tmp) \
+ : [ptr] "r" (_ptr), \
+ [oldv] "r" (_old), \
+ [newv] "r" (_new) \
+ : "cc", "memory"); \
+ }
+
+static __inline int
+atomic_fcmpset_8(volatile uint8_t *_ptr, uint8_t *_old, uint8_t _new)
+{
+ int ret;
+
+ ATOMIC_FCMPSET_CODE(ret, uint8_t, "b");
+ return (ret);
+}
+#define atomic_fcmpset_8 atomic_fcmpset_8
+
+static __inline int
+atomic_fcmpset_acq_8(volatile uint8_t *_ptr, uint8_t *_old, uint8_t _new)
+{
+ int ret;
+
+ ATOMIC_FCMPSET_CODE(ret, uint8_t, "b");
+ dmb();
+ return (ret);
+}
+
+static __inline int
+atomic_fcmpset_rel_8(volatile uint8_t *_ptr, uint8_t *_old, uint8_t _new)
+{
+ int ret;
+
+ dmb();
+ ATOMIC_FCMPSET_CODE(ret, uint8_t, "b");
+ return (ret);
+}
+
+static __inline int
+atomic_fcmpset_16(volatile uint16_t *_ptr, uint16_t *_old, uint16_t _new)
+{
+ int ret;
+
+ ATOMIC_FCMPSET_CODE(ret, uint16_t, "h");
+ return (ret);
+}
+#define atomic_fcmpset_16 atomic_fcmpset_16
+
+static __inline int
+atomic_fcmpset_acq_16(volatile uint16_t *_ptr, uint16_t *_old, uint16_t _new)
+{
+ int ret;
+
+ ATOMIC_FCMPSET_CODE(ret, uint16_t, "h");
+ dmb();
+ return (ret);
+}
+
+static __inline int
+atomic_fcmpset_rel_16(volatile uint16_t *_ptr, uint16_t *_old, uint16_t _new)
+{
+ int ret;
+
+ dmb();
+ ATOMIC_FCMPSET_CODE(ret, uint16_t, "h");
+ return (ret);
+}
+
+static __inline int
+atomic_fcmpset_32(volatile uint32_t *_ptr, uint32_t *_old, uint32_t _new)
+{
+ int ret;
+
+ ATOMIC_FCMPSET_CODE(ret, uint32_t, "");
+ return (ret);
+}
+
+static __inline int
+atomic_fcmpset_acq_32(volatile uint32_t *_ptr, uint32_t *_old, uint32_t _new)
+{
+ int ret;
+
+ ATOMIC_FCMPSET_CODE(ret, uint32_t, "");
+ dmb();
+ return (ret);
+}
+
+static __inline int
+atomic_fcmpset_rel_32(volatile uint32_t *_ptr, uint32_t *_old, uint32_t _new)
+{
+ int ret;
+
+ dmb();
+ ATOMIC_FCMPSET_CODE(ret, uint32_t, "");
+ return (ret);
+}
+
+static __inline int
+atomic_fcmpset_long(volatile u_long *_ptr, u_long *_old, u_long _new)
+{
+ int ret;
+
+ ATOMIC_FCMPSET_CODE(ret, u_long, "");
+ return (ret);
+}
+
+static __inline int
+atomic_fcmpset_acq_long(volatile u_long *_ptr, u_long *_old, u_long _new)
+{
+ int ret;
+
+ ATOMIC_FCMPSET_CODE(ret, u_long, "");
+ dmb();
+ return (ret);
+}
+
+static __inline int
+atomic_fcmpset_rel_long(volatile u_long *_ptr, u_long *_old, u_long _new)
+{
+ int ret;
+
+ dmb();
+ ATOMIC_FCMPSET_CODE(ret, u_long, "");
+ return (ret);
+}
+
+static __inline int
+atomic_fcmpset_64(volatile uint64_t *_ptr, uint64_t *_old, uint64_t _new)
+{
+ int ret;
+
+ ATOMIC_FCMPSET_CODE64(ret);
+ return (ret);
+}
+
+static __inline int
+atomic_fcmpset_acq_64(volatile uint64_t *_ptr, uint64_t *_old, uint64_t _new)
+{
+ int ret;
+
+ ATOMIC_FCMPSET_CODE64(ret);
+ dmb();
+ return (ret);
+}
+
+static __inline int
+atomic_fcmpset_rel_64(volatile uint64_t *_ptr, uint64_t *_old, uint64_t _new)
+{
+ int ret;
+
+ dmb();
+ ATOMIC_FCMPSET_CODE64(ret);
+ return (ret);
+}
+
+#define ATOMIC_CMPSET_CODE(RET, SUF) \
+ { \
+ __asm __volatile( \
+ "1: ldrex" SUF " %[ret], [%[ptr]] \n" \
+ " teq %[ret], %[oldv] \n" \
+ " itee ne \n" \
+ " movne %[ret], #0 \n" \
+ " strex" SUF "eq %[ret], %[newv], [%[ptr]] \n" \
+ " eorseq %[ret], #1 \n" \
+ " beq 1b \n" \
+ : [ret] "=&r" (RET) \
+ : [ptr] "r" (_ptr), \
+ [oldv] "r" (_old), \
+ [newv] "r" (_new) \
+ : "cc", "memory"); \
+ }
+
+#define ATOMIC_CMPSET_CODE64(RET) \
+ { \
+ uint64_t tmp; \
+ \
+ __asm __volatile( \
+ "1: ldrexd %Q[tmp], %R[tmp], [%[ptr]] \n" \
+ " teq %Q[tmp], %Q[oldv] \n" \
+ " it eq \n" \
+ " teqeq %R[tmp], %R[oldv] \n" \
+ " itee ne \n" \
+ " movne %[ret], #0 \n" \
+ " strexdeq %[ret], %Q[newv], %R[newv], [%[ptr]] \n" \
+ " eorseq %[ret], #1 \n" \
+ " beq 1b \n" \
+ : [ret] "=&r" (RET), \
+ [tmp] "=&r" (tmp) \
+ : [ptr] "r" (_ptr), \
+ [oldv] "r" (_old), \
+ [newv] "r" (_new) \
+ : "cc", "memory"); \
+ }
+
+static __inline int
+atomic_cmpset_8(volatile uint8_t *_ptr, uint8_t _old, uint8_t _new)
+{
+ int ret;
+
+ ATOMIC_CMPSET_CODE(ret, "b");
+ return (ret);
+}
+#define atomic_cmpset_8 atomic_cmpset_8
+
+static __inline int
+atomic_cmpset_acq_8(volatile uint8_t *_ptr, uint8_t _old, uint8_t _new)
+{
+ int ret;
+
+ ATOMIC_CMPSET_CODE(ret, "b");
+ dmb();
+ return (ret);
+}
+
+static __inline int
+atomic_cmpset_rel_8(volatile uint8_t *_ptr, uint8_t _old, uint8_t _new)
+{
+ int ret;
+
+ dmb();
+ ATOMIC_CMPSET_CODE(ret, "b");
+ return (ret);
+}
+
+static __inline int
+atomic_cmpset_16(volatile uint16_t *_ptr, uint16_t _old, uint16_t _new)
+{
+ int ret;
+
+ ATOMIC_CMPSET_CODE(ret, "h");
+ return (ret);
+}
+#define atomic_cmpset_16 atomic_cmpset_16
+
+static __inline int
+atomic_cmpset_acq_16(volatile uint16_t *_ptr, uint16_t _old, uint16_t _new)
+{
+ int ret;
+
+ ATOMIC_CMPSET_CODE(ret, "h");
+ dmb();
+ return (ret);
+}
+
+static __inline int
+atomic_cmpset_rel_16(volatile uint16_t *_ptr, uint16_t _old, uint16_t _new)
+{
+ int ret;
+
+ dmb();
+ ATOMIC_CMPSET_CODE(ret, "h");
+ return (ret);
+}
+
+static __inline int
+atomic_cmpset_32(volatile uint32_t *_ptr, uint32_t _old, uint32_t _new)
+{
+ int ret;
+
+ ATOMIC_CMPSET_CODE(ret, "");
+ return (ret);
+}
+
+static __inline int
+atomic_cmpset_acq_32(volatile uint32_t *_ptr, uint32_t _old, uint32_t _new)
+{
+ int ret;
+
+ ATOMIC_CMPSET_CODE(ret, "");
+ dmb();
+ return (ret);
+}
+
+static __inline int
+atomic_cmpset_rel_32(volatile uint32_t *_ptr, uint32_t _old, uint32_t _new)
+{
+ int ret;
+
+ dmb();
+ ATOMIC_CMPSET_CODE(ret, "");
+ return (ret);
+}
+
+static __inline int
+atomic_cmpset_long(volatile u_long *_ptr, u_long _old, u_long _new)
+{
+ int ret;
+
+ ATOMIC_CMPSET_CODE(ret, "");
+ return (ret);
+}
+
+static __inline int
+atomic_cmpset_acq_long(volatile u_long *_ptr, u_long _old, u_long _new)
+{
+ int ret;
+
+ ATOMIC_CMPSET_CODE(ret, "");
+ dmb();
+ return (ret);
+}
+
+static __inline int
+atomic_cmpset_rel_long(volatile u_long *_ptr, u_long _old, u_long _new)
+{
+ int ret;
+
+ dmb();
+ ATOMIC_CMPSET_CODE(ret, "");
+ return (ret);
+}
+
+static __inline int
+atomic_cmpset_64(volatile uint64_t *_ptr, uint64_t _old, uint64_t _new)
+{
+ int ret;
+
+ ATOMIC_CMPSET_CODE64(ret);
+ return (ret);
+}
+
+static __inline int
+atomic_cmpset_acq_64(volatile uint64_t *_ptr, uint64_t _old, uint64_t _new)
+{
+ int ret;
+
+ ATOMIC_CMPSET_CODE64(ret);
+ dmb();
+ return (ret);
+}
+
+static __inline int
+atomic_cmpset_rel_64(volatile uint64_t *_ptr, uint64_t _old, uint64_t _new)
+{
+ int ret;
+
+ dmb();
+ ATOMIC_CMPSET_CODE64(ret);
+ return (ret);
+}
+
+static __inline uint32_t
+atomic_fetchadd_32(volatile uint32_t *p, uint32_t val)
+{
+ uint32_t tmp = 0, tmp2 = 0, ret = 0;
+
+ __asm __volatile(
+ "1: ldrex %0, [%3] \n"
+ " add %1, %0, %4 \n"
+ " strex %2, %1, [%3] \n"
+ " cmp %2, #0 \n"
+ " it ne \n"
+ " bne 1b \n"
+ : "+r" (ret), "=&r" (tmp), "+r" (tmp2), "+r" (p), "+r" (val)
+ : : "cc", "memory");
+ return (ret);
+}
+
+static __inline uint64_t
+atomic_fetchadd_64(volatile uint64_t *p, uint64_t val)
+{
+ uint64_t ret, tmp;
+ uint32_t exflag;
+
+ __asm __volatile(
+ "1: \n"
+ " ldrexd %Q[ret], %R[ret], [%[ptr]] \n"
+ " adds %Q[tmp], %Q[ret], %Q[val] \n"
+ " adc %R[tmp], %R[ret], %R[val] \n"
+ " strexd %[exf], %Q[tmp], %R[tmp], [%[ptr]] \n"
+ " teq %[exf], #0 \n"
+ " it ne \n"
+ " bne 1b \n"
+ : [ret] "=&r" (ret),
+ [exf] "=&r" (exflag),
+ [tmp] "=&r" (tmp)
+ : [ptr] "r" (p),
+ [val] "r" (val)
+ : "cc", "memory");
+ return (ret);
+}
+
+static __inline u_long
+atomic_fetchadd_long(volatile u_long *p, u_long val)
+{
+
+ return (atomic_fetchadd_32((volatile uint32_t *)p, val));
+}
+
+static __inline uint32_t
+atomic_load_acq_32(volatile uint32_t *p)
+{
+ uint32_t v;
+
+ v = *p;
+ dmb();
+ return (v);
+}
+
+static __inline uint64_t
+atomic_load_64(volatile uint64_t *p)
+{
+ uint64_t ret;
+
+ /*
+ * The only way to atomically load 64 bits is with LDREXD which puts the
+ * exclusive monitor into the exclusive state, so reset it to open state
+ * with CLREX because we don't actually need to store anything.
+ */
+ __asm __volatile(
+ "ldrexd %Q[ret], %R[ret], [%[ptr]] \n"
+ "clrex \n"
+ : [ret] "=&r" (ret)
+ : [ptr] "r" (p)
+ : "cc", "memory");
+ return (ret);
+}
+
+static __inline uint64_t
+atomic_load_acq_64(volatile uint64_t *p)
+{
+ uint64_t ret;
+
+ ret = atomic_load_64(p);
+ dmb();
+ return (ret);
+}
+
+static __inline u_long
+atomic_load_acq_long(volatile u_long *p)
+{
+ u_long v;
+
+ v = *p;
+ dmb();
+ return (v);
+}
+
+static __inline uint32_t
+atomic_readandclear_32(volatile uint32_t *p)
+{
+ uint32_t ret, tmp = 0, tmp2 = 0;
+
+ __asm __volatile(
+ "1: ldrex %0, [%3] \n"
+ " mov %1, #0 \n"
+ " strex %2, %1, [%3] \n"
+ " cmp %2, #0 \n"
+ " it ne \n"
+ " bne 1b \n"
+ : "=r" (ret), "=&r" (tmp), "+r" (tmp2), "+r" (p)
+ : : "cc", "memory");
+ return (ret);
+}
+
+static __inline uint64_t
+atomic_readandclear_64(volatile uint64_t *p)
+{
+ uint64_t ret, tmp;
+ uint32_t exflag;
+
+ __asm __volatile(
+ "1: \n"
+ " ldrexd %Q[ret], %R[ret], [%[ptr]] \n"
+ " mov %Q[tmp], #0 \n"
+ " mov %R[tmp], #0 \n"
+ " strexd %[exf], %Q[tmp], %R[tmp], [%[ptr]] \n"
+ " teq %[exf], #0 \n"
+ " it ne \n"
+ " bne 1b \n"
+ : [ret] "=&r" (ret),
+ [exf] "=&r" (exflag),
+ [tmp] "=&r" (tmp)
+ : [ptr] "r" (p)
+ : "cc", "memory");
+ return (ret);
+}
+
+static __inline u_long
+atomic_readandclear_long(volatile u_long *p)
+{
+
+ return (atomic_readandclear_32((volatile uint32_t *)p));
+}
+
+static __inline void
+atomic_set_32(volatile uint32_t *address, uint32_t setmask)
+{
+ uint32_t tmp = 0, tmp2 = 0;
+
+ __asm __volatile(
+ "1: ldrex %0, [%2] \n"
+ " orr %0, %0, %3 \n"
+ " strex %1, %0, [%2] \n"
+ " cmp %1, #0 \n"
+ " it ne \n"
+ " bne 1b \n"
+ : "=&r" (tmp), "+r" (tmp2), "+r" (address), "+r" (setmask)
+ : : "cc", "memory");
+}
+
+static __inline void
+atomic_set_64(volatile uint64_t *p, uint64_t val)
+{
+ uint64_t tmp;
+ uint32_t exflag;
+
+ __asm __volatile(
+ "1: \n"
+ " ldrexd %Q[tmp], %R[tmp], [%[ptr]] \n"
+ " orr %Q[tmp], %Q[val] \n"
+ " orr %R[tmp], %R[val] \n"
+ " strexd %[exf], %Q[tmp], %R[tmp], [%[ptr]] \n"
+ " teq %[exf], #0 \n"
+ " it ne \n"
+ " bne 1b \n"
+ : [exf] "=&r" (exflag),
+ [tmp] "=&r" (tmp)
+ : [ptr] "r" (p),
+ [val] "r" (val)
+ : "cc", "memory");
+}
+
+static __inline void
+atomic_set_long(volatile u_long *address, u_long setmask)
+{
+
+ atomic_set_32((volatile uint32_t *)address, setmask);
+}
+
+ATOMIC_ACQ_REL(set, 32)
+ATOMIC_ACQ_REL(set, 64)
+ATOMIC_ACQ_REL_LONG(set)
+
+static __inline void
+atomic_subtract_32(volatile uint32_t *p, uint32_t val)
+{
+ uint32_t tmp = 0, tmp2 = 0;
+
+ __asm __volatile(
+ "1: ldrex %0, [%2] \n"
+ " sub %0, %0, %3 \n"
+ " strex %1, %0, [%2] \n"
+ " cmp %1, #0 \n"
+ " it ne \n"
+ " bne 1b \n"
+ : "=&r" (tmp), "+r" (tmp2), "+r" (p), "+r" (val)
+ : : "cc", "memory");
+}
+
+static __inline void
+atomic_subtract_64(volatile uint64_t *p, uint64_t val)
+{
+ uint64_t tmp;
+ uint32_t exflag;
+
+ __asm __volatile(
+ "1: \n"
+ " ldrexd %Q[tmp], %R[tmp], [%[ptr]] \n"
+ " subs %Q[tmp], %Q[val] \n"
+ " sbc %R[tmp], %R[tmp], %R[val] \n"
+ " strexd %[exf], %Q[tmp], %R[tmp], [%[ptr]] \n"
+ " teq %[exf], #0 \n"
+ " it ne \n"
+ " bne 1b \n"
+ : [exf] "=&r" (exflag),
+ [tmp] "=&r" (tmp)
+ : [ptr] "r" (p),
+ [val] "r" (val)
+ : "cc", "memory");
+}
+
+static __inline void
+atomic_subtract_long(volatile u_long *p, u_long val)
+{
+
+ atomic_subtract_32((volatile uint32_t *)p, val);
+}
+
+ATOMIC_ACQ_REL(subtract, 32)
+ATOMIC_ACQ_REL(subtract, 64)
+ATOMIC_ACQ_REL_LONG(subtract)
+
+static __inline void
+atomic_store_64(volatile uint64_t *p, uint64_t val)
+{
+ uint64_t tmp;
+ uint32_t exflag;
+
+ /*
+ * The only way to atomically store 64 bits is with STREXD, which will
+ * succeed only if paired up with a preceeding LDREXD using the same
+ * address, so we read and discard the existing value before storing.
+ */
+ __asm __volatile(
+ "1: \n"
+ " ldrexd %Q[tmp], %R[tmp], [%[ptr]] \n"
+ " strexd %[exf], %Q[val], %R[val], [%[ptr]] \n"
+ " teq %[exf], #0 \n"
+ " it ne \n"
+ " bne 1b \n"
+ : [tmp] "=&r" (tmp),
+ [exf] "=&r" (exflag)
+ : [ptr] "r" (p),
+ [val] "r" (val)
+ : "cc", "memory");
+}
+
+static __inline void
+atomic_store_rel_32(volatile uint32_t *p, uint32_t v)
+{
+
+ dmb();
+ *p = v;
+}
+
+static __inline void
+atomic_store_rel_64(volatile uint64_t *p, uint64_t val)
+{
+
+ dmb();
+ atomic_store_64(p, val);
+}
+
+static __inline void
+atomic_store_rel_long(volatile u_long *p, u_long v)
+{
+
+ dmb();
+ *p = v;
+}
+
+static __inline int
+atomic_testandclear_32(volatile uint32_t *ptr, u_int bit)
+{
+ int newv, oldv, result;
+
+ __asm __volatile(
+ " mov ip, #1 \n"
+ " lsl ip, ip, %[bit] \n"
+ /* Done with %[bit] as input, reuse below as output. */
+ "1: \n"
+ " ldrex %[oldv], [%[ptr]] \n"
+ " bic %[newv], %[oldv], ip \n"
+ " strex %[bit], %[newv], [%[ptr]] \n"
+ " teq %[bit], #0 \n"
+ " it ne \n"
+ " bne 1b \n"
+ " ands %[bit], %[oldv], ip \n"
+ " it ne \n"
+ " movne %[bit], #1 \n"
+ : [bit] "=&r" (result),
+ [oldv] "=&r" (oldv),
+ [newv] "=&r" (newv)
+ : [ptr] "r" (ptr),
+ "[bit]" (bit & 0x1f)
+ : "cc", "ip", "memory");
+
+ return (result);
+}
+
+static __inline int
+atomic_testandclear_int(volatile u_int *p, u_int v)
+{
+
+ return (atomic_testandclear_32((volatile uint32_t *)p, v));
+}
+
+static __inline int
+atomic_testandclear_long(volatile u_long *p, u_int v)
+{
+
+ return (atomic_testandclear_32((volatile uint32_t *)p, v));
+}
+#define atomic_testandclear_long atomic_testandclear_long
+
+
+static __inline int
+atomic_testandclear_64(volatile uint64_t *p, u_int v)
+{
+ volatile uint32_t *p32;
+
+ p32 = (volatile uint32_t *)p;
+ /*
+ * Assume little-endian,
+ * atomic_testandclear_32() uses only last 5 bits of v
+ */
+ if ((v & 0x20) != 0)
+ p32++;
+ return (atomic_testandclear_32(p32, v));
+}
+
+static __inline int
+atomic_testandset_32(volatile uint32_t *ptr, u_int bit)
+{
+ int newv, oldv, result;
+
+ __asm __volatile(
+ " mov ip, #1 \n"
+ " lsl ip, ip, %[bit] \n"
+ /* Done with %[bit] as input, reuse below as output. */
+ "1: \n"
+ " ldrex %[oldv], [%[ptr]] \n"
+ " orr %[newv], %[oldv], ip \n"
+ " strex %[bit], %[newv], [%[ptr]] \n"
+ " teq %[bit], #0 \n"
+ " it ne \n"
+ " bne 1b \n"
+ " ands %[bit], %[oldv], ip \n"
+ " it ne \n"
+ " movne %[bit], #1 \n"
+ : [bit] "=&r" (result),
+ [oldv] "=&r" (oldv),
+ [newv] "=&r" (newv)
+ : [ptr] "r" (ptr),
+ "[bit]" (bit & 0x1f)
+ : "cc", "ip", "memory");
+
+ return (result);
+}
+
+static __inline int
+atomic_testandset_int(volatile u_int *p, u_int v)
+{
+
+ return (atomic_testandset_32((volatile uint32_t *)p, v));
+}
+
+static __inline int
+atomic_testandset_long(volatile u_long *p, u_int v)
+{
+
+ return (atomic_testandset_32((volatile uint32_t *)p, v));
+}
+#define atomic_testandset_long atomic_testandset_long
+
+static __inline int
+atomic_testandset_64(volatile uint64_t *p, u_int v)
+{
+ volatile uint32_t *p32;
+
+ p32 = (volatile uint32_t *)p;
+ /*
+ * Assume little-endian,
+ * atomic_testandset_32() uses only last 5 bits of v
+ */
+ if ((v & 0x20) != 0)
+ p32++;
+ return (atomic_testandset_32(p32, v));
+}
+
+static __inline uint32_t
+atomic_swap_32(volatile uint32_t *p, uint32_t v)
+{
+ uint32_t ret, exflag;
+
+ __asm __volatile(
+ "1: ldrex %[ret], [%[ptr]] \n"
+ " strex %[exf], %[val], [%[ptr]] \n"
+ " teq %[exf], #0 \n"
+ " it ne \n"
+ " bne 1b \n"
+ : [ret] "=&r" (ret),
+ [exf] "=&r" (exflag)
+ : [val] "r" (v),
+ [ptr] "r" (p)
+ : "cc", "memory");
+ return (ret);
+}
+
+static __inline uint64_t
+atomic_swap_64(volatile uint64_t *p, uint64_t v)
+{
+ uint64_t ret;
+ uint32_t exflag;
+
+ __asm __volatile(
+ "1: ldrexd %Q[ret], %R[ret], [%[ptr]] \n"
+ " strexd %[exf], %Q[val], %R[val], [%[ptr]] \n"
+ " teq %[exf], #0 \n"
+ " it ne \n"
+ " bne 1b \n"
+ : [ret] "=&r" (ret),
+ [exf] "=&r" (exflag)
+ : [val] "r" (v),
+ [ptr] "r" (p)
+ : "cc", "memory");
+ return (ret);
+}
+
+#undef ATOMIC_ACQ_REL
+#undef ATOMIC_ACQ_REL_LONG
+
+static __inline void
+atomic_thread_fence_acq(void)
+{
+
+ dmb();
+}
+
+static __inline void
+atomic_thread_fence_rel(void)
+{
+
+ dmb();
+}
+
+static __inline void
+atomic_thread_fence_acq_rel(void)
+{
+
+ dmb();
+}
+
+static __inline void
+atomic_thread_fence_seq_cst(void)
+{
+
+ dmb();
+}
+
+#endif /* _MACHINE_ATOMIC_V6_H_ */
diff --git a/sys/arm/include/atomic.h b/sys/arm/include/atomic.h
new file mode 100644
index 000000000000..f5d98d920ebd
--- /dev/null
+++ b/sys/arm/include/atomic.h
@@ -0,0 +1,104 @@
+/* $NetBSD: atomic.h,v 1.1 2002/10/19 12:22:34 bsh Exp $ */
+
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (C) 2003-2004 Olivier Houchard
+ * Copyright (C) 1994-1997 Mark Brinicombe
+ * Copyright (C) 1994 Brini
+ * All rights reserved.
+ *
+ * This code is derived from software written for Brini by Mark Brinicombe
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Brini.
+ * 4. The name of Brini may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BRINI BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_ATOMIC_H_
+#define _MACHINE_ATOMIC_H_
+
+#include <sys/atomic_common.h>
+
+#include <machine/armreg.h>
+
+#ifndef _KERNEL
+#include <machine/sysarch.h>
+#endif
+
+#include <machine/atomic-v6.h>
+
+static __inline u_long
+atomic_swap_long(volatile u_long *p, u_long v)
+{
+
+ return (atomic_swap_32((volatile uint32_t *)p, v));
+}
+
+#define atomic_clear_ptr atomic_clear_32
+#define atomic_clear_acq_ptr atomic_clear_acq_32
+#define atomic_clear_rel_ptr atomic_clear_rel_32
+#define atomic_set_ptr atomic_set_32
+#define atomic_set_acq_ptr atomic_set_acq_32
+#define atomic_set_rel_ptr atomic_set_rel_32
+#define atomic_fcmpset_ptr atomic_fcmpset_32
+#define atomic_fcmpset_rel_ptr atomic_fcmpset_rel_32
+#define atomic_fcmpset_acq_ptr atomic_fcmpset_acq_32
+#define atomic_cmpset_ptr atomic_cmpset_32
+#define atomic_cmpset_acq_ptr atomic_cmpset_acq_32
+#define atomic_cmpset_rel_ptr atomic_cmpset_rel_32
+#define atomic_load_acq_ptr atomic_load_acq_32
+#define atomic_store_rel_ptr atomic_store_rel_32
+#define atomic_swap_ptr atomic_swap_32
+#define atomic_readandclear_ptr atomic_readandclear_32
+
+#define atomic_add_int atomic_add_32
+#define atomic_add_acq_int atomic_add_acq_32
+#define atomic_add_rel_int atomic_add_rel_32
+#define atomic_subtract_int atomic_subtract_32
+#define atomic_subtract_acq_int atomic_subtract_acq_32
+#define atomic_subtract_rel_int atomic_subtract_rel_32
+#define atomic_clear_int atomic_clear_32
+#define atomic_clear_acq_int atomic_clear_acq_32
+#define atomic_clear_rel_int atomic_clear_rel_32
+#define atomic_set_int atomic_set_32
+#define atomic_set_acq_int atomic_set_acq_32
+#define atomic_set_rel_int atomic_set_rel_32
+#define atomic_fcmpset_int atomic_fcmpset_32
+#define atomic_fcmpset_acq_int atomic_fcmpset_acq_32
+#define atomic_fcmpset_rel_int atomic_fcmpset_rel_32
+#define atomic_cmpset_int atomic_cmpset_32
+#define atomic_cmpset_acq_int atomic_cmpset_acq_32
+#define atomic_cmpset_rel_int atomic_cmpset_rel_32
+#define atomic_fetchadd_int atomic_fetchadd_32
+#define atomic_readandclear_int atomic_readandclear_32
+#define atomic_load_acq_int atomic_load_acq_32
+#define atomic_store_rel_int atomic_store_rel_32
+#define atomic_swap_int atomic_swap_32
+
+#include <sys/_atomic_subword.h>
+
+#endif /* _MACHINE_ATOMIC_H_ */
diff --git a/sys/arm/include/blockio.h b/sys/arm/include/blockio.h
new file mode 100644
index 000000000000..963ddfc082fe
--- /dev/null
+++ b/sys/arm/include/blockio.h
@@ -0,0 +1,58 @@
+/* $NetBSD: blockio.h,v 1.2 2001/06/02 10:44:56 bjh21 Exp $ */
+
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 2001 Ben Harris
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+/*
+ * blockio.h - low level functions for bulk PIO data transfer
+ */
+
+#ifndef _MACHINE_BLOCKIO_H_
+#define _MACHINE_BLOCKIO_H_
+
+/*
+ * All these take three arguments:
+ * I/O address
+ * Memory address
+ * Number of bytes to copy
+ */
+
+void read_multi_1(u_int, void *, u_int);
+void write_multi_1(u_int, const void *, u_int);
+#define read_multi_2 insw16
+#define write_multi_2 outsw16
+
+void insw(u_int, void *, u_int);
+void outsw(u_int, void *, u_int);
+void insw16(u_int, void *, u_int);
+void outsw16(u_int, void *, u_int);
+
+#endif /* !_MACHINE_BLOCKIO_H_ */
diff --git a/sys/arm/include/bus.h b/sys/arm/include/bus.h
new file mode 100644
index 000000000000..e1c7098a69f0
--- /dev/null
+++ b/sys/arm/include/bus.h
@@ -0,0 +1,784 @@
+/* $NetBSD: bus.h,v 1.11 2003/07/28 17:35:54 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 1996, 1997, 1998, 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
+ * NASA Ames Research Center.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 1996 Charles M. Hannum. All rights reserved.
+ * Copyright (c) 1996 Christopher G. Demetriou. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Christopher G. Demetriou
+ * for the NetBSD Project.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_BUS_H_
+#define _MACHINE_BUS_H_
+
+#include <machine/_bus.h>
+
+/*
+ * int bus_space_map (bus_space_tag_t t, bus_addr_t addr,
+ * bus_size_t size, int flags, bus_space_handle_t *bshp);
+ *
+ * Map a region of bus space.
+ */
+
+#define BUS_SPACE_MAP_CACHEABLE 0x01
+#define BUS_SPACE_MAP_LINEAR 0x02
+#define BUS_SPACE_MAP_PREFETCHABLE 0x04
+
+/*
+ * Bus space for ARM.
+ *
+ * The functions used most often are grouped together at the beginning to ensure
+ * that all the data fits into a single cache line. The inline implementations
+ * of single read/write access these values a lot.
+ */
+struct bus_space {
+ /* Read/write single and barrier: the most commonly used functions. */
+ uint8_t (*bs_r_1)(bus_space_tag_t, bus_space_handle_t, bus_size_t);
+ uint32_t (*bs_r_4)(bus_space_tag_t, bus_space_handle_t, bus_size_t);
+ void (*bs_w_1)(bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, uint8_t);
+ void (*bs_w_4)(bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, uint32_t);
+ void (*bs_barrier)(bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, bus_size_t, int);
+
+ /* Backlink to parent (if copied), and implementation private data. */
+ struct bus_space *bs_parent;
+ void *bs_privdata;
+
+ /* mapping/unmapping */
+ int (*bs_map) (bus_space_tag_t, bus_addr_t, bus_size_t,
+ int, bus_space_handle_t *);
+ void (*bs_unmap) (bus_space_tag_t, bus_space_handle_t, bus_size_t);
+ int (*bs_subregion) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, bus_size_t, bus_space_handle_t *);
+
+ /* allocation/deallocation */
+ int (*bs_alloc) (bus_space_tag_t, bus_addr_t, bus_addr_t,
+ bus_size_t, bus_size_t, bus_size_t, int,
+ bus_addr_t *, bus_space_handle_t *);
+ void (*bs_free) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t);
+
+ /* Read single, the less commonly used functions. */
+ uint16_t (*bs_r_2) (bus_space_tag_t, bus_space_handle_t, bus_size_t);
+ uint64_t (*bs_r_8) (bus_space_tag_t, bus_space_handle_t, bus_size_t);
+
+ /* read multiple */
+ void (*bs_rm_1) (bus_space_tag_t, bus_space_handle_t, bus_size_t,
+ uint8_t *, bus_size_t);
+ void (*bs_rm_2) (bus_space_tag_t, bus_space_handle_t, bus_size_t,
+ uint16_t *, bus_size_t);
+ void (*bs_rm_4) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, uint32_t *, bus_size_t);
+ void (*bs_rm_8) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, uint64_t *, bus_size_t);
+
+ /* read region */
+ void (*bs_rr_1) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, uint8_t *, bus_size_t);
+ void (*bs_rr_2) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, uint16_t *, bus_size_t);
+ void (*bs_rr_4) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, uint32_t *, bus_size_t);
+ void (*bs_rr_8) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, uint64_t *, bus_size_t);
+
+ /* Write single, the less commonly used functions. */
+ void (*bs_w_2) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, uint16_t);
+ void (*bs_w_8) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, uint64_t);
+
+ /* write multiple */
+ void (*bs_wm_1) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, const uint8_t *, bus_size_t);
+ void (*bs_wm_2) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, const uint16_t *, bus_size_t);
+ void (*bs_wm_4) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, const uint32_t *, bus_size_t);
+ void (*bs_wm_8) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, const uint64_t *, bus_size_t);
+
+ /* write region */
+ void (*bs_wr_1) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, const uint8_t *, bus_size_t);
+ void (*bs_wr_2) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, const uint16_t *, bus_size_t);
+ void (*bs_wr_4) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, const uint32_t *, bus_size_t);
+ void (*bs_wr_8) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, const uint64_t *, bus_size_t);
+
+ /* set multiple */
+ void (*bs_sm_1) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, uint8_t, bus_size_t);
+ void (*bs_sm_2) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, uint16_t, bus_size_t);
+ void (*bs_sm_4) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, uint32_t, bus_size_t);
+ void (*bs_sm_8) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, uint64_t, bus_size_t);
+
+ /* set region */
+ void (*bs_sr_1) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, uint8_t, bus_size_t);
+ void (*bs_sr_2) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, uint16_t, bus_size_t);
+ void (*bs_sr_4) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, uint32_t, bus_size_t);
+ void (*bs_sr_8) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, uint64_t, bus_size_t);
+
+ /* copy */
+ void (*bs_c_1) (bus_space_tag_t, bus_space_handle_t, bus_size_t,
+ bus_space_handle_t, bus_size_t, bus_size_t);
+ void (*bs_c_2) (bus_space_tag_t, bus_space_handle_t, bus_size_t,
+ bus_space_handle_t, bus_size_t, bus_size_t);
+ void (*bs_c_4) (bus_space_tag_t, bus_space_handle_t, bus_size_t,
+ bus_space_handle_t, bus_size_t, bus_size_t);
+ void (*bs_c_8) (bus_space_tag_t, bus_space_handle_t, bus_size_t,
+ bus_space_handle_t, bus_size_t, bus_size_t);
+
+ /* read stream (single) */
+ uint8_t (*bs_r_1_s) (bus_space_tag_t, bus_space_handle_t, bus_size_t);
+ uint16_t (*bs_r_2_s) (bus_space_tag_t, bus_space_handle_t, bus_size_t);
+ uint32_t (*bs_r_4_s) (bus_space_tag_t, bus_space_handle_t, bus_size_t);
+ uint64_t (*bs_r_8_s) (bus_space_tag_t, bus_space_handle_t, bus_size_t);
+
+ /* read multiple stream */
+ void (*bs_rm_1_s) (bus_space_tag_t, bus_space_handle_t, bus_size_t,
+ uint8_t *, bus_size_t);
+ void (*bs_rm_2_s) (bus_space_tag_t, bus_space_handle_t, bus_size_t,
+ uint16_t *, bus_size_t);
+ void (*bs_rm_4_s) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, uint32_t *, bus_size_t);
+ void (*bs_rm_8_s) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, uint64_t *, bus_size_t);
+
+ /* read region stream */
+ void (*bs_rr_1_s) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, uint8_t *, bus_size_t);
+ void (*bs_rr_2_s) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, uint16_t *, bus_size_t);
+ void (*bs_rr_4_s) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, uint32_t *, bus_size_t);
+ void (*bs_rr_8_s) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, uint64_t *, bus_size_t);
+
+ /* write stream (single) */
+ void (*bs_w_1_s) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, uint8_t);
+ void (*bs_w_2_s) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, uint16_t);
+ void (*bs_w_4_s) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, uint32_t);
+ void (*bs_w_8_s) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, uint64_t);
+
+ /* write multiple stream */
+ void (*bs_wm_1_s) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, const uint8_t *, bus_size_t);
+ void (*bs_wm_2_s) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, const uint16_t *, bus_size_t);
+ void (*bs_wm_4_s) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, const uint32_t *, bus_size_t);
+ void (*bs_wm_8_s) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, const uint64_t *, bus_size_t);
+
+ /* write region stream */
+ void (*bs_wr_1_s) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, const uint8_t *, bus_size_t);
+ void (*bs_wr_2_s) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, const uint16_t *, bus_size_t);
+ void (*bs_wr_4_s) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, const uint32_t *, bus_size_t);
+ void (*bs_wr_8_s) (bus_space_tag_t, bus_space_handle_t,
+ bus_size_t, const uint64_t *, bus_size_t);
+};
+
+/*
+ * Utility macros; INTERNAL USE ONLY.
+ */
+#define __bs_c(a,b) __CONCAT(a,b)
+#define __bs_opname(op,size) __bs_c(__bs_c(__bs_c(bs_,op),_),size)
+
+#define __bs_nonsingle(type, sz, t, h, o, a, c) \
+ (*(t)->__bs_opname(type,sz))((t), h, o, a, c)
+#define __bs_set(type, sz, t, h, o, v, c) \
+ (*(t)->__bs_opname(type,sz))((t), h, o, v, c)
+#define __bs_copy(sz, t, h1, o1, h2, o2, cnt) \
+ (*(t)->__bs_opname(c,sz))((t), h1, o1, h2, o2, cnt)
+
+#define __bs_opname_s(op,size) __bs_c(__bs_c(__bs_c(__bs_c(bs_,op),_),size),_s)
+#define __bs_rs_s(sz, t, h, o) \
+ (*(t)->__bs_opname_s(r,sz))((t), h, o)
+#define __bs_ws_s(sz, t, h, o, v) \
+ (*(t)->__bs_opname_s(w,sz))((t), h, o, v)
+#define __bs_nonsingle_s(type, sz, t, h, o, a, c) \
+ (*(t)->__bs_opname_s(type,sz))((t), h, o, a, c)
+
+#define __generate_inline_bs_rs(IFN, MBR, TYP) \
+ static inline TYP \
+ IFN(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o) \
+ { \
+ \
+ if (__predict_true(t->MBR == NULL)) \
+ return (*(volatile TYP *)(h + o)); \
+ else \
+ return (t->MBR(t, h, o)); \
+ }
+
+#define __generate_inline_bs_ws(IFN, MBR, TYP) \
+ static inline void \
+ IFN(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, TYP v)\
+ { \
+ \
+ if (__predict_true(t->MBR == NULL)) \
+ *(volatile TYP *)(h + o) = v; \
+ else \
+ t->MBR(t, h, o, v); \
+ }
+
+/*
+ * Mapping and unmapping operations.
+ */
+#define bus_space_map(t, a, s, c, hp) \
+ (*(t)->bs_map)((t), (a), (s), (c), (hp))
+#define bus_space_unmap(t, h, s) \
+ (*(t)->bs_unmap)((t), (h), (s))
+#define bus_space_subregion(t, h, o, s, hp) \
+ (*(t)->bs_subregion)((t), (h), (o), (s), (hp))
+
+/*
+ * Allocation and deallocation operations.
+ */
+#define bus_space_alloc(t, rs, re, s, a, b, c, ap, hp) \
+ (*(t)->bs_alloc)((t), (rs), (re), (s), (a), (b), \
+ (c), (ap), (hp))
+#define bus_space_free(t, h, s) \
+ (*(t)->bs_free)((t), (h), (s))
+
+/*
+ * Bus barrier operations.
+ */
+#define bus_space_barrier(t, h, o, l, f) \
+ (*(t)->bs_barrier)((t), (h), (o), (l), (f))
+
+#define BUS_SPACE_BARRIER_READ 0x01
+#define BUS_SPACE_BARRIER_WRITE 0x02
+
+/*
+ * Bus read (single) operations.
+ */
+__generate_inline_bs_rs(bus_space_read_1, bs_r_1, uint8_t);
+__generate_inline_bs_rs(bus_space_read_2, bs_r_2, uint16_t);
+__generate_inline_bs_rs(bus_space_read_4, bs_r_4, uint32_t);
+__generate_inline_bs_rs(bus_space_read_8, bs_r_8, uint64_t);
+
+__generate_inline_bs_rs(bus_space_read_stream_1, bs_r_1_s, uint8_t);
+__generate_inline_bs_rs(bus_space_read_stream_2, bs_r_2_s, uint16_t);
+__generate_inline_bs_rs(bus_space_read_stream_4, bs_r_4_s, uint32_t);
+__generate_inline_bs_rs(bus_space_read_stream_8, bs_r_8_s, uint64_t);
+
+/*
+ * Bus read multiple operations.
+ */
+#define bus_space_read_multi_1(t, h, o, a, c) \
+ __bs_nonsingle(rm,1,(t),(h),(o),(a),(c))
+#define bus_space_read_multi_2(t, h, o, a, c) \
+ __bs_nonsingle(rm,2,(t),(h),(o),(a),(c))
+#define bus_space_read_multi_4(t, h, o, a, c) \
+ __bs_nonsingle(rm,4,(t),(h),(o),(a),(c))
+#define bus_space_read_multi_8(t, h, o, a, c) \
+ __bs_nonsingle(rm,8,(t),(h),(o),(a),(c))
+
+#define bus_space_read_multi_stream_1(t, h, o, a, c) \
+ __bs_nonsingle_s(rm,1,(t),(h),(o),(a),(c))
+#define bus_space_read_multi_stream_2(t, h, o, a, c) \
+ __bs_nonsingle_s(rm,2,(t),(h),(o),(a),(c))
+#define bus_space_read_multi_stream_4(t, h, o, a, c) \
+ __bs_nonsingle_s(rm,4,(t),(h),(o),(a),(c))
+#define bus_space_read_multi_stream_8(t, h, o, a, c) \
+ __bs_nonsingle_s(rm,8,(t),(h),(o),(a),(c))
+
+/*
+ * Bus read region operations.
+ */
+#define bus_space_read_region_1(t, h, o, a, c) \
+ __bs_nonsingle(rr,1,(t),(h),(o),(a),(c))
+#define bus_space_read_region_2(t, h, o, a, c) \
+ __bs_nonsingle(rr,2,(t),(h),(o),(a),(c))
+#define bus_space_read_region_4(t, h, o, a, c) \
+ __bs_nonsingle(rr,4,(t),(h),(o),(a),(c))
+#define bus_space_read_region_8(t, h, o, a, c) \
+ __bs_nonsingle(rr,8,(t),(h),(o),(a),(c))
+
+#define bus_space_read_region_stream_1(t, h, o, a, c) \
+ __bs_nonsingle_s(rr,1,(t),(h),(o),(a),(c))
+#define bus_space_read_region_stream_2(t, h, o, a, c) \
+ __bs_nonsingle_s(rr,2,(t),(h),(o),(a),(c))
+#define bus_space_read_region_stream_4(t, h, o, a, c) \
+ __bs_nonsingle_s(rr,4,(t),(h),(o),(a),(c))
+#define bus_space_read_region_stream_8(t, h, o, a, c) \
+ __bs_nonsingle_s(rr,8,(t),(h),(o),(a),(c))
+
+/*
+ * Bus write (single) operations.
+ */
+__generate_inline_bs_ws(bus_space_write_1, bs_w_1, uint8_t);
+__generate_inline_bs_ws(bus_space_write_2, bs_w_2, uint16_t);
+__generate_inline_bs_ws(bus_space_write_4, bs_w_4, uint32_t);
+__generate_inline_bs_ws(bus_space_write_8, bs_w_8, uint64_t);
+
+__generate_inline_bs_ws(bus_space_write_stream_1, bs_w_1_s, uint8_t);
+__generate_inline_bs_ws(bus_space_write_stream_2, bs_w_2_s, uint16_t);
+__generate_inline_bs_ws(bus_space_write_stream_4, bs_w_4_s, uint32_t);
+__generate_inline_bs_ws(bus_space_write_stream_8, bs_w_8_s, uint64_t);
+
+/*
+ * Bus write multiple operations.
+ */
+#define bus_space_write_multi_1(t, h, o, a, c) \
+ __bs_nonsingle(wm,1,(t),(h),(o),(a),(c))
+#define bus_space_write_multi_2(t, h, o, a, c) \
+ __bs_nonsingle(wm,2,(t),(h),(o),(a),(c))
+#define bus_space_write_multi_4(t, h, o, a, c) \
+ __bs_nonsingle(wm,4,(t),(h),(o),(a),(c))
+#define bus_space_write_multi_8(t, h, o, a, c) \
+ __bs_nonsingle(wm,8,(t),(h),(o),(a),(c))
+
+#define bus_space_write_multi_stream_1(t, h, o, a, c) \
+ __bs_nonsingle_s(wm,1,(t),(h),(o),(a),(c))
+#define bus_space_write_multi_stream_2(t, h, o, a, c) \
+ __bs_nonsingle_s(wm,2,(t),(h),(o),(a),(c))
+#define bus_space_write_multi_stream_4(t, h, o, a, c) \
+ __bs_nonsingle_s(wm,4,(t),(h),(o),(a),(c))
+#define bus_space_write_multi_stream_8(t, h, o, a, c) \
+ __bs_nonsingle_s(wm,8,(t),(h),(o),(a),(c))
+
+/*
+ * Bus write region operations.
+ */
+#define bus_space_write_region_1(t, h, o, a, c) \
+ __bs_nonsingle(wr,1,(t),(h),(o),(a),(c))
+#define bus_space_write_region_2(t, h, o, a, c) \
+ __bs_nonsingle(wr,2,(t),(h),(o),(a),(c))
+#define bus_space_write_region_4(t, h, o, a, c) \
+ __bs_nonsingle(wr,4,(t),(h),(o),(a),(c))
+#define bus_space_write_region_8(t, h, o, a, c) \
+ __bs_nonsingle(wr,8,(t),(h),(o),(a),(c))
+
+#define bus_space_write_region_stream_1(t, h, o, a, c) \
+ __bs_nonsingle_s(wr,1,(t),(h),(o),(a),(c))
+#define bus_space_write_region_stream_2(t, h, o, a, c) \
+ __bs_nonsingle_s(wr,2,(t),(h),(o),(a),(c))
+#define bus_space_write_region_stream_4(t, h, o, a, c) \
+ __bs_nonsingle_s(wr,4,(t),(h),(o),(a),(c))
+#define bus_space_write_region_stream_8(t, h, o, a, c) \
+ __bs_nonsingle_s(wr,8,(t),(h),(o),(a),(c))
+
+/*
+ * Set multiple operations.
+ */
+#define bus_space_set_multi_1(t, h, o, v, c) \
+ __bs_set(sm,1,(t),(h),(o),(v),(c))
+#define bus_space_set_multi_2(t, h, o, v, c) \
+ __bs_set(sm,2,(t),(h),(o),(v),(c))
+#define bus_space_set_multi_4(t, h, o, v, c) \
+ __bs_set(sm,4,(t),(h),(o),(v),(c))
+#define bus_space_set_multi_8(t, h, o, v, c) \
+ __bs_set(sm,8,(t),(h),(o),(v),(c))
+
+/*
+ * Set region operations.
+ */
+#define bus_space_set_region_1(t, h, o, v, c) \
+ __bs_set(sr,1,(t),(h),(o),(v),(c))
+#define bus_space_set_region_2(t, h, o, v, c) \
+ __bs_set(sr,2,(t),(h),(o),(v),(c))
+#define bus_space_set_region_4(t, h, o, v, c) \
+ __bs_set(sr,4,(t),(h),(o),(v),(c))
+#define bus_space_set_region_8(t, h, o, v, c) \
+ __bs_set(sr,8,(t),(h),(o),(v),(c))
+
+/*
+ * Copy operations.
+ */
+#define bus_space_copy_region_1(t, h1, o1, h2, o2, c) \
+ __bs_copy(1, t, h1, o1, h2, o2, c)
+#define bus_space_copy_region_2(t, h1, o1, h2, o2, c) \
+ __bs_copy(2, t, h1, o1, h2, o2, c)
+#define bus_space_copy_region_4(t, h1, o1, h2, o2, c) \
+ __bs_copy(4, t, h1, o1, h2, o2, c)
+#define bus_space_copy_region_8(t, h1, o1, h2, o2, c) \
+ __bs_copy(8, t, h1, o1, h2, o2, c)
+
+/*
+ * Macros to provide prototypes for all the functions used in the
+ * bus_space structure
+ */
+
+#define bs_map_proto(f) \
+int __bs_c(f,_bs_map) (bus_space_tag_t t, bus_addr_t addr, \
+ bus_size_t size, int cacheable, bus_space_handle_t *bshp);
+
+#define bs_unmap_proto(f) \
+void __bs_c(f,_bs_unmap) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t size);
+
+#define bs_subregion_proto(f) \
+int __bs_c(f,_bs_subregion) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset, bus_size_t size, \
+ bus_space_handle_t *nbshp);
+
+#define bs_alloc_proto(f) \
+int __bs_c(f,_bs_alloc) (bus_space_tag_t t, bus_addr_t rstart, \
+ bus_addr_t rend, bus_size_t size, bus_size_t align, \
+ bus_size_t boundary, int cacheable, bus_addr_t *addrp, \
+ bus_space_handle_t *bshp);
+
+#define bs_free_proto(f) \
+void __bs_c(f,_bs_free) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t size);
+
+#define bs_mmap_proto(f) \
+int __bs_c(f,_bs_mmap) (struct cdev *, vm_offset_t, vm_paddr_t *, int);
+
+#define bs_barrier_proto(f) \
+void __bs_c(f,_bs_barrier) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset, bus_size_t len, int flags);
+
+#define bs_r_1_proto(f) \
+uint8_t __bs_c(f,_bs_r_1) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset);
+
+#define bs_r_2_proto(f) \
+uint16_t __bs_c(f,_bs_r_2) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset);
+
+#define bs_r_4_proto(f) \
+uint32_t __bs_c(f,_bs_r_4) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset);
+
+#define bs_r_8_proto(f) \
+uint64_t __bs_c(f,_bs_r_8) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset);
+
+#define bs_r_1_s_proto(f) \
+uint8_t __bs_c(f,_bs_r_1_s) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset);
+
+#define bs_r_2_s_proto(f) \
+uint16_t __bs_c(f,_bs_r_2_s) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset);
+
+#define bs_r_4_s_proto(f) \
+uint32_t __bs_c(f,_bs_r_4_s) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset);
+
+#define bs_w_1_proto(f) \
+void __bs_c(f,_bs_w_1) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset, uint8_t value);
+
+#define bs_w_2_proto(f) \
+void __bs_c(f,_bs_w_2) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset, uint16_t value);
+
+#define bs_w_4_proto(f) \
+void __bs_c(f,_bs_w_4) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset, uint32_t value);
+
+#define bs_w_8_proto(f) \
+void __bs_c(f,_bs_w_8) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset, uint64_t value);
+
+#define bs_w_1_s_proto(f) \
+void __bs_c(f,_bs_w_1_s) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset, uint8_t value);
+
+#define bs_w_2_s_proto(f) \
+void __bs_c(f,_bs_w_2_s) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset, uint16_t value);
+
+#define bs_w_4_s_proto(f) \
+void __bs_c(f,_bs_w_4_s) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset, uint32_t value);
+
+#define bs_rm_1_proto(f) \
+void __bs_c(f,_bs_rm_1) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset, uint8_t *addr, bus_size_t count);
+
+#define bs_rm_2_proto(f) \
+void __bs_c(f,_bs_rm_2) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset, uint16_t *addr, bus_size_t count);
+
+#define bs_rm_4_proto(f) \
+void __bs_c(f,_bs_rm_4) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset, uint32_t *addr, bus_size_t count);
+
+#define bs_rm_8_proto(f) \
+void __bs_c(f,_bs_rm_8) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset, uint64_t *addr, bus_size_t count);
+
+#define bs_wm_1_proto(f) \
+void __bs_c(f,_bs_wm_1) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset, const uint8_t *addr, bus_size_t count);
+
+#define bs_wm_2_proto(f) \
+void __bs_c(f,_bs_wm_2) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset, const uint16_t *addr, bus_size_t count);
+
+#define bs_wm_4_proto(f) \
+void __bs_c(f,_bs_wm_4) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset, const uint32_t *addr, bus_size_t count);
+
+#define bs_wm_8_proto(f) \
+void __bs_c(f,_bs_wm_8) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset, const uint64_t *addr, bus_size_t count);
+
+#define bs_rr_1_proto(f) \
+void __bs_c(f, _bs_rr_1) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset, uint8_t *addr, bus_size_t count);
+
+#define bs_rr_2_proto(f) \
+void __bs_c(f, _bs_rr_2) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset, uint16_t *addr, bus_size_t count);
+
+#define bs_rr_4_proto(f) \
+void __bs_c(f, _bs_rr_4) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset, uint32_t *addr, bus_size_t count);
+
+#define bs_rr_8_proto(f) \
+void __bs_c(f, _bs_rr_8) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset, uint64_t *addr, bus_size_t count);
+
+#define bs_wr_1_proto(f) \
+void __bs_c(f, _bs_wr_1) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset, const uint8_t *addr, bus_size_t count);
+
+#define bs_wr_2_proto(f) \
+void __bs_c(f, _bs_wr_2) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset, const uint16_t *addr, bus_size_t count);
+
+#define bs_wr_4_proto(f) \
+void __bs_c(f, _bs_wr_4) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset, const uint32_t *addr, bus_size_t count);
+
+#define bs_wr_8_proto(f) \
+void __bs_c(f, _bs_wr_8) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset, const uint64_t *addr, bus_size_t count);
+
+#define bs_sm_1_proto(f) \
+void __bs_c(f,_bs_sm_1) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset, uint8_t value, bus_size_t count);
+
+#define bs_sm_2_proto(f) \
+void __bs_c(f,_bs_sm_2) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset, uint16_t value, bus_size_t count);
+
+#define bs_sm_4_proto(f) \
+void __bs_c(f,_bs_sm_4) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset, uint32_t value, bus_size_t count);
+
+#define bs_sm_8_proto(f) \
+void __bs_c(f,_bs_sm_8) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset, uint64_t value, bus_size_t count);
+
+#define bs_sr_1_proto(f) \
+void __bs_c(f,_bs_sr_1) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset, uint8_t value, bus_size_t count);
+
+#define bs_sr_2_proto(f) \
+void __bs_c(f,_bs_sr_2) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset, uint16_t value, bus_size_t count);
+
+#define bs_sr_4_proto(f) \
+void __bs_c(f,_bs_sr_4) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset, uint32_t value, bus_size_t count);
+
+#define bs_sr_8_proto(f) \
+void __bs_c(f,_bs_sr_8) (bus_space_tag_t t, bus_space_handle_t bsh, \
+ bus_size_t offset, uint64_t value, bus_size_t count);
+
+#define bs_c_1_proto(f) \
+void __bs_c(f,_bs_c_1) (bus_space_tag_t t, bus_space_handle_t bsh1, \
+ bus_size_t offset1, bus_space_handle_t bsh2, \
+ bus_size_t offset2, bus_size_t count);
+
+#define bs_c_2_proto(f) \
+void __bs_c(f,_bs_c_2) (bus_space_tag_t t, bus_space_handle_t bsh1, \
+ bus_size_t offset1, bus_space_handle_t bsh2, \
+ bus_size_t offset2, bus_size_t count);
+
+#define bs_c_4_proto(f) \
+void __bs_c(f,_bs_c_4) (bus_space_tag_t t, bus_space_handle_t bsh1, \
+ bus_size_t offset1, bus_space_handle_t bsh2, \
+ bus_size_t offset2, bus_size_t count);
+
+#define bs_c_8_proto(f) \
+void __bs_c(f,_bs_c_8) (bus_space_tag_t t, bus_space_handle_t bsh1, \
+ bus_size_t offset1, bus_space_handle_t bsh2, \
+ bus_size_t offset2, bus_size_t count);
+
+#define bs_protos(f) \
+bs_map_proto(f); \
+bs_unmap_proto(f); \
+bs_subregion_proto(f); \
+bs_alloc_proto(f); \
+bs_free_proto(f); \
+bs_mmap_proto(f); \
+bs_barrier_proto(f); \
+bs_r_1_proto(f); \
+bs_r_2_proto(f); \
+bs_r_4_proto(f); \
+bs_r_8_proto(f); \
+bs_r_1_s_proto(f); \
+bs_r_2_s_proto(f); \
+bs_r_4_s_proto(f); \
+bs_w_1_proto(f); \
+bs_w_2_proto(f); \
+bs_w_4_proto(f); \
+bs_w_8_proto(f); \
+bs_w_1_s_proto(f); \
+bs_w_2_s_proto(f); \
+bs_w_4_s_proto(f); \
+bs_rm_1_proto(f); \
+bs_rm_2_proto(f); \
+bs_rm_4_proto(f); \
+bs_rm_8_proto(f); \
+bs_wm_1_proto(f); \
+bs_wm_2_proto(f); \
+bs_wm_4_proto(f); \
+bs_wm_8_proto(f); \
+bs_rr_1_proto(f); \
+bs_rr_2_proto(f); \
+bs_rr_4_proto(f); \
+bs_rr_8_proto(f); \
+bs_wr_1_proto(f); \
+bs_wr_2_proto(f); \
+bs_wr_4_proto(f); \
+bs_wr_8_proto(f); \
+bs_sm_1_proto(f); \
+bs_sm_2_proto(f); \
+bs_sm_4_proto(f); \
+bs_sm_8_proto(f); \
+bs_sr_1_proto(f); \
+bs_sr_2_proto(f); \
+bs_sr_4_proto(f); \
+bs_sr_8_proto(f); \
+bs_c_1_proto(f); \
+bs_c_2_proto(f); \
+bs_c_4_proto(f); \
+bs_c_8_proto(f);
+
+void generic_bs_unimplemented(void);
+#define BS_UNIMPLEMENTED (void *)generic_bs_unimplemented
+
+#define BUS_SPACE_ALIGNED_POINTER(p, t) ALIGNED_POINTER(p, t)
+
+#define BUS_SPACE_MAXADDR_24BIT 0xFFFFFF
+#define BUS_SPACE_MAXADDR_32BIT 0xFFFFFFFF
+#define BUS_SPACE_MAXADDR 0xFFFFFFFF
+#define BUS_SPACE_MAXSIZE_24BIT 0xFFFFFF
+#define BUS_SPACE_MAXSIZE_32BIT 0xFFFFFFFF
+#define BUS_SPACE_MAXSIZE 0xFFFFFFFF
+
+#define BUS_SPACE_UNRESTRICTED (~0)
+
+#define BUS_PEEK_FUNC(width, type) \
+ static inline int \
+ bus_space_peek_##width(bus_space_tag_t tag, \
+ bus_space_handle_t hnd, bus_size_t offset, type *value) \
+ { \
+ type tmp; \
+ tmp = bus_space_read_##width(tag, hnd, offset); \
+ *value = (type)tmp; \
+ return (0); \
+ }
+BUS_PEEK_FUNC(1, uint8_t)
+BUS_PEEK_FUNC(2, uint16_t)
+BUS_PEEK_FUNC(4, uint32_t)
+BUS_PEEK_FUNC(8, uint64_t)
+
+#define BUS_POKE_FUNC(width, type) \
+ static inline int \
+ bus_space_poke_##width(bus_space_tag_t tag, \
+ bus_space_handle_t hnd, bus_size_t offset, type value) \
+ { \
+ bus_space_write_##width(tag, hnd, offset, value); \
+ return (0); \
+ }
+BUS_POKE_FUNC(1, uint8_t)
+BUS_POKE_FUNC(2, uint16_t)
+BUS_POKE_FUNC(4, uint32_t)
+BUS_POKE_FUNC(8, uint64_t)
+
+#include <machine/bus_dma.h>
+
+/*
+ * Get the physical address of a bus space memory-mapped resource.
+ * Doing this as a macro is a temporary solution until a more robust fix is
+ * designed. It also serves to mark the locations needing that fix.
+ */
+#define BUS_SPACE_PHYSADDR(res, offs) \
+ ((u_int)(rman_get_start(res)+(offs)))
+
+#endif /* _MACHINE_BUS_H_ */
diff --git a/sys/arm/include/bus_dma.h b/sys/arm/include/bus_dma.h
new file mode 100644
index 000000000000..d716f4f01a17
--- /dev/null
+++ b/sys/arm/include/bus_dma.h
@@ -0,0 +1,78 @@
+/* $NetBSD: bus.h,v 1.11 2003/07/28 17:35:54 thorpej Exp $ */
+
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-NetBSD
+ *
+ * Copyright (c) 1996, 1997, 1998, 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
+ * NASA Ames Research Center.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) 1996 Charles M. Hannum. All rights reserved.
+ * Copyright (c) 1996 Christopher G. Demetriou. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Christopher G. Demetriou
+ * for the NetBSD Project.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _ARM_BUS_DMA_H
+#define _ARM_BUS_DMA_H
+
+#include <sys/bus_dma.h>
+#include <sys/bus_dma_internal.h>
+
+/* Bus Space DMA macros */
+
+#define BUS_DMA_TAG_VALID(t) ((t) != (bus_dma_tag_t)0)
+
+#endif /* _ARM_BUS_DMA_H */
diff --git a/sys/arm/include/clock.h b/sys/arm/include/clock.h
new file mode 100644
index 000000000000..c9ac2039f836
--- /dev/null
+++ b/sys/arm/include/clock.h
@@ -0,0 +1,34 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2004 Olivier Houchard
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_CLOCK_H_
+#define _MACHINE_CLOCK_H_
+
+#endif /* !_MACHINE_CLOCK_H_ */
diff --git a/sys/arm/include/counter.h b/sys/arm/include/counter.h
new file mode 100644
index 000000000000..e3fb03abd352
--- /dev/null
+++ b/sys/arm/include/counter.h
@@ -0,0 +1,91 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Konstantin Belousov <kib@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __MACHINE_COUNTER_H__
+#define __MACHINE_COUNTER_H__
+
+#include <sys/pcpu.h>
+#include <machine/atomic.h>
+
+#define EARLY_COUNTER &__pcpu[0].pc_early_dummy_counter
+
+#define counter_enter() do {} while (0)
+#define counter_exit() do {} while (0)
+
+#ifdef IN_SUBR_COUNTER_C
+
+static inline uint64_t
+counter_u64_read_one(uint64_t *p, int cpu)
+{
+
+ return (atomic_load_64((uint64_t *)((char *)p + UMA_PCPU_ALLOC_SIZE *
+ cpu)));
+}
+
+static inline uint64_t
+counter_u64_fetch_inline(uint64_t *p)
+{
+ uint64_t r;
+ int i;
+
+ r = 0;
+ CPU_FOREACH(i)
+ r += counter_u64_read_one((uint64_t *)p, i);
+
+ return (r);
+}
+
+static void
+counter_u64_zero_one_cpu(void *arg)
+{
+
+ atomic_store_64((uint64_t *)((char *)arg + UMA_PCPU_ALLOC_SIZE *
+ PCPU_GET(cpuid)), 0);
+}
+
+static inline void
+counter_u64_zero_inline(counter_u64_t c)
+{
+
+ smp_rendezvous(smp_no_rendezvous_barrier, counter_u64_zero_one_cpu,
+ smp_no_rendezvous_barrier, c);
+}
+#endif
+
+#define counter_u64_add_protected(c, inc) counter_u64_add(c, inc)
+
+static inline void
+counter_u64_add(counter_u64_t c, int64_t inc)
+{
+
+ atomic_add_64((uint64_t *)zpcpu_get(c), inc);
+}
+
+#endif /* ! __MACHINE_COUNTER_H__ */
diff --git a/sys/arm/include/cpu-v6.h b/sys/arm/include/cpu-v6.h
new file mode 100644
index 000000000000..4b301cfd1c4e
--- /dev/null
+++ b/sys/arm/include/cpu-v6.h
@@ -0,0 +1,685 @@
+/*-
+ * Copyright 2014 Svatopluk Kraus <onwahe@gmail.com>
+ * Copyright 2014 Michal Meloun <meloun@miracle.cz>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef MACHINE_CPU_V6_H
+#define MACHINE_CPU_V6_H
+
+/* There are no user serviceable parts here, they may change without notice */
+#ifndef _KERNEL
+#error Only include this file in the kernel
+#endif
+
+#include <machine/atomic.h>
+#include <machine/cpufunc.h>
+#include <machine/cpuinfo.h>
+#include <machine/sysreg.h>
+
+/*
+ * Some kernel modules (dtrace all for example) are compiled
+ * unconditionally with -DSMP. Although it looks like a bug,
+ * handle this case here and in #elif condition in ARM_SMP_UP macro.
+ */
+#if __ARM_ARCH <= 6 && defined(SMP) && !defined(KLD_MODULE)
+#error SMP option is not supported on ARMv6
+#endif
+
+#if __ARM_ARCH <= 6 && defined(SMP_ON_UP)
+#error SMP_ON_UP option is only supported on ARMv7+ CPUs
+#endif
+
+#if !defined(SMP) && defined(SMP_ON_UP)
+#error SMP option must be defined for SMP_ON_UP option
+#endif
+
+#define CPU_ASID_KERNEL 0
+
+#if defined(SMP_ON_UP)
+#define ARM_SMP_UP(smp_code, up_code) \
+do { \
+ if (cpuinfo.mp_ext != 0) { \
+ smp_code; \
+ } else { \
+ up_code; \
+ } \
+} while (0)
+#elif defined(SMP) && __ARM_ARCH > 6
+#define ARM_SMP_UP(smp_code, up_code) \
+do { \
+ smp_code; \
+} while (0)
+#else
+#define ARM_SMP_UP(smp_code, up_code) \
+do { \
+ up_code; \
+} while (0)
+#endif
+
+void dcache_wbinv_poc_all(void); /* !!! NOT SMP coherent function !!! */
+vm_offset_t dcache_wb_pou_checked(vm_offset_t, vm_size_t);
+vm_offset_t icache_inv_pou_checked(vm_offset_t, vm_size_t);
+
+#ifdef DEV_PMU
+#include <sys/pcpu.h>
+#define PMU_OVSR_C 0x80000000 /* Cycle Counter */
+extern uint32_t ccnt_hi[MAXCPU];
+extern int pmu_attched;
+#endif /* DEV_PMU */
+
+#define sev() __asm __volatile("sev" : : : "memory")
+#define wfe() __asm __volatile("wfe" : : : "memory")
+
+/*
+ * Macros to generate CP15 (system control processor) read/write functions.
+ */
+#define _FX(s...) #s
+
+#define _RF0(fname, aname...) \
+static __inline uint32_t \
+fname(void) \
+{ \
+ uint32_t reg; \
+ __asm __volatile("mrc\t" _FX(aname): "=r" (reg)); \
+ return(reg); \
+}
+
+#define _R64F0(fname, aname) \
+static __inline uint64_t \
+fname(void) \
+{ \
+ uint64_t reg; \
+ __asm __volatile("mrrc\t" _FX(aname): "=r" (reg)); \
+ return(reg); \
+}
+
+#define _WF0(fname, aname...) \
+static __inline void \
+fname(void) \
+{ \
+ __asm __volatile("mcr\t" _FX(aname)); \
+}
+
+#define _WF1(fname, aname...) \
+static __inline void \
+fname(uint32_t reg) \
+{ \
+ __asm __volatile("mcr\t" _FX(aname):: "r" (reg)); \
+}
+
+#define _W64F1(fname, aname...) \
+static __inline void \
+fname(uint64_t reg) \
+{ \
+ __asm __volatile("mcrr\t" _FX(aname):: "r" (reg)); \
+}
+
+/*
+ * Raw CP15 maintenance operations
+ * !!! not for external use !!!
+ */
+
+/* TLB */
+
+_WF0(_CP15_TLBIALL, CP15_TLBIALL) /* Invalidate entire unified TLB */
+#if __ARM_ARCH >= 7 && defined(SMP)
+_WF0(_CP15_TLBIALLIS, CP15_TLBIALLIS) /* Invalidate entire unified TLB IS */
+#endif
+_WF1(_CP15_TLBIASID, CP15_TLBIASID(%0)) /* Invalidate unified TLB by ASID */
+#if __ARM_ARCH >= 7 && defined(SMP)
+_WF1(_CP15_TLBIASIDIS, CP15_TLBIASIDIS(%0)) /* Invalidate unified TLB by ASID IS */
+#endif
+_WF1(_CP15_TLBIMVAA, CP15_TLBIMVAA(%0)) /* Invalidate unified TLB by MVA, all ASID */
+#if __ARM_ARCH >= 7 && defined(SMP)
+_WF1(_CP15_TLBIMVAAIS, CP15_TLBIMVAAIS(%0)) /* Invalidate unified TLB by MVA, all ASID IS */
+#endif
+_WF1(_CP15_TLBIMVA, CP15_TLBIMVA(%0)) /* Invalidate unified TLB by MVA */
+
+_WF1(_CP15_TTB_SET, CP15_TTBR0(%0))
+
+/* Cache and Branch predictor */
+
+_WF0(_CP15_BPIALL, CP15_BPIALL) /* Branch predictor invalidate all */
+#if __ARM_ARCH >= 7 && defined(SMP)
+_WF0(_CP15_BPIALLIS, CP15_BPIALLIS) /* Branch predictor invalidate all IS */
+#endif
+_WF1(_CP15_BPIMVA, CP15_BPIMVA(%0)) /* Branch predictor invalidate by MVA */
+_WF1(_CP15_DCCIMVAC, CP15_DCCIMVAC(%0)) /* Data cache clean and invalidate by MVA PoC */
+_WF1(_CP15_DCCISW, CP15_DCCISW(%0)) /* Data cache clean and invalidate by set/way */
+_WF1(_CP15_DCCMVAC, CP15_DCCMVAC(%0)) /* Data cache clean by MVA PoC */
+#if __ARM_ARCH >= 7
+_WF1(_CP15_DCCMVAU, CP15_DCCMVAU(%0)) /* Data cache clean by MVA PoU */
+#endif
+_WF1(_CP15_DCCSW, CP15_DCCSW(%0)) /* Data cache clean by set/way */
+_WF1(_CP15_DCIMVAC, CP15_DCIMVAC(%0)) /* Data cache invalidate by MVA PoC */
+_WF1(_CP15_DCISW, CP15_DCISW(%0)) /* Data cache invalidate by set/way */
+_WF0(_CP15_ICIALLU, CP15_ICIALLU) /* Instruction cache invalidate all PoU */
+#if __ARM_ARCH >= 7 && defined(SMP)
+_WF0(_CP15_ICIALLUIS, CP15_ICIALLUIS) /* Instruction cache invalidate all PoU IS */
+#endif
+_WF1(_CP15_ICIMVAU, CP15_ICIMVAU(%0)) /* Instruction cache invalidate */
+
+/*
+ * Publicly accessible functions
+ */
+
+/* CP14 Debug Registers */
+_RF0(cp14_dbgdidr_get, CP14_DBGDIDR(%0))
+_RF0(cp14_dbgprsr_get, CP14_DBGPRSR(%0))
+_RF0(cp14_dbgoslsr_get, CP14_DBGOSLSR(%0))
+_RF0(cp14_dbgosdlr_get, CP14_DBGOSDLR(%0))
+_RF0(cp14_dbgdscrint_get, CP14_DBGDSCRint(%0))
+
+_WF1(cp14_dbgdscr_v6_set, CP14_DBGDSCRext_V6(%0))
+_WF1(cp14_dbgdscr_v7_set, CP14_DBGDSCRext_V7(%0))
+_WF1(cp14_dbgvcr_set, CP14_DBGVCR(%0))
+_WF1(cp14_dbgoslar_set, CP14_DBGOSLAR(%0))
+
+/* Various control registers */
+
+_RF0(cp15_cpacr_get, CP15_CPACR(%0))
+_WF1(cp15_cpacr_set, CP15_CPACR(%0))
+_RF0(cp15_dfsr_get, CP15_DFSR(%0))
+_RF0(cp15_ifsr_get, CP15_IFSR(%0))
+_WF1(cp15_prrr_set, CP15_PRRR(%0))
+_WF1(cp15_nmrr_set, CP15_NMRR(%0))
+_RF0(cp15_ttbr_get, CP15_TTBR0(%0))
+_RF0(cp15_dfar_get, CP15_DFAR(%0))
+#if __ARM_ARCH >= 7
+_RF0(cp15_ifar_get, CP15_IFAR(%0))
+_RF0(cp15_l2ctlr_get, CP15_L2CTLR(%0))
+#endif
+_RF0(cp15_actlr_get, CP15_ACTLR(%0))
+_WF1(cp15_actlr_set, CP15_ACTLR(%0))
+_WF1(cp15_ats1cpr_set, CP15_ATS1CPR(%0))
+_WF1(cp15_ats1cpw_set, CP15_ATS1CPW(%0))
+_WF1(cp15_ats1cur_set, CP15_ATS1CUR(%0))
+_WF1(cp15_ats1cuw_set, CP15_ATS1CUW(%0))
+_RF0(cp15_par_get, CP15_PAR(%0))
+_RF0(cp15_sctlr_get, CP15_SCTLR(%0))
+
+/*CPU id registers */
+_RF0(cp15_midr_get, CP15_MIDR(%0))
+_RF0(cp15_ctr_get, CP15_CTR(%0))
+_RF0(cp15_tcmtr_get, CP15_TCMTR(%0))
+_RF0(cp15_tlbtr_get, CP15_TLBTR(%0))
+_RF0(cp15_mpidr_get, CP15_MPIDR(%0))
+_RF0(cp15_revidr_get, CP15_REVIDR(%0))
+_RF0(cp15_ccsidr_get, CP15_CCSIDR(%0))
+_RF0(cp15_clidr_get, CP15_CLIDR(%0))
+_RF0(cp15_aidr_get, CP15_AIDR(%0))
+_WF1(cp15_csselr_set, CP15_CSSELR(%0))
+_RF0(cp15_id_pfr0_get, CP15_ID_PFR0(%0))
+_RF0(cp15_id_pfr1_get, CP15_ID_PFR1(%0))
+_RF0(cp15_id_dfr0_get, CP15_ID_DFR0(%0))
+_RF0(cp15_id_afr0_get, CP15_ID_AFR0(%0))
+_RF0(cp15_id_mmfr0_get, CP15_ID_MMFR0(%0))
+_RF0(cp15_id_mmfr1_get, CP15_ID_MMFR1(%0))
+_RF0(cp15_id_mmfr2_get, CP15_ID_MMFR2(%0))
+_RF0(cp15_id_mmfr3_get, CP15_ID_MMFR3(%0))
+_RF0(cp15_id_isar0_get, CP15_ID_ISAR0(%0))
+_RF0(cp15_id_isar1_get, CP15_ID_ISAR1(%0))
+_RF0(cp15_id_isar2_get, CP15_ID_ISAR2(%0))
+_RF0(cp15_id_isar3_get, CP15_ID_ISAR3(%0))
+_RF0(cp15_id_isar4_get, CP15_ID_ISAR4(%0))
+_RF0(cp15_id_isar5_get, CP15_ID_ISAR5(%0))
+_RF0(cp15_cbar_get, CP15_CBAR(%0))
+
+/* Performance Monitor registers */
+
+#if __ARM_ARCH == 6 && defined(CPU_ARM1176)
+_RF0(cp15_pmuserenr_get, CP15_PMUSERENR(%0))
+_WF1(cp15_pmuserenr_set, CP15_PMUSERENR(%0))
+_RF0(cp15_pmcr_get, CP15_PMCR(%0))
+_WF1(cp15_pmcr_set, CP15_PMCR(%0))
+_RF0(cp15_pmccntr_get, CP15_PMCCNTR(%0))
+_WF1(cp15_pmccntr_set, CP15_PMCCNTR(%0))
+#elif __ARM_ARCH > 6
+_RF0(cp15_pmcr_get, CP15_PMCR(%0))
+_WF1(cp15_pmcr_set, CP15_PMCR(%0))
+_RF0(cp15_pmcnten_get, CP15_PMCNTENSET(%0))
+_WF1(cp15_pmcnten_set, CP15_PMCNTENSET(%0))
+_WF1(cp15_pmcnten_clr, CP15_PMCNTENCLR(%0))
+_RF0(cp15_pmovsr_get, CP15_PMOVSR(%0))
+_WF1(cp15_pmovsr_set, CP15_PMOVSR(%0))
+_WF1(cp15_pmswinc_set, CP15_PMSWINC(%0))
+_RF0(cp15_pmselr_get, CP15_PMSELR(%0))
+_WF1(cp15_pmselr_set, CP15_PMSELR(%0))
+_RF0(cp15_pmccntr_get, CP15_PMCCNTR(%0))
+_WF1(cp15_pmccntr_set, CP15_PMCCNTR(%0))
+_RF0(cp15_pmxevtyper_get, CP15_PMXEVTYPER(%0))
+_WF1(cp15_pmxevtyper_set, CP15_PMXEVTYPER(%0))
+_RF0(cp15_pmxevcntr_get, CP15_PMXEVCNTRR(%0))
+_WF1(cp15_pmxevcntr_set, CP15_PMXEVCNTRR(%0))
+_RF0(cp15_pmuserenr_get, CP15_PMUSERENR(%0))
+_WF1(cp15_pmuserenr_set, CP15_PMUSERENR(%0))
+_RF0(cp15_pminten_get, CP15_PMINTENSET(%0))
+_WF1(cp15_pminten_set, CP15_PMINTENSET(%0))
+_WF1(cp15_pminten_clr, CP15_PMINTENCLR(%0))
+#endif
+
+_RF0(cp15_tpidrurw_get, CP15_TPIDRURW(%0))
+_WF1(cp15_tpidrurw_set, CP15_TPIDRURW(%0))
+_RF0(cp15_tpidruro_get, CP15_TPIDRURO(%0))
+_WF1(cp15_tpidruro_set, CP15_TPIDRURO(%0))
+_RF0(cp15_tpidrpwr_get, CP15_TPIDRPRW(%0))
+_WF1(cp15_tpidrpwr_set, CP15_TPIDRPRW(%0))
+
+/* Generic Timer registers - only use when you know the hardware is available */
+_RF0(cp15_cntfrq_get, CP15_CNTFRQ(%0))
+_WF1(cp15_cntfrq_set, CP15_CNTFRQ(%0))
+_RF0(cp15_cntkctl_get, CP15_CNTKCTL(%0))
+_WF1(cp15_cntkctl_set, CP15_CNTKCTL(%0))
+_RF0(cp15_cntp_tval_get, CP15_CNTP_TVAL(%0))
+_WF1(cp15_cntp_tval_set, CP15_CNTP_TVAL(%0))
+_RF0(cp15_cntp_ctl_get, CP15_CNTP_CTL(%0))
+_WF1(cp15_cntp_ctl_set, CP15_CNTP_CTL(%0))
+_RF0(cp15_cntv_tval_get, CP15_CNTV_TVAL(%0))
+_WF1(cp15_cntv_tval_set, CP15_CNTV_TVAL(%0))
+_RF0(cp15_cntv_ctl_get, CP15_CNTV_CTL(%0))
+_WF1(cp15_cntv_ctl_set, CP15_CNTV_CTL(%0))
+_RF0(cp15_cnthctl_get, CP15_CNTHCTL(%0))
+_WF1(cp15_cnthctl_set, CP15_CNTHCTL(%0))
+_RF0(cp15_cnthp_tval_get, CP15_CNTHP_TVAL(%0))
+_WF1(cp15_cnthp_tval_set, CP15_CNTHP_TVAL(%0))
+_RF0(cp15_cnthp_ctl_get, CP15_CNTHP_CTL(%0))
+_WF1(cp15_cnthp_ctl_set, CP15_CNTHP_CTL(%0))
+
+_R64F0(cp15_cntpct_get, CP15_CNTPCT(%Q0, %R0))
+_R64F0(cp15_cntvct_get, CP15_CNTVCT(%Q0, %R0))
+_R64F0(cp15_cntp_cval_get, CP15_CNTP_CVAL(%Q0, %R0))
+_W64F1(cp15_cntp_cval_set, CP15_CNTP_CVAL(%Q0, %R0))
+_R64F0(cp15_cntv_cval_get, CP15_CNTV_CVAL(%Q0, %R0))
+_W64F1(cp15_cntv_cval_set, CP15_CNTV_CVAL(%Q0, %R0))
+_R64F0(cp15_cntvoff_get, CP15_CNTVOFF(%Q0, %R0))
+_W64F1(cp15_cntvoff_set, CP15_CNTVOFF(%Q0, %R0))
+_R64F0(cp15_cnthp_cval_get, CP15_CNTHP_CVAL(%Q0, %R0))
+_W64F1(cp15_cnthp_cval_set, CP15_CNTHP_CVAL(%Q0, %R0))
+
+#undef _FX
+#undef _RF0
+#undef _WF0
+#undef _WF1
+
+/*
+ * TLB maintenance operations.
+ */
+
+/* Local (i.e. not broadcasting ) operations. */
+
+/* Flush all TLB entries (even global). */
+static __inline void
+tlb_flush_all_local(void)
+{
+
+ dsb();
+ _CP15_TLBIALL();
+ dsb();
+}
+
+/* Flush all not global TLB entries. */
+static __inline void
+tlb_flush_all_ng_local(void)
+{
+
+ dsb();
+ _CP15_TLBIASID(CPU_ASID_KERNEL);
+ dsb();
+}
+
+/* Flush single TLB entry (even global). */
+static __inline void
+tlb_flush_local(vm_offset_t va)
+{
+
+ KASSERT((va & PAGE_MASK) == 0, ("%s: va %#x not aligned", __func__, va));
+
+ dsb();
+ _CP15_TLBIMVA(va | CPU_ASID_KERNEL);
+ dsb();
+}
+
+/* Flush range of TLB entries (even global). */
+static __inline void
+tlb_flush_range_local(vm_offset_t va, vm_size_t size)
+{
+ vm_offset_t eva = va + size;
+
+ KASSERT((va & PAGE_MASK) == 0, ("%s: va %#x not aligned", __func__, va));
+ KASSERT((size & PAGE_MASK) == 0, ("%s: size %#x not aligned", __func__,
+ size));
+
+ dsb();
+ for (; va < eva; va += PAGE_SIZE)
+ _CP15_TLBIMVA(va | CPU_ASID_KERNEL);
+ dsb();
+}
+
+/* Broadcasting operations. */
+#if __ARM_ARCH >= 7 && defined(SMP)
+
+static __inline void
+tlb_flush_all(void)
+{
+
+ dsb();
+ ARM_SMP_UP(
+ _CP15_TLBIALLIS(),
+ _CP15_TLBIALL()
+ );
+ dsb();
+}
+
+static __inline void
+tlb_flush_all_ng(void)
+{
+
+ dsb();
+ ARM_SMP_UP(
+ _CP15_TLBIASIDIS(CPU_ASID_KERNEL),
+ _CP15_TLBIASID(CPU_ASID_KERNEL)
+ );
+ dsb();
+}
+
+static __inline void
+tlb_flush(vm_offset_t va)
+{
+
+ KASSERT((va & PAGE_MASK) == 0, ("%s: va %#x not aligned", __func__, va));
+
+ dsb();
+ ARM_SMP_UP(
+ _CP15_TLBIMVAAIS(va),
+ _CP15_TLBIMVA(va | CPU_ASID_KERNEL)
+ );
+ dsb();
+}
+
+static __inline void
+tlb_flush_range(vm_offset_t va, vm_size_t size)
+{
+ vm_offset_t eva = va + size;
+
+ KASSERT((va & PAGE_MASK) == 0, ("%s: va %#x not aligned", __func__, va));
+ KASSERT((size & PAGE_MASK) == 0, ("%s: size %#x not aligned", __func__,
+ size));
+
+ dsb();
+ ARM_SMP_UP(
+ {
+ for (; va < eva; va += PAGE_SIZE)
+ _CP15_TLBIMVAAIS(va);
+ },
+ {
+ for (; va < eva; va += PAGE_SIZE)
+ _CP15_TLBIMVA(va | CPU_ASID_KERNEL);
+ }
+ );
+ dsb();
+}
+#else /* __ARM_ARCH < 7 */
+
+#define tlb_flush_all() tlb_flush_all_local()
+#define tlb_flush_all_ng() tlb_flush_all_ng_local()
+#define tlb_flush(va) tlb_flush_local(va)
+#define tlb_flush_range(va, size) tlb_flush_range_local(va, size)
+
+#endif /* __ARM_ARCH < 7 */
+
+/*
+ * Cache maintenance operations.
+ */
+
+/* Sync I and D caches to PoU */
+static __inline void
+icache_sync(vm_offset_t va, vm_size_t size)
+{
+ vm_offset_t eva = va + size;
+
+ dsb();
+ va &= ~cpuinfo.dcache_line_mask;
+
+ for ( ; va < eva; va += cpuinfo.dcache_line_size) {
+#if __ARM_ARCH >= 7
+ _CP15_DCCMVAU(va);
+#else
+ _CP15_DCCMVAC(va);
+#endif
+ }
+ dsb();
+ ARM_SMP_UP(
+ _CP15_ICIALLUIS(),
+ _CP15_ICIALLU()
+ );
+ dsb();
+ isb();
+}
+
+/* Invalidate I cache */
+static __inline void
+icache_inv_all(void)
+{
+
+ ARM_SMP_UP(
+ _CP15_ICIALLUIS(),
+ _CP15_ICIALLU()
+ );
+ dsb();
+ isb();
+}
+
+/* Invalidate branch predictor buffer */
+static __inline void
+bpb_inv_all(void)
+{
+
+ ARM_SMP_UP(
+ _CP15_BPIALLIS(),
+ _CP15_BPIALL()
+ );
+ dsb();
+ isb();
+}
+
+/* Write back D-cache to PoU */
+static __inline void
+dcache_wb_pou(vm_offset_t va, vm_size_t size)
+{
+ vm_offset_t eva = va + size;
+
+ dsb();
+ va &= ~cpuinfo.dcache_line_mask;
+ for ( ; va < eva; va += cpuinfo.dcache_line_size) {
+#if __ARM_ARCH >= 7
+ _CP15_DCCMVAU(va);
+#else
+ _CP15_DCCMVAC(va);
+#endif
+ }
+ dsb();
+}
+
+/*
+ * Invalidate D-cache to PoC
+ *
+ * Caches are invalidated from outermost to innermost as fresh cachelines
+ * flow in this direction. In given range, if there was no dirty cacheline
+ * in any cache before, no stale cacheline should remain in them after this
+ * operation finishes.
+ */
+static __inline void
+dcache_inv_poc(vm_offset_t va, vm_paddr_t pa, vm_size_t size)
+{
+ vm_offset_t eva = va + size;
+
+ dsb();
+ /* invalidate L2 first */
+ cpu_l2cache_inv_range(pa, size);
+
+ /* then L1 */
+ va &= ~cpuinfo.dcache_line_mask;
+ for ( ; va < eva; va += cpuinfo.dcache_line_size) {
+ _CP15_DCIMVAC(va);
+ }
+ dsb();
+}
+
+/*
+ * Discard D-cache lines to PoC, prior to overwrite by DMA engine.
+ *
+ * Normal invalidation does L2 then L1 to ensure that stale data from L2 doesn't
+ * flow into L1 while invalidating. This routine is intended to be used only
+ * when invalidating a buffer before a DMA operation loads new data into memory.
+ * The concern in this case is that dirty lines are not evicted to main memory,
+ * overwriting the DMA data. For that reason, the L1 is done first to ensure
+ * that an evicted L1 line doesn't flow to L2 after the L2 has been cleaned.
+ */
+static __inline void
+dcache_inv_poc_dma(vm_offset_t va, vm_paddr_t pa, vm_size_t size)
+{
+ vm_offset_t eva = va + size;
+
+ /* invalidate L1 first */
+ dsb();
+ va &= ~cpuinfo.dcache_line_mask;
+ for ( ; va < eva; va += cpuinfo.dcache_line_size) {
+ _CP15_DCIMVAC(va);
+ }
+ dsb();
+
+ /* then L2 */
+ cpu_l2cache_inv_range(pa, size);
+}
+
+/*
+ * Write back D-cache to PoC
+ *
+ * Caches are written back from innermost to outermost as dirty cachelines
+ * flow in this direction. In given range, no dirty cacheline should remain
+ * in any cache after this operation finishes.
+ */
+static __inline void
+dcache_wb_poc(vm_offset_t va, vm_paddr_t pa, vm_size_t size)
+{
+ vm_offset_t eva = va + size;
+
+ dsb();
+ va &= ~cpuinfo.dcache_line_mask;
+ for ( ; va < eva; va += cpuinfo.dcache_line_size) {
+ _CP15_DCCMVAC(va);
+ }
+ dsb();
+
+ cpu_l2cache_wb_range(pa, size);
+}
+
+/* Write back and invalidate D-cache to PoC */
+static __inline void
+dcache_wbinv_poc(vm_offset_t sva, vm_paddr_t pa, vm_size_t size)
+{
+ vm_offset_t va;
+ vm_offset_t eva = sva + size;
+
+ dsb();
+ /* write back L1 first */
+ va = sva & ~cpuinfo.dcache_line_mask;
+ for ( ; va < eva; va += cpuinfo.dcache_line_size) {
+ _CP15_DCCMVAC(va);
+ }
+ dsb();
+
+ /* then write back and invalidate L2 */
+ cpu_l2cache_wbinv_range(pa, size);
+
+ /* then invalidate L1 */
+ va = sva & ~cpuinfo.dcache_line_mask;
+ for ( ; va < eva; va += cpuinfo.dcache_line_size) {
+ _CP15_DCIMVAC(va);
+ }
+ dsb();
+}
+
+/* Set TTB0 register */
+static __inline void
+cp15_ttbr_set(uint32_t reg)
+{
+ dsb();
+ _CP15_TTB_SET(reg);
+ dsb();
+ _CP15_BPIALL();
+ dsb();
+ isb();
+ tlb_flush_all_ng_local();
+}
+
+/*
+ * Functions for address checking:
+ *
+ * cp15_ats1cpr_check() ... check stage 1 privileged (PL1) read access
+ * cp15_ats1cpw_check() ... check stage 1 privileged (PL1) write access
+ * cp15_ats1cur_check() ... check stage 1 unprivileged (PL0) read access
+ * cp15_ats1cuw_check() ... check stage 1 unprivileged (PL0) write access
+ *
+ * They must be called while interrupts are disabled to get consistent result.
+ */
+static __inline int
+cp15_ats1cpr_check(vm_offset_t addr)
+{
+
+ cp15_ats1cpr_set(addr);
+ isb();
+ return (cp15_par_get() & 0x01 ? EFAULT : 0);
+}
+
+static __inline int
+cp15_ats1cpw_check(vm_offset_t addr)
+{
+
+ cp15_ats1cpw_set(addr);
+ isb();
+ return (cp15_par_get() & 0x01 ? EFAULT : 0);
+}
+
+static __inline int
+cp15_ats1cur_check(vm_offset_t addr)
+{
+
+ cp15_ats1cur_set(addr);
+ isb();
+ return (cp15_par_get() & 0x01 ? EFAULT : 0);
+}
+
+static __inline int
+cp15_ats1cuw_check(vm_offset_t addr)
+{
+
+ cp15_ats1cuw_set(addr);
+ isb();
+ return (cp15_par_get() & 0x01 ? EFAULT : 0);
+}
+
+#endif /* !MACHINE_CPU_V6_H */
diff --git a/sys/arm/include/cpu.h b/sys/arm/include/cpu.h
new file mode 100644
index 000000000000..8937a87aebea
--- /dev/null
+++ b/sys/arm/include/cpu.h
@@ -0,0 +1,90 @@
+/* $NetBSD: cpu.h,v 1.2 2001/02/23 21:23:52 reinoud Exp $ */
+/* $FreeBSD$ */
+
+#ifndef MACHINE_CPU_H
+#define MACHINE_CPU_H
+
+#include <machine/armreg.h>
+#include <machine/frame.h>
+
+void cpu_halt(void);
+void swi_vm(void *);
+
+#ifdef _KERNEL
+#include <machine/cpu-v6.h>
+
+static __inline uint64_t
+get_cyclecount(void)
+{
+#if __ARM_ARCH > 6 || (__ARM_ARCH == 6 && defined(CPU_ARM1176))
+#if (__ARM_ARCH > 6) && defined(DEV_PMU)
+ if (pmu_attched) {
+ u_int cpu;
+ uint64_t h, h2;
+ uint32_t l, r;
+
+ cpu = PCPU_GET(cpuid);
+ h = (uint64_t)atomic_load_acq_32(&ccnt_hi[cpu]);
+ l = cp15_pmccntr_get();
+ /* In case interrupts are disabled we need to check for overflow. */
+ r = cp15_pmovsr_get();
+ if (r & PMU_OVSR_C) {
+ atomic_add_32(&ccnt_hi[cpu], 1);
+ /* Clear the event. */
+ cp15_pmovsr_set(PMU_OVSR_C);
+ }
+ /* Make sure there was no wrap-around while we read the lo half. */
+ h2 = (uint64_t)atomic_load_acq_32(&ccnt_hi[cpu]);
+ if (h != h2)
+ l = cp15_pmccntr_get();
+ return (h2 << 32 | l);
+ } else
+#endif
+ return cp15_pmccntr_get();
+#else /* No performance counters, so use binuptime(9). This is slooooow */
+ struct bintime bt;
+
+ binuptime(&bt);
+ return ((uint64_t)bt.sec << 56 | bt.frac >> 8);
+#endif
+}
+#endif
+
+#define TRAPF_USERMODE(frame) ((frame->tf_spsr & PSR_MODE) == PSR_USR32_MODE)
+
+#define TRAPF_PC(tfp) ((tfp)->tf_pc)
+
+#define cpu_getstack(td) ((td)->td_frame->tf_usr_sp)
+#define cpu_setstack(td, sp) ((td)->td_frame->tf_usr_sp = (sp))
+#define cpu_spinwait() /* nothing */
+#define cpu_lock_delay() DELAY(1)
+
+#define ARM_NVEC 8
+#define ARM_VEC_ALL 0xffffffff
+
+extern vm_offset_t vector_page;
+
+/*
+ * Params passed into initarm. If you change the size of this you will
+ * need to update locore.S to allocate more memory on the stack before
+ * it calls initarm.
+ */
+struct arm_boot_params {
+ register_t abp_size; /* Size of this structure */
+ register_t abp_r0; /* r0 from the boot loader */
+ register_t abp_r1; /* r1 from the boot loader */
+ register_t abp_r2; /* r2 from the boot loader */
+ register_t abp_r3; /* r3 from the boot loader */
+ vm_offset_t abp_physaddr; /* The kernel physical address */
+ vm_offset_t abp_pagetable; /* The early page table */
+};
+
+void arm_vector_init(vm_offset_t, int);
+void fork_trampoline(void);
+void identify_arm_cpu(void);
+void *initarm(struct arm_boot_params *);
+
+extern char btext[];
+extern char etext[];
+int badaddr_read(void *, size_t, void *);
+#endif /* !MACHINE_CPU_H */
diff --git a/sys/arm/include/cpufunc.h b/sys/arm/include/cpufunc.h
new file mode 100644
index 000000000000..a5db3486aff1
--- /dev/null
+++ b/sys/arm/include/cpufunc.h
@@ -0,0 +1,259 @@
+/* $NetBSD: cpufunc.h,v 1.29 2003/09/06 09:08:35 rearnsha Exp $ */
+
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 1997 Mark Brinicombe.
+ * Copyright (c) 1997 Causality Limited
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Causality Limited.
+ * 4. The name of Causality Limited may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CAUSALITY LIMITED ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL CAUSALITY LIMITED BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * RiscBSD kernel project
+ *
+ * cpufunc.h
+ *
+ * Prototypes for cpu, mmu and tlb related functions.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_CPUFUNC_H_
+#define _MACHINE_CPUFUNC_H_
+
+#ifdef _KERNEL
+
+#include <sys/types.h>
+#include <machine/armreg.h>
+
+static __inline void
+breakpoint(void)
+{
+ __asm("udf 0xffff");
+}
+
+struct cpu_functions {
+ /* CPU functions */
+ void (*cf_l2cache_wbinv_all) (void);
+ void (*cf_l2cache_wbinv_range) (vm_offset_t, vm_size_t);
+ void (*cf_l2cache_inv_range) (vm_offset_t, vm_size_t);
+ void (*cf_l2cache_wb_range) (vm_offset_t, vm_size_t);
+ void (*cf_l2cache_drain_writebuf) (void);
+
+ /* Other functions */
+
+ void (*cf_sleep) (int mode);
+
+ void (*cf_setup) (void);
+};
+
+extern struct cpu_functions cpufuncs;
+extern u_int cputype;
+
+#define cpu_l2cache_wbinv_all() cpufuncs.cf_l2cache_wbinv_all()
+#define cpu_l2cache_wb_range(a, s) cpufuncs.cf_l2cache_wb_range((a), (s))
+#define cpu_l2cache_inv_range(a, s) cpufuncs.cf_l2cache_inv_range((a), (s))
+#define cpu_l2cache_wbinv_range(a, s) cpufuncs.cf_l2cache_wbinv_range((a), (s))
+#define cpu_l2cache_drain_writebuf() cpufuncs.cf_l2cache_drain_writebuf()
+
+#define cpu_sleep(m) cpufuncs.cf_sleep(m)
+
+#define cpu_setup() cpufuncs.cf_setup()
+
+int set_cpufuncs (void);
+#define ARCHITECTURE_NOT_PRESENT 1 /* known but not configured */
+
+void cpufunc_nullop (void);
+u_int cpufunc_control (u_int clear, u_int bic);
+
+
+#if defined(CPU_CORTEXA) || defined(CPU_MV_PJ4B) || defined(CPU_KRAIT)
+void armv7_cpu_sleep (int);
+#endif
+#if defined(CPU_MV_PJ4B)
+void pj4b_config (void);
+#endif
+
+#if defined(CPU_ARM1176)
+void arm11x6_sleep (int); /* no ref. for errata */
+#endif
+
+
+/*
+ * Macros for manipulating CPU interrupts
+ */
+#define __ARM_INTR_BITS (PSR_I | PSR_F | PSR_A)
+
+static __inline uint32_t
+__set_cpsr(uint32_t bic, uint32_t eor)
+{
+ uint32_t tmp, ret;
+
+ __asm __volatile(
+ "mrs %0, cpsr\n" /* Get the CPSR */
+ "bic %1, %0, %2\n" /* Clear bits */
+ "eor %1, %1, %3\n" /* XOR bits */
+ "msr cpsr_xc, %1\n" /* Set the CPSR */
+ : "=&r" (ret), "=&r" (tmp)
+ : "r" (bic), "r" (eor) : "memory");
+
+ return ret;
+}
+
+static __inline uint32_t
+disable_interrupts(uint32_t mask)
+{
+
+ return (__set_cpsr(mask & __ARM_INTR_BITS, mask & __ARM_INTR_BITS));
+}
+
+static __inline uint32_t
+enable_interrupts(uint32_t mask)
+{
+
+ return (__set_cpsr(mask & __ARM_INTR_BITS, 0));
+}
+
+static __inline uint32_t
+restore_interrupts(uint32_t old_cpsr)
+{
+
+ return (__set_cpsr(__ARM_INTR_BITS, old_cpsr & __ARM_INTR_BITS));
+}
+
+static __inline register_t
+intr_disable(void)
+{
+
+ return (disable_interrupts(PSR_I | PSR_F));
+}
+
+static __inline void
+intr_restore(register_t s)
+{
+
+ restore_interrupts(s);
+}
+#undef __ARM_INTR_BITS
+
+/*
+ * Functions to manipulate cpu r13
+ * (in arm/arm32/setstack.S)
+ */
+
+void set_stackptr (u_int mode, u_int address);
+u_int get_stackptr (u_int mode);
+
+/*
+ * CPU functions from locore.S
+ */
+
+void cpu_reset (void) __attribute__((__noreturn__));
+
+/*
+ * Cache info variables.
+ */
+
+/* PRIMARY CACHE VARIABLES */
+extern int arm_dcache_align;
+extern int arm_dcache_align_mask;
+
+
+#define HAVE_INLINE_FFS
+
+static __inline __pure2 int
+ffs(int mask)
+{
+
+ return (__builtin_ffs(mask));
+}
+
+#define HAVE_INLINE_FFSL
+
+static __inline __pure2 int
+ffsl(long mask)
+{
+
+ return (__builtin_ffsl(mask));
+}
+
+#define HAVE_INLINE_FFSLL
+
+static __inline __pure2 int
+ffsll(long long mask)
+{
+
+ return (__builtin_ffsll(mask));
+}
+
+#define HAVE_INLINE_FLS
+
+static __inline __pure2 int
+fls(int mask)
+{
+
+ return (mask == 0 ? 0 :
+ 8 * sizeof(mask) - __builtin_clz((u_int)mask));
+}
+
+#define HAVE_INLINE_FLSL
+
+static __inline __pure2 int
+flsl(long mask)
+{
+
+ return (mask == 0 ? 0 :
+ 8 * sizeof(mask) - __builtin_clzl((u_long)mask));
+}
+
+#define HAVE_INLINE_FLSLL
+
+static __inline __pure2 int
+flsll(long long mask)
+{
+
+ return (mask == 0 ? 0 :
+ 8 * sizeof(mask) - __builtin_clzll((unsigned long long)mask));
+}
+#else /* !_KERNEL */
+
+static __inline void
+breakpoint(void)
+{
+
+ /*
+ * This matches the instruction used by GDB for software
+ * breakpoints.
+ */
+ __asm("udf 0xfdee");
+}
+
+#endif /* _KERNEL */
+#endif /* _MACHINE_CPUFUNC_H_ */
+
+/* End of cpufunc.h */
diff --git a/sys/arm/include/cpuinfo.h b/sys/arm/include/cpuinfo.h
new file mode 100644
index 000000000000..97e063d384c2
--- /dev/null
+++ b/sys/arm/include/cpuinfo.h
@@ -0,0 +1,129 @@
+/*-
+ * Copyright 2014 Svatopluk Kraus <onwahe@gmail.com>
+ * Copyright 2014 Michal Meloun <meloun@miracle.cz>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_CPUINFO_H_
+#define _MACHINE_CPUINFO_H_
+
+#include <sys/types.h>
+
+#define CPU_IMPLEMENTER_ARM 0x41
+#define CPU_IMPLEMENTER_QCOM 0x51
+#define CPU_IMPLEMENTER_MRVL 0x56
+
+/* ARM */
+#define CPU_ARCH_ARM1176 0xB76
+#define CPU_ARCH_CORTEX_A5 0xC05
+#define CPU_ARCH_CORTEX_A7 0xC07
+#define CPU_ARCH_CORTEX_A8 0xC08
+#define CPU_ARCH_CORTEX_A9 0xC09
+#define CPU_ARCH_CORTEX_A12 0xC0D
+#define CPU_ARCH_CORTEX_A15 0xC0F
+#define CPU_ARCH_CORTEX_A17 0xC11
+#define CPU_ARCH_CORTEX_A53 0xD03
+#define CPU_ARCH_CORTEX_A57 0xD07
+#define CPU_ARCH_CORTEX_A72 0xD08
+#define CPU_ARCH_CORTEX_A73 0xD09
+#define CPU_ARCH_CORTEX_A75 0xD0A
+
+/* QCOM */
+#define CPU_ARCH_KRAIT_300 0x06F
+
+/* MRVL */
+#define CPU_ARCH_SHEEVA_581 0x581 /* PJ4/PJ4B */
+#define CPU_ARCH_SHEEVA_584 0x584 /* PJ4B-MP/PJ4C */
+
+struct cpuinfo {
+ /* raw id registers */
+ uint32_t midr;
+ uint32_t ctr;
+ uint32_t tcmtr;
+ uint32_t tlbtr;
+ uint32_t mpidr;
+ uint32_t revidr;
+ uint32_t id_pfr0;
+ uint32_t id_pfr1;
+ uint32_t id_dfr0;
+ uint32_t id_afr0;
+ uint32_t id_mmfr0;
+ uint32_t id_mmfr1;
+ uint32_t id_mmfr2;
+ uint32_t id_mmfr3;
+ uint32_t id_isar0;
+ uint32_t id_isar1;
+ uint32_t id_isar2;
+ uint32_t id_isar3;
+ uint32_t id_isar4;
+ uint32_t id_isar5;
+ uint32_t cbar;
+ uint32_t ccsidr;
+ uint32_t clidr;
+
+ /* Parsed bits of above registers... */
+
+ /* midr */
+ int implementer;
+ int revision;
+ int architecture;
+ int part_number;
+ int patch;
+
+ /* id_mmfr0 */
+ int outermost_shareability;
+ int shareability_levels;
+ int auxiliary_registers;
+ int innermost_shareability;
+
+ /* id_mmfr1 */
+ int mem_barrier;
+
+ /* id_mmfr3 */
+ int coherent_walk;
+ int maintenance_broadcast;
+
+ /* id_pfr1 */
+ int generic_timer_ext;
+ int virtualization_ext;
+ int security_ext;
+
+ /* L1 cache info */
+ int dcache_line_size;
+ int dcache_line_mask;
+ int icache_line_size;
+ int icache_line_mask;
+
+ /* mpidr */
+ int mp_ext;
+};
+
+extern struct cpuinfo cpuinfo;
+
+void cpuinfo_init(void);
+void cpuinfo_init_bp_hardening(void);
+void cpuinfo_reinit_mmu(uint32_t ttb);
+#endif /* _MACHINE_CPUINFO_H_ */
diff --git a/sys/arm/include/db_machdep.h b/sys/arm/include/db_machdep.h
new file mode 100644
index 000000000000..d6d5ade51a86
--- /dev/null
+++ b/sys/arm/include/db_machdep.h
@@ -0,0 +1,94 @@
+/*-
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: FreeBSD: src/sys/i386/include/db_machdep.h,v 1.16 1999/10/04
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_DB_MACHDEP_H_
+#define _MACHINE_DB_MACHDEP_H_
+
+#include <machine/frame.h>
+#include <machine/trap.h>
+#include <machine/armreg.h>
+
+#define T_BREAKPOINT (1)
+#define T_WATCHPOINT (2)
+typedef vm_offset_t db_addr_t;
+typedef int db_expr_t;
+
+#define PC_REGS() ((db_addr_t)kdb_thrctx->pcb_regs.sf_pc)
+
+#define BKPT_INST (KERNEL_BREAKPOINT)
+#define BKPT_SIZE (INSN_SIZE)
+#define BKPT_SET(inst) (BKPT_INST)
+
+#define BKPT_SKIP do { \
+ kdb_frame->tf_pc += BKPT_SIZE; \
+} while (0)
+
+#define db_clear_single_step kdb_cpu_clear_singlestep
+#define db_set_single_step kdb_cpu_set_singlestep
+#define db_pc_is_singlestep kdb_cpu_pc_is_singlestep
+
+#define IS_BREAKPOINT_TRAP(type, code) (type == T_BREAKPOINT)
+#define IS_WATCHPOINT_TRAP(type, code) (type == T_WATCHPOINT)
+
+#define inst_trap_return(ins) (0)
+/* ldmxx reg, {..., pc}
+ 01800000 stack mode
+ 000f0000 register
+ 0000ffff register list */
+/* mov pc, reg
+ 0000000f register */
+#define inst_return(ins) (((ins) & 0x0e108000) == 0x08108000 || \
+ ((ins) & 0x0ff0fff0) == 0x01a0f000 || \
+ ((ins) & 0x0ffffff0) == 0x012fff10) /* bx */
+/* bl ...
+ 00ffffff offset>>2 */
+#define inst_call(ins) (((ins) & 0x0f000000) == 0x0b000000)
+/* b ...
+ 00ffffff offset>>2 */
+/* ldr pc, [pc, reg, lsl #2]
+ 0000000f register */
+
+#define inst_branch(ins) (((ins) & 0x0f000000) == 0x0a000000 || \
+ ((ins) & 0x0fdffff0) == 0x079ff100 || \
+ ((ins) & 0x0cd0f000) == 0x0490f000 || \
+ ((ins) & 0x0ffffff0) == 0x012fff30 || /* blx */ \
+ ((ins) & 0x0de0f000) == 0x0080f000)
+
+#define inst_load(ins) (0)
+#define inst_store(ins) (0)
+
+#define next_instr_address(pc, bd) ((bd) ? (pc) : ((pc) + INSN_SIZE))
+
+#define DB_ELFSIZE 32
+
+int db_validate_address(vm_offset_t);
+
+u_int branch_taken (u_int insn, db_addr_t pc);
+
+#endif /* !_MACHINE_DB_MACHDEP_H_ */
diff --git a/sys/arm/include/debug_monitor.h b/sys/arm/include/debug_monitor.h
new file mode 100644
index 000000000000..c160397393ad
--- /dev/null
+++ b/sys/arm/include/debug_monitor.h
@@ -0,0 +1,60 @@
+/*-
+ * Copyright (c) 2014 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Semihalf under
+ * the sponsorship of the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_DEBUG_MONITOR_H_
+#define _MACHINE_DEBUG_MONITOR_H_
+
+#ifdef DDB
+
+#include <machine/db_machdep.h>
+
+enum dbg_access_t {
+ HW_BREAKPOINT_X = 0,
+ HW_WATCHPOINT_R = 1,
+ HW_WATCHPOINT_W = 2,
+ HW_WATCHPOINT_RW = HW_WATCHPOINT_R | HW_WATCHPOINT_W,
+};
+
+void dbg_monitor_init(void);
+void dbg_monitor_init_secondary(void);
+void dbg_show_watchpoint(void);
+int dbg_setup_watchpoint(db_expr_t, db_expr_t, enum dbg_access_t);
+int dbg_remove_watchpoint(db_expr_t, db_expr_t);
+void dbg_resume_dbreg(void);
+
+#else /* DDB */
+static __inline void
+dbg_monitor_init(void)
+{
+}
+#endif
+
+#endif /* _MACHINE_DEBUG_MONITOR_H_ */
diff --git a/sys/arm/include/disassem.h b/sys/arm/include/disassem.h
new file mode 100644
index 000000000000..2ef5cda04c2f
--- /dev/null
+++ b/sys/arm/include/disassem.h
@@ -0,0 +1,56 @@
+/* $NetBSD: disassem.h,v 1.4 2001/03/04 04:15:58 matt Exp $ */
+
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 1997 Mark Brinicombe.
+ * Copyright (c) 1997 Causality Limited.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Mark Brinicombe.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Define the interface structure required by the disassembler.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_DISASSEM_H_
+#define _MACHINE_DISASSEM_H_
+typedef struct {
+ u_int (*di_readword)(u_int);
+ void (*di_printaddr)(u_int);
+ int (*di_printf)(const char *, ...) __printflike(1, 2);
+} disasm_interface_t;
+
+/* Prototypes for callable functions */
+
+vm_offset_t disasm(const disasm_interface_t *, vm_offset_t, int);
+void disassemble(u_int);
+
+#endif /* !_MACHINE_DISASSEM_H_ */
diff --git a/sys/arm/include/dump.h b/sys/arm/include/dump.h
new file mode 100644
index 000000000000..3a7432fd49d3
--- /dev/null
+++ b/sys/arm/include/dump.h
@@ -0,0 +1,70 @@
+/*-
+ * Copyright (c) 2014 EMC Corp.
+ * Author: Conrad Meyer <conrad.meyer@isilon.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_DUMP_H_
+#define _MACHINE_DUMP_H_
+
+#define KERNELDUMP_ARCH_VERSION KERNELDUMP_ARM_VERSION
+#define EM_VALUE EM_ARM
+/* XXX: I suppose 20 should be enough. */
+#define DUMPSYS_MD_PA_NPAIRS 20
+#define DUMPSYS_NUM_AUX_HDRS 1
+
+void dumpsys_wbinv_all(void);
+int dumpsys_write_aux_headers(struct dumperinfo *di);
+
+static inline void
+dumpsys_pa_init(void)
+{
+
+ dumpsys_gen_pa_init();
+}
+
+static inline struct dump_pa *
+dumpsys_pa_next(struct dump_pa *p)
+{
+
+ return (dumpsys_gen_pa_next(p));
+}
+
+static inline void
+dumpsys_unmap_chunk(vm_paddr_t pa, size_t s, void *va)
+{
+
+ dumpsys_gen_unmap_chunk(pa, s, va);
+}
+
+static inline int
+dumpsys(struct dumperinfo *di)
+{
+
+ return (dumpsys_generic(di));
+}
+
+#endif /* !_MACHINE_DUMP_H_ */
diff --git a/sys/arm/include/efi.h b/sys/arm/include/efi.h
new file mode 100644
index 000000000000..02e1e7e975ef
--- /dev/null
+++ b/sys/arm/include/efi.h
@@ -0,0 +1,12 @@
+/*-
+ * This file is in the public domain since it's just boilerplate.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __ARM_INCLUDE_EFI_H_
+#define __ARM_INCLUDE_EFI_H_
+
+#define EFIABI_ATTR
+
+#endif /* __ARM_INCLUDE_EFI_H_ */
diff --git a/sys/arm/include/elf.h b/sys/arm/include/elf.h
new file mode 100644
index 000000000000..affd4b349163
--- /dev/null
+++ b/sys/arm/include/elf.h
@@ -0,0 +1,115 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2001 David E. O'Brien
+ * Copyright (c) 1996-1997 John D. Polstra.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_ELF_H_
+#define _MACHINE_ELF_H_ 1
+
+/*
+ * EABI ELF definitions for the StrongARM architecture.
+ * See "ARM ELF", document no. `SWS ESPC 0003 A-08' for details.
+ */
+
+#include <sys/elf32.h> /* Definitions common to all 32 bit architectures. */
+
+#define __ELF_WORD_SIZE 32 /* Used by <sys/elf_generic.h> */
+#include <sys/elf_generic.h>
+
+typedef struct { /* Auxiliary vector entry on initial stack */
+ int a_type; /* Entry type. */
+ union {
+ long a_val; /* Integer value. */
+ void *a_ptr; /* Address. */
+ void (*a_fcn)(void); /* Function pointer (not used). */
+ } a_un;
+} Elf32_Auxinfo;
+
+__ElfType(Auxinfo);
+
+#define ELF_ARCH EM_ARM
+
+#define ELF_MACHINE_OK(x) ((x) == EM_ARM)
+
+/*
+ * Relocation types.
+ */
+
+#define R_ARM_COUNT 33 /* Count of defined relocation types. */
+
+/* Define "machine" characteristics */
+#define ELF_TARG_CLASS ELFCLASS32
+#ifdef __ARMEB__
+#define ELF_TARG_DATA ELFDATA2MSB
+#else
+#define ELF_TARG_DATA ELFDATA2LSB
+#endif
+#define ELF_TARG_MACH EM_ARM
+#define ELF_TARG_VER 1
+
+/* Defines specific for arm headers */
+#define EF_ARM_EABI_VERSION(x) (((x) & EF_ARM_EABIMASK) >> 24)
+#define EF_ARM_EABI_VERSION_UNKNOWN 0
+#define EF_ARM_EABI_FREEBSD_MIN 4
+
+#define ET_DYN_LOAD_ADDR 0x500000
+
+/* Flags passed in AT_HWCAP. */
+#define HWCAP_SWP 0x00000001 /* Unsupported, never set. */
+#define HWCAP_HALF 0x00000002 /* Always set. */
+#define HWCAP_THUMB 0x00000004
+#define HWCAP_26BIT 0x00000008 /* Unsupported, never set. */
+#define HWCAP_FAST_MULT 0x00000010 /* Always set. */
+#define HWCAP_FPA 0x00000020 /* Unsupported, never set. */
+#define HWCAP_VFP 0x00000040
+#define HWCAP_EDSP 0x00000080 /* Always set for ARMv6+. */
+#define HWCAP_JAVA 0x00000100 /* Unsupported, never set. */
+#define HWCAP_IWMMXT 0x00000200 /* Unsupported, never set. */
+#define HWCAP_CRUNCH 0x00000400 /* Unsupported, never set. */
+#define HWCAP_THUMBEE 0x00000800
+#define HWCAP_NEON 0x00001000
+#define HWCAP_VFPv3 0x00002000
+#define HWCAP_VFPv3D16 0x00004000
+#define HWCAP_TLS 0x00008000 /* Always set for ARMv6+. */
+#define HWCAP_VFPv4 0x00010000
+#define HWCAP_IDIVA 0x00020000
+#define HWCAP_IDIVT 0x00040000
+#define HWCAP_VFPD32 0x00080000
+#define HWCAP_IDIV (HWCAP_IDIVA | HWCAP_IDIVT)
+#define HWCAP_LPAE 0x00100000
+#define HWCAP_EVTSTRM 0x00200000 /* Not implemented yet. */
+
+/* Flags passed in AT_HWCAP2. */
+#define HWCAP2_AES 0x00000001
+#define HWCAP2_PMULL 0x00000002
+#define HWCAP2_SHA1 0x00000004
+#define HWCAP2_SHA2 0x00000008
+#define HWCAP2_CRC32 0x00000010
+
+#endif /* !_MACHINE_ELF_H_ */
diff --git a/sys/arm/include/endian.h b/sys/arm/include/endian.h
new file mode 100644
index 000000000000..5fb94db3b9b8
--- /dev/null
+++ b/sys/arm/include/endian.h
@@ -0,0 +1,142 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 2001 David E. O'Brien
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)endian.h 8.1 (Berkeley) 6/10/93
+ * $NetBSD: endian.h,v 1.7 1999/08/21 05:53:51 simonb Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _ENDIAN_H_
+#define _ENDIAN_H_
+
+#include <sys/_types.h>
+
+/*
+ * Definitions for byte order, according to byte significance from low
+ * address to high.
+ */
+#define _LITTLE_ENDIAN 1234 /* LSB first: i386, vax */
+#define _BIG_ENDIAN 4321 /* MSB first: 68000, ibm, net */
+#define _PDP_ENDIAN 3412 /* LSB first in word, MSW first in long */
+
+#ifdef __ARMEB__
+#define _BYTE_ORDER _BIG_ENDIAN
+#else
+#define _BYTE_ORDER _LITTLE_ENDIAN
+#endif /* __ARMEB__ */
+
+#if __BSD_VISIBLE
+#define LITTLE_ENDIAN _LITTLE_ENDIAN
+#define BIG_ENDIAN _BIG_ENDIAN
+#define PDP_ENDIAN _PDP_ENDIAN
+#define BYTE_ORDER _BYTE_ORDER
+#endif
+
+#ifdef __ARMEB__
+#define _QUAD_HIGHWORD 0
+#define _QUAD_LOWWORD 1
+#define __ntohl(x) ((__uint32_t)(x))
+#define __ntohs(x) ((__uint16_t)(x))
+#define __htonl(x) ((__uint32_t)(x))
+#define __htons(x) ((__uint16_t)(x))
+#else
+#define _QUAD_HIGHWORD 1
+#define _QUAD_LOWWORD 0
+#define __ntohl(x) (__bswap32(x))
+#define __ntohs(x) (__bswap16(x))
+#define __htonl(x) (__bswap32(x))
+#define __htons(x) (__bswap16(x))
+#endif /* __ARMEB__ */
+
+static __inline __uint64_t
+__bswap64(__uint64_t _x)
+{
+
+ return ((_x >> 56) | ((_x >> 40) & 0xff00) | ((_x >> 24) & 0xff0000) |
+ ((_x >> 8) & 0xff000000) | ((_x << 8) & ((__uint64_t)0xff << 32)) |
+ ((_x << 24) & ((__uint64_t)0xff << 40)) |
+ ((_x << 40) & ((__uint64_t)0xff << 48)) | ((_x << 56)));
+}
+
+static __inline __uint32_t
+__bswap32_var(__uint32_t v)
+{
+ __uint32_t t1;
+
+ __asm __volatile("eor %1, %0, %0, ror #16\n"
+ "bic %1, %1, #0x00ff0000\n"
+ "mov %0, %0, ror #8\n"
+ "eor %0, %0, %1, lsr #8\n"
+ : "+r" (v), "=r" (t1));
+
+ return (v);
+}
+
+static __inline __uint16_t
+__bswap16_var(__uint16_t v)
+{
+ __uint32_t ret = v & 0xffff;
+
+ __asm __volatile(
+ "mov %0, %0, ror #8\n"
+ "orr %0, %0, %0, lsr #16\n"
+ "bic %0, %0, %0, lsl #16"
+ : "+r" (ret));
+
+ return ((__uint16_t)ret);
+}
+
+#ifdef __OPTIMIZE__
+
+#define __bswap32_constant(x) \
+ ((((x) & 0xff000000U) >> 24) | \
+ (((x) & 0x00ff0000U) >> 8) | \
+ (((x) & 0x0000ff00U) << 8) | \
+ (((x) & 0x000000ffU) << 24))
+
+#define __bswap16_constant(x) \
+ ((((x) & 0xff00) >> 8) | \
+ (((x) & 0x00ff) << 8))
+
+#define __bswap16(x) \
+ ((__uint16_t)(__builtin_constant_p(x) ? \
+ __bswap16_constant(x) : \
+ __bswap16_var(x)))
+
+#define __bswap32(x) \
+ ((__uint32_t)(__builtin_constant_p(x) ? \
+ __bswap32_constant(x) : \
+ __bswap32_var(x)))
+
+#else
+#define __bswap16(x) __bswap16_var(x)
+#define __bswap32(x) __bswap32_var(x)
+
+#endif /* __OPTIMIZE__ */
+#endif /* !_ENDIAN_H_ */
diff --git a/sys/arm/include/exec.h b/sys/arm/include/exec.h
new file mode 100644
index 000000000000..be6bc6f8677b
--- /dev/null
+++ b/sys/arm/include/exec.h
@@ -0,0 +1,39 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 2001 David E. O'Brien
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_EXEC_H_
+#define _MACHINE_EXEC_H_
+
+#define __LDPGSZ 4096
+
+#endif /* !_MACHINE_EXEC_H_ */
diff --git a/sys/arm/include/fdt.h b/sys/arm/include/fdt.h
new file mode 100644
index 000000000000..e8af11c8c4b0
--- /dev/null
+++ b/sys/arm/include/fdt.h
@@ -0,0 +1,44 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2010 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Semihalf under sponsorship from
+ * the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_FDT_H_
+#define _MACHINE_FDT_H_
+
+#include <machine/bus.h>
+
+/*
+ * Bus space tag. XXX endianess info needs to be derived from the blob.
+ */
+extern bus_space_tag_t fdtbus_bs_tag;
+
+#endif /* _MACHINE_FDT_H_ */
diff --git a/sys/arm/include/fiq.h b/sys/arm/include/fiq.h
new file mode 100644
index 000000000000..a075c99e7d9b
--- /dev/null
+++ b/sys/arm/include/fiq.h
@@ -0,0 +1,73 @@
+/* $NetBSD: fiq.h,v 1.1 2001/12/20 01:20:23 thorpej Exp $ */
+
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 2001 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Jason R. Thorpe for Wasabi Systems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the NetBSD Project by
+ * Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#ifndef _MACHINE_FIQ_H_
+#define _MACHINE_FIQ_H_
+
+#include <sys/queue.h>
+
+struct fiqregs {
+ u_int fr_r8; /* FIQ mode r8 */
+ u_int fr_r9; /* FIQ mode r9 */
+ u_int fr_r10; /* FIQ mode r10 */
+ u_int fr_r11; /* FIQ mode r11 */
+ u_int fr_r12; /* FIQ mode r12 */
+ u_int fr_r13; /* FIQ mode r13 */
+};
+
+struct fiqhandler {
+ TAILQ_ENTRY(fiqhandler) fh_list;/* link in the FIQ handler stack */
+ void *fh_func; /* FIQ handler routine */
+ size_t fh_size; /* size of FIQ handler */
+ int fh_flags; /* flags; see below */
+ struct fiqregs *fh_regs; /* pointer to regs structure */
+};
+
+#define FH_CANPUSH 0x01 /* can push this handler out of the way */
+
+int fiq_claim(struct fiqhandler *);
+void fiq_release(struct fiqhandler *);
+
+void fiq_getregs(struct fiqregs *);
+void fiq_setregs(struct fiqregs *);
+
+#endif /* _MACHINE_FIQ_H_ */
diff --git a/sys/arm/include/float.h b/sys/arm/include/float.h
new file mode 100644
index 000000000000..857d76ea57e7
--- /dev/null
+++ b/sys/arm/include/float.h
@@ -0,0 +1,100 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)float.h 7.1 (Berkeley) 5/8/90
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_FLOAT_H_
+#define _MACHINE_FLOAT_H_ 1
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+extern int __flt_rounds(void);
+__END_DECLS
+
+#define FLT_RADIX 2 /* b */
+#ifndef _ARM_HARD_FLOAT
+#define FLT_ROUNDS __flt_rounds()
+#else
+#define FLT_ROUNDS (-1)
+#endif
+#if __ISO_C_VISIBLE >= 1999
+#define FLT_EVAL_METHOD 0
+#define DECIMAL_DIG 17 /* max precision in decimal digits */
+#endif
+
+#define FLT_MANT_DIG 24 /* p */
+#define FLT_EPSILON 1.19209290E-07F /* b**(1-p) */
+#define FLT_DIG 6 /* floor((p-1)*log10(b))+(b == 10) */
+#define FLT_MIN_EXP (-125) /* emin */
+#define FLT_MIN 1.17549435E-38F /* b**(emin-1) */
+#define FLT_MIN_10_EXP (-37) /* ceil(log10(b**(emin-1))) */
+#define FLT_MAX_EXP 128 /* emax */
+#define FLT_MAX 3.40282347E+38F /* (1-b**(-p))*b**emax */
+#define FLT_MAX_10_EXP 38 /* floor(log10((1-b**(-p))*b**emax)) */
+#if __ISO_C_VISIBLE >= 2011
+#define FLT_TRUE_MIN 1.40129846E-45F /* b**(emin-p) */
+#define FLT_DECIMAL_DIG 9 /* ceil(1+p*log10(b)) */
+#define FLT_HAS_SUBNORM 1
+#endif /* __ISO_C_VISIBLE >= 2011 */
+
+#define DBL_MANT_DIG 53
+#define DBL_EPSILON 2.2204460492503131E-16
+#define DBL_DIG 15
+#define DBL_MIN_EXP (-1021)
+#define DBL_MIN 2.2250738585072014E-308
+#define DBL_MIN_10_EXP (-307)
+#define DBL_MAX_EXP 1024
+#define DBL_MAX 1.7976931348623157E+308
+#define DBL_MAX_10_EXP 308
+#if __ISO_C_VISIBLE >= 2011
+#define DBL_TRUE_MIN 4.9406564584124654E-324
+#define DBL_DECIMAL_DIG 17
+#define DBL_HAS_SUBNORM 1
+#endif /* __ISO_C_VISIBLE >= 2011 */
+
+#define LDBL_MANT_DIG DBL_MANT_DIG
+#define LDBL_EPSILON ((long double)DBL_EPSILON)
+#define LDBL_DIG DBL_DIG
+#define LDBL_MIN_EXP DBL_MIN_EXP
+#define LDBL_MIN ((long double)DBL_MIN)
+#define LDBL_MIN_10_EXP DBL_MIN_10_EXP
+#define LDBL_MAX_EXP DBL_MAX_EXP
+#define LDBL_MAX ((long double)DBL_MAX)
+#define LDBL_MAX_10_EXP DBL_MAX_10_EXP
+#if __ISO_C_VISIBLE >= 2011
+#define LDBL_TRUE_MIN ((long double)DBL_TRUE_MIN)
+#define LDBL_DECIMAL_DIG DBL_DECIMAL_DIG
+#define LDBL_HAS_SUBNORM DBL_HAS_SUBNORM
+#endif /* __ISO_C_VISIBLE >= 2011 */
+
+#endif /* _MACHINE_FLOAT_H_ */
diff --git a/sys/arm/include/floatingpoint.h b/sys/arm/include/floatingpoint.h
new file mode 100644
index 000000000000..b1c28e91d79e
--- /dev/null
+++ b/sys/arm/include/floatingpoint.h
@@ -0,0 +1,44 @@
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 1993 Andrew Moore, Talke Studio
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#) floatingpoint.h 1.0 (Berkeley) 9/23/93
+ * $FreeBSD$
+ */
+
+#ifndef _FLOATINGPOINT_H_
+#define _FLOATINGPOINT_H_
+
+#include <machine/ieeefp.h>
+
+#endif /* !_FLOATINGPOINT_H_ */
diff --git a/sys/arm/include/frame.h b/sys/arm/include/frame.h
new file mode 100644
index 000000000000..8866e8ab0a33
--- /dev/null
+++ b/sys/arm/include/frame.h
@@ -0,0 +1,137 @@
+/* $NetBSD: frame.h,v 1.5 2002/10/19 00:10:54 bjh21 Exp $ */
+
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 1994-1997 Mark Brinicombe.
+ * Copyright (c) 1994 Brini.
+ * All rights reserved.
+ *
+ * This code is derived from software written for Brini by Mark Brinicombe
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Brini.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * RiscBSD kernel project
+ *
+ * frame.h
+ *
+ * Stack frames structures
+ *
+ * Created : 30/09/94
+ *
+ * $FreeBSD$
+ *
+ */
+
+#ifndef _MACHINE_FRAME_H_
+#define _MACHINE_FRAME_H_
+
+#ifndef _LOCORE
+
+#include <sys/signal.h>
+#include <sys/ucontext.h>
+
+/*
+ * Trap frame. Pushed onto the kernel stack on a trap (synchronous exception).
+ */
+
+struct trapframe {
+ register_t tf_spsr; /* Zero on arm26 */
+ register_t tf_r0;
+ register_t tf_r1;
+ register_t tf_r2;
+ register_t tf_r3;
+ register_t tf_r4;
+ register_t tf_r5;
+ register_t tf_r6;
+ register_t tf_r7;
+ register_t tf_r8;
+ register_t tf_r9;
+ register_t tf_r10;
+ register_t tf_r11;
+ register_t tf_r12;
+ register_t tf_usr_sp;
+ register_t tf_usr_lr;
+ register_t tf_svc_sp; /* Not used on arm26 */
+ register_t tf_svc_lr; /* Not used on arm26 */
+ register_t tf_pc;
+ register_t tf_pad;
+};
+
+/* Register numbers */
+#define tf_r13 tf_usr_sp
+#define tf_r14 tf_usr_lr
+#define tf_r15 tf_pc
+
+/*
+ * Signal frame. Pushed onto user stack before calling sigcode.
+ * The pointers are used in the trampoline code to locate the ucontext.
+ */
+struct sigframe {
+ siginfo_t sf_si; /* actual saved siginfo */
+ ucontext_t sf_uc; /* actual saved ucontext */
+ mcontext_vfp_t sf_vfp; /* actual saved VFP context */
+};
+
+/*
+ * Switch frame.
+ *
+ * It is important this is a multiple of 8 bytes so the stack is correctly
+ * aligned when we create new threads.
+ */
+struct switchframe
+{
+ register_t sf_r4;
+ register_t sf_r5;
+ register_t sf_r6;
+ register_t sf_r7;
+ register_t sf_r8;
+ register_t sf_r9;
+ register_t sf_r10;
+ register_t sf_r11;
+ register_t sf_r12;
+ register_t sf_sp;
+ register_t sf_lr;
+ register_t sf_pc;
+ register_t sf_tpidrurw;
+ register_t sf_spare0;
+};
+
+/*
+ * Stack frame. Used during stack traces (db_trace.c)
+ */
+struct frame {
+ u_int fr_fp;
+ u_int fr_sp;
+ u_int fr_lr;
+ u_int fr_pc;
+};
+
+#endif /* !_LOCORE */
+
+#endif /* _MACHINE_FRAME_H_ */
diff --git a/sys/arm/include/gdb_machdep.h b/sys/arm/include/gdb_machdep.h
new file mode 100644
index 000000000000..017025253bd5
--- /dev/null
+++ b/sys/arm/include/gdb_machdep.h
@@ -0,0 +1,73 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2006 Olivier Houchard
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_GDB_MACHDEP_H_
+#define _MACHINE_GDB_MACHDEP_H_
+
+#define GDB_BUFSZ 400
+#define GDB_NREGS 26
+#define GDB_REG_SP 13
+#define GDB_REG_LR 14
+#define GDB_REG_PC 15
+
+static __inline size_t
+gdb_cpu_regsz(int regnum)
+{
+ /*
+ * GDB expects the FPA registers f0-f7, each 96 bits wide, to be placed
+ * in between the PC and CSPR in response to a "g" packet.
+ */
+ return (regnum >= 16 && regnum <= 23 ? 12 : sizeof(int));
+}
+
+static __inline int
+gdb_cpu_query(void)
+{
+ return (0);
+}
+
+static __inline void *
+gdb_begin_write(void)
+{
+
+ return (NULL);
+}
+
+static __inline void
+gdb_end_write(void *arg __unused)
+{
+
+}
+
+void *gdb_cpu_getreg(int, size_t *);
+void gdb_cpu_setreg(int, void *);
+int gdb_cpu_signal(int, int);
+
+#endif /* !_MACHINE_GDB_MACHDEP_H_ */
diff --git a/sys/arm/include/ieee.h b/sys/arm/include/ieee.h
new file mode 100644
index 000000000000..0942eac8eadf
--- /dev/null
+++ b/sys/arm/include/ieee.h
@@ -0,0 +1,167 @@
+/* $NetBSD: ieee754.h,v 1.4 2003/10/27 02:30:26 simonb Exp $ */
+
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Lawrence Berkeley Laboratory.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ieee.h 8.1 (Berkeley) 6/11/93
+ *
+ * $FreeBSD$
+ *
+ */
+
+/*
+ * NOTICE: This is not a standalone file. To use it, #include it in
+ * your port's ieee.h header.
+ */
+
+#include <machine/endian.h>
+
+/*
+ * <sys/ieee754.h> defines the layout of IEEE 754 floating point types.
+ * Only single-precision and double-precision types are defined here;
+ * extended types, if available, are defined in the machine-dependent
+ * header.
+ */
+
+/*
+ * Define the number of bits in each fraction and exponent.
+ *
+ * k k+1
+ * Note that 1.0 x 2 == 0.1 x 2 and that denorms are represented
+ *
+ * (-exp_bias+1)
+ * as fractions that look like 0.fffff x 2 . This means that
+ *
+ * -126
+ * the number 0.10000 x 2 , for instance, is the same as the normalized
+ *
+ * -127 -128
+ * float 1.0 x 2 . Thus, to represent 2 , we need one leading zero
+ *
+ * -129
+ * in the fraction; to represent 2 , we need two, and so on. This
+ *
+ * (-exp_bias-fracbits+1)
+ * implies that the smallest denormalized number is 2
+ *
+ * for whichever format we are talking about: for single precision, for
+ *
+ * -126 -149
+ * instance, we get .00000000000000000000001 x 2 , or 1.0 x 2 , and
+ *
+ * -149 == -127 - 23 + 1.
+ */
+#define SNG_EXPBITS 8
+#define SNG_FRACBITS 23
+
+#define DBL_EXPBITS 11
+#define DBL_FRACBITS 52
+
+#if defined(__VFP_FP__) || defined(__ARM_EABI__)
+#define _IEEE_WORD_ORDER _BYTE_ORDER
+#else
+#define _IEEE_WORD_ORDER _BIG_ENDIAN
+#endif
+
+struct ieee_single {
+#if _BYTE_ORDER == _BIG_ENDIAN
+ u_int sng_sign:1;
+ u_int sng_exp:8;
+ u_int sng_frac:23;
+#else
+ u_int sng_frac:23;
+ u_int sng_exp:8;
+ u_int sng_sign:1;
+#endif
+};
+
+struct ieee_double {
+#if _BYTE_ORDER == _BIG_ENDIAN
+ u_int dbl_sign:1;
+ u_int dbl_exp:11;
+ u_int dbl_frach:20;
+ u_int dbl_fracl;
+#else
+#if _IEEE_WORD_ORDER == _LITTLE_ENDIAN
+ u_int dbl_fracl;
+#endif
+ u_int dbl_frach:20;
+ u_int dbl_exp:11;
+ u_int dbl_sign:1;
+#if _IEEE_WORD_ORDER == _BIG_ENDIAN
+ u_int dbl_fracl;
+#endif
+#endif
+};
+
+/*
+ * Floats whose exponent is in [1..INFNAN) (of whatever type) are
+ * `normal'. Floats whose exponent is INFNAN are either Inf or NaN.
+ * Floats whose exponent is zero are either zero (iff all fraction
+ * bits are zero) or subnormal values.
+ *
+ * A NaN is a `signalling NaN' if its QUIETNAN bit is clear in its
+ * high fraction; if the bit is set, it is a `quiet NaN'.
+ */
+#define SNG_EXP_INFNAN 255
+#define DBL_EXP_INFNAN 2047
+
+#if 0
+#define SNG_QUIETNAN (1 << 22)
+#define DBL_QUIETNAN (1 << 19)
+#endif
+
+/*
+ * Exponent biases.
+ */
+#define SNG_EXP_BIAS 127
+#define DBL_EXP_BIAS 1023
+
+/*
+ * Convenience data structures.
+ */
+union ieee_single_u {
+ float sngu_f;
+ struct ieee_single sngu_sng;
+};
+
+union ieee_double_u {
+ double dblu_d;
+ struct ieee_double dblu_dbl;
+};
diff --git a/sys/arm/include/ieeefp.h b/sys/arm/include/ieeefp.h
new file mode 100644
index 000000000000..0b61744b450b
--- /dev/null
+++ b/sys/arm/include/ieeefp.h
@@ -0,0 +1,53 @@
+/* $NetBSD: ieeefp.h,v 1.1 2001/01/10 19:02:06 bjh21 Exp $ */
+/* $FreeBSD$ */
+/*-
+ * Based on ieeefp.h written by J.T. Conklin, Apr 28, 1995
+ * Public domain.
+ */
+
+#ifndef _MACHINE_IEEEFP_H_
+#define _MACHINE_IEEEFP_H_
+
+/* Deprecated historical FPU control interface */
+
+/* FP exception codes */
+#define FP_EXCEPT_INV 0
+#define FP_EXCEPT_DZ 1
+#define FP_EXCEPT_OFL 2
+#define FP_EXCEPT_UFL 3
+#define FP_EXCEPT_IMP 4
+
+/* Exception type (used by fpsetmask() et al.) */
+
+typedef int fp_except;
+
+/* Bit defines for fp_except */
+
+#define FP_X_INV (1 << FP_EXCEPT_INV) /* invalid operation exception */
+#define FP_X_DZ (1 << FP_EXCEPT_DZ) /* divide-by-zero exception */
+#define FP_X_OFL (1 << FP_EXCEPT_OFL) /* overflow exception */
+#define FP_X_UFL (1 << FP_EXCEPT_UFL) /* underflow exception */
+#define FP_X_IMP (1 << FP_EXCEPT_IMP) /* imprecise (loss of precision; "inexact") */
+
+/* Rounding modes */
+
+typedef enum {
+ FP_RN=0, /* round to nearest representable number */
+ FP_RP=1, /* round toward positive infinity */
+ FP_RM=2, /* round toward negative infinity */
+ FP_RZ=3 /* round to zero (truncate) */
+} fp_rnd_t;
+
+/*
+ * FP precision modes
+ */
+typedef enum {
+ FP_PS=0, /* 24 bit (single-precision) */
+ FP_PRS, /* reserved */
+ FP_PD, /* 53 bit (double-precision) */
+ FP_PE /* 64 bit (extended-precision) */
+} fp_prec_t;
+
+#define fp_except_t int
+
+#endif /* _MACHINE_IEEEFP_H_ */
diff --git a/sys/arm/include/in_cksum.h b/sys/arm/include/in_cksum.h
new file mode 100644
index 000000000000..a75433830d73
--- /dev/null
+++ b/sys/arm/include/in_cksum.h
@@ -0,0 +1,66 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from tahoe: in_cksum.c 1.2 86/01/05
+ * from: @(#)in_cksum.c 1.3 (Berkeley) 1/19/91
+ * from: Id: in_cksum.c,v 1.8 1995/12/03 18:35:19 bde Exp
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_IN_CKSUM_H_
+#define _MACHINE_IN_CKSUM_H_ 1
+
+#include <sys/cdefs.h>
+
+#ifdef _KERNEL
+u_short in_cksum(struct mbuf *m, int len);
+u_short in_addword(u_short sum, u_short b);
+u_short in_cksum_skip(struct mbuf *m, int len, int skip);
+u_int do_cksum(const void *, int);
+#if defined(IPVERSION) && (IPVERSION == 4)
+u_int in_cksum_hdr(const struct ip *);
+#endif
+
+static __inline u_short
+in_pseudo(u_int sum, u_int b, u_int c)
+{
+ __asm __volatile("adds %0, %0, %1\n"
+ "adcs %0, %0, %2\n"
+ "adc %0, %0, #0\n"
+ : "+r" (sum)
+ : "r" (b), "r" (c));
+ sum = (sum & 0xffff) + (sum >> 16);
+ if (sum > 0xffff)
+ sum -= 0xffff;
+ return (sum);
+}
+
+#endif /* _KERNEL */
+#endif /* _MACHINE_IN_CKSUM_H_ */
diff --git a/sys/arm/include/intr.h b/sys/arm/include/intr.h
new file mode 100644
index 000000000000..c783c41fd35d
--- /dev/null
+++ b/sys/arm/include/intr.h
@@ -0,0 +1,69 @@
+/* $NetBSD: intr.h,v 1.7 2003/06/16 20:01:00 thorpej Exp $ */
+
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 1997 Mark Brinicombe.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Mark Brinicombe
+ * for the NetBSD Project.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#ifndef _MACHINE_INTR_H_
+#define _MACHINE_INTR_H_
+
+#ifdef FDT
+#include <dev/ofw/openfirm.h>
+#endif
+
+#ifndef NIRQ
+#define NIRQ 1024 /* XXX - It should be an option. */
+#endif
+
+#include <sys/intr.h>
+
+#ifdef SMP
+typedef void intr_ipi_send_t(void *, cpuset_t, u_int);
+typedef void intr_ipi_handler_t(void *);
+
+void intr_ipi_dispatch(u_int, struct trapframe *);
+void intr_ipi_send(cpuset_t, u_int);
+
+void intr_ipi_setup(u_int, const char *, intr_ipi_handler_t *, void *,
+ intr_ipi_send_t *, void *);
+
+int intr_pic_ipi_setup(u_int, const char *, intr_ipi_handler_t *, void *);
+#endif
+
+void arm_irq_memory_barrier(uintptr_t);
+
+#endif /* _MACHINE_INTR_H */
diff --git a/sys/arm/include/kdb.h b/sys/arm/include/kdb.h
new file mode 100644
index 000000000000..42677499ed78
--- /dev/null
+++ b/sys/arm/include/kdb.h
@@ -0,0 +1,57 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2004 Marcel Moolenaar
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_KDB_H_
+#define _MACHINE_KDB_H_
+
+#include <machine/cpu.h>
+#include <machine/db_machdep.h>
+#include <machine/frame.h>
+#include <machine/psl.h>
+
+#define KDB_STOPPEDPCB(pc) &stoppcbs[pc->pc_cpuid]
+
+extern void kdb_cpu_clear_singlestep(void);
+extern void kdb_cpu_set_singlestep(void);
+boolean_t kdb_cpu_pc_is_singlestep(db_addr_t);
+
+static __inline void
+kdb_cpu_sync_icache(unsigned char *addr, size_t size)
+{
+
+ icache_sync((vm_offset_t)addr, size);
+}
+
+static __inline void
+kdb_cpu_trap(int type, int code)
+{
+}
+
+#endif /* _MACHINE_KDB_H_ */
diff --git a/sys/arm/include/limits.h b/sys/arm/include/limits.h
new file mode 100644
index 000000000000..5a7b831b3089
--- /dev/null
+++ b/sys/arm/include/limits.h
@@ -0,0 +1,46 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)limits.h 8.3 (Berkeley) 1/4/94
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_LIMITS_H_
+#define _MACHINE_LIMITS_H_
+
+#include <sys/cdefs.h>
+
+#ifdef __CC_SUPPORTS_WARNING
+#warning "machine/limits.h is deprecated. Include sys/limits.h instead."
+#endif
+
+#include <sys/limits.h>
+
+#endif /* !_MACHINE_LIMITS_H_ */
diff --git a/sys/arm/include/machdep.h b/sys/arm/include/machdep.h
new file mode 100644
index 000000000000..3efad39753d3
--- /dev/null
+++ b/sys/arm/include/machdep.h
@@ -0,0 +1,75 @@
+/* $NetBSD: machdep.h,v 1.7 2002/02/21 02:52:21 thorpej Exp $ */
+/* $FreeBSD$ */
+
+#ifndef _MACHDEP_BOOT_MACHDEP_H_
+#define _MACHDEP_BOOT_MACHDEP_H_
+
+/* Structs that need to be initialised by initarm */
+extern vm_offset_t irqstack;
+extern vm_offset_t undstack;
+extern vm_offset_t abtstack;
+
+/* Define various stack sizes in pages */
+#define IRQ_STACK_SIZE 1
+#define ABT_STACK_SIZE 1
+#define UND_STACK_SIZE 1
+
+/* misc prototypes used by the many arm machdeps */
+struct trapframe;
+void init_proc0(vm_offset_t kstack);
+void halt(void);
+void abort_handler(struct trapframe *, int );
+void set_stackptrs(int cpu);
+void undefinedinstruction_bounce(struct trapframe *);
+
+/* Early boot related helper functions */
+struct arm_boot_params;
+vm_offset_t default_parse_boot_param(struct arm_boot_params *abp);
+vm_offset_t fake_preload_metadata(struct arm_boot_params *abp,
+ void *dtb_ptr, size_t dtb_size);
+vm_offset_t parse_boot_param(struct arm_boot_params *abp);
+void arm_parse_fdt_bootargs(void);
+void arm_print_kenv(void);
+
+int arm_get_vfpstate(struct thread *td, void *args);
+
+/* Board-specific attributes */
+void board_set_serial(uint64_t);
+void board_set_revision(uint32_t);
+
+int arm_predict_branch(void *, u_int, register_t, register_t *,
+ u_int (*)(void*, int), u_int (*)(void*, vm_offset_t, u_int*));
+
+#ifdef PLATFORM
+typedef void delay_func(int, void *);
+void arm_set_delay(delay_func *, void *);
+#endif
+
+#ifdef EFI
+struct efi_map_header;
+struct mem_region;
+void arm_add_efi_map_entries(struct efi_map_header *efihdr,
+ struct mem_region *mr, int *mrcnt);
+#endif
+
+/*
+ * Symbols created by ldscript.arm which are accessible in the kernel as global
+ * symbols. They have uint8 type because they mark the byte location where the
+ * corresponding data starts or ends (in the end case, it's the next byte
+ * following the data, so the data size is end-start). These are listed below
+ * in the order they occur within the kernel (i.e., the address of each variable
+ * should be greater than any of the ones before it).
+ */
+extern uint8_t _start; /* Kernel entry point in locore.S */
+extern uint8_t _etext; /* text segment end */
+extern uint8_t _extab_start; /* unwind table start */
+extern uint8_t _exidx_start; /* unwind index start */
+extern uint8_t _exidx_end; /* unwind index end */
+extern uint8_t _start_ctors; /* ctors data start */
+extern uint8_t _stop_ctors; /* ctors data end */
+extern uint8_t _edata; /* data segment end */
+extern uint8_t __bss_start; /* bss segment start */
+extern uint8_t _ebss; /* bss segment end */
+extern uint8_t _end; /* End of kernel (text+ctors+unwind+data+bss) */
+
+#endif /* !_MACHINE_MACHDEP_H_ */
diff --git a/sys/arm/include/md_var.h b/sys/arm/include/md_var.h
new file mode 100644
index 000000000000..d60992f104e1
--- /dev/null
+++ b/sys/arm/include/md_var.h
@@ -0,0 +1,71 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1995 Bruce D. Evans.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: FreeBSD: src/sys/i386/include/md_var.h,v 1.40 2001/07/12
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_MD_VAR_H_
+#define _MACHINE_MD_VAR_H_
+
+extern long Maxmem;
+extern char sigcode[];
+extern int szsigcode;
+extern u_long elf_hwcap;
+extern u_long elf_hwcap2;
+extern vm_paddr_t arm_physmem_kernaddr;
+
+extern int (*_arm_memcpy)(void *, void *, int, int);
+extern int (*_arm_bzero)(void *, int, int);
+
+extern int _min_memcpy_size;
+extern int _min_bzero_size;
+
+#define DST_IS_USER 0x1
+#define SRC_IS_USER 0x2
+#define IS_PHYSICAL 0x4
+
+enum cpu_class {
+ CPU_CLASS_NONE,
+ CPU_CLASS_CORTEXA,
+ CPU_CLASS_KRAIT,
+ CPU_CLASS_ARM11J,
+ CPU_CLASS_MARVELL
+};
+extern enum cpu_class cpu_class;
+
+struct dumperinfo;
+extern int busdma_swi_pending;
+void busdma_swi(void);
+int minidumpsys(struct dumperinfo *);
+
+extern uint32_t initial_fpscr;
+
+#endif /* !_MACHINE_MD_VAR_H_ */
diff --git a/sys/arm/include/memdev.h b/sys/arm/include/memdev.h
new file mode 100644
index 000000000000..b9b2172d4647
--- /dev/null
+++ b/sys/arm/include/memdev.h
@@ -0,0 +1,42 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2004 Mark R V Murray
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_MEMDEV_H_
+#define _MACHINE_MEMDEV_H_
+
+#define CDEV_MINOR_MEM 0
+#define CDEV_MINOR_KMEM 1
+
+d_open_t memopen;
+d_read_t memrw;
+d_mmap_t memmmap;
+d_ioctl_t memioctl_md;
+
+#endif /* _MACHINE_MEMDEV_H_ */
diff --git a/sys/arm/include/metadata.h b/sys/arm/include/metadata.h
new file mode 100644
index 000000000000..587fd901b379
--- /dev/null
+++ b/sys/arm/include/metadata.h
@@ -0,0 +1,59 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2003 Peter Wemm <peter@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_METADATA_H_
+#define _MACHINE_METADATA_H_
+
+#define MODINFOMD_BOOTINFO 0x1001
+#define MODINFOMD_DTBP 0x1002
+#define MODINFOMD_EFI_MAP 0x1003
+
+struct efi_map_header {
+ uint64_t memory_size;
+ uint64_t descriptor_size;
+ uint32_t descriptor_version;
+};
+
+/*
+ * Placeholder for now
+ */
+struct efi_fb {
+ uint64_t fb_addr;
+ uint64_t fb_size;
+ uint32_t fb_height;
+ uint32_t fb_width;
+ uint32_t fb_stride;
+ uint32_t fb_mask_red;
+ uint32_t fb_mask_green;
+ uint32_t fb_mask_blue;
+ uint32_t fb_mask_reserved;
+};
+
+#endif /* !_MACHINE_METADATA_H_ */
diff --git a/sys/arm/include/minidump.h b/sys/arm/include/minidump.h
new file mode 100644
index 000000000000..d11e997d94dc
--- /dev/null
+++ b/sys/arm/include/minidump.h
@@ -0,0 +1,62 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2006 Peter Wemm
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * From: FreeBSD: src/sys/i386/include/minidump.h,v 1.1 2006/04/21 04:28:43
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_MINIDUMP_H_
+#define _MACHINE_MINIDUMP_H_
+
+#define MINIDUMP_MAGIC "minidump FreeBSD/arm"
+#define MINIDUMP_VERSION 2
+
+/*
+ * The first page of vmcore is dedicated to the following header.
+ * As the rest of the page is zeroed, any header extension can be
+ * done without version bumping. It should be taken into account
+ * only that new entries will be zero in old vmcores.
+ */
+
+struct minidumphdr {
+ char magic[24];
+ uint32_t version;
+ uint32_t msgbufsize;
+ uint32_t bitmapsize;
+ uint32_t ptesize;
+ uint32_t kernbase;
+ uint32_t arch;
+ uint32_t mmuformat;
+ uint32_t dumpavailsize;
+};
+
+#define MINIDUMP_MMU_FORMAT_UNKNOWN 0
+#define MINIDUMP_MMU_FORMAT_V4 1
+#define MINIDUMP_MMU_FORMAT_V6 2
+#define MINIDUMP_MMU_FORMAT_V6_LPAE 3
+
+#endif /* _MACHINE_MINIDUMP_H_ */
diff --git a/sys/arm/include/ofw_machdep.h b/sys/arm/include/ofw_machdep.h
new file mode 100644
index 000000000000..76ab31c963cf
--- /dev/null
+++ b/sys/arm/include/ofw_machdep.h
@@ -0,0 +1,49 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2009 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Semihalf under sponsorship from
+ * the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_OFW_MACHDEP_H_
+#define _MACHINE_OFW_MACHDEP_H_
+
+#include <sys/types.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <vm/vm.h>
+
+typedef uint32_t cell_t;
+
+struct mem_region {
+ uint64_t mr_start;
+ uint64_t mr_size;
+};
+
+#endif /* _MACHINE_OFW_MACHDEP_H_ */
diff --git a/sys/arm/include/param.h b/sys/arm/include/param.h
new file mode 100644
index 000000000000..807b492324fa
--- /dev/null
+++ b/sys/arm/include/param.h
@@ -0,0 +1,146 @@
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 2001 David E. O'Brien
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)param.h 5.8 (Berkeley) 6/28/91
+ * $FreeBSD$
+ */
+
+#ifndef _ARM_INCLUDE_PARAM_H_
+#define _ARM_INCLUDE_PARAM_H_
+
+/*
+ * Machine dependent constants for StrongARM
+ */
+
+#include <machine/_align.h>
+
+#define STACKALIGNBYTES (8 - 1)
+#define STACKALIGN(p) ((u_int)(p) & ~STACKALIGNBYTES)
+
+#define __PCI_REROUTE_INTERRUPT
+
+#ifndef MACHINE
+#define MACHINE "arm"
+#endif
+#ifndef MACHINE_ARCH
+#if __ARM_ARCH >= 7
+#define MACHINE_ARCH "armv7"
+#else
+#define MACHINE_ARCH "armv6"
+#endif
+#endif
+
+#ifdef SMP
+#ifndef MAXCPU
+#define MAXCPU 4
+#endif
+#else
+#define MAXCPU 1
+#endif
+
+#ifndef MAXMEMDOM
+#define MAXMEMDOM 1
+#endif
+
+#define ALIGNBYTES _ALIGNBYTES
+#define ALIGN(p) _ALIGN(p)
+/*
+ * ALIGNED_POINTER is a boolean macro that checks whether an address
+ * is valid to fetch data elements of type t from on this architecture.
+ * This does not reflect the optimal alignment, just the possibility
+ * (within reasonable limits).
+ *
+ * armv4 and v5 require alignment to the type's size. armv6 requires 8-byte
+ * alignment for the ldrd/strd instructions, but otherwise follows armv7 rules.
+ * armv7 requires that an 8-byte type be aligned to at least a 4-byte boundary;
+ * access to smaller types can be unaligned, except that the compiler may
+ * optimize access to adjacent uint32_t values into a single load/store-multiple
+ * instruction which requires 4-byte alignment, so we must provide the most-
+ * pessimistic answer possible even on armv7.
+ */
+#define ALIGNED_POINTER(p, t) ((((unsigned)(p)) & (sizeof(t)-1)) == 0)
+
+/*
+ * CACHE_LINE_SIZE is the compile-time maximum cache line size for an
+ * architecture. It should be used with appropriate caution.
+ */
+#define CACHE_LINE_SHIFT 6
+#define CACHE_LINE_SIZE (1 << CACHE_LINE_SHIFT)
+
+#define PAGE_SHIFT 12
+#define PAGE_SIZE (1 << PAGE_SHIFT) /* Page size */
+#define PAGE_MASK (PAGE_SIZE - 1)
+
+#define PDR_SHIFT 20 /* log2(NBPDR) */
+#define NBPDR (1 << PDR_SHIFT)
+#define PDRMASK (NBPDR - 1)
+#define NPDEPG (1 << (32 - PDR_SHIFT))
+
+#define MAXPAGESIZES 2 /* maximum number of supported page sizes */
+
+#ifndef KSTACK_PAGES
+#define KSTACK_PAGES 2
+#endif /* !KSTACK_PAGES */
+
+#ifndef FPCONTEXTSIZE
+#define FPCONTEXTSIZE (0x100)
+#endif
+
+#ifndef KSTACK_GUARD_PAGES
+#define KSTACK_GUARD_PAGES 1
+#endif /* !KSTACK_GUARD_PAGES */
+
+#define USPACE_SVC_STACK_TOP (kstack_pages * PAGE_SIZE)
+
+/*
+ * Mach derived conversion macros
+ */
+#define trunc_page(x) ((x) & ~PAGE_MASK)
+#define round_page(x) (((x) + PAGE_MASK) & ~PAGE_MASK)
+#define trunc_1mpage(x) ((unsigned)(x) & ~PDRMASK)
+#define round_1mpage(x) ((((unsigned)(x)) + PDRMASK) & ~PDRMASK)
+
+#define atop(x) ((unsigned)(x) >> PAGE_SHIFT)
+#define ptoa(x) ((unsigned)(x) << PAGE_SHIFT)
+
+#define arm32_btop(x) ((unsigned)(x) >> PAGE_SHIFT)
+#define arm32_ptob(x) ((unsigned)(x) << PAGE_SHIFT)
+
+#define pgtok(x) ((x) * (PAGE_SIZE / 1024))
+
+#endif /* !_ARM_INCLUDE_PARAM_H_ */
diff --git a/sys/arm/include/pcb.h b/sys/arm/include/pcb.h
new file mode 100644
index 000000000000..078a13c13796
--- /dev/null
+++ b/sys/arm/include/pcb.h
@@ -0,0 +1,89 @@
+/* $NetBSD: pcb.h,v 1.10 2003/10/13 21:46:39 scw Exp $ */
+
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 2001 Matt Thomas <matt@3am-software.com>.
+ * Copyright (c) 1994 Mark Brinicombe.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the RiscBSD team.
+ * 4. The name "RiscBSD" nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY RISCBSD ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL RISCBSD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_PCB_H_
+#define _MACHINE_PCB_H_
+
+#include <machine/frame.h>
+#include <machine/vfp.h>
+
+/*
+ * WARNING!
+ * Keep pcb_regs first for faster access in switch.S
+ */
+struct pcb {
+ struct switchframe pcb_regs; /* CPU state */
+ u_int pcb_flags;
+#define PCB_OWNFPU 0x00000001
+#define PCB_NOALIGNFLT 0x00000002
+ caddr_t pcb_onfault; /* On fault handler */
+ vm_offset_t pcb_pagedir; /* TTB0 value */
+ /*
+ * XXX:
+ * Variables pcb_pl1vec, pcb_l1vec, pcb_dacr are used solely
+ * by old PMAP. Keep them here for PCB binary compatibility
+ * between old and new PMAP.
+ */
+ uint32_t *pcb_pl1vec; /* PTR to vector_base L1 entry*/
+ uint32_t pcb_l1vec; /* Value to stuff on ctx sw */
+ u_int pcb_dacr; /* Domain Access Control Reg */
+
+ struct vfp_state pcb_vfpstate; /* VP/NEON state */
+ u_int pcb_vfpcpu; /* VP/NEON last cpu */
+} __aligned(8); /*
+ * We need the PCB to be aligned on 8 bytes, as we may
+ * access it using ldrd/strd, and ARM ABI require it
+ * to by aligned on 8 bytes.
+ */
+
+/*
+ * No additional data for core dumps.
+ */
+struct md_coredump {
+ int md_empty;
+};
+
+void makectx(struct trapframe *tf, struct pcb *pcb);
+
+#ifdef _KERNEL
+
+void savectx(struct pcb *) __returns_twice;
+#endif /* _KERNEL */
+
+#endif /* !_MACHINE_PCB_H_ */
diff --git a/sys/arm/include/pcpu.h b/sys/arm/include/pcpu.h
new file mode 100644
index 000000000000..4d609b10bf73
--- /dev/null
+++ b/sys/arm/include/pcpu.h
@@ -0,0 +1,146 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 1999 Luoqi Chen <luoqi@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: FreeBSD: src/sys/i386/include/globaldata.h,v 1.27 2001/04/27
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_PCPU_H_
+#define _MACHINE_PCPU_H_
+
+#ifdef _KERNEL
+
+#include <sys/_lock.h>
+#include <sys/_mutex.h>
+
+#define ALT_STACK_SIZE 128
+
+struct vmspace;
+
+#endif /* _KERNEL */
+
+/* Branch predictor hardening method */
+#define PCPU_BP_HARDEN_KIND_NONE 0
+#define PCPU_BP_HARDEN_KIND_BPIALL 1
+#define PCPU_BP_HARDEN_KIND_ICIALLU 2
+
+#define PCPU_MD_FIELDS \
+ unsigned int pc_vfpsid; \
+ unsigned int pc_vfpmvfr0; \
+ unsigned int pc_vfpmvfr1; \
+ struct pmap *pc_curpmap; \
+ struct mtx pc_cmap_lock; \
+ void *pc_cmap1_pte2p; \
+ void *pc_cmap2_pte2p; \
+ caddr_t pc_cmap1_addr; \
+ caddr_t pc_cmap2_addr; \
+ vm_offset_t pc_qmap_addr; \
+ void *pc_qmap_pte2p; \
+ unsigned int pc_dbreg[32]; \
+ int pc_dbreg_cmd; \
+ int pc_bp_harden_kind; \
+ uint32_t pc_original_actlr; \
+ uint64_t pc_clock; \
+ uint32_t pc_mpidr; \
+ char __pad[135]
+
+#ifdef _KERNEL
+
+#define PC_DBREG_CMD_NONE 0
+#define PC_DBREG_CMD_LOAD 1
+
+struct pcb;
+struct pcpu;
+
+extern struct pcpu *pcpup;
+
+#define CPU_MASK (0xf)
+
+#ifndef SMP
+#define get_pcpu() (pcpup)
+#else
+#define get_pcpu() __extension__ ({ \
+ int id; \
+ __asm __volatile("mrc p15, 0, %0, c0, c0, 5" : "=r" (id)); \
+ (pcpup + (id & CPU_MASK)); \
+ })
+#endif
+
+static inline struct thread *
+get_curthread(void)
+{
+ void *ret;
+
+ __asm __volatile("mrc p15, 0, %0, c13, c0, 4" : "=r" (ret));
+ return (ret);
+}
+
+static inline void
+set_curthread(struct thread *td)
+{
+
+ __asm __volatile("mcr p15, 0, %0, c13, c0, 4" : : "r" (td));
+}
+
+static inline void *
+get_tls(void)
+{
+ void *tls;
+
+ /* TPIDRURW contains the authoritative value. */
+ __asm __volatile("mrc p15, 0, %0, c13, c0, 2" : "=r" (tls));
+ return (tls);
+}
+
+static inline void
+set_tls(void *tls)
+{
+
+ /*
+ * Update both TPIDRURW and TPIDRURO. TPIDRURW needs to be written
+ * first to ensure that a context switch between the two writes will
+ * still give the desired result of updating both.
+ */
+ __asm __volatile(
+ "mcr p15, 0, %0, c13, c0, 2\n"
+ "mcr p15, 0, %0, c13, c0, 3\n"
+ : : "r" (tls));
+}
+
+#define curthread get_curthread()
+
+
+#define PCPU_GET(member) (get_pcpu()->pc_ ## member)
+#define PCPU_ADD(member, value) (get_pcpu()->pc_ ## member += (value))
+#define PCPU_INC(member) PCPU_ADD(member, 1)
+#define PCPU_PTR(member) (&get_pcpu()->pc_ ## member)
+#define PCPU_SET(member,value) (get_pcpu()->pc_ ## member = (value))
+
+void pcpu0_init(void);
+#endif /* _KERNEL */
+
+#endif /* !_MACHINE_PCPU_H_ */
diff --git a/sys/arm/include/pcpu_aux.h b/sys/arm/include/pcpu_aux.h
new file mode 100644
index 000000000000..3d4c70c491d6
--- /dev/null
+++ b/sys/arm/include/pcpu_aux.h
@@ -0,0 +1,52 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by Konstantin Belousov <kib@FreeBSD.org>
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_PCPU_AUX_H_
+#define _MACHINE_PCPU_AUX_H_
+
+#ifndef _KERNEL
+#error "Not for userspace"
+#endif
+
+#ifndef _SYS_PCPU_H_
+#error "Do not include machine/pcpu_aux.h directly"
+#endif
+
+/*
+ * To minimize memory waste in per-cpu UMA zones, the page size should
+ * be a multiple of the size of struct pcpu.
+ */
+_Static_assert(PAGE_SIZE % sizeof(struct pcpu) == 0, "fix pcpu size");
+
+extern struct pcpu __pcpu[];
+
+#endif /* _MACHINE_PCPU_AUX_H_ */
diff --git a/sys/arm/include/pl310.h b/sys/arm/include/pl310.h
new file mode 100644
index 000000000000..39e8434097a7
--- /dev/null
+++ b/sys/arm/include/pl310.h
@@ -0,0 +1,191 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Olivier Houchard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * $FreeBSD$
+ */
+
+#ifndef PL310_H_
+#define PL310_H_
+
+/**
+ * PL310 - L2 Cache Controller register offsets.
+ *
+ */
+#define PL310_CACHE_ID 0x000
+#define CACHE_ID_RELEASE_SHIFT 0
+#define CACHE_ID_RELEASE_MASK 0x3f
+#define CACHE_ID_RELEASE_r0p0 0x00
+#define CACHE_ID_RELEASE_r1p0 0x02
+#define CACHE_ID_RELEASE_r2p0 0x04
+#define CACHE_ID_RELEASE_r3p0 0x05
+#define CACHE_ID_RELEASE_r3p1 0x06
+#define CACHE_ID_RELEASE_r3p2 0x08
+#define CACHE_ID_RELEASE_r3p3 0x09
+#define CACHE_ID_PARTNUM_SHIFT 6
+#define CACHE_ID_PARTNUM_MASK 0xf
+#define CACHE_ID_PARTNUM_VALUE 0x3
+#define PL310_CACHE_TYPE 0x004
+#define PL310_CTRL 0x100
+#define CTRL_ENABLED 0x01
+#define CTRL_DISABLED 0x00
+#define PL310_AUX_CTRL 0x104
+#define AUX_CTRL_MASK 0xc0000fff
+#define AUX_CTRL_ASSOCIATIVITY_SHIFT 16
+#define AUX_CTRL_WAY_SIZE_SHIFT 17
+#define AUX_CTRL_WAY_SIZE_MASK (0x7 << 17)
+#define AUX_CTRL_SHARE_OVERRIDE (1 << 22)
+#define AUX_CTRL_NS_LOCKDOWN (1 << 26)
+#define AUX_CTRL_NS_INT_CTRL (1 << 27)
+#define AUX_CTRL_DATA_PREFETCH (1 << 28)
+#define AUX_CTRL_INSTR_PREFETCH (1 << 29)
+#define AUX_CTRL_EARLY_BRESP (1 << 30)
+#define PL310_TAG_RAM_CTRL 0x108
+#define PL310_DATA_RAM_CTRL 0x10C
+#define RAM_CTRL_WRITE_SHIFT 8
+#define RAM_CTRL_WRITE_MASK (0x7 << 8)
+#define RAM_CTRL_READ_SHIFT 4
+#define RAM_CTRL_READ_MASK (0x7 << 4)
+#define RAM_CTRL_SETUP_SHIFT 0
+#define RAM_CTRL_SETUP_MASK (0x7 << 0)
+#define PL310_EVENT_COUNTER_CTRL 0x200
+#define EVENT_COUNTER_CTRL_ENABLED (1 << 0)
+#define EVENT_COUNTER_CTRL_C0_RESET (1 << 1)
+#define EVENT_COUNTER_CTRL_C1_RESET (1 << 2)
+#define PL310_EVENT_COUNTER1_CONF 0x204
+#define PL310_EVENT_COUNTER0_CONF 0x208
+#define EVENT_COUNTER_CONF_NOINTR 0
+#define EVENT_COUNTER_CONF_INCR 1
+#define EVENT_COUNTER_CONF_OVFW 2
+#define EVENT_COUNTER_CONF_NOEV (0 << 2)
+#define EVENT_COUNTER_CONF_CO (1 << 2)
+#define EVENT_COUNTER_CONF_DRHIT (2 << 2)
+#define EVENT_COUNTER_CONF_DRREQ (3 << 2)
+#define EVENT_COUNTER_CONF_DWHIT (4 << 2)
+#define EVENT_COUNTER_CONF_DWREQ (5 << 2)
+#define EVENT_COUNTER_CONF_DWTREQ (6 << 2)
+#define EVENT_COUNTER_CONF_DIRHIT (7 << 2)
+#define EVENT_COUNTER_CONF_DIRREQ (8 << 2)
+#define EVENT_COUNTER_CONF_WA (9 << 2)
+#define PL310_EVENT_COUNTER1_VAL 0x20C
+#define PL310_EVENT_COUNTER0_VAL 0x210
+#define PL310_INTR_MASK 0x214
+#define PL310_MASKED_INTR_STAT 0x218
+#define PL310_RAW_INTR_STAT 0x21C
+#define PL310_INTR_CLEAR 0x220
+#define INTR_MASK_ALL ((1 << 9) - 1)
+#define INTR_MASK_ECNTR (1 << 0)
+#define INTR_MASK_PARRT (1 << 1)
+#define INTR_MASK_PARRD (1 << 2)
+#define INTR_MASK_ERRWT (1 << 3)
+#define INTR_MASK_ERRWD (1 << 4)
+#define INTR_MASK_ERRRT (1 << 5)
+#define INTR_MASK_ERRRD (1 << 6)
+#define INTR_MASK_SLVERR (1 << 7)
+#define INTR_MASK_DECERR (1 << 8)
+#define PL310_CACHE_SYNC 0x730
+#define PL310_INV_LINE_PA 0x770
+#define PL310_INV_WAY 0x77C
+#define PL310_CLEAN_LINE_PA 0x7B0
+#define PL310_CLEAN_LINE_IDX 0x7B8
+#define PL310_CLEAN_WAY 0x7BC
+#define PL310_CLEAN_INV_LINE_PA 0x7F0
+#define PL310_CLEAN_INV_LINE_IDX 0x7F8
+#define PL310_CLEAN_INV_WAY 0x7FC
+#define PL310_LOCKDOWN_D_WAY(x) (0x900 + ((x) * 8))
+#define PL310_LOCKDOWN_I_WAY(x) (0x904 + ((x) * 8))
+#define PL310_LOCKDOWN_LINE_ENABLE 0x950
+#define PL310_UNLOCK_ALL_LINES_WAY 0x954
+#define PL310_ADDR_FILTER_STAR 0xC00
+#define PL310_ADDR_FILTER_END 0xC04
+#define PL310_DEBUG_CTRL 0xF40
+#define DEBUG_CTRL_DISABLE_LINEFILL (1 << 0)
+#define DEBUG_CTRL_DISABLE_WRITEBACK (1 << 1)
+#define DEBUG_CTRL_SPNIDEN (1 << 2)
+#define PL310_PREFETCH_CTRL 0xF60
+#define PREFETCH_CTRL_OFFSET_MASK (0x1f)
+#define PREFETCH_CTRL_NOTSAMEID (1 << 21)
+#define PREFETCH_CTRL_INCR_DL (1 << 23)
+#define PREFETCH_CTRL_PREFETCH_DROP (1 << 24)
+#define PREFETCH_CTRL_DL_ON_WRAP (1 << 27)
+#define PREFETCH_CTRL_DATA_PREFETCH (1 << 28)
+#define PREFETCH_CTRL_INSTR_PREFETCH (1 << 29)
+#define PREFETCH_CTRL_DL (1 << 30)
+#define PL310_POWER_CTRL 0xF80
+#define POWER_CTRL_ENABLE_GATING (1 << 1)
+#define POWER_CTRL_ENABLE_STANDBY (1 << 0)
+
+struct intr_config_hook;
+
+struct pl310_softc {
+ device_t sc_dev;
+ struct resource *sc_mem_res;
+ struct resource *sc_irq_res;
+ void* sc_irq_h;
+ int sc_enabled;
+ struct mtx sc_mtx;
+ u_int sc_rtl_revision;
+ struct intr_config_hook *sc_ich;
+ boolean_t sc_io_coherent;
+};
+
+/**
+ * pl310_read4 - read a 32-bit value from the PL310 registers
+ * pl310_write4 - write a 32-bit value from the PL310 registers
+ * @off: byte offset within the register set to read from
+ * @val: the value to write into the register
+ *
+ *
+ * LOCKING:
+ * None
+ *
+ * RETURNS:
+ * nothing in case of write function, if read function returns the value read.
+ */
+static __inline uint32_t
+pl310_read4(struct pl310_softc *sc, bus_size_t off)
+{
+
+ return bus_read_4(sc->sc_mem_res, off);
+}
+
+static __inline void
+pl310_write4(struct pl310_softc *sc, bus_size_t off, uint32_t val)
+{
+
+ bus_write_4(sc->sc_mem_res, off, val);
+}
+
+void pl310_set_ram_latency(struct pl310_softc *sc, uint32_t which_reg,
+ uint32_t read, uint32_t write, uint32_t setup);
+
+#ifndef PLATFORM
+void platform_pl310_init(struct pl310_softc *);
+void platform_pl310_write_ctrl(struct pl310_softc *, uint32_t);
+void platform_pl310_write_debug(struct pl310_softc *, uint32_t);
+#endif
+
+#endif /* PL310_H_ */
diff --git a/sys/arm/include/platform.h b/sys/arm/include/platform.h
new file mode 100644
index 000000000000..5632003bcc27
--- /dev/null
+++ b/sys/arm/include/platform.h
@@ -0,0 +1,66 @@
+/*-
+ * Copyright (c) 2014 Andrew Turner
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_PLATFORM_H_
+#define _MACHINE_PLATFORM_H_
+
+/*
+ * Initialization functions called by the common initarm() function in
+ * arm/machdep.c (but not necessarily from the custom initarm() functions of
+ * older code).
+ *
+ * - platform_probe_and_attach() is called very early, after parsing the boot
+ * params and after physical memory has been located and sized.
+ *
+ * - platform_devmap_init() is called as one of the last steps of early virtual
+ * memory initialization, shortly before the new page tables are installed.
+ *
+ * - platform_lastaddr() is called after platform_devmap_init(), and must return
+ * the address of the first byte of unusable KVA space. This allows a
+ * platform to carve out of the top of the KVA space whatever reserves it
+ * needs for things like static device mapping, and this is called to get the
+ * value before calling pmap_bootstrap() which uses the value to size the
+ * available KVA.
+ *
+ * - platform_gpio_init() is called after the static device mappings are
+ * established and just before cninit(). The intention is that the routine
+ * can do any hardware setup (such as gpio or pinmux) necessary to make the
+ * console functional.
+ *
+ * - platform_late_init() is called just after cninit(). This is the first of
+ * the init routines that can use printf() and expect the output to appear on
+ * a standard console.
+ *
+ */
+void platform_probe_and_attach(void);
+int platform_devmap_init(void);
+vm_offset_t platform_lastaddr(void);
+void platform_gpio_init(void);
+void platform_late_init(void);
+
+#endif /* _MACHINE_PLATFORM_H_ */
diff --git a/sys/arm/include/platformvar.h b/sys/arm/include/platformvar.h
new file mode 100644
index 000000000000..4fbe658d69ae
--- /dev/null
+++ b/sys/arm/include/platformvar.h
@@ -0,0 +1,124 @@
+/*-
+ * Copyright (c) 2005 Peter Grehan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_PLATFORMVAR_H_
+#define _MACHINE_PLATFORMVAR_H_
+
+/*
+ * An ARM platform implementation is declared with a kernel object and
+ * an associated method table, similar to a device driver.
+ *
+ * e.g.
+ *
+ * static platform_method_t bcm2835_methods[] = {
+ * PLATFORMMETHOD(platform_probe, bcm2835_probe),
+ * ...
+ * PLATFORMMETHOD_END
+ * };
+ *
+ * static platform_def_t bcm3835_platform = {
+ * "bcm2835",
+ * bcm2835_methods,
+ * sizeof(bcm2835_platform_softc), // or 0 if no softc
+ * };
+ *
+ * PLATFORM_DEF(bcm2835_platform);
+ */
+
+#include <sys/kobj.h>
+#include <sys/linker_set.h>
+
+struct platform_class {
+ KOBJ_CLASS_FIELDS;
+
+ /* How many times to loop to delay approximately 1us */
+ int delay_count;
+};
+
+struct platform_kobj {
+ /*
+ * A platform instance is a kernel object
+ */
+ KOBJ_FIELDS;
+
+ /* Platform class, for access to class specific data */
+ struct platform_class *cls;
+};
+
+typedef struct platform_kobj *platform_t;
+typedef struct platform_class platform_def_t;
+#define platform_method_t kobj_method_t
+
+#define PLATFORMMETHOD KOBJMETHOD
+#define PLATFORMMETHOD_END KOBJMETHOD_END
+
+#define PLATFORM_DEF(name) DATA_SET(platform_set, name)
+
+#ifdef FDT
+struct fdt_platform_class {
+ KOBJ_CLASS_FIELDS;
+
+ const char *fdt_compatible;
+};
+
+typedef struct fdt_platform_class fdt_platform_def_t;
+
+extern platform_method_t fdt_platform_methods[];
+
+#define FDT_PLATFORM_DEF2(NAME, VAR_NAME, NAME_STR, _size, _compatible, \
+ _delay) \
+CTASSERT(_delay > 0); \
+static fdt_platform_def_t VAR_NAME ## _fdt_platform = { \
+ .name = NAME_STR, \
+ .methods = fdt_platform_methods, \
+ .fdt_compatible = _compatible, \
+}; \
+static kobj_class_t VAR_NAME ## _baseclasses[] = \
+ { (kobj_class_t)&VAR_NAME ## _fdt_platform, NULL }; \
+static platform_def_t VAR_NAME ## _platform = { \
+ .name = NAME_STR, \
+ .methods = NAME ## _methods, \
+ .size = _size, \
+ .baseclasses = VAR_NAME ## _baseclasses, \
+ .delay_count = _delay, \
+}; \
+DATA_SET(platform_set, VAR_NAME ## _platform)
+
+#define FDT_PLATFORM_DEF(NAME, NAME_STR, size, compatible, delay) \
+ FDT_PLATFORM_DEF2(NAME, NAME, NAME_STR, size, compatible, delay)
+
+#endif
+
+/*
+ * Helper to get the platform object
+ */
+platform_t platform_obj(void);
+
+bool arm_tmr_timed_wait(platform_t, int);
+
+#endif /* _MACHINE_PLATFORMVAR_H_ */
diff --git a/sys/arm/include/pmap-v6.h b/sys/arm/include/pmap-v6.h
new file mode 100644
index 000000000000..aa596aa699c6
--- /dev/null
+++ b/sys/arm/include/pmap-v6.h
@@ -0,0 +1,194 @@
+/*-
+ * Copyright 2014 Svatopluk Kraus <onwahe@gmail.com>
+ * Copyright 2014 Michal Meloun <meloun@miracle.cz>
+ * Copyright (c) 1991 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Systems Programming Group of the University of Utah Computer
+ * Science Department and William Jolitz of UUNET Technologies Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The ARM version of this file was more or less based on the i386 version,
+ * which has the following provenance...
+ *
+ * Derived from hp300 version by Mike Hibler, this version by William
+ * Jolitz uses a recursive map [a pde points to the page directory] to
+ * map the page tables using the pagetables themselves. This is done to
+ * reduce the impact on kernel virtual memory for lots of sparse address
+ * space, and to reduce the cost of memory to each process.
+ *
+ * from: hp300: @(#)pmap.h 7.2 (Berkeley) 12/16/90
+ * from: @(#)pmap.h 7.4 (Berkeley) 5/12/91
+ * from: FreeBSD: src/sys/i386/include/pmap.h,v 1.70 2000/11/30
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_PMAP_V6_H_
+#define _MACHINE_PMAP_V6_H_
+
+#include <sys/queue.h>
+#include <sys/_cpuset.h>
+#include <sys/_lock.h>
+#include <sys/_mutex.h>
+
+typedef uint32_t pt1_entry_t; /* L1 table entry */
+typedef uint32_t pt2_entry_t; /* L2 table entry */
+typedef uint32_t ttb_entry_t; /* TTB entry */
+
+#ifdef _KERNEL
+
+#if 0
+#define PMAP_PTE_NOCACHE // Use uncached page tables
+#endif
+
+/*
+ * (1) During pmap bootstrap, physical pages for L2 page tables are
+ * allocated in advance which are used for KVA continuous mapping
+ * starting from KERNBASE. This makes things more simple.
+ * (2) During vm subsystem initialization, only vm subsystem itself can
+ * allocate physical memory safely. As pmap_map() is called during
+ * this initialization, we must be prepared for that and have some
+ * preallocated physical pages for L2 page tables.
+ *
+ * Note that some more pages for L2 page tables are preallocated too
+ * for mappings laying above VM_MAX_KERNEL_ADDRESS.
+ */
+#ifndef NKPT2PG
+/*
+ * The optimal way is to define this in board configuration as
+ * definition here must be safe enough. It means really big.
+ *
+ * 1 GB KVA <=> 256 kernel L2 page table pages
+ *
+ * From real platforms:
+ * 1 GB physical memory <=> 10 pages is enough
+ * 2 GB physical memory <=> 21 pages is enough
+ */
+#define NKPT2PG 32
+#endif
+#endif /* _KERNEL */
+
+/*
+ * Pmap stuff
+ */
+struct pv_entry;
+struct pv_chunk;
+
+struct md_page {
+ TAILQ_HEAD(,pv_entry) pv_list;
+ uint16_t pt2_wirecount[4];
+ vm_memattr_t pat_mode;
+};
+
+struct pmap {
+ struct mtx pm_mtx;
+ pt1_entry_t *pm_pt1; /* KVA of pt1 */
+ pt2_entry_t *pm_pt2tab; /* KVA of pt2 pages table */
+ TAILQ_HEAD(,pv_chunk) pm_pvchunk; /* list of mappings in pmap */
+ cpuset_t pm_active; /* active on cpus */
+ struct pmap_statistics pm_stats; /* pmap statictics */
+ LIST_ENTRY(pmap) pm_list; /* List of all pmaps */
+};
+
+typedef struct pmap *pmap_t;
+
+#ifdef _KERNEL
+extern struct pmap kernel_pmap_store;
+#define kernel_pmap (&kernel_pmap_store)
+
+#define PMAP_LOCK(pmap) mtx_lock(&(pmap)->pm_mtx)
+#define PMAP_LOCK_ASSERT(pmap, type) \
+ mtx_assert(&(pmap)->pm_mtx, (type))
+#define PMAP_LOCK_DESTROY(pmap) mtx_destroy(&(pmap)->pm_mtx)
+#define PMAP_LOCK_INIT(pmap) mtx_init(&(pmap)->pm_mtx, "pmap", \
+ NULL, MTX_DEF | MTX_DUPOK)
+#define PMAP_LOCKED(pmap) mtx_owned(&(pmap)->pm_mtx)
+#define PMAP_MTX(pmap) (&(pmap)->pm_mtx)
+#define PMAP_TRYLOCK(pmap) mtx_trylock(&(pmap)->pm_mtx)
+#define PMAP_UNLOCK(pmap) mtx_unlock(&(pmap)->pm_mtx)
+#endif
+
+/*
+ * For each vm_page_t, there is a list of all currently valid virtual
+ * mappings of that page. An entry is a pv_entry_t, the list is pv_list.
+ */
+typedef struct pv_entry {
+ vm_offset_t pv_va; /* virtual address for mapping */
+ TAILQ_ENTRY(pv_entry) pv_next;
+} *pv_entry_t;
+
+/*
+ * pv_entries are allocated in chunks per-process. This avoids the
+ * need to track per-pmap assignments.
+ */
+#define _NPCM 11
+#define _NPCPV 336
+struct pv_chunk {
+ pmap_t pc_pmap;
+ TAILQ_ENTRY(pv_chunk) pc_list;
+ uint32_t pc_map[_NPCM]; /* bitmap; 1 = free */
+ TAILQ_ENTRY(pv_chunk) pc_lru;
+ struct pv_entry pc_pventry[_NPCPV];
+};
+
+#ifdef _KERNEL
+extern ttb_entry_t pmap_kern_ttb; /* TTB for kernel pmap */
+
+#define pmap_page_get_memattr(m) ((m)->md.pat_mode)
+
+/*
+ * Only the following functions or macros may be used before pmap_bootstrap()
+ * is called: pmap_kenter(), pmap_kextract(), pmap_kremove(), vtophys(), and
+ * vtopte2().
+ */
+void pmap_bootstrap(vm_offset_t);
+void pmap_kenter(vm_offset_t, vm_paddr_t);
+void pmap_kremove(vm_offset_t);
+boolean_t pmap_page_is_mapped(vm_page_t);
+bool pmap_ps_enabled(pmap_t pmap);
+
+void pmap_tlb_flush(pmap_t, vm_offset_t);
+void pmap_tlb_flush_range(pmap_t, vm_offset_t, vm_size_t);
+
+vm_paddr_t pmap_dump_kextract(vm_offset_t, pt2_entry_t *);
+
+int pmap_fault(pmap_t, vm_offset_t, uint32_t, int, bool);
+
+void pmap_set_tex(void);
+
+/*
+ * Pre-bootstrap epoch functions set.
+ */
+void pmap_bootstrap_prepare(vm_paddr_t);
+vm_paddr_t pmap_preboot_get_pages(u_int);
+void pmap_preboot_map_pages(vm_paddr_t, vm_offset_t, u_int);
+vm_offset_t pmap_preboot_reserve_pages(u_int);
+vm_offset_t pmap_preboot_get_vpages(u_int);
+void pmap_preboot_map_attr(vm_paddr_t, vm_offset_t, vm_size_t, vm_prot_t,
+ vm_memattr_t);
+void pmap_remap_vm_attr(vm_memattr_t old_attr, vm_memattr_t new_attr);
+
+#endif /* _KERNEL */
+#endif /* !_MACHINE_PMAP_V6_H_ */
diff --git a/sys/arm/include/pmap.h b/sys/arm/include/pmap.h
new file mode 100644
index 000000000000..2f407a9c8760
--- /dev/null
+++ b/sys/arm/include/pmap.h
@@ -0,0 +1,77 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2016 Svatopluk Kraus
+ * Copyright (c) 2016 Michal Meloun
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_PMAP_H_
+#define _MACHINE_PMAP_H_
+
+#include <machine/pmap-v6.h>
+
+#ifdef _KERNEL
+#include <sys/systm.h>
+
+extern char *_tmppt; /* poor name! */
+
+extern vm_offset_t virtual_avail;
+extern vm_offset_t virtual_end;
+
+void *pmap_kenter_temporary(vm_paddr_t, int);
+#define pmap_page_is_write_mapped(m) (((m)->a.flags & PGA_WRITEABLE) != 0)
+void pmap_page_set_memattr(vm_page_t, vm_memattr_t);
+
+void *pmap_mapdev(vm_paddr_t, vm_size_t);
+void pmap_unmapdev(vm_offset_t, vm_size_t);
+
+static inline void *
+pmap_mapdev_attr(vm_paddr_t addr, vm_size_t size, int attr)
+{
+ panic("%s is not implemented yet!\n", __func__);
+}
+
+struct pcb;
+void pmap_set_pcb_pagedir(pmap_t, struct pcb *);
+
+void pmap_kenter_device(vm_offset_t, vm_size_t, vm_paddr_t);
+void pmap_kremove_device(vm_offset_t, vm_size_t);
+
+vm_paddr_t pmap_kextract(vm_offset_t);
+#define vtophys(va) pmap_kextract((vm_offset_t)(va))
+
+static inline int
+pmap_vmspace_copy(pmap_t dst_pmap __unused, pmap_t src_pmap __unused)
+{
+
+ return (0);
+}
+
+#define PMAP_ENTER_QUICK_LOCKED 0x10000000
+
+#endif /* _KERNEL */
+#endif /* !_MACHINE_PMAP_H_ */
diff --git a/sys/arm/include/pmap_var.h b/sys/arm/include/pmap_var.h
new file mode 100644
index 000000000000..34c101df5c4d
--- /dev/null
+++ b/sys/arm/include/pmap_var.h
@@ -0,0 +1,494 @@
+/*-
+ * Copyright 2014 Svatopluk Kraus <onwahe@gmail.com>
+ * Copyright 2014 Michal Meloun <meloun@miracle.cz>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_PMAP_VAR_H_
+#define _MACHINE_PMAP_VAR_H_
+
+#include <machine/cpu-v6.h>
+#include <machine/pte-v6.h>
+/*
+ * Various PMAP defines, exports, and inline functions
+ * definitions also usable in other MD code.
+ */
+
+/* A number of pages in L1 page table. */
+#define NPG_IN_PT1 (NB_IN_PT1 / PAGE_SIZE)
+
+/* A number of L2 page tables in a page. */
+#define NPT2_IN_PG (PAGE_SIZE / NB_IN_PT2)
+
+/* A number of L2 page table entries in a page. */
+#define NPTE2_IN_PG (NPT2_IN_PG * NPTE2_IN_PT2)
+
+#ifdef _KERNEL
+
+/*
+ * A L2 page tables page contains NPT2_IN_PG L2 page tables. Masking of
+ * pte1_idx by PT2PG_MASK gives us an index to associated L2 page table
+ * in a page. The PT2PG_SHIFT definition depends on NPT2_IN_PG strictly.
+ * I.e., (1 << PT2PG_SHIFT) == NPT2_IN_PG must be fulfilled.
+ */
+#define PT2PG_SHIFT 2
+#define PT2PG_MASK ((1 << PT2PG_SHIFT) - 1)
+
+/*
+ * A PT2TAB holds all allocated L2 page table pages in a pmap.
+ * Right shifting of virtual address by PT2TAB_SHIFT gives us an index
+ * to L2 page table page in PT2TAB which holds the address mapping.
+ */
+#define PT2TAB_ENTRIES (NPTE1_IN_PT1 / NPT2_IN_PG)
+#define PT2TAB_SHIFT (PTE1_SHIFT + PT2PG_SHIFT)
+
+/*
+ * All allocated L2 page table pages in a pmap are mapped into PT2MAP space.
+ * An virtual address right shifting by PT2MAP_SHIFT gives us an index to PTE2
+ * which maps the address.
+ */
+#define PT2MAP_SIZE (NPTE1_IN_PT1 * NB_IN_PT2)
+#define PT2MAP_SHIFT PTE2_SHIFT
+
+extern pt1_entry_t *kern_pt1;
+extern pt2_entry_t *kern_pt2tab;
+extern pt2_entry_t *PT2MAP;
+
+/*
+ * Virtual interface for L1 page table management.
+ */
+
+static __inline u_int
+pte1_index(vm_offset_t va)
+{
+
+ return (va >> PTE1_SHIFT);
+}
+
+static __inline pt1_entry_t *
+pte1_ptr(pt1_entry_t *pt1, vm_offset_t va)
+{
+
+ return (pt1 + pte1_index(va));
+}
+
+static __inline vm_offset_t
+pte1_trunc(vm_offset_t va)
+{
+
+ return (va & PTE1_FRAME);
+}
+
+static __inline vm_offset_t
+pte1_roundup(vm_offset_t va)
+{
+
+ return ((va + PTE1_OFFSET) & PTE1_FRAME);
+}
+
+/*
+ * Virtual interface for L1 page table entries management.
+ *
+ * XXX: Some of the following functions now with a synchronization barrier
+ * are called in a loop, so it could be useful to have two versions of them.
+ * One with the barrier and one without the barrier. In this case, pure
+ * barrier pte1_sync() should be implemented as well.
+ */
+static __inline void
+pte1_sync(pt1_entry_t *pte1p)
+{
+
+ dsb();
+#ifndef PMAP_PTE_NOCACHE
+ if (!cpuinfo.coherent_walk)
+ dcache_wb_pou((vm_offset_t)pte1p, sizeof(*pte1p));
+#endif
+}
+
+static __inline void
+pte1_sync_range(pt1_entry_t *pte1p, vm_size_t size)
+{
+
+ dsb();
+#ifndef PMAP_PTE_NOCACHE
+ if (!cpuinfo.coherent_walk)
+ dcache_wb_pou((vm_offset_t)pte1p, size);
+#endif
+}
+
+static __inline void
+pte1_store(pt1_entry_t *pte1p, pt1_entry_t pte1)
+{
+
+ dmb();
+ *pte1p = pte1;
+ pte1_sync(pte1p);
+}
+
+static __inline void
+pte1_clear(pt1_entry_t *pte1p)
+{
+
+ pte1_store(pte1p, 0);
+}
+
+static __inline void
+pte1_clear_bit(pt1_entry_t *pte1p, uint32_t bit)
+{
+
+ *pte1p &= ~bit;
+ pte1_sync(pte1p);
+}
+
+static __inline boolean_t
+pte1_is_link(pt1_entry_t pte1)
+{
+
+ return ((pte1 & L1_TYPE_MASK) == L1_TYPE_C);
+}
+
+static __inline int
+pte1_is_section(pt1_entry_t pte1)
+{
+
+ return ((pte1 & L1_TYPE_MASK) == L1_TYPE_S);
+}
+
+static __inline boolean_t
+pte1_is_dirty(pt1_entry_t pte1)
+{
+
+ return ((pte1 & (PTE1_NM | PTE1_RO)) == 0);
+}
+
+static __inline boolean_t
+pte1_is_global(pt1_entry_t pte1)
+{
+
+ return ((pte1 & PTE1_NG) == 0);
+}
+
+static __inline boolean_t
+pte1_is_valid(pt1_entry_t pte1)
+{
+ int l1_type;
+
+ l1_type = pte1 & L1_TYPE_MASK;
+ return ((l1_type == L1_TYPE_C) || (l1_type == L1_TYPE_S));
+}
+
+static __inline boolean_t
+pte1_is_wired(pt1_entry_t pte1)
+{
+
+ return (pte1 & PTE1_W);
+}
+
+static __inline pt1_entry_t
+pte1_load(pt1_entry_t *pte1p)
+{
+ pt1_entry_t pte1;
+
+ pte1 = *pte1p;
+ return (pte1);
+}
+
+static __inline pt1_entry_t
+pte1_load_clear(pt1_entry_t *pte1p)
+{
+ pt1_entry_t opte1;
+
+ opte1 = *pte1p;
+ *pte1p = 0;
+ pte1_sync(pte1p);
+ return (opte1);
+}
+
+static __inline void
+pte1_set_bit(pt1_entry_t *pte1p, uint32_t bit)
+{
+
+ *pte1p |= bit;
+ pte1_sync(pte1p);
+}
+
+static __inline vm_paddr_t
+pte1_pa(pt1_entry_t pte1)
+{
+
+ return ((vm_paddr_t)(pte1 & PTE1_FRAME));
+}
+
+static __inline vm_paddr_t
+pte1_link_pa(pt1_entry_t pte1)
+{
+
+ return ((vm_paddr_t)(pte1 & L1_C_ADDR_MASK));
+}
+
+/*
+ * Virtual interface for L2 page table entries management.
+ *
+ * XXX: Some of the following functions now with a synchronization barrier
+ * are called in a loop, so it could be useful to have two versions of them.
+ * One with the barrier and one without the barrier.
+ */
+
+static __inline void
+pte2_sync(pt2_entry_t *pte2p)
+{
+
+ dsb();
+#ifndef PMAP_PTE_NOCACHE
+ if (!cpuinfo.coherent_walk)
+ dcache_wb_pou((vm_offset_t)pte2p, sizeof(*pte2p));
+#endif
+}
+
+static __inline void
+pte2_sync_range(pt2_entry_t *pte2p, vm_size_t size)
+{
+
+ dsb();
+#ifndef PMAP_PTE_NOCACHE
+ if (!cpuinfo.coherent_walk)
+ dcache_wb_pou((vm_offset_t)pte2p, size);
+#endif
+}
+
+static __inline void
+pte2_store(pt2_entry_t *pte2p, pt2_entry_t pte2)
+{
+
+ dmb();
+ *pte2p = pte2;
+ pte2_sync(pte2p);
+}
+
+static __inline void
+pte2_clear(pt2_entry_t *pte2p)
+{
+
+ pte2_store(pte2p, 0);
+}
+
+static __inline void
+pte2_clear_bit(pt2_entry_t *pte2p, uint32_t bit)
+{
+
+ *pte2p &= ~bit;
+ pte2_sync(pte2p);
+}
+
+static __inline boolean_t
+pte2_is_dirty(pt2_entry_t pte2)
+{
+
+ return ((pte2 & (PTE2_NM | PTE2_RO)) == 0);
+}
+
+static __inline boolean_t
+pte2_is_global(pt2_entry_t pte2)
+{
+
+ return ((pte2 & PTE2_NG) == 0);
+}
+
+static __inline boolean_t
+pte2_is_valid(pt2_entry_t pte2)
+{
+
+ return (pte2 & PTE2_V);
+}
+
+static __inline boolean_t
+pte2_is_wired(pt2_entry_t pte2)
+{
+
+ return (pte2 & PTE2_W);
+}
+
+static __inline pt2_entry_t
+pte2_load(pt2_entry_t *pte2p)
+{
+ pt2_entry_t pte2;
+
+ pte2 = *pte2p;
+ return (pte2);
+}
+
+static __inline pt2_entry_t
+pte2_load_clear(pt2_entry_t *pte2p)
+{
+ pt2_entry_t opte2;
+
+ opte2 = *pte2p;
+ *pte2p = 0;
+ pte2_sync(pte2p);
+ return (opte2);
+}
+
+static __inline void
+pte2_set_bit(pt2_entry_t *pte2p, uint32_t bit)
+{
+
+ *pte2p |= bit;
+ pte2_sync(pte2p);
+}
+
+static __inline void
+pte2_set_wired(pt2_entry_t *pte2p, boolean_t wired)
+{
+
+ /*
+ * Wired bit is transparent for page table walk,
+ * so pte2_sync() is not needed.
+ */
+ if (wired)
+ *pte2p |= PTE2_W;
+ else
+ *pte2p &= ~PTE2_W;
+}
+
+static __inline vm_paddr_t
+pte2_pa(pt2_entry_t pte2)
+{
+
+ return ((vm_paddr_t)(pte2 & PTE2_FRAME));
+}
+
+static __inline u_int
+pte2_attr(pt2_entry_t pte2)
+{
+
+ return ((u_int)(pte2 & PTE2_ATTR_MASK));
+}
+
+/*
+ * Virtual interface for L2 page tables mapping management.
+ */
+
+static __inline u_int
+pt2tab_index(vm_offset_t va)
+{
+
+ return (va >> PT2TAB_SHIFT);
+}
+
+static __inline pt2_entry_t *
+pt2tab_entry(pt2_entry_t *pt2tab, vm_offset_t va)
+{
+
+ return (pt2tab + pt2tab_index(va));
+}
+
+static __inline void
+pt2tab_store(pt2_entry_t *pte2p, pt2_entry_t pte2)
+{
+
+ pte2_store(pte2p,pte2);
+}
+
+static __inline pt2_entry_t
+pt2tab_load(pt2_entry_t *pte2p)
+{
+
+ return (pte2_load(pte2p));
+}
+
+static __inline pt2_entry_t
+pt2tab_load_clear(pt2_entry_t *pte2p)
+{
+
+ return (pte2_load_clear(pte2p));
+}
+
+static __inline u_int
+pt2map_index(vm_offset_t va)
+{
+
+ return (va >> PT2MAP_SHIFT);
+}
+
+static __inline pt2_entry_t *
+pt2map_entry(vm_offset_t va)
+{
+
+ return (PT2MAP + pt2map_index(va));
+}
+
+/*
+ * Virtual interface for pmap structure & kernel shortcuts.
+ */
+
+static __inline pt1_entry_t *
+pmap_pte1(pmap_t pmap, vm_offset_t va)
+{
+
+ return (pte1_ptr(pmap->pm_pt1, va));
+}
+
+static __inline pt1_entry_t *
+kern_pte1(vm_offset_t va)
+{
+
+ return (pte1_ptr(kern_pt1, va));
+}
+
+static __inline pt2_entry_t *
+pmap_pt2tab_entry(pmap_t pmap, vm_offset_t va)
+{
+
+ return (pt2tab_entry(pmap->pm_pt2tab, va));
+}
+
+static __inline pt2_entry_t *
+kern_pt2tab_entry(vm_offset_t va)
+{
+
+ return (pt2tab_entry(kern_pt2tab, va));
+}
+
+static __inline vm_page_t
+pmap_pt2_page(pmap_t pmap, vm_offset_t va)
+{
+ pt2_entry_t pte2;
+
+ pte2 = pte2_load(pmap_pt2tab_entry(pmap, va));
+ return (PHYS_TO_VM_PAGE(pte2 & PTE2_FRAME));
+}
+
+static __inline vm_page_t
+kern_pt2_page(vm_offset_t va)
+{
+ pt2_entry_t pte2;
+
+ pte2 = pte2_load(kern_pt2tab_entry(va));
+ return (PHYS_TO_VM_PAGE(pte2 & PTE2_FRAME));
+}
+
+#endif /* _KERNEL */
+#endif /* !_MACHINE_PMAP_VAR_H_ */
diff --git a/sys/arm/include/pmc_mdep.h b/sys/arm/include/pmc_mdep.h
new file mode 100644
index 000000000000..69cb0c84deca
--- /dev/null
+++ b/sys/arm/include/pmc_mdep.h
@@ -0,0 +1,83 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2009 Rui Paulo <rpaulo@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_PMC_MDEP_H_
+#define _MACHINE_PMC_MDEP_H_
+
+#define PMC_MDEP_CLASS_INDEX_ARMV7 1
+/*
+ * On the ARM platform we support the following PMCs.
+ *
+ * ARMV7 ARM Cortex-A processors
+ */
+#include <dev/hwpmc/hwpmc_armv7.h>
+
+union pmc_md_op_pmcallocate {
+ uint64_t __pad[4];
+};
+
+/* Logging */
+#define PMCLOG_READADDR PMCLOG_READ32
+#define PMCLOG_EMITADDR PMCLOG_EMIT32
+
+#ifdef _KERNEL
+union pmc_md_pmc {
+ struct pmc_md_armv7_pmc pm_armv7;
+};
+
+#define PMC_IN_KERNEL_STACK(S,START,END) \
+ ((S) >= (START) && (S) < (END))
+#define PMC_IN_KERNEL(va) INKERNEL((va))
+
+#define PMC_IN_USERSPACE(va) ((va) <= VM_MAXUSER_ADDRESS)
+
+#define PMC_TRAPFRAME_TO_PC(TF) ((TF)->tf_pc)
+#define PMC_TRAPFRAME_TO_FP(TF) ((TF)->tf_r11)
+#define PMC_TRAPFRAME_TO_SVC_SP(TF) ((TF)->tf_svc_sp)
+#define PMC_TRAPFRAME_TO_USR_SP(TF) ((TF)->tf_usr_sp)
+#define PMC_TRAPFRAME_TO_SVC_LR(TF) ((TF)->tf_svc_lr)
+#define PMC_TRAPFRAME_TO_USR_LR(TF) ((TF)->tf_usr_lr)
+
+/* Build a fake kernel trapframe from current instruction pointer. */
+#define PMC_FAKE_TRAPFRAME(TF) \
+ do { \
+ (TF)->tf_spsr = PSR_SVC32_MODE; \
+ __asm __volatile("mov %0, pc" : "=r" ((TF)->tf_pc)); \
+ __asm __volatile("mov %0, r11" : "=r" ((TF)->tf_r11)); \
+ } while (0)
+
+/*
+ * Prototypes
+ */
+struct pmc_mdep *pmc_armv7_initialize(void);
+void pmc_armv7_finalize(struct pmc_mdep *_md);
+#endif /* _KERNEL */
+
+#endif /* !_MACHINE_PMC_MDEP_H_ */
diff --git a/sys/arm/include/proc.h b/sys/arm/include/proc.h
new file mode 100644
index 000000000000..a37ccd8f621c
--- /dev/null
+++ b/sys/arm/include/proc.h
@@ -0,0 +1,82 @@
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 1991 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)proc.h 7.1 (Berkeley) 5/15/91
+ * from: FreeBSD: src/sys/i386/include/proc.h,v 1.11 2001/06/29
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_PROC_H_
+#define _MACHINE_PROC_H_
+
+#include <machine/utrap.h>
+
+struct md_utrap {
+ utrap_entry_t *ut_precise[UT_MAX]; /* must be first */
+ int ut_refcnt;
+};
+
+struct mdthread {
+ int md_spinlock_count; /* (k) */
+ register_t md_saved_cspr; /* (k) */
+ register_t md_spurflt_addr; /* (k) Spurious page fault address. */
+ int md_ptrace_instr;
+ int md_ptrace_addr;
+ int md_ptrace_instr_alt;
+ int md_ptrace_addr_alt;
+};
+
+struct mdproc {
+ struct md_utrap *md_utrap;
+ void *md_sigtramp;
+};
+
+#define KINFO_PROC_SIZE 816
+
+#define MAXARGS 8
+/*
+ * This holds the syscall state for a single system call.
+ * As some syscall arguments may be 64-bit aligned we need to ensure the
+ * args value is 64-bit aligned. The ABI will then ensure any 64-bit
+ * arguments are already correctly aligned, even if they were passed in
+ * via registers, we just need to make sure we copy them to an aligned
+ * buffer.
+ */
+struct syscall_args {
+ u_int code;
+ struct sysent *callp;
+ register_t args[MAXARGS];
+} __aligned(8);
+
+#endif /* !_MACHINE_PROC_H_ */
diff --git a/sys/arm/include/procctl.h b/sys/arm/include/procctl.h
new file mode 100644
index 000000000000..5221cfcd7be1
--- /dev/null
+++ b/sys/arm/include/procctl.h
@@ -0,0 +1,4 @@
+/*-
+ * This file is in the public domain.
+ */
+/* $FreeBSD$ */
diff --git a/sys/arm/include/profile.h b/sys/arm/include/profile.h
new file mode 100644
index 000000000000..cab8f89ddadf
--- /dev/null
+++ b/sys/arm/include/profile.h
@@ -0,0 +1,124 @@
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)profile.h 8.1 (Berkeley) 6/11/93
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_PROFILE_H_
+#define _MACHINE_PROFILE_H_
+
+/*
+ * Config generates something to tell the compiler to align functions on 32
+ * byte boundaries. A strict alignment is good for keeping the tables small.
+ */
+#define FUNCTION_ALIGNMENT 16
+
+#define _MCOUNT_DECL void mcount
+
+typedef u_long fptrdiff_t;
+
+/*
+ * Cannot implement mcount in C as GCC will trash the ip register when it
+ * pushes a trapframe. Pity we cannot insert assembly before the function
+ * prologue.
+ */
+
+#ifndef PLTSYM
+#define PLTSYM
+#endif
+
+#define MCOUNT \
+ __asm__(".text"); \
+ __asm__(".align 2"); \
+ __asm__(".type __mcount ,%function"); \
+ __asm__(".global __mcount"); \
+ __asm__("__mcount:"); \
+ /* \
+ * Preserve registers that are trashed during mcount \
+ */ \
+ __asm__("stmfd sp!, {r0-r3, ip, lr}"); \
+ /* \
+ * find the return address for mcount, \
+ * and the return address for mcount's caller. \
+ * \
+ * frompcindex = pc pushed by call into self. \
+ */ \
+ __asm__("mov r0, ip"); \
+ /* \
+ * selfpc = pc pushed by mcount call \
+ */ \
+ __asm__("mov r1, lr"); \
+ /* \
+ * Call the real mcount code \
+ */ \
+ __asm__("bl mcount"); \
+ /* \
+ * Restore registers that were trashed during mcount \
+ */ \
+ __asm__("ldmfd sp!, {r0-r3, lr}"); \
+ /* \
+ * Return to the caller. Loading lr and pc in one instruction \
+ * is deprecated on ARMv7 so we need this on its own. \
+ */ \
+ __asm__("ldmfd sp!, {pc}");
+void bintr(void);
+void btrap(void);
+void eintr(void);
+void user(void);
+
+#define MCOUNT_FROMPC_USER(pc) \
+ ((pc < (uintfptr_t)VM_MAXUSER_ADDRESS) ? (uintfptr_t)user : pc)
+
+#define MCOUNT_FROMPC_INTR(pc) \
+ ((pc >= (uintfptr_t)btrap && pc < (uintfptr_t)eintr) ? \
+ ((pc >= (uintfptr_t)bintr) ? (uintfptr_t)bintr : \
+ (uintfptr_t)btrap) : ~0U)
+
+#ifdef _KERNEL
+
+#define MCOUNT_DECL(s) register_t s;
+
+#include <machine/asm.h>
+#include <machine/cpufunc.h>
+#define MCOUNT_ENTER(s) {s = intr_disable(); } /* kill IRQ */
+#define MCOUNT_EXIT(s) {intr_restore(s); } /* restore old value */
+
+void mcount(uintfptr_t frompc, uintfptr_t selfpc);
+
+#else
+typedef u_int uintfptr_t;
+#endif /* _KERNEL */
+
+#endif /* !_MACHINE_PROFILE_H_ */
diff --git a/sys/arm/include/psl.h b/sys/arm/include/psl.h
new file mode 100644
index 000000000000..000e93dd64a6
--- /dev/null
+++ b/sys/arm/include/psl.h
@@ -0,0 +1,84 @@
+/* $NetBSD: psl.h,v 1.6 2003/06/16 20:00:58 thorpej Exp $ */
+
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 1995 Mark Brinicombe.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Mark Brinicombe
+ * for the NetBSD Project.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * RiscBSD kernel project
+ *
+ * psl.h
+ *
+ * spl prototypes.
+ * Eventually this will become a set of defines.
+ *
+ * Created : 21/07/95
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_PSL_H_
+#define _MACHINE_PSL_H_
+
+/*
+ * These are the different SPL states
+ *
+ * Each state has an interrupt mask associated with it which
+ * indicate which interrupts are allowed.
+ */
+
+#define _SPL_0 0
+#define _SPL_SOFTCLOCK 1
+#define _SPL_SOFTNET 2
+#define _SPL_BIO 3
+#define _SPL_NET 4
+#define _SPL_SOFTSERIAL 5
+#define _SPL_TTY 6
+#define _SPL_VM 7
+#define _SPL_AUDIO 8
+#define _SPL_CLOCK 9
+#define _SPL_STATCLOCK 10
+#define _SPL_HIGH 11
+#define _SPL_SERIAL 12
+#define _SPL_LEVELS 13
+
+#ifdef _KERNEL
+#ifndef _LOCORE
+extern int current_spl_level;
+
+extern u_int spl_masks[_SPL_LEVELS + 1];
+extern u_int spl_smasks[_SPL_LEVELS];
+#endif /* _LOCORE */
+#endif /* _KERNEL */
+
+#endif /* _ARM_PSL_H_ */
+/* End of psl.h */
diff --git a/sys/arm/include/pte-v6.h b/sys/arm/include/pte-v6.h
new file mode 100644
index 000000000000..d9aaaea6ef56
--- /dev/null
+++ b/sys/arm/include/pte-v6.h
@@ -0,0 +1,297 @@
+/*-
+ * Copyright 2014 Svatopluk Kraus <onwahe@gmail.com>
+ * Copyright 2014 Michal Meloun <meloun@miracle.cz>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_PTE_V6_H_
+#define _MACHINE_PTE_V6_H_
+
+/*
+ * Domain Types for the Domain Access Control Register.
+ */
+#define DOMAIN_FAULT 0x00 /* no access */
+#define DOMAIN_CLIENT 0x01 /* client */
+#define DOMAIN_RESERVED 0x02 /* reserved */
+#define DOMAIN_MANAGER 0x03 /* manager */
+
+/*
+ * TEX remap registers attributes
+ */
+#define PRRR_SO 0 /* Strongly ordered memory */
+#define PRRR_DEV 1 /* Device memory */
+#define PRRR_MEM 2 /* Normal memory */
+#define PRRR_DS0 (1 << 16) /* Shared bit for Device, S = 0 */
+#define PRRR_DS1 (1 << 17) /* Shared bit for Device, S = 1 */
+#define PRRR_NS0 (1 << 18) /* Shared bit for Normal, S = 0 */
+#define PRRR_NS1 (1 << 19) /* Shared bit for Normal, S = 1 */
+#define PRRR_NOS_SHIFT 24 /* base shif for Not Outer Shared bits */
+
+#define NMRR_NC 0 /* Noncachable*/
+#define NMRR_WB_WA 1 /* Write Back, Write Allocate */
+#define NMRR_WT 2 /* Write Through, Non-Write Allocate */
+#define NMRR_WB 3 /* Write Back, Non-Write Allocate */
+
+/*
+ *
+ * The ARM MMU is capable of mapping memory in the following chunks:
+ *
+ * 16M Supersections (L1 table)
+ *
+ * 1M Sections (L1 table)
+ *
+ * 64K Large Pages (L2 table)
+ *
+ * 4K Small Pages (L2 table)
+ *
+ *
+ * Coarse Tables can map Large and Small Pages.
+ * Coarse Tables are 1K in length.
+ *
+ * The Translation Table Base register holds the pointer to the
+ * L1 Table. The L1 Table is a 16K contiguous chunk of memory
+ * aligned to a 16K boundary. Each entry in the L1 Table maps
+ * 1M of virtual address space, either via a Section mapping or
+ * via an L2 Table.
+ *
+ */
+#define L1_TABLE_SIZE 0x4000 /* 16K */
+#define L1_ENTRIES 0x1000 /* 4K */
+#define L2_TABLE_SIZE 0x0400 /* 1K */
+#define L2_ENTRIES 0x0100 /* 256 */
+
+/* ARMv6 super-sections. */
+#define L1_SUP_SIZE 0x01000000 /* 16M */
+#define L1_SUP_OFFSET (L1_SUP_SIZE - 1)
+#define L1_SUP_FRAME (~L1_SUP_OFFSET)
+#define L1_SUP_SHIFT 24
+
+#define L1_S_SIZE 0x00100000 /* 1M */
+#define L1_S_OFFSET (L1_S_SIZE - 1)
+#define L1_S_FRAME (~L1_S_OFFSET)
+#define L1_S_SHIFT 20
+
+#define L2_L_SIZE 0x00010000 /* 64K */
+#define L2_L_OFFSET (L2_L_SIZE - 1)
+#define L2_L_FRAME (~L2_L_OFFSET)
+#define L2_L_SHIFT 16
+
+#define L2_S_SIZE 0x00001000 /* 4K */
+#define L2_S_OFFSET (L2_S_SIZE - 1)
+#define L2_S_FRAME (~L2_S_OFFSET)
+#define L2_S_SHIFT 12
+
+/*
+ * ARM MMU L1 Descriptors
+ */
+#define L1_TYPE_INV 0x00 /* Invalid (fault) */
+#define L1_TYPE_C 0x01 /* Coarse L2 */
+#define L1_TYPE_S 0x02 /* Section */
+#define L1_TYPE_MASK 0x03 /* Mask of type bits */
+
+/* L1 Section Descriptor */
+#define L1_S_B 0x00000004 /* bufferable Section */
+#define L1_S_C 0x00000008 /* cacheable Section */
+#define L1_S_NX 0x00000010 /* not executeable */
+#define L1_S_DOM(x) ((x) << 5) /* domain */
+#define L1_S_DOM_MASK L1_S_DOM(0xf)
+#define L1_S_P 0x00000200 /* ECC enable for this section */
+#define L1_S_AP(x) ((x) << 10) /* access permissions */
+#define L1_S_AP0 0x00000400 /* access permissions bit 0 */
+#define L1_S_AP1 0x00000800 /* access permissions bit 1 */
+#define L1_S_TEX(x) ((x) << 12) /* type extension */
+#define L1_S_TEX0 0x00001000 /* type extension bit 0 */
+#define L1_S_TEX1 0x00002000 /* type extension bit 1 */
+#define L1_S_TEX2 0x00004000 /* type extension bit 2 */
+#define L1_S_AP2 0x00008000 /* access permissions bit 2 */
+#define L1_S_SHARED 0x00010000 /* shared */
+#define L1_S_NG 0x00020000 /* not global */
+#define L1_S_SUPERSEC 0x00040000 /* Section is a super-section. */
+#define L1_S_ADDR_MASK 0xfff00000 /* phys address of section */
+
+/* L1 Coarse Descriptor */
+#define L1_C_DOM(x) ((x) << 5) /* domain */
+#define L1_C_DOM_MASK L1_C_DOM(0xf)
+#define L1_C_P 0x00000200 /* ECC enable for this section */
+#define L1_C_ADDR_MASK 0xfffffc00 /* phys address of L2 Table */
+
+/*
+ * ARM MMU L2 Descriptors
+ */
+#define L2_TYPE_INV 0x00 /* Invalid (fault) */
+#define L2_TYPE_L 0x01 /* Large Page - 64k - not used yet*/
+#define L2_TYPE_S 0x02 /* Small Page - 4 */
+#define L2_TYPE_MASK 0x03
+
+#define L2_NX 0x00000001 /* Not executable */
+#define L2_B 0x00000004 /* Bufferable page */
+#define L2_C 0x00000008 /* Cacheable page */
+#define L2_CB_SHIFT 2 /* C,B bit field shift */
+#define L2_AP(x) ((x) << 4)
+#define L2_AP0 0x00000010 /* access permissions bit 0*/
+#define L2_AP1 0x00000020 /* access permissions bit 1*/
+#define L2_TEX_SHIFT 6 /* type extension field shift */
+#define L2_TEX(x) ((x) << L2_TEX_SHIFT) /* type extension */
+#define L2_TEX0 0x00000040 /* type extension bit 0 */
+#define L2_TEX1 0x00000080 /* type extension bit 1 */
+#define L2_TEX2 0x00000100 /* type extension bit 2 */
+#define L2_AP2 0x00000200 /* access permissions bit 2*/
+#define L2_SHARED 0x00000400 /* shared */
+#define L2_NG 0x00000800 /* not global */
+
+/*
+ * TEX classes encoding
+ */
+#define TEX1_CLASS_0 ( 0)
+#define TEX1_CLASS_1 ( L1_S_B)
+#define TEX1_CLASS_2 ( L1_S_C )
+#define TEX1_CLASS_3 ( L1_S_C | L1_S_B)
+#define TEX1_CLASS_4 (L1_S_TEX0 )
+#define TEX1_CLASS_5 (L1_S_TEX0 | L1_S_B)
+#define TEX1_CLASS_6 (L1_S_TEX0 | L1_S_C ) /* Reserved for ARM11 */
+#define TEX1_CLASS_7 (L1_S_TEX0 | L1_S_C | L1_S_B)
+
+#define TEX2_CLASS_0 ( 0)
+#define TEX2_CLASS_1 ( L2_B)
+#define TEX2_CLASS_2 ( L2_C )
+#define TEX2_CLASS_3 ( L2_C | L2_B)
+#define TEX2_CLASS_4 (L2_TEX0 )
+#define TEX2_CLASS_5 (L2_TEX0 | L2_B)
+#define TEX2_CLASS_6 (L2_TEX0 | L2_C ) /* Reserved for ARM11 */
+#define TEX2_CLASS_7 (L2_TEX0 | L2_C | L2_B)
+
+/* L1 table definitions. */
+#define NB_IN_PT1 L1_TABLE_SIZE
+#define NPTE1_IN_PT1 L1_ENTRIES
+
+/* L2 table definitions. */
+#define NB_IN_PT2 L2_TABLE_SIZE
+#define NPTE2_IN_PT2 L2_ENTRIES
+
+/*
+ * Map memory attributes to TEX classes
+ */
+#define PTE2_ATTR_WB_WA TEX2_CLASS_0
+#define PTE2_ATTR_NOCACHE TEX2_CLASS_1
+#define PTE2_ATTR_DEVICE TEX2_CLASS_2
+#define PTE2_ATTR_SO TEX2_CLASS_3
+#define PTE2_ATTR_WT TEX2_CLASS_4
+/*
+ * Software defined bits for L1 descriptors
+ * - L1_AP0 is used as page accessed bit
+ * - L1_AP2 (RO / not RW) is used as page not modified bit
+ * - L1_TEX0 is used as software emulated RO bit
+ */
+#define PTE1_V L1_TYPE_S /* Valid bit */
+#define PTE1_A L1_S_AP0 /* Accessed - software emulated */
+#define PTE1_NM L1_S_AP2 /* not modified bit - software emulated
+ * used as real write enable bit */
+#define PTE1_M 0 /* Modified (dummy) */
+#define PTE1_S L1_S_SHARED /* Shared */
+#define PTE1_NG L1_S_NG /* Not global */
+#define PTE1_G 0 /* Global (dummy) */
+#define PTE1_NX L1_S_NX /* Not executable */
+#define PTE1_X 0 /* Executable (dummy) */
+#define PTE1_RO L1_S_TEX1 /* Read Only */
+#define PTE1_RW 0 /* Read-Write (dummy) */
+#define PTE1_U L1_S_AP1 /* User */
+#define PTE1_NU 0 /* Not user (kernel only) (dummy) */
+#define PTE1_W L1_S_TEX2 /* Wired */
+
+#define PTE1_SHIFT L1_S_SHIFT
+#define PTE1_SIZE L1_S_SIZE
+#define PTE1_OFFSET L1_S_OFFSET
+#define PTE1_FRAME L1_S_FRAME
+
+#define PTE1_ATTR_MASK (L1_S_TEX0 | L1_S_C | L1_S_B)
+
+#define PTE1_AP_KR (PTE1_RO | PTE1_NM)
+#define PTE1_AP_KRW 0
+#define PTE1_AP_KRUR (PTE1_RO | PTE1_NM | PTE1_U)
+#define PTE1_AP_KRWURW PTE1_U
+
+/*
+ * PTE1 descriptors creation macros.
+ */
+#define PTE1_PA(pa) ((pa) & PTE1_FRAME)
+#define PTE1_AP_COMMON (PTE1_V | PTE1_S)
+
+#define PTE1(pa, ap, attr) (PTE1_PA(pa) | (ap) | (attr) | PTE1_AP_COMMON)
+
+#define PTE1_KERN(pa, ap, attr) PTE1(pa, (ap) | PTE1_A | PTE1_G, attr)
+#define PTE1_KERN_NG(pa, ap, attr) PTE1(pa, (ap) | PTE1_A | PTE1_NG, attr)
+
+#define PTE1_LINK(pa) (((pa) & L1_C_ADDR_MASK) | L1_TYPE_C)
+
+/*
+ * Software defined bits for L2 descriptors
+ * - L2_AP0 is used as page accessed bit
+ * - L2_AP2 (RO / not RW) is used as page not modified bit
+ * - L2_TEX0 is used as software emulated RO bit
+ */
+#define PTE2_V L2_TYPE_S /* Valid bit */
+#define PTE2_A L2_AP0 /* Accessed - software emulated */
+#define PTE2_NM L2_AP2 /* not modified bit - software emulated
+ * used as real write enable bit */
+#define PTE2_M 0 /* Modified (dummy) */
+#define PTE2_S L2_SHARED /* Shared */
+#define PTE2_NG L2_NG /* Not global */
+#define PTE2_G 0 /* Global (dummy) */
+#define PTE2_NX L2_NX /* Not executable */
+#define PTE2_X 0 /* Not executable (dummy) */
+#define PTE2_RO L2_TEX1 /* Read Only */
+#define PTE2_U L2_AP1 /* User */
+#define PTE2_NU 0 /* Not user (kernel only) (dummy) */
+#define PTE2_W L2_TEX2 /* Wired */
+
+#define PTE2_SHIFT L2_S_SHIFT
+#define PTE2_SIZE L2_S_SIZE
+#define PTE2_OFFSET L2_S_OFFSET
+#define PTE2_FRAME L2_S_FRAME
+
+#define PTE2_ATTR_MASK (L2_TEX0 | L2_C | L2_B)
+/* PTE2 attributes to TEX class index: (TEX0 C B) */
+#define PTE2_ATTR2IDX(attr) \
+ ((((attr) & (L2_C | L2_B)) >> L2_CB_SHIFT) | \
+ (((attr) & L2_TEX0) >> (L2_TEX_SHIFT - L2_CB_SHIFT)))
+
+#define PTE2_AP_KR (PTE2_RO | PTE2_NM)
+#define PTE2_AP_KRW 0
+#define PTE2_AP_KRUR (PTE2_RO | PTE2_NM | PTE2_U)
+#define PTE2_AP_KRWURW PTE2_U
+
+/*
+ * PTE2 descriptors creation macros.
+ */
+#define PTE2_PA(pa) ((pa) & PTE2_FRAME)
+#define PTE2_AP_COMMON (PTE2_V | PTE2_S)
+
+#define PTE2(pa, ap, attr) (PTE2_PA(pa) | (ap) | (attr) | PTE2_AP_COMMON)
+
+#define PTE2_KERN(pa, ap, attr) PTE2(pa, (ap) | PTE2_A | PTE2_G, attr)
+#define PTE2_KERN_NG(pa, ap, attr) PTE2(pa, (ap) | PTE2_A | PTE2_NG, attr)
+
+#endif /* !_MACHINE_PTE_V6_H_ */
diff --git a/sys/arm/include/ptrace.h b/sys/arm/include/ptrace.h
new file mode 100644
index 000000000000..44085a3b7079
--- /dev/null
+++ b/sys/arm/include/ptrace.h
@@ -0,0 +1,22 @@
+/* $NetBSD: ptrace.h,v 1.2 2001/02/23 21:23:52 reinoud Exp $ */
+/* $FreeBSD$ */
+
+#ifndef _MACHINE_PTRACE_H_
+#define _MACHINE_PTRACE_H_
+
+#define __HAVE_PTRACE_MACHDEP
+
+/*
+ * Must match mcontext_vfp_t. Note that mcontext_vfp_t does not
+ * include explicit padding.
+ */
+struct vfpreg {
+ __uint64_t vfp_reg[32];
+ __uint32_t vfp_scr;
+ __uint32_t vfp_pad0;
+};
+
+#define PT_GETVFPREGS (PT_FIRSTMACH + 0)
+#define PT_SETVFPREGS (PT_FIRSTMACH + 1)
+
+#endif /* !_MACHINE_PTRACE_H */
diff --git a/sys/arm/include/reg.h b/sys/arm/include/reg.h
new file mode 100644
index 000000000000..ab70ae128d39
--- /dev/null
+++ b/sys/arm/include/reg.h
@@ -0,0 +1,42 @@
+/* $NetBSD: reg.h,v 1.2 2001/02/23 21:23:52 reinoud Exp $ */
+/* $FreeBSD$ */
+#ifndef MACHINE_REG_H
+#define MACHINE_REG_H
+
+struct reg {
+ unsigned int r[13];
+ unsigned int r_sp;
+ unsigned int r_lr;
+ unsigned int r_pc;
+ unsigned int r_cpsr;
+};
+
+struct fp_extended_precision {
+ u_int32_t fp_exponent;
+ u_int32_t fp_mantissa_hi;
+ u_int32_t fp_mantissa_lo;
+};
+
+typedef struct fp_extended_precision fp_reg_t;
+
+struct fpreg {
+ unsigned int fpr_fpsr;
+ fp_reg_t fpr[8];
+};
+
+struct dbreg {
+#define ARM_WR_MAX 16 /* Maximum number of watchpoint registers */
+ unsigned int dbg_wcr[ARM_WR_MAX]; /* Watchpoint Control Registers */
+ unsigned int dbg_wvr[ARM_WR_MAX]; /* Watchpoint Value Registers */
+};
+
+#ifdef _KERNEL
+int fill_regs(struct thread *, struct reg *);
+int set_regs(struct thread *, struct reg *);
+int fill_fpregs(struct thread *, struct fpreg *);
+int set_fpregs(struct thread *, struct fpreg *);
+int fill_dbregs(struct thread *, struct dbreg *);
+int set_dbregs(struct thread *, struct dbreg *);
+#endif
+
+#endif /* !MACHINE_REG_H */
diff --git a/sys/arm/include/reloc.h b/sys/arm/include/reloc.h
new file mode 100644
index 000000000000..688475703225
--- /dev/null
+++ b/sys/arm/include/reloc.h
@@ -0,0 +1,55 @@
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)reloc.h 8.1 (Berkeley) 6/10/93
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_RELOC_H_
+#define _MACHINE_RELOC_H_
+
+/* Relocation format. */
+struct relocation_info {
+ int r_address; /* offset in text or data segment */
+ unsigned int r_symbolnum : 24, /* ordinal number of add symbol */
+ r_pcrel : 1, /* 1 if value should be pc-relative */
+ r_length : 2, /* log base 2 of value's width */
+ r_extern : 1, /* 1 if need to add symbol to value */
+ r_baserel : 1, /* linkage table relative */
+ r_jmptable : 1, /* relocate to jump table */
+ r_relative : 1, /* load address relative */
+ r_copy : 1; /* run time copy */
+};
+
+#endif
diff --git a/sys/arm/include/resource.h b/sys/arm/include/resource.h
new file mode 100644
index 000000000000..bfa5b3f98790
--- /dev/null
+++ b/sys/arm/include/resource.h
@@ -0,0 +1,49 @@
+/*-
+ * Copyright 1998 Massachusetts Institute of Technology
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that both the above copyright notice and this
+ * permission notice appear in all copies, that both the above
+ * copyright notice and this permission notice appear in all
+ * supporting documentation, and that the name of M.I.T. not be used
+ * in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission. M.I.T. makes
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
+ * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_RESOURCE_H_
+#define _MACHINE_RESOURCE_H_ 1
+
+/*
+ * Definitions of resource types for Intel Architecture machines
+ * with support for legacy ISA devices and drivers.
+ */
+
+#define SYS_RES_IRQ 1 /* interrupt lines */
+#define SYS_RES_DRQ 2 /* isa dma lines */
+#define SYS_RES_MEMORY 3 /* i/o memory */
+#define SYS_RES_IOPORT 4 /* i/o ports */
+#define SYS_RES_GPIO 5 /* general purpose i/o */
+#ifdef NEW_PCIB
+#define PCI_RES_BUS 6 /* PCI bus numbers */
+#endif
+
+#endif /* !_MACHINE_RESOURCE_H_ */
diff --git a/sys/arm/include/runq.h b/sys/arm/include/runq.h
new file mode 100644
index 000000000000..ba21b9ab4316
--- /dev/null
+++ b/sys/arm/include/runq.h
@@ -0,0 +1,48 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2001 Jake Burkholder <jake@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_RUNQ_H_
+#define _MACHINE_RUNQ_H_
+
+#define RQB_LEN (2) /* Number of priority status words. */
+#define RQB_L2BPW (5) /* Log2(sizeof(rqb_word_t) * NBBY)). */
+#define RQB_BPW (1<<RQB_L2BPW) /* Bits in an rqb_word_t. */
+
+#define RQB_BIT(pri) (1 << ((pri) & (RQB_BPW - 1)))
+#define RQB_WORD(pri) ((pri) >> RQB_L2BPW)
+
+#define RQB_FFS(word) (ffs(word) - 1)
+
+/*
+ * Type of run queue status word.
+ */
+typedef u_int32_t rqb_word_t;
+
+#endif
diff --git a/sys/arm/include/sc_machdep.h b/sys/arm/include/sc_machdep.h
new file mode 100644
index 000000000000..61a2a86f702d
--- /dev/null
+++ b/sys/arm/include/sc_machdep.h
@@ -0,0 +1,73 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2003 Jake Burkholder.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_SC_MACHDEP_H_
+#define _MACHINE_SC_MACHDEP_H_
+
+/* Color attributes for foreground text */
+
+#define FG_BLACK 0x0
+#define FG_BLUE 0x1
+#define FG_GREEN 0x2
+#define FG_CYAN 0x3
+#define FG_RED 0x4
+#define FG_MAGENTA 0x5
+#define FG_BROWN 0x6
+#define FG_LIGHTGREY 0x7 /* aka white */
+#define FG_DARKGREY 0x8
+#define FG_LIGHTBLUE 0x9
+#define FG_LIGHTGREEN 0xa
+#define FG_LIGHTCYAN 0xb
+#define FG_LIGHTRED 0xc
+#define FG_LIGHTMAGENTA 0xd
+#define FG_YELLOW 0xe
+#define FG_WHITE 0xf /* aka bright white */
+#define FG_BLINK 0x80
+
+/* Color attributes for text background */
+
+#define BG_BLACK 0x00
+#define BG_BLUE 0x10
+#define BG_GREEN 0x20
+#define BG_CYAN 0x30
+#define BG_RED 0x40
+#define BG_MAGENTA 0x50
+#define BG_BROWN 0x60
+#define BG_LIGHTGREY 0x70
+#define BG_DARKGREY 0x80
+#define BG_LIGHTBLUE 0x90
+#define BG_LIGHTGREEN 0xa0
+#define BG_LIGHTCYAN 0xb0
+#define BG_LIGHTRED 0xc0
+#define BG_LIGHTMAGENTA 0xd0
+#define BG_YELLOW 0xe0
+#define BG_WHITE 0xf0
+
+#endif /* !_MACHINE_SC_MACHDEP_H_ */
diff --git a/sys/arm/include/setjmp.h b/sys/arm/include/setjmp.h
new file mode 100644
index 000000000000..2090c5d15f40
--- /dev/null
+++ b/sys/arm/include/setjmp.h
@@ -0,0 +1,107 @@
+/* $NetBSD: setjmp.h,v 1.5 2013/01/11 13:56:32 matt Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * machine/setjmp.h: machine dependent setjmp-related information.
+ */
+
+#ifndef _MACHINE_SETJMP_H_
+#define _MACHINE_SETJMP_H_
+
+#define _JBLEN 64 /* size, in longs, of a jmp_buf */
+
+/*
+ * NOTE: The internal structure of a jmp_buf is *PRIVATE*
+ * This information is provided as there is software
+ * that fiddles with this with obtain the stack pointer
+ * (yes really ! and its commercial !).
+ *
+ * Description of the setjmp buffer
+ *
+ * word 0 magic number (dependent on creator)
+ * 13 fpscr vfp status control register
+ * 14 r4 register 4
+ * 15 r5 register 5
+ * 16 r6 register 6
+ * 17 r7 register 7
+ * 18 r8 register 8
+ * 19 r9 register 9
+ * 20 r10 register 10 (sl)
+ * 21 r11 register 11 (fp)
+ * 22 r12 register 12 (ip)
+ * 23 r13 register 13 (sp)
+ * 24 r14 register 14 (lr)
+ * 25 signal mask (dependent on magic)
+ * 26 (con't)
+ * 27 (con't)
+ * 28 (con't)
+ * 32-33 d8 (vfp register d8)
+ * 34-35 d9 (vfp register d9)
+ * 36-37 d10 (vfp register d10)
+ * 38-39 d11 (vfp register d11)
+ * 40-41 d12 (vfp register d12)
+ * 42-43 d13 (vfp register d13)
+ * 44-45 d14 (vfp register d14)
+ * 46-47 d15 (vfp register d15)
+ *
+ * The magic number number identifies the jmp_buf and
+ * how the buffer was created as well as providing
+ * a sanity check
+ *
+ * A side note I should mention - Please do not tamper
+ * with the floating point fields. While they are
+ * always saved and restored at the moment this cannot
+ * be garenteed especially if the compiler happens
+ * to be generating soft-float code so no fp
+ * registers will be used.
+ *
+ * Whilst this can be seen an encouraging people to
+ * use the setjmp buffer in this way I think that it
+ * is for the best then if changes occur compiles will
+ * break rather than just having new builds falling over
+ * mysteriously.
+ */
+
+#define _JB_MAGIC__SETJMP 0x4278f500
+#define _JB_MAGIC_SETJMP 0x4278f501
+#define _JB_MAGIC__SETJMP_VFP 0x4278f502
+#define _JB_MAGIC_SETJMP_VFP 0x4278f503
+
+/* Valid for all jmp_buf's */
+
+#define _JB_MAGIC 0
+#define _JB_REG_FPSCR 13
+#define _JB_REG_R4 14
+#define _JB_REG_R5 15
+#define _JB_REG_R6 16
+#define _JB_REG_R7 17
+#define _JB_REG_R8 18
+#define _JB_REG_R9 19
+#define _JB_REG_R10 20
+#define _JB_REG_R11 21
+#define _JB_REG_R12 22
+#define _JB_REG_R13 23
+#define _JB_REG_R14 24
+
+/* Only valid with the _JB_MAGIC_SETJMP magic */
+
+#define _JB_SIGMASK 25
+
+#define _JB_REG_D8 32
+#define _JB_REG_D9 34
+#define _JB_REG_D10 36
+#define _JB_REG_D11 38
+#define _JB_REG_D12 40
+#define _JB_REG_D13 42
+#define _JB_REG_D14 44
+#define _JB_REG_D15 46
+
+#ifndef __ASSEMBLER__
+#if __BSD_VISIBLE || __POSIX_VISIBLE || __XSI_VISIBLE
+typedef struct _sigjmp_buf { int _sjb[_JBLEN + 1]; } sigjmp_buf[1];
+#endif
+
+typedef struct _jmp_buf { int _jb[_JBLEN + 1]; } jmp_buf[1];
+#endif
+
+#endif /* !_MACHINE_SETJMP_H_ */
diff --git a/sys/arm/include/sf_buf.h b/sys/arm/include/sf_buf.h
new file mode 100644
index 000000000000..65ef6f99b7a1
--- /dev/null
+++ b/sys/arm/include/sf_buf.h
@@ -0,0 +1,48 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2003 Alan L. Cox <alc@cs.rice.edu>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_SF_BUF_H_
+#define _MACHINE_SF_BUF_H_
+
+static inline void
+sf_buf_map(struct sf_buf *sf, int flags)
+{
+
+ pmap_qenter(sf->kva, &(sf->m), 1);
+}
+
+static inline int
+sf_buf_unmap(struct sf_buf *sf)
+{
+
+ pmap_qremove(sf->kva, 1);
+ return (1);
+}
+#endif /* !_MACHINE_SF_BUF_H_ */
diff --git a/sys/arm/include/sigframe.h b/sys/arm/include/sigframe.h
new file mode 100644
index 000000000000..9787f579d563
--- /dev/null
+++ b/sys/arm/include/sigframe.h
@@ -0,0 +1,2 @@
+/* $FreeBSD$ */
+#include <machine/frame.h>
diff --git a/sys/arm/include/signal.h b/sys/arm/include/signal.h
new file mode 100644
index 000000000000..2286bcea2d2d
--- /dev/null
+++ b/sys/arm/include/signal.h
@@ -0,0 +1,52 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1986, 1989, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)signal.h 8.1 (Berkeley) 6/11/93
+ * from: FreeBSD: src/sys/i386/include/signal.h,v 1.13 2000/11/09
+ * from: FreeBSD: src/sys/sparc64/include/signal.h,v 1.6 2001/09/30 18:52:17
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_SIGNAL_H_
+#define _MACHINE_SIGNAL_H_
+
+#include <sys/cdefs.h>
+
+typedef long sig_atomic_t;
+
+#if __BSD_VISIBLE
+
+struct sigcontext {
+ int _dummy;
+};
+
+#endif
+
+#endif /* !_MACHINE_SIGNAL_H_ */
diff --git a/sys/arm/include/smp.h b/sys/arm/include/smp.h
new file mode 100644
index 000000000000..4f1545872a4e
--- /dev/null
+++ b/sys/arm/include/smp.h
@@ -0,0 +1,35 @@
+/* $FreeBSD$ */
+
+#ifndef _MACHINE_SMP_H_
+#define _MACHINE_SMP_H_
+
+#include <sys/_cpuset.h>
+#include <machine/pcb.h>
+
+enum {
+ IPI_AST,
+ IPI_PREEMPT,
+ IPI_RENDEZVOUS,
+ IPI_STOP,
+ IPI_STOP_HARD = IPI_STOP, /* These are synonyms on arm. */
+ IPI_HARDCLOCK,
+ IPI_TLB, /* Not used now, but keep it reserved. */
+ IPI_CACHE, /* Not used now, but keep it reserved. */
+ INTR_IPI_COUNT
+};
+
+void init_secondary(int cpu);
+void mpentry(void);
+
+void ipi_all_but_self(u_int ipi);
+void ipi_cpu(int cpu, u_int ipi);
+void ipi_selected(cpuset_t cpus, u_int ipi);
+
+/* Platform interface */
+void platform_mp_setmaxid(void);
+void platform_mp_start_ap(void);
+
+/* global data in mp_machdep.c */
+extern struct pcb stoppcbs[];
+
+#endif /* !_MACHINE_SMP_H_ */
diff --git a/sys/arm/include/stack.h b/sys/arm/include/stack.h
new file mode 100644
index 000000000000..4bc384f775bc
--- /dev/null
+++ b/sys/arm/include/stack.h
@@ -0,0 +1,68 @@
+/*-
+ * Copyright (c) 2000, 2001 Ben Harris
+ * Copyright (c) 1996 Scott K. Stevens
+ *
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_STACK_H_
+#define _MACHINE_STACK_H_
+
+#define INKERNEL(va) (((vm_offset_t)(va)) >= VM_MIN_KERNEL_ADDRESS)
+
+#define FR_SCP (0)
+#define FR_RLV (-1)
+#define FR_RSP (-2)
+#define FR_RFP (-3)
+
+/* The state of the unwind process */
+struct unwind_state {
+ uint32_t registers[16];
+ uint32_t start_pc;
+ uint32_t *insn;
+ u_int entries;
+ u_int byte;
+ uint16_t update_mask;
+};
+
+/* The register names */
+#define FP 11
+#define SP 13
+#define LR 14
+#define PC 15
+
+#ifdef _KERNEL
+
+int unwind_stack_one(struct unwind_state *, int);
+
+struct linker_file;
+void unwind_module_loaded(struct linker_file *);
+void unwind_module_unloaded(struct linker_file *);
+
+#endif
+
+#endif /* !_MACHINE_STACK_H_ */
diff --git a/sys/arm/include/stdarg.h b/sys/arm/include/stdarg.h
new file mode 100644
index 000000000000..acb526429ac2
--- /dev/null
+++ b/sys/arm/include/stdarg.h
@@ -0,0 +1,39 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2017 Poul-Henning Kamp. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_STDARG_H_
+#define _MACHINE_STDARG_H_
+
+#include <sys/_stdarg.h>
+
+#ifndef va_start
+ #error this file needs to be ported to your compiler
+#endif
+
+#endif /* !_MACHINE_STDARG_H_ */
diff --git a/sys/arm/include/swi.h b/sys/arm/include/swi.h
new file mode 100644
index 000000000000..b2caf5ee1c31
--- /dev/null
+++ b/sys/arm/include/swi.h
@@ -0,0 +1,22 @@
+/* $NetBSD: swi.h,v 1.1 2002/01/13 15:03:06 bjh21 Exp $ */
+/* $FreeBSD$ */
+
+/*-
+ * This file is in the Public Domain.
+ * Ben Harris, 2002.
+ */
+
+#ifndef _MACHINE_SWI_H_
+#define _MACHINE_SWI_H_
+
+#define SWI_OS_MASK 0xf00000
+#define SWI_OS_RISCOS 0x000000
+#define SWI_OS_RISCIX 0x800000
+#define SWI_OS_LINUX 0x900000
+#define SWI_OS_NETBSD 0xa00000
+#define SWI_OS_ARM 0xf00000
+
+#define SWI_IMB 0xf00000
+#define SWI_IMBrange 0xf00001
+
+#endif /* !_MACHINE_SWI_H_ */
diff --git a/sys/arm/include/sysarch.h b/sys/arm/include/sysarch.h
new file mode 100644
index 000000000000..603d8c5a852c
--- /dev/null
+++ b/sys/arm/include/sysarch.h
@@ -0,0 +1,83 @@
+/* $NetBSD: sysarch.h,v 1.5 2003/09/11 09:40:12 kleink Exp $ */
+
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 1996-1997 Mark Brinicombe.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Mark Brinicombe.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $FreeBSD$ */
+
+#ifndef _ARM_SYSARCH_H_
+#define _ARM_SYSARCH_H_
+
+#include <machine/armreg.h>
+
+#ifndef LOCORE
+#ifndef __ASSEMBLER__
+
+/*
+ * Pickup definition of various __types.
+ */
+#include <sys/_types.h>
+
+/*
+ * Architecture specific syscalls (arm)
+ */
+
+#define ARM_SYNC_ICACHE 0
+#define ARM_DRAIN_WRITEBUF 1
+#define ARM_SET_TP 2
+#define ARM_GET_TP 3
+#define ARM_GET_VFPSTATE 4
+
+struct arm_sync_icache_args {
+ __uintptr_t addr; /* Virtual start address */
+ __size_t len; /* Region size */
+};
+
+struct arm_get_vfpstate_args {
+ __size_t mc_vfp_size;
+ void *mc_vfp;
+};
+
+#ifndef _KERNEL
+__BEGIN_DECLS
+int arm_sync_icache(unsigned int, int);
+int arm_drain_writebuf(void);
+int sysarch(int, void *);
+__END_DECLS
+#endif
+
+#endif /* __ASSEMBLER__ */
+#endif /* LOCORE */
+
+#endif /* !_ARM_SYSARCH_H_ */
diff --git a/sys/arm/include/sysreg.h b/sys/arm/include/sysreg.h
new file mode 100644
index 000000000000..ca8a1abdea3f
--- /dev/null
+++ b/sys/arm/include/sysreg.h
@@ -0,0 +1,325 @@
+/*-
+ * Copyright 2014 Svatopluk Kraus <onwahe@gmail.com>
+ * Copyright 2014 Michal Meloun <meloun@miracle.cz>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Macros to make working with the System Control Registers simpler.
+ *
+ * Note that when register r0 is hard-coded in these definitions it means the
+ * cp15 operation neither reads nor writes the register, and r0 is used only
+ * because some syntatically-valid register name has to appear at that point to
+ * keep the asm parser happy.
+ */
+
+#ifndef MACHINE_SYSREG_H
+#define MACHINE_SYSREG_H
+
+/*
+ * CP14 registers
+ */
+#define CP14_DBGDIDR(rr) p14, 0, rr, c0, c0, 0 /* Debug ID Register */
+#define CP14_DBGDSCRext_V6(rr) p14, 0, rr, c0, c1, 0 /* Debug Status and Ctrl Register v6 */
+#define CP14_DBGDSCRext_V7(rr) p14, 0, rr, c0, c2, 2 /* Debug Status and Ctrl Register v7 */
+#define CP14_DBGVCR(rr) p14, 0, rr, c0, c7, 0 /* Vector Catch Register */
+#define CP14_DBGOSLAR(rr) p14, 0, rr, c1, c0, 4 /* OS Lock Access Register */
+#define CP14_DBGOSLSR(rr) p14, 0, rr, c1, c1, 4 /* OS Lock Status Register */
+#define CP14_DBGOSDLR(rr) p14, 0, rr, c1, c3, 4 /* OS Double Lock Register */
+#define CP14_DBGPRSR(rr) p14, 0, rr, c1, c5, 4 /* Device Powerdown and Reset Status */
+
+#define CP14_DBGDSCRint(rr) CP14_DBGDSCRext_V6(rr) /* Debug Status and Ctrl internal view */
+
+/*
+ * CP15 C0 registers
+ */
+#define CP15_MIDR(rr) p15, 0, rr, c0, c0, 0 /* Main ID Register */
+#define CP15_CTR(rr) p15, 0, rr, c0, c0, 1 /* Cache Type Register */
+#define CP15_TCMTR(rr) p15, 0, rr, c0, c0, 2 /* TCM Type Register */
+#define CP15_TLBTR(rr) p15, 0, rr, c0, c0, 3 /* TLB Type Register */
+#define CP15_MPIDR(rr) p15, 0, rr, c0, c0, 5 /* Multiprocessor Affinity Register */
+#define CP15_REVIDR(rr) p15, 0, rr, c0, c0, 6 /* Revision ID Register */
+
+#define CP15_ID_PFR0(rr) p15, 0, rr, c0, c1, 0 /* Processor Feature Register 0 */
+#define CP15_ID_PFR1(rr) p15, 0, rr, c0, c1, 1 /* Processor Feature Register 1 */
+#define CP15_ID_DFR0(rr) p15, 0, rr, c0, c1, 2 /* Debug Feature Register 0 */
+#define CP15_ID_AFR0(rr) p15, 0, rr, c0, c1, 3 /* Auxiliary Feature Register 0 */
+#define CP15_ID_MMFR0(rr) p15, 0, rr, c0, c1, 4 /* Memory Model Feature Register 0 */
+#define CP15_ID_MMFR1(rr) p15, 0, rr, c0, c1, 5 /* Memory Model Feature Register 1 */
+#define CP15_ID_MMFR2(rr) p15, 0, rr, c0, c1, 6 /* Memory Model Feature Register 2 */
+#define CP15_ID_MMFR3(rr) p15, 0, rr, c0, c1, 7 /* Memory Model Feature Register 3 */
+
+#define CP15_ID_ISAR0(rr) p15, 0, rr, c0, c2, 0 /* Instruction Set Attribute Register 0 */
+#define CP15_ID_ISAR1(rr) p15, 0, rr, c0, c2, 1 /* Instruction Set Attribute Register 1 */
+#define CP15_ID_ISAR2(rr) p15, 0, rr, c0, c2, 2 /* Instruction Set Attribute Register 2 */
+#define CP15_ID_ISAR3(rr) p15, 0, rr, c0, c2, 3 /* Instruction Set Attribute Register 3 */
+#define CP15_ID_ISAR4(rr) p15, 0, rr, c0, c2, 4 /* Instruction Set Attribute Register 4 */
+#define CP15_ID_ISAR5(rr) p15, 0, rr, c0, c2, 5 /* Instruction Set Attribute Register 5 */
+
+#define CP15_CCSIDR(rr) p15, 1, rr, c0, c0, 0 /* Cache Size ID Registers */
+#define CP15_CLIDR(rr) p15, 1, rr, c0, c0, 1 /* Cache Level ID Register */
+#define CP15_AIDR(rr) p15, 1, rr, c0, c0, 7 /* Auxiliary ID Register */
+
+#define CP15_CSSELR(rr) p15, 2, rr, c0, c0, 0 /* Cache Size Selection Register */
+
+#define CP15_VPIDR(rr) p15, 4, rr, c0, c0, 0 /* Virtualization Processor ID Register */
+#define CP15_VMPIDR(rr) p15, 4, rr, c0, c0, 5 /* Virtualization Multiprocessor ID Register */
+
+/*
+ * CP15 C1 registers
+ */
+#define CP15_SCTLR(rr) p15, 0, rr, c1, c0, 0 /* System Control Register */
+#define CP15_ACTLR(rr) p15, 0, rr, c1, c0, 1 /* IMPLEMENTATION DEFINED Auxiliary Control Register */
+#define CP15_CPACR(rr) p15, 0, rr, c1, c0, 2 /* Coprocessor Access Control Register */
+
+#define CP15_SCR(rr) p15, 0, rr, c1, c1, 0 /* Secure Configuration Register */
+#define CP15_SDER(rr) p15, 0, rr, c1, c1, 1 /* Secure Debug Enable Register */
+#define CP15_NSACR(rr) p15, 0, rr, c1, c1, 2 /* Non-Secure Access Control Register */
+
+#define CP15_HSCTLR(rr) p15, 4, rr, c1, c0, 0 /* Hyp System Control Register */
+
+#define CP15_HCR(rr) p15, 4, rr, c1, c1, 0 /* Hyp Configuration Register */
+#define CP15_HDCR(rr) p15, 4, rr, c1, c1, 1 /* Hyp Debug Configuration Register */
+#define CP15_HCPTR(rr) p15, 4, rr, c1, c1, 2 /* Hyp Coprocessor Trap Register */
+#define CP15_HSTR(rr) p15, 4, rr, c1, c1, 3 /* Hyp System Trap Register */
+
+/*
+ * CP15 C2 registers
+ */
+#define CP15_TTBR0(rr) p15, 0, rr, c2, c0, 0 /* Translation Table Base Register 0 */
+#define CP15_TTBR1(rr) p15, 0, rr, c2, c0, 1 /* Translation Table Base Register 1 */
+#define CP15_TTBCR(rr) p15, 0, rr, c2, c0, 2 /* Translation Table Base Control Register */
+
+#define CP15_HTCR(rr) p15, 4, rr, c2, c0, 2 /* Hyp Translation Control Register */
+#define CP15_VTCR(rr) p15, 4, rr, c2, c1, 2 /* Virtualization Translation Control Register */
+
+/*
+ * CP15 C3 registers
+ */
+#define CP15_DACR(rr) p15, 0, rr, c3, c0, 0 /* Domain Access Control Register */
+
+/*
+ * CP15 C5 registers
+ */
+#define CP15_DFSR(rr) p15, 0, rr, c5, c0, 0 /* Data Fault Status Register */
+#define CP15_HSR(rr) p15, 4, rr, c5, c2, 0 /* Hyp Syndrome Register */
+
+/* From ARMv6: */
+#define CP15_IFSR(rr) p15, 0, rr, c5, c0, 1 /* Instruction Fault Status Register */
+#if __ARM_ARCH >= 7
+/* From ARMv7: */
+#define CP15_ADFSR(rr) p15, 0, rr, c5, c1, 0 /* Auxiliary Data Fault Status Register */
+#define CP15_AIFSR(rr) p15, 0, rr, c5, c1, 1 /* Auxiliary Instruction Fault Status Register */
+#endif
+
+/*
+ * CP15 C6 registers
+ */
+#define CP15_DFAR(rr) p15, 0, rr, c6, c0, 0 /* Data Fault Address Register */
+#define CP15_HDFAR(rr) p15, 4, rr, c6, c0, 0 /* Hyp Data Fault Address Register */
+#define CP15_HIFAR(rr) p15, 4, rr, c6, c0, 2 /* Hyp Instruction Fault Address Register */
+#define CP15_HPFAR(rr) p15, 4, rr, c6, c0, 4 /* Hyp IPA Fault Address Register */
+
+/* From ARMv6k: */
+#define CP15_IFAR(rr) p15, 0, rr, c6, c0, 2 /* Instruction Fault Address Register */
+
+/*
+ * CP15 C7 registers
+ */
+#if __ARM_ARCH >= 7 && defined(SMP)
+/* From ARMv7: */
+#define CP15_ICIALLUIS p15, 0, r0, c7, c1, 0 /* Instruction cache invalidate all PoU, IS */
+#define CP15_BPIALLIS p15, 0, r0, c7, c1, 6 /* Branch predictor invalidate all IS */
+#endif
+
+#define CP15_PAR(rr) p15, 0, rr, c7, c4, 0 /* Physical Address Register */
+
+#define CP15_ICIALLU p15, 0, r0, c7, c5, 0 /* Instruction cache invalidate all PoU */
+#define CP15_ICIMVAU(rr) p15, 0, rr, c7, c5, 1 /* Instruction cache invalidate */
+#if __ARM_ARCH == 6
+/* Deprecated in ARMv7 */
+#define CP15_CP15ISB p15, 0, r0, c7, c5, 4 /* ISB */
+#endif
+#define CP15_BPIALL p15, 0, r0, c7, c5, 6 /* Branch predictor invalidate all */
+#define CP15_BPIMVA p15, 0, rr, c7, c5, 7 /* Branch predictor invalidate by MVA */
+
+#if __ARM_ARCH == 6
+/* Only ARMv6: */
+#define CP15_DCIALL p15, 0, r0, c7, c6, 0 /* Data cache invalidate all */
+#endif
+#define CP15_DCIMVAC(rr) p15, 0, rr, c7, c6, 1 /* Data cache invalidate by MVA PoC */
+#define CP15_DCISW(rr) p15, 0, rr, c7, c6, 2 /* Data cache invalidate by set/way */
+
+#define CP15_ATS1CPR(rr) p15, 0, rr, c7, c8, 0 /* Stage 1 Current state PL1 read */
+#define CP15_ATS1CPW(rr) p15, 0, rr, c7, c8, 1 /* Stage 1 Current state PL1 write */
+#define CP15_ATS1CUR(rr) p15, 0, rr, c7, c8, 2 /* Stage 1 Current state unprivileged read */
+#define CP15_ATS1CUW(rr) p15, 0, rr, c7, c8, 3 /* Stage 1 Current state unprivileged write */
+
+#if __ARM_ARCH >= 7
+/* From ARMv7: */
+#define CP15_ATS12NSOPR(rr) p15, 0, rr, c7, c8, 4 /* Stages 1 and 2 Non-secure only PL1 read */
+#define CP15_ATS12NSOPW(rr) p15, 0, rr, c7, c8, 5 /* Stages 1 and 2 Non-secure only PL1 write */
+#define CP15_ATS12NSOUR(rr) p15, 0, rr, c7, c8, 6 /* Stages 1 and 2 Non-secure only unprivileged read */
+#define CP15_ATS12NSOUW(rr) p15, 0, rr, c7, c8, 7 /* Stages 1 and 2 Non-secure only unprivileged write */
+#endif
+
+#if __ARM_ARCH == 6
+/* Only ARMv6: */
+#define CP15_DCCALL p15, 0, r0, c7, c10, 0 /* Data cache clean all */
+#endif
+#define CP15_DCCMVAC(rr) p15, 0, rr, c7, c10, 1 /* Data cache clean by MVA PoC */
+#define CP15_DCCSW(rr) p15, 0, rr, c7, c10, 2 /* Data cache clean by set/way */
+#if __ARM_ARCH == 6
+/* Only ARMv6: */
+#define CP15_CP15DSB p15, 0, r0, c7, c10, 4 /* DSB */
+#define CP15_CP15DMB p15, 0, r0, c7, c10, 5 /* DMB */
+#define CP15_CP15WFI p15, 0, r0, c7, c0, 4 /* WFI */
+#endif
+
+#if __ARM_ARCH >= 7
+/* From ARMv7: */
+#define CP15_DCCMVAU(rr) p15, 0, rr, c7, c11, 1 /* Data cache clean by MVA PoU */
+#endif
+
+#if __ARM_ARCH == 6
+/* Only ARMv6: */
+#define CP15_DCCIALL p15, 0, r0, c7, c14, 0 /* Data cache clean and invalidate all */
+#endif
+#define CP15_DCCIMVAC(rr) p15, 0, rr, c7, c14, 1 /* Data cache clean and invalidate by MVA PoC */
+#define CP15_DCCISW(rr) p15, 0, rr, c7, c14, 2 /* Data cache clean and invalidate by set/way */
+
+/*
+ * CP15 C8 registers
+ */
+#if __ARM_ARCH >= 7 && defined(SMP)
+/* From ARMv7: */
+#define CP15_TLBIALLIS p15, 0, r0, c8, c3, 0 /* Invalidate entire unified TLB IS */
+#define CP15_TLBIMVAIS(rr) p15, 0, rr, c8, c3, 1 /* Invalidate unified TLB by MVA IS */
+#define CP15_TLBIASIDIS(rr) p15, 0, rr, c8, c3, 2 /* Invalidate unified TLB by ASID IS */
+#define CP15_TLBIMVAAIS(rr) p15, 0, rr, c8, c3, 3 /* Invalidate unified TLB by MVA, all ASID IS */
+#endif
+
+#define CP15_TLBIALL p15, 0, r0, c8, c7, 0 /* Invalidate entire unified TLB */
+#define CP15_TLBIMVA(rr) p15, 0, rr, c8, c7, 1 /* Invalidate unified TLB by MVA */
+#define CP15_TLBIASID(rr) p15, 0, rr, c8, c7, 2 /* Invalidate unified TLB by ASID */
+
+#define CP15_TLBIALLH(rr) p15, 4, rr, c8, c7, 0 /* Invalidate Entire Hyp Unified TLB */
+
+/* From ARMv6: */
+#define CP15_TLBIMVAA(rr) p15, 0, rr, c8, c7, 3 /* Invalidate unified TLB by MVA, all ASID */
+
+/*
+ * CP15 C9 registers
+ */
+#if __ARM_ARCH == 6 && defined(CPU_ARM1176)
+#define CP15_PMUSERENR(rr) p15, 0, rr, c15, c9, 0 /* Access Validation Control Register */
+#define CP15_PMCR(rr) p15, 0, rr, c15, c12, 0 /* Performance Monitor Control Register */
+#define CP15_PMCCNTR(rr) p15, 0, rr, c15, c12, 1 /* PM Cycle Count Register */
+#else
+#define CP15_L2CTLR(rr) p15, 1, rr, c9, c0, 2 /* L2 Control Register */
+#define CP15_PMCR(rr) p15, 0, rr, c9, c12, 0 /* Performance Monitor Control Register */
+#define CP15_PMCNTENSET(rr) p15, 0, rr, c9, c12, 1 /* PM Count Enable Set Register */
+#define CP15_PMCNTENCLR(rr) p15, 0, rr, c9, c12, 2 /* PM Count Enable Clear Register */
+#define CP15_PMOVSR(rr) p15, 0, rr, c9, c12, 3 /* PM Overflow Flag Status Register */
+#define CP15_PMSWINC(rr) p15, 0, rr, c9, c12, 4 /* PM Software Increment Register */
+#define CP15_PMSELR(rr) p15, 0, rr, c9, c12, 5 /* PM Event Counter Selection Register */
+#define CP15_PMCCNTR(rr) p15, 0, rr, c9, c13, 0 /* PM Cycle Count Register */
+#define CP15_PMXEVTYPER(rr) p15, 0, rr, c9, c13, 1 /* PM Event Type Select Register */
+#define CP15_PMXEVCNTRR(rr) p15, 0, rr, c9, c13, 2 /* PM Event Count Register */
+#define CP15_PMUSERENR(rr) p15, 0, rr, c9, c14, 0 /* PM User Enable Register */
+#define CP15_PMINTENSET(rr) p15, 0, rr, c9, c14, 1 /* PM Interrupt Enable Set Register */
+#define CP15_PMINTENCLR(rr) p15, 0, rr, c9, c14, 2 /* PM Interrupt Enable Clear Register */
+#endif
+
+/*
+ * CP15 C10 registers
+ */
+/* Without LPAE this is PRRR, with LPAE it's MAIR0 */
+#define CP15_PRRR(rr) p15, 0, rr, c10, c2, 0 /* Primary Region Remap Register */
+#define CP15_MAIR0(rr) p15, 0, rr, c10, c2, 0 /* Memory Attribute Indirection Register 0 */
+/* Without LPAE this is NMRR, with LPAE it's MAIR1 */
+#define CP15_NMRR(rr) p15, 0, rr, c10, c2, 1 /* Normal Memory Remap Register */
+#define CP15_MAIR1(rr) p15, 0, rr, c10, c2, 1 /* Memory Attribute Indirection Register 1 */
+
+#define CP15_AMAIR0(rr) p15, 0, rr, c10, c3, 0 /* Auxiliary Memory Attribute Indirection Register 0 */
+#define CP15_AMAIR1(rr) p15, 0, rr, c10, c3, 1 /* Auxiliary Memory Attribute Indirection Register 1 */
+
+#define CP15_HMAIR0(rr) p15, 4, rr, c10, c2, 0 /* Hyp Memory Attribute Indirection Register 0 */
+#define CP15_HMAIR1(rr) p15, 4, rr, c10, c2, 1 /* Hyp Memory Attribute Indirection Register 1 */
+
+/*
+ * CP15 C12 registers
+ */
+#define CP15_VBAR(rr) p15, 0, rr, c12, c0, 0 /* Vector Base Address Register */
+#define CP15_MVBAR(rr) p15, 0, rr, c12, c0, 1 /* Monitor Vector Base Address Register */
+
+#define CP15_ISR(rr) p15, 0, rr, c12, c1, 0 /* Interrupt Status Register */
+#define CP15_HVBAR(rr) p15, 4, rr, c12, c0, 0 /* Hyp Vector Base Address Register*/
+
+/*
+ * CP15 C13 registers
+ */
+#define CP15_FCSEIDR(rr) p15, 0, rr, c13, c0, 0 /* FCSE Process ID Register */
+#define CP15_CONTEXTIDR(rr) p15, 0, rr, c13, c0, 1 /* Context ID Register */
+#define CP15_TPIDRURW(rr) p15, 0, rr, c13, c0, 2 /* User Read/Write Thread ID Register */
+#define CP15_TPIDRURO(rr) p15, 0, rr, c13, c0, 3 /* User Read-Only Thread ID Register */
+#define CP15_TPIDRPRW(rr) p15, 0, rr, c13, c0, 4 /* PL1 only Thread ID Register */
+#define CP15_HTPIDR(rr) p15, 4, rr, c13, c0, 2 /* Hyp Software Thread ID Register */
+
+/*
+ * CP15 C14 registers
+ * These are the Generic Timer registers and may be unallocated on some SoCs.
+ * Only use these when you know the Generic Timer is available.
+ */
+#define CP15_CNTFRQ(rr) p15, 0, rr, c14, c0, 0 /* Counter Frequency Register */
+#define CP15_CNTKCTL(rr) p15, 0, rr, c14, c1, 0 /* Timer PL1 Control Register */
+#define CP15_CNTP_TVAL(rr) p15, 0, rr, c14, c2, 0 /* PL1 Physical Timer Value Register */
+#define CP15_CNTP_CTL(rr) p15, 0, rr, c14, c2, 1 /* PL1 Physical Timer Control Register */
+#define CP15_CNTV_TVAL(rr) p15, 0, rr, c14, c3, 0 /* Virtual Timer Value Register */
+#define CP15_CNTV_CTL(rr) p15, 0, rr, c14, c3, 1 /* Virtual Timer Control Register */
+#define CP15_CNTHCTL(rr) p15, 4, rr, c14, c1, 0 /* Timer PL2 Control Register */
+#define CP15_CNTHP_TVAL(rr) p15, 4, rr, c14, c2, 0 /* PL2 Physical Timer Value Register */
+#define CP15_CNTHP_CTL(rr) p15, 4, rr, c14, c2, 1 /* PL2 Physical Timer Control Register */
+/* 64-bit registers for use with mcrr/mrrc */
+#define CP15_CNTPCT(rq, rr) p15, 0, rq, rr, c14 /* Physical Count Register */
+#define CP15_CNTVCT(rq, rr) p15, 1, rq, rr, c14 /* Virtual Count Register */
+#define CP15_CNTP_CVAL(rq, rr) p15, 2, rq, rr, c14 /* PL1 Physical Timer Compare Value Register */
+#define CP15_CNTV_CVAL(rq, rr) p15, 3, rq, rr, c14 /* Virtual Timer Compare Value Register */
+#define CP15_CNTVOFF(rq, rr) p15, 4, rq, rr, c14 /* Virtual Offset Register */
+#define CP15_CNTHP_CVAL(rq, rr) p15, 6, rq, rr, c14 /* PL2 Physical Timer Compare Value Register */
+
+#define CP15_VTTBR(rq, rr) p15, 6, rq, rr, c2 /* Virtualization Translation Table Base Register */
+#define CP15_HTTBR(rq, rr) p15, 4, rq, rr, c2 /* Hyp Translation Table Base Register */
+#define CP15_TTBR0_2(rq, rr) p15, 0, rq, rr, c2 /* Translation Table Base Register 0 */
+#define CP15_TTBR1_2(rq, rr) p15, 1, rq, rr, c2 /* Translation Table Base Register 1 */
+#define CP15_PAR_2(rq, rr) p15, 0, rq, rr, c7 /* Physical Address Register */
+
+/*
+ * CP15 C15 registers
+ */
+#define CP15_CBAR(rr) p15, 4, rr, c15, c0, 0 /* Configuration Base Address Register */
+
+#endif /* !MACHINE_SYSREG_H */
diff --git a/sys/arm/include/trap.h b/sys/arm/include/trap.h
new file mode 100644
index 000000000000..ae1c571255f5
--- /dev/null
+++ b/sys/arm/include/trap.h
@@ -0,0 +1,11 @@
+/* $NetBSD: trap.h,v 1.1 2001/02/23 03:48:19 ichiro Exp $ */
+/* $FreeBSD$ */
+
+#ifndef _MACHINE_TRAP_H_
+#define _MACHINE_TRAP_H_
+#define GDB_BREAKPOINT 0xe6000011
+#define GDB5_BREAKPOINT 0xe7ffdefe
+#define PTRACE_BREAKPOINT 0xe7fffff0
+#define KERNEL_BREAKPOINT 0xe7ffffff
+#define FBT_BREAKPOINT 0xe7f000f0
+#endif /* _MACHINE_TRAP_H_ */
diff --git a/sys/arm/include/ucontext.h b/sys/arm/include/ucontext.h
new file mode 100644
index 000000000000..2fdc5b68230b
--- /dev/null
+++ b/sys/arm/include/ucontext.h
@@ -0,0 +1,89 @@
+/* $NetBSD: mcontext.h,v 1.4 2003/10/08 22:43:01 thorpej Exp $ */
+
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-NetBSD
+ *
+ * Copyright (c) 2001, 2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Klaus Klein and by Jason R. Thorpe of Wasabi Systems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_MCONTEXT_H_
+#define _MACHINE_MCONTEXT_H_
+/*
+ * General register state
+ */
+#define _NGREG 17
+typedef unsigned int __greg_t;
+typedef __greg_t __gregset_t[_NGREG];
+
+#define _REG_R0 0
+#define _REG_R1 1
+#define _REG_R2 2
+#define _REG_R3 3
+#define _REG_R4 4
+#define _REG_R5 5
+#define _REG_R6 6
+#define _REG_R7 7
+#define _REG_R8 8
+#define _REG_R9 9
+#define _REG_R10 10
+#define _REG_R11 11
+#define _REG_R12 12
+#define _REG_R13 13
+#define _REG_R14 14
+#define _REG_R15 15
+#define _REG_CPSR 16
+/* Convenience synonyms */
+#define _REG_FP _REG_R11
+#define _REG_SP _REG_R13
+#define _REG_LR _REG_R14
+#define _REG_PC _REG_R15
+
+/*
+ * Floating point register state
+ */
+typedef struct {
+ __uint64_t mcv_reg[32];
+ __uint32_t mcv_fpscr;
+} mcontext_vfp_t;
+
+typedef struct {
+ __gregset_t __gregs;
+
+ /*
+ * Originally, rest of this structure was named __fpu, 35 * 4 bytes
+ * long, never accessed from kernel.
+ */
+ __size_t mc_vfp_size;
+ void *mc_vfp_ptr;
+ unsigned int mc_spare[33];
+} mcontext_t;
+
+#define UC_
+#endif /* !_MACHINE_MCONTEXT_H_ */
diff --git a/sys/arm/include/undefined.h b/sys/arm/include/undefined.h
new file mode 100644
index 000000000000..139448be4c0e
--- /dev/null
+++ b/sys/arm/include/undefined.h
@@ -0,0 +1,93 @@
+/* $NetBSD: undefined.h,v 1.4 2001/12/20 01:20:23 thorpej Exp $ */
+
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 1995-1996 Mark Brinicombe.
+ * Copyright (c) 1995 Brini.
+ * All rights reserved.
+ *
+ * This code is derived from software written for Brini by Mark Brinicombe
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Brini.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * RiscBSD kernel project
+ *
+ * undefined.h
+ *
+ * Undefined instruction types, symbols and prototypes
+ *
+ * Created : 08/02/95
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_UNDEFINED_H_
+#define _MACHINE_UNDEFINED_H_
+#ifdef _KERNEL
+
+#include <sys/queue.h>
+
+struct trapframe;
+
+typedef int (*undef_handler_t) (unsigned int, unsigned int, struct trapframe *, int);
+
+#define FP_COPROC 1
+#define FP_COPROC2 2
+#define MAX_COPROCS 16
+
+/* Prototypes for undefined.c */
+
+void *install_coproc_handler (int, undef_handler_t);
+void remove_coproc_handler (void *);
+void undefined_init (void);
+
+/*
+ * XXX Stuff below here is for use before malloc() is available. Most code
+ * shouldn't use it.
+ */
+
+struct undefined_handler {
+ LIST_ENTRY(undefined_handler) uh_link;
+ undef_handler_t uh_handler;
+};
+
+/*
+ * Handlers installed using install_coproc_handler_static shouldn't be
+ * removed.
+ */
+void install_coproc_handler_static (int, struct undefined_handler *);
+
+/* Calls up to undefined.c from trap handlers */
+void undefinedinstruction(struct trapframe *);
+
+#endif
+
+/* End of undefined.h */
+
+#endif /* _MACHINE_UNDEFINED_H_ */
diff --git a/sys/arm/include/utrap.h b/sys/arm/include/utrap.h
new file mode 100644
index 000000000000..b261d87d566f
--- /dev/null
+++ b/sys/arm/include/utrap.h
@@ -0,0 +1,112 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2001 Jake Burkholder.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_UTRAP_H_
+#define _MACHINE_UTRAP_H_
+
+#define UT_INSTRUCTION_EXCEPTION 1
+#define UT_INSTRUCTION_ERROR 2
+#define UT_INSTRUCTION_PROTECTION 3
+#define UT_ILLTRAP_INSTRUCTION 4
+#define UT_ILLEGAL_INSTRUCTION 5
+#define UT_PRIVILEGED_OPCODE 6
+#define UT_FP_DISABLED 7
+#define UT_FP_EXCEPTION_IEEE_754 8
+#define UT_FP_EXCEPTION_OTHER 9
+#define UT_TAG_OFERFLOW 10
+#define UT_DIVISION_BY_ZERO 11
+#define UT_DATA_EXCEPTION 12
+#define UT_DATA_ERROR 13
+#define UT_DATA_PROTECTION 14
+#define UT_MEM_ADDRESS_NOT_ALIGNED 15
+#define UT_PRIVILEGED_ACTION 16
+#define UT_ASYNC_DATA_ERROR 17
+#define UT_TRAP_INSTRUCTION_16 18
+#define UT_TRAP_INSTRUCTION_17 19
+#define UT_TRAP_INSTRUCTION_18 20
+#define UT_TRAP_INSTRUCTION_19 21
+#define UT_TRAP_INSTRUCTION_20 22
+#define UT_TRAP_INSTRUCTION_21 23
+#define UT_TRAP_INSTRUCTION_22 24
+#define UT_TRAP_INSTRUCTION_23 25
+#define UT_TRAP_INSTRUCTION_24 26
+#define UT_TRAP_INSTRUCTION_25 27
+#define UT_TRAP_INSTRUCTION_26 28
+#define UT_TRAP_INSTRUCTION_27 29
+#define UT_TRAP_INSTRUCTION_28 30
+#define UT_TRAP_INSTRUCTION_29 31
+#define UT_TRAP_INSTRUCTION_30 32
+#define UT_TRAP_INSTRUCTION_31 33
+#define UT_INSTRUCTION_MISS 34
+#define UT_DATA_MISS 35
+#define UT_MAX 36
+
+#define ST_SUNOS_SYSCALL 0
+#define ST_BREAKPOINT 1
+#define ST_DIVISION_BY_ZERO 2
+#define ST_FLUSH_WINDOWS 3 /* XXX implement! */
+#define ST_CLEAN_WINDOW 4
+#define ST_RANGE_CHECK 5
+#define ST_FIX_ALIGNMENT 6
+#define ST_INTEGER_OVERFLOW 7
+/* 8 is 32-bit ABI syscall (old solaris syscall?) */
+#define ST_BSD_SYSCALL 9
+#define ST_FP_RESTORE 10
+/* 11-15 are available */
+/* 16 is linux 32 bit syscall (but supposed to be reserved, grr) */
+/* 17 is old linux 64 bit syscall (but supposed to be reserved, grr) */
+/* 16-31 are reserved for user applications (utraps) */
+#define ST_GETCC 32 /* XXX implement! */
+#define ST_SETCC 33 /* XXX implement! */
+#define ST_GETPSR 34 /* XXX implement! */
+#define ST_SETPSR 35 /* XXX implement! */
+/* 36-63 are available */
+#define ST_SOLARIS_SYSCALL 64
+#define ST_SYSCALL 65
+#define ST_SYSCALL32 66
+/* 67 is reserved to OS source licensee */
+/* 68 is return from deferred trap (not supported) */
+/* 69-95 are reserved to SPARC international */
+/* 96-108 are available */
+/* 109 is linux 64 bit syscall */
+/* 110 is linux 64 bit getcontext (?) */
+/* 111 is linux 64 bit setcontext (?) */
+/* 112-255 are available */
+
+#define UTH_NOCHANGE (-1)
+
+#ifndef __ASM__
+
+typedef int utrap_entry_t;
+typedef void *utrap_handler_t;
+
+#endif
+
+#endif
diff --git a/sys/arm/include/vdso.h b/sys/arm/include/vdso.h
new file mode 100644
index 000000000000..8d805bba0d88
--- /dev/null
+++ b/sys/arm/include/vdso.h
@@ -0,0 +1,39 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright 2012 Konstantin Belousov <kib@FreeBSD.ORG>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _ARM_VDSO_H
+#define _ARM_VDSO_H
+
+#define VDSO_TIMEHANDS_MD \
+ uint32_t th_physical; \
+ uint32_t th_res[7];
+
+#define VDSO_TH_ALGO_ARM_GENTIM VDSO_TH_ALGO_1
+
+#endif
diff --git a/sys/arm/include/vfp.h b/sys/arm/include/vfp.h
new file mode 100644
index 000000000000..b9cc6efb9589
--- /dev/null
+++ b/sys/arm/include/vfp.h
@@ -0,0 +1,160 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Mark Tinguely
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE__VFP_H_
+#define _MACHINE__VFP_H_
+
+/* fpsid, fpscr, fpexc are defined in the newer gas */
+#define VFPSID cr0
+#define VFPSCR cr1
+#define VMVFR1 cr6
+#define VMVFR0 cr7
+#define VFPEXC cr8
+#define VFPINST cr9 /* vfp 1 and 2 except instruction */
+#define VFPINST2 cr10 /* vfp 2? */
+
+/* VFPSID */
+#define VFPSID_IMPLEMENTOR_OFF 24
+#define VFPSID_IMPLEMENTOR_MASK (0xff000000)
+#define VFPSID_HARDSOFT_IMP (0x00800000)
+#define VFPSID_SINGLE_PREC 20 /* version 1 and 2 */
+#define VFPSID_SUBVERSION_OFF 16
+#define VFPSID_SUBVERSION2_MASK (0x000f0000) /* version 1 and 2 */
+#define VFPSID_SUBVERSION3_MASK (0x007f0000) /* version 3 */
+#define VFP_ARCH1 0x0
+#define VFP_ARCH2 0x1
+#define VFP_ARCH3 0x2
+#define VFPSID_PARTNUMBER_OFF 8
+#define VFPSID_PARTNUMBER_MASK (0x0000ff00)
+#define VFPSID_VARIANT_OFF 4
+#define VFPSID_VARIANT_MASK (0x000000f0)
+#define VFPSID_REVISION_MASK 0x0f
+
+/* VFPSCR */
+#define VFPSCR_CC_N (0x80000000) /* comparison less than */
+#define VFPSCR_CC_Z (0x40000000) /* comparison equal */
+#define VFPSCR_CC_C (0x20000000) /* comparison = > unordered */
+#define VFPSCR_CC_V (0x10000000) /* comparison unordered */
+#define VFPSCR_QC (0x08000000) /* saturation cululative */
+#define VFPSCR_DN (0x02000000) /* default NaN enable */
+#define VFPSCR_FZ (0x01000000) /* flush to zero enabled */
+
+#define VFPSCR_RMODE_OFF 22 /* rounding mode offset */
+#define VFPSCR_RMODE_MASK (0x00c00000) /* rounding mode mask */
+#define VFPSCR_RMODE_RN (0x00000000) /* round nearest */
+#define VFPSCR_RMODE_RPI (0x00400000) /* round to plus infinity */
+#define VFPSCR_RMODE_RNI (0x00800000) /* round to neg infinity */
+#define VFPSCR_RMODE_RM (0x00c00000) /* round to zero */
+
+#define VFPSCR_STRIDE_OFF 20 /* vector stride -1 */
+#define VFPSCR_STRIDE_MASK (0x00300000)
+#define VFPSCR_LEN_OFF 16 /* vector length -1 */
+#define VFPSCR_LEN_MASK (0x00070000)
+#define VFPSCR_IDE (0x00008000) /* input subnormal exc enable */
+#define VFPSCR_IXE (0x00001000) /* inexact exception enable */
+#define VFPSCR_UFE (0x00000800) /* underflow exception enable */
+#define VFPSCR_OFE (0x00000400) /* overflow exception enable */
+#define VFPSCR_DNZ (0x00000200) /* div by zero exception en */
+#define VFPSCR_IOE (0x00000100) /* invalid op exec enable */
+#define VFPSCR_IDC (0x00000080) /* input subnormal cumul */
+#define VFPSCR_IXC (0x00000010) /* Inexact cumulative flag */
+#define VFPSCR_UFC (0x00000008) /* underflow cumulative flag */
+#define VFPSCR_OFC (0x00000004) /* overflow cumulative flag */
+#define VFPSCR_DZC (0x00000002) /* division by zero flag */
+#define VFPSCR_IOC (0x00000001) /* invalid operation cumul */
+
+/* VFPEXC */
+#define VFPEXC_EX (0x80000000) /* exception v1 v2 */
+#define VFPEXC_EN (0x40000000) /* vfp enable */
+#define VFPEXC_DEX (0x20000000) /* Synchronous exception */
+#define VFPEXC_FP2V (0x10000000) /* FPINST2 valid */
+#define VFPEXC_INV (0x00000080) /* Input exception */
+#define VFPEXC_UFC (0x00000008) /* Underflow exception */
+#define VFPEXC_OFC (0x00000004) /* Overflow exception */
+#define VFPEXC_IOC (0x00000001) /* Invlaid operation */
+
+/* version 3 registers */
+/* VMVFR0 */
+#define VMVFR0_RM_OFF 28
+#define VMVFR0_RM_MASK (0xf0000000) /* VFP rounding modes */
+
+#define VMVFR0_SV_OFF 24
+#define VMVFR0_SV_MASK (0x0f000000) /* VFP short vector supp */
+#define VMVFR0_SR_OFF 20
+#define VMVFR0_SR (0x00f00000) /* VFP hw sqrt supp */
+#define VMVFR0_D_OFF 16
+#define VMVFR0_D_MASK (0x000f0000) /* VFP divide supp */
+#define VMVFR0_TE_OFF 12
+#define VMVFR0_TE_MASK (0x0000f000) /* VFP trap exception supp */
+#define VMVFR0_DP_OFF 8
+#define VMVFR0_DP_MASK (0x00000f00) /* VFP double prec support */
+#define VMVFR0_SP_OFF 4
+#define VMVFR0_SP_MASK (0x000000f0) /* VFP single prec support */
+#define VMVFR0_RB_MASK (0x0000000f) /* VFP 64 bit media support */
+
+/* VMVFR1 */
+#define VMVFR1_FMAC_OFF 28
+#define VMVFR1_FMAC_MASK (0xf0000000) /* Neon FMAC support */
+#define VMVFR1_VFP_HP_OFF 24
+#define VMVFR1_VFP_HP_MASK (0x0f000000) /* VFP half prec support */
+#define VMVFR1_HP_OFF 20
+#define VMVFR1_HP_MASK (0x00f00000) /* Neon half prec support */
+#define VMVFR1_SP_OFF 16
+#define VMVFR1_SP_MASK (0x000f0000) /* Neon single prec support */
+#define VMVFR1_I_OFF 12
+#define VMVFR1_I_MASK (0x0000f000) /* Neon integer support */
+#define VMVFR1_LS_OFF 8
+#define VMVFR1_LS_MASK (0x00000f00) /* Neon ld/st instr support */
+#define VMVFR1_DN_OFF 4
+#define VMVFR1_DN_MASK (0x000000f0) /* Neon prop NaN support */
+#define VMVFR1_FZ_MASK (0x0000000f) /* Neon denormal arith supp */
+
+#define COPROC10 (0x3 << 20)
+#define COPROC11 (0x3 << 22)
+
+#ifndef LOCORE
+struct vfp_state {
+ uint64_t reg[32];
+ uint32_t fpscr;
+ uint32_t fpexec;
+ uint32_t fpinst;
+ uint32_t fpinst2;
+};
+
+#ifdef _KERNEL
+void get_vfpcontext(struct thread *, mcontext_vfp_t *);
+void set_vfpcontext(struct thread *, mcontext_vfp_t *);
+void vfp_init(void);
+void vfp_store(struct vfp_state *, boolean_t);
+void vfp_discard(struct thread *);
+#endif /* _KERNEL */
+#endif /* LOCORE */
+
+#endif
diff --git a/sys/arm/include/vm.h b/sys/arm/include/vm.h
new file mode 100644
index 000000000000..b9f72fe9a5f7
--- /dev/null
+++ b/sys/arm/include/vm.h
@@ -0,0 +1,48 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2009 Alan L. Cox <alc@cs.rice.edu>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_VM_H_
+#define _MACHINE_VM_H_
+
+#define VM_MEMATTR_WB_WA ((vm_memattr_t)0)
+#define VM_MEMATTR_NOCACHE ((vm_memattr_t)1)
+#define VM_MEMATTR_DEVICE ((vm_memattr_t)2)
+#define VM_MEMATTR_SO ((vm_memattr_t)3)
+#define VM_MEMATTR_WRITE_THROUGH ((vm_memattr_t)4)
+
+#define VM_MEMATTR_DEFAULT VM_MEMATTR_WB_WA
+#define VM_MEMATTR_UNCACHEABLE VM_MEMATTR_SO /* misused by DMA */
+#ifdef _KERNEL
+/* Don't export aliased VM_MEMATTR to userland */
+#define VM_MEMATTR_WRITE_COMBINING VM_MEMATTR_WRITE_THROUGH /* for DRM */
+#define VM_MEMATTR_WRITE_BACK VM_MEMATTR_WB_WA /* for DRM */
+#endif
+
+#endif /* !_MACHINE_VM_H_ */
diff --git a/sys/arm/include/vmparam.h b/sys/arm/include/vmparam.h
new file mode 100644
index 000000000000..75c61fff803a
--- /dev/null
+++ b/sys/arm/include/vmparam.h
@@ -0,0 +1,206 @@
+/* $NetBSD: vmparam.h,v 1.26 2003/08/07 16:27:47 agc Exp $ */
+
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1988 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_VMPARAM_H_
+#define _MACHINE_VMPARAM_H_
+
+/*
+ * Machine dependent constants for ARM.
+ */
+
+/*
+ * Virtual memory related constants, all in bytes
+ */
+#ifndef MAXTSIZ
+#define MAXTSIZ (256UL*1024*1024) /* max text size */
+#endif
+#ifndef DFLDSIZ
+#define DFLDSIZ (128UL*1024*1024) /* initial data size limit */
+#endif
+#ifndef MAXDSIZ
+#define MAXDSIZ (512UL*1024*1024) /* max data size */
+#endif
+#ifndef DFLSSIZ
+#define DFLSSIZ (4UL*1024*1024) /* initial stack size limit */
+#endif
+#ifndef MAXSSIZ
+#define MAXSSIZ (64UL*1024*1024) /* max stack size */
+#endif
+#ifndef SGROWSIZ
+#define SGROWSIZ (128UL*1024) /* amount to grow stack */
+#endif
+
+/*
+ * Address space constants
+ */
+
+/*
+ * The line between user space and kernel space
+ * Mappings >= KERNEL_BASE are constant across all processes
+ */
+#ifndef KERNBASE
+#define KERNBASE 0xc0000000
+#endif
+
+/*
+ * The virtual address the kernel is linked to run at. For armv4/5 platforms
+ * the low-order 30 bits of this must match the low-order bits of the physical
+ * address the kernel is loaded at, so the value is most often provided as a
+ * kernel config option in the std.platform file. For armv6/7 the kernel can
+ * be loaded at any 2MB boundary, and KERNVIRTADDR can also be set to any 2MB
+ * boundary. It is typically overridden in the std.platform file only when
+ * KERNBASE is also set to a lower address to provide more KVA.
+ */
+#ifndef KERNVIRTADDR
+#define KERNVIRTADDR 0xc0000000
+#endif
+
+/*
+ * max number of non-contig chunks of physical RAM you can have
+ */
+
+#define VM_PHYSSEG_MAX 32
+
+/*
+ * The physical address space may be sparsely populated on some ARM systems.
+ */
+#define VM_PHYSSEG_SPARSE
+
+/*
+ * Create one free page pool. Since the ARM kernel virtual address
+ * space does not include a mapping onto the machine's entire physical
+ * memory, VM_FREEPOOL_DIRECT is defined as an alias for the default
+ * pool, VM_FREEPOOL_DEFAULT.
+ */
+#define VM_NFREEPOOL 1
+#define VM_FREEPOOL_DEFAULT 0
+#define VM_FREEPOOL_DIRECT 0
+
+/*
+ * We need just one free list: DEFAULT.
+ */
+#define VM_NFREELIST 1
+#define VM_FREELIST_DEFAULT 0
+
+/*
+ * The largest allocation size is 1MB.
+ */
+#define VM_NFREEORDER 9
+
+/*
+ * Enable superpage reservations: 1 level.
+ */
+#ifndef VM_NRESERVLEVEL
+#define VM_NRESERVLEVEL 1
+#endif
+
+/*
+ * Level 0 reservations consist of 256 pages.
+ */
+#ifndef VM_LEVEL_0_ORDER
+#define VM_LEVEL_0_ORDER 8
+#endif
+
+#define VM_MIN_ADDRESS (0x00001000)
+#ifndef VM_MAXUSER_ADDRESS
+#define VM_MAXUSER_ADDRESS (KERNBASE - 0x00400000) /* !!! PT2MAP_SIZE */
+#endif
+#define VM_MAX_ADDRESS VM_MAXUSER_ADDRESS
+
+#define SHAREDPAGE (VM_MAXUSER_ADDRESS - PAGE_SIZE)
+#define USRSTACK SHAREDPAGE
+
+/* initial pagein size of beginning of executable file */
+#ifndef VM_INITIAL_PAGEIN
+#define VM_INITIAL_PAGEIN 16
+#endif
+
+#ifndef VM_MIN_KERNEL_ADDRESS
+#define VM_MIN_KERNEL_ADDRESS KERNBASE
+#endif
+
+#define VM_MAX_KERNEL_ADDRESS (vm_max_kernel_address)
+
+/*
+ * How many physical pages per kmem arena virtual page.
+ */
+#ifndef VM_KMEM_SIZE_SCALE
+#define VM_KMEM_SIZE_SCALE (3)
+#endif
+
+/*
+ * Optional floor (in bytes) on the size of the kmem arena.
+ */
+#ifndef VM_KMEM_SIZE_MIN
+#define VM_KMEM_SIZE_MIN (12 * 1024 * 1024)
+#endif
+
+/*
+ * Optional ceiling (in bytes) on the size of the kmem arena: 40% of the
+ * kernel map.
+ */
+#ifndef VM_KMEM_SIZE_MAX
+#define VM_KMEM_SIZE_MAX ((vm_max_kernel_address - \
+ VM_MIN_KERNEL_ADDRESS + 1) * 2 / 5)
+#endif
+
+extern vm_offset_t vm_max_kernel_address;
+
+#define ZERO_REGION_SIZE (64 * 1024) /* 64KB */
+
+#ifndef VM_MAX_AUTOTUNE_MAXUSERS
+#define VM_MAX_AUTOTUNE_MAXUSERS 384
+#endif
+
+#define SFBUF
+#define SFBUF_MAP
+
+#define PMAP_HAS_DMAP 0
+#define PHYS_TO_DMAP(x) ({ panic("No direct map exists"); 0; })
+#define DMAP_TO_PHYS(x) ({ panic("No direct map exists"); 0; })
+
+#define DEVMAP_MAX_VADDR ARM_VECTORS_HIGH
+
+/*
+ * No non-transparent large page support in the pmap.
+ */
+#define PMAP_HAS_LARGEPAGES 0
+
+/*
+ * Need a page dump array for minidump.
+ */
+#define MINIDUMP_PAGE_TRACKING 1
+
+#endif /* _MACHINE_VMPARAM_H_ */
diff --git a/sys/arm/linux/Makefile b/sys/arm/linux/Makefile
new file mode 100644
index 000000000000..662c7f8fc42f
--- /dev/null
+++ b/sys/arm/linux/Makefile
@@ -0,0 +1,7 @@
+# Makefile for syscall tables
+#
+# $FreeBSD$
+
+GENERATED_PREFIX= linux_
+
+.include "../../conf/sysent.mk"
diff --git a/sys/arm/linux/linux_proto.h b/sys/arm/linux/linux_proto.h
new file mode 100644
index 000000000000..71225320bea1
--- /dev/null
+++ b/sys/arm/linux/linux_proto.h
@@ -0,0 +1,1842 @@
+/*
+ * System call prototypes.
+ *
+ * DO NOT EDIT-- this file is automatically @generated.
+ * $FreeBSD$
+ */
+
+#ifndef _LINUX_SYSPROTO_H_
+#define _LINUX_SYSPROTO_H_
+
+#include <sys/signal.h>
+#include <sys/acl.h>
+#include <sys/cpuset.h>
+#include <sys/domainset.h>
+#include <sys/_ffcounter.h>
+#include <sys/_semaphore.h>
+#include <sys/ucontext.h>
+#include <sys/wait.h>
+
+#include <bsm/audit_kevents.h>
+
+struct proc;
+
+struct thread;
+
+#define PAD_(t) (sizeof(register_t) <= sizeof(t) ? \
+ 0 : sizeof(register_t) - sizeof(t))
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define PADL_(t) 0
+#define PADR_(t) PAD_(t)
+#else
+#define PADL_(t) PAD_(t)
+#define PADR_(t) 0
+#endif
+
+#define nosys linux_nosys
+struct linux_exit_args {
+ char rval_l_[PADL_(int)]; int rval; char rval_r_[PADR_(int)];
+};
+struct linux_fork_args {
+ register_t dummy;
+};
+struct linux_open_args {
+ char path_l_[PADL_(char *)]; char * path; char path_r_[PADR_(char *)];
+ char flags_l_[PADL_(l_int)]; l_int flags; char flags_r_[PADR_(l_int)];
+ char mode_l_[PADL_(l_int)]; l_int mode; char mode_r_[PADR_(l_int)];
+};
+struct linux_creat_args {
+ char path_l_[PADL_(char *)]; char * path; char path_r_[PADR_(char *)];
+ char mode_l_[PADL_(l_int)]; l_int mode; char mode_r_[PADR_(l_int)];
+};
+struct linux_link_args {
+ char path_l_[PADL_(char *)]; char * path; char path_r_[PADR_(char *)];
+ char to_l_[PADL_(char *)]; char * to; char to_r_[PADR_(char *)];
+};
+struct linux_unlink_args {
+ char path_l_[PADL_(char *)]; char * path; char path_r_[PADR_(char *)];
+};
+struct linux_execve_args {
+ char path_l_[PADL_(char *)]; char * path; char path_r_[PADR_(char *)];
+ char argp_l_[PADL_(char **)]; char ** argp; char argp_r_[PADR_(char **)];
+ char envp_l_[PADL_(char **)]; char ** envp; char envp_r_[PADR_(char **)];
+};
+struct linux_chdir_args {
+ char path_l_[PADL_(char *)]; char * path; char path_r_[PADR_(char *)];
+};
+struct linux_mknod_args {
+ char path_l_[PADL_(char *)]; char * path; char path_r_[PADR_(char *)];
+ char mode_l_[PADL_(l_int)]; l_int mode; char mode_r_[PADR_(l_int)];
+ char dev_l_[PADL_(l_dev_t)]; l_dev_t dev; char dev_r_[PADR_(l_dev_t)];
+};
+struct linux_chmod_args {
+ char path_l_[PADL_(char *)]; char * path; char path_r_[PADR_(char *)];
+ char mode_l_[PADL_(l_mode_t)]; l_mode_t mode; char mode_r_[PADR_(l_mode_t)];
+};
+struct linux_lchown16_args {
+ char path_l_[PADL_(char *)]; char * path; char path_r_[PADR_(char *)];
+ char uid_l_[PADL_(l_uid16_t)]; l_uid16_t uid; char uid_r_[PADR_(l_uid16_t)];
+ char gid_l_[PADL_(l_gid16_t)]; l_gid16_t gid; char gid_r_[PADR_(l_gid16_t)];
+};
+struct linux_lseek_args {
+ char fdes_l_[PADL_(l_uint)]; l_uint fdes; char fdes_r_[PADR_(l_uint)];
+ char off_l_[PADL_(l_off_t)]; l_off_t off; char off_r_[PADR_(l_off_t)];
+ char whence_l_[PADL_(l_int)]; l_int whence; char whence_r_[PADR_(l_int)];
+};
+struct linux_getpid_args {
+ register_t dummy;
+};
+struct linux_mount_args {
+ char specialfile_l_[PADL_(char *)]; char * specialfile; char specialfile_r_[PADR_(char *)];
+ char dir_l_[PADL_(char *)]; char * dir; char dir_r_[PADR_(char *)];
+ char filesystemtype_l_[PADL_(char *)]; char * filesystemtype; char filesystemtype_r_[PADR_(char *)];
+ char rwflag_l_[PADL_(l_ulong)]; l_ulong rwflag; char rwflag_r_[PADR_(l_ulong)];
+ char data_l_[PADL_(void *)]; void * data; char data_r_[PADR_(void *)];
+};
+struct linux_setuid16_args {
+ char uid_l_[PADL_(l_uid16_t)]; l_uid16_t uid; char uid_r_[PADR_(l_uid16_t)];
+};
+struct linux_getuid16_args {
+ register_t dummy;
+};
+struct linux_pause_args {
+ register_t dummy;
+};
+struct linux_access_args {
+ char path_l_[PADL_(char *)]; char * path; char path_r_[PADR_(char *)];
+ char amode_l_[PADL_(l_int)]; l_int amode; char amode_r_[PADR_(l_int)];
+};
+struct linux_nice_args {
+ char inc_l_[PADL_(l_int)]; l_int inc; char inc_r_[PADR_(l_int)];
+};
+struct linux_kill_args {
+ char pid_l_[PADL_(l_int)]; l_int pid; char pid_r_[PADR_(l_int)];
+ char signum_l_[PADL_(l_int)]; l_int signum; char signum_r_[PADR_(l_int)];
+};
+struct linux_rename_args {
+ char from_l_[PADL_(char *)]; char * from; char from_r_[PADR_(char *)];
+ char to_l_[PADL_(char *)]; char * to; char to_r_[PADR_(char *)];
+};
+struct linux_mkdir_args {
+ char path_l_[PADL_(char *)]; char * path; char path_r_[PADR_(char *)];
+ char mode_l_[PADL_(l_int)]; l_int mode; char mode_r_[PADR_(l_int)];
+};
+struct linux_rmdir_args {
+ char path_l_[PADL_(char *)]; char * path; char path_r_[PADR_(char *)];
+};
+struct linux_pipe_args {
+ char pipefds_l_[PADL_(l_int *)]; l_int * pipefds; char pipefds_r_[PADR_(l_int *)];
+};
+struct linux_times_args {
+ char buf_l_[PADL_(struct l_times_argv *)]; struct l_times_argv * buf; char buf_r_[PADR_(struct l_times_argv *)];
+};
+struct linux_brk_args {
+ char dsend_l_[PADL_(l_ulong)]; l_ulong dsend; char dsend_r_[PADR_(l_ulong)];
+};
+struct linux_setgid16_args {
+ char gid_l_[PADL_(l_gid16_t)]; l_gid16_t gid; char gid_r_[PADR_(l_gid16_t)];
+};
+struct linux_getgid16_args {
+ register_t dummy;
+};
+struct linux_geteuid16_args {
+ register_t dummy;
+};
+struct linux_getegid16_args {
+ register_t dummy;
+};
+struct linux_umount_args {
+ char path_l_[PADL_(char *)]; char * path; char path_r_[PADR_(char *)];
+ char flags_l_[PADL_(l_int)]; l_int flags; char flags_r_[PADR_(l_int)];
+};
+struct linux_ioctl_args {
+ char fd_l_[PADL_(l_uint)]; l_uint fd; char fd_r_[PADR_(l_uint)];
+ char cmd_l_[PADL_(l_uint)]; l_uint cmd; char cmd_r_[PADR_(l_uint)];
+ char arg_l_[PADL_(l_ulong)]; l_ulong arg; char arg_r_[PADR_(l_ulong)];
+};
+struct linux_fcntl_args {
+ char fd_l_[PADL_(l_uint)]; l_uint fd; char fd_r_[PADR_(l_uint)];
+ char cmd_l_[PADL_(l_uint)]; l_uint cmd; char cmd_r_[PADR_(l_uint)];
+ char arg_l_[PADL_(l_ulong)]; l_ulong arg; char arg_r_[PADR_(l_ulong)];
+};
+struct linux_ustat_args {
+ char dev_l_[PADL_(l_dev_t)]; l_dev_t dev; char dev_r_[PADR_(l_dev_t)];
+ char ubuf_l_[PADL_(struct l_ustat *)]; struct l_ustat * ubuf; char ubuf_r_[PADR_(struct l_ustat *)];
+};
+struct linux_getppid_args {
+ register_t dummy;
+};
+struct linux_sigaction_args {
+ char sig_l_[PADL_(l_int)]; l_int sig; char sig_r_[PADR_(l_int)];
+ char nsa_l_[PADL_(l_osigaction_t *)]; l_osigaction_t * nsa; char nsa_r_[PADR_(l_osigaction_t *)];
+ char osa_l_[PADL_(l_osigaction_t *)]; l_osigaction_t * osa; char osa_r_[PADR_(l_osigaction_t *)];
+};
+struct linux_setreuid16_args {
+ char ruid_l_[PADL_(l_uid16_t)]; l_uid16_t ruid; char ruid_r_[PADR_(l_uid16_t)];
+ char euid_l_[PADL_(l_uid16_t)]; l_uid16_t euid; char euid_r_[PADR_(l_uid16_t)];
+};
+struct linux_setregid16_args {
+ char rgid_l_[PADL_(l_gid16_t)]; l_gid16_t rgid; char rgid_r_[PADR_(l_gid16_t)];
+ char egid_l_[PADL_(l_gid16_t)]; l_gid16_t egid; char egid_r_[PADR_(l_gid16_t)];
+};
+struct linux_sigsuspend_args {
+ char hist0_l_[PADL_(l_int)]; l_int hist0; char hist0_r_[PADR_(l_int)];
+ char hist1_l_[PADL_(l_int)]; l_int hist1; char hist1_r_[PADR_(l_int)];
+ char mask_l_[PADL_(l_osigset_t)]; l_osigset_t mask; char mask_r_[PADR_(l_osigset_t)];
+};
+struct linux_sigpending_args {
+ char mask_l_[PADL_(l_osigset_t *)]; l_osigset_t * mask; char mask_r_[PADR_(l_osigset_t *)];
+};
+struct linux_sethostname_args {
+ char hostname_l_[PADL_(char *)]; char * hostname; char hostname_r_[PADR_(char *)];
+ char len_l_[PADL_(u_int)]; u_int len; char len_r_[PADR_(u_int)];
+};
+struct linux_setrlimit_args {
+ char resource_l_[PADL_(l_uint)]; l_uint resource; char resource_r_[PADR_(l_uint)];
+ char rlim_l_[PADL_(struct l_rlimit *)]; struct l_rlimit * rlim; char rlim_r_[PADR_(struct l_rlimit *)];
+};
+struct linux_gettimeofday_args {
+ char tp_l_[PADL_(struct timeval32 *)]; struct timeval32 * tp; char tp_r_[PADR_(struct timeval32 *)];
+ char tzp_l_[PADL_(struct timezone *)]; struct timezone * tzp; char tzp_r_[PADR_(struct timezone *)];
+};
+struct linux_settimeofday_args {
+ char tp_l_[PADL_(struct timeval32 *)]; struct timeval32 * tp; char tp_r_[PADR_(struct timeval32 *)];
+ char tzp_l_[PADL_(struct timezone *)]; struct timezone * tzp; char tzp_r_[PADR_(struct timezone *)];
+};
+struct linux_getgroups16_args {
+ char gidsetsize_l_[PADL_(l_uint)]; l_uint gidsetsize; char gidsetsize_r_[PADR_(l_uint)];
+ char gidset_l_[PADL_(l_gid16_t *)]; l_gid16_t * gidset; char gidset_r_[PADR_(l_gid16_t *)];
+};
+struct linux_setgroups16_args {
+ char gidsetsize_l_[PADL_(l_uint)]; l_uint gidsetsize; char gidsetsize_r_[PADR_(l_uint)];
+ char gidset_l_[PADL_(l_gid16_t *)]; l_gid16_t * gidset; char gidset_r_[PADR_(l_gid16_t *)];
+};
+struct linux_symlink_args {
+ char path_l_[PADL_(char *)]; char * path; char path_r_[PADR_(char *)];
+ char to_l_[PADL_(char *)]; char * to; char to_r_[PADR_(char *)];
+};
+struct linux_readlink_args {
+ char name_l_[PADL_(char *)]; char * name; char name_r_[PADR_(char *)];
+ char buf_l_[PADL_(char *)]; char * buf; char buf_r_[PADR_(char *)];
+ char count_l_[PADL_(l_int)]; l_int count; char count_r_[PADR_(l_int)];
+};
+struct linux_reboot_args {
+ char magic1_l_[PADL_(l_int)]; l_int magic1; char magic1_r_[PADR_(l_int)];
+ char magic2_l_[PADL_(l_int)]; l_int magic2; char magic2_r_[PADR_(l_int)];
+ char cmd_l_[PADL_(l_uint)]; l_uint cmd; char cmd_r_[PADR_(l_uint)];
+ char arg_l_[PADL_(void *)]; void * arg; char arg_r_[PADR_(void *)];
+};
+struct linux_truncate_args {
+ char path_l_[PADL_(char *)]; char * path; char path_r_[PADR_(char *)];
+ char length_l_[PADL_(l_ulong)]; l_ulong length; char length_r_[PADR_(l_ulong)];
+};
+struct linux_ftruncate_args {
+ char fd_l_[PADL_(int)]; int fd; char fd_r_[PADR_(int)];
+ char length_l_[PADL_(long)]; long length; char length_r_[PADR_(long)];
+};
+struct linux_getpriority_args {
+ char which_l_[PADL_(int)]; int which; char which_r_[PADR_(int)];
+ char who_l_[PADL_(int)]; int who; char who_r_[PADR_(int)];
+};
+struct linux_statfs_args {
+ char path_l_[PADL_(char *)]; char * path; char path_r_[PADR_(char *)];
+ char buf_l_[PADL_(struct l_statfs_buf *)]; struct l_statfs_buf * buf; char buf_r_[PADR_(struct l_statfs_buf *)];
+};
+struct linux_fstatfs_args {
+ char fd_l_[PADL_(l_uint)]; l_uint fd; char fd_r_[PADR_(l_uint)];
+ char buf_l_[PADL_(struct l_statfs_buf *)]; struct l_statfs_buf * buf; char buf_r_[PADR_(struct l_statfs_buf *)];
+};
+struct linux_syslog_args {
+ char type_l_[PADL_(l_int)]; l_int type; char type_r_[PADR_(l_int)];
+ char buf_l_[PADL_(char *)]; char * buf; char buf_r_[PADR_(char *)];
+ char len_l_[PADL_(l_int)]; l_int len; char len_r_[PADR_(l_int)];
+};
+struct linux_setitimer_args {
+ char which_l_[PADL_(l_int)]; l_int which; char which_r_[PADR_(l_int)];
+ char itv_l_[PADL_(struct l_itimerval *)]; struct l_itimerval * itv; char itv_r_[PADR_(struct l_itimerval *)];
+ char oitv_l_[PADL_(struct l_itimerval *)]; struct l_itimerval * oitv; char oitv_r_[PADR_(struct l_itimerval *)];
+};
+struct linux_getitimer_args {
+ char which_l_[PADL_(l_int)]; l_int which; char which_r_[PADR_(l_int)];
+ char itv_l_[PADL_(struct l_itimerval *)]; struct l_itimerval * itv; char itv_r_[PADR_(struct l_itimerval *)];
+};
+struct linux_newstat_args {
+ char path_l_[PADL_(char *)]; char * path; char path_r_[PADR_(char *)];
+ char buf_l_[PADL_(struct l_newstat *)]; struct l_newstat * buf; char buf_r_[PADR_(struct l_newstat *)];
+};
+struct linux_newlstat_args {
+ char path_l_[PADL_(char *)]; char * path; char path_r_[PADR_(char *)];
+ char buf_l_[PADL_(struct l_newstat *)]; struct l_newstat * buf; char buf_r_[PADR_(struct l_newstat *)];
+};
+struct linux_newfstat_args {
+ char fd_l_[PADL_(l_uint)]; l_uint fd; char fd_r_[PADR_(l_uint)];
+ char buf_l_[PADL_(struct l_newstat *)]; struct l_newstat * buf; char buf_r_[PADR_(struct l_newstat *)];
+};
+struct linux_vhangup_args {
+ register_t dummy;
+};
+struct linux_wait4_args {
+ char pid_l_[PADL_(l_pid_t)]; l_pid_t pid; char pid_r_[PADR_(l_pid_t)];
+ char status_l_[PADL_(l_int *)]; l_int * status; char status_r_[PADR_(l_int *)];
+ char options_l_[PADL_(l_int)]; l_int options; char options_r_[PADR_(l_int)];
+ char rusage_l_[PADL_(void *)]; void * rusage; char rusage_r_[PADR_(void *)];
+};
+struct linux_swapoff_args {
+ register_t dummy;
+};
+struct linux_sysinfo_args {
+ char info_l_[PADL_(struct l_sysinfo *)]; struct l_sysinfo * info; char info_r_[PADR_(struct l_sysinfo *)];
+};
+struct linux_sigreturn_args {
+ char sfp_l_[PADL_(struct l_sigframe *)]; struct l_sigframe * sfp; char sfp_r_[PADR_(struct l_sigframe *)];
+};
+struct linux_clone_args {
+ char flags_l_[PADL_(l_int)]; l_int flags; char flags_r_[PADR_(l_int)];
+ char stack_l_[PADL_(void *)]; void * stack; char stack_r_[PADR_(void *)];
+ char parent_tidptr_l_[PADL_(void *)]; void * parent_tidptr; char parent_tidptr_r_[PADR_(void *)];
+ char tls_l_[PADL_(void *)]; void * tls; char tls_r_[PADR_(void *)];
+ char child_tidptr_l_[PADL_(void *)]; void * child_tidptr; char child_tidptr_r_[PADR_(void *)];
+};
+struct linux_setdomainname_args {
+ char name_l_[PADL_(char *)]; char * name; char name_r_[PADR_(char *)];
+ char len_l_[PADL_(int)]; int len; char len_r_[PADR_(int)];
+};
+struct linux_newuname_args {
+ char buf_l_[PADL_(struct l_new_utsname *)]; struct l_new_utsname * buf; char buf_r_[PADR_(struct l_new_utsname *)];
+};
+struct linux_adjtimex_args {
+ register_t dummy;
+};
+struct linux_mprotect_args {
+ char addr_l_[PADL_(caddr_t)]; caddr_t addr; char addr_r_[PADR_(caddr_t)];
+ char len_l_[PADL_(int)]; int len; char len_r_[PADR_(int)];
+ char prot_l_[PADL_(int)]; int prot; char prot_r_[PADR_(int)];
+};
+struct linux_sigprocmask_args {
+ char how_l_[PADL_(l_int)]; l_int how; char how_r_[PADR_(l_int)];
+ char mask_l_[PADL_(l_osigset_t *)]; l_osigset_t * mask; char mask_r_[PADR_(l_osigset_t *)];
+ char omask_l_[PADL_(l_osigset_t *)]; l_osigset_t * omask; char omask_r_[PADR_(l_osigset_t *)];
+};
+struct linux_init_module_args {
+ register_t dummy;
+};
+struct linux_delete_module_args {
+ register_t dummy;
+};
+struct linux_quotactl_args {
+ register_t dummy;
+};
+struct linux_bdflush_args {
+ register_t dummy;
+};
+struct linux_sysfs_args {
+ char option_l_[PADL_(l_int)]; l_int option; char option_r_[PADR_(l_int)];
+ char arg1_l_[PADL_(l_ulong)]; l_ulong arg1; char arg1_r_[PADR_(l_ulong)];
+ char arg2_l_[PADL_(l_ulong)]; l_ulong arg2; char arg2_r_[PADR_(l_ulong)];
+};
+struct linux_personality_args {
+ char per_l_[PADL_(l_ulong)]; l_ulong per; char per_r_[PADR_(l_ulong)];
+};
+struct linux_setfsuid16_args {
+ char uid_l_[PADL_(l_uid16_t)]; l_uid16_t uid; char uid_r_[PADR_(l_uid16_t)];
+};
+struct linux_setfsgid16_args {
+ char gid_l_[PADL_(l_gid16_t)]; l_gid16_t gid; char gid_r_[PADR_(l_gid16_t)];
+};
+struct linux_llseek_args {
+ char fd_l_[PADL_(l_int)]; l_int fd; char fd_r_[PADR_(l_int)];
+ char ohigh_l_[PADL_(l_ulong)]; l_ulong ohigh; char ohigh_r_[PADR_(l_ulong)];
+ char olow_l_[PADL_(l_ulong)]; l_ulong olow; char olow_r_[PADR_(l_ulong)];
+ char res_l_[PADL_(l_loff_t *)]; l_loff_t * res; char res_r_[PADR_(l_loff_t *)];
+ char whence_l_[PADL_(l_uint)]; l_uint whence; char whence_r_[PADR_(l_uint)];
+};
+struct linux_getdents_args {
+ char fd_l_[PADL_(l_uint)]; l_uint fd; char fd_r_[PADR_(l_uint)];
+ char dent_l_[PADL_(void *)]; void * dent; char dent_r_[PADR_(void *)];
+ char count_l_[PADL_(l_uint)]; l_uint count; char count_r_[PADR_(l_uint)];
+};
+struct linux_select_args {
+ char nfds_l_[PADL_(l_int)]; l_int nfds; char nfds_r_[PADR_(l_int)];
+ char readfds_l_[PADL_(l_fd_set *)]; l_fd_set * readfds; char readfds_r_[PADR_(l_fd_set *)];
+ char writefds_l_[PADL_(l_fd_set *)]; l_fd_set * writefds; char writefds_r_[PADR_(l_fd_set *)];
+ char exceptfds_l_[PADL_(l_fd_set *)]; l_fd_set * exceptfds; char exceptfds_r_[PADR_(l_fd_set *)];
+ char timeout_l_[PADL_(struct l_timeval *)]; struct l_timeval * timeout; char timeout_r_[PADR_(struct l_timeval *)];
+};
+struct linux_msync_args {
+ char addr_l_[PADL_(l_ulong)]; l_ulong addr; char addr_r_[PADR_(l_ulong)];
+ char len_l_[PADL_(l_size_t)]; l_size_t len; char len_r_[PADR_(l_size_t)];
+ char fl_l_[PADL_(l_int)]; l_int fl; char fl_r_[PADR_(l_int)];
+};
+struct linux_getsid_args {
+ char pid_l_[PADL_(l_pid_t)]; l_pid_t pid; char pid_r_[PADR_(l_pid_t)];
+};
+struct linux_fdatasync_args {
+ char fd_l_[PADL_(l_uint)]; l_uint fd; char fd_r_[PADR_(l_uint)];
+};
+struct linux_sysctl_args {
+ char args_l_[PADL_(struct l___sysctl_args *)]; struct l___sysctl_args * args; char args_r_[PADR_(struct l___sysctl_args *)];
+};
+struct linux_sched_setparam_args {
+ char pid_l_[PADL_(l_pid_t)]; l_pid_t pid; char pid_r_[PADR_(l_pid_t)];
+ char param_l_[PADL_(struct sched_param *)]; struct sched_param * param; char param_r_[PADR_(struct sched_param *)];
+};
+struct linux_sched_getparam_args {
+ char pid_l_[PADL_(l_pid_t)]; l_pid_t pid; char pid_r_[PADR_(l_pid_t)];
+ char param_l_[PADL_(struct sched_param *)]; struct sched_param * param; char param_r_[PADR_(struct sched_param *)];
+};
+struct linux_sched_setscheduler_args {
+ char pid_l_[PADL_(l_pid_t)]; l_pid_t pid; char pid_r_[PADR_(l_pid_t)];
+ char policy_l_[PADL_(l_int)]; l_int policy; char policy_r_[PADR_(l_int)];
+ char param_l_[PADL_(struct sched_param *)]; struct sched_param * param; char param_r_[PADR_(struct sched_param *)];
+};
+struct linux_sched_getscheduler_args {
+ char pid_l_[PADL_(l_pid_t)]; l_pid_t pid; char pid_r_[PADR_(l_pid_t)];
+};
+struct linux_sched_get_priority_max_args {
+ char policy_l_[PADL_(l_int)]; l_int policy; char policy_r_[PADR_(l_int)];
+};
+struct linux_sched_get_priority_min_args {
+ char policy_l_[PADL_(l_int)]; l_int policy; char policy_r_[PADR_(l_int)];
+};
+struct linux_sched_rr_get_interval_args {
+ char pid_l_[PADL_(l_pid_t)]; l_pid_t pid; char pid_r_[PADR_(l_pid_t)];
+ char interval_l_[PADL_(struct l_timespec *)]; struct l_timespec * interval; char interval_r_[PADR_(struct l_timespec *)];
+};
+struct linux_nanosleep_args {
+ char rqtp_l_[PADL_(const struct l_timespec *)]; const struct l_timespec * rqtp; char rqtp_r_[PADR_(const struct l_timespec *)];
+ char rmtp_l_[PADL_(struct l_timespec *)]; struct l_timespec * rmtp; char rmtp_r_[PADR_(struct l_timespec *)];
+};
+struct linux_mremap_args {
+ char addr_l_[PADL_(l_ulong)]; l_ulong addr; char addr_r_[PADR_(l_ulong)];
+ char old_len_l_[PADL_(l_ulong)]; l_ulong old_len; char old_len_r_[PADR_(l_ulong)];
+ char new_len_l_[PADL_(l_ulong)]; l_ulong new_len; char new_len_r_[PADR_(l_ulong)];
+ char flags_l_[PADL_(l_ulong)]; l_ulong flags; char flags_r_[PADR_(l_ulong)];
+ char new_addr_l_[PADL_(l_ulong)]; l_ulong new_addr; char new_addr_r_[PADR_(l_ulong)];
+};
+struct linux_setresuid16_args {
+ char ruid_l_[PADL_(l_uid16_t)]; l_uid16_t ruid; char ruid_r_[PADR_(l_uid16_t)];
+ char euid_l_[PADL_(l_uid16_t)]; l_uid16_t euid; char euid_r_[PADR_(l_uid16_t)];
+ char suid_l_[PADL_(l_uid16_t)]; l_uid16_t suid; char suid_r_[PADR_(l_uid16_t)];
+};
+struct linux_getresuid16_args {
+ char ruid_l_[PADL_(l_uid16_t *)]; l_uid16_t * ruid; char ruid_r_[PADR_(l_uid16_t *)];
+ char euid_l_[PADL_(l_uid16_t *)]; l_uid16_t * euid; char euid_r_[PADR_(l_uid16_t *)];
+ char suid_l_[PADL_(l_uid16_t *)]; l_uid16_t * suid; char suid_r_[PADR_(l_uid16_t *)];
+};
+struct linux_setresgid16_args {
+ char rgid_l_[PADL_(l_gid16_t)]; l_gid16_t rgid; char rgid_r_[PADR_(l_gid16_t)];
+ char egid_l_[PADL_(l_gid16_t)]; l_gid16_t egid; char egid_r_[PADR_(l_gid16_t)];
+ char sgid_l_[PADL_(l_gid16_t)]; l_gid16_t sgid; char sgid_r_[PADR_(l_gid16_t)];
+};
+struct linux_getresgid16_args {
+ char rgid_l_[PADL_(l_gid16_t *)]; l_gid16_t * rgid; char rgid_r_[PADR_(l_gid16_t *)];
+ char egid_l_[PADL_(l_gid16_t *)]; l_gid16_t * egid; char egid_r_[PADR_(l_gid16_t *)];
+ char sgid_l_[PADL_(l_gid16_t *)]; l_gid16_t * sgid; char sgid_r_[PADR_(l_gid16_t *)];
+};
+struct linux_prctl_args {
+ char option_l_[PADL_(l_int)]; l_int option; char option_r_[PADR_(l_int)];
+ char arg2_l_[PADL_(l_int)]; l_int arg2; char arg2_r_[PADR_(l_int)];
+ char arg3_l_[PADL_(l_int)]; l_int arg3; char arg3_r_[PADR_(l_int)];
+ char arg4_l_[PADL_(l_int)]; l_int arg4; char arg4_r_[PADR_(l_int)];
+ char arg5_l_[PADL_(l_int)]; l_int arg5; char arg5_r_[PADR_(l_int)];
+};
+struct linux_rt_sigreturn_args {
+ char ucp_l_[PADL_(struct l_ucontext *)]; struct l_ucontext * ucp; char ucp_r_[PADR_(struct l_ucontext *)];
+};
+struct linux_rt_sigaction_args {
+ char sig_l_[PADL_(l_int)]; l_int sig; char sig_r_[PADR_(l_int)];
+ char act_l_[PADL_(l_sigaction_t *)]; l_sigaction_t * act; char act_r_[PADR_(l_sigaction_t *)];
+ char oact_l_[PADL_(l_sigaction_t *)]; l_sigaction_t * oact; char oact_r_[PADR_(l_sigaction_t *)];
+ char sigsetsize_l_[PADL_(l_size_t)]; l_size_t sigsetsize; char sigsetsize_r_[PADR_(l_size_t)];
+};
+struct linux_rt_sigprocmask_args {
+ char how_l_[PADL_(l_int)]; l_int how; char how_r_[PADR_(l_int)];
+ char mask_l_[PADL_(l_sigset_t *)]; l_sigset_t * mask; char mask_r_[PADR_(l_sigset_t *)];
+ char omask_l_[PADL_(l_sigset_t *)]; l_sigset_t * omask; char omask_r_[PADR_(l_sigset_t *)];
+ char sigsetsize_l_[PADL_(l_size_t)]; l_size_t sigsetsize; char sigsetsize_r_[PADR_(l_size_t)];
+};
+struct linux_rt_sigpending_args {
+ char set_l_[PADL_(l_sigset_t *)]; l_sigset_t * set; char set_r_[PADR_(l_sigset_t *)];
+ char sigsetsize_l_[PADL_(l_size_t)]; l_size_t sigsetsize; char sigsetsize_r_[PADR_(l_size_t)];
+};
+struct linux_rt_sigtimedwait_args {
+ char mask_l_[PADL_(l_sigset_t *)]; l_sigset_t * mask; char mask_r_[PADR_(l_sigset_t *)];
+ char ptr_l_[PADL_(l_siginfo_t *)]; l_siginfo_t * ptr; char ptr_r_[PADR_(l_siginfo_t *)];
+ char timeout_l_[PADL_(struct l_timeval *)]; struct l_timeval * timeout; char timeout_r_[PADR_(struct l_timeval *)];
+ char sigsetsize_l_[PADL_(l_size_t)]; l_size_t sigsetsize; char sigsetsize_r_[PADR_(l_size_t)];
+};
+struct linux_rt_sigqueueinfo_args {
+ char pid_l_[PADL_(l_pid_t)]; l_pid_t pid; char pid_r_[PADR_(l_pid_t)];
+ char sig_l_[PADL_(l_int)]; l_int sig; char sig_r_[PADR_(l_int)];
+ char info_l_[PADL_(l_siginfo_t *)]; l_siginfo_t * info; char info_r_[PADR_(l_siginfo_t *)];
+};
+struct linux_rt_sigsuspend_args {
+ char newset_l_[PADL_(l_sigset_t *)]; l_sigset_t * newset; char newset_r_[PADR_(l_sigset_t *)];
+ char sigsetsize_l_[PADL_(l_size_t)]; l_size_t sigsetsize; char sigsetsize_r_[PADR_(l_size_t)];
+};
+struct linux_pread_args {
+ char fd_l_[PADL_(l_uint)]; l_uint fd; char fd_r_[PADR_(l_uint)];
+ char buf_l_[PADL_(char *)]; char * buf; char buf_r_[PADR_(char *)];
+ char nbyte_l_[PADL_(l_size_t)]; l_size_t nbyte; char nbyte_r_[PADR_(l_size_t)];
+ char offset_l_[PADL_(l_loff_t)]; l_loff_t offset; char offset_r_[PADR_(l_loff_t)];
+};
+struct linux_pwrite_args {
+ char fd_l_[PADL_(l_uint)]; l_uint fd; char fd_r_[PADR_(l_uint)];
+ char buf_l_[PADL_(char *)]; char * buf; char buf_r_[PADR_(char *)];
+ char nbyte_l_[PADL_(l_size_t)]; l_size_t nbyte; char nbyte_r_[PADR_(l_size_t)];
+ char offset_l_[PADL_(l_loff_t)]; l_loff_t offset; char offset_r_[PADR_(l_loff_t)];
+};
+struct linux_chown16_args {
+ char path_l_[PADL_(char *)]; char * path; char path_r_[PADR_(char *)];
+ char uid_l_[PADL_(l_uid16_t)]; l_uid16_t uid; char uid_r_[PADR_(l_uid16_t)];
+ char gid_l_[PADL_(l_gid16_t)]; l_gid16_t gid; char gid_r_[PADR_(l_gid16_t)];
+};
+struct linux_getcwd_args {
+ char buf_l_[PADL_(char *)]; char * buf; char buf_r_[PADR_(char *)];
+ char bufsize_l_[PADL_(l_ulong)]; l_ulong bufsize; char bufsize_r_[PADR_(l_ulong)];
+};
+struct linux_capget_args {
+ char hdrp_l_[PADL_(struct l_user_cap_header *)]; struct l_user_cap_header * hdrp; char hdrp_r_[PADR_(struct l_user_cap_header *)];
+ char datap_l_[PADL_(struct l_user_cap_data *)]; struct l_user_cap_data * datap; char datap_r_[PADR_(struct l_user_cap_data *)];
+};
+struct linux_capset_args {
+ char hdrp_l_[PADL_(struct l_user_cap_header *)]; struct l_user_cap_header * hdrp; char hdrp_r_[PADR_(struct l_user_cap_header *)];
+ char datap_l_[PADL_(struct l_user_cap_data *)]; struct l_user_cap_data * datap; char datap_r_[PADR_(struct l_user_cap_data *)];
+};
+struct linux_sigaltstack_args {
+ char uss_l_[PADL_(l_stack_t *)]; l_stack_t * uss; char uss_r_[PADR_(l_stack_t *)];
+ char uoss_l_[PADL_(l_stack_t *)]; l_stack_t * uoss; char uoss_r_[PADR_(l_stack_t *)];
+};
+struct linux_sendfile_args {
+ char out_l_[PADL_(l_int)]; l_int out; char out_r_[PADR_(l_int)];
+ char in_l_[PADL_(l_int)]; l_int in; char in_r_[PADR_(l_int)];
+ char offset_l_[PADL_(l_long *)]; l_long * offset; char offset_r_[PADR_(l_long *)];
+ char count_l_[PADL_(l_size_t)]; l_size_t count; char count_r_[PADR_(l_size_t)];
+};
+struct linux_vfork_args {
+ register_t dummy;
+};
+struct linux_getrlimit_args {
+ char resource_l_[PADL_(l_uint)]; l_uint resource; char resource_r_[PADR_(l_uint)];
+ char rlim_l_[PADL_(struct l_rlimit *)]; struct l_rlimit * rlim; char rlim_r_[PADR_(struct l_rlimit *)];
+};
+struct linux_mmap2_args {
+ char addr_l_[PADL_(l_ulong)]; l_ulong addr; char addr_r_[PADR_(l_ulong)];
+ char len_l_[PADL_(l_ulong)]; l_ulong len; char len_r_[PADR_(l_ulong)];
+ char prot_l_[PADL_(l_ulong)]; l_ulong prot; char prot_r_[PADR_(l_ulong)];
+ char flags_l_[PADL_(l_ulong)]; l_ulong flags; char flags_r_[PADR_(l_ulong)];
+ char fd_l_[PADL_(l_ulong)]; l_ulong fd; char fd_r_[PADR_(l_ulong)];
+ char pgoff_l_[PADL_(l_ulong)]; l_ulong pgoff; char pgoff_r_[PADR_(l_ulong)];
+};
+struct linux_truncate64_args {
+ char path_l_[PADL_(char *)]; char * path; char path_r_[PADR_(char *)];
+ char length_l_[PADL_(l_loff_t)]; l_loff_t length; char length_r_[PADR_(l_loff_t)];
+};
+struct linux_ftruncate64_args {
+ char fd_l_[PADL_(l_uint)]; l_uint fd; char fd_r_[PADR_(l_uint)];
+ char length_l_[PADL_(l_loff_t)]; l_loff_t length; char length_r_[PADR_(l_loff_t)];
+};
+struct linux_stat64_args {
+ char filename_l_[PADL_(const char *)]; const char * filename; char filename_r_[PADR_(const char *)];
+ char statbuf_l_[PADL_(struct l_stat64 *)]; struct l_stat64 * statbuf; char statbuf_r_[PADR_(struct l_stat64 *)];
+};
+struct linux_lstat64_args {
+ char filename_l_[PADL_(const char *)]; const char * filename; char filename_r_[PADR_(const char *)];
+ char statbuf_l_[PADL_(struct l_stat64 *)]; struct l_stat64 * statbuf; char statbuf_r_[PADR_(struct l_stat64 *)];
+};
+struct linux_fstat64_args {
+ char fd_l_[PADL_(l_int)]; l_int fd; char fd_r_[PADR_(l_int)];
+ char statbuf_l_[PADL_(struct l_stat64 *)]; struct l_stat64 * statbuf; char statbuf_r_[PADR_(struct l_stat64 *)];
+};
+struct linux_lchown_args {
+ char path_l_[PADL_(char *)]; char * path; char path_r_[PADR_(char *)];
+ char uid_l_[PADL_(l_uid_t)]; l_uid_t uid; char uid_r_[PADR_(l_uid_t)];
+ char gid_l_[PADL_(l_gid_t)]; l_gid_t gid; char gid_r_[PADR_(l_gid_t)];
+};
+struct linux_getuid_args {
+ register_t dummy;
+};
+struct linux_getgid_args {
+ register_t dummy;
+};
+struct linux_getgroups_args {
+ char gidsetsize_l_[PADL_(l_int)]; l_int gidsetsize; char gidsetsize_r_[PADR_(l_int)];
+ char grouplist_l_[PADL_(l_gid_t *)]; l_gid_t * grouplist; char grouplist_r_[PADR_(l_gid_t *)];
+};
+struct linux_setgroups_args {
+ char gidsetsize_l_[PADL_(l_int)]; l_int gidsetsize; char gidsetsize_r_[PADR_(l_int)];
+ char grouplist_l_[PADL_(l_gid_t *)]; l_gid_t * grouplist; char grouplist_r_[PADR_(l_gid_t *)];
+};
+struct linux_chown_args {
+ char path_l_[PADL_(char *)]; char * path; char path_r_[PADR_(char *)];
+ char uid_l_[PADL_(l_uid_t)]; l_uid_t uid; char uid_r_[PADR_(l_uid_t)];
+ char gid_l_[PADL_(l_gid_t)]; l_gid_t gid; char gid_r_[PADR_(l_gid_t)];
+};
+struct linux_setfsuid_args {
+ char uid_l_[PADL_(l_uid_t)]; l_uid_t uid; char uid_r_[PADR_(l_uid_t)];
+};
+struct linux_setfsgid_args {
+ char gid_l_[PADL_(l_gid_t)]; l_gid_t gid; char gid_r_[PADR_(l_gid_t)];
+};
+struct linux_getdents64_args {
+ char fd_l_[PADL_(l_uint)]; l_uint fd; char fd_r_[PADR_(l_uint)];
+ char dirent_l_[PADL_(void *)]; void * dirent; char dirent_r_[PADR_(void *)];
+ char count_l_[PADL_(l_uint)]; l_uint count; char count_r_[PADR_(l_uint)];
+};
+struct linux_pivot_root_args {
+ char new_root_l_[PADL_(char *)]; char * new_root; char new_root_r_[PADR_(char *)];
+ char put_old_l_[PADL_(char *)]; char * put_old; char put_old_r_[PADR_(char *)];
+};
+struct linux_mincore_args {
+ char start_l_[PADL_(l_ulong)]; l_ulong start; char start_r_[PADR_(l_ulong)];
+ char len_l_[PADL_(l_size_t)]; l_size_t len; char len_r_[PADR_(l_size_t)];
+ char vec_l_[PADL_(u_char *)]; u_char * vec; char vec_r_[PADR_(u_char *)];
+};
+struct linux_fcntl64_args {
+ char fd_l_[PADL_(l_uint)]; l_uint fd; char fd_r_[PADR_(l_uint)];
+ char cmd_l_[PADL_(l_uint)]; l_uint cmd; char cmd_r_[PADR_(l_uint)];
+ char arg_l_[PADL_(l_ulong)]; l_ulong arg; char arg_r_[PADR_(l_ulong)];
+};
+struct linux_gettid_args {
+ register_t dummy;
+};
+struct linux_setxattr_args {
+ register_t dummy;
+};
+struct linux_lsetxattr_args {
+ register_t dummy;
+};
+struct linux_fsetxattr_args {
+ register_t dummy;
+};
+struct linux_getxattr_args {
+ register_t dummy;
+};
+struct linux_lgetxattr_args {
+ register_t dummy;
+};
+struct linux_fgetxattr_args {
+ register_t dummy;
+};
+struct linux_listxattr_args {
+ register_t dummy;
+};
+struct linux_llistxattr_args {
+ register_t dummy;
+};
+struct linux_flistxattr_args {
+ register_t dummy;
+};
+struct linux_removexattr_args {
+ register_t dummy;
+};
+struct linux_lremovexattr_args {
+ register_t dummy;
+};
+struct linux_fremovexattr_args {
+ register_t dummy;
+};
+struct linux_tkill_args {
+ char tid_l_[PADL_(int)]; int tid; char tid_r_[PADR_(int)];
+ char sig_l_[PADL_(int)]; int sig; char sig_r_[PADR_(int)];
+};
+struct linux_sendfile64_args {
+ char out_l_[PADL_(l_int)]; l_int out; char out_r_[PADR_(l_int)];
+ char in_l_[PADL_(l_int)]; l_int in; char in_r_[PADR_(l_int)];
+ char offset_l_[PADL_(l_loff_t *)]; l_loff_t * offset; char offset_r_[PADR_(l_loff_t *)];
+ char count_l_[PADL_(l_size_t)]; l_size_t count; char count_r_[PADR_(l_size_t)];
+};
+struct linux_sys_futex_args {
+ char uaddr_l_[PADL_(void *)]; void * uaddr; char uaddr_r_[PADR_(void *)];
+ char op_l_[PADL_(int)]; int op; char op_r_[PADR_(int)];
+ char val_l_[PADL_(uint32_t)]; uint32_t val; char val_r_[PADR_(uint32_t)];
+ char timeout_l_[PADL_(struct l_timespec *)]; struct l_timespec * timeout; char timeout_r_[PADR_(struct l_timespec *)];
+ char uaddr2_l_[PADL_(uint32_t *)]; uint32_t * uaddr2; char uaddr2_r_[PADR_(uint32_t *)];
+ char val3_l_[PADL_(uint32_t)]; uint32_t val3; char val3_r_[PADR_(uint32_t)];
+};
+struct linux_sched_setaffinity_args {
+ char pid_l_[PADL_(l_pid_t)]; l_pid_t pid; char pid_r_[PADR_(l_pid_t)];
+ char len_l_[PADL_(l_uint)]; l_uint len; char len_r_[PADR_(l_uint)];
+ char user_mask_ptr_l_[PADL_(l_ulong *)]; l_ulong * user_mask_ptr; char user_mask_ptr_r_[PADR_(l_ulong *)];
+};
+struct linux_sched_getaffinity_args {
+ char pid_l_[PADL_(l_pid_t)]; l_pid_t pid; char pid_r_[PADR_(l_pid_t)];
+ char len_l_[PADL_(l_uint)]; l_uint len; char len_r_[PADR_(l_uint)];
+ char user_mask_ptr_l_[PADL_(l_ulong *)]; l_ulong * user_mask_ptr; char user_mask_ptr_r_[PADR_(l_ulong *)];
+};
+struct linux_exit_group_args {
+ char error_code_l_[PADL_(int)]; int error_code; char error_code_r_[PADR_(int)];
+};
+struct linux_lookup_dcookie_args {
+ register_t dummy;
+};
+struct linux_epoll_create_args {
+ char size_l_[PADL_(l_int)]; l_int size; char size_r_[PADR_(l_int)];
+};
+struct linux_epoll_ctl_args {
+ char epfd_l_[PADL_(l_int)]; l_int epfd; char epfd_r_[PADR_(l_int)];
+ char op_l_[PADL_(l_int)]; l_int op; char op_r_[PADR_(l_int)];
+ char fd_l_[PADL_(l_int)]; l_int fd; char fd_r_[PADR_(l_int)];
+ char event_l_[PADL_(struct epoll_event *)]; struct epoll_event * event; char event_r_[PADR_(struct epoll_event *)];
+};
+struct linux_epoll_wait_args {
+ char epfd_l_[PADL_(l_int)]; l_int epfd; char epfd_r_[PADR_(l_int)];
+ char events_l_[PADL_(struct epoll_event *)]; struct epoll_event * events; char events_r_[PADR_(struct epoll_event *)];
+ char maxevents_l_[PADL_(l_int)]; l_int maxevents; char maxevents_r_[PADR_(l_int)];
+ char timeout_l_[PADL_(l_int)]; l_int timeout; char timeout_r_[PADR_(l_int)];
+};
+struct linux_remap_file_pages_args {
+ register_t dummy;
+};
+struct linux_set_tid_address_args {
+ char tidptr_l_[PADL_(int *)]; int * tidptr; char tidptr_r_[PADR_(int *)];
+};
+struct linux_timer_create_args {
+ char clock_id_l_[PADL_(clockid_t)]; clockid_t clock_id; char clock_id_r_[PADR_(clockid_t)];
+ char evp_l_[PADL_(struct sigevent *)]; struct sigevent * evp; char evp_r_[PADR_(struct sigevent *)];
+ char timerid_l_[PADL_(l_timer_t *)]; l_timer_t * timerid; char timerid_r_[PADR_(l_timer_t *)];
+};
+struct linux_timer_settime_args {
+ char timerid_l_[PADL_(l_timer_t)]; l_timer_t timerid; char timerid_r_[PADR_(l_timer_t)];
+ char flags_l_[PADL_(l_int)]; l_int flags; char flags_r_[PADR_(l_int)];
+ char new_l_[PADL_(const struct itimerspec *)]; const struct itimerspec * new; char new_r_[PADR_(const struct itimerspec *)];
+ char old_l_[PADL_(struct itimerspec *)]; struct itimerspec * old; char old_r_[PADR_(struct itimerspec *)];
+};
+struct linux_timer_gettime_args {
+ char timerid_l_[PADL_(l_timer_t)]; l_timer_t timerid; char timerid_r_[PADR_(l_timer_t)];
+ char setting_l_[PADL_(struct itimerspec *)]; struct itimerspec * setting; char setting_r_[PADR_(struct itimerspec *)];
+};
+struct linux_timer_getoverrun_args {
+ char timerid_l_[PADL_(l_timer_t)]; l_timer_t timerid; char timerid_r_[PADR_(l_timer_t)];
+};
+struct linux_timer_delete_args {
+ char timerid_l_[PADL_(l_timer_t)]; l_timer_t timerid; char timerid_r_[PADR_(l_timer_t)];
+};
+struct linux_clock_settime_args {
+ char which_l_[PADL_(clockid_t)]; clockid_t which; char which_r_[PADR_(clockid_t)];
+ char tp_l_[PADL_(struct l_timespec *)]; struct l_timespec * tp; char tp_r_[PADR_(struct l_timespec *)];
+};
+struct linux_clock_gettime_args {
+ char which_l_[PADL_(clockid_t)]; clockid_t which; char which_r_[PADR_(clockid_t)];
+ char tp_l_[PADL_(struct l_timespec *)]; struct l_timespec * tp; char tp_r_[PADR_(struct l_timespec *)];
+};
+struct linux_clock_getres_args {
+ char which_l_[PADL_(clockid_t)]; clockid_t which; char which_r_[PADR_(clockid_t)];
+ char tp_l_[PADL_(struct l_timespec *)]; struct l_timespec * tp; char tp_r_[PADR_(struct l_timespec *)];
+};
+struct linux_clock_nanosleep_args {
+ char which_l_[PADL_(clockid_t)]; clockid_t which; char which_r_[PADR_(clockid_t)];
+ char flags_l_[PADL_(int)]; int flags; char flags_r_[PADR_(int)];
+ char rqtp_l_[PADL_(struct l_timespec *)]; struct l_timespec * rqtp; char rqtp_r_[PADR_(struct l_timespec *)];
+ char rmtp_l_[PADL_(struct l_timespec *)]; struct l_timespec * rmtp; char rmtp_r_[PADR_(struct l_timespec *)];
+};
+struct linux_statfs64_args {
+ char path_l_[PADL_(char *)]; char * path; char path_r_[PADR_(char *)];
+ char bufsize_l_[PADL_(size_t)]; size_t bufsize; char bufsize_r_[PADR_(size_t)];
+ char buf_l_[PADL_(struct l_statfs64_buf *)]; struct l_statfs64_buf * buf; char buf_r_[PADR_(struct l_statfs64_buf *)];
+};
+struct linux_fstatfs64_args {
+ char fd_l_[PADL_(l_uint)]; l_uint fd; char fd_r_[PADR_(l_uint)];
+ char bufsize_l_[PADL_(size_t)]; size_t bufsize; char bufsize_r_[PADR_(size_t)];
+ char buf_l_[PADL_(struct l_statfs64_buf *)]; struct l_statfs64_buf * buf; char buf_r_[PADR_(struct l_statfs64_buf *)];
+};
+struct linux_tgkill_args {
+ char tgid_l_[PADL_(int)]; int tgid; char tgid_r_[PADR_(int)];
+ char pid_l_[PADL_(int)]; int pid; char pid_r_[PADR_(int)];
+ char sig_l_[PADL_(int)]; int sig; char sig_r_[PADR_(int)];
+};
+struct linux_utimes_args {
+ char fname_l_[PADL_(char *)]; char * fname; char fname_r_[PADR_(char *)];
+ char tptr_l_[PADL_(struct l_timeval *)]; struct l_timeval * tptr; char tptr_r_[PADR_(struct l_timeval *)];
+};
+struct linux_fadvise64_64_args {
+ char fd_l_[PADL_(int)]; int fd; char fd_r_[PADR_(int)];
+ char offset_l_[PADL_(l_loff_t)]; l_loff_t offset; char offset_r_[PADR_(l_loff_t)];
+ char len_l_[PADL_(l_loff_t)]; l_loff_t len; char len_r_[PADR_(l_loff_t)];
+ char advice_l_[PADL_(int)]; int advice; char advice_r_[PADR_(int)];
+};
+struct linux_mq_open_args {
+ char name_l_[PADL_(const char *)]; const char * name; char name_r_[PADR_(const char *)];
+ char oflag_l_[PADL_(int)]; int oflag; char oflag_r_[PADR_(int)];
+ char mode_l_[PADL_(mode_t)]; mode_t mode; char mode_r_[PADR_(mode_t)];
+ char attr_l_[PADL_(struct mq_attr *)]; struct mq_attr * attr; char attr_r_[PADR_(struct mq_attr *)];
+};
+struct linux_mq_unlink_args {
+ char name_l_[PADL_(const char *)]; const char * name; char name_r_[PADR_(const char *)];
+};
+struct linux_mq_timedsend_args {
+ char mqd_l_[PADL_(l_mqd_t)]; l_mqd_t mqd; char mqd_r_[PADR_(l_mqd_t)];
+ char msg_ptr_l_[PADL_(const char *)]; const char * msg_ptr; char msg_ptr_r_[PADR_(const char *)];
+ char msg_len_l_[PADL_(size_t)]; size_t msg_len; char msg_len_r_[PADR_(size_t)];
+ char msg_prio_l_[PADL_(unsigned int)]; unsigned int msg_prio; char msg_prio_r_[PADR_(unsigned int)];
+ char abs_timeout_l_[PADL_(const struct l_timespec *)]; const struct l_timespec * abs_timeout; char abs_timeout_r_[PADR_(const struct l_timespec *)];
+};
+struct linux_mq_timedreceive_args {
+ char mqd_l_[PADL_(l_mqd_t)]; l_mqd_t mqd; char mqd_r_[PADR_(l_mqd_t)];
+ char msg_ptr_l_[PADL_(char *)]; char * msg_ptr; char msg_ptr_r_[PADR_(char *)];
+ char msg_len_l_[PADL_(size_t)]; size_t msg_len; char msg_len_r_[PADR_(size_t)];
+ char msg_prio_l_[PADL_(unsigned int)]; unsigned int msg_prio; char msg_prio_r_[PADR_(unsigned int)];
+ char abs_timeout_l_[PADL_(const struct l_timespec *)]; const struct l_timespec * abs_timeout; char abs_timeout_r_[PADR_(const struct l_timespec *)];
+};
+struct linux_mq_notify_args {
+ char mqd_l_[PADL_(l_mqd_t)]; l_mqd_t mqd; char mqd_r_[PADR_(l_mqd_t)];
+ char abs_timeout_l_[PADL_(const struct l_timespec *)]; const struct l_timespec * abs_timeout; char abs_timeout_r_[PADR_(const struct l_timespec *)];
+};
+struct linux_mq_getsetattr_args {
+ char mqd_l_[PADL_(l_mqd_t)]; l_mqd_t mqd; char mqd_r_[PADR_(l_mqd_t)];
+ char attr_l_[PADL_(const struct mq_attr *)]; const struct mq_attr * attr; char attr_r_[PADR_(const struct mq_attr *)];
+ char oattr_l_[PADL_(struct mq_attr *)]; struct mq_attr * oattr; char oattr_r_[PADR_(struct mq_attr *)];
+};
+struct linux_waitid_args {
+ char idtype_l_[PADL_(int)]; int idtype; char idtype_r_[PADR_(int)];
+ char id_l_[PADL_(l_pid_t)]; l_pid_t id; char id_r_[PADR_(l_pid_t)];
+ char info_l_[PADL_(l_siginfo_t *)]; l_siginfo_t * info; char info_r_[PADR_(l_siginfo_t *)];
+ char options_l_[PADL_(int)]; int options; char options_r_[PADR_(int)];
+ char rusage_l_[PADL_(void *)]; void * rusage; char rusage_r_[PADR_(void *)];
+};
+struct linux_socket_args {
+ char domain_l_[PADL_(l_int)]; l_int domain; char domain_r_[PADR_(l_int)];
+ char type_l_[PADL_(l_int)]; l_int type; char type_r_[PADR_(l_int)];
+ char protocol_l_[PADL_(l_int)]; l_int protocol; char protocol_r_[PADR_(l_int)];
+};
+struct linux_bind_args {
+ char s_l_[PADL_(l_int)]; l_int s; char s_r_[PADR_(l_int)];
+ char name_l_[PADL_(l_uintptr_t)]; l_uintptr_t name; char name_r_[PADR_(l_uintptr_t)];
+ char namelen_l_[PADL_(l_int)]; l_int namelen; char namelen_r_[PADR_(l_int)];
+};
+struct linux_connect_args {
+ char s_l_[PADL_(l_int)]; l_int s; char s_r_[PADR_(l_int)];
+ char name_l_[PADL_(l_uintptr_t)]; l_uintptr_t name; char name_r_[PADR_(l_uintptr_t)];
+ char namelen_l_[PADL_(l_int)]; l_int namelen; char namelen_r_[PADR_(l_int)];
+};
+struct linux_listen_args {
+ char s_l_[PADL_(l_int)]; l_int s; char s_r_[PADR_(l_int)];
+ char backlog_l_[PADL_(l_int)]; l_int backlog; char backlog_r_[PADR_(l_int)];
+};
+struct linux_accept_args {
+ char s_l_[PADL_(l_int)]; l_int s; char s_r_[PADR_(l_int)];
+ char addr_l_[PADL_(l_uintptr_t)]; l_uintptr_t addr; char addr_r_[PADR_(l_uintptr_t)];
+ char namelen_l_[PADL_(l_uintptr_t)]; l_uintptr_t namelen; char namelen_r_[PADR_(l_uintptr_t)];
+};
+struct linux_getsockname_args {
+ char s_l_[PADL_(l_int)]; l_int s; char s_r_[PADR_(l_int)];
+ char addr_l_[PADL_(l_uintptr_t)]; l_uintptr_t addr; char addr_r_[PADR_(l_uintptr_t)];
+ char namelen_l_[PADL_(l_uintptr_t)]; l_uintptr_t namelen; char namelen_r_[PADR_(l_uintptr_t)];
+};
+struct linux_getpeername_args {
+ char s_l_[PADL_(l_int)]; l_int s; char s_r_[PADR_(l_int)];
+ char addr_l_[PADL_(l_uintptr_t)]; l_uintptr_t addr; char addr_r_[PADR_(l_uintptr_t)];
+ char namelen_l_[PADL_(l_uintptr_t)]; l_uintptr_t namelen; char namelen_r_[PADR_(l_uintptr_t)];
+};
+struct linux_socketpair_args {
+ char domain_l_[PADL_(l_int)]; l_int domain; char domain_r_[PADR_(l_int)];
+ char type_l_[PADL_(l_int)]; l_int type; char type_r_[PADR_(l_int)];
+ char protocol_l_[PADL_(l_int)]; l_int protocol; char protocol_r_[PADR_(l_int)];
+ char rsv_l_[PADL_(l_uintptr_t)]; l_uintptr_t rsv; char rsv_r_[PADR_(l_uintptr_t)];
+};
+struct linux_send_args {
+ char s_l_[PADL_(int)]; int s; char s_r_[PADR_(int)];
+ char msg_l_[PADL_(l_uintptr_t)]; l_uintptr_t msg; char msg_r_[PADR_(l_uintptr_t)];
+ char len_l_[PADL_(int)]; int len; char len_r_[PADR_(int)];
+ char flags_l_[PADL_(int)]; int flags; char flags_r_[PADR_(int)];
+};
+struct linux_sendto_args {
+ char s_l_[PADL_(l_int)]; l_int s; char s_r_[PADR_(l_int)];
+ char msg_l_[PADL_(l_uintptr_t)]; l_uintptr_t msg; char msg_r_[PADR_(l_uintptr_t)];
+ char len_l_[PADL_(l_int)]; l_int len; char len_r_[PADR_(l_int)];
+ char flags_l_[PADL_(l_int)]; l_int flags; char flags_r_[PADR_(l_int)];
+ char to_l_[PADL_(l_uintptr_t)]; l_uintptr_t to; char to_r_[PADR_(l_uintptr_t)];
+ char tolen_l_[PADL_(l_int)]; l_int tolen; char tolen_r_[PADR_(l_int)];
+};
+struct linux_recv_args {
+ char s_l_[PADL_(int)]; int s; char s_r_[PADR_(int)];
+ char msg_l_[PADL_(l_uintptr_t)]; l_uintptr_t msg; char msg_r_[PADR_(l_uintptr_t)];
+ char len_l_[PADL_(int)]; int len; char len_r_[PADR_(int)];
+ char flags_l_[PADL_(int)]; int flags; char flags_r_[PADR_(int)];
+};
+struct linux_recvfrom_args {
+ char s_l_[PADL_(l_int)]; l_int s; char s_r_[PADR_(l_int)];
+ char buf_l_[PADL_(l_uintptr_t)]; l_uintptr_t buf; char buf_r_[PADR_(l_uintptr_t)];
+ char len_l_[PADL_(l_size_t)]; l_size_t len; char len_r_[PADR_(l_size_t)];
+ char flags_l_[PADL_(l_int)]; l_int flags; char flags_r_[PADR_(l_int)];
+ char from_l_[PADL_(l_uintptr_t)]; l_uintptr_t from; char from_r_[PADR_(l_uintptr_t)];
+ char fromlen_l_[PADL_(l_uintptr_t)]; l_uintptr_t fromlen; char fromlen_r_[PADR_(l_uintptr_t)];
+};
+struct linux_shutdown_args {
+ char s_l_[PADL_(l_int)]; l_int s; char s_r_[PADR_(l_int)];
+ char how_l_[PADL_(l_int)]; l_int how; char how_r_[PADR_(l_int)];
+};
+struct linux_setsockopt_args {
+ char s_l_[PADL_(l_int)]; l_int s; char s_r_[PADR_(l_int)];
+ char level_l_[PADL_(l_int)]; l_int level; char level_r_[PADR_(l_int)];
+ char optname_l_[PADL_(l_int)]; l_int optname; char optname_r_[PADR_(l_int)];
+ char optval_l_[PADL_(l_uintptr_t)]; l_uintptr_t optval; char optval_r_[PADR_(l_uintptr_t)];
+ char optlen_l_[PADL_(l_int)]; l_int optlen; char optlen_r_[PADR_(l_int)];
+};
+struct linux_getsockopt_args {
+ char s_l_[PADL_(l_int)]; l_int s; char s_r_[PADR_(l_int)];
+ char level_l_[PADL_(l_int)]; l_int level; char level_r_[PADR_(l_int)];
+ char optname_l_[PADL_(l_int)]; l_int optname; char optname_r_[PADR_(l_int)];
+ char optval_l_[PADL_(l_uintptr_t)]; l_uintptr_t optval; char optval_r_[PADR_(l_uintptr_t)];
+ char optlen_l_[PADL_(l_uintptr_t)]; l_uintptr_t optlen; char optlen_r_[PADR_(l_uintptr_t)];
+};
+struct linux_sendmsg_args {
+ char s_l_[PADL_(l_int)]; l_int s; char s_r_[PADR_(l_int)];
+ char msg_l_[PADL_(l_uintptr_t)]; l_uintptr_t msg; char msg_r_[PADR_(l_uintptr_t)];
+ char flags_l_[PADL_(l_int)]; l_int flags; char flags_r_[PADR_(l_int)];
+};
+struct linux_recvmsg_args {
+ char s_l_[PADL_(l_int)]; l_int s; char s_r_[PADR_(l_int)];
+ char msg_l_[PADL_(l_uintptr_t)]; l_uintptr_t msg; char msg_r_[PADR_(l_uintptr_t)];
+ char flags_l_[PADL_(l_int)]; l_int flags; char flags_r_[PADR_(l_int)];
+};
+struct linux_semop_args {
+ char semid_l_[PADL_(l_int)]; l_int semid; char semid_r_[PADR_(l_int)];
+ char tsops_l_[PADL_(struct l_sembuf *)]; struct l_sembuf * tsops; char tsops_r_[PADR_(struct l_sembuf *)];
+ char nsops_l_[PADL_(l_uint)]; l_uint nsops; char nsops_r_[PADR_(l_uint)];
+};
+struct linux_semget_args {
+ char key_l_[PADL_(l_key_t)]; l_key_t key; char key_r_[PADR_(l_key_t)];
+ char nsems_l_[PADL_(l_int)]; l_int nsems; char nsems_r_[PADR_(l_int)];
+ char semflg_l_[PADL_(l_int)]; l_int semflg; char semflg_r_[PADR_(l_int)];
+};
+struct linux_semctl_args {
+ char semid_l_[PADL_(l_int)]; l_int semid; char semid_r_[PADR_(l_int)];
+ char semnum_l_[PADL_(l_int)]; l_int semnum; char semnum_r_[PADR_(l_int)];
+ char cmd_l_[PADL_(l_int)]; l_int cmd; char cmd_r_[PADR_(l_int)];
+ char arg_l_[PADL_(union l_semun)]; union l_semun arg; char arg_r_[PADR_(union l_semun)];
+};
+struct linux_msgsnd_args {
+ char msqid_l_[PADL_(l_int)]; l_int msqid; char msqid_r_[PADR_(l_int)];
+ char msgp_l_[PADL_(struct l_msgbuf *)]; struct l_msgbuf * msgp; char msgp_r_[PADR_(struct l_msgbuf *)];
+ char msgsz_l_[PADL_(l_size_t)]; l_size_t msgsz; char msgsz_r_[PADR_(l_size_t)];
+ char msgflg_l_[PADL_(l_int)]; l_int msgflg; char msgflg_r_[PADR_(l_int)];
+};
+struct linux_msgrcv_args {
+ char msqid_l_[PADL_(l_int)]; l_int msqid; char msqid_r_[PADR_(l_int)];
+ char msgp_l_[PADL_(struct l_msgbuf *)]; struct l_msgbuf * msgp; char msgp_r_[PADR_(struct l_msgbuf *)];
+ char msgsz_l_[PADL_(l_size_t)]; l_size_t msgsz; char msgsz_r_[PADR_(l_size_t)];
+ char msgtyp_l_[PADL_(l_long)]; l_long msgtyp; char msgtyp_r_[PADR_(l_long)];
+ char msgflg_l_[PADL_(l_int)]; l_int msgflg; char msgflg_r_[PADR_(l_int)];
+};
+struct linux_msgget_args {
+ char key_l_[PADL_(l_key_t)]; l_key_t key; char key_r_[PADR_(l_key_t)];
+ char msgflg_l_[PADL_(l_int)]; l_int msgflg; char msgflg_r_[PADR_(l_int)];
+};
+struct linux_msgctl_args {
+ char msqid_l_[PADL_(l_int)]; l_int msqid; char msqid_r_[PADR_(l_int)];
+ char cmd_l_[PADL_(l_int)]; l_int cmd; char cmd_r_[PADR_(l_int)];
+ char buf_l_[PADL_(struct l_msqid_ds *)]; struct l_msqid_ds * buf; char buf_r_[PADR_(struct l_msqid_ds *)];
+};
+struct linux_shmat_args {
+ char shmid_l_[PADL_(l_int)]; l_int shmid; char shmid_r_[PADR_(l_int)];
+ char shmaddr_l_[PADL_(char *)]; char * shmaddr; char shmaddr_r_[PADR_(char *)];
+ char shmflg_l_[PADL_(l_int)]; l_int shmflg; char shmflg_r_[PADR_(l_int)];
+};
+struct linux_shmdt_args {
+ char shmaddr_l_[PADL_(char *)]; char * shmaddr; char shmaddr_r_[PADR_(char *)];
+};
+struct linux_shmget_args {
+ char key_l_[PADL_(l_key_t)]; l_key_t key; char key_r_[PADR_(l_key_t)];
+ char size_l_[PADL_(l_size_t)]; l_size_t size; char size_r_[PADR_(l_size_t)];
+ char shmflg_l_[PADL_(l_int)]; l_int shmflg; char shmflg_r_[PADR_(l_int)];
+};
+struct linux_shmctl_args {
+ char shmid_l_[PADL_(l_int)]; l_int shmid; char shmid_r_[PADR_(l_int)];
+ char cmd_l_[PADL_(l_int)]; l_int cmd; char cmd_r_[PADR_(l_int)];
+ char buf_l_[PADL_(struct l_shmid_ds *)]; struct l_shmid_ds * buf; char buf_r_[PADR_(struct l_shmid_ds *)];
+};
+struct linux_add_key_args {
+ register_t dummy;
+};
+struct linux_request_key_args {
+ register_t dummy;
+};
+struct linux_keyctl_args {
+ register_t dummy;
+};
+struct linux_ioprio_set_args {
+ register_t dummy;
+};
+struct linux_ioprio_get_args {
+ register_t dummy;
+};
+struct linux_inotify_init_args {
+ register_t dummy;
+};
+struct linux_inotify_add_watch_args {
+ register_t dummy;
+};
+struct linux_inotify_rm_watch_args {
+ register_t dummy;
+};
+struct linux_mbind_args {
+ register_t dummy;
+};
+struct linux_get_mempolicy_args {
+ register_t dummy;
+};
+struct linux_set_mempolicy_args {
+ register_t dummy;
+};
+struct linux_openat_args {
+ char dfd_l_[PADL_(l_int)]; l_int dfd; char dfd_r_[PADR_(l_int)];
+ char filename_l_[PADL_(const char *)]; const char * filename; char filename_r_[PADR_(const char *)];
+ char flags_l_[PADL_(l_int)]; l_int flags; char flags_r_[PADR_(l_int)];
+ char mode_l_[PADL_(l_int)]; l_int mode; char mode_r_[PADR_(l_int)];
+};
+struct linux_mkdirat_args {
+ char dfd_l_[PADL_(l_int)]; l_int dfd; char dfd_r_[PADR_(l_int)];
+ char pathname_l_[PADL_(const char *)]; const char * pathname; char pathname_r_[PADR_(const char *)];
+ char mode_l_[PADL_(l_int)]; l_int mode; char mode_r_[PADR_(l_int)];
+};
+struct linux_mknodat_args {
+ char dfd_l_[PADL_(l_int)]; l_int dfd; char dfd_r_[PADR_(l_int)];
+ char filename_l_[PADL_(const char *)]; const char * filename; char filename_r_[PADR_(const char *)];
+ char mode_l_[PADL_(l_int)]; l_int mode; char mode_r_[PADR_(l_int)];
+ char dev_l_[PADL_(l_uint)]; l_uint dev; char dev_r_[PADR_(l_uint)];
+};
+struct linux_fchownat_args {
+ char dfd_l_[PADL_(l_int)]; l_int dfd; char dfd_r_[PADR_(l_int)];
+ char filename_l_[PADL_(const char *)]; const char * filename; char filename_r_[PADR_(const char *)];
+ char uid_l_[PADL_(l_uid16_t)]; l_uid16_t uid; char uid_r_[PADR_(l_uid16_t)];
+ char gid_l_[PADL_(l_gid16_t)]; l_gid16_t gid; char gid_r_[PADR_(l_gid16_t)];
+ char flag_l_[PADL_(l_int)]; l_int flag; char flag_r_[PADR_(l_int)];
+};
+struct linux_futimesat_args {
+ char dfd_l_[PADL_(l_int)]; l_int dfd; char dfd_r_[PADR_(l_int)];
+ char filename_l_[PADL_(char *)]; char * filename; char filename_r_[PADR_(char *)];
+ char utimes_l_[PADL_(struct l_timeval *)]; struct l_timeval * utimes; char utimes_r_[PADR_(struct l_timeval *)];
+};
+struct linux_fstatat64_args {
+ char dfd_l_[PADL_(l_int)]; l_int dfd; char dfd_r_[PADR_(l_int)];
+ char pathname_l_[PADL_(char *)]; char * pathname; char pathname_r_[PADR_(char *)];
+ char statbuf_l_[PADL_(struct l_stat64 *)]; struct l_stat64 * statbuf; char statbuf_r_[PADR_(struct l_stat64 *)];
+ char flag_l_[PADL_(l_int)]; l_int flag; char flag_r_[PADR_(l_int)];
+};
+struct linux_unlinkat_args {
+ char dfd_l_[PADL_(l_int)]; l_int dfd; char dfd_r_[PADR_(l_int)];
+ char pathname_l_[PADL_(const char *)]; const char * pathname; char pathname_r_[PADR_(const char *)];
+ char flag_l_[PADL_(l_int)]; l_int flag; char flag_r_[PADR_(l_int)];
+};
+struct linux_renameat_args {
+ char olddfd_l_[PADL_(l_int)]; l_int olddfd; char olddfd_r_[PADR_(l_int)];
+ char oldname_l_[PADL_(const char *)]; const char * oldname; char oldname_r_[PADR_(const char *)];
+ char newdfd_l_[PADL_(l_int)]; l_int newdfd; char newdfd_r_[PADR_(l_int)];
+ char newname_l_[PADL_(const char *)]; const char * newname; char newname_r_[PADR_(const char *)];
+};
+struct linux_linkat_args {
+ char olddfd_l_[PADL_(l_int)]; l_int olddfd; char olddfd_r_[PADR_(l_int)];
+ char oldname_l_[PADL_(const char *)]; const char * oldname; char oldname_r_[PADR_(const char *)];
+ char newdfd_l_[PADL_(l_int)]; l_int newdfd; char newdfd_r_[PADR_(l_int)];
+ char newname_l_[PADL_(const char *)]; const char * newname; char newname_r_[PADR_(const char *)];
+ char flag_l_[PADL_(l_int)]; l_int flag; char flag_r_[PADR_(l_int)];
+};
+struct linux_symlinkat_args {
+ char oldname_l_[PADL_(const char *)]; const char * oldname; char oldname_r_[PADR_(const char *)];
+ char newdfd_l_[PADL_(l_int)]; l_int newdfd; char newdfd_r_[PADR_(l_int)];
+ char newname_l_[PADL_(const char *)]; const char * newname; char newname_r_[PADR_(const char *)];
+};
+struct linux_readlinkat_args {
+ char dfd_l_[PADL_(l_int)]; l_int dfd; char dfd_r_[PADR_(l_int)];
+ char path_l_[PADL_(const char *)]; const char * path; char path_r_[PADR_(const char *)];
+ char buf_l_[PADL_(char *)]; char * buf; char buf_r_[PADR_(char *)];
+ char bufsiz_l_[PADL_(l_int)]; l_int bufsiz; char bufsiz_r_[PADR_(l_int)];
+};
+struct linux_fchmodat_args {
+ char dfd_l_[PADL_(l_int)]; l_int dfd; char dfd_r_[PADR_(l_int)];
+ char filename_l_[PADL_(const char *)]; const char * filename; char filename_r_[PADR_(const char *)];
+ char mode_l_[PADL_(l_mode_t)]; l_mode_t mode; char mode_r_[PADR_(l_mode_t)];
+};
+struct linux_faccessat_args {
+ char dfd_l_[PADL_(l_int)]; l_int dfd; char dfd_r_[PADR_(l_int)];
+ char filename_l_[PADL_(const char *)]; const char * filename; char filename_r_[PADR_(const char *)];
+ char amode_l_[PADL_(l_int)]; l_int amode; char amode_r_[PADR_(l_int)];
+};
+struct linux_pselect6_args {
+ char nfds_l_[PADL_(l_int)]; l_int nfds; char nfds_r_[PADR_(l_int)];
+ char readfds_l_[PADL_(l_fd_set *)]; l_fd_set * readfds; char readfds_r_[PADR_(l_fd_set *)];
+ char writefds_l_[PADL_(l_fd_set *)]; l_fd_set * writefds; char writefds_r_[PADR_(l_fd_set *)];
+ char exceptfds_l_[PADL_(l_fd_set *)]; l_fd_set * exceptfds; char exceptfds_r_[PADR_(l_fd_set *)];
+ char tsp_l_[PADL_(struct l_timespec *)]; struct l_timespec * tsp; char tsp_r_[PADR_(struct l_timespec *)];
+ char sig_l_[PADL_(l_uintptr_t *)]; l_uintptr_t * sig; char sig_r_[PADR_(l_uintptr_t *)];
+};
+struct linux_ppoll_args {
+ char fds_l_[PADL_(struct pollfd *)]; struct pollfd * fds; char fds_r_[PADR_(struct pollfd *)];
+ char nfds_l_[PADL_(uint32_t)]; uint32_t nfds; char nfds_r_[PADR_(uint32_t)];
+ char tsp_l_[PADL_(struct l_timespec *)]; struct l_timespec * tsp; char tsp_r_[PADR_(struct l_timespec *)];
+ char sset_l_[PADL_(l_sigset_t *)]; l_sigset_t * sset; char sset_r_[PADR_(l_sigset_t *)];
+ char ssize_l_[PADL_(l_size_t)]; l_size_t ssize; char ssize_r_[PADR_(l_size_t)];
+};
+struct linux_unshare_args {
+ register_t dummy;
+};
+struct linux_set_robust_list_args {
+ char head_l_[PADL_(struct linux_robust_list_head *)]; struct linux_robust_list_head * head; char head_r_[PADR_(struct linux_robust_list_head *)];
+ char len_l_[PADL_(l_size_t)]; l_size_t len; char len_r_[PADR_(l_size_t)];
+};
+struct linux_get_robust_list_args {
+ char pid_l_[PADL_(l_int)]; l_int pid; char pid_r_[PADR_(l_int)];
+ char head_l_[PADL_(struct linux_robust_list_head **)]; struct linux_robust_list_head ** head; char head_r_[PADR_(struct linux_robust_list_head **)];
+ char len_l_[PADL_(l_size_t *)]; l_size_t * len; char len_r_[PADR_(l_size_t *)];
+};
+struct linux_splice_args {
+ register_t dummy;
+};
+struct linux_sync_file_range_args {
+ char fd_l_[PADL_(l_int)]; l_int fd; char fd_r_[PADR_(l_int)];
+ char offset_l_[PADL_(l_loff_t)]; l_loff_t offset; char offset_r_[PADR_(l_loff_t)];
+ char nbytes_l_[PADL_(l_loff_t)]; l_loff_t nbytes; char nbytes_r_[PADR_(l_loff_t)];
+ char flags_l_[PADL_(unsigned int)]; unsigned int flags; char flags_r_[PADR_(unsigned int)];
+};
+struct linux_tee_args {
+ register_t dummy;
+};
+struct linux_vmsplice_args {
+ register_t dummy;
+};
+struct linux_move_pages_args {
+ register_t dummy;
+};
+struct linux_getcpu_args {
+ char cpu_l_[PADL_(l_uint *)]; l_uint * cpu; char cpu_r_[PADR_(l_uint *)];
+ char node_l_[PADL_(l_uint *)]; l_uint * node; char node_r_[PADR_(l_uint *)];
+ char cache_l_[PADL_(void *)]; void * cache; char cache_r_[PADR_(void *)];
+};
+struct linux_epoll_pwait_args {
+ char epfd_l_[PADL_(l_int)]; l_int epfd; char epfd_r_[PADR_(l_int)];
+ char events_l_[PADL_(struct epoll_event *)]; struct epoll_event * events; char events_r_[PADR_(struct epoll_event *)];
+ char maxevents_l_[PADL_(l_int)]; l_int maxevents; char maxevents_r_[PADR_(l_int)];
+ char timeout_l_[PADL_(l_int)]; l_int timeout; char timeout_r_[PADR_(l_int)];
+ char mask_l_[PADL_(l_sigset_t *)]; l_sigset_t * mask; char mask_r_[PADR_(l_sigset_t *)];
+};
+struct linux_kexec_load_args {
+ register_t dummy;
+};
+struct linux_utimensat_args {
+ char dfd_l_[PADL_(l_int)]; l_int dfd; char dfd_r_[PADR_(l_int)];
+ char pathname_l_[PADL_(const char *)]; const char * pathname; char pathname_r_[PADR_(const char *)];
+ char times_l_[PADL_(const struct l_timespec *)]; const struct l_timespec * times; char times_r_[PADR_(const struct l_timespec *)];
+ char flags_l_[PADL_(l_int)]; l_int flags; char flags_r_[PADR_(l_int)];
+};
+struct linux_signalfd_args {
+ register_t dummy;
+};
+struct linux_timerfd_create_args {
+ register_t dummy;
+};
+struct linux_eventfd_args {
+ char initval_l_[PADL_(l_uint)]; l_uint initval; char initval_r_[PADR_(l_uint)];
+};
+struct linux_fallocate_args {
+ char fd_l_[PADL_(l_int)]; l_int fd; char fd_r_[PADR_(l_int)];
+ char mode_l_[PADL_(l_int)]; l_int mode; char mode_r_[PADR_(l_int)];
+ char offset_l_[PADL_(l_loff_t)]; l_loff_t offset; char offset_r_[PADR_(l_loff_t)];
+ char len_l_[PADL_(l_loff_t)]; l_loff_t len; char len_r_[PADR_(l_loff_t)];
+};
+struct linux_timerfd_settime_args {
+ register_t dummy;
+};
+struct linux_timerfd_gettime_args {
+ register_t dummy;
+};
+struct linux_signalfd4_args {
+ register_t dummy;
+};
+struct linux_eventfd2_args {
+ char initval_l_[PADL_(l_uint)]; l_uint initval; char initval_r_[PADR_(l_uint)];
+ char flags_l_[PADL_(l_int)]; l_int flags; char flags_r_[PADR_(l_int)];
+};
+struct linux_epoll_create1_args {
+ char flags_l_[PADL_(l_int)]; l_int flags; char flags_r_[PADR_(l_int)];
+};
+struct linux_dup3_args {
+ char oldfd_l_[PADL_(l_int)]; l_int oldfd; char oldfd_r_[PADR_(l_int)];
+ char newfd_l_[PADL_(l_int)]; l_int newfd; char newfd_r_[PADR_(l_int)];
+ char flags_l_[PADL_(l_int)]; l_int flags; char flags_r_[PADR_(l_int)];
+};
+struct linux_pipe2_args {
+ char pipefds_l_[PADL_(l_int *)]; l_int * pipefds; char pipefds_r_[PADR_(l_int *)];
+ char flags_l_[PADL_(l_int)]; l_int flags; char flags_r_[PADR_(l_int)];
+};
+struct linux_inotify_init1_args {
+ register_t dummy;
+};
+struct linux_preadv_args {
+ register_t dummy;
+};
+struct linux_pwritev_args {
+ register_t dummy;
+};
+struct linux_rt_tsigqueueinfo_args {
+ register_t dummy;
+};
+struct linux_perf_event_open_args {
+ register_t dummy;
+};
+struct linux_recvmmsg_args {
+ char s_l_[PADL_(l_int)]; l_int s; char s_r_[PADR_(l_int)];
+ char msg_l_[PADL_(struct l_mmsghdr *)]; struct l_mmsghdr * msg; char msg_r_[PADR_(struct l_mmsghdr *)];
+ char vlen_l_[PADL_(l_uint)]; l_uint vlen; char vlen_r_[PADR_(l_uint)];
+ char flags_l_[PADL_(l_uint)]; l_uint flags; char flags_r_[PADR_(l_uint)];
+ char timeout_l_[PADL_(struct l_timespec *)]; struct l_timespec * timeout; char timeout_r_[PADR_(struct l_timespec *)];
+};
+struct linux_accept4_args {
+ char s_l_[PADL_(l_int)]; l_int s; char s_r_[PADR_(l_int)];
+ char addr_l_[PADL_(l_uintptr_t)]; l_uintptr_t addr; char addr_r_[PADR_(l_uintptr_t)];
+ char namelen_l_[PADL_(l_uintptr_t)]; l_uintptr_t namelen; char namelen_r_[PADR_(l_uintptr_t)];
+ char flags_l_[PADL_(int)]; int flags; char flags_r_[PADR_(int)];
+};
+struct linux_fanotify_init_args {
+ register_t dummy;
+};
+struct linux_fanotify_mark_args {
+ register_t dummy;
+};
+struct linux_prlimit64_args {
+ char pid_l_[PADL_(l_pid_t)]; l_pid_t pid; char pid_r_[PADR_(l_pid_t)];
+ char resource_l_[PADL_(l_uint)]; l_uint resource; char resource_r_[PADR_(l_uint)];
+ char new_l_[PADL_(struct rlimit *)]; struct rlimit * new; char new_r_[PADR_(struct rlimit *)];
+ char old_l_[PADL_(struct rlimit *)]; struct rlimit * old; char old_r_[PADR_(struct rlimit *)];
+};
+struct linux_name_to_handle_at_args {
+ register_t dummy;
+};
+struct linux_open_by_handle_at_args {
+ register_t dummy;
+};
+struct linux_clock_adjtime_args {
+ register_t dummy;
+};
+struct linux_syncfs_args {
+ char fd_l_[PADL_(l_int)]; l_int fd; char fd_r_[PADR_(l_int)];
+};
+struct linux_sendmmsg_args {
+ char s_l_[PADL_(l_int)]; l_int s; char s_r_[PADR_(l_int)];
+ char msg_l_[PADL_(struct l_mmsghdr *)]; struct l_mmsghdr * msg; char msg_r_[PADR_(struct l_mmsghdr *)];
+ char vlen_l_[PADL_(l_uint)]; l_uint vlen; char vlen_r_[PADR_(l_uint)];
+ char flags_l_[PADL_(l_uint)]; l_uint flags; char flags_r_[PADR_(l_uint)];
+};
+struct linux_setns_args {
+ register_t dummy;
+};
+struct linux_process_vm_readv_args {
+ register_t dummy;
+};
+struct linux_process_vm_writev_args {
+ register_t dummy;
+};
+struct linux_set_tls_args {
+ char tls_l_[PADL_(void*)]; void* tls; char tls_r_[PADR_(void*)];
+};
+#define nosys linux_nosys
+int linux_exit(struct thread *, struct linux_exit_args *);
+int linux_fork(struct thread *, struct linux_fork_args *);
+int linux_open(struct thread *, struct linux_open_args *);
+int linux_creat(struct thread *, struct linux_creat_args *);
+int linux_link(struct thread *, struct linux_link_args *);
+int linux_unlink(struct thread *, struct linux_unlink_args *);
+int linux_execve(struct thread *, struct linux_execve_args *);
+int linux_chdir(struct thread *, struct linux_chdir_args *);
+int linux_mknod(struct thread *, struct linux_mknod_args *);
+int linux_chmod(struct thread *, struct linux_chmod_args *);
+int linux_lchown16(struct thread *, struct linux_lchown16_args *);
+int linux_lseek(struct thread *, struct linux_lseek_args *);
+int linux_getpid(struct thread *, struct linux_getpid_args *);
+int linux_mount(struct thread *, struct linux_mount_args *);
+int linux_setuid16(struct thread *, struct linux_setuid16_args *);
+int linux_getuid16(struct thread *, struct linux_getuid16_args *);
+int linux_pause(struct thread *, struct linux_pause_args *);
+int linux_access(struct thread *, struct linux_access_args *);
+int linux_nice(struct thread *, struct linux_nice_args *);
+int linux_kill(struct thread *, struct linux_kill_args *);
+int linux_rename(struct thread *, struct linux_rename_args *);
+int linux_mkdir(struct thread *, struct linux_mkdir_args *);
+int linux_rmdir(struct thread *, struct linux_rmdir_args *);
+int linux_pipe(struct thread *, struct linux_pipe_args *);
+int linux_times(struct thread *, struct linux_times_args *);
+int linux_brk(struct thread *, struct linux_brk_args *);
+int linux_setgid16(struct thread *, struct linux_setgid16_args *);
+int linux_getgid16(struct thread *, struct linux_getgid16_args *);
+int linux_geteuid16(struct thread *, struct linux_geteuid16_args *);
+int linux_getegid16(struct thread *, struct linux_getegid16_args *);
+int linux_umount(struct thread *, struct linux_umount_args *);
+int linux_ioctl(struct thread *, struct linux_ioctl_args *);
+int linux_fcntl(struct thread *, struct linux_fcntl_args *);
+int linux_ustat(struct thread *, struct linux_ustat_args *);
+int linux_getppid(struct thread *, struct linux_getppid_args *);
+int linux_sigaction(struct thread *, struct linux_sigaction_args *);
+int linux_setreuid16(struct thread *, struct linux_setreuid16_args *);
+int linux_setregid16(struct thread *, struct linux_setregid16_args *);
+int linux_sigsuspend(struct thread *, struct linux_sigsuspend_args *);
+int linux_sigpending(struct thread *, struct linux_sigpending_args *);
+int linux_sethostname(struct thread *, struct linux_sethostname_args *);
+int linux_setrlimit(struct thread *, struct linux_setrlimit_args *);
+int linux_gettimeofday(struct thread *, struct linux_gettimeofday_args *);
+int linux_settimeofday(struct thread *, struct linux_settimeofday_args *);
+int linux_getgroups16(struct thread *, struct linux_getgroups16_args *);
+int linux_setgroups16(struct thread *, struct linux_setgroups16_args *);
+int linux_symlink(struct thread *, struct linux_symlink_args *);
+int linux_readlink(struct thread *, struct linux_readlink_args *);
+int linux_reboot(struct thread *, struct linux_reboot_args *);
+int linux_truncate(struct thread *, struct linux_truncate_args *);
+int linux_ftruncate(struct thread *, struct linux_ftruncate_args *);
+int linux_getpriority(struct thread *, struct linux_getpriority_args *);
+int linux_statfs(struct thread *, struct linux_statfs_args *);
+int linux_fstatfs(struct thread *, struct linux_fstatfs_args *);
+int linux_syslog(struct thread *, struct linux_syslog_args *);
+int linux_setitimer(struct thread *, struct linux_setitimer_args *);
+int linux_getitimer(struct thread *, struct linux_getitimer_args *);
+int linux_newstat(struct thread *, struct linux_newstat_args *);
+int linux_newlstat(struct thread *, struct linux_newlstat_args *);
+int linux_newfstat(struct thread *, struct linux_newfstat_args *);
+int linux_vhangup(struct thread *, struct linux_vhangup_args *);
+int linux_wait4(struct thread *, struct linux_wait4_args *);
+int linux_swapoff(struct thread *, struct linux_swapoff_args *);
+int linux_sysinfo(struct thread *, struct linux_sysinfo_args *);
+int linux_sigreturn(struct thread *, struct linux_sigreturn_args *);
+int linux_clone(struct thread *, struct linux_clone_args *);
+int linux_setdomainname(struct thread *, struct linux_setdomainname_args *);
+int linux_newuname(struct thread *, struct linux_newuname_args *);
+int linux_adjtimex(struct thread *, struct linux_adjtimex_args *);
+int linux_mprotect(struct thread *, struct linux_mprotect_args *);
+int linux_sigprocmask(struct thread *, struct linux_sigprocmask_args *);
+int linux_init_module(struct thread *, struct linux_init_module_args *);
+int linux_delete_module(struct thread *, struct linux_delete_module_args *);
+int linux_quotactl(struct thread *, struct linux_quotactl_args *);
+int linux_bdflush(struct thread *, struct linux_bdflush_args *);
+int linux_sysfs(struct thread *, struct linux_sysfs_args *);
+int linux_personality(struct thread *, struct linux_personality_args *);
+int linux_setfsuid16(struct thread *, struct linux_setfsuid16_args *);
+int linux_setfsgid16(struct thread *, struct linux_setfsgid16_args *);
+int linux_llseek(struct thread *, struct linux_llseek_args *);
+int linux_getdents(struct thread *, struct linux_getdents_args *);
+int linux_select(struct thread *, struct linux_select_args *);
+int linux_msync(struct thread *, struct linux_msync_args *);
+int linux_getsid(struct thread *, struct linux_getsid_args *);
+int linux_fdatasync(struct thread *, struct linux_fdatasync_args *);
+int linux_sysctl(struct thread *, struct linux_sysctl_args *);
+int linux_sched_setparam(struct thread *, struct linux_sched_setparam_args *);
+int linux_sched_getparam(struct thread *, struct linux_sched_getparam_args *);
+int linux_sched_setscheduler(struct thread *, struct linux_sched_setscheduler_args *);
+int linux_sched_getscheduler(struct thread *, struct linux_sched_getscheduler_args *);
+int linux_sched_get_priority_max(struct thread *, struct linux_sched_get_priority_max_args *);
+int linux_sched_get_priority_min(struct thread *, struct linux_sched_get_priority_min_args *);
+int linux_sched_rr_get_interval(struct thread *, struct linux_sched_rr_get_interval_args *);
+int linux_nanosleep(struct thread *, struct linux_nanosleep_args *);
+int linux_mremap(struct thread *, struct linux_mremap_args *);
+int linux_setresuid16(struct thread *, struct linux_setresuid16_args *);
+int linux_getresuid16(struct thread *, struct linux_getresuid16_args *);
+int linux_setresgid16(struct thread *, struct linux_setresgid16_args *);
+int linux_getresgid16(struct thread *, struct linux_getresgid16_args *);
+int linux_prctl(struct thread *, struct linux_prctl_args *);
+int linux_rt_sigreturn(struct thread *, struct linux_rt_sigreturn_args *);
+int linux_rt_sigaction(struct thread *, struct linux_rt_sigaction_args *);
+int linux_rt_sigprocmask(struct thread *, struct linux_rt_sigprocmask_args *);
+int linux_rt_sigpending(struct thread *, struct linux_rt_sigpending_args *);
+int linux_rt_sigtimedwait(struct thread *, struct linux_rt_sigtimedwait_args *);
+int linux_rt_sigqueueinfo(struct thread *, struct linux_rt_sigqueueinfo_args *);
+int linux_rt_sigsuspend(struct thread *, struct linux_rt_sigsuspend_args *);
+int linux_pread(struct thread *, struct linux_pread_args *);
+int linux_pwrite(struct thread *, struct linux_pwrite_args *);
+int linux_chown16(struct thread *, struct linux_chown16_args *);
+int linux_getcwd(struct thread *, struct linux_getcwd_args *);
+int linux_capget(struct thread *, struct linux_capget_args *);
+int linux_capset(struct thread *, struct linux_capset_args *);
+int linux_sigaltstack(struct thread *, struct linux_sigaltstack_args *);
+int linux_sendfile(struct thread *, struct linux_sendfile_args *);
+int linux_vfork(struct thread *, struct linux_vfork_args *);
+int linux_getrlimit(struct thread *, struct linux_getrlimit_args *);
+int linux_mmap2(struct thread *, struct linux_mmap2_args *);
+int linux_truncate64(struct thread *, struct linux_truncate64_args *);
+int linux_ftruncate64(struct thread *, struct linux_ftruncate64_args *);
+int linux_stat64(struct thread *, struct linux_stat64_args *);
+int linux_lstat64(struct thread *, struct linux_lstat64_args *);
+int linux_fstat64(struct thread *, struct linux_fstat64_args *);
+int linux_lchown(struct thread *, struct linux_lchown_args *);
+int linux_getuid(struct thread *, struct linux_getuid_args *);
+int linux_getgid(struct thread *, struct linux_getgid_args *);
+int linux_getgroups(struct thread *, struct linux_getgroups_args *);
+int linux_setgroups(struct thread *, struct linux_setgroups_args *);
+int linux_chown(struct thread *, struct linux_chown_args *);
+int linux_setfsuid(struct thread *, struct linux_setfsuid_args *);
+int linux_setfsgid(struct thread *, struct linux_setfsgid_args *);
+int linux_getdents64(struct thread *, struct linux_getdents64_args *);
+int linux_pivot_root(struct thread *, struct linux_pivot_root_args *);
+int linux_mincore(struct thread *, struct linux_mincore_args *);
+int linux_fcntl64(struct thread *, struct linux_fcntl64_args *);
+int linux_gettid(struct thread *, struct linux_gettid_args *);
+int linux_setxattr(struct thread *, struct linux_setxattr_args *);
+int linux_lsetxattr(struct thread *, struct linux_lsetxattr_args *);
+int linux_fsetxattr(struct thread *, struct linux_fsetxattr_args *);
+int linux_getxattr(struct thread *, struct linux_getxattr_args *);
+int linux_lgetxattr(struct thread *, struct linux_lgetxattr_args *);
+int linux_fgetxattr(struct thread *, struct linux_fgetxattr_args *);
+int linux_listxattr(struct thread *, struct linux_listxattr_args *);
+int linux_llistxattr(struct thread *, struct linux_llistxattr_args *);
+int linux_flistxattr(struct thread *, struct linux_flistxattr_args *);
+int linux_removexattr(struct thread *, struct linux_removexattr_args *);
+int linux_lremovexattr(struct thread *, struct linux_lremovexattr_args *);
+int linux_fremovexattr(struct thread *, struct linux_fremovexattr_args *);
+int linux_tkill(struct thread *, struct linux_tkill_args *);
+int linux_sendfile64(struct thread *, struct linux_sendfile64_args *);
+int linux_sys_futex(struct thread *, struct linux_sys_futex_args *);
+int linux_sched_setaffinity(struct thread *, struct linux_sched_setaffinity_args *);
+int linux_sched_getaffinity(struct thread *, struct linux_sched_getaffinity_args *);
+int linux_exit_group(struct thread *, struct linux_exit_group_args *);
+int linux_lookup_dcookie(struct thread *, struct linux_lookup_dcookie_args *);
+int linux_epoll_create(struct thread *, struct linux_epoll_create_args *);
+int linux_epoll_ctl(struct thread *, struct linux_epoll_ctl_args *);
+int linux_epoll_wait(struct thread *, struct linux_epoll_wait_args *);
+int linux_remap_file_pages(struct thread *, struct linux_remap_file_pages_args *);
+int linux_set_tid_address(struct thread *, struct linux_set_tid_address_args *);
+int linux_timer_create(struct thread *, struct linux_timer_create_args *);
+int linux_timer_settime(struct thread *, struct linux_timer_settime_args *);
+int linux_timer_gettime(struct thread *, struct linux_timer_gettime_args *);
+int linux_timer_getoverrun(struct thread *, struct linux_timer_getoverrun_args *);
+int linux_timer_delete(struct thread *, struct linux_timer_delete_args *);
+int linux_clock_settime(struct thread *, struct linux_clock_settime_args *);
+int linux_clock_gettime(struct thread *, struct linux_clock_gettime_args *);
+int linux_clock_getres(struct thread *, struct linux_clock_getres_args *);
+int linux_clock_nanosleep(struct thread *, struct linux_clock_nanosleep_args *);
+int linux_statfs64(struct thread *, struct linux_statfs64_args *);
+int linux_fstatfs64(struct thread *, struct linux_fstatfs64_args *);
+int linux_tgkill(struct thread *, struct linux_tgkill_args *);
+int linux_utimes(struct thread *, struct linux_utimes_args *);
+int linux_fadvise64_64(struct thread *, struct linux_fadvise64_64_args *);
+int linux_mq_open(struct thread *, struct linux_mq_open_args *);
+int linux_mq_unlink(struct thread *, struct linux_mq_unlink_args *);
+int linux_mq_timedsend(struct thread *, struct linux_mq_timedsend_args *);
+int linux_mq_timedreceive(struct thread *, struct linux_mq_timedreceive_args *);
+int linux_mq_notify(struct thread *, struct linux_mq_notify_args *);
+int linux_mq_getsetattr(struct thread *, struct linux_mq_getsetattr_args *);
+int linux_waitid(struct thread *, struct linux_waitid_args *);
+int linux_socket(struct thread *, struct linux_socket_args *);
+int linux_bind(struct thread *, struct linux_bind_args *);
+int linux_connect(struct thread *, struct linux_connect_args *);
+int linux_listen(struct thread *, struct linux_listen_args *);
+int linux_accept(struct thread *, struct linux_accept_args *);
+int linux_getsockname(struct thread *, struct linux_getsockname_args *);
+int linux_getpeername(struct thread *, struct linux_getpeername_args *);
+int linux_socketpair(struct thread *, struct linux_socketpair_args *);
+int linux_send(struct thread *, struct linux_send_args *);
+int linux_sendto(struct thread *, struct linux_sendto_args *);
+int linux_recv(struct thread *, struct linux_recv_args *);
+int linux_recvfrom(struct thread *, struct linux_recvfrom_args *);
+int linux_shutdown(struct thread *, struct linux_shutdown_args *);
+int linux_setsockopt(struct thread *, struct linux_setsockopt_args *);
+int linux_getsockopt(struct thread *, struct linux_getsockopt_args *);
+int linux_sendmsg(struct thread *, struct linux_sendmsg_args *);
+int linux_recvmsg(struct thread *, struct linux_recvmsg_args *);
+int linux_semop(struct thread *, struct linux_semop_args *);
+int linux_semget(struct thread *, struct linux_semget_args *);
+int linux_semctl(struct thread *, struct linux_semctl_args *);
+int linux_msgsnd(struct thread *, struct linux_msgsnd_args *);
+int linux_msgrcv(struct thread *, struct linux_msgrcv_args *);
+int linux_msgget(struct thread *, struct linux_msgget_args *);
+int linux_msgctl(struct thread *, struct linux_msgctl_args *);
+int linux_shmat(struct thread *, struct linux_shmat_args *);
+int linux_shmdt(struct thread *, struct linux_shmdt_args *);
+int linux_shmget(struct thread *, struct linux_shmget_args *);
+int linux_shmctl(struct thread *, struct linux_shmctl_args *);
+int linux_add_key(struct thread *, struct linux_add_key_args *);
+int linux_request_key(struct thread *, struct linux_request_key_args *);
+int linux_keyctl(struct thread *, struct linux_keyctl_args *);
+int linux_ioprio_set(struct thread *, struct linux_ioprio_set_args *);
+int linux_ioprio_get(struct thread *, struct linux_ioprio_get_args *);
+int linux_inotify_init(struct thread *, struct linux_inotify_init_args *);
+int linux_inotify_add_watch(struct thread *, struct linux_inotify_add_watch_args *);
+int linux_inotify_rm_watch(struct thread *, struct linux_inotify_rm_watch_args *);
+int linux_mbind(struct thread *, struct linux_mbind_args *);
+int linux_get_mempolicy(struct thread *, struct linux_get_mempolicy_args *);
+int linux_set_mempolicy(struct thread *, struct linux_set_mempolicy_args *);
+int linux_openat(struct thread *, struct linux_openat_args *);
+int linux_mkdirat(struct thread *, struct linux_mkdirat_args *);
+int linux_mknodat(struct thread *, struct linux_mknodat_args *);
+int linux_fchownat(struct thread *, struct linux_fchownat_args *);
+int linux_futimesat(struct thread *, struct linux_futimesat_args *);
+int linux_fstatat64(struct thread *, struct linux_fstatat64_args *);
+int linux_unlinkat(struct thread *, struct linux_unlinkat_args *);
+int linux_renameat(struct thread *, struct linux_renameat_args *);
+int linux_linkat(struct thread *, struct linux_linkat_args *);
+int linux_symlinkat(struct thread *, struct linux_symlinkat_args *);
+int linux_readlinkat(struct thread *, struct linux_readlinkat_args *);
+int linux_fchmodat(struct thread *, struct linux_fchmodat_args *);
+int linux_faccessat(struct thread *, struct linux_faccessat_args *);
+int linux_pselect6(struct thread *, struct linux_pselect6_args *);
+int linux_ppoll(struct thread *, struct linux_ppoll_args *);
+int linux_unshare(struct thread *, struct linux_unshare_args *);
+int linux_set_robust_list(struct thread *, struct linux_set_robust_list_args *);
+int linux_get_robust_list(struct thread *, struct linux_get_robust_list_args *);
+int linux_splice(struct thread *, struct linux_splice_args *);
+int linux_sync_file_range(struct thread *, struct linux_sync_file_range_args *);
+int linux_tee(struct thread *, struct linux_tee_args *);
+int linux_vmsplice(struct thread *, struct linux_vmsplice_args *);
+int linux_move_pages(struct thread *, struct linux_move_pages_args *);
+int linux_getcpu(struct thread *, struct linux_getcpu_args *);
+int linux_epoll_pwait(struct thread *, struct linux_epoll_pwait_args *);
+int linux_kexec_load(struct thread *, struct linux_kexec_load_args *);
+int linux_utimensat(struct thread *, struct linux_utimensat_args *);
+int linux_signalfd(struct thread *, struct linux_signalfd_args *);
+int linux_timerfd_create(struct thread *, struct linux_timerfd_create_args *);
+int linux_eventfd(struct thread *, struct linux_eventfd_args *);
+int linux_fallocate(struct thread *, struct linux_fallocate_args *);
+int linux_timerfd_settime(struct thread *, struct linux_timerfd_settime_args *);
+int linux_timerfd_gettime(struct thread *, struct linux_timerfd_gettime_args *);
+int linux_signalfd4(struct thread *, struct linux_signalfd4_args *);
+int linux_eventfd2(struct thread *, struct linux_eventfd2_args *);
+int linux_epoll_create1(struct thread *, struct linux_epoll_create1_args *);
+int linux_dup3(struct thread *, struct linux_dup3_args *);
+int linux_pipe2(struct thread *, struct linux_pipe2_args *);
+int linux_inotify_init1(struct thread *, struct linux_inotify_init1_args *);
+int linux_preadv(struct thread *, struct linux_preadv_args *);
+int linux_pwritev(struct thread *, struct linux_pwritev_args *);
+int linux_rt_tsigqueueinfo(struct thread *, struct linux_rt_tsigqueueinfo_args *);
+int linux_perf_event_open(struct thread *, struct linux_perf_event_open_args *);
+int linux_recvmmsg(struct thread *, struct linux_recvmmsg_args *);
+int linux_accept4(struct thread *, struct linux_accept4_args *);
+int linux_fanotify_init(struct thread *, struct linux_fanotify_init_args *);
+int linux_fanotify_mark(struct thread *, struct linux_fanotify_mark_args *);
+int linux_prlimit64(struct thread *, struct linux_prlimit64_args *);
+int linux_name_to_handle_at(struct thread *, struct linux_name_to_handle_at_args *);
+int linux_open_by_handle_at(struct thread *, struct linux_open_by_handle_at_args *);
+int linux_clock_adjtime(struct thread *, struct linux_clock_adjtime_args *);
+int linux_syncfs(struct thread *, struct linux_syncfs_args *);
+int linux_sendmmsg(struct thread *, struct linux_sendmmsg_args *);
+int linux_setns(struct thread *, struct linux_setns_args *);
+int linux_process_vm_readv(struct thread *, struct linux_process_vm_readv_args *);
+int linux_process_vm_writev(struct thread *, struct linux_process_vm_writev_args *);
+int linux_set_tls(struct thread *, struct linux_set_tls_args *);
+
+#ifdef COMPAT_43
+
+#define nosys linux_nosys
+
+#endif /* COMPAT_43 */
+
+
+#ifdef COMPAT_FREEBSD4
+
+#define nosys linux_nosys
+
+#endif /* COMPAT_FREEBSD4 */
+
+
+#ifdef COMPAT_FREEBSD6
+
+#define nosys linux_nosys
+
+#endif /* COMPAT_FREEBSD6 */
+
+
+#ifdef COMPAT_FREEBSD7
+
+#define nosys linux_nosys
+
+#endif /* COMPAT_FREEBSD7 */
+
+
+#ifdef COMPAT_FREEBSD10
+
+#define nosys linux_nosys
+
+#endif /* COMPAT_FREEBSD10 */
+
+
+#ifdef COMPAT_FREEBSD11
+
+#define nosys linux_nosys
+
+#endif /* COMPAT_FREEBSD11 */
+
+
+#ifdef COMPAT_FREEBSD12
+
+#define nosys linux_nosys
+
+#endif /* COMPAT_FREEBSD12 */
+
+#define LINUX_SYS_AUE_linux_exit AUE_EXIT
+#define LINUX_SYS_AUE_linux_fork AUE_FORK
+#define LINUX_SYS_AUE_linux_open AUE_OPEN_RWTC
+#define LINUX_SYS_AUE_linux_creat AUE_CREAT
+#define LINUX_SYS_AUE_linux_link AUE_LINK
+#define LINUX_SYS_AUE_linux_unlink AUE_UNLINK
+#define LINUX_SYS_AUE_linux_execve AUE_EXECVE
+#define LINUX_SYS_AUE_linux_chdir AUE_CHDIR
+#define LINUX_SYS_AUE_linux_mknod AUE_MKNOD
+#define LINUX_SYS_AUE_linux_chmod AUE_CHMOD
+#define LINUX_SYS_AUE_linux_lchown16 AUE_LCHOWN
+#define LINUX_SYS_AUE_linux_lseek AUE_LSEEK
+#define LINUX_SYS_AUE_linux_getpid AUE_GETPID
+#define LINUX_SYS_AUE_linux_mount AUE_MOUNT
+#define LINUX_SYS_AUE_linux_setuid16 AUE_SETUID
+#define LINUX_SYS_AUE_linux_getuid16 AUE_GETUID
+#define LINUX_SYS_AUE_linux_pause AUE_NULL
+#define LINUX_SYS_AUE_linux_access AUE_ACCESS
+#define LINUX_SYS_AUE_linux_nice AUE_NICE
+#define LINUX_SYS_AUE_linux_kill AUE_KILL
+#define LINUX_SYS_AUE_linux_rename AUE_RENAME
+#define LINUX_SYS_AUE_linux_mkdir AUE_MKDIR
+#define LINUX_SYS_AUE_linux_rmdir AUE_RMDIR
+#define LINUX_SYS_AUE_linux_pipe AUE_PIPE
+#define LINUX_SYS_AUE_linux_times AUE_NULL
+#define LINUX_SYS_AUE_linux_brk AUE_NULL
+#define LINUX_SYS_AUE_linux_setgid16 AUE_SETGID
+#define LINUX_SYS_AUE_linux_getgid16 AUE_GETGID
+#define LINUX_SYS_AUE_linux_geteuid16 AUE_GETEUID
+#define LINUX_SYS_AUE_linux_getegid16 AUE_GETEGID
+#define LINUX_SYS_AUE_linux_umount AUE_UMOUNT
+#define LINUX_SYS_AUE_linux_ioctl AUE_IOCTL
+#define LINUX_SYS_AUE_linux_fcntl AUE_FCNTL
+#define LINUX_SYS_AUE_linux_ustat AUE_NULL
+#define LINUX_SYS_AUE_linux_getppid AUE_GETPPID
+#define LINUX_SYS_AUE_linux_sigaction AUE_NULL
+#define LINUX_SYS_AUE_linux_setreuid16 AUE_SETREUID
+#define LINUX_SYS_AUE_linux_setregid16 AUE_SETREGID
+#define LINUX_SYS_AUE_linux_sigsuspend AUE_NULL
+#define LINUX_SYS_AUE_linux_sigpending AUE_NULL
+#define LINUX_SYS_AUE_linux_sethostname AUE_SYSCTL
+#define LINUX_SYS_AUE_linux_setrlimit AUE_SETRLIMIT
+#define LINUX_SYS_AUE_linux_gettimeofday AUE_GETTIMEOFDAY
+#define LINUX_SYS_AUE_linux_settimeofday AUE_SETTIMEOFDAY
+#define LINUX_SYS_AUE_linux_getgroups16 AUE_GETGROUPS
+#define LINUX_SYS_AUE_linux_setgroups16 AUE_SETGROUPS
+#define LINUX_SYS_AUE_linux_symlink AUE_SYMLINK
+#define LINUX_SYS_AUE_linux_readlink AUE_READLINK
+#define LINUX_SYS_AUE_linux_reboot AUE_REBOOT
+#define LINUX_SYS_AUE_linux_truncate AUE_TRUNCATE
+#define LINUX_SYS_AUE_linux_ftruncate AUE_FTRUNCATE
+#define LINUX_SYS_AUE_linux_getpriority AUE_GETPRIORITY
+#define LINUX_SYS_AUE_linux_statfs AUE_STATFS
+#define LINUX_SYS_AUE_linux_fstatfs AUE_FSTATFS
+#define LINUX_SYS_AUE_linux_syslog AUE_NULL
+#define LINUX_SYS_AUE_linux_setitimer AUE_SETITIMER
+#define LINUX_SYS_AUE_linux_getitimer AUE_GETITIMER
+#define LINUX_SYS_AUE_linux_newstat AUE_STAT
+#define LINUX_SYS_AUE_linux_newlstat AUE_LSTAT
+#define LINUX_SYS_AUE_linux_newfstat AUE_FSTAT
+#define LINUX_SYS_AUE_linux_vhangup AUE_NULL
+#define LINUX_SYS_AUE_linux_wait4 AUE_WAIT4
+#define LINUX_SYS_AUE_linux_swapoff AUE_SWAPOFF
+#define LINUX_SYS_AUE_linux_sysinfo AUE_NULL
+#define LINUX_SYS_AUE_linux_sigreturn AUE_SIGRETURN
+#define LINUX_SYS_AUE_linux_clone AUE_RFORK
+#define LINUX_SYS_AUE_linux_setdomainname AUE_SYSCTL
+#define LINUX_SYS_AUE_linux_newuname AUE_NULL
+#define LINUX_SYS_AUE_linux_adjtimex AUE_ADJTIME
+#define LINUX_SYS_AUE_linux_mprotect AUE_MPROTECT
+#define LINUX_SYS_AUE_linux_sigprocmask AUE_SIGPROCMASK
+#define LINUX_SYS_AUE_linux_init_module AUE_NULL
+#define LINUX_SYS_AUE_linux_delete_module AUE_NULL
+#define LINUX_SYS_AUE_linux_quotactl AUE_QUOTACTL
+#define LINUX_SYS_AUE_linux_bdflush AUE_BDFLUSH
+#define LINUX_SYS_AUE_linux_sysfs AUE_NULL
+#define LINUX_SYS_AUE_linux_personality AUE_PERSONALITY
+#define LINUX_SYS_AUE_linux_setfsuid16 AUE_SETFSUID
+#define LINUX_SYS_AUE_linux_setfsgid16 AUE_SETFSGID
+#define LINUX_SYS_AUE_linux_llseek AUE_LSEEK
+#define LINUX_SYS_AUE_linux_getdents AUE_GETDIRENTRIES
+#define LINUX_SYS_AUE_linux_select AUE_SELECT
+#define LINUX_SYS_AUE_linux_msync AUE_MSYNC
+#define LINUX_SYS_AUE_linux_getsid AUE_GETSID
+#define LINUX_SYS_AUE_linux_fdatasync AUE_NULL
+#define LINUX_SYS_AUE_linux_sysctl AUE_SYSCTL
+#define LINUX_SYS_AUE_linux_sched_setparam AUE_SCHED_SETPARAM
+#define LINUX_SYS_AUE_linux_sched_getparam AUE_SCHED_GETPARAM
+#define LINUX_SYS_AUE_linux_sched_setscheduler AUE_SCHED_SETSCHEDULER
+#define LINUX_SYS_AUE_linux_sched_getscheduler AUE_SCHED_GETSCHEDULER
+#define LINUX_SYS_AUE_linux_sched_get_priority_max AUE_SCHED_GET_PRIORITY_MAX
+#define LINUX_SYS_AUE_linux_sched_get_priority_min AUE_SCHED_GET_PRIORITY_MIN
+#define LINUX_SYS_AUE_linux_sched_rr_get_interval AUE_SCHED_RR_GET_INTERVAL
+#define LINUX_SYS_AUE_linux_nanosleep AUE_NULL
+#define LINUX_SYS_AUE_linux_mremap AUE_NULL
+#define LINUX_SYS_AUE_linux_setresuid16 AUE_SETRESUID
+#define LINUX_SYS_AUE_linux_getresuid16 AUE_GETRESUID
+#define LINUX_SYS_AUE_linux_setresgid16 AUE_SETRESGID
+#define LINUX_SYS_AUE_linux_getresgid16 AUE_GETRESGID
+#define LINUX_SYS_AUE_linux_prctl AUE_PRCTL
+#define LINUX_SYS_AUE_linux_rt_sigreturn AUE_NULL
+#define LINUX_SYS_AUE_linux_rt_sigaction AUE_NULL
+#define LINUX_SYS_AUE_linux_rt_sigprocmask AUE_NULL
+#define LINUX_SYS_AUE_linux_rt_sigpending AUE_NULL
+#define LINUX_SYS_AUE_linux_rt_sigtimedwait AUE_NULL
+#define LINUX_SYS_AUE_linux_rt_sigqueueinfo AUE_NULL
+#define LINUX_SYS_AUE_linux_rt_sigsuspend AUE_NULL
+#define LINUX_SYS_AUE_linux_pread AUE_PREAD
+#define LINUX_SYS_AUE_linux_pwrite AUE_PWRITE
+#define LINUX_SYS_AUE_linux_chown16 AUE_CHOWN
+#define LINUX_SYS_AUE_linux_getcwd AUE_GETCWD
+#define LINUX_SYS_AUE_linux_capget AUE_CAPGET
+#define LINUX_SYS_AUE_linux_capset AUE_CAPSET
+#define LINUX_SYS_AUE_linux_sigaltstack AUE_NULL
+#define LINUX_SYS_AUE_linux_sendfile AUE_SENDFILE
+#define LINUX_SYS_AUE_linux_vfork AUE_VFORK
+#define LINUX_SYS_AUE_linux_getrlimit AUE_GETRLIMIT
+#define LINUX_SYS_AUE_linux_mmap2 AUE_MMAP
+#define LINUX_SYS_AUE_linux_truncate64 AUE_TRUNCATE
+#define LINUX_SYS_AUE_linux_ftruncate64 AUE_FTRUNCATE
+#define LINUX_SYS_AUE_linux_stat64 AUE_STAT
+#define LINUX_SYS_AUE_linux_lstat64 AUE_LSTAT
+#define LINUX_SYS_AUE_linux_fstat64 AUE_FSTAT
+#define LINUX_SYS_AUE_linux_lchown AUE_LCHOWN
+#define LINUX_SYS_AUE_linux_getuid AUE_GETUID
+#define LINUX_SYS_AUE_linux_getgid AUE_GETGID
+#define LINUX_SYS_AUE_linux_getgroups AUE_GETGROUPS
+#define LINUX_SYS_AUE_linux_setgroups AUE_SETGROUPS
+#define LINUX_SYS_AUE_linux_chown AUE_CHOWN
+#define LINUX_SYS_AUE_linux_setfsuid AUE_SETFSUID
+#define LINUX_SYS_AUE_linux_setfsgid AUE_SETFSGID
+#define LINUX_SYS_AUE_linux_getdents64 AUE_GETDIRENTRIES
+#define LINUX_SYS_AUE_linux_pivot_root AUE_PIVOT_ROOT
+#define LINUX_SYS_AUE_linux_mincore AUE_MINCORE
+#define LINUX_SYS_AUE_linux_fcntl64 AUE_FCNTL
+#define LINUX_SYS_AUE_linux_gettid AUE_NULL
+#define LINUX_SYS_AUE_linux_setxattr AUE_NULL
+#define LINUX_SYS_AUE_linux_lsetxattr AUE_NULL
+#define LINUX_SYS_AUE_linux_fsetxattr AUE_NULL
+#define LINUX_SYS_AUE_linux_getxattr AUE_NULL
+#define LINUX_SYS_AUE_linux_lgetxattr AUE_NULL
+#define LINUX_SYS_AUE_linux_fgetxattr AUE_NULL
+#define LINUX_SYS_AUE_linux_listxattr AUE_NULL
+#define LINUX_SYS_AUE_linux_llistxattr AUE_NULL
+#define LINUX_SYS_AUE_linux_flistxattr AUE_NULL
+#define LINUX_SYS_AUE_linux_removexattr AUE_NULL
+#define LINUX_SYS_AUE_linux_lremovexattr AUE_NULL
+#define LINUX_SYS_AUE_linux_fremovexattr AUE_NULL
+#define LINUX_SYS_AUE_linux_tkill AUE_NULL
+#define LINUX_SYS_AUE_linux_sendfile64 AUE_SENDFILE
+#define LINUX_SYS_AUE_linux_sys_futex AUE_NULL
+#define LINUX_SYS_AUE_linux_sched_setaffinity AUE_NULL
+#define LINUX_SYS_AUE_linux_sched_getaffinity AUE_NULL
+#define LINUX_SYS_AUE_linux_exit_group AUE_EXIT
+#define LINUX_SYS_AUE_linux_lookup_dcookie AUE_NULL
+#define LINUX_SYS_AUE_linux_epoll_create AUE_NULL
+#define LINUX_SYS_AUE_linux_epoll_ctl AUE_NULL
+#define LINUX_SYS_AUE_linux_epoll_wait AUE_NULL
+#define LINUX_SYS_AUE_linux_remap_file_pages AUE_NULL
+#define LINUX_SYS_AUE_linux_set_tid_address AUE_NULL
+#define LINUX_SYS_AUE_linux_timer_create AUE_NULL
+#define LINUX_SYS_AUE_linux_timer_settime AUE_NULL
+#define LINUX_SYS_AUE_linux_timer_gettime AUE_NULL
+#define LINUX_SYS_AUE_linux_timer_getoverrun AUE_NULL
+#define LINUX_SYS_AUE_linux_timer_delete AUE_NULL
+#define LINUX_SYS_AUE_linux_clock_settime AUE_CLOCK_SETTIME
+#define LINUX_SYS_AUE_linux_clock_gettime AUE_NULL
+#define LINUX_SYS_AUE_linux_clock_getres AUE_NULL
+#define LINUX_SYS_AUE_linux_clock_nanosleep AUE_NULL
+#define LINUX_SYS_AUE_linux_statfs64 AUE_STATFS
+#define LINUX_SYS_AUE_linux_fstatfs64 AUE_FSTATFS
+#define LINUX_SYS_AUE_linux_tgkill AUE_NULL
+#define LINUX_SYS_AUE_linux_utimes AUE_UTIMES
+#define LINUX_SYS_AUE_linux_fadvise64_64 AUE_NULL
+#define LINUX_SYS_AUE_linux_mq_open AUE_NULL
+#define LINUX_SYS_AUE_linux_mq_unlink AUE_NULL
+#define LINUX_SYS_AUE_linux_mq_timedsend AUE_NULL
+#define LINUX_SYS_AUE_linux_mq_timedreceive AUE_NULL
+#define LINUX_SYS_AUE_linux_mq_notify AUE_NULL
+#define LINUX_SYS_AUE_linux_mq_getsetattr AUE_NULL
+#define LINUX_SYS_AUE_linux_waitid AUE_WAIT6
+#define LINUX_SYS_AUE_linux_socket AUE_SOCKET
+#define LINUX_SYS_AUE_linux_bind AUE_BIND
+#define LINUX_SYS_AUE_linux_connect AUE_CONNECT
+#define LINUX_SYS_AUE_linux_listen AUE_LISTEN
+#define LINUX_SYS_AUE_linux_accept AUE_ACCEPT
+#define LINUX_SYS_AUE_linux_getsockname AUE_GETSOCKNAME
+#define LINUX_SYS_AUE_linux_getpeername AUE_GETPEERNAME
+#define LINUX_SYS_AUE_linux_socketpair AUE_SOCKETPAIR
+#define LINUX_SYS_AUE_linux_send AUE_SEND
+#define LINUX_SYS_AUE_linux_sendto AUE_SENDTO
+#define LINUX_SYS_AUE_linux_recv AUE_RECV
+#define LINUX_SYS_AUE_linux_recvfrom AUE_RECVFROM
+#define LINUX_SYS_AUE_linux_shutdown AUE_NULL
+#define LINUX_SYS_AUE_linux_setsockopt AUE_SETSOCKOPT
+#define LINUX_SYS_AUE_linux_getsockopt AUE_GETSOCKOPT
+#define LINUX_SYS_AUE_linux_sendmsg AUE_SENDMSG
+#define LINUX_SYS_AUE_linux_recvmsg AUE_RECVMSG
+#define LINUX_SYS_AUE_linux_semop AUE_NULL
+#define LINUX_SYS_AUE_linux_semget AUE_NULL
+#define LINUX_SYS_AUE_linux_semctl AUE_NULL
+#define LINUX_SYS_AUE_linux_msgsnd AUE_NULL
+#define LINUX_SYS_AUE_linux_msgrcv AUE_NULL
+#define LINUX_SYS_AUE_linux_msgget AUE_NULL
+#define LINUX_SYS_AUE_linux_msgctl AUE_NULL
+#define LINUX_SYS_AUE_linux_shmat AUE_NULL
+#define LINUX_SYS_AUE_linux_shmdt AUE_NULL
+#define LINUX_SYS_AUE_linux_shmget AUE_NULL
+#define LINUX_SYS_AUE_linux_shmctl AUE_NULL
+#define LINUX_SYS_AUE_linux_add_key AUE_NULL
+#define LINUX_SYS_AUE_linux_request_key AUE_NULL
+#define LINUX_SYS_AUE_linux_keyctl AUE_NULL
+#define LINUX_SYS_AUE_linux_ioprio_set AUE_NULL
+#define LINUX_SYS_AUE_linux_ioprio_get AUE_NULL
+#define LINUX_SYS_AUE_linux_inotify_init AUE_NULL
+#define LINUX_SYS_AUE_linux_inotify_add_watch AUE_NULL
+#define LINUX_SYS_AUE_linux_inotify_rm_watch AUE_NULL
+#define LINUX_SYS_AUE_linux_mbind AUE_NULL
+#define LINUX_SYS_AUE_linux_get_mempolicy AUE_NULL
+#define LINUX_SYS_AUE_linux_set_mempolicy AUE_NULL
+#define LINUX_SYS_AUE_linux_openat AUE_OPEN_RWTC
+#define LINUX_SYS_AUE_linux_mkdirat AUE_MKDIRAT
+#define LINUX_SYS_AUE_linux_mknodat AUE_MKNODAT
+#define LINUX_SYS_AUE_linux_fchownat AUE_FCHOWNAT
+#define LINUX_SYS_AUE_linux_futimesat AUE_FUTIMESAT
+#define LINUX_SYS_AUE_linux_fstatat64 AUE_FSTATAT
+#define LINUX_SYS_AUE_linux_unlinkat AUE_UNLINKAT
+#define LINUX_SYS_AUE_linux_renameat AUE_RENAMEAT
+#define LINUX_SYS_AUE_linux_linkat AUE_LINKAT
+#define LINUX_SYS_AUE_linux_symlinkat AUE_SYMLINKAT
+#define LINUX_SYS_AUE_linux_readlinkat AUE_READLINKAT
+#define LINUX_SYS_AUE_linux_fchmodat AUE_FCHMODAT
+#define LINUX_SYS_AUE_linux_faccessat AUE_FACCESSAT
+#define LINUX_SYS_AUE_linux_pselect6 AUE_SELECT
+#define LINUX_SYS_AUE_linux_ppoll AUE_POLL
+#define LINUX_SYS_AUE_linux_unshare AUE_NULL
+#define LINUX_SYS_AUE_linux_set_robust_list AUE_NULL
+#define LINUX_SYS_AUE_linux_get_robust_list AUE_NULL
+#define LINUX_SYS_AUE_linux_splice AUE_NULL
+#define LINUX_SYS_AUE_linux_sync_file_range AUE_NULL
+#define LINUX_SYS_AUE_linux_tee AUE_NULL
+#define LINUX_SYS_AUE_linux_vmsplice AUE_NULL
+#define LINUX_SYS_AUE_linux_move_pages AUE_NULL
+#define LINUX_SYS_AUE_linux_getcpu AUE_NULL
+#define LINUX_SYS_AUE_linux_epoll_pwait AUE_NULL
+#define LINUX_SYS_AUE_linux_kexec_load AUE_NULL
+#define LINUX_SYS_AUE_linux_utimensat AUE_FUTIMESAT
+#define LINUX_SYS_AUE_linux_signalfd AUE_NULL
+#define LINUX_SYS_AUE_linux_timerfd_create AUE_NULL
+#define LINUX_SYS_AUE_linux_eventfd AUE_NULL
+#define LINUX_SYS_AUE_linux_fallocate AUE_NULL
+#define LINUX_SYS_AUE_linux_timerfd_settime AUE_NULL
+#define LINUX_SYS_AUE_linux_timerfd_gettime AUE_NULL
+#define LINUX_SYS_AUE_linux_signalfd4 AUE_NULL
+#define LINUX_SYS_AUE_linux_eventfd2 AUE_NULL
+#define LINUX_SYS_AUE_linux_epoll_create1 AUE_NULL
+#define LINUX_SYS_AUE_linux_dup3 AUE_NULL
+#define LINUX_SYS_AUE_linux_pipe2 AUE_NULL
+#define LINUX_SYS_AUE_linux_inotify_init1 AUE_NULL
+#define LINUX_SYS_AUE_linux_preadv AUE_NULL
+#define LINUX_SYS_AUE_linux_pwritev AUE_NULL
+#define LINUX_SYS_AUE_linux_rt_tsigqueueinfo AUE_NULL
+#define LINUX_SYS_AUE_linux_perf_event_open AUE_NULL
+#define LINUX_SYS_AUE_linux_recvmmsg AUE_NULL
+#define LINUX_SYS_AUE_linux_accept4 AUE_ACCEPT
+#define LINUX_SYS_AUE_linux_fanotify_init AUE_NULL
+#define LINUX_SYS_AUE_linux_fanotify_mark AUE_NULL
+#define LINUX_SYS_AUE_linux_prlimit64 AUE_NULL
+#define LINUX_SYS_AUE_linux_name_to_handle_at AUE_NULL
+#define LINUX_SYS_AUE_linux_open_by_handle_at AUE_NULL
+#define LINUX_SYS_AUE_linux_clock_adjtime AUE_NULL
+#define LINUX_SYS_AUE_linux_syncfs AUE_SYNC
+#define LINUX_SYS_AUE_linux_sendmmsg AUE_NULL
+#define LINUX_SYS_AUE_linux_setns AUE_NULL
+#define LINUX_SYS_AUE_linux_process_vm_readv AUE_NULL
+#define LINUX_SYS_AUE_linux_process_vm_writev AUE_NULL
+#define LINUX_SYS_AUE_linux_set_tls AUE_NULL
+
+#undef PAD_
+#undef PADL_
+#undef PADR_
+
+#endif /* !_LINUX_SYSPROTO_H_ */
diff --git a/sys/arm/linux/linux_syscall.h b/sys/arm/linux/linux_syscall.h
new file mode 100644
index 000000000000..1621fde6c0b8
--- /dev/null
+++ b/sys/arm/linux/linux_syscall.h
@@ -0,0 +1,326 @@
+/*
+ * System call numbers.
+ *
+ * DO NOT EDIT-- this file is automatically @generated.
+ * $FreeBSD$
+ */
+
+#define LINUX_SYS_linux_exit 1
+#define LINUX_SYS_linux_fork 2
+#define LINUX_SYS_read 3
+#define LINUX_SYS_write 4
+#define LINUX_SYS_linux_open 5
+#define LINUX_SYS_close 6
+#define LINUX_SYS_linux_creat 8
+#define LINUX_SYS_linux_link 9
+#define LINUX_SYS_linux_unlink 10
+#define LINUX_SYS_linux_execve 11
+#define LINUX_SYS_linux_chdir 12
+#define LINUX_SYS_linux_mknod 14
+#define LINUX_SYS_linux_chmod 15
+#define LINUX_SYS_linux_lchown16 16
+#define LINUX_SYS_linux_lseek 19
+#define LINUX_SYS_linux_getpid 20
+#define LINUX_SYS_linux_mount 21
+#define LINUX_SYS_linux_setuid16 23
+#define LINUX_SYS_linux_getuid16 24
+#define LINUX_SYS_linux_pause 29
+#define LINUX_SYS_linux_access 33
+#define LINUX_SYS_linux_nice 34
+#define LINUX_SYS_sync 36
+#define LINUX_SYS_linux_kill 37
+#define LINUX_SYS_linux_rename 38
+#define LINUX_SYS_linux_mkdir 39
+#define LINUX_SYS_linux_rmdir 40
+#define LINUX_SYS_dup 41
+#define LINUX_SYS_linux_pipe 42
+#define LINUX_SYS_linux_times 43
+#define LINUX_SYS_linux_brk 45
+#define LINUX_SYS_linux_setgid16 46
+#define LINUX_SYS_linux_getgid16 47
+#define LINUX_SYS_linux_geteuid16 49
+#define LINUX_SYS_linux_getegid16 50
+#define LINUX_SYS_acct 51
+#define LINUX_SYS_linux_umount 52
+#define LINUX_SYS_linux_ioctl 54
+#define LINUX_SYS_linux_fcntl 55
+#define LINUX_SYS_setpgid 57
+#define LINUX_SYS_umask 60
+#define LINUX_SYS_chroot 61
+#define LINUX_SYS_linux_ustat 62
+#define LINUX_SYS_dup2 63
+#define LINUX_SYS_linux_getppid 64
+#define LINUX_SYS_getpgrp 65
+#define LINUX_SYS_setsid 66
+#define LINUX_SYS_linux_sigaction 67
+#define LINUX_SYS_linux_setreuid16 70
+#define LINUX_SYS_linux_setregid16 71
+#define LINUX_SYS_linux_sigsuspend 72
+#define LINUX_SYS_linux_sigpending 73
+#define LINUX_SYS_linux_sethostname 74
+#define LINUX_SYS_linux_setrlimit 75
+#define LINUX_SYS_getrusage 77
+#define LINUX_SYS_linux_gettimeofday 78
+#define LINUX_SYS_linux_settimeofday 79
+#define LINUX_SYS_linux_getgroups16 80
+#define LINUX_SYS_linux_setgroups16 81
+#define LINUX_SYS_linux_symlink 83
+#define LINUX_SYS_linux_readlink 85
+#define LINUX_SYS_swapon 87
+#define LINUX_SYS_linux_reboot 88
+#define LINUX_SYS_munmap 91
+#define LINUX_SYS_linux_truncate 92
+#define LINUX_SYS_linux_ftruncate 93
+#define LINUX_SYS_fchmod 94
+#define LINUX_SYS_fchown 95
+#define LINUX_SYS_linux_getpriority 96
+#define LINUX_SYS_setpriority 97
+#define LINUX_SYS_linux_statfs 99
+#define LINUX_SYS_linux_fstatfs 100
+#define LINUX_SYS_linux_syslog 103
+#define LINUX_SYS_linux_setitimer 104
+#define LINUX_SYS_linux_getitimer 105
+#define LINUX_SYS_linux_newstat 106
+#define LINUX_SYS_linux_newlstat 107
+#define LINUX_SYS_linux_newfstat 108
+#define LINUX_SYS_linux_vhangup 111
+#define LINUX_SYS_linux_wait4 114
+#define LINUX_SYS_linux_swapoff 115
+#define LINUX_SYS_linux_sysinfo 116
+#define LINUX_SYS_fsync 118
+#define LINUX_SYS_linux_sigreturn 119
+#define LINUX_SYS_linux_clone 120
+#define LINUX_SYS_linux_setdomainname 121
+#define LINUX_SYS_linux_newuname 122
+#define LINUX_SYS_linux_adjtimex 124
+#define LINUX_SYS_linux_mprotect 125
+#define LINUX_SYS_linux_sigprocmask 126
+#define LINUX_SYS_linux_init_module 128
+#define LINUX_SYS_linux_delete_module 129
+#define LINUX_SYS_linux_quotactl 131
+#define LINUX_SYS_getpgid 132
+#define LINUX_SYS_fchdir 133
+#define LINUX_SYS_linux_bdflush 134
+#define LINUX_SYS_linux_sysfs 135
+#define LINUX_SYS_linux_personality 136
+#define LINUX_SYS_linux_setfsuid16 138
+#define LINUX_SYS_linux_setfsgid16 139
+#define LINUX_SYS_linux_llseek 140
+#define LINUX_SYS_linux_getdents 141
+#define LINUX_SYS_linux_select 142
+#define LINUX_SYS_flock 143
+#define LINUX_SYS_linux_msync 144
+#define LINUX_SYS_readv 145
+#define LINUX_SYS_writev 146
+#define LINUX_SYS_linux_getsid 147
+#define LINUX_SYS_linux_fdatasync 148
+#define LINUX_SYS_linux_sysctl 149
+#define LINUX_SYS_mlock 150
+#define LINUX_SYS_munlock 151
+#define LINUX_SYS_mlockall 152
+#define LINUX_SYS_munlockall 153
+#define LINUX_SYS_linux_sched_setparam 154
+#define LINUX_SYS_linux_sched_getparam 155
+#define LINUX_SYS_linux_sched_setscheduler 156
+#define LINUX_SYS_linux_sched_getscheduler 157
+#define LINUX_SYS_sched_yield 158
+#define LINUX_SYS_linux_sched_get_priority_max 159
+#define LINUX_SYS_linux_sched_get_priority_min 160
+#define LINUX_SYS_linux_sched_rr_get_interval 161
+#define LINUX_SYS_linux_nanosleep 162
+#define LINUX_SYS_linux_mremap 163
+#define LINUX_SYS_linux_setresuid16 164
+#define LINUX_SYS_linux_getresuid16 165
+#define LINUX_SYS_poll 168
+#define LINUX_SYS_linux_setresgid16 170
+#define LINUX_SYS_linux_getresgid16 171
+#define LINUX_SYS_linux_prctl 172
+#define LINUX_SYS_linux_rt_sigreturn 173
+#define LINUX_SYS_linux_rt_sigaction 174
+#define LINUX_SYS_linux_rt_sigprocmask 175
+#define LINUX_SYS_linux_rt_sigpending 176
+#define LINUX_SYS_linux_rt_sigtimedwait 177
+#define LINUX_SYS_linux_rt_sigqueueinfo 178
+#define LINUX_SYS_linux_rt_sigsuspend 179
+#define LINUX_SYS_linux_pread 180
+#define LINUX_SYS_linux_pwrite 181
+#define LINUX_SYS_linux_chown16 182
+#define LINUX_SYS_linux_getcwd 183
+#define LINUX_SYS_linux_capget 184
+#define LINUX_SYS_linux_capset 185
+#define LINUX_SYS_linux_sigaltstack 186
+#define LINUX_SYS_linux_sendfile 187
+#define LINUX_SYS_linux_vfork 190
+#define LINUX_SYS_linux_getrlimit 191
+#define LINUX_SYS_linux_mmap2 192
+#define LINUX_SYS_linux_truncate64 193
+#define LINUX_SYS_linux_ftruncate64 194
+#define LINUX_SYS_linux_stat64 195
+#define LINUX_SYS_linux_lstat64 196
+#define LINUX_SYS_linux_fstat64 197
+#define LINUX_SYS_linux_lchown 198
+#define LINUX_SYS_linux_getuid 199
+#define LINUX_SYS_linux_getgid 200
+#define LINUX_SYS_geteuid 201
+#define LINUX_SYS_getegid 202
+#define LINUX_SYS_setreuid 203
+#define LINUX_SYS_setregid 204
+#define LINUX_SYS_linux_getgroups 205
+#define LINUX_SYS_linux_setgroups 206
+#define LINUX_SYS_setresuid 208
+#define LINUX_SYS_getresuid 209
+#define LINUX_SYS_setresgid 210
+#define LINUX_SYS_getresgid 211
+#define LINUX_SYS_linux_chown 212
+#define LINUX_SYS_setuid 213
+#define LINUX_SYS_setgid 214
+#define LINUX_SYS_linux_setfsuid 215
+#define LINUX_SYS_linux_setfsgid 216
+#define LINUX_SYS_linux_getdents64 217
+#define LINUX_SYS_linux_pivot_root 218
+#define LINUX_SYS_linux_mincore 219
+#define LINUX_SYS_madvise 220
+#define LINUX_SYS_linux_fcntl64 221
+#define LINUX_SYS_linux_gettid 224
+#define LINUX_SYS_linux_setxattr 226
+#define LINUX_SYS_linux_lsetxattr 227
+#define LINUX_SYS_linux_fsetxattr 228
+#define LINUX_SYS_linux_getxattr 229
+#define LINUX_SYS_linux_lgetxattr 230
+#define LINUX_SYS_linux_fgetxattr 231
+#define LINUX_SYS_linux_listxattr 232
+#define LINUX_SYS_linux_llistxattr 233
+#define LINUX_SYS_linux_flistxattr 234
+#define LINUX_SYS_linux_removexattr 235
+#define LINUX_SYS_linux_lremovexattr 236
+#define LINUX_SYS_linux_fremovexattr 237
+#define LINUX_SYS_linux_tkill 238
+#define LINUX_SYS_linux_sendfile64 239
+#define LINUX_SYS_linux_sys_futex 240
+#define LINUX_SYS_linux_sched_setaffinity 241
+#define LINUX_SYS_linux_sched_getaffinity 242
+#define LINUX_SYS_linux_exit_group 248
+#define LINUX_SYS_linux_lookup_dcookie 249
+#define LINUX_SYS_linux_epoll_create 250
+#define LINUX_SYS_linux_epoll_ctl 251
+#define LINUX_SYS_linux_epoll_wait 252
+#define LINUX_SYS_linux_remap_file_pages 253
+#define LINUX_SYS_linux_set_tid_address 256
+#define LINUX_SYS_linux_timer_create 257
+#define LINUX_SYS_linux_timer_settime 258
+#define LINUX_SYS_linux_timer_gettime 259
+#define LINUX_SYS_linux_timer_getoverrun 260
+#define LINUX_SYS_linux_timer_delete 261
+#define LINUX_SYS_linux_clock_settime 262
+#define LINUX_SYS_linux_clock_gettime 263
+#define LINUX_SYS_linux_clock_getres 264
+#define LINUX_SYS_linux_clock_nanosleep 265
+#define LINUX_SYS_linux_statfs64 266
+#define LINUX_SYS_linux_fstatfs64 267
+#define LINUX_SYS_linux_tgkill 268
+#define LINUX_SYS_linux_utimes 269
+#define LINUX_SYS_linux_fadvise64_64 270
+#define LINUX_SYS_linux_mq_open 274
+#define LINUX_SYS_linux_mq_unlink 275
+#define LINUX_SYS_linux_mq_timedsend 276
+#define LINUX_SYS_linux_mq_timedreceive 277
+#define LINUX_SYS_linux_mq_notify 278
+#define LINUX_SYS_linux_mq_getsetattr 279
+#define LINUX_SYS_linux_waitid 280
+#define LINUX_SYS_linux_socket 281
+#define LINUX_SYS_linux_bind 282
+#define LINUX_SYS_linux_connect 283
+#define LINUX_SYS_linux_listen 284
+#define LINUX_SYS_linux_accept 285
+#define LINUX_SYS_linux_getsockname 286
+#define LINUX_SYS_linux_getpeername 287
+#define LINUX_SYS_linux_socketpair 288
+#define LINUX_SYS_linux_send 289
+#define LINUX_SYS_linux_sendto 290
+#define LINUX_SYS_linux_recv 291
+#define LINUX_SYS_linux_recvfrom 292
+#define LINUX_SYS_linux_shutdown 293
+#define LINUX_SYS_linux_setsockopt 294
+#define LINUX_SYS_linux_getsockopt 295
+#define LINUX_SYS_linux_sendmsg 296
+#define LINUX_SYS_linux_recvmsg 297
+#define LINUX_SYS_linux_semop 298
+#define LINUX_SYS_linux_semget 299
+#define LINUX_SYS_linux_semctl 300
+#define LINUX_SYS_linux_msgsnd 301
+#define LINUX_SYS_linux_msgrcv 302
+#define LINUX_SYS_linux_msgget 303
+#define LINUX_SYS_linux_msgctl 304
+#define LINUX_SYS_linux_shmat 305
+#define LINUX_SYS_linux_shmdt 306
+#define LINUX_SYS_linux_shmget 307
+#define LINUX_SYS_linux_shmctl 308
+#define LINUX_SYS_linux_add_key 309
+#define LINUX_SYS_linux_request_key 310
+#define LINUX_SYS_linux_keyctl 311
+#define LINUX_SYS_linux_ioprio_set 314
+#define LINUX_SYS_linux_ioprio_get 315
+#define LINUX_SYS_linux_inotify_init 316
+#define LINUX_SYS_linux_inotify_add_watch 317
+#define LINUX_SYS_linux_inotify_rm_watch 318
+#define LINUX_SYS_linux_mbind 319
+#define LINUX_SYS_linux_get_mempolicy 320
+#define LINUX_SYS_linux_set_mempolicy 321
+#define LINUX_SYS_linux_openat 322
+#define LINUX_SYS_linux_mkdirat 323
+#define LINUX_SYS_linux_mknodat 324
+#define LINUX_SYS_linux_fchownat 325
+#define LINUX_SYS_linux_futimesat 326
+#define LINUX_SYS_linux_fstatat64 327
+#define LINUX_SYS_linux_unlinkat 328
+#define LINUX_SYS_linux_renameat 329
+#define LINUX_SYS_linux_linkat 330
+#define LINUX_SYS_linux_symlinkat 331
+#define LINUX_SYS_linux_readlinkat 332
+#define LINUX_SYS_linux_fchmodat 333
+#define LINUX_SYS_linux_faccessat 334
+#define LINUX_SYS_linux_pselect6 335
+#define LINUX_SYS_linux_ppoll 336
+#define LINUX_SYS_linux_unshare 337
+#define LINUX_SYS_linux_set_robust_list 338
+#define LINUX_SYS_linux_get_robust_list 339
+#define LINUX_SYS_linux_splice 340
+#define LINUX_SYS_linux_sync_file_range 341
+#define LINUX_SYS_linux_tee 342
+#define LINUX_SYS_linux_vmsplice 343
+#define LINUX_SYS_linux_move_pages 344
+#define LINUX_SYS_linux_getcpu 345
+#define LINUX_SYS_linux_epoll_pwait 346
+#define LINUX_SYS_linux_kexec_load 347
+#define LINUX_SYS_linux_utimensat 348
+#define LINUX_SYS_linux_signalfd 349
+#define LINUX_SYS_linux_timerfd_create 350
+#define LINUX_SYS_linux_eventfd 351
+#define LINUX_SYS_linux_fallocate 352
+#define LINUX_SYS_linux_timerfd_settime 353
+#define LINUX_SYS_linux_timerfd_gettime 354
+#define LINUX_SYS_linux_signalfd4 355
+#define LINUX_SYS_linux_eventfd2 356
+#define LINUX_SYS_linux_epoll_create1 357
+#define LINUX_SYS_linux_dup3 358
+#define LINUX_SYS_linux_pipe2 359
+#define LINUX_SYS_linux_inotify_init1 360
+#define LINUX_SYS_linux_preadv 361
+#define LINUX_SYS_linux_pwritev 362
+#define LINUX_SYS_linux_rt_tsigqueueinfo 363
+#define LINUX_SYS_linux_perf_event_open 364
+#define LINUX_SYS_linux_recvmmsg 365
+#define LINUX_SYS_linux_accept4 366
+#define LINUX_SYS_linux_fanotify_init 367
+#define LINUX_SYS_linux_fanotify_mark 368
+#define LINUX_SYS_linux_prlimit64 369
+#define LINUX_SYS_linux_name_to_handle_at 370
+#define LINUX_SYS_linux_open_by_handle_at 371
+#define LINUX_SYS_linux_clock_adjtime 372
+#define LINUX_SYS_linux_syncfs 373
+#define LINUX_SYS_linux_sendmmsg 374
+#define LINUX_SYS_linux_setns 375
+#define LINUX_SYS_linux_process_vm_readv 376
+#define LINUX_SYS_linux_process_vm_writev 377
+#define LINUX_SYS_linux_set_tls 405
+#define LINUX_SYS_MAXSYSCALL 407
diff --git a/sys/arm/linux/linux_syscalls.c b/sys/arm/linux/linux_syscalls.c
new file mode 100644
index 000000000000..d7f743f1a825
--- /dev/null
+++ b/sys/arm/linux/linux_syscalls.c
@@ -0,0 +1,417 @@
+/*
+ * System call names.
+ *
+ * DO NOT EDIT-- this file is automatically @generated.
+ * $FreeBSD$
+ */
+
+const char *linux_syscallnames[] = {
+#define nosys linux_nosys
+ "#0", /* 0 = setup */
+ "linux_exit", /* 1 = linux_exit */
+ "linux_fork", /* 2 = linux_fork */
+ "read", /* 3 = read */
+ "write", /* 4 = write */
+ "linux_open", /* 5 = linux_open */
+ "close", /* 6 = close */
+ "#7", /* 7 = ; */
+ "linux_creat", /* 8 = linux_creat */
+ "linux_link", /* 9 = linux_link */
+ "linux_unlink", /* 10 = linux_unlink */
+ "linux_execve", /* 11 = linux_execve */
+ "linux_chdir", /* 12 = linux_chdir */
+ "#13", /* 13 = ; */
+ "linux_mknod", /* 14 = linux_mknod */
+ "linux_chmod", /* 15 = linux_chmod */
+ "linux_lchown16", /* 16 = linux_lchown16 */
+ "#17", /* 17 = ; */
+ "#18", /* 18 = ; */
+ "linux_lseek", /* 19 = linux_lseek */
+ "linux_getpid", /* 20 = linux_getpid */
+ "linux_mount", /* 21 = linux_mount */
+ "#22", /* 22 = ; */
+ "linux_setuid16", /* 23 = linux_setuid16 */
+ "linux_getuid16", /* 24 = linux_getuid16 */
+ "#25", /* 25 = ; */
+ "#26", /* 26 = ptrace */
+ "#27", /* 27 = ; */
+ "#28", /* 28 = ; */
+ "linux_pause", /* 29 = linux_pause */
+ "#30", /* 30 = ; */
+ "#31", /* 31 = ; */
+ "#32", /* 32 = ; */
+ "linux_access", /* 33 = linux_access */
+ "linux_nice", /* 34 = linux_nice */
+ "#35", /* 35 = ; */
+ "sync", /* 36 = sync */
+ "linux_kill", /* 37 = linux_kill */
+ "linux_rename", /* 38 = linux_rename */
+ "linux_mkdir", /* 39 = linux_mkdir */
+ "linux_rmdir", /* 40 = linux_rmdir */
+ "dup", /* 41 = dup */
+ "linux_pipe", /* 42 = linux_pipe */
+ "linux_times", /* 43 = linux_times */
+ "#44", /* 44 = ; */
+ "linux_brk", /* 45 = linux_brk */
+ "linux_setgid16", /* 46 = linux_setgid16 */
+ "linux_getgid16", /* 47 = linux_getgid16 */
+ "#48", /* 48 = ; */
+ "linux_geteuid16", /* 49 = linux_geteuid16 */
+ "linux_getegid16", /* 50 = linux_getegid16 */
+ "acct", /* 51 = acct */
+ "linux_umount", /* 52 = linux_umount */
+ "#53", /* 53 = ; */
+ "linux_ioctl", /* 54 = linux_ioctl */
+ "linux_fcntl", /* 55 = linux_fcntl */
+ "#56", /* 56 = ; */
+ "setpgid", /* 57 = setpgid */
+ "#58", /* 58 = ; */
+ "#59", /* 59 = ; */
+ "umask", /* 60 = umask */
+ "chroot", /* 61 = chroot */
+ "linux_ustat", /* 62 = linux_ustat */
+ "dup2", /* 63 = dup2 */
+ "linux_getppid", /* 64 = linux_getppid */
+ "getpgrp", /* 65 = getpgrp */
+ "setsid", /* 66 = setsid */
+ "linux_sigaction", /* 67 = linux_sigaction */
+ "#68", /* 68 = ; */
+ "#69", /* 69 = ; */
+ "linux_setreuid16", /* 70 = linux_setreuid16 */
+ "linux_setregid16", /* 71 = linux_setregid16 */
+ "linux_sigsuspend", /* 72 = linux_sigsuspend */
+ "linux_sigpending", /* 73 = linux_sigpending */
+ "linux_sethostname", /* 74 = linux_sethostname */
+ "linux_setrlimit", /* 75 = linux_setrlimit */
+ "#76", /* 76 = ; */
+ "getrusage", /* 77 = getrusage */
+ "linux_gettimeofday", /* 78 = linux_gettimeofday */
+ "linux_settimeofday", /* 79 = linux_settimeofday */
+ "linux_getgroups16", /* 80 = linux_getgroups16 */
+ "linux_setgroups16", /* 81 = linux_setgroups16 */
+ "#82", /* 82 = ; */
+ "linux_symlink", /* 83 = linux_symlink */
+ "#84", /* 84 = ; */
+ "linux_readlink", /* 85 = linux_readlink */
+ "#86", /* 86 = uselib */
+ "swapon", /* 87 = swapon */
+ "linux_reboot", /* 88 = linux_reboot */
+ "#89", /* 89 = ; */
+ "#90", /* 90 = ; */
+ "munmap", /* 91 = munmap */
+ "linux_truncate", /* 92 = linux_truncate */
+ "linux_ftruncate", /* 93 = linux_ftruncate */
+ "fchmod", /* 94 = fchmod */
+ "fchown", /* 95 = fchown */
+ "linux_getpriority", /* 96 = linux_getpriority */
+ "setpriority", /* 97 = setpriority */
+ "#98", /* 98 = ; */
+ "linux_statfs", /* 99 = linux_statfs */
+ "linux_fstatfs", /* 100 = linux_fstatfs */
+ "#101", /* 101 = ; */
+ "#102", /* 102 = ; */
+ "linux_syslog", /* 103 = linux_syslog */
+ "linux_setitimer", /* 104 = linux_setitimer */
+ "linux_getitimer", /* 105 = linux_getitimer */
+ "linux_newstat", /* 106 = linux_newstat */
+ "linux_newlstat", /* 107 = linux_newlstat */
+ "linux_newfstat", /* 108 = linux_newfstat */
+ "#109", /* 109 = ; */
+ "#110", /* 110 = ; */
+ "linux_vhangup", /* 111 = linux_vhangup */
+ "#112", /* 112 = ; */
+ "#113", /* 113 = ; */
+ "linux_wait4", /* 114 = linux_wait4 */
+ "linux_swapoff", /* 115 = linux_swapoff */
+ "linux_sysinfo", /* 116 = linux_sysinfo */
+ "#117", /* 117 = ; */
+ "fsync", /* 118 = fsync */
+ "linux_sigreturn", /* 119 = linux_sigreturn */
+ "linux_clone", /* 120 = linux_clone */
+ "linux_setdomainname", /* 121 = linux_setdomainname */
+ "linux_newuname", /* 122 = linux_newuname */
+ "#123", /* 123 = ; */
+ "linux_adjtimex", /* 124 = linux_adjtimex */
+ "linux_mprotect", /* 125 = linux_mprotect */
+ "linux_sigprocmask", /* 126 = linux_sigprocmask */
+ "#127", /* 127 = ; */
+ "linux_init_module", /* 128 = linux_init_module */
+ "linux_delete_module", /* 129 = linux_delete_module */
+ "#130", /* 130 = ; */
+ "linux_quotactl", /* 131 = linux_quotactl */
+ "getpgid", /* 132 = getpgid */
+ "fchdir", /* 133 = fchdir */
+ "linux_bdflush", /* 134 = linux_bdflush */
+ "linux_sysfs", /* 135 = linux_sysfs */
+ "linux_personality", /* 136 = linux_personality */
+ "#137", /* 137 = ; */
+ "linux_setfsuid16", /* 138 = linux_setfsuid16 */
+ "linux_setfsgid16", /* 139 = linux_setfsgid16 */
+ "linux_llseek", /* 140 = linux_llseek */
+ "linux_getdents", /* 141 = linux_getdents */
+ "linux_select", /* 142 = linux_select */
+ "flock", /* 143 = flock */
+ "linux_msync", /* 144 = linux_msync */
+ "readv", /* 145 = readv */
+ "writev", /* 146 = writev */
+ "linux_getsid", /* 147 = linux_getsid */
+ "linux_fdatasync", /* 148 = linux_fdatasync */
+ "linux_sysctl", /* 149 = linux_sysctl */
+ "mlock", /* 150 = mlock */
+ "munlock", /* 151 = munlock */
+ "mlockall", /* 152 = mlockall */
+ "munlockall", /* 153 = munlockall */
+ "linux_sched_setparam", /* 154 = linux_sched_setparam */
+ "linux_sched_getparam", /* 155 = linux_sched_getparam */
+ "linux_sched_setscheduler", /* 156 = linux_sched_setscheduler */
+ "linux_sched_getscheduler", /* 157 = linux_sched_getscheduler */
+ "sched_yield", /* 158 = sched_yield */
+ "linux_sched_get_priority_max", /* 159 = linux_sched_get_priority_max */
+ "linux_sched_get_priority_min", /* 160 = linux_sched_get_priority_min */
+ "linux_sched_rr_get_interval", /* 161 = linux_sched_rr_get_interval */
+ "linux_nanosleep", /* 162 = linux_nanosleep */
+ "linux_mremap", /* 163 = linux_mremap */
+ "linux_setresuid16", /* 164 = linux_setresuid16 */
+ "linux_getresuid16", /* 165 = linux_getresuid16 */
+ "#166", /* 166 = ; */
+ "#167", /* 167 = ; */
+ "poll", /* 168 = poll */
+ "#169", /* 169 = ; */
+ "linux_setresgid16", /* 170 = linux_setresgid16 */
+ "linux_getresgid16", /* 171 = linux_getresgid16 */
+ "linux_prctl", /* 172 = linux_prctl */
+ "linux_rt_sigreturn", /* 173 = linux_rt_sigreturn */
+ "linux_rt_sigaction", /* 174 = linux_rt_sigaction */
+ "linux_rt_sigprocmask", /* 175 = linux_rt_sigprocmask */
+ "linux_rt_sigpending", /* 176 = linux_rt_sigpending */
+ "linux_rt_sigtimedwait", /* 177 = linux_rt_sigtimedwait */
+ "linux_rt_sigqueueinfo", /* 178 = linux_rt_sigqueueinfo */
+ "linux_rt_sigsuspend", /* 179 = linux_rt_sigsuspend */
+ "linux_pread", /* 180 = linux_pread */
+ "linux_pwrite", /* 181 = linux_pwrite */
+ "linux_chown16", /* 182 = linux_chown16 */
+ "linux_getcwd", /* 183 = linux_getcwd */
+ "linux_capget", /* 184 = linux_capget */
+ "linux_capset", /* 185 = linux_capset */
+ "linux_sigaltstack", /* 186 = linux_sigaltstack */
+ "linux_sendfile", /* 187 = linux_sendfile */
+ "#188", /* 188 = ; */
+ "#189", /* 189 = ; */
+ "linux_vfork", /* 190 = linux_vfork */
+ "linux_getrlimit", /* 191 = linux_getrlimit */
+ "linux_mmap2", /* 192 = linux_mmap2 */
+ "linux_truncate64", /* 193 = linux_truncate64 */
+ "linux_ftruncate64", /* 194 = linux_ftruncate64 */
+ "linux_stat64", /* 195 = linux_stat64 */
+ "linux_lstat64", /* 196 = linux_lstat64 */
+ "linux_fstat64", /* 197 = linux_fstat64 */
+ "linux_lchown", /* 198 = linux_lchown */
+ "linux_getuid", /* 199 = linux_getuid */
+ "linux_getgid", /* 200 = linux_getgid */
+ "geteuid", /* 201 = geteuid */
+ "getegid", /* 202 = getegid */
+ "setreuid", /* 203 = setreuid */
+ "setregid", /* 204 = setregid */
+ "linux_getgroups", /* 205 = linux_getgroups */
+ "linux_setgroups", /* 206 = linux_setgroups */
+ "fchown", /* 207 = fchown */
+ "setresuid", /* 208 = setresuid */
+ "getresuid", /* 209 = getresuid */
+ "setresgid", /* 210 = setresgid */
+ "getresgid", /* 211 = getresgid */
+ "linux_chown", /* 212 = linux_chown */
+ "setuid", /* 213 = setuid */
+ "setgid", /* 214 = setgid */
+ "linux_setfsuid", /* 215 = linux_setfsuid */
+ "linux_setfsgid", /* 216 = linux_setfsgid */
+ "linux_getdents64", /* 217 = linux_getdents64 */
+ "linux_pivot_root", /* 218 = linux_pivot_root */
+ "linux_mincore", /* 219 = linux_mincore */
+ "madvise", /* 220 = madvise */
+ "linux_fcntl64", /* 221 = linux_fcntl64 */
+ "#222", /* 222 = */
+ "#223", /* 223 = */
+ "linux_gettid", /* 224 = linux_gettid */
+ "#225", /* 225 = linux_readahead */
+ "linux_setxattr", /* 226 = linux_setxattr */
+ "linux_lsetxattr", /* 227 = linux_lsetxattr */
+ "linux_fsetxattr", /* 228 = linux_fsetxattr */
+ "linux_getxattr", /* 229 = linux_getxattr */
+ "linux_lgetxattr", /* 230 = linux_lgetxattr */
+ "linux_fgetxattr", /* 231 = linux_fgetxattr */
+ "linux_listxattr", /* 232 = linux_listxattr */
+ "linux_llistxattr", /* 233 = linux_llistxattr */
+ "linux_flistxattr", /* 234 = linux_flistxattr */
+ "linux_removexattr", /* 235 = linux_removexattr */
+ "linux_lremovexattr", /* 236 = linux_lremovexattr */
+ "linux_fremovexattr", /* 237 = linux_fremovexattr */
+ "linux_tkill", /* 238 = linux_tkill */
+ "linux_sendfile64", /* 239 = linux_sendfile64 */
+ "linux_sys_futex", /* 240 = linux_sys_futex */
+ "linux_sched_setaffinity", /* 241 = linux_sched_setaffinity */
+ "linux_sched_getaffinity", /* 242 = linux_sched_getaffinity */
+ "#243", /* 243 = linux_io_setup */
+ "#244", /* 244 = linux_io_destroy */
+ "#245", /* 245 = linux_io_getevents */
+ "#246", /* 246 = linux_io_submit */
+ "#247", /* 247 = linux_io_cancel */
+ "linux_exit_group", /* 248 = linux_exit_group */
+ "linux_lookup_dcookie", /* 249 = linux_lookup_dcookie */
+ "linux_epoll_create", /* 250 = linux_epoll_create */
+ "linux_epoll_ctl", /* 251 = linux_epoll_ctl */
+ "linux_epoll_wait", /* 252 = linux_epoll_wait */
+ "linux_remap_file_pages", /* 253 = linux_remap_file_pages */
+ "#254", /* 254 = ; */
+ "#255", /* 255 = ; */
+ "linux_set_tid_address", /* 256 = linux_set_tid_address */
+ "linux_timer_create", /* 257 = linux_timer_create */
+ "linux_timer_settime", /* 258 = linux_timer_settime */
+ "linux_timer_gettime", /* 259 = linux_timer_gettime */
+ "linux_timer_getoverrun", /* 260 = linux_timer_getoverrun */
+ "linux_timer_delete", /* 261 = linux_timer_delete */
+ "linux_clock_settime", /* 262 = linux_clock_settime */
+ "linux_clock_gettime", /* 263 = linux_clock_gettime */
+ "linux_clock_getres", /* 264 = linux_clock_getres */
+ "linux_clock_nanosleep", /* 265 = linux_clock_nanosleep */
+ "linux_statfs64", /* 266 = linux_statfs64 */
+ "linux_fstatfs64", /* 267 = linux_fstatfs64 */
+ "linux_tgkill", /* 268 = linux_tgkill */
+ "linux_utimes", /* 269 = linux_utimes */
+ "linux_fadvise64_64", /* 270 = linux_fadvise64_64 */
+ "#271", /* 271 = pciconfig_iobase */
+ "#272", /* 272 = pciconfig_read */
+ "#273", /* 273 = pciconfig_write */
+ "linux_mq_open", /* 274 = linux_mq_open */
+ "linux_mq_unlink", /* 275 = linux_mq_unlink */
+ "linux_mq_timedsend", /* 276 = linux_mq_timedsend */
+ "linux_mq_timedreceive", /* 277 = linux_mq_timedreceive */
+ "linux_mq_notify", /* 278 = linux_mq_notify */
+ "linux_mq_getsetattr", /* 279 = linux_mq_getsetattr */
+ "linux_waitid", /* 280 = linux_waitid */
+ "linux_socket", /* 281 = linux_socket */
+ "linux_bind", /* 282 = linux_bind */
+ "linux_connect", /* 283 = linux_connect */
+ "linux_listen", /* 284 = linux_listen */
+ "linux_accept", /* 285 = linux_accept */
+ "linux_getsockname", /* 286 = linux_getsockname */
+ "linux_getpeername", /* 287 = linux_getpeername */
+ "linux_socketpair", /* 288 = linux_socketpair */
+ "linux_send", /* 289 = linux_send */
+ "linux_sendto", /* 290 = linux_sendto */
+ "linux_recv", /* 291 = linux_recv */
+ "linux_recvfrom", /* 292 = linux_recvfrom */
+ "linux_shutdown", /* 293 = linux_shutdown */
+ "linux_setsockopt", /* 294 = linux_setsockopt */
+ "linux_getsockopt", /* 295 = linux_getsockopt */
+ "linux_sendmsg", /* 296 = linux_sendmsg */
+ "linux_recvmsg", /* 297 = linux_recvmsg */
+ "linux_semop", /* 298 = linux_semop */
+ "linux_semget", /* 299 = linux_semget */
+ "linux_semctl", /* 300 = linux_semctl */
+ "linux_msgsnd", /* 301 = linux_msgsnd */
+ "linux_msgrcv", /* 302 = linux_msgrcv */
+ "linux_msgget", /* 303 = linux_msgget */
+ "linux_msgctl", /* 304 = linux_msgctl */
+ "linux_shmat", /* 305 = linux_shmat */
+ "linux_shmdt", /* 306 = linux_shmdt */
+ "linux_shmget", /* 307 = linux_shmget */
+ "linux_shmctl", /* 308 = linux_shmctl */
+ "linux_add_key", /* 309 = linux_add_key */
+ "linux_request_key", /* 310 = linux_request_key */
+ "linux_keyctl", /* 311 = linux_keyctl */
+ "#312", /* 312 = semtimedop */
+ "#313", /* 313 = ; */
+ "linux_ioprio_set", /* 314 = linux_ioprio_set */
+ "linux_ioprio_get", /* 315 = linux_ioprio_get */
+ "linux_inotify_init", /* 316 = linux_inotify_init */
+ "linux_inotify_add_watch", /* 317 = linux_inotify_add_watch */
+ "linux_inotify_rm_watch", /* 318 = linux_inotify_rm_watch */
+ "linux_mbind", /* 319 = linux_mbind */
+ "linux_get_mempolicy", /* 320 = linux_get_mempolicy */
+ "linux_set_mempolicy", /* 321 = linux_set_mempolicy */
+ "linux_openat", /* 322 = linux_openat */
+ "linux_mkdirat", /* 323 = linux_mkdirat */
+ "linux_mknodat", /* 324 = linux_mknodat */
+ "linux_fchownat", /* 325 = linux_fchownat */
+ "linux_futimesat", /* 326 = linux_futimesat */
+ "linux_fstatat64", /* 327 = linux_fstatat64 */
+ "linux_unlinkat", /* 328 = linux_unlinkat */
+ "linux_renameat", /* 329 = linux_renameat */
+ "linux_linkat", /* 330 = linux_linkat */
+ "linux_symlinkat", /* 331 = linux_symlinkat */
+ "linux_readlinkat", /* 332 = linux_readlinkat */
+ "linux_fchmodat", /* 333 = linux_fchmodat */
+ "linux_faccessat", /* 334 = linux_faccessat */
+ "linux_pselect6", /* 335 = linux_pselect6 */
+ "linux_ppoll", /* 336 = linux_ppoll */
+ "linux_unshare", /* 337 = linux_unshare */
+ "linux_set_robust_list", /* 338 = linux_set_robust_list */
+ "linux_get_robust_list", /* 339 = linux_get_robust_list */
+ "linux_splice", /* 340 = linux_splice */
+ "linux_sync_file_range", /* 341 = linux_sync_file_range */
+ "linux_tee", /* 342 = linux_tee */
+ "linux_vmsplice", /* 343 = linux_vmsplice */
+ "linux_move_pages", /* 344 = linux_move_pages */
+ "linux_getcpu", /* 345 = linux_getcpu */
+ "linux_epoll_pwait", /* 346 = linux_epoll_pwait */
+ "linux_kexec_load", /* 347 = linux_kexec_load */
+ "linux_utimensat", /* 348 = linux_utimensat */
+ "linux_signalfd", /* 349 = linux_signalfd */
+ "linux_timerfd_create", /* 350 = linux_timerfd_create */
+ "linux_eventfd", /* 351 = linux_eventfd */
+ "linux_fallocate", /* 352 = linux_fallocate */
+ "linux_timerfd_settime", /* 353 = linux_timerfd_settime */
+ "linux_timerfd_gettime", /* 354 = linux_timerfd_gettime */
+ "linux_signalfd4", /* 355 = linux_signalfd4 */
+ "linux_eventfd2", /* 356 = linux_eventfd2 */
+ "linux_epoll_create1", /* 357 = linux_epoll_create1 */
+ "linux_dup3", /* 358 = linux_dup3 */
+ "linux_pipe2", /* 359 = linux_pipe2 */
+ "linux_inotify_init1", /* 360 = linux_inotify_init1 */
+ "linux_preadv", /* 361 = linux_preadv */
+ "linux_pwritev", /* 362 = linux_pwritev */
+ "linux_rt_tsigqueueinfo", /* 363 = linux_rt_tsigqueueinfo */
+ "linux_perf_event_open", /* 364 = linux_perf_event_open */
+ "linux_recvmmsg", /* 365 = linux_recvmmsg */
+ "linux_accept4", /* 366 = linux_accept4 */
+ "linux_fanotify_init", /* 367 = linux_fanotify_init */
+ "linux_fanotify_mark", /* 368 = linux_fanotify_mark */
+ "linux_prlimit64", /* 369 = linux_prlimit64 */
+ "linux_name_to_handle_at", /* 370 = linux_name_to_handle_at */
+ "linux_open_by_handle_at", /* 371 = linux_open_by_handle_at */
+ "linux_clock_adjtime", /* 372 = linux_clock_adjtime */
+ "linux_syncfs", /* 373 = linux_syncfs */
+ "linux_sendmmsg", /* 374 = linux_sendmmsg */
+ "linux_setns", /* 375 = linux_setns */
+ "linux_process_vm_readv", /* 376 = linux_process_vm_readv */
+ "linux_process_vm_writev", /* 377 = linux_process_vm_writev */
+ "#378", /* 378 = kcmp */
+ "#379", /* 379 = finit_module */
+ "#380", /* 380 = sys_sched_setattr */
+ "#381", /* 381 = sys_sched_getattr */
+ "#382", /* 382 = sys_renameat2 */
+ "#383", /* 383 = sys_seccomp */
+ "#384", /* 384 = sys_getrandom */
+ "#385", /* 385 = sys_memfd_create */
+ "#386", /* 386 = sys_bpf */
+ "#387", /* 387 = sys_execveat */
+ "#388", /* 388 = sys_userfaultfd */
+ "#389", /* 389 = sys_membarrier */
+ "#390", /* 390 = sys_mlock2 */
+ "#391", /* 391 = sys_copy_file_range */
+ "#392", /* 392 = sys_preadv2 */
+ "#393", /* 393 = sys_pwritev2 */
+ "#394", /* 394 = */
+ "#395", /* 395 = */
+ "#396", /* 396 = */
+ "#397", /* 397 = */
+ "#398", /* 398 = */
+ "#399", /* 399 = */
+ "#400", /* 400 = unimpl400 */
+ "#401", /* 401 = breakpoint */
+ "#402", /* 402 = cacheflush */
+ "#403", /* 403 = usr26 */
+ "#404", /* 404 = usr32 */
+ "linux_set_tls", /* 405 = linux_set_tls */
+ "#406", /* 406 = nosys */
+};
diff --git a/sys/arm/linux/linux_sysent.c b/sys/arm/linux/linux_sysent.c
new file mode 100644
index 000000000000..ad24f759cd2f
--- /dev/null
+++ b/sys/arm/linux/linux_sysent.c
@@ -0,0 +1,427 @@
+/*
+ * System call switch table.
+ *
+ * DO NOT EDIT-- this file is automatically @generated.
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/sysent.h>
+#include <sys/sysproto.h>
+#include <compat/linux/linux_sysproto.h>
+#include <arm/linux/linux.h>
+#include <arm/linux/linux_proto.h>
+
+#define AS(name) (sizeof(struct name) / sizeof(register_t))
+
+/* The casts are bogus but will do for now. */
+struct sysent linux_sysent[] = {
+#define nosys linux_nosys
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 0 = setup */
+ { .sy_narg = AS(linux_exit_args), .sy_call = (sy_call_t *)linux_exit, .sy_auevent = AUE_EXIT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 1 = linux_exit */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_fork, .sy_auevent = AUE_FORK, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 2 = linux_fork */
+ { .sy_narg = AS(read_args), .sy_call = (sy_call_t *)sys_read, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 3 = read */
+ { .sy_narg = AS(write_args), .sy_call = (sy_call_t *)sys_write, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 4 = write */
+ { .sy_narg = AS(linux_open_args), .sy_call = (sy_call_t *)linux_open, .sy_auevent = AUE_OPEN_RWTC, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 5 = linux_open */
+ { .sy_narg = AS(close_args), .sy_call = (sy_call_t *)sys_close, .sy_auevent = AUE_CLOSE, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 6 = close */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 7 = ; */
+ { .sy_narg = AS(linux_creat_args), .sy_call = (sy_call_t *)linux_creat, .sy_auevent = AUE_CREAT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 8 = linux_creat */
+ { .sy_narg = AS(linux_link_args), .sy_call = (sy_call_t *)linux_link, .sy_auevent = AUE_LINK, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 9 = linux_link */
+ { .sy_narg = AS(linux_unlink_args), .sy_call = (sy_call_t *)linux_unlink, .sy_auevent = AUE_UNLINK, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 10 = linux_unlink */
+ { .sy_narg = AS(linux_execve_args), .sy_call = (sy_call_t *)linux_execve, .sy_auevent = AUE_EXECVE, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 11 = linux_execve */
+ { .sy_narg = AS(linux_chdir_args), .sy_call = (sy_call_t *)linux_chdir, .sy_auevent = AUE_CHDIR, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 12 = linux_chdir */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 13 = ; */
+ { .sy_narg = AS(linux_mknod_args), .sy_call = (sy_call_t *)linux_mknod, .sy_auevent = AUE_MKNOD, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 14 = linux_mknod */
+ { .sy_narg = AS(linux_chmod_args), .sy_call = (sy_call_t *)linux_chmod, .sy_auevent = AUE_CHMOD, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 15 = linux_chmod */
+ { .sy_narg = AS(linux_lchown16_args), .sy_call = (sy_call_t *)linux_lchown16, .sy_auevent = AUE_LCHOWN, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 16 = linux_lchown16 */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 17 = ; */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 18 = ; */
+ { .sy_narg = AS(linux_lseek_args), .sy_call = (sy_call_t *)linux_lseek, .sy_auevent = AUE_LSEEK, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 19 = linux_lseek */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_getpid, .sy_auevent = AUE_GETPID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 20 = linux_getpid */
+ { .sy_narg = AS(linux_mount_args), .sy_call = (sy_call_t *)linux_mount, .sy_auevent = AUE_MOUNT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 21 = linux_mount */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 22 = ; */
+ { .sy_narg = AS(linux_setuid16_args), .sy_call = (sy_call_t *)linux_setuid16, .sy_auevent = AUE_SETUID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 23 = linux_setuid16 */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_getuid16, .sy_auevent = AUE_GETUID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 24 = linux_getuid16 */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 25 = ; */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 26 = ptrace */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 27 = ; */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 28 = ; */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_pause, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 29 = linux_pause */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 30 = ; */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 31 = ; */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 32 = ; */
+ { .sy_narg = AS(linux_access_args), .sy_call = (sy_call_t *)linux_access, .sy_auevent = AUE_ACCESS, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 33 = linux_access */
+ { .sy_narg = AS(linux_nice_args), .sy_call = (sy_call_t *)linux_nice, .sy_auevent = AUE_NICE, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 34 = linux_nice */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 35 = ; */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)sys_sync, .sy_auevent = AUE_SYNC, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 36 = sync */
+ { .sy_narg = AS(linux_kill_args), .sy_call = (sy_call_t *)linux_kill, .sy_auevent = AUE_KILL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 37 = linux_kill */
+ { .sy_narg = AS(linux_rename_args), .sy_call = (sy_call_t *)linux_rename, .sy_auevent = AUE_RENAME, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 38 = linux_rename */
+ { .sy_narg = AS(linux_mkdir_args), .sy_call = (sy_call_t *)linux_mkdir, .sy_auevent = AUE_MKDIR, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 39 = linux_mkdir */
+ { .sy_narg = AS(linux_rmdir_args), .sy_call = (sy_call_t *)linux_rmdir, .sy_auevent = AUE_RMDIR, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 40 = linux_rmdir */
+ { .sy_narg = AS(dup_args), .sy_call = (sy_call_t *)sys_dup, .sy_auevent = AUE_DUP, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 41 = dup */
+ { .sy_narg = AS(linux_pipe_args), .sy_call = (sy_call_t *)linux_pipe, .sy_auevent = AUE_PIPE, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 42 = linux_pipe */
+ { .sy_narg = AS(linux_times_args), .sy_call = (sy_call_t *)linux_times, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 43 = linux_times */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 44 = ; */
+ { .sy_narg = AS(linux_brk_args), .sy_call = (sy_call_t *)linux_brk, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 45 = linux_brk */
+ { .sy_narg = AS(linux_setgid16_args), .sy_call = (sy_call_t *)linux_setgid16, .sy_auevent = AUE_SETGID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 46 = linux_setgid16 */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_getgid16, .sy_auevent = AUE_GETGID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 47 = linux_getgid16 */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 48 = ; */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_geteuid16, .sy_auevent = AUE_GETEUID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 49 = linux_geteuid16 */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_getegid16, .sy_auevent = AUE_GETEGID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 50 = linux_getegid16 */
+ { .sy_narg = AS(acct_args), .sy_call = (sy_call_t *)sys_acct, .sy_auevent = AUE_ACCT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 51 = acct */
+ { .sy_narg = AS(linux_umount_args), .sy_call = (sy_call_t *)linux_umount, .sy_auevent = AUE_UMOUNT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 52 = linux_umount */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 53 = ; */
+ { .sy_narg = AS(linux_ioctl_args), .sy_call = (sy_call_t *)linux_ioctl, .sy_auevent = AUE_IOCTL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 54 = linux_ioctl */
+ { .sy_narg = AS(linux_fcntl_args), .sy_call = (sy_call_t *)linux_fcntl, .sy_auevent = AUE_FCNTL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 55 = linux_fcntl */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 56 = ; */
+ { .sy_narg = AS(setpgid_args), .sy_call = (sy_call_t *)sys_setpgid, .sy_auevent = AUE_SETPGRP, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 57 = setpgid */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 58 = ; */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 59 = ; */
+ { .sy_narg = AS(umask_args), .sy_call = (sy_call_t *)sys_umask, .sy_auevent = AUE_UMASK, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 60 = umask */
+ { .sy_narg = AS(chroot_args), .sy_call = (sy_call_t *)sys_chroot, .sy_auevent = AUE_CHROOT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 61 = chroot */
+ { .sy_narg = AS(linux_ustat_args), .sy_call = (sy_call_t *)linux_ustat, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 62 = linux_ustat */
+ { .sy_narg = AS(dup2_args), .sy_call = (sy_call_t *)sys_dup2, .sy_auevent = AUE_DUP2, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 63 = dup2 */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_getppid, .sy_auevent = AUE_GETPPID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 64 = linux_getppid */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)sys_getpgrp, .sy_auevent = AUE_GETPGRP, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 65 = getpgrp */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)sys_setsid, .sy_auevent = AUE_SETSID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 66 = setsid */
+ { .sy_narg = AS(linux_sigaction_args), .sy_call = (sy_call_t *)linux_sigaction, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 67 = linux_sigaction */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 68 = ; */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 69 = ; */
+ { .sy_narg = AS(linux_setreuid16_args), .sy_call = (sy_call_t *)linux_setreuid16, .sy_auevent = AUE_SETREUID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 70 = linux_setreuid16 */
+ { .sy_narg = AS(linux_setregid16_args), .sy_call = (sy_call_t *)linux_setregid16, .sy_auevent = AUE_SETREGID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 71 = linux_setregid16 */
+ { .sy_narg = AS(linux_sigsuspend_args), .sy_call = (sy_call_t *)linux_sigsuspend, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 72 = linux_sigsuspend */
+ { .sy_narg = AS(linux_sigpending_args), .sy_call = (sy_call_t *)linux_sigpending, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 73 = linux_sigpending */
+ { .sy_narg = AS(linux_sethostname_args), .sy_call = (sy_call_t *)linux_sethostname, .sy_auevent = AUE_SYSCTL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 74 = linux_sethostname */
+ { .sy_narg = AS(linux_setrlimit_args), .sy_call = (sy_call_t *)linux_setrlimit, .sy_auevent = AUE_SETRLIMIT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 75 = linux_setrlimit */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 76 = ; */
+ { .sy_narg = AS(getrusage_args), .sy_call = (sy_call_t *)sys_getrusage, .sy_auevent = AUE_GETRUSAGE, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 77 = getrusage */
+ { .sy_narg = AS(linux_gettimeofday_args), .sy_call = (sy_call_t *)linux_gettimeofday, .sy_auevent = AUE_GETTIMEOFDAY, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 78 = linux_gettimeofday */
+ { .sy_narg = AS(linux_settimeofday_args), .sy_call = (sy_call_t *)linux_settimeofday, .sy_auevent = AUE_SETTIMEOFDAY, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 79 = linux_settimeofday */
+ { .sy_narg = AS(linux_getgroups16_args), .sy_call = (sy_call_t *)linux_getgroups16, .sy_auevent = AUE_GETGROUPS, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 80 = linux_getgroups16 */
+ { .sy_narg = AS(linux_setgroups16_args), .sy_call = (sy_call_t *)linux_setgroups16, .sy_auevent = AUE_SETGROUPS, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 81 = linux_setgroups16 */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 82 = ; */
+ { .sy_narg = AS(linux_symlink_args), .sy_call = (sy_call_t *)linux_symlink, .sy_auevent = AUE_SYMLINK, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 83 = linux_symlink */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 84 = ; */
+ { .sy_narg = AS(linux_readlink_args), .sy_call = (sy_call_t *)linux_readlink, .sy_auevent = AUE_READLINK, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 85 = linux_readlink */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 86 = uselib */
+ { .sy_narg = AS(swapon_args), .sy_call = (sy_call_t *)sys_swapon, .sy_auevent = AUE_SWAPON, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 87 = swapon */
+ { .sy_narg = AS(linux_reboot_args), .sy_call = (sy_call_t *)linux_reboot, .sy_auevent = AUE_REBOOT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 88 = linux_reboot */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 89 = ; */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 90 = ; */
+ { .sy_narg = AS(munmap_args), .sy_call = (sy_call_t *)sys_munmap, .sy_auevent = AUE_MUNMAP, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 91 = munmap */
+ { .sy_narg = AS(linux_truncate_args), .sy_call = (sy_call_t *)linux_truncate, .sy_auevent = AUE_TRUNCATE, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 92 = linux_truncate */
+ { .sy_narg = AS(linux_ftruncate_args), .sy_call = (sy_call_t *)linux_ftruncate, .sy_auevent = AUE_FTRUNCATE, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 93 = linux_ftruncate */
+ { .sy_narg = AS(fchmod_args), .sy_call = (sy_call_t *)sys_fchmod, .sy_auevent = AUE_FCHMOD, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 94 = fchmod */
+ { .sy_narg = AS(fchown_args), .sy_call = (sy_call_t *)sys_fchown, .sy_auevent = AUE_FCHOWN, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 95 = fchown */
+ { .sy_narg = AS(linux_getpriority_args), .sy_call = (sy_call_t *)linux_getpriority, .sy_auevent = AUE_GETPRIORITY, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 96 = linux_getpriority */
+ { .sy_narg = AS(setpriority_args), .sy_call = (sy_call_t *)sys_setpriority, .sy_auevent = AUE_SETPRIORITY, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 97 = setpriority */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 98 = ; */
+ { .sy_narg = AS(linux_statfs_args), .sy_call = (sy_call_t *)linux_statfs, .sy_auevent = AUE_STATFS, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 99 = linux_statfs */
+ { .sy_narg = AS(linux_fstatfs_args), .sy_call = (sy_call_t *)linux_fstatfs, .sy_auevent = AUE_FSTATFS, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 100 = linux_fstatfs */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 101 = ; */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 102 = ; */
+ { .sy_narg = AS(linux_syslog_args), .sy_call = (sy_call_t *)linux_syslog, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 103 = linux_syslog */
+ { .sy_narg = AS(linux_setitimer_args), .sy_call = (sy_call_t *)linux_setitimer, .sy_auevent = AUE_SETITIMER, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 104 = linux_setitimer */
+ { .sy_narg = AS(linux_getitimer_args), .sy_call = (sy_call_t *)linux_getitimer, .sy_auevent = AUE_GETITIMER, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 105 = linux_getitimer */
+ { .sy_narg = AS(linux_newstat_args), .sy_call = (sy_call_t *)linux_newstat, .sy_auevent = AUE_STAT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 106 = linux_newstat */
+ { .sy_narg = AS(linux_newlstat_args), .sy_call = (sy_call_t *)linux_newlstat, .sy_auevent = AUE_LSTAT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 107 = linux_newlstat */
+ { .sy_narg = AS(linux_newfstat_args), .sy_call = (sy_call_t *)linux_newfstat, .sy_auevent = AUE_FSTAT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 108 = linux_newfstat */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 109 = ; */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 110 = ; */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_vhangup, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 111 = linux_vhangup */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 112 = ; */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 113 = ; */
+ { .sy_narg = AS(linux_wait4_args), .sy_call = (sy_call_t *)linux_wait4, .sy_auevent = AUE_WAIT4, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 114 = linux_wait4 */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_swapoff, .sy_auevent = AUE_SWAPOFF, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 115 = linux_swapoff */
+ { .sy_narg = AS(linux_sysinfo_args), .sy_call = (sy_call_t *)linux_sysinfo, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 116 = linux_sysinfo */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 117 = ; */
+ { .sy_narg = AS(fsync_args), .sy_call = (sy_call_t *)sys_fsync, .sy_auevent = AUE_FSYNC, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 118 = fsync */
+ { .sy_narg = AS(linux_sigreturn_args), .sy_call = (sy_call_t *)linux_sigreturn, .sy_auevent = AUE_SIGRETURN, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 119 = linux_sigreturn */
+ { .sy_narg = AS(linux_clone_args), .sy_call = (sy_call_t *)linux_clone, .sy_auevent = AUE_RFORK, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 120 = linux_clone */
+ { .sy_narg = AS(linux_setdomainname_args), .sy_call = (sy_call_t *)linux_setdomainname, .sy_auevent = AUE_SYSCTL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 121 = linux_setdomainname */
+ { .sy_narg = AS(linux_newuname_args), .sy_call = (sy_call_t *)linux_newuname, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 122 = linux_newuname */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 123 = ; */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_adjtimex, .sy_auevent = AUE_ADJTIME, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 124 = linux_adjtimex */
+ { .sy_narg = AS(linux_mprotect_args), .sy_call = (sy_call_t *)linux_mprotect, .sy_auevent = AUE_MPROTECT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 125 = linux_mprotect */
+ { .sy_narg = AS(linux_sigprocmask_args), .sy_call = (sy_call_t *)linux_sigprocmask, .sy_auevent = AUE_SIGPROCMASK, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 126 = linux_sigprocmask */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 127 = ; */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_init_module, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 128 = linux_init_module */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_delete_module, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 129 = linux_delete_module */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 130 = ; */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_quotactl, .sy_auevent = AUE_QUOTACTL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 131 = linux_quotactl */
+ { .sy_narg = AS(getpgid_args), .sy_call = (sy_call_t *)sys_getpgid, .sy_auevent = AUE_GETPGID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 132 = getpgid */
+ { .sy_narg = AS(fchdir_args), .sy_call = (sy_call_t *)sys_fchdir, .sy_auevent = AUE_FCHDIR, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 133 = fchdir */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_bdflush, .sy_auevent = AUE_BDFLUSH, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 134 = linux_bdflush */
+ { .sy_narg = AS(linux_sysfs_args), .sy_call = (sy_call_t *)linux_sysfs, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 135 = linux_sysfs */
+ { .sy_narg = AS(linux_personality_args), .sy_call = (sy_call_t *)linux_personality, .sy_auevent = AUE_PERSONALITY, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 136 = linux_personality */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 137 = ; */
+ { .sy_narg = AS(linux_setfsuid16_args), .sy_call = (sy_call_t *)linux_setfsuid16, .sy_auevent = AUE_SETFSUID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 138 = linux_setfsuid16 */
+ { .sy_narg = AS(linux_setfsgid16_args), .sy_call = (sy_call_t *)linux_setfsgid16, .sy_auevent = AUE_SETFSGID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 139 = linux_setfsgid16 */
+ { .sy_narg = AS(linux_llseek_args), .sy_call = (sy_call_t *)linux_llseek, .sy_auevent = AUE_LSEEK, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 140 = linux_llseek */
+ { .sy_narg = AS(linux_getdents_args), .sy_call = (sy_call_t *)linux_getdents, .sy_auevent = AUE_GETDIRENTRIES, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 141 = linux_getdents */
+ { .sy_narg = AS(linux_select_args), .sy_call = (sy_call_t *)linux_select, .sy_auevent = AUE_SELECT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 142 = linux_select */
+ { .sy_narg = AS(flock_args), .sy_call = (sy_call_t *)sys_flock, .sy_auevent = AUE_FLOCK, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 143 = flock */
+ { .sy_narg = AS(linux_msync_args), .sy_call = (sy_call_t *)linux_msync, .sy_auevent = AUE_MSYNC, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 144 = linux_msync */
+ { .sy_narg = AS(readv_args), .sy_call = (sy_call_t *)sys_readv, .sy_auevent = AUE_READV, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 145 = readv */
+ { .sy_narg = AS(writev_args), .sy_call = (sy_call_t *)sys_writev, .sy_auevent = AUE_WRITEV, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 146 = writev */
+ { .sy_narg = AS(linux_getsid_args), .sy_call = (sy_call_t *)linux_getsid, .sy_auevent = AUE_GETSID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 147 = linux_getsid */
+ { .sy_narg = AS(linux_fdatasync_args), .sy_call = (sy_call_t *)linux_fdatasync, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 148 = linux_fdatasync */
+ { .sy_narg = AS(linux_sysctl_args), .sy_call = (sy_call_t *)linux_sysctl, .sy_auevent = AUE_SYSCTL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 149 = linux_sysctl */
+ { .sy_narg = AS(mlock_args), .sy_call = (sy_call_t *)sys_mlock, .sy_auevent = AUE_MLOCK, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 150 = mlock */
+ { .sy_narg = AS(munlock_args), .sy_call = (sy_call_t *)sys_munlock, .sy_auevent = AUE_MUNLOCK, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 151 = munlock */
+ { .sy_narg = AS(mlockall_args), .sy_call = (sy_call_t *)sys_mlockall, .sy_auevent = AUE_MLOCKALL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 152 = mlockall */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)sys_munlockall, .sy_auevent = AUE_MUNLOCKALL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 153 = munlockall */
+ { .sy_narg = AS(linux_sched_setparam_args), .sy_call = (sy_call_t *)linux_sched_setparam, .sy_auevent = AUE_SCHED_SETPARAM, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 154 = linux_sched_setparam */
+ { .sy_narg = AS(linux_sched_getparam_args), .sy_call = (sy_call_t *)linux_sched_getparam, .sy_auevent = AUE_SCHED_GETPARAM, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 155 = linux_sched_getparam */
+ { .sy_narg = AS(linux_sched_setscheduler_args), .sy_call = (sy_call_t *)linux_sched_setscheduler, .sy_auevent = AUE_SCHED_SETSCHEDULER, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 156 = linux_sched_setscheduler */
+ { .sy_narg = AS(linux_sched_getscheduler_args), .sy_call = (sy_call_t *)linux_sched_getscheduler, .sy_auevent = AUE_SCHED_GETSCHEDULER, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 157 = linux_sched_getscheduler */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)sys_sched_yield, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 158 = sched_yield */
+ { .sy_narg = AS(linux_sched_get_priority_max_args), .sy_call = (sy_call_t *)linux_sched_get_priority_max, .sy_auevent = AUE_SCHED_GET_PRIORITY_MAX, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 159 = linux_sched_get_priority_max */
+ { .sy_narg = AS(linux_sched_get_priority_min_args), .sy_call = (sy_call_t *)linux_sched_get_priority_min, .sy_auevent = AUE_SCHED_GET_PRIORITY_MIN, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 160 = linux_sched_get_priority_min */
+ { .sy_narg = AS(linux_sched_rr_get_interval_args), .sy_call = (sy_call_t *)linux_sched_rr_get_interval, .sy_auevent = AUE_SCHED_RR_GET_INTERVAL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 161 = linux_sched_rr_get_interval */
+ { .sy_narg = AS(linux_nanosleep_args), .sy_call = (sy_call_t *)linux_nanosleep, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 162 = linux_nanosleep */
+ { .sy_narg = AS(linux_mremap_args), .sy_call = (sy_call_t *)linux_mremap, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 163 = linux_mremap */
+ { .sy_narg = AS(linux_setresuid16_args), .sy_call = (sy_call_t *)linux_setresuid16, .sy_auevent = AUE_SETRESUID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 164 = linux_setresuid16 */
+ { .sy_narg = AS(linux_getresuid16_args), .sy_call = (sy_call_t *)linux_getresuid16, .sy_auevent = AUE_GETRESUID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 165 = linux_getresuid16 */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 166 = ; */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 167 = ; */
+ { .sy_narg = AS(poll_args), .sy_call = (sy_call_t *)sys_poll, .sy_auevent = AUE_POLL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 168 = poll */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 169 = ; */
+ { .sy_narg = AS(linux_setresgid16_args), .sy_call = (sy_call_t *)linux_setresgid16, .sy_auevent = AUE_SETRESGID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 170 = linux_setresgid16 */
+ { .sy_narg = AS(linux_getresgid16_args), .sy_call = (sy_call_t *)linux_getresgid16, .sy_auevent = AUE_GETRESGID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 171 = linux_getresgid16 */
+ { .sy_narg = AS(linux_prctl_args), .sy_call = (sy_call_t *)linux_prctl, .sy_auevent = AUE_PRCTL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 172 = linux_prctl */
+ { .sy_narg = AS(linux_rt_sigreturn_args), .sy_call = (sy_call_t *)linux_rt_sigreturn, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 173 = linux_rt_sigreturn */
+ { .sy_narg = AS(linux_rt_sigaction_args), .sy_call = (sy_call_t *)linux_rt_sigaction, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 174 = linux_rt_sigaction */
+ { .sy_narg = AS(linux_rt_sigprocmask_args), .sy_call = (sy_call_t *)linux_rt_sigprocmask, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 175 = linux_rt_sigprocmask */
+ { .sy_narg = AS(linux_rt_sigpending_args), .sy_call = (sy_call_t *)linux_rt_sigpending, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 176 = linux_rt_sigpending */
+ { .sy_narg = AS(linux_rt_sigtimedwait_args), .sy_call = (sy_call_t *)linux_rt_sigtimedwait, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 177 = linux_rt_sigtimedwait */
+ { .sy_narg = AS(linux_rt_sigqueueinfo_args), .sy_call = (sy_call_t *)linux_rt_sigqueueinfo, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 178 = linux_rt_sigqueueinfo */
+ { .sy_narg = AS(linux_rt_sigsuspend_args), .sy_call = (sy_call_t *)linux_rt_sigsuspend, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 179 = linux_rt_sigsuspend */
+ { .sy_narg = AS(linux_pread_args), .sy_call = (sy_call_t *)linux_pread, .sy_auevent = AUE_PREAD, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 180 = linux_pread */
+ { .sy_narg = AS(linux_pwrite_args), .sy_call = (sy_call_t *)linux_pwrite, .sy_auevent = AUE_PWRITE, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 181 = linux_pwrite */
+ { .sy_narg = AS(linux_chown16_args), .sy_call = (sy_call_t *)linux_chown16, .sy_auevent = AUE_CHOWN, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 182 = linux_chown16 */
+ { .sy_narg = AS(linux_getcwd_args), .sy_call = (sy_call_t *)linux_getcwd, .sy_auevent = AUE_GETCWD, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 183 = linux_getcwd */
+ { .sy_narg = AS(linux_capget_args), .sy_call = (sy_call_t *)linux_capget, .sy_auevent = AUE_CAPGET, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 184 = linux_capget */
+ { .sy_narg = AS(linux_capset_args), .sy_call = (sy_call_t *)linux_capset, .sy_auevent = AUE_CAPSET, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 185 = linux_capset */
+ { .sy_narg = AS(linux_sigaltstack_args), .sy_call = (sy_call_t *)linux_sigaltstack, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 186 = linux_sigaltstack */
+ { .sy_narg = AS(linux_sendfile_args), .sy_call = (sy_call_t *)linux_sendfile, .sy_auevent = AUE_SENDFILE, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 187 = linux_sendfile */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 188 = ; */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 189 = ; */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_vfork, .sy_auevent = AUE_VFORK, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 190 = linux_vfork */
+ { .sy_narg = AS(linux_getrlimit_args), .sy_call = (sy_call_t *)linux_getrlimit, .sy_auevent = AUE_GETRLIMIT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 191 = linux_getrlimit */
+ { .sy_narg = AS(linux_mmap2_args), .sy_call = (sy_call_t *)linux_mmap2, .sy_auevent = AUE_MMAP, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 192 = linux_mmap2 */
+ { .sy_narg = AS(linux_truncate64_args), .sy_call = (sy_call_t *)linux_truncate64, .sy_auevent = AUE_TRUNCATE, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 193 = linux_truncate64 */
+ { .sy_narg = AS(linux_ftruncate64_args), .sy_call = (sy_call_t *)linux_ftruncate64, .sy_auevent = AUE_FTRUNCATE, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 194 = linux_ftruncate64 */
+ { .sy_narg = AS(linux_stat64_args), .sy_call = (sy_call_t *)linux_stat64, .sy_auevent = AUE_STAT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 195 = linux_stat64 */
+ { .sy_narg = AS(linux_lstat64_args), .sy_call = (sy_call_t *)linux_lstat64, .sy_auevent = AUE_LSTAT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 196 = linux_lstat64 */
+ { .sy_narg = AS(linux_fstat64_args), .sy_call = (sy_call_t *)linux_fstat64, .sy_auevent = AUE_FSTAT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 197 = linux_fstat64 */
+ { .sy_narg = AS(linux_lchown_args), .sy_call = (sy_call_t *)linux_lchown, .sy_auevent = AUE_LCHOWN, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 198 = linux_lchown */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_getuid, .sy_auevent = AUE_GETUID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 199 = linux_getuid */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_getgid, .sy_auevent = AUE_GETGID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 200 = linux_getgid */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)sys_geteuid, .sy_auevent = AUE_GETEUID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 201 = geteuid */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)sys_getegid, .sy_auevent = AUE_GETEGID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 202 = getegid */
+ { .sy_narg = AS(setreuid_args), .sy_call = (sy_call_t *)sys_setreuid, .sy_auevent = AUE_SETREUID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 203 = setreuid */
+ { .sy_narg = AS(setregid_args), .sy_call = (sy_call_t *)sys_setregid, .sy_auevent = AUE_SETREGID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 204 = setregid */
+ { .sy_narg = AS(linux_getgroups_args), .sy_call = (sy_call_t *)linux_getgroups, .sy_auevent = AUE_GETGROUPS, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 205 = linux_getgroups */
+ { .sy_narg = AS(linux_setgroups_args), .sy_call = (sy_call_t *)linux_setgroups, .sy_auevent = AUE_SETGROUPS, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 206 = linux_setgroups */
+ { .sy_narg = AS(fchown_args), .sy_call = (sy_call_t *)sys_fchown, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 207 = fchown */
+ { .sy_narg = AS(setresuid_args), .sy_call = (sy_call_t *)sys_setresuid, .sy_auevent = AUE_SETRESUID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 208 = setresuid */
+ { .sy_narg = AS(getresuid_args), .sy_call = (sy_call_t *)sys_getresuid, .sy_auevent = AUE_GETRESUID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 209 = getresuid */
+ { .sy_narg = AS(setresgid_args), .sy_call = (sy_call_t *)sys_setresgid, .sy_auevent = AUE_SETRESGID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 210 = setresgid */
+ { .sy_narg = AS(getresgid_args), .sy_call = (sy_call_t *)sys_getresgid, .sy_auevent = AUE_GETRESGID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 211 = getresgid */
+ { .sy_narg = AS(linux_chown_args), .sy_call = (sy_call_t *)linux_chown, .sy_auevent = AUE_CHOWN, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 212 = linux_chown */
+ { .sy_narg = AS(setuid_args), .sy_call = (sy_call_t *)sys_setuid, .sy_auevent = AUE_SETUID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 213 = setuid */
+ { .sy_narg = AS(setgid_args), .sy_call = (sy_call_t *)sys_setgid, .sy_auevent = AUE_SETGID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 214 = setgid */
+ { .sy_narg = AS(linux_setfsuid_args), .sy_call = (sy_call_t *)linux_setfsuid, .sy_auevent = AUE_SETFSUID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 215 = linux_setfsuid */
+ { .sy_narg = AS(linux_setfsgid_args), .sy_call = (sy_call_t *)linux_setfsgid, .sy_auevent = AUE_SETFSGID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 216 = linux_setfsgid */
+ { .sy_narg = AS(linux_getdents64_args), .sy_call = (sy_call_t *)linux_getdents64, .sy_auevent = AUE_GETDIRENTRIES, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 217 = linux_getdents64 */
+ { .sy_narg = AS(linux_pivot_root_args), .sy_call = (sy_call_t *)linux_pivot_root, .sy_auevent = AUE_PIVOT_ROOT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 218 = linux_pivot_root */
+ { .sy_narg = AS(linux_mincore_args), .sy_call = (sy_call_t *)linux_mincore, .sy_auevent = AUE_MINCORE, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 219 = linux_mincore */
+ { .sy_narg = AS(madvise_args), .sy_call = (sy_call_t *)sys_madvise, .sy_auevent = AUE_MADVISE, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 220 = madvise */
+ { .sy_narg = AS(linux_fcntl64_args), .sy_call = (sy_call_t *)linux_fcntl64, .sy_auevent = AUE_FCNTL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 221 = linux_fcntl64 */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 222 = */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 223 = */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_gettid, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 224 = linux_gettid */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 225 = linux_readahead */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_setxattr, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 226 = linux_setxattr */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_lsetxattr, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 227 = linux_lsetxattr */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_fsetxattr, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 228 = linux_fsetxattr */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_getxattr, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 229 = linux_getxattr */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_lgetxattr, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 230 = linux_lgetxattr */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_fgetxattr, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 231 = linux_fgetxattr */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_listxattr, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 232 = linux_listxattr */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_llistxattr, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 233 = linux_llistxattr */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_flistxattr, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 234 = linux_flistxattr */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_removexattr, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 235 = linux_removexattr */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_lremovexattr, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 236 = linux_lremovexattr */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_fremovexattr, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 237 = linux_fremovexattr */
+ { .sy_narg = AS(linux_tkill_args), .sy_call = (sy_call_t *)linux_tkill, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 238 = linux_tkill */
+ { .sy_narg = AS(linux_sendfile64_args), .sy_call = (sy_call_t *)linux_sendfile64, .sy_auevent = AUE_SENDFILE, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 239 = linux_sendfile64 */
+ { .sy_narg = AS(linux_sys_futex_args), .sy_call = (sy_call_t *)linux_sys_futex, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 240 = linux_sys_futex */
+ { .sy_narg = AS(linux_sched_setaffinity_args), .sy_call = (sy_call_t *)linux_sched_setaffinity, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 241 = linux_sched_setaffinity */
+ { .sy_narg = AS(linux_sched_getaffinity_args), .sy_call = (sy_call_t *)linux_sched_getaffinity, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 242 = linux_sched_getaffinity */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 243 = linux_io_setup */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 244 = linux_io_destroy */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 245 = linux_io_getevents */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 246 = linux_io_submit */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 247 = linux_io_cancel */
+ { .sy_narg = AS(linux_exit_group_args), .sy_call = (sy_call_t *)linux_exit_group, .sy_auevent = AUE_EXIT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 248 = linux_exit_group */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_lookup_dcookie, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 249 = linux_lookup_dcookie */
+ { .sy_narg = AS(linux_epoll_create_args), .sy_call = (sy_call_t *)linux_epoll_create, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 250 = linux_epoll_create */
+ { .sy_narg = AS(linux_epoll_ctl_args), .sy_call = (sy_call_t *)linux_epoll_ctl, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 251 = linux_epoll_ctl */
+ { .sy_narg = AS(linux_epoll_wait_args), .sy_call = (sy_call_t *)linux_epoll_wait, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 252 = linux_epoll_wait */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_remap_file_pages, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 253 = linux_remap_file_pages */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 254 = ; */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 255 = ; */
+ { .sy_narg = AS(linux_set_tid_address_args), .sy_call = (sy_call_t *)linux_set_tid_address, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 256 = linux_set_tid_address */
+ { .sy_narg = AS(linux_timer_create_args), .sy_call = (sy_call_t *)linux_timer_create, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 257 = linux_timer_create */
+ { .sy_narg = AS(linux_timer_settime_args), .sy_call = (sy_call_t *)linux_timer_settime, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 258 = linux_timer_settime */
+ { .sy_narg = AS(linux_timer_gettime_args), .sy_call = (sy_call_t *)linux_timer_gettime, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 259 = linux_timer_gettime */
+ { .sy_narg = AS(linux_timer_getoverrun_args), .sy_call = (sy_call_t *)linux_timer_getoverrun, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 260 = linux_timer_getoverrun */
+ { .sy_narg = AS(linux_timer_delete_args), .sy_call = (sy_call_t *)linux_timer_delete, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 261 = linux_timer_delete */
+ { .sy_narg = AS(linux_clock_settime_args), .sy_call = (sy_call_t *)linux_clock_settime, .sy_auevent = AUE_CLOCK_SETTIME, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 262 = linux_clock_settime */
+ { .sy_narg = AS(linux_clock_gettime_args), .sy_call = (sy_call_t *)linux_clock_gettime, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 263 = linux_clock_gettime */
+ { .sy_narg = AS(linux_clock_getres_args), .sy_call = (sy_call_t *)linux_clock_getres, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 264 = linux_clock_getres */
+ { .sy_narg = AS(linux_clock_nanosleep_args), .sy_call = (sy_call_t *)linux_clock_nanosleep, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 265 = linux_clock_nanosleep */
+ { .sy_narg = AS(linux_statfs64_args), .sy_call = (sy_call_t *)linux_statfs64, .sy_auevent = AUE_STATFS, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 266 = linux_statfs64 */
+ { .sy_narg = AS(linux_fstatfs64_args), .sy_call = (sy_call_t *)linux_fstatfs64, .sy_auevent = AUE_FSTATFS, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 267 = linux_fstatfs64 */
+ { .sy_narg = AS(linux_tgkill_args), .sy_call = (sy_call_t *)linux_tgkill, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 268 = linux_tgkill */
+ { .sy_narg = AS(linux_utimes_args), .sy_call = (sy_call_t *)linux_utimes, .sy_auevent = AUE_UTIMES, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 269 = linux_utimes */
+ { .sy_narg = AS(linux_fadvise64_64_args), .sy_call = (sy_call_t *)linux_fadvise64_64, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 270 = linux_fadvise64_64 */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 271 = pciconfig_iobase */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 272 = pciconfig_read */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 273 = pciconfig_write */
+ { .sy_narg = AS(linux_mq_open_args), .sy_call = (sy_call_t *)linux_mq_open, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 274 = linux_mq_open */
+ { .sy_narg = AS(linux_mq_unlink_args), .sy_call = (sy_call_t *)linux_mq_unlink, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 275 = linux_mq_unlink */
+ { .sy_narg = AS(linux_mq_timedsend_args), .sy_call = (sy_call_t *)linux_mq_timedsend, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 276 = linux_mq_timedsend */
+ { .sy_narg = AS(linux_mq_timedreceive_args), .sy_call = (sy_call_t *)linux_mq_timedreceive, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 277 = linux_mq_timedreceive */
+ { .sy_narg = AS(linux_mq_notify_args), .sy_call = (sy_call_t *)linux_mq_notify, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 278 = linux_mq_notify */
+ { .sy_narg = AS(linux_mq_getsetattr_args), .sy_call = (sy_call_t *)linux_mq_getsetattr, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 279 = linux_mq_getsetattr */
+ { .sy_narg = AS(linux_waitid_args), .sy_call = (sy_call_t *)linux_waitid, .sy_auevent = AUE_WAIT6, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 280 = linux_waitid */
+ { .sy_narg = AS(linux_socket_args), .sy_call = (sy_call_t *)linux_socket, .sy_auevent = AUE_SOCKET, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 281 = linux_socket */
+ { .sy_narg = AS(linux_bind_args), .sy_call = (sy_call_t *)linux_bind, .sy_auevent = AUE_BIND, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 282 = linux_bind */
+ { .sy_narg = AS(linux_connect_args), .sy_call = (sy_call_t *)linux_connect, .sy_auevent = AUE_CONNECT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 283 = linux_connect */
+ { .sy_narg = AS(linux_listen_args), .sy_call = (sy_call_t *)linux_listen, .sy_auevent = AUE_LISTEN, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 284 = linux_listen */
+ { .sy_narg = AS(linux_accept_args), .sy_call = (sy_call_t *)linux_accept, .sy_auevent = AUE_ACCEPT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 285 = linux_accept */
+ { .sy_narg = AS(linux_getsockname_args), .sy_call = (sy_call_t *)linux_getsockname, .sy_auevent = AUE_GETSOCKNAME, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 286 = linux_getsockname */
+ { .sy_narg = AS(linux_getpeername_args), .sy_call = (sy_call_t *)linux_getpeername, .sy_auevent = AUE_GETPEERNAME, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 287 = linux_getpeername */
+ { .sy_narg = AS(linux_socketpair_args), .sy_call = (sy_call_t *)linux_socketpair, .sy_auevent = AUE_SOCKETPAIR, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 288 = linux_socketpair */
+ { .sy_narg = AS(linux_send_args), .sy_call = (sy_call_t *)linux_send, .sy_auevent = AUE_SEND, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 289 = linux_send */
+ { .sy_narg = AS(linux_sendto_args), .sy_call = (sy_call_t *)linux_sendto, .sy_auevent = AUE_SENDTO, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 290 = linux_sendto */
+ { .sy_narg = AS(linux_recv_args), .sy_call = (sy_call_t *)linux_recv, .sy_auevent = AUE_RECV, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 291 = linux_recv */
+ { .sy_narg = AS(linux_recvfrom_args), .sy_call = (sy_call_t *)linux_recvfrom, .sy_auevent = AUE_RECVFROM, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 292 = linux_recvfrom */
+ { .sy_narg = AS(linux_shutdown_args), .sy_call = (sy_call_t *)linux_shutdown, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 293 = linux_shutdown */
+ { .sy_narg = AS(linux_setsockopt_args), .sy_call = (sy_call_t *)linux_setsockopt, .sy_auevent = AUE_SETSOCKOPT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 294 = linux_setsockopt */
+ { .sy_narg = AS(linux_getsockopt_args), .sy_call = (sy_call_t *)linux_getsockopt, .sy_auevent = AUE_GETSOCKOPT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 295 = linux_getsockopt */
+ { .sy_narg = AS(linux_sendmsg_args), .sy_call = (sy_call_t *)linux_sendmsg, .sy_auevent = AUE_SENDMSG, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 296 = linux_sendmsg */
+ { .sy_narg = AS(linux_recvmsg_args), .sy_call = (sy_call_t *)linux_recvmsg, .sy_auevent = AUE_RECVMSG, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 297 = linux_recvmsg */
+ { .sy_narg = AS(linux_semop_args), .sy_call = (sy_call_t *)linux_semop, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 298 = linux_semop */
+ { .sy_narg = AS(linux_semget_args), .sy_call = (sy_call_t *)linux_semget, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 299 = linux_semget */
+ { .sy_narg = AS(linux_semctl_args), .sy_call = (sy_call_t *)linux_semctl, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 300 = linux_semctl */
+ { .sy_narg = AS(linux_msgsnd_args), .sy_call = (sy_call_t *)linux_msgsnd, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 301 = linux_msgsnd */
+ { .sy_narg = AS(linux_msgrcv_args), .sy_call = (sy_call_t *)linux_msgrcv, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 302 = linux_msgrcv */
+ { .sy_narg = AS(linux_msgget_args), .sy_call = (sy_call_t *)linux_msgget, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 303 = linux_msgget */
+ { .sy_narg = AS(linux_msgctl_args), .sy_call = (sy_call_t *)linux_msgctl, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 304 = linux_msgctl */
+ { .sy_narg = AS(linux_shmat_args), .sy_call = (sy_call_t *)linux_shmat, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 305 = linux_shmat */
+ { .sy_narg = AS(linux_shmdt_args), .sy_call = (sy_call_t *)linux_shmdt, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 306 = linux_shmdt */
+ { .sy_narg = AS(linux_shmget_args), .sy_call = (sy_call_t *)linux_shmget, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 307 = linux_shmget */
+ { .sy_narg = AS(linux_shmctl_args), .sy_call = (sy_call_t *)linux_shmctl, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 308 = linux_shmctl */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_add_key, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 309 = linux_add_key */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_request_key, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 310 = linux_request_key */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_keyctl, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 311 = linux_keyctl */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 312 = semtimedop */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 313 = ; */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_ioprio_set, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 314 = linux_ioprio_set */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_ioprio_get, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 315 = linux_ioprio_get */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_inotify_init, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 316 = linux_inotify_init */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_inotify_add_watch, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 317 = linux_inotify_add_watch */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_inotify_rm_watch, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 318 = linux_inotify_rm_watch */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_mbind, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 319 = linux_mbind */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_get_mempolicy, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 320 = linux_get_mempolicy */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_set_mempolicy, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 321 = linux_set_mempolicy */
+ { .sy_narg = AS(linux_openat_args), .sy_call = (sy_call_t *)linux_openat, .sy_auevent = AUE_OPEN_RWTC, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 322 = linux_openat */
+ { .sy_narg = AS(linux_mkdirat_args), .sy_call = (sy_call_t *)linux_mkdirat, .sy_auevent = AUE_MKDIRAT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 323 = linux_mkdirat */
+ { .sy_narg = AS(linux_mknodat_args), .sy_call = (sy_call_t *)linux_mknodat, .sy_auevent = AUE_MKNODAT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 324 = linux_mknodat */
+ { .sy_narg = AS(linux_fchownat_args), .sy_call = (sy_call_t *)linux_fchownat, .sy_auevent = AUE_FCHOWNAT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 325 = linux_fchownat */
+ { .sy_narg = AS(linux_futimesat_args), .sy_call = (sy_call_t *)linux_futimesat, .sy_auevent = AUE_FUTIMESAT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 326 = linux_futimesat */
+ { .sy_narg = AS(linux_fstatat64_args), .sy_call = (sy_call_t *)linux_fstatat64, .sy_auevent = AUE_FSTATAT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 327 = linux_fstatat64 */
+ { .sy_narg = AS(linux_unlinkat_args), .sy_call = (sy_call_t *)linux_unlinkat, .sy_auevent = AUE_UNLINKAT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 328 = linux_unlinkat */
+ { .sy_narg = AS(linux_renameat_args), .sy_call = (sy_call_t *)linux_renameat, .sy_auevent = AUE_RENAMEAT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 329 = linux_renameat */
+ { .sy_narg = AS(linux_linkat_args), .sy_call = (sy_call_t *)linux_linkat, .sy_auevent = AUE_LINKAT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 330 = linux_linkat */
+ { .sy_narg = AS(linux_symlinkat_args), .sy_call = (sy_call_t *)linux_symlinkat, .sy_auevent = AUE_SYMLINKAT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 331 = linux_symlinkat */
+ { .sy_narg = AS(linux_readlinkat_args), .sy_call = (sy_call_t *)linux_readlinkat, .sy_auevent = AUE_READLINKAT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 332 = linux_readlinkat */
+ { .sy_narg = AS(linux_fchmodat_args), .sy_call = (sy_call_t *)linux_fchmodat, .sy_auevent = AUE_FCHMODAT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 333 = linux_fchmodat */
+ { .sy_narg = AS(linux_faccessat_args), .sy_call = (sy_call_t *)linux_faccessat, .sy_auevent = AUE_FACCESSAT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 334 = linux_faccessat */
+ { .sy_narg = AS(linux_pselect6_args), .sy_call = (sy_call_t *)linux_pselect6, .sy_auevent = AUE_SELECT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 335 = linux_pselect6 */
+ { .sy_narg = AS(linux_ppoll_args), .sy_call = (sy_call_t *)linux_ppoll, .sy_auevent = AUE_POLL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 336 = linux_ppoll */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_unshare, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 337 = linux_unshare */
+ { .sy_narg = AS(linux_set_robust_list_args), .sy_call = (sy_call_t *)linux_set_robust_list, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 338 = linux_set_robust_list */
+ { .sy_narg = AS(linux_get_robust_list_args), .sy_call = (sy_call_t *)linux_get_robust_list, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 339 = linux_get_robust_list */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_splice, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 340 = linux_splice */
+ { .sy_narg = AS(linux_sync_file_range_args), .sy_call = (sy_call_t *)linux_sync_file_range, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 341 = linux_sync_file_range */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_tee, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 342 = linux_tee */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_vmsplice, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 343 = linux_vmsplice */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_move_pages, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 344 = linux_move_pages */
+ { .sy_narg = AS(linux_getcpu_args), .sy_call = (sy_call_t *)linux_getcpu, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 345 = linux_getcpu */
+ { .sy_narg = AS(linux_epoll_pwait_args), .sy_call = (sy_call_t *)linux_epoll_pwait, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 346 = linux_epoll_pwait */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_kexec_load, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 347 = linux_kexec_load */
+ { .sy_narg = AS(linux_utimensat_args), .sy_call = (sy_call_t *)linux_utimensat, .sy_auevent = AUE_FUTIMESAT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 348 = linux_utimensat */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_signalfd, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 349 = linux_signalfd */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_timerfd_create, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 350 = linux_timerfd_create */
+ { .sy_narg = AS(linux_eventfd_args), .sy_call = (sy_call_t *)linux_eventfd, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 351 = linux_eventfd */
+ { .sy_narg = AS(linux_fallocate_args), .sy_call = (sy_call_t *)linux_fallocate, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 352 = linux_fallocate */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_timerfd_settime, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 353 = linux_timerfd_settime */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_timerfd_gettime, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 354 = linux_timerfd_gettime */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_signalfd4, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 355 = linux_signalfd4 */
+ { .sy_narg = AS(linux_eventfd2_args), .sy_call = (sy_call_t *)linux_eventfd2, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 356 = linux_eventfd2 */
+ { .sy_narg = AS(linux_epoll_create1_args), .sy_call = (sy_call_t *)linux_epoll_create1, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 357 = linux_epoll_create1 */
+ { .sy_narg = AS(linux_dup3_args), .sy_call = (sy_call_t *)linux_dup3, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 358 = linux_dup3 */
+ { .sy_narg = AS(linux_pipe2_args), .sy_call = (sy_call_t *)linux_pipe2, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 359 = linux_pipe2 */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_inotify_init1, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 360 = linux_inotify_init1 */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_preadv, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 361 = linux_preadv */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_pwritev, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 362 = linux_pwritev */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_rt_tsigqueueinfo, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 363 = linux_rt_tsigqueueinfo */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_perf_event_open, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 364 = linux_perf_event_open */
+ { .sy_narg = AS(linux_recvmmsg_args), .sy_call = (sy_call_t *)linux_recvmmsg, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 365 = linux_recvmmsg */
+ { .sy_narg = AS(linux_accept4_args), .sy_call = (sy_call_t *)linux_accept4, .sy_auevent = AUE_ACCEPT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 366 = linux_accept4 */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_fanotify_init, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 367 = linux_fanotify_init */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_fanotify_mark, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 368 = linux_fanotify_mark */
+ { .sy_narg = AS(linux_prlimit64_args), .sy_call = (sy_call_t *)linux_prlimit64, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 369 = linux_prlimit64 */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_name_to_handle_at, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 370 = linux_name_to_handle_at */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_open_by_handle_at, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 371 = linux_open_by_handle_at */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_clock_adjtime, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 372 = linux_clock_adjtime */
+ { .sy_narg = AS(linux_syncfs_args), .sy_call = (sy_call_t *)linux_syncfs, .sy_auevent = AUE_SYNC, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 373 = linux_syncfs */
+ { .sy_narg = AS(linux_sendmmsg_args), .sy_call = (sy_call_t *)linux_sendmmsg, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 374 = linux_sendmmsg */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_setns, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 375 = linux_setns */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_process_vm_readv, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 376 = linux_process_vm_readv */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)linux_process_vm_writev, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 377 = linux_process_vm_writev */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 378 = kcmp */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 379 = finit_module */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 380 = sys_sched_setattr */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 381 = sys_sched_getattr */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 382 = sys_renameat2 */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 383 = sys_seccomp */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 384 = sys_getrandom */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 385 = sys_memfd_create */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 386 = sys_bpf */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 387 = sys_execveat */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 388 = sys_userfaultfd */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 389 = sys_membarrier */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 390 = sys_mlock2 */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 391 = sys_copy_file_range */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 392 = sys_preadv2 */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 393 = sys_pwritev2 */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 394 = */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 395 = */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 396 = */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 397 = */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 398 = */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 399 = */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 400 = unimpl400 */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 401 = breakpoint */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 402 = cacheflush */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 403 = usr26 */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 404 = usr32 */
+ { .sy_narg = AS(linux_set_tls_args), .sy_call = (sy_call_t *)linux_set_tls, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 405 = linux_set_tls */
+ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 406 = nosys */
+};
diff --git a/sys/arm/linux/linux_systrace_args.c b/sys/arm/linux/linux_systrace_args.c
new file mode 100644
index 000000000000..2129b996b173
--- /dev/null
+++ b/sys/arm/linux/linux_systrace_args.c
@@ -0,0 +1,7741 @@
+/*
+ * System call argument to DTrace register array converstion.
+ *
+ * DO NOT EDIT-- this file is automatically @generated.
+ * $FreeBSD$
+ * This file is part of the DTrace syscall provider.
+ */
+
+static void
+systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args)
+{
+ int64_t *iarg = (int64_t *) uarg;
+ switch (sysnum) {
+#define nosys linux_nosys
+ /* linux_exit */
+ case 1: {
+ struct linux_exit_args *p = params;
+ iarg[0] = p->rval; /* int */
+ *n_args = 1;
+ break;
+ }
+ /* linux_fork */
+ case 2: {
+ *n_args = 0;
+ break;
+ }
+ /* read */
+ case 3: {
+ struct read_args *p = params;
+ iarg[0] = p->fd; /* int */
+ uarg[1] = (intptr_t) p->buf; /* char * */
+ uarg[2] = p->nbyte; /* u_int */
+ *n_args = 3;
+ break;
+ }
+ /* write */
+ case 4: {
+ struct write_args *p = params;
+ iarg[0] = p->fd; /* int */
+ uarg[1] = (intptr_t) p->buf; /* char * */
+ uarg[2] = p->nbyte; /* u_int */
+ *n_args = 3;
+ break;
+ }
+ /* linux_open */
+ case 5: {
+ struct linux_open_args *p = params;
+ uarg[0] = (intptr_t) p->path; /* char * */
+ iarg[1] = p->flags; /* l_int */
+ iarg[2] = p->mode; /* l_int */
+ *n_args = 3;
+ break;
+ }
+ /* close */
+ case 6: {
+ struct close_args *p = params;
+ iarg[0] = p->fd; /* int */
+ *n_args = 1;
+ break;
+ }
+ /* linux_creat */
+ case 8: {
+ struct linux_creat_args *p = params;
+ uarg[0] = (intptr_t) p->path; /* char * */
+ iarg[1] = p->mode; /* l_int */
+ *n_args = 2;
+ break;
+ }
+ /* linux_link */
+ case 9: {
+ struct linux_link_args *p = params;
+ uarg[0] = (intptr_t) p->path; /* char * */
+ uarg[1] = (intptr_t) p->to; /* char * */
+ *n_args = 2;
+ break;
+ }
+ /* linux_unlink */
+ case 10: {
+ struct linux_unlink_args *p = params;
+ uarg[0] = (intptr_t) p->path; /* char * */
+ *n_args = 1;
+ break;
+ }
+ /* linux_execve */
+ case 11: {
+ struct linux_execve_args *p = params;
+ uarg[0] = (intptr_t) p->path; /* char * */
+ uarg[1] = (intptr_t) p->argp; /* char ** */
+ uarg[2] = (intptr_t) p->envp; /* char ** */
+ *n_args = 3;
+ break;
+ }
+ /* linux_chdir */
+ case 12: {
+ struct linux_chdir_args *p = params;
+ uarg[0] = (intptr_t) p->path; /* char * */
+ *n_args = 1;
+ break;
+ }
+ /* linux_mknod */
+ case 14: {
+ struct linux_mknod_args *p = params;
+ uarg[0] = (intptr_t) p->path; /* char * */
+ iarg[1] = p->mode; /* l_int */
+ iarg[2] = p->dev; /* l_dev_t */
+ *n_args = 3;
+ break;
+ }
+ /* linux_chmod */
+ case 15: {
+ struct linux_chmod_args *p = params;
+ uarg[0] = (intptr_t) p->path; /* char * */
+ iarg[1] = p->mode; /* l_mode_t */
+ *n_args = 2;
+ break;
+ }
+ /* linux_lchown16 */
+ case 16: {
+ struct linux_lchown16_args *p = params;
+ uarg[0] = (intptr_t) p->path; /* char * */
+ iarg[1] = p->uid; /* l_uid16_t */
+ iarg[2] = p->gid; /* l_gid16_t */
+ *n_args = 3;
+ break;
+ }
+ /* linux_lseek */
+ case 19: {
+ struct linux_lseek_args *p = params;
+ iarg[0] = p->fdes; /* l_uint */
+ iarg[1] = p->off; /* l_off_t */
+ iarg[2] = p->whence; /* l_int */
+ *n_args = 3;
+ break;
+ }
+ /* linux_getpid */
+ case 20: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_mount */
+ case 21: {
+ struct linux_mount_args *p = params;
+ uarg[0] = (intptr_t) p->specialfile; /* char * */
+ uarg[1] = (intptr_t) p->dir; /* char * */
+ uarg[2] = (intptr_t) p->filesystemtype; /* char * */
+ iarg[3] = p->rwflag; /* l_ulong */
+ uarg[4] = (intptr_t) p->data; /* void * */
+ *n_args = 5;
+ break;
+ }
+ /* linux_setuid16 */
+ case 23: {
+ struct linux_setuid16_args *p = params;
+ iarg[0] = p->uid; /* l_uid16_t */
+ *n_args = 1;
+ break;
+ }
+ /* linux_getuid16 */
+ case 24: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_pause */
+ case 29: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_access */
+ case 33: {
+ struct linux_access_args *p = params;
+ uarg[0] = (intptr_t) p->path; /* char * */
+ iarg[1] = p->amode; /* l_int */
+ *n_args = 2;
+ break;
+ }
+ /* linux_nice */
+ case 34: {
+ struct linux_nice_args *p = params;
+ iarg[0] = p->inc; /* l_int */
+ *n_args = 1;
+ break;
+ }
+ /* sync */
+ case 36: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_kill */
+ case 37: {
+ struct linux_kill_args *p = params;
+ iarg[0] = p->pid; /* l_int */
+ iarg[1] = p->signum; /* l_int */
+ *n_args = 2;
+ break;
+ }
+ /* linux_rename */
+ case 38: {
+ struct linux_rename_args *p = params;
+ uarg[0] = (intptr_t) p->from; /* char * */
+ uarg[1] = (intptr_t) p->to; /* char * */
+ *n_args = 2;
+ break;
+ }
+ /* linux_mkdir */
+ case 39: {
+ struct linux_mkdir_args *p = params;
+ uarg[0] = (intptr_t) p->path; /* char * */
+ iarg[1] = p->mode; /* l_int */
+ *n_args = 2;
+ break;
+ }
+ /* linux_rmdir */
+ case 40: {
+ struct linux_rmdir_args *p = params;
+ uarg[0] = (intptr_t) p->path; /* char * */
+ *n_args = 1;
+ break;
+ }
+ /* dup */
+ case 41: {
+ struct dup_args *p = params;
+ uarg[0] = p->fd; /* u_int */
+ *n_args = 1;
+ break;
+ }
+ /* linux_pipe */
+ case 42: {
+ struct linux_pipe_args *p = params;
+ uarg[0] = (intptr_t) p->pipefds; /* l_int * */
+ *n_args = 1;
+ break;
+ }
+ /* linux_times */
+ case 43: {
+ struct linux_times_args *p = params;
+ uarg[0] = (intptr_t) p->buf; /* struct l_times_argv * */
+ *n_args = 1;
+ break;
+ }
+ /* linux_brk */
+ case 45: {
+ struct linux_brk_args *p = params;
+ iarg[0] = p->dsend; /* l_ulong */
+ *n_args = 1;
+ break;
+ }
+ /* linux_setgid16 */
+ case 46: {
+ struct linux_setgid16_args *p = params;
+ iarg[0] = p->gid; /* l_gid16_t */
+ *n_args = 1;
+ break;
+ }
+ /* linux_getgid16 */
+ case 47: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_geteuid16 */
+ case 49: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_getegid16 */
+ case 50: {
+ *n_args = 0;
+ break;
+ }
+ /* acct */
+ case 51: {
+ struct acct_args *p = params;
+ uarg[0] = (intptr_t) p->path; /* char * */
+ *n_args = 1;
+ break;
+ }
+ /* linux_umount */
+ case 52: {
+ struct linux_umount_args *p = params;
+ uarg[0] = (intptr_t) p->path; /* char * */
+ iarg[1] = p->flags; /* l_int */
+ *n_args = 2;
+ break;
+ }
+ /* linux_ioctl */
+ case 54: {
+ struct linux_ioctl_args *p = params;
+ iarg[0] = p->fd; /* l_uint */
+ iarg[1] = p->cmd; /* l_uint */
+ iarg[2] = p->arg; /* l_ulong */
+ *n_args = 3;
+ break;
+ }
+ /* linux_fcntl */
+ case 55: {
+ struct linux_fcntl_args *p = params;
+ iarg[0] = p->fd; /* l_uint */
+ iarg[1] = p->cmd; /* l_uint */
+ iarg[2] = p->arg; /* l_ulong */
+ *n_args = 3;
+ break;
+ }
+ /* setpgid */
+ case 57: {
+ struct setpgid_args *p = params;
+ iarg[0] = p->pid; /* int */
+ iarg[1] = p->pgid; /* int */
+ *n_args = 2;
+ break;
+ }
+ /* umask */
+ case 60: {
+ struct umask_args *p = params;
+ iarg[0] = p->newmask; /* int */
+ *n_args = 1;
+ break;
+ }
+ /* chroot */
+ case 61: {
+ struct chroot_args *p = params;
+ uarg[0] = (intptr_t) p->path; /* char * */
+ *n_args = 1;
+ break;
+ }
+ /* linux_ustat */
+ case 62: {
+ struct linux_ustat_args *p = params;
+ iarg[0] = p->dev; /* l_dev_t */
+ uarg[1] = (intptr_t) p->ubuf; /* struct l_ustat * */
+ *n_args = 2;
+ break;
+ }
+ /* dup2 */
+ case 63: {
+ struct dup2_args *p = params;
+ uarg[0] = p->from; /* u_int */
+ uarg[1] = p->to; /* u_int */
+ *n_args = 2;
+ break;
+ }
+ /* linux_getppid */
+ case 64: {
+ *n_args = 0;
+ break;
+ }
+ /* getpgrp */
+ case 65: {
+ *n_args = 0;
+ break;
+ }
+ /* setsid */
+ case 66: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_sigaction */
+ case 67: {
+ struct linux_sigaction_args *p = params;
+ iarg[0] = p->sig; /* l_int */
+ uarg[1] = (intptr_t) p->nsa; /* l_osigaction_t * */
+ uarg[2] = (intptr_t) p->osa; /* l_osigaction_t * */
+ *n_args = 3;
+ break;
+ }
+ /* linux_setreuid16 */
+ case 70: {
+ struct linux_setreuid16_args *p = params;
+ iarg[0] = p->ruid; /* l_uid16_t */
+ iarg[1] = p->euid; /* l_uid16_t */
+ *n_args = 2;
+ break;
+ }
+ /* linux_setregid16 */
+ case 71: {
+ struct linux_setregid16_args *p = params;
+ iarg[0] = p->rgid; /* l_gid16_t */
+ iarg[1] = p->egid; /* l_gid16_t */
+ *n_args = 2;
+ break;
+ }
+ /* linux_sigsuspend */
+ case 72: {
+ struct linux_sigsuspend_args *p = params;
+ iarg[0] = p->hist0; /* l_int */
+ iarg[1] = p->hist1; /* l_int */
+ iarg[2] = p->mask; /* l_osigset_t */
+ *n_args = 3;
+ break;
+ }
+ /* linux_sigpending */
+ case 73: {
+ struct linux_sigpending_args *p = params;
+ uarg[0] = (intptr_t) p->mask; /* l_osigset_t * */
+ *n_args = 1;
+ break;
+ }
+ /* linux_sethostname */
+ case 74: {
+ struct linux_sethostname_args *p = params;
+ uarg[0] = (intptr_t) p->hostname; /* char * */
+ uarg[1] = p->len; /* u_int */
+ *n_args = 2;
+ break;
+ }
+ /* linux_setrlimit */
+ case 75: {
+ struct linux_setrlimit_args *p = params;
+ iarg[0] = p->resource; /* l_uint */
+ uarg[1] = (intptr_t) p->rlim; /* struct l_rlimit * */
+ *n_args = 2;
+ break;
+ }
+ /* getrusage */
+ case 77: {
+ struct getrusage_args *p = params;
+ iarg[0] = p->who; /* int */
+ uarg[1] = (intptr_t) p->rusage; /* struct rusage * */
+ *n_args = 2;
+ break;
+ }
+ /* linux_gettimeofday */
+ case 78: {
+ struct linux_gettimeofday_args *p = params;
+ uarg[0] = (intptr_t) p->tp; /* struct timeval32 * */
+ uarg[1] = (intptr_t) p->tzp; /* struct timezone * */
+ *n_args = 2;
+ break;
+ }
+ /* linux_settimeofday */
+ case 79: {
+ struct linux_settimeofday_args *p = params;
+ uarg[0] = (intptr_t) p->tp; /* struct timeval32 * */
+ uarg[1] = (intptr_t) p->tzp; /* struct timezone * */
+ *n_args = 2;
+ break;
+ }
+ /* linux_getgroups16 */
+ case 80: {
+ struct linux_getgroups16_args *p = params;
+ iarg[0] = p->gidsetsize; /* l_uint */
+ uarg[1] = (intptr_t) p->gidset; /* l_gid16_t * */
+ *n_args = 2;
+ break;
+ }
+ /* linux_setgroups16 */
+ case 81: {
+ struct linux_setgroups16_args *p = params;
+ iarg[0] = p->gidsetsize; /* l_uint */
+ uarg[1] = (intptr_t) p->gidset; /* l_gid16_t * */
+ *n_args = 2;
+ break;
+ }
+ /* linux_symlink */
+ case 83: {
+ struct linux_symlink_args *p = params;
+ uarg[0] = (intptr_t) p->path; /* char * */
+ uarg[1] = (intptr_t) p->to; /* char * */
+ *n_args = 2;
+ break;
+ }
+ /* linux_readlink */
+ case 85: {
+ struct linux_readlink_args *p = params;
+ uarg[0] = (intptr_t) p->name; /* char * */
+ uarg[1] = (intptr_t) p->buf; /* char * */
+ iarg[2] = p->count; /* l_int */
+ *n_args = 3;
+ break;
+ }
+ /* swapon */
+ case 87: {
+ struct swapon_args *p = params;
+ uarg[0] = (intptr_t) p->name; /* char * */
+ *n_args = 1;
+ break;
+ }
+ /* linux_reboot */
+ case 88: {
+ struct linux_reboot_args *p = params;
+ iarg[0] = p->magic1; /* l_int */
+ iarg[1] = p->magic2; /* l_int */
+ iarg[2] = p->cmd; /* l_uint */
+ uarg[3] = (intptr_t) p->arg; /* void * */
+ *n_args = 4;
+ break;
+ }
+ /* munmap */
+ case 91: {
+ struct munmap_args *p = params;
+ uarg[0] = (intptr_t) p->addr; /* caddr_t */
+ iarg[1] = p->len; /* int */
+ *n_args = 2;
+ break;
+ }
+ /* linux_truncate */
+ case 92: {
+ struct linux_truncate_args *p = params;
+ uarg[0] = (intptr_t) p->path; /* char * */
+ iarg[1] = p->length; /* l_ulong */
+ *n_args = 2;
+ break;
+ }
+ /* linux_ftruncate */
+ case 93: {
+ struct linux_ftruncate_args *p = params;
+ iarg[0] = p->fd; /* int */
+ iarg[1] = p->length; /* long */
+ *n_args = 2;
+ break;
+ }
+ /* fchmod */
+ case 94: {
+ struct fchmod_args *p = params;
+ iarg[0] = p->fd; /* int */
+ iarg[1] = p->mode; /* int */
+ *n_args = 2;
+ break;
+ }
+ /* fchown */
+ case 95: {
+ struct fchown_args *p = params;
+ iarg[0] = p->fd; /* int */
+ iarg[1] = p->uid; /* int */
+ iarg[2] = p->gid; /* int */
+ *n_args = 3;
+ break;
+ }
+ /* linux_getpriority */
+ case 96: {
+ struct linux_getpriority_args *p = params;
+ iarg[0] = p->which; /* int */
+ iarg[1] = p->who; /* int */
+ *n_args = 2;
+ break;
+ }
+ /* setpriority */
+ case 97: {
+ struct setpriority_args *p = params;
+ iarg[0] = p->which; /* int */
+ iarg[1] = p->who; /* int */
+ iarg[2] = p->prio; /* int */
+ *n_args = 3;
+ break;
+ }
+ /* linux_statfs */
+ case 99: {
+ struct linux_statfs_args *p = params;
+ uarg[0] = (intptr_t) p->path; /* char * */
+ uarg[1] = (intptr_t) p->buf; /* struct l_statfs_buf * */
+ *n_args = 2;
+ break;
+ }
+ /* linux_fstatfs */
+ case 100: {
+ struct linux_fstatfs_args *p = params;
+ iarg[0] = p->fd; /* l_uint */
+ uarg[1] = (intptr_t) p->buf; /* struct l_statfs_buf * */
+ *n_args = 2;
+ break;
+ }
+ /* linux_syslog */
+ case 103: {
+ struct linux_syslog_args *p = params;
+ iarg[0] = p->type; /* l_int */
+ uarg[1] = (intptr_t) p->buf; /* char * */
+ iarg[2] = p->len; /* l_int */
+ *n_args = 3;
+ break;
+ }
+ /* linux_setitimer */
+ case 104: {
+ struct linux_setitimer_args *p = params;
+ iarg[0] = p->which; /* l_int */
+ uarg[1] = (intptr_t) p->itv; /* struct l_itimerval * */
+ uarg[2] = (intptr_t) p->oitv; /* struct l_itimerval * */
+ *n_args = 3;
+ break;
+ }
+ /* linux_getitimer */
+ case 105: {
+ struct linux_getitimer_args *p = params;
+ iarg[0] = p->which; /* l_int */
+ uarg[1] = (intptr_t) p->itv; /* struct l_itimerval * */
+ *n_args = 2;
+ break;
+ }
+ /* linux_newstat */
+ case 106: {
+ struct linux_newstat_args *p = params;
+ uarg[0] = (intptr_t) p->path; /* char * */
+ uarg[1] = (intptr_t) p->buf; /* struct l_newstat * */
+ *n_args = 2;
+ break;
+ }
+ /* linux_newlstat */
+ case 107: {
+ struct linux_newlstat_args *p = params;
+ uarg[0] = (intptr_t) p->path; /* char * */
+ uarg[1] = (intptr_t) p->buf; /* struct l_newstat * */
+ *n_args = 2;
+ break;
+ }
+ /* linux_newfstat */
+ case 108: {
+ struct linux_newfstat_args *p = params;
+ iarg[0] = p->fd; /* l_uint */
+ uarg[1] = (intptr_t) p->buf; /* struct l_newstat * */
+ *n_args = 2;
+ break;
+ }
+ /* linux_vhangup */
+ case 111: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_wait4 */
+ case 114: {
+ struct linux_wait4_args *p = params;
+ iarg[0] = p->pid; /* l_pid_t */
+ uarg[1] = (intptr_t) p->status; /* l_int * */
+ iarg[2] = p->options; /* l_int */
+ uarg[3] = (intptr_t) p->rusage; /* void * */
+ *n_args = 4;
+ break;
+ }
+ /* linux_swapoff */
+ case 115: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_sysinfo */
+ case 116: {
+ struct linux_sysinfo_args *p = params;
+ uarg[0] = (intptr_t) p->info; /* struct l_sysinfo * */
+ *n_args = 1;
+ break;
+ }
+ /* fsync */
+ case 118: {
+ struct fsync_args *p = params;
+ iarg[0] = p->fd; /* int */
+ *n_args = 1;
+ break;
+ }
+ /* linux_sigreturn */
+ case 119: {
+ struct linux_sigreturn_args *p = params;
+ uarg[0] = (intptr_t) p->sfp; /* struct l_sigframe * */
+ *n_args = 1;
+ break;
+ }
+ /* linux_clone */
+ case 120: {
+ struct linux_clone_args *p = params;
+ iarg[0] = p->flags; /* l_int */
+ uarg[1] = (intptr_t) p->stack; /* void * */
+ uarg[2] = (intptr_t) p->parent_tidptr; /* void * */
+ uarg[3] = (intptr_t) p->tls; /* void * */
+ uarg[4] = (intptr_t) p->child_tidptr; /* void * */
+ *n_args = 5;
+ break;
+ }
+ /* linux_setdomainname */
+ case 121: {
+ struct linux_setdomainname_args *p = params;
+ uarg[0] = (intptr_t) p->name; /* char * */
+ iarg[1] = p->len; /* int */
+ *n_args = 2;
+ break;
+ }
+ /* linux_newuname */
+ case 122: {
+ struct linux_newuname_args *p = params;
+ uarg[0] = (intptr_t) p->buf; /* struct l_new_utsname * */
+ *n_args = 1;
+ break;
+ }
+ /* linux_adjtimex */
+ case 124: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_mprotect */
+ case 125: {
+ struct linux_mprotect_args *p = params;
+ uarg[0] = (intptr_t) p->addr; /* caddr_t */
+ iarg[1] = p->len; /* int */
+ iarg[2] = p->prot; /* int */
+ *n_args = 3;
+ break;
+ }
+ /* linux_sigprocmask */
+ case 126: {
+ struct linux_sigprocmask_args *p = params;
+ iarg[0] = p->how; /* l_int */
+ uarg[1] = (intptr_t) p->mask; /* l_osigset_t * */
+ uarg[2] = (intptr_t) p->omask; /* l_osigset_t * */
+ *n_args = 3;
+ break;
+ }
+ /* linux_init_module */
+ case 128: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_delete_module */
+ case 129: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_quotactl */
+ case 131: {
+ *n_args = 0;
+ break;
+ }
+ /* getpgid */
+ case 132: {
+ struct getpgid_args *p = params;
+ iarg[0] = p->pid; /* int */
+ *n_args = 1;
+ break;
+ }
+ /* fchdir */
+ case 133: {
+ struct fchdir_args *p = params;
+ iarg[0] = p->fd; /* int */
+ *n_args = 1;
+ break;
+ }
+ /* linux_bdflush */
+ case 134: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_sysfs */
+ case 135: {
+ struct linux_sysfs_args *p = params;
+ iarg[0] = p->option; /* l_int */
+ iarg[1] = p->arg1; /* l_ulong */
+ iarg[2] = p->arg2; /* l_ulong */
+ *n_args = 3;
+ break;
+ }
+ /* linux_personality */
+ case 136: {
+ struct linux_personality_args *p = params;
+ iarg[0] = p->per; /* l_ulong */
+ *n_args = 1;
+ break;
+ }
+ /* linux_setfsuid16 */
+ case 138: {
+ struct linux_setfsuid16_args *p = params;
+ iarg[0] = p->uid; /* l_uid16_t */
+ *n_args = 1;
+ break;
+ }
+ /* linux_setfsgid16 */
+ case 139: {
+ struct linux_setfsgid16_args *p = params;
+ iarg[0] = p->gid; /* l_gid16_t */
+ *n_args = 1;
+ break;
+ }
+ /* linux_llseek */
+ case 140: {
+ struct linux_llseek_args *p = params;
+ iarg[0] = p->fd; /* l_int */
+ iarg[1] = p->ohigh; /* l_ulong */
+ iarg[2] = p->olow; /* l_ulong */
+ uarg[3] = (intptr_t) p->res; /* l_loff_t * */
+ iarg[4] = p->whence; /* l_uint */
+ *n_args = 5;
+ break;
+ }
+ /* linux_getdents */
+ case 141: {
+ struct linux_getdents_args *p = params;
+ iarg[0] = p->fd; /* l_uint */
+ uarg[1] = (intptr_t) p->dent; /* void * */
+ iarg[2] = p->count; /* l_uint */
+ *n_args = 3;
+ break;
+ }
+ /* linux_select */
+ case 142: {
+ struct linux_select_args *p = params;
+ iarg[0] = p->nfds; /* l_int */
+ uarg[1] = (intptr_t) p->readfds; /* l_fd_set * */
+ uarg[2] = (intptr_t) p->writefds; /* l_fd_set * */
+ uarg[3] = (intptr_t) p->exceptfds; /* l_fd_set * */
+ uarg[4] = (intptr_t) p->timeout; /* struct l_timeval * */
+ *n_args = 5;
+ break;
+ }
+ /* flock */
+ case 143: {
+ struct flock_args *p = params;
+ iarg[0] = p->fd; /* int */
+ iarg[1] = p->how; /* int */
+ *n_args = 2;
+ break;
+ }
+ /* linux_msync */
+ case 144: {
+ struct linux_msync_args *p = params;
+ iarg[0] = p->addr; /* l_ulong */
+ iarg[1] = p->len; /* l_size_t */
+ iarg[2] = p->fl; /* l_int */
+ *n_args = 3;
+ break;
+ }
+ /* readv */
+ case 145: {
+ struct readv_args *p = params;
+ iarg[0] = p->fd; /* int */
+ uarg[1] = (intptr_t) p->iovp; /* struct iovec * */
+ uarg[2] = p->iovcnt; /* u_int */
+ *n_args = 3;
+ break;
+ }
+ /* writev */
+ case 146: {
+ struct writev_args *p = params;
+ iarg[0] = p->fd; /* int */
+ uarg[1] = (intptr_t) p->iovp; /* struct iovec * */
+ uarg[2] = p->iovcnt; /* u_int */
+ *n_args = 3;
+ break;
+ }
+ /* linux_getsid */
+ case 147: {
+ struct linux_getsid_args *p = params;
+ iarg[0] = p->pid; /* l_pid_t */
+ *n_args = 1;
+ break;
+ }
+ /* linux_fdatasync */
+ case 148: {
+ struct linux_fdatasync_args *p = params;
+ iarg[0] = p->fd; /* l_uint */
+ *n_args = 1;
+ break;
+ }
+ /* linux_sysctl */
+ case 149: {
+ struct linux_sysctl_args *p = params;
+ uarg[0] = (intptr_t) p->args; /* struct l___sysctl_args * */
+ *n_args = 1;
+ break;
+ }
+ /* mlock */
+ case 150: {
+ struct mlock_args *p = params;
+ uarg[0] = (intptr_t) p->addr; /* const void * */
+ uarg[1] = p->len; /* size_t */
+ *n_args = 2;
+ break;
+ }
+ /* munlock */
+ case 151: {
+ struct munlock_args *p = params;
+ uarg[0] = (intptr_t) p->addr; /* const void * */
+ uarg[1] = p->len; /* size_t */
+ *n_args = 2;
+ break;
+ }
+ /* mlockall */
+ case 152: {
+ struct mlockall_args *p = params;
+ iarg[0] = p->how; /* int */
+ *n_args = 1;
+ break;
+ }
+ /* munlockall */
+ case 153: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_sched_setparam */
+ case 154: {
+ struct linux_sched_setparam_args *p = params;
+ iarg[0] = p->pid; /* l_pid_t */
+ uarg[1] = (intptr_t) p->param; /* struct sched_param * */
+ *n_args = 2;
+ break;
+ }
+ /* linux_sched_getparam */
+ case 155: {
+ struct linux_sched_getparam_args *p = params;
+ iarg[0] = p->pid; /* l_pid_t */
+ uarg[1] = (intptr_t) p->param; /* struct sched_param * */
+ *n_args = 2;
+ break;
+ }
+ /* linux_sched_setscheduler */
+ case 156: {
+ struct linux_sched_setscheduler_args *p = params;
+ iarg[0] = p->pid; /* l_pid_t */
+ iarg[1] = p->policy; /* l_int */
+ uarg[2] = (intptr_t) p->param; /* struct sched_param * */
+ *n_args = 3;
+ break;
+ }
+ /* linux_sched_getscheduler */
+ case 157: {
+ struct linux_sched_getscheduler_args *p = params;
+ iarg[0] = p->pid; /* l_pid_t */
+ *n_args = 1;
+ break;
+ }
+ /* sched_yield */
+ case 158: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_sched_get_priority_max */
+ case 159: {
+ struct linux_sched_get_priority_max_args *p = params;
+ iarg[0] = p->policy; /* l_int */
+ *n_args = 1;
+ break;
+ }
+ /* linux_sched_get_priority_min */
+ case 160: {
+ struct linux_sched_get_priority_min_args *p = params;
+ iarg[0] = p->policy; /* l_int */
+ *n_args = 1;
+ break;
+ }
+ /* linux_sched_rr_get_interval */
+ case 161: {
+ struct linux_sched_rr_get_interval_args *p = params;
+ iarg[0] = p->pid; /* l_pid_t */
+ uarg[1] = (intptr_t) p->interval; /* struct l_timespec * */
+ *n_args = 2;
+ break;
+ }
+ /* linux_nanosleep */
+ case 162: {
+ struct linux_nanosleep_args *p = params;
+ uarg[0] = (intptr_t) p->rqtp; /* const struct l_timespec * */
+ uarg[1] = (intptr_t) p->rmtp; /* struct l_timespec * */
+ *n_args = 2;
+ break;
+ }
+ /* linux_mremap */
+ case 163: {
+ struct linux_mremap_args *p = params;
+ iarg[0] = p->addr; /* l_ulong */
+ iarg[1] = p->old_len; /* l_ulong */
+ iarg[2] = p->new_len; /* l_ulong */
+ iarg[3] = p->flags; /* l_ulong */
+ iarg[4] = p->new_addr; /* l_ulong */
+ *n_args = 5;
+ break;
+ }
+ /* linux_setresuid16 */
+ case 164: {
+ struct linux_setresuid16_args *p = params;
+ iarg[0] = p->ruid; /* l_uid16_t */
+ iarg[1] = p->euid; /* l_uid16_t */
+ iarg[2] = p->suid; /* l_uid16_t */
+ *n_args = 3;
+ break;
+ }
+ /* linux_getresuid16 */
+ case 165: {
+ struct linux_getresuid16_args *p = params;
+ uarg[0] = (intptr_t) p->ruid; /* l_uid16_t * */
+ uarg[1] = (intptr_t) p->euid; /* l_uid16_t * */
+ uarg[2] = (intptr_t) p->suid; /* l_uid16_t * */
+ *n_args = 3;
+ break;
+ }
+ /* poll */
+ case 168: {
+ struct poll_args *p = params;
+ uarg[0] = (intptr_t) p->fds; /* struct pollfd* */
+ uarg[1] = p->nfds; /* unsigned int */
+ iarg[2] = p->timeout; /* long */
+ *n_args = 3;
+ break;
+ }
+ /* linux_setresgid16 */
+ case 170: {
+ struct linux_setresgid16_args *p = params;
+ iarg[0] = p->rgid; /* l_gid16_t */
+ iarg[1] = p->egid; /* l_gid16_t */
+ iarg[2] = p->sgid; /* l_gid16_t */
+ *n_args = 3;
+ break;
+ }
+ /* linux_getresgid16 */
+ case 171: {
+ struct linux_getresgid16_args *p = params;
+ uarg[0] = (intptr_t) p->rgid; /* l_gid16_t * */
+ uarg[1] = (intptr_t) p->egid; /* l_gid16_t * */
+ uarg[2] = (intptr_t) p->sgid; /* l_gid16_t * */
+ *n_args = 3;
+ break;
+ }
+ /* linux_prctl */
+ case 172: {
+ struct linux_prctl_args *p = params;
+ iarg[0] = p->option; /* l_int */
+ iarg[1] = p->arg2; /* l_int */
+ iarg[2] = p->arg3; /* l_int */
+ iarg[3] = p->arg4; /* l_int */
+ iarg[4] = p->arg5; /* l_int */
+ *n_args = 5;
+ break;
+ }
+ /* linux_rt_sigreturn */
+ case 173: {
+ struct linux_rt_sigreturn_args *p = params;
+ uarg[0] = (intptr_t) p->ucp; /* struct l_ucontext * */
+ *n_args = 1;
+ break;
+ }
+ /* linux_rt_sigaction */
+ case 174: {
+ struct linux_rt_sigaction_args *p = params;
+ iarg[0] = p->sig; /* l_int */
+ uarg[1] = (intptr_t) p->act; /* l_sigaction_t * */
+ uarg[2] = (intptr_t) p->oact; /* l_sigaction_t * */
+ iarg[3] = p->sigsetsize; /* l_size_t */
+ *n_args = 4;
+ break;
+ }
+ /* linux_rt_sigprocmask */
+ case 175: {
+ struct linux_rt_sigprocmask_args *p = params;
+ iarg[0] = p->how; /* l_int */
+ uarg[1] = (intptr_t) p->mask; /* l_sigset_t * */
+ uarg[2] = (intptr_t) p->omask; /* l_sigset_t * */
+ iarg[3] = p->sigsetsize; /* l_size_t */
+ *n_args = 4;
+ break;
+ }
+ /* linux_rt_sigpending */
+ case 176: {
+ struct linux_rt_sigpending_args *p = params;
+ uarg[0] = (intptr_t) p->set; /* l_sigset_t * */
+ iarg[1] = p->sigsetsize; /* l_size_t */
+ *n_args = 2;
+ break;
+ }
+ /* linux_rt_sigtimedwait */
+ case 177: {
+ struct linux_rt_sigtimedwait_args *p = params;
+ uarg[0] = (intptr_t) p->mask; /* l_sigset_t * */
+ uarg[1] = (intptr_t) p->ptr; /* l_siginfo_t * */
+ uarg[2] = (intptr_t) p->timeout; /* struct l_timeval * */
+ iarg[3] = p->sigsetsize; /* l_size_t */
+ *n_args = 4;
+ break;
+ }
+ /* linux_rt_sigqueueinfo */
+ case 178: {
+ struct linux_rt_sigqueueinfo_args *p = params;
+ iarg[0] = p->pid; /* l_pid_t */
+ iarg[1] = p->sig; /* l_int */
+ uarg[2] = (intptr_t) p->info; /* l_siginfo_t * */
+ *n_args = 3;
+ break;
+ }
+ /* linux_rt_sigsuspend */
+ case 179: {
+ struct linux_rt_sigsuspend_args *p = params;
+ uarg[0] = (intptr_t) p->newset; /* l_sigset_t * */
+ iarg[1] = p->sigsetsize; /* l_size_t */
+ *n_args = 2;
+ break;
+ }
+ /* linux_pread */
+ case 180: {
+ struct linux_pread_args *p = params;
+ iarg[0] = p->fd; /* l_uint */
+ uarg[1] = (intptr_t) p->buf; /* char * */
+ iarg[2] = p->nbyte; /* l_size_t */
+ iarg[3] = p->offset; /* l_loff_t */
+ *n_args = 4;
+ break;
+ }
+ /* linux_pwrite */
+ case 181: {
+ struct linux_pwrite_args *p = params;
+ iarg[0] = p->fd; /* l_uint */
+ uarg[1] = (intptr_t) p->buf; /* char * */
+ iarg[2] = p->nbyte; /* l_size_t */
+ iarg[3] = p->offset; /* l_loff_t */
+ *n_args = 4;
+ break;
+ }
+ /* linux_chown16 */
+ case 182: {
+ struct linux_chown16_args *p = params;
+ uarg[0] = (intptr_t) p->path; /* char * */
+ iarg[1] = p->uid; /* l_uid16_t */
+ iarg[2] = p->gid; /* l_gid16_t */
+ *n_args = 3;
+ break;
+ }
+ /* linux_getcwd */
+ case 183: {
+ struct linux_getcwd_args *p = params;
+ uarg[0] = (intptr_t) p->buf; /* char * */
+ iarg[1] = p->bufsize; /* l_ulong */
+ *n_args = 2;
+ break;
+ }
+ /* linux_capget */
+ case 184: {
+ struct linux_capget_args *p = params;
+ uarg[0] = (intptr_t) p->hdrp; /* struct l_user_cap_header * */
+ uarg[1] = (intptr_t) p->datap; /* struct l_user_cap_data * */
+ *n_args = 2;
+ break;
+ }
+ /* linux_capset */
+ case 185: {
+ struct linux_capset_args *p = params;
+ uarg[0] = (intptr_t) p->hdrp; /* struct l_user_cap_header * */
+ uarg[1] = (intptr_t) p->datap; /* struct l_user_cap_data * */
+ *n_args = 2;
+ break;
+ }
+ /* linux_sigaltstack */
+ case 186: {
+ struct linux_sigaltstack_args *p = params;
+ uarg[0] = (intptr_t) p->uss; /* l_stack_t * */
+ uarg[1] = (intptr_t) p->uoss; /* l_stack_t * */
+ *n_args = 2;
+ break;
+ }
+ /* linux_sendfile */
+ case 187: {
+ struct linux_sendfile_args *p = params;
+ iarg[0] = p->out; /* l_int */
+ iarg[1] = p->in; /* l_int */
+ uarg[2] = (intptr_t) p->offset; /* l_long * */
+ iarg[3] = p->count; /* l_size_t */
+ *n_args = 4;
+ break;
+ }
+ /* linux_vfork */
+ case 190: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_getrlimit */
+ case 191: {
+ struct linux_getrlimit_args *p = params;
+ iarg[0] = p->resource; /* l_uint */
+ uarg[1] = (intptr_t) p->rlim; /* struct l_rlimit * */
+ *n_args = 2;
+ break;
+ }
+ /* linux_mmap2 */
+ case 192: {
+ struct linux_mmap2_args *p = params;
+ iarg[0] = p->addr; /* l_ulong */
+ iarg[1] = p->len; /* l_ulong */
+ iarg[2] = p->prot; /* l_ulong */
+ iarg[3] = p->flags; /* l_ulong */
+ iarg[4] = p->fd; /* l_ulong */
+ iarg[5] = p->pgoff; /* l_ulong */
+ *n_args = 6;
+ break;
+ }
+ /* linux_truncate64 */
+ case 193: {
+ struct linux_truncate64_args *p = params;
+ uarg[0] = (intptr_t) p->path; /* char * */
+ iarg[1] = p->length; /* l_loff_t */
+ *n_args = 2;
+ break;
+ }
+ /* linux_ftruncate64 */
+ case 194: {
+ struct linux_ftruncate64_args *p = params;
+ iarg[0] = p->fd; /* l_uint */
+ iarg[1] = p->length; /* l_loff_t */
+ *n_args = 2;
+ break;
+ }
+ /* linux_stat64 */
+ case 195: {
+ struct linux_stat64_args *p = params;
+ uarg[0] = (intptr_t) p->filename; /* const char * */
+ uarg[1] = (intptr_t) p->statbuf; /* struct l_stat64 * */
+ *n_args = 2;
+ break;
+ }
+ /* linux_lstat64 */
+ case 196: {
+ struct linux_lstat64_args *p = params;
+ uarg[0] = (intptr_t) p->filename; /* const char * */
+ uarg[1] = (intptr_t) p->statbuf; /* struct l_stat64 * */
+ *n_args = 2;
+ break;
+ }
+ /* linux_fstat64 */
+ case 197: {
+ struct linux_fstat64_args *p = params;
+ iarg[0] = p->fd; /* l_int */
+ uarg[1] = (intptr_t) p->statbuf; /* struct l_stat64 * */
+ *n_args = 2;
+ break;
+ }
+ /* linux_lchown */
+ case 198: {
+ struct linux_lchown_args *p = params;
+ uarg[0] = (intptr_t) p->path; /* char * */
+ iarg[1] = p->uid; /* l_uid_t */
+ iarg[2] = p->gid; /* l_gid_t */
+ *n_args = 3;
+ break;
+ }
+ /* linux_getuid */
+ case 199: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_getgid */
+ case 200: {
+ *n_args = 0;
+ break;
+ }
+ /* geteuid */
+ case 201: {
+ *n_args = 0;
+ break;
+ }
+ /* getegid */
+ case 202: {
+ *n_args = 0;
+ break;
+ }
+ /* setreuid */
+ case 203: {
+ struct setreuid_args *p = params;
+ uarg[0] = p->ruid; /* uid_t */
+ uarg[1] = p->euid; /* uid_t */
+ *n_args = 2;
+ break;
+ }
+ /* setregid */
+ case 204: {
+ struct setregid_args *p = params;
+ iarg[0] = p->rgid; /* gid_t */
+ iarg[1] = p->egid; /* gid_t */
+ *n_args = 2;
+ break;
+ }
+ /* linux_getgroups */
+ case 205: {
+ struct linux_getgroups_args *p = params;
+ iarg[0] = p->gidsetsize; /* l_int */
+ uarg[1] = (intptr_t) p->grouplist; /* l_gid_t * */
+ *n_args = 2;
+ break;
+ }
+ /* linux_setgroups */
+ case 206: {
+ struct linux_setgroups_args *p = params;
+ iarg[0] = p->gidsetsize; /* l_int */
+ uarg[1] = (intptr_t) p->grouplist; /* l_gid_t * */
+ *n_args = 2;
+ break;
+ }
+ /* fchown */
+ case 207: {
+ *n_args = 0;
+ break;
+ }
+ /* setresuid */
+ case 208: {
+ struct setresuid_args *p = params;
+ uarg[0] = p->ruid; /* uid_t */
+ uarg[1] = p->euid; /* uid_t */
+ uarg[2] = p->suid; /* uid_t */
+ *n_args = 3;
+ break;
+ }
+ /* getresuid */
+ case 209: {
+ struct getresuid_args *p = params;
+ uarg[0] = (intptr_t) p->ruid; /* uid_t * */
+ uarg[1] = (intptr_t) p->euid; /* uid_t * */
+ uarg[2] = (intptr_t) p->suid; /* uid_t * */
+ *n_args = 3;
+ break;
+ }
+ /* setresgid */
+ case 210: {
+ struct setresgid_args *p = params;
+ iarg[0] = p->rgid; /* gid_t */
+ iarg[1] = p->egid; /* gid_t */
+ iarg[2] = p->sgid; /* gid_t */
+ *n_args = 3;
+ break;
+ }
+ /* getresgid */
+ case 211: {
+ struct getresgid_args *p = params;
+ uarg[0] = (intptr_t) p->rgid; /* gid_t * */
+ uarg[1] = (intptr_t) p->egid; /* gid_t * */
+ uarg[2] = (intptr_t) p->sgid; /* gid_t * */
+ *n_args = 3;
+ break;
+ }
+ /* linux_chown */
+ case 212: {
+ struct linux_chown_args *p = params;
+ uarg[0] = (intptr_t) p->path; /* char * */
+ iarg[1] = p->uid; /* l_uid_t */
+ iarg[2] = p->gid; /* l_gid_t */
+ *n_args = 3;
+ break;
+ }
+ /* setuid */
+ case 213: {
+ struct setuid_args *p = params;
+ uarg[0] = p->uid; /* uid_t */
+ *n_args = 1;
+ break;
+ }
+ /* setgid */
+ case 214: {
+ struct setgid_args *p = params;
+ iarg[0] = p->gid; /* gid_t */
+ *n_args = 1;
+ break;
+ }
+ /* linux_setfsuid */
+ case 215: {
+ struct linux_setfsuid_args *p = params;
+ iarg[0] = p->uid; /* l_uid_t */
+ *n_args = 1;
+ break;
+ }
+ /* linux_setfsgid */
+ case 216: {
+ struct linux_setfsgid_args *p = params;
+ iarg[0] = p->gid; /* l_gid_t */
+ *n_args = 1;
+ break;
+ }
+ /* linux_getdents64 */
+ case 217: {
+ struct linux_getdents64_args *p = params;
+ iarg[0] = p->fd; /* l_uint */
+ uarg[1] = (intptr_t) p->dirent; /* void * */
+ iarg[2] = p->count; /* l_uint */
+ *n_args = 3;
+ break;
+ }
+ /* linux_pivot_root */
+ case 218: {
+ struct linux_pivot_root_args *p = params;
+ uarg[0] = (intptr_t) p->new_root; /* char * */
+ uarg[1] = (intptr_t) p->put_old; /* char * */
+ *n_args = 2;
+ break;
+ }
+ /* linux_mincore */
+ case 219: {
+ struct linux_mincore_args *p = params;
+ iarg[0] = p->start; /* l_ulong */
+ iarg[1] = p->len; /* l_size_t */
+ uarg[2] = (intptr_t) p->vec; /* u_char * */
+ *n_args = 3;
+ break;
+ }
+ /* madvise */
+ case 220: {
+ struct madvise_args *p = params;
+ uarg[0] = (intptr_t) p->addr; /* void * */
+ uarg[1] = p->len; /* size_t */
+ iarg[2] = p->behav; /* int */
+ *n_args = 3;
+ break;
+ }
+ /* linux_fcntl64 */
+ case 221: {
+ struct linux_fcntl64_args *p = params;
+ iarg[0] = p->fd; /* l_uint */
+ iarg[1] = p->cmd; /* l_uint */
+ iarg[2] = p->arg; /* l_ulong */
+ *n_args = 3;
+ break;
+ }
+ /* linux_gettid */
+ case 224: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_setxattr */
+ case 226: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_lsetxattr */
+ case 227: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_fsetxattr */
+ case 228: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_getxattr */
+ case 229: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_lgetxattr */
+ case 230: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_fgetxattr */
+ case 231: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_listxattr */
+ case 232: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_llistxattr */
+ case 233: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_flistxattr */
+ case 234: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_removexattr */
+ case 235: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_lremovexattr */
+ case 236: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_fremovexattr */
+ case 237: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_tkill */
+ case 238: {
+ struct linux_tkill_args *p = params;
+ iarg[0] = p->tid; /* int */
+ iarg[1] = p->sig; /* int */
+ *n_args = 2;
+ break;
+ }
+ /* linux_sendfile64 */
+ case 239: {
+ struct linux_sendfile64_args *p = params;
+ iarg[0] = p->out; /* l_int */
+ iarg[1] = p->in; /* l_int */
+ uarg[2] = (intptr_t) p->offset; /* l_loff_t * */
+ iarg[3] = p->count; /* l_size_t */
+ *n_args = 4;
+ break;
+ }
+ /* linux_sys_futex */
+ case 240: {
+ struct linux_sys_futex_args *p = params;
+ uarg[0] = (intptr_t) p->uaddr; /* void * */
+ iarg[1] = p->op; /* int */
+ uarg[2] = p->val; /* uint32_t */
+ uarg[3] = (intptr_t) p->timeout; /* struct l_timespec * */
+ uarg[4] = (intptr_t) p->uaddr2; /* uint32_t * */
+ uarg[5] = p->val3; /* uint32_t */
+ *n_args = 6;
+ break;
+ }
+ /* linux_sched_setaffinity */
+ case 241: {
+ struct linux_sched_setaffinity_args *p = params;
+ iarg[0] = p->pid; /* l_pid_t */
+ iarg[1] = p->len; /* l_uint */
+ uarg[2] = (intptr_t) p->user_mask_ptr; /* l_ulong * */
+ *n_args = 3;
+ break;
+ }
+ /* linux_sched_getaffinity */
+ case 242: {
+ struct linux_sched_getaffinity_args *p = params;
+ iarg[0] = p->pid; /* l_pid_t */
+ iarg[1] = p->len; /* l_uint */
+ uarg[2] = (intptr_t) p->user_mask_ptr; /* l_ulong * */
+ *n_args = 3;
+ break;
+ }
+ /* linux_exit_group */
+ case 248: {
+ struct linux_exit_group_args *p = params;
+ iarg[0] = p->error_code; /* int */
+ *n_args = 1;
+ break;
+ }
+ /* linux_lookup_dcookie */
+ case 249: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_epoll_create */
+ case 250: {
+ struct linux_epoll_create_args *p = params;
+ iarg[0] = p->size; /* l_int */
+ *n_args = 1;
+ break;
+ }
+ /* linux_epoll_ctl */
+ case 251: {
+ struct linux_epoll_ctl_args *p = params;
+ iarg[0] = p->epfd; /* l_int */
+ iarg[1] = p->op; /* l_int */
+ iarg[2] = p->fd; /* l_int */
+ uarg[3] = (intptr_t) p->event; /* struct epoll_event * */
+ *n_args = 4;
+ break;
+ }
+ /* linux_epoll_wait */
+ case 252: {
+ struct linux_epoll_wait_args *p = params;
+ iarg[0] = p->epfd; /* l_int */
+ uarg[1] = (intptr_t) p->events; /* struct epoll_event * */
+ iarg[2] = p->maxevents; /* l_int */
+ iarg[3] = p->timeout; /* l_int */
+ *n_args = 4;
+ break;
+ }
+ /* linux_remap_file_pages */
+ case 253: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_set_tid_address */
+ case 256: {
+ struct linux_set_tid_address_args *p = params;
+ uarg[0] = (intptr_t) p->tidptr; /* int * */
+ *n_args = 1;
+ break;
+ }
+ /* linux_timer_create */
+ case 257: {
+ struct linux_timer_create_args *p = params;
+ iarg[0] = p->clock_id; /* clockid_t */
+ uarg[1] = (intptr_t) p->evp; /* struct sigevent * */
+ uarg[2] = (intptr_t) p->timerid; /* l_timer_t * */
+ *n_args = 3;
+ break;
+ }
+ /* linux_timer_settime */
+ case 258: {
+ struct linux_timer_settime_args *p = params;
+ iarg[0] = p->timerid; /* l_timer_t */
+ iarg[1] = p->flags; /* l_int */
+ uarg[2] = (intptr_t) p->new; /* const struct itimerspec * */
+ uarg[3] = (intptr_t) p->old; /* struct itimerspec * */
+ *n_args = 4;
+ break;
+ }
+ /* linux_timer_gettime */
+ case 259: {
+ struct linux_timer_gettime_args *p = params;
+ iarg[0] = p->timerid; /* l_timer_t */
+ uarg[1] = (intptr_t) p->setting; /* struct itimerspec * */
+ *n_args = 2;
+ break;
+ }
+ /* linux_timer_getoverrun */
+ case 260: {
+ struct linux_timer_getoverrun_args *p = params;
+ iarg[0] = p->timerid; /* l_timer_t */
+ *n_args = 1;
+ break;
+ }
+ /* linux_timer_delete */
+ case 261: {
+ struct linux_timer_delete_args *p = params;
+ iarg[0] = p->timerid; /* l_timer_t */
+ *n_args = 1;
+ break;
+ }
+ /* linux_clock_settime */
+ case 262: {
+ struct linux_clock_settime_args *p = params;
+ iarg[0] = p->which; /* clockid_t */
+ uarg[1] = (intptr_t) p->tp; /* struct l_timespec * */
+ *n_args = 2;
+ break;
+ }
+ /* linux_clock_gettime */
+ case 263: {
+ struct linux_clock_gettime_args *p = params;
+ iarg[0] = p->which; /* clockid_t */
+ uarg[1] = (intptr_t) p->tp; /* struct l_timespec * */
+ *n_args = 2;
+ break;
+ }
+ /* linux_clock_getres */
+ case 264: {
+ struct linux_clock_getres_args *p = params;
+ iarg[0] = p->which; /* clockid_t */
+ uarg[1] = (intptr_t) p->tp; /* struct l_timespec * */
+ *n_args = 2;
+ break;
+ }
+ /* linux_clock_nanosleep */
+ case 265: {
+ struct linux_clock_nanosleep_args *p = params;
+ iarg[0] = p->which; /* clockid_t */
+ iarg[1] = p->flags; /* int */
+ uarg[2] = (intptr_t) p->rqtp; /* struct l_timespec * */
+ uarg[3] = (intptr_t) p->rmtp; /* struct l_timespec * */
+ *n_args = 4;
+ break;
+ }
+ /* linux_statfs64 */
+ case 266: {
+ struct linux_statfs64_args *p = params;
+ uarg[0] = (intptr_t) p->path; /* char * */
+ uarg[1] = p->bufsize; /* size_t */
+ uarg[2] = (intptr_t) p->buf; /* struct l_statfs64_buf * */
+ *n_args = 3;
+ break;
+ }
+ /* linux_fstatfs64 */
+ case 267: {
+ struct linux_fstatfs64_args *p = params;
+ iarg[0] = p->fd; /* l_uint */
+ uarg[1] = p->bufsize; /* size_t */
+ uarg[2] = (intptr_t) p->buf; /* struct l_statfs64_buf * */
+ *n_args = 3;
+ break;
+ }
+ /* linux_tgkill */
+ case 268: {
+ struct linux_tgkill_args *p = params;
+ iarg[0] = p->tgid; /* int */
+ iarg[1] = p->pid; /* int */
+ iarg[2] = p->sig; /* int */
+ *n_args = 3;
+ break;
+ }
+ /* linux_utimes */
+ case 269: {
+ struct linux_utimes_args *p = params;
+ uarg[0] = (intptr_t) p->fname; /* char * */
+ uarg[1] = (intptr_t) p->tptr; /* struct l_timeval * */
+ *n_args = 2;
+ break;
+ }
+ /* linux_fadvise64_64 */
+ case 270: {
+ struct linux_fadvise64_64_args *p = params;
+ iarg[0] = p->fd; /* int */
+ iarg[1] = p->offset; /* l_loff_t */
+ iarg[2] = p->len; /* l_loff_t */
+ iarg[3] = p->advice; /* int */
+ *n_args = 4;
+ break;
+ }
+ /* linux_mq_open */
+ case 274: {
+ struct linux_mq_open_args *p = params;
+ uarg[0] = (intptr_t) p->name; /* const char * */
+ iarg[1] = p->oflag; /* int */
+ iarg[2] = p->mode; /* mode_t */
+ uarg[3] = (intptr_t) p->attr; /* struct mq_attr * */
+ *n_args = 4;
+ break;
+ }
+ /* linux_mq_unlink */
+ case 275: {
+ struct linux_mq_unlink_args *p = params;
+ uarg[0] = (intptr_t) p->name; /* const char * */
+ *n_args = 1;
+ break;
+ }
+ /* linux_mq_timedsend */
+ case 276: {
+ struct linux_mq_timedsend_args *p = params;
+ iarg[0] = p->mqd; /* l_mqd_t */
+ uarg[1] = (intptr_t) p->msg_ptr; /* const char * */
+ uarg[2] = p->msg_len; /* size_t */
+ uarg[3] = p->msg_prio; /* unsigned int */
+ uarg[4] = (intptr_t) p->abs_timeout; /* const struct l_timespec * */
+ *n_args = 5;
+ break;
+ }
+ /* linux_mq_timedreceive */
+ case 277: {
+ struct linux_mq_timedreceive_args *p = params;
+ iarg[0] = p->mqd; /* l_mqd_t */
+ uarg[1] = (intptr_t) p->msg_ptr; /* char * */
+ uarg[2] = p->msg_len; /* size_t */
+ uarg[3] = p->msg_prio; /* unsigned int */
+ uarg[4] = (intptr_t) p->abs_timeout; /* const struct l_timespec * */
+ *n_args = 5;
+ break;
+ }
+ /* linux_mq_notify */
+ case 278: {
+ struct linux_mq_notify_args *p = params;
+ iarg[0] = p->mqd; /* l_mqd_t */
+ uarg[1] = (intptr_t) p->abs_timeout; /* const struct l_timespec * */
+ *n_args = 2;
+ break;
+ }
+ /* linux_mq_getsetattr */
+ case 279: {
+ struct linux_mq_getsetattr_args *p = params;
+ iarg[0] = p->mqd; /* l_mqd_t */
+ uarg[1] = (intptr_t) p->attr; /* const struct mq_attr * */
+ uarg[2] = (intptr_t) p->oattr; /* struct mq_attr * */
+ *n_args = 3;
+ break;
+ }
+ /* linux_waitid */
+ case 280: {
+ struct linux_waitid_args *p = params;
+ iarg[0] = p->idtype; /* int */
+ iarg[1] = p->id; /* l_pid_t */
+ uarg[2] = (intptr_t) p->info; /* l_siginfo_t * */
+ iarg[3] = p->options; /* int */
+ uarg[4] = (intptr_t) p->rusage; /* void * */
+ *n_args = 5;
+ break;
+ }
+ /* linux_socket */
+ case 281: {
+ struct linux_socket_args *p = params;
+ iarg[0] = p->domain; /* l_int */
+ iarg[1] = p->type; /* l_int */
+ iarg[2] = p->protocol; /* l_int */
+ *n_args = 3;
+ break;
+ }
+ /* linux_bind */
+ case 282: {
+ struct linux_bind_args *p = params;
+ iarg[0] = p->s; /* l_int */
+ iarg[1] = p->name; /* l_uintptr_t */
+ iarg[2] = p->namelen; /* l_int */
+ *n_args = 3;
+ break;
+ }
+ /* linux_connect */
+ case 283: {
+ struct linux_connect_args *p = params;
+ iarg[0] = p->s; /* l_int */
+ iarg[1] = p->name; /* l_uintptr_t */
+ iarg[2] = p->namelen; /* l_int */
+ *n_args = 3;
+ break;
+ }
+ /* linux_listen */
+ case 284: {
+ struct linux_listen_args *p = params;
+ iarg[0] = p->s; /* l_int */
+ iarg[1] = p->backlog; /* l_int */
+ *n_args = 2;
+ break;
+ }
+ /* linux_accept */
+ case 285: {
+ struct linux_accept_args *p = params;
+ iarg[0] = p->s; /* l_int */
+ iarg[1] = p->addr; /* l_uintptr_t */
+ iarg[2] = p->namelen; /* l_uintptr_t */
+ *n_args = 3;
+ break;
+ }
+ /* linux_getsockname */
+ case 286: {
+ struct linux_getsockname_args *p = params;
+ iarg[0] = p->s; /* l_int */
+ iarg[1] = p->addr; /* l_uintptr_t */
+ iarg[2] = p->namelen; /* l_uintptr_t */
+ *n_args = 3;
+ break;
+ }
+ /* linux_getpeername */
+ case 287: {
+ struct linux_getpeername_args *p = params;
+ iarg[0] = p->s; /* l_int */
+ iarg[1] = p->addr; /* l_uintptr_t */
+ iarg[2] = p->namelen; /* l_uintptr_t */
+ *n_args = 3;
+ break;
+ }
+ /* linux_socketpair */
+ case 288: {
+ struct linux_socketpair_args *p = params;
+ iarg[0] = p->domain; /* l_int */
+ iarg[1] = p->type; /* l_int */
+ iarg[2] = p->protocol; /* l_int */
+ iarg[3] = p->rsv; /* l_uintptr_t */
+ *n_args = 4;
+ break;
+ }
+ /* linux_send */
+ case 289: {
+ struct linux_send_args *p = params;
+ iarg[0] = p->s; /* int */
+ iarg[1] = p->msg; /* l_uintptr_t */
+ iarg[2] = p->len; /* int */
+ iarg[3] = p->flags; /* int */
+ *n_args = 4;
+ break;
+ }
+ /* linux_sendto */
+ case 290: {
+ struct linux_sendto_args *p = params;
+ iarg[0] = p->s; /* l_int */
+ iarg[1] = p->msg; /* l_uintptr_t */
+ iarg[2] = p->len; /* l_int */
+ iarg[3] = p->flags; /* l_int */
+ iarg[4] = p->to; /* l_uintptr_t */
+ iarg[5] = p->tolen; /* l_int */
+ *n_args = 6;
+ break;
+ }
+ /* linux_recv */
+ case 291: {
+ struct linux_recv_args *p = params;
+ iarg[0] = p->s; /* int */
+ iarg[1] = p->msg; /* l_uintptr_t */
+ iarg[2] = p->len; /* int */
+ iarg[3] = p->flags; /* int */
+ *n_args = 4;
+ break;
+ }
+ /* linux_recvfrom */
+ case 292: {
+ struct linux_recvfrom_args *p = params;
+ iarg[0] = p->s; /* l_int */
+ iarg[1] = p->buf; /* l_uintptr_t */
+ iarg[2] = p->len; /* l_size_t */
+ iarg[3] = p->flags; /* l_int */
+ iarg[4] = p->from; /* l_uintptr_t */
+ iarg[5] = p->fromlen; /* l_uintptr_t */
+ *n_args = 6;
+ break;
+ }
+ /* linux_shutdown */
+ case 293: {
+ struct linux_shutdown_args *p = params;
+ iarg[0] = p->s; /* l_int */
+ iarg[1] = p->how; /* l_int */
+ *n_args = 2;
+ break;
+ }
+ /* linux_setsockopt */
+ case 294: {
+ struct linux_setsockopt_args *p = params;
+ iarg[0] = p->s; /* l_int */
+ iarg[1] = p->level; /* l_int */
+ iarg[2] = p->optname; /* l_int */
+ iarg[3] = p->optval; /* l_uintptr_t */
+ iarg[4] = p->optlen; /* l_int */
+ *n_args = 5;
+ break;
+ }
+ /* linux_getsockopt */
+ case 295: {
+ struct linux_getsockopt_args *p = params;
+ iarg[0] = p->s; /* l_int */
+ iarg[1] = p->level; /* l_int */
+ iarg[2] = p->optname; /* l_int */
+ iarg[3] = p->optval; /* l_uintptr_t */
+ iarg[4] = p->optlen; /* l_uintptr_t */
+ *n_args = 5;
+ break;
+ }
+ /* linux_sendmsg */
+ case 296: {
+ struct linux_sendmsg_args *p = params;
+ iarg[0] = p->s; /* l_int */
+ iarg[1] = p->msg; /* l_uintptr_t */
+ iarg[2] = p->flags; /* l_int */
+ *n_args = 3;
+ break;
+ }
+ /* linux_recvmsg */
+ case 297: {
+ struct linux_recvmsg_args *p = params;
+ iarg[0] = p->s; /* l_int */
+ iarg[1] = p->msg; /* l_uintptr_t */
+ iarg[2] = p->flags; /* l_int */
+ *n_args = 3;
+ break;
+ }
+ /* linux_semop */
+ case 298: {
+ struct linux_semop_args *p = params;
+ iarg[0] = p->semid; /* l_int */
+ uarg[1] = (intptr_t) p->tsops; /* struct l_sembuf * */
+ iarg[2] = p->nsops; /* l_uint */
+ *n_args = 3;
+ break;
+ }
+ /* linux_semget */
+ case 299: {
+ struct linux_semget_args *p = params;
+ iarg[0] = p->key; /* l_key_t */
+ iarg[1] = p->nsems; /* l_int */
+ iarg[2] = p->semflg; /* l_int */
+ *n_args = 3;
+ break;
+ }
+ /* linux_semctl */
+ case 300: {
+ struct linux_semctl_args *p = params;
+ iarg[0] = p->semid; /* l_int */
+ iarg[1] = p->semnum; /* l_int */
+ iarg[2] = p->cmd; /* l_int */
+ uarg[3] = p->arg.buf; /* union l_semun */
+ *n_args = 4;
+ break;
+ }
+ /* linux_msgsnd */
+ case 301: {
+ struct linux_msgsnd_args *p = params;
+ iarg[0] = p->msqid; /* l_int */
+ uarg[1] = (intptr_t) p->msgp; /* struct l_msgbuf * */
+ iarg[2] = p->msgsz; /* l_size_t */
+ iarg[3] = p->msgflg; /* l_int */
+ *n_args = 4;
+ break;
+ }
+ /* linux_msgrcv */
+ case 302: {
+ struct linux_msgrcv_args *p = params;
+ iarg[0] = p->msqid; /* l_int */
+ uarg[1] = (intptr_t) p->msgp; /* struct l_msgbuf * */
+ iarg[2] = p->msgsz; /* l_size_t */
+ iarg[3] = p->msgtyp; /* l_long */
+ iarg[4] = p->msgflg; /* l_int */
+ *n_args = 5;
+ break;
+ }
+ /* linux_msgget */
+ case 303: {
+ struct linux_msgget_args *p = params;
+ iarg[0] = p->key; /* l_key_t */
+ iarg[1] = p->msgflg; /* l_int */
+ *n_args = 2;
+ break;
+ }
+ /* linux_msgctl */
+ case 304: {
+ struct linux_msgctl_args *p = params;
+ iarg[0] = p->msqid; /* l_int */
+ iarg[1] = p->cmd; /* l_int */
+ uarg[2] = (intptr_t) p->buf; /* struct l_msqid_ds * */
+ *n_args = 3;
+ break;
+ }
+ /* linux_shmat */
+ case 305: {
+ struct linux_shmat_args *p = params;
+ iarg[0] = p->shmid; /* l_int */
+ uarg[1] = (intptr_t) p->shmaddr; /* char * */
+ iarg[2] = p->shmflg; /* l_int */
+ *n_args = 3;
+ break;
+ }
+ /* linux_shmdt */
+ case 306: {
+ struct linux_shmdt_args *p = params;
+ uarg[0] = (intptr_t) p->shmaddr; /* char * */
+ *n_args = 1;
+ break;
+ }
+ /* linux_shmget */
+ case 307: {
+ struct linux_shmget_args *p = params;
+ iarg[0] = p->key; /* l_key_t */
+ iarg[1] = p->size; /* l_size_t */
+ iarg[2] = p->shmflg; /* l_int */
+ *n_args = 3;
+ break;
+ }
+ /* linux_shmctl */
+ case 308: {
+ struct linux_shmctl_args *p = params;
+ iarg[0] = p->shmid; /* l_int */
+ iarg[1] = p->cmd; /* l_int */
+ uarg[2] = (intptr_t) p->buf; /* struct l_shmid_ds * */
+ *n_args = 3;
+ break;
+ }
+ /* linux_add_key */
+ case 309: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_request_key */
+ case 310: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_keyctl */
+ case 311: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_ioprio_set */
+ case 314: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_ioprio_get */
+ case 315: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_inotify_init */
+ case 316: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_inotify_add_watch */
+ case 317: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_inotify_rm_watch */
+ case 318: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_mbind */
+ case 319: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_get_mempolicy */
+ case 320: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_set_mempolicy */
+ case 321: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_openat */
+ case 322: {
+ struct linux_openat_args *p = params;
+ iarg[0] = p->dfd; /* l_int */
+ uarg[1] = (intptr_t) p->filename; /* const char * */
+ iarg[2] = p->flags; /* l_int */
+ iarg[3] = p->mode; /* l_int */
+ *n_args = 4;
+ break;
+ }
+ /* linux_mkdirat */
+ case 323: {
+ struct linux_mkdirat_args *p = params;
+ iarg[0] = p->dfd; /* l_int */
+ uarg[1] = (intptr_t) p->pathname; /* const char * */
+ iarg[2] = p->mode; /* l_int */
+ *n_args = 3;
+ break;
+ }
+ /* linux_mknodat */
+ case 324: {
+ struct linux_mknodat_args *p = params;
+ iarg[0] = p->dfd; /* l_int */
+ uarg[1] = (intptr_t) p->filename; /* const char * */
+ iarg[2] = p->mode; /* l_int */
+ iarg[3] = p->dev; /* l_uint */
+ *n_args = 4;
+ break;
+ }
+ /* linux_fchownat */
+ case 325: {
+ struct linux_fchownat_args *p = params;
+ iarg[0] = p->dfd; /* l_int */
+ uarg[1] = (intptr_t) p->filename; /* const char * */
+ iarg[2] = p->uid; /* l_uid16_t */
+ iarg[3] = p->gid; /* l_gid16_t */
+ iarg[4] = p->flag; /* l_int */
+ *n_args = 5;
+ break;
+ }
+ /* linux_futimesat */
+ case 326: {
+ struct linux_futimesat_args *p = params;
+ iarg[0] = p->dfd; /* l_int */
+ uarg[1] = (intptr_t) p->filename; /* char * */
+ uarg[2] = (intptr_t) p->utimes; /* struct l_timeval * */
+ *n_args = 3;
+ break;
+ }
+ /* linux_fstatat64 */
+ case 327: {
+ struct linux_fstatat64_args *p = params;
+ iarg[0] = p->dfd; /* l_int */
+ uarg[1] = (intptr_t) p->pathname; /* char * */
+ uarg[2] = (intptr_t) p->statbuf; /* struct l_stat64 * */
+ iarg[3] = p->flag; /* l_int */
+ *n_args = 4;
+ break;
+ }
+ /* linux_unlinkat */
+ case 328: {
+ struct linux_unlinkat_args *p = params;
+ iarg[0] = p->dfd; /* l_int */
+ uarg[1] = (intptr_t) p->pathname; /* const char * */
+ iarg[2] = p->flag; /* l_int */
+ *n_args = 3;
+ break;
+ }
+ /* linux_renameat */
+ case 329: {
+ struct linux_renameat_args *p = params;
+ iarg[0] = p->olddfd; /* l_int */
+ uarg[1] = (intptr_t) p->oldname; /* const char * */
+ iarg[2] = p->newdfd; /* l_int */
+ uarg[3] = (intptr_t) p->newname; /* const char * */
+ *n_args = 4;
+ break;
+ }
+ /* linux_linkat */
+ case 330: {
+ struct linux_linkat_args *p = params;
+ iarg[0] = p->olddfd; /* l_int */
+ uarg[1] = (intptr_t) p->oldname; /* const char * */
+ iarg[2] = p->newdfd; /* l_int */
+ uarg[3] = (intptr_t) p->newname; /* const char * */
+ iarg[4] = p->flag; /* l_int */
+ *n_args = 5;
+ break;
+ }
+ /* linux_symlinkat */
+ case 331: {
+ struct linux_symlinkat_args *p = params;
+ uarg[0] = (intptr_t) p->oldname; /* const char * */
+ iarg[1] = p->newdfd; /* l_int */
+ uarg[2] = (intptr_t) p->newname; /* const char * */
+ *n_args = 3;
+ break;
+ }
+ /* linux_readlinkat */
+ case 332: {
+ struct linux_readlinkat_args *p = params;
+ iarg[0] = p->dfd; /* l_int */
+ uarg[1] = (intptr_t) p->path; /* const char * */
+ uarg[2] = (intptr_t) p->buf; /* char * */
+ iarg[3] = p->bufsiz; /* l_int */
+ *n_args = 4;
+ break;
+ }
+ /* linux_fchmodat */
+ case 333: {
+ struct linux_fchmodat_args *p = params;
+ iarg[0] = p->dfd; /* l_int */
+ uarg[1] = (intptr_t) p->filename; /* const char * */
+ iarg[2] = p->mode; /* l_mode_t */
+ *n_args = 3;
+ break;
+ }
+ /* linux_faccessat */
+ case 334: {
+ struct linux_faccessat_args *p = params;
+ iarg[0] = p->dfd; /* l_int */
+ uarg[1] = (intptr_t) p->filename; /* const char * */
+ iarg[2] = p->amode; /* l_int */
+ *n_args = 3;
+ break;
+ }
+ /* linux_pselect6 */
+ case 335: {
+ struct linux_pselect6_args *p = params;
+ iarg[0] = p->nfds; /* l_int */
+ uarg[1] = (intptr_t) p->readfds; /* l_fd_set * */
+ uarg[2] = (intptr_t) p->writefds; /* l_fd_set * */
+ uarg[3] = (intptr_t) p->exceptfds; /* l_fd_set * */
+ uarg[4] = (intptr_t) p->tsp; /* struct l_timespec * */
+ uarg[5] = (intptr_t) p->sig; /* l_uintptr_t * */
+ *n_args = 6;
+ break;
+ }
+ /* linux_ppoll */
+ case 336: {
+ struct linux_ppoll_args *p = params;
+ uarg[0] = (intptr_t) p->fds; /* struct pollfd * */
+ uarg[1] = p->nfds; /* uint32_t */
+ uarg[2] = (intptr_t) p->tsp; /* struct l_timespec * */
+ uarg[3] = (intptr_t) p->sset; /* l_sigset_t * */
+ iarg[4] = p->ssize; /* l_size_t */
+ *n_args = 5;
+ break;
+ }
+ /* linux_unshare */
+ case 337: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_set_robust_list */
+ case 338: {
+ struct linux_set_robust_list_args *p = params;
+ uarg[0] = (intptr_t) p->head; /* struct linux_robust_list_head * */
+ iarg[1] = p->len; /* l_size_t */
+ *n_args = 2;
+ break;
+ }
+ /* linux_get_robust_list */
+ case 339: {
+ struct linux_get_robust_list_args *p = params;
+ iarg[0] = p->pid; /* l_int */
+ uarg[1] = (intptr_t) p->head; /* struct linux_robust_list_head ** */
+ uarg[2] = (intptr_t) p->len; /* l_size_t * */
+ *n_args = 3;
+ break;
+ }
+ /* linux_splice */
+ case 340: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_sync_file_range */
+ case 341: {
+ struct linux_sync_file_range_args *p = params;
+ iarg[0] = p->fd; /* l_int */
+ iarg[1] = p->offset; /* l_loff_t */
+ iarg[2] = p->nbytes; /* l_loff_t */
+ uarg[3] = p->flags; /* unsigned int */
+ *n_args = 4;
+ break;
+ }
+ /* linux_tee */
+ case 342: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_vmsplice */
+ case 343: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_move_pages */
+ case 344: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_getcpu */
+ case 345: {
+ struct linux_getcpu_args *p = params;
+ uarg[0] = (intptr_t) p->cpu; /* l_uint * */
+ uarg[1] = (intptr_t) p->node; /* l_uint * */
+ uarg[2] = (intptr_t) p->cache; /* void * */
+ *n_args = 3;
+ break;
+ }
+ /* linux_epoll_pwait */
+ case 346: {
+ struct linux_epoll_pwait_args *p = params;
+ iarg[0] = p->epfd; /* l_int */
+ uarg[1] = (intptr_t) p->events; /* struct epoll_event * */
+ iarg[2] = p->maxevents; /* l_int */
+ iarg[3] = p->timeout; /* l_int */
+ uarg[4] = (intptr_t) p->mask; /* l_sigset_t * */
+ *n_args = 5;
+ break;
+ }
+ /* linux_kexec_load */
+ case 347: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_utimensat */
+ case 348: {
+ struct linux_utimensat_args *p = params;
+ iarg[0] = p->dfd; /* l_int */
+ uarg[1] = (intptr_t) p->pathname; /* const char * */
+ uarg[2] = (intptr_t) p->times; /* const struct l_timespec * */
+ iarg[3] = p->flags; /* l_int */
+ *n_args = 4;
+ break;
+ }
+ /* linux_signalfd */
+ case 349: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_timerfd_create */
+ case 350: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_eventfd */
+ case 351: {
+ struct linux_eventfd_args *p = params;
+ iarg[0] = p->initval; /* l_uint */
+ *n_args = 1;
+ break;
+ }
+ /* linux_fallocate */
+ case 352: {
+ struct linux_fallocate_args *p = params;
+ iarg[0] = p->fd; /* l_int */
+ iarg[1] = p->mode; /* l_int */
+ iarg[2] = p->offset; /* l_loff_t */
+ iarg[3] = p->len; /* l_loff_t */
+ *n_args = 4;
+ break;
+ }
+ /* linux_timerfd_settime */
+ case 353: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_timerfd_gettime */
+ case 354: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_signalfd4 */
+ case 355: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_eventfd2 */
+ case 356: {
+ struct linux_eventfd2_args *p = params;
+ iarg[0] = p->initval; /* l_uint */
+ iarg[1] = p->flags; /* l_int */
+ *n_args = 2;
+ break;
+ }
+ /* linux_epoll_create1 */
+ case 357: {
+ struct linux_epoll_create1_args *p = params;
+ iarg[0] = p->flags; /* l_int */
+ *n_args = 1;
+ break;
+ }
+ /* linux_dup3 */
+ case 358: {
+ struct linux_dup3_args *p = params;
+ iarg[0] = p->oldfd; /* l_int */
+ iarg[1] = p->newfd; /* l_int */
+ iarg[2] = p->flags; /* l_int */
+ *n_args = 3;
+ break;
+ }
+ /* linux_pipe2 */
+ case 359: {
+ struct linux_pipe2_args *p = params;
+ uarg[0] = (intptr_t) p->pipefds; /* l_int * */
+ iarg[1] = p->flags; /* l_int */
+ *n_args = 2;
+ break;
+ }
+ /* linux_inotify_init1 */
+ case 360: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_preadv */
+ case 361: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_pwritev */
+ case 362: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_rt_tsigqueueinfo */
+ case 363: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_perf_event_open */
+ case 364: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_recvmmsg */
+ case 365: {
+ struct linux_recvmmsg_args *p = params;
+ iarg[0] = p->s; /* l_int */
+ uarg[1] = (intptr_t) p->msg; /* struct l_mmsghdr * */
+ iarg[2] = p->vlen; /* l_uint */
+ iarg[3] = p->flags; /* l_uint */
+ uarg[4] = (intptr_t) p->timeout; /* struct l_timespec * */
+ *n_args = 5;
+ break;
+ }
+ /* linux_accept4 */
+ case 366: {
+ struct linux_accept4_args *p = params;
+ iarg[0] = p->s; /* l_int */
+ iarg[1] = p->addr; /* l_uintptr_t */
+ iarg[2] = p->namelen; /* l_uintptr_t */
+ iarg[3] = p->flags; /* int */
+ *n_args = 4;
+ break;
+ }
+ /* linux_fanotify_init */
+ case 367: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_fanotify_mark */
+ case 368: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_prlimit64 */
+ case 369: {
+ struct linux_prlimit64_args *p = params;
+ iarg[0] = p->pid; /* l_pid_t */
+ iarg[1] = p->resource; /* l_uint */
+ uarg[2] = (intptr_t) p->new; /* struct rlimit * */
+ uarg[3] = (intptr_t) p->old; /* struct rlimit * */
+ *n_args = 4;
+ break;
+ }
+ /* linux_name_to_handle_at */
+ case 370: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_open_by_handle_at */
+ case 371: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_clock_adjtime */
+ case 372: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_syncfs */
+ case 373: {
+ struct linux_syncfs_args *p = params;
+ iarg[0] = p->fd; /* l_int */
+ *n_args = 1;
+ break;
+ }
+ /* linux_sendmmsg */
+ case 374: {
+ struct linux_sendmmsg_args *p = params;
+ iarg[0] = p->s; /* l_int */
+ uarg[1] = (intptr_t) p->msg; /* struct l_mmsghdr * */
+ iarg[2] = p->vlen; /* l_uint */
+ iarg[3] = p->flags; /* l_uint */
+ *n_args = 4;
+ break;
+ }
+ /* linux_setns */
+ case 375: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_process_vm_readv */
+ case 376: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_process_vm_writev */
+ case 377: {
+ *n_args = 0;
+ break;
+ }
+ /* linux_set_tls */
+ case 405: {
+ struct linux_set_tls_args *p = params;
+ uarg[0] = (intptr_t) p->tls; /* void* */
+ *n_args = 1;
+ break;
+ }
+ default:
+ *n_args = 0;
+ break;
+ };
+}
+static void
+systrace_entry_setargdesc(int sysnum, int ndx, char *desc, size_t descsz)
+{
+ const char *p = NULL;
+ switch (sysnum) {
+#define nosys linux_nosys
+ /* linux_exit */
+ case 1:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_fork */
+ case 2:
+ break;
+ /* read */
+ case 3:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "userland char *";
+ break;
+ case 2:
+ p = "u_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* write */
+ case 4:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "userland char *";
+ break;
+ case 2:
+ p = "u_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_open */
+ case 5:
+ switch(ndx) {
+ case 0:
+ p = "userland char *";
+ break;
+ case 1:
+ p = "l_int";
+ break;
+ case 2:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* close */
+ case 6:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_creat */
+ case 8:
+ switch(ndx) {
+ case 0:
+ p = "userland char *";
+ break;
+ case 1:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_link */
+ case 9:
+ switch(ndx) {
+ case 0:
+ p = "userland char *";
+ break;
+ case 1:
+ p = "userland char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_unlink */
+ case 10:
+ switch(ndx) {
+ case 0:
+ p = "userland char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_execve */
+ case 11:
+ switch(ndx) {
+ case 0:
+ p = "userland char *";
+ break;
+ case 1:
+ p = "userland char **";
+ break;
+ case 2:
+ p = "userland char **";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_chdir */
+ case 12:
+ switch(ndx) {
+ case 0:
+ p = "userland char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_mknod */
+ case 14:
+ switch(ndx) {
+ case 0:
+ p = "userland char *";
+ break;
+ case 1:
+ p = "l_int";
+ break;
+ case 2:
+ p = "l_dev_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_chmod */
+ case 15:
+ switch(ndx) {
+ case 0:
+ p = "userland char *";
+ break;
+ case 1:
+ p = "l_mode_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_lchown16 */
+ case 16:
+ switch(ndx) {
+ case 0:
+ p = "userland char *";
+ break;
+ case 1:
+ p = "l_uid16_t";
+ break;
+ case 2:
+ p = "l_gid16_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_lseek */
+ case 19:
+ switch(ndx) {
+ case 0:
+ p = "l_uint";
+ break;
+ case 1:
+ p = "l_off_t";
+ break;
+ case 2:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_getpid */
+ case 20:
+ break;
+ /* linux_mount */
+ case 21:
+ switch(ndx) {
+ case 0:
+ p = "userland char *";
+ break;
+ case 1:
+ p = "userland char *";
+ break;
+ case 2:
+ p = "userland char *";
+ break;
+ case 3:
+ p = "l_ulong";
+ break;
+ case 4:
+ p = "userland void *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_setuid16 */
+ case 23:
+ switch(ndx) {
+ case 0:
+ p = "l_uid16_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_getuid16 */
+ case 24:
+ break;
+ /* linux_pause */
+ case 29:
+ break;
+ /* linux_access */
+ case 33:
+ switch(ndx) {
+ case 0:
+ p = "userland char *";
+ break;
+ case 1:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_nice */
+ case 34:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* sync */
+ case 36:
+ break;
+ /* linux_kill */
+ case 37:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_rename */
+ case 38:
+ switch(ndx) {
+ case 0:
+ p = "userland char *";
+ break;
+ case 1:
+ p = "userland char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_mkdir */
+ case 39:
+ switch(ndx) {
+ case 0:
+ p = "userland char *";
+ break;
+ case 1:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_rmdir */
+ case 40:
+ switch(ndx) {
+ case 0:
+ p = "userland char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* dup */
+ case 41:
+ switch(ndx) {
+ case 0:
+ p = "u_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_pipe */
+ case 42:
+ switch(ndx) {
+ case 0:
+ p = "userland l_int *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_times */
+ case 43:
+ switch(ndx) {
+ case 0:
+ p = "userland struct l_times_argv *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_brk */
+ case 45:
+ switch(ndx) {
+ case 0:
+ p = "l_ulong";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_setgid16 */
+ case 46:
+ switch(ndx) {
+ case 0:
+ p = "l_gid16_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_getgid16 */
+ case 47:
+ break;
+ /* linux_geteuid16 */
+ case 49:
+ break;
+ /* linux_getegid16 */
+ case 50:
+ break;
+ /* acct */
+ case 51:
+ switch(ndx) {
+ case 0:
+ p = "userland char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_umount */
+ case 52:
+ switch(ndx) {
+ case 0:
+ p = "userland char *";
+ break;
+ case 1:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_ioctl */
+ case 54:
+ switch(ndx) {
+ case 0:
+ p = "l_uint";
+ break;
+ case 1:
+ p = "l_uint";
+ break;
+ case 2:
+ p = "l_ulong";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_fcntl */
+ case 55:
+ switch(ndx) {
+ case 0:
+ p = "l_uint";
+ break;
+ case 1:
+ p = "l_uint";
+ break;
+ case 2:
+ p = "l_ulong";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* setpgid */
+ case 57:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* umask */
+ case 60:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* chroot */
+ case 61:
+ switch(ndx) {
+ case 0:
+ p = "userland char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_ustat */
+ case 62:
+ switch(ndx) {
+ case 0:
+ p = "l_dev_t";
+ break;
+ case 1:
+ p = "userland struct l_ustat *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* dup2 */
+ case 63:
+ switch(ndx) {
+ case 0:
+ p = "u_int";
+ break;
+ case 1:
+ p = "u_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_getppid */
+ case 64:
+ break;
+ /* getpgrp */
+ case 65:
+ break;
+ /* setsid */
+ case 66:
+ break;
+ /* linux_sigaction */
+ case 67:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "userland l_osigaction_t *";
+ break;
+ case 2:
+ p = "userland l_osigaction_t *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_setreuid16 */
+ case 70:
+ switch(ndx) {
+ case 0:
+ p = "l_uid16_t";
+ break;
+ case 1:
+ p = "l_uid16_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_setregid16 */
+ case 71:
+ switch(ndx) {
+ case 0:
+ p = "l_gid16_t";
+ break;
+ case 1:
+ p = "l_gid16_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_sigsuspend */
+ case 72:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "l_int";
+ break;
+ case 2:
+ p = "l_osigset_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_sigpending */
+ case 73:
+ switch(ndx) {
+ case 0:
+ p = "userland l_osigset_t *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_sethostname */
+ case 74:
+ switch(ndx) {
+ case 0:
+ p = "userland char *";
+ break;
+ case 1:
+ p = "u_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_setrlimit */
+ case 75:
+ switch(ndx) {
+ case 0:
+ p = "l_uint";
+ break;
+ case 1:
+ p = "userland struct l_rlimit *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* getrusage */
+ case 77:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "userland struct rusage *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_gettimeofday */
+ case 78:
+ switch(ndx) {
+ case 0:
+ p = "userland struct timeval32 *";
+ break;
+ case 1:
+ p = "userland struct timezone *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_settimeofday */
+ case 79:
+ switch(ndx) {
+ case 0:
+ p = "userland struct timeval32 *";
+ break;
+ case 1:
+ p = "userland struct timezone *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_getgroups16 */
+ case 80:
+ switch(ndx) {
+ case 0:
+ p = "l_uint";
+ break;
+ case 1:
+ p = "userland l_gid16_t *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_setgroups16 */
+ case 81:
+ switch(ndx) {
+ case 0:
+ p = "l_uint";
+ break;
+ case 1:
+ p = "userland l_gid16_t *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_symlink */
+ case 83:
+ switch(ndx) {
+ case 0:
+ p = "userland char *";
+ break;
+ case 1:
+ p = "userland char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_readlink */
+ case 85:
+ switch(ndx) {
+ case 0:
+ p = "userland char *";
+ break;
+ case 1:
+ p = "userland char *";
+ break;
+ case 2:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* swapon */
+ case 87:
+ switch(ndx) {
+ case 0:
+ p = "userland char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_reboot */
+ case 88:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "l_int";
+ break;
+ case 2:
+ p = "l_uint";
+ break;
+ case 3:
+ p = "userland void *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* munmap */
+ case 91:
+ switch(ndx) {
+ case 0:
+ p = "caddr_t";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_truncate */
+ case 92:
+ switch(ndx) {
+ case 0:
+ p = "userland char *";
+ break;
+ case 1:
+ p = "l_ulong";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_ftruncate */
+ case 93:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "long";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* fchmod */
+ case 94:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* fchown */
+ case 95:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_getpriority */
+ case 96:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* setpriority */
+ case 97:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_statfs */
+ case 99:
+ switch(ndx) {
+ case 0:
+ p = "userland char *";
+ break;
+ case 1:
+ p = "userland struct l_statfs_buf *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_fstatfs */
+ case 100:
+ switch(ndx) {
+ case 0:
+ p = "l_uint";
+ break;
+ case 1:
+ p = "userland struct l_statfs_buf *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_syslog */
+ case 103:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "userland char *";
+ break;
+ case 2:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_setitimer */
+ case 104:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "userland struct l_itimerval *";
+ break;
+ case 2:
+ p = "userland struct l_itimerval *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_getitimer */
+ case 105:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "userland struct l_itimerval *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_newstat */
+ case 106:
+ switch(ndx) {
+ case 0:
+ p = "userland char *";
+ break;
+ case 1:
+ p = "userland struct l_newstat *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_newlstat */
+ case 107:
+ switch(ndx) {
+ case 0:
+ p = "userland char *";
+ break;
+ case 1:
+ p = "userland struct l_newstat *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_newfstat */
+ case 108:
+ switch(ndx) {
+ case 0:
+ p = "l_uint";
+ break;
+ case 1:
+ p = "userland struct l_newstat *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_vhangup */
+ case 111:
+ break;
+ /* linux_wait4 */
+ case 114:
+ switch(ndx) {
+ case 0:
+ p = "l_pid_t";
+ break;
+ case 1:
+ p = "userland l_int *";
+ break;
+ case 2:
+ p = "l_int";
+ break;
+ case 3:
+ p = "userland void *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_swapoff */
+ case 115:
+ break;
+ /* linux_sysinfo */
+ case 116:
+ switch(ndx) {
+ case 0:
+ p = "userland struct l_sysinfo *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* fsync */
+ case 118:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_sigreturn */
+ case 119:
+ switch(ndx) {
+ case 0:
+ p = "userland struct l_sigframe *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_clone */
+ case 120:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "userland void *";
+ break;
+ case 2:
+ p = "userland void *";
+ break;
+ case 3:
+ p = "userland void *";
+ break;
+ case 4:
+ p = "userland void *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_setdomainname */
+ case 121:
+ switch(ndx) {
+ case 0:
+ p = "userland char *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_newuname */
+ case 122:
+ switch(ndx) {
+ case 0:
+ p = "userland struct l_new_utsname *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_adjtimex */
+ case 124:
+ break;
+ /* linux_mprotect */
+ case 125:
+ switch(ndx) {
+ case 0:
+ p = "caddr_t";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_sigprocmask */
+ case 126:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "userland l_osigset_t *";
+ break;
+ case 2:
+ p = "userland l_osigset_t *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_init_module */
+ case 128:
+ break;
+ /* linux_delete_module */
+ case 129:
+ break;
+ /* linux_quotactl */
+ case 131:
+ break;
+ /* getpgid */
+ case 132:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* fchdir */
+ case 133:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_bdflush */
+ case 134:
+ break;
+ /* linux_sysfs */
+ case 135:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "l_ulong";
+ break;
+ case 2:
+ p = "l_ulong";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_personality */
+ case 136:
+ switch(ndx) {
+ case 0:
+ p = "l_ulong";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_setfsuid16 */
+ case 138:
+ switch(ndx) {
+ case 0:
+ p = "l_uid16_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_setfsgid16 */
+ case 139:
+ switch(ndx) {
+ case 0:
+ p = "l_gid16_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_llseek */
+ case 140:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "l_ulong";
+ break;
+ case 2:
+ p = "l_ulong";
+ break;
+ case 3:
+ p = "userland l_loff_t *";
+ break;
+ case 4:
+ p = "l_uint";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_getdents */
+ case 141:
+ switch(ndx) {
+ case 0:
+ p = "l_uint";
+ break;
+ case 1:
+ p = "userland void *";
+ break;
+ case 2:
+ p = "l_uint";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_select */
+ case 142:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "userland l_fd_set *";
+ break;
+ case 2:
+ p = "userland l_fd_set *";
+ break;
+ case 3:
+ p = "userland l_fd_set *";
+ break;
+ case 4:
+ p = "userland struct l_timeval *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* flock */
+ case 143:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_msync */
+ case 144:
+ switch(ndx) {
+ case 0:
+ p = "l_ulong";
+ break;
+ case 1:
+ p = "l_size_t";
+ break;
+ case 2:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* readv */
+ case 145:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "userland struct iovec *";
+ break;
+ case 2:
+ p = "u_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* writev */
+ case 146:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "userland struct iovec *";
+ break;
+ case 2:
+ p = "u_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_getsid */
+ case 147:
+ switch(ndx) {
+ case 0:
+ p = "l_pid_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_fdatasync */
+ case 148:
+ switch(ndx) {
+ case 0:
+ p = "l_uint";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_sysctl */
+ case 149:
+ switch(ndx) {
+ case 0:
+ p = "userland struct l___sysctl_args *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* mlock */
+ case 150:
+ switch(ndx) {
+ case 0:
+ p = "userland const void *";
+ break;
+ case 1:
+ p = "size_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* munlock */
+ case 151:
+ switch(ndx) {
+ case 0:
+ p = "userland const void *";
+ break;
+ case 1:
+ p = "size_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* mlockall */
+ case 152:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* munlockall */
+ case 153:
+ break;
+ /* linux_sched_setparam */
+ case 154:
+ switch(ndx) {
+ case 0:
+ p = "l_pid_t";
+ break;
+ case 1:
+ p = "userland struct sched_param *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_sched_getparam */
+ case 155:
+ switch(ndx) {
+ case 0:
+ p = "l_pid_t";
+ break;
+ case 1:
+ p = "userland struct sched_param *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_sched_setscheduler */
+ case 156:
+ switch(ndx) {
+ case 0:
+ p = "l_pid_t";
+ break;
+ case 1:
+ p = "l_int";
+ break;
+ case 2:
+ p = "userland struct sched_param *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_sched_getscheduler */
+ case 157:
+ switch(ndx) {
+ case 0:
+ p = "l_pid_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* sched_yield */
+ case 158:
+ break;
+ /* linux_sched_get_priority_max */
+ case 159:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_sched_get_priority_min */
+ case 160:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_sched_rr_get_interval */
+ case 161:
+ switch(ndx) {
+ case 0:
+ p = "l_pid_t";
+ break;
+ case 1:
+ p = "userland struct l_timespec *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_nanosleep */
+ case 162:
+ switch(ndx) {
+ case 0:
+ p = "userland const struct l_timespec *";
+ break;
+ case 1:
+ p = "userland struct l_timespec *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_mremap */
+ case 163:
+ switch(ndx) {
+ case 0:
+ p = "l_ulong";
+ break;
+ case 1:
+ p = "l_ulong";
+ break;
+ case 2:
+ p = "l_ulong";
+ break;
+ case 3:
+ p = "l_ulong";
+ break;
+ case 4:
+ p = "l_ulong";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_setresuid16 */
+ case 164:
+ switch(ndx) {
+ case 0:
+ p = "l_uid16_t";
+ break;
+ case 1:
+ p = "l_uid16_t";
+ break;
+ case 2:
+ p = "l_uid16_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_getresuid16 */
+ case 165:
+ switch(ndx) {
+ case 0:
+ p = "userland l_uid16_t *";
+ break;
+ case 1:
+ p = "userland l_uid16_t *";
+ break;
+ case 2:
+ p = "userland l_uid16_t *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* poll */
+ case 168:
+ switch(ndx) {
+ case 0:
+ p = "userland struct pollfd*";
+ break;
+ case 1:
+ p = "unsigned int";
+ break;
+ case 2:
+ p = "long";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_setresgid16 */
+ case 170:
+ switch(ndx) {
+ case 0:
+ p = "l_gid16_t";
+ break;
+ case 1:
+ p = "l_gid16_t";
+ break;
+ case 2:
+ p = "l_gid16_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_getresgid16 */
+ case 171:
+ switch(ndx) {
+ case 0:
+ p = "userland l_gid16_t *";
+ break;
+ case 1:
+ p = "userland l_gid16_t *";
+ break;
+ case 2:
+ p = "userland l_gid16_t *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_prctl */
+ case 172:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "l_int";
+ break;
+ case 2:
+ p = "l_int";
+ break;
+ case 3:
+ p = "l_int";
+ break;
+ case 4:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_rt_sigreturn */
+ case 173:
+ switch(ndx) {
+ case 0:
+ p = "userland struct l_ucontext *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_rt_sigaction */
+ case 174:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "userland l_sigaction_t *";
+ break;
+ case 2:
+ p = "userland l_sigaction_t *";
+ break;
+ case 3:
+ p = "l_size_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_rt_sigprocmask */
+ case 175:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "userland l_sigset_t *";
+ break;
+ case 2:
+ p = "userland l_sigset_t *";
+ break;
+ case 3:
+ p = "l_size_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_rt_sigpending */
+ case 176:
+ switch(ndx) {
+ case 0:
+ p = "userland l_sigset_t *";
+ break;
+ case 1:
+ p = "l_size_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_rt_sigtimedwait */
+ case 177:
+ switch(ndx) {
+ case 0:
+ p = "userland l_sigset_t *";
+ break;
+ case 1:
+ p = "userland l_siginfo_t *";
+ break;
+ case 2:
+ p = "userland struct l_timeval *";
+ break;
+ case 3:
+ p = "l_size_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_rt_sigqueueinfo */
+ case 178:
+ switch(ndx) {
+ case 0:
+ p = "l_pid_t";
+ break;
+ case 1:
+ p = "l_int";
+ break;
+ case 2:
+ p = "userland l_siginfo_t *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_rt_sigsuspend */
+ case 179:
+ switch(ndx) {
+ case 0:
+ p = "userland l_sigset_t *";
+ break;
+ case 1:
+ p = "l_size_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_pread */
+ case 180:
+ switch(ndx) {
+ case 0:
+ p = "l_uint";
+ break;
+ case 1:
+ p = "userland char *";
+ break;
+ case 2:
+ p = "l_size_t";
+ break;
+ case 3:
+ p = "l_loff_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_pwrite */
+ case 181:
+ switch(ndx) {
+ case 0:
+ p = "l_uint";
+ break;
+ case 1:
+ p = "userland char *";
+ break;
+ case 2:
+ p = "l_size_t";
+ break;
+ case 3:
+ p = "l_loff_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_chown16 */
+ case 182:
+ switch(ndx) {
+ case 0:
+ p = "userland char *";
+ break;
+ case 1:
+ p = "l_uid16_t";
+ break;
+ case 2:
+ p = "l_gid16_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_getcwd */
+ case 183:
+ switch(ndx) {
+ case 0:
+ p = "userland char *";
+ break;
+ case 1:
+ p = "l_ulong";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_capget */
+ case 184:
+ switch(ndx) {
+ case 0:
+ p = "userland struct l_user_cap_header *";
+ break;
+ case 1:
+ p = "userland struct l_user_cap_data *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_capset */
+ case 185:
+ switch(ndx) {
+ case 0:
+ p = "userland struct l_user_cap_header *";
+ break;
+ case 1:
+ p = "userland struct l_user_cap_data *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_sigaltstack */
+ case 186:
+ switch(ndx) {
+ case 0:
+ p = "userland l_stack_t *";
+ break;
+ case 1:
+ p = "userland l_stack_t *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_sendfile */
+ case 187:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "l_int";
+ break;
+ case 2:
+ p = "userland l_long *";
+ break;
+ case 3:
+ p = "l_size_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_vfork */
+ case 190:
+ break;
+ /* linux_getrlimit */
+ case 191:
+ switch(ndx) {
+ case 0:
+ p = "l_uint";
+ break;
+ case 1:
+ p = "userland struct l_rlimit *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_mmap2 */
+ case 192:
+ switch(ndx) {
+ case 0:
+ p = "l_ulong";
+ break;
+ case 1:
+ p = "l_ulong";
+ break;
+ case 2:
+ p = "l_ulong";
+ break;
+ case 3:
+ p = "l_ulong";
+ break;
+ case 4:
+ p = "l_ulong";
+ break;
+ case 5:
+ p = "l_ulong";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_truncate64 */
+ case 193:
+ switch(ndx) {
+ case 0:
+ p = "userland char *";
+ break;
+ case 1:
+ p = "l_loff_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_ftruncate64 */
+ case 194:
+ switch(ndx) {
+ case 0:
+ p = "l_uint";
+ break;
+ case 1:
+ p = "l_loff_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_stat64 */
+ case 195:
+ switch(ndx) {
+ case 0:
+ p = "userland const char *";
+ break;
+ case 1:
+ p = "userland struct l_stat64 *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_lstat64 */
+ case 196:
+ switch(ndx) {
+ case 0:
+ p = "userland const char *";
+ break;
+ case 1:
+ p = "userland struct l_stat64 *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_fstat64 */
+ case 197:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "userland struct l_stat64 *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_lchown */
+ case 198:
+ switch(ndx) {
+ case 0:
+ p = "userland char *";
+ break;
+ case 1:
+ p = "l_uid_t";
+ break;
+ case 2:
+ p = "l_gid_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_getuid */
+ case 199:
+ break;
+ /* linux_getgid */
+ case 200:
+ break;
+ /* geteuid */
+ case 201:
+ break;
+ /* getegid */
+ case 202:
+ break;
+ /* setreuid */
+ case 203:
+ switch(ndx) {
+ case 0:
+ p = "uid_t";
+ break;
+ case 1:
+ p = "uid_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* setregid */
+ case 204:
+ switch(ndx) {
+ case 0:
+ p = "gid_t";
+ break;
+ case 1:
+ p = "gid_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_getgroups */
+ case 205:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "userland l_gid_t *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_setgroups */
+ case 206:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "userland l_gid_t *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* fchown */
+ case 207:
+ break;
+ /* setresuid */
+ case 208:
+ switch(ndx) {
+ case 0:
+ p = "uid_t";
+ break;
+ case 1:
+ p = "uid_t";
+ break;
+ case 2:
+ p = "uid_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* getresuid */
+ case 209:
+ switch(ndx) {
+ case 0:
+ p = "userland uid_t *";
+ break;
+ case 1:
+ p = "userland uid_t *";
+ break;
+ case 2:
+ p = "userland uid_t *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* setresgid */
+ case 210:
+ switch(ndx) {
+ case 0:
+ p = "gid_t";
+ break;
+ case 1:
+ p = "gid_t";
+ break;
+ case 2:
+ p = "gid_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* getresgid */
+ case 211:
+ switch(ndx) {
+ case 0:
+ p = "userland gid_t *";
+ break;
+ case 1:
+ p = "userland gid_t *";
+ break;
+ case 2:
+ p = "userland gid_t *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_chown */
+ case 212:
+ switch(ndx) {
+ case 0:
+ p = "userland char *";
+ break;
+ case 1:
+ p = "l_uid_t";
+ break;
+ case 2:
+ p = "l_gid_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* setuid */
+ case 213:
+ switch(ndx) {
+ case 0:
+ p = "uid_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* setgid */
+ case 214:
+ switch(ndx) {
+ case 0:
+ p = "gid_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_setfsuid */
+ case 215:
+ switch(ndx) {
+ case 0:
+ p = "l_uid_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_setfsgid */
+ case 216:
+ switch(ndx) {
+ case 0:
+ p = "l_gid_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_getdents64 */
+ case 217:
+ switch(ndx) {
+ case 0:
+ p = "l_uint";
+ break;
+ case 1:
+ p = "userland void *";
+ break;
+ case 2:
+ p = "l_uint";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_pivot_root */
+ case 218:
+ switch(ndx) {
+ case 0:
+ p = "userland char *";
+ break;
+ case 1:
+ p = "userland char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_mincore */
+ case 219:
+ switch(ndx) {
+ case 0:
+ p = "l_ulong";
+ break;
+ case 1:
+ p = "l_size_t";
+ break;
+ case 2:
+ p = "userland u_char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* madvise */
+ case 220:
+ switch(ndx) {
+ case 0:
+ p = "userland void *";
+ break;
+ case 1:
+ p = "size_t";
+ break;
+ case 2:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_fcntl64 */
+ case 221:
+ switch(ndx) {
+ case 0:
+ p = "l_uint";
+ break;
+ case 1:
+ p = "l_uint";
+ break;
+ case 2:
+ p = "l_ulong";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_gettid */
+ case 224:
+ break;
+ /* linux_setxattr */
+ case 226:
+ break;
+ /* linux_lsetxattr */
+ case 227:
+ break;
+ /* linux_fsetxattr */
+ case 228:
+ break;
+ /* linux_getxattr */
+ case 229:
+ break;
+ /* linux_lgetxattr */
+ case 230:
+ break;
+ /* linux_fgetxattr */
+ case 231:
+ break;
+ /* linux_listxattr */
+ case 232:
+ break;
+ /* linux_llistxattr */
+ case 233:
+ break;
+ /* linux_flistxattr */
+ case 234:
+ break;
+ /* linux_removexattr */
+ case 235:
+ break;
+ /* linux_lremovexattr */
+ case 236:
+ break;
+ /* linux_fremovexattr */
+ case 237:
+ break;
+ /* linux_tkill */
+ case 238:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_sendfile64 */
+ case 239:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "l_int";
+ break;
+ case 2:
+ p = "userland l_loff_t *";
+ break;
+ case 3:
+ p = "l_size_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_sys_futex */
+ case 240:
+ switch(ndx) {
+ case 0:
+ p = "userland void *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "uint32_t";
+ break;
+ case 3:
+ p = "userland struct l_timespec *";
+ break;
+ case 4:
+ p = "userland uint32_t *";
+ break;
+ case 5:
+ p = "uint32_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_sched_setaffinity */
+ case 241:
+ switch(ndx) {
+ case 0:
+ p = "l_pid_t";
+ break;
+ case 1:
+ p = "l_uint";
+ break;
+ case 2:
+ p = "userland l_ulong *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_sched_getaffinity */
+ case 242:
+ switch(ndx) {
+ case 0:
+ p = "l_pid_t";
+ break;
+ case 1:
+ p = "l_uint";
+ break;
+ case 2:
+ p = "userland l_ulong *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_exit_group */
+ case 248:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_lookup_dcookie */
+ case 249:
+ break;
+ /* linux_epoll_create */
+ case 250:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_epoll_ctl */
+ case 251:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "l_int";
+ break;
+ case 2:
+ p = "l_int";
+ break;
+ case 3:
+ p = "userland struct epoll_event *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_epoll_wait */
+ case 252:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "userland struct epoll_event *";
+ break;
+ case 2:
+ p = "l_int";
+ break;
+ case 3:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_remap_file_pages */
+ case 253:
+ break;
+ /* linux_set_tid_address */
+ case 256:
+ switch(ndx) {
+ case 0:
+ p = "userland int *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_timer_create */
+ case 257:
+ switch(ndx) {
+ case 0:
+ p = "clockid_t";
+ break;
+ case 1:
+ p = "userland struct sigevent *";
+ break;
+ case 2:
+ p = "userland l_timer_t *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_timer_settime */
+ case 258:
+ switch(ndx) {
+ case 0:
+ p = "l_timer_t";
+ break;
+ case 1:
+ p = "l_int";
+ break;
+ case 2:
+ p = "userland const struct itimerspec *";
+ break;
+ case 3:
+ p = "userland struct itimerspec *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_timer_gettime */
+ case 259:
+ switch(ndx) {
+ case 0:
+ p = "l_timer_t";
+ break;
+ case 1:
+ p = "userland struct itimerspec *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_timer_getoverrun */
+ case 260:
+ switch(ndx) {
+ case 0:
+ p = "l_timer_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_timer_delete */
+ case 261:
+ switch(ndx) {
+ case 0:
+ p = "l_timer_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_clock_settime */
+ case 262:
+ switch(ndx) {
+ case 0:
+ p = "clockid_t";
+ break;
+ case 1:
+ p = "userland struct l_timespec *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_clock_gettime */
+ case 263:
+ switch(ndx) {
+ case 0:
+ p = "clockid_t";
+ break;
+ case 1:
+ p = "userland struct l_timespec *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_clock_getres */
+ case 264:
+ switch(ndx) {
+ case 0:
+ p = "clockid_t";
+ break;
+ case 1:
+ p = "userland struct l_timespec *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_clock_nanosleep */
+ case 265:
+ switch(ndx) {
+ case 0:
+ p = "clockid_t";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "userland struct l_timespec *";
+ break;
+ case 3:
+ p = "userland struct l_timespec *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_statfs64 */
+ case 266:
+ switch(ndx) {
+ case 0:
+ p = "userland char *";
+ break;
+ case 1:
+ p = "size_t";
+ break;
+ case 2:
+ p = "userland struct l_statfs64_buf *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_fstatfs64 */
+ case 267:
+ switch(ndx) {
+ case 0:
+ p = "l_uint";
+ break;
+ case 1:
+ p = "size_t";
+ break;
+ case 2:
+ p = "userland struct l_statfs64_buf *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_tgkill */
+ case 268:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_utimes */
+ case 269:
+ switch(ndx) {
+ case 0:
+ p = "userland char *";
+ break;
+ case 1:
+ p = "userland struct l_timeval *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_fadvise64_64 */
+ case 270:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "l_loff_t";
+ break;
+ case 2:
+ p = "l_loff_t";
+ break;
+ case 3:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_mq_open */
+ case 274:
+ switch(ndx) {
+ case 0:
+ p = "userland const char *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "mode_t";
+ break;
+ case 3:
+ p = "userland struct mq_attr *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_mq_unlink */
+ case 275:
+ switch(ndx) {
+ case 0:
+ p = "userland const char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_mq_timedsend */
+ case 276:
+ switch(ndx) {
+ case 0:
+ p = "l_mqd_t";
+ break;
+ case 1:
+ p = "userland const char *";
+ break;
+ case 2:
+ p = "size_t";
+ break;
+ case 3:
+ p = "unsigned int";
+ break;
+ case 4:
+ p = "userland const struct l_timespec *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_mq_timedreceive */
+ case 277:
+ switch(ndx) {
+ case 0:
+ p = "l_mqd_t";
+ break;
+ case 1:
+ p = "userland char *";
+ break;
+ case 2:
+ p = "size_t";
+ break;
+ case 3:
+ p = "unsigned int";
+ break;
+ case 4:
+ p = "userland const struct l_timespec *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_mq_notify */
+ case 278:
+ switch(ndx) {
+ case 0:
+ p = "l_mqd_t";
+ break;
+ case 1:
+ p = "userland const struct l_timespec *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_mq_getsetattr */
+ case 279:
+ switch(ndx) {
+ case 0:
+ p = "l_mqd_t";
+ break;
+ case 1:
+ p = "userland const struct mq_attr *";
+ break;
+ case 2:
+ p = "userland struct mq_attr *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_waitid */
+ case 280:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "l_pid_t";
+ break;
+ case 2:
+ p = "userland l_siginfo_t *";
+ break;
+ case 3:
+ p = "int";
+ break;
+ case 4:
+ p = "userland void *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_socket */
+ case 281:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "l_int";
+ break;
+ case 2:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_bind */
+ case 282:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "l_uintptr_t";
+ break;
+ case 2:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_connect */
+ case 283:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "l_uintptr_t";
+ break;
+ case 2:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_listen */
+ case 284:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_accept */
+ case 285:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "l_uintptr_t";
+ break;
+ case 2:
+ p = "l_uintptr_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_getsockname */
+ case 286:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "l_uintptr_t";
+ break;
+ case 2:
+ p = "l_uintptr_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_getpeername */
+ case 287:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "l_uintptr_t";
+ break;
+ case 2:
+ p = "l_uintptr_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_socketpair */
+ case 288:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "l_int";
+ break;
+ case 2:
+ p = "l_int";
+ break;
+ case 3:
+ p = "l_uintptr_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_send */
+ case 289:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "l_uintptr_t";
+ break;
+ case 2:
+ p = "int";
+ break;
+ case 3:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_sendto */
+ case 290:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "l_uintptr_t";
+ break;
+ case 2:
+ p = "l_int";
+ break;
+ case 3:
+ p = "l_int";
+ break;
+ case 4:
+ p = "l_uintptr_t";
+ break;
+ case 5:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_recv */
+ case 291:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "l_uintptr_t";
+ break;
+ case 2:
+ p = "int";
+ break;
+ case 3:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_recvfrom */
+ case 292:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "l_uintptr_t";
+ break;
+ case 2:
+ p = "l_size_t";
+ break;
+ case 3:
+ p = "l_int";
+ break;
+ case 4:
+ p = "l_uintptr_t";
+ break;
+ case 5:
+ p = "l_uintptr_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_shutdown */
+ case 293:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_setsockopt */
+ case 294:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "l_int";
+ break;
+ case 2:
+ p = "l_int";
+ break;
+ case 3:
+ p = "l_uintptr_t";
+ break;
+ case 4:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_getsockopt */
+ case 295:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "l_int";
+ break;
+ case 2:
+ p = "l_int";
+ break;
+ case 3:
+ p = "l_uintptr_t";
+ break;
+ case 4:
+ p = "l_uintptr_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_sendmsg */
+ case 296:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "l_uintptr_t";
+ break;
+ case 2:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_recvmsg */
+ case 297:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "l_uintptr_t";
+ break;
+ case 2:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_semop */
+ case 298:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "userland struct l_sembuf *";
+ break;
+ case 2:
+ p = "l_uint";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_semget */
+ case 299:
+ switch(ndx) {
+ case 0:
+ p = "l_key_t";
+ break;
+ case 1:
+ p = "l_int";
+ break;
+ case 2:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_semctl */
+ case 300:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "l_int";
+ break;
+ case 2:
+ p = "l_int";
+ break;
+ case 3:
+ p = "union l_semun";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_msgsnd */
+ case 301:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "userland struct l_msgbuf *";
+ break;
+ case 2:
+ p = "l_size_t";
+ break;
+ case 3:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_msgrcv */
+ case 302:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "userland struct l_msgbuf *";
+ break;
+ case 2:
+ p = "l_size_t";
+ break;
+ case 3:
+ p = "l_long";
+ break;
+ case 4:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_msgget */
+ case 303:
+ switch(ndx) {
+ case 0:
+ p = "l_key_t";
+ break;
+ case 1:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_msgctl */
+ case 304:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "l_int";
+ break;
+ case 2:
+ p = "userland struct l_msqid_ds *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_shmat */
+ case 305:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "userland char *";
+ break;
+ case 2:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_shmdt */
+ case 306:
+ switch(ndx) {
+ case 0:
+ p = "userland char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_shmget */
+ case 307:
+ switch(ndx) {
+ case 0:
+ p = "l_key_t";
+ break;
+ case 1:
+ p = "l_size_t";
+ break;
+ case 2:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_shmctl */
+ case 308:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "l_int";
+ break;
+ case 2:
+ p = "userland struct l_shmid_ds *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_add_key */
+ case 309:
+ break;
+ /* linux_request_key */
+ case 310:
+ break;
+ /* linux_keyctl */
+ case 311:
+ break;
+ /* linux_ioprio_set */
+ case 314:
+ break;
+ /* linux_ioprio_get */
+ case 315:
+ break;
+ /* linux_inotify_init */
+ case 316:
+ break;
+ /* linux_inotify_add_watch */
+ case 317:
+ break;
+ /* linux_inotify_rm_watch */
+ case 318:
+ break;
+ /* linux_mbind */
+ case 319:
+ break;
+ /* linux_get_mempolicy */
+ case 320:
+ break;
+ /* linux_set_mempolicy */
+ case 321:
+ break;
+ /* linux_openat */
+ case 322:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "userland const char *";
+ break;
+ case 2:
+ p = "l_int";
+ break;
+ case 3:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_mkdirat */
+ case 323:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "userland const char *";
+ break;
+ case 2:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_mknodat */
+ case 324:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "userland const char *";
+ break;
+ case 2:
+ p = "l_int";
+ break;
+ case 3:
+ p = "l_uint";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_fchownat */
+ case 325:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "userland const char *";
+ break;
+ case 2:
+ p = "l_uid16_t";
+ break;
+ case 3:
+ p = "l_gid16_t";
+ break;
+ case 4:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_futimesat */
+ case 326:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "userland char *";
+ break;
+ case 2:
+ p = "userland struct l_timeval *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_fstatat64 */
+ case 327:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "userland char *";
+ break;
+ case 2:
+ p = "userland struct l_stat64 *";
+ break;
+ case 3:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_unlinkat */
+ case 328:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "userland const char *";
+ break;
+ case 2:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_renameat */
+ case 329:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "userland const char *";
+ break;
+ case 2:
+ p = "l_int";
+ break;
+ case 3:
+ p = "userland const char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_linkat */
+ case 330:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "userland const char *";
+ break;
+ case 2:
+ p = "l_int";
+ break;
+ case 3:
+ p = "userland const char *";
+ break;
+ case 4:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_symlinkat */
+ case 331:
+ switch(ndx) {
+ case 0:
+ p = "userland const char *";
+ break;
+ case 1:
+ p = "l_int";
+ break;
+ case 2:
+ p = "userland const char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_readlinkat */
+ case 332:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "userland const char *";
+ break;
+ case 2:
+ p = "userland char *";
+ break;
+ case 3:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_fchmodat */
+ case 333:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "userland const char *";
+ break;
+ case 2:
+ p = "l_mode_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_faccessat */
+ case 334:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "userland const char *";
+ break;
+ case 2:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_pselect6 */
+ case 335:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "userland l_fd_set *";
+ break;
+ case 2:
+ p = "userland l_fd_set *";
+ break;
+ case 3:
+ p = "userland l_fd_set *";
+ break;
+ case 4:
+ p = "userland struct l_timespec *";
+ break;
+ case 5:
+ p = "userland l_uintptr_t *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_ppoll */
+ case 336:
+ switch(ndx) {
+ case 0:
+ p = "userland struct pollfd *";
+ break;
+ case 1:
+ p = "uint32_t";
+ break;
+ case 2:
+ p = "userland struct l_timespec *";
+ break;
+ case 3:
+ p = "userland l_sigset_t *";
+ break;
+ case 4:
+ p = "l_size_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_unshare */
+ case 337:
+ break;
+ /* linux_set_robust_list */
+ case 338:
+ switch(ndx) {
+ case 0:
+ p = "userland struct linux_robust_list_head *";
+ break;
+ case 1:
+ p = "l_size_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_get_robust_list */
+ case 339:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "userland struct linux_robust_list_head **";
+ break;
+ case 2:
+ p = "userland l_size_t *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_splice */
+ case 340:
+ break;
+ /* linux_sync_file_range */
+ case 341:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "l_loff_t";
+ break;
+ case 2:
+ p = "l_loff_t";
+ break;
+ case 3:
+ p = "unsigned int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_tee */
+ case 342:
+ break;
+ /* linux_vmsplice */
+ case 343:
+ break;
+ /* linux_move_pages */
+ case 344:
+ break;
+ /* linux_getcpu */
+ case 345:
+ switch(ndx) {
+ case 0:
+ p = "userland l_uint *";
+ break;
+ case 1:
+ p = "userland l_uint *";
+ break;
+ case 2:
+ p = "userland void *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_epoll_pwait */
+ case 346:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "userland struct epoll_event *";
+ break;
+ case 2:
+ p = "l_int";
+ break;
+ case 3:
+ p = "l_int";
+ break;
+ case 4:
+ p = "userland l_sigset_t *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_kexec_load */
+ case 347:
+ break;
+ /* linux_utimensat */
+ case 348:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "userland const char *";
+ break;
+ case 2:
+ p = "userland const struct l_timespec *";
+ break;
+ case 3:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_signalfd */
+ case 349:
+ break;
+ /* linux_timerfd_create */
+ case 350:
+ break;
+ /* linux_eventfd */
+ case 351:
+ switch(ndx) {
+ case 0:
+ p = "l_uint";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_fallocate */
+ case 352:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "l_int";
+ break;
+ case 2:
+ p = "l_loff_t";
+ break;
+ case 3:
+ p = "l_loff_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_timerfd_settime */
+ case 353:
+ break;
+ /* linux_timerfd_gettime */
+ case 354:
+ break;
+ /* linux_signalfd4 */
+ case 355:
+ break;
+ /* linux_eventfd2 */
+ case 356:
+ switch(ndx) {
+ case 0:
+ p = "l_uint";
+ break;
+ case 1:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_epoll_create1 */
+ case 357:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_dup3 */
+ case 358:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "l_int";
+ break;
+ case 2:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_pipe2 */
+ case 359:
+ switch(ndx) {
+ case 0:
+ p = "userland l_int *";
+ break;
+ case 1:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_inotify_init1 */
+ case 360:
+ break;
+ /* linux_preadv */
+ case 361:
+ break;
+ /* linux_pwritev */
+ case 362:
+ break;
+ /* linux_rt_tsigqueueinfo */
+ case 363:
+ break;
+ /* linux_perf_event_open */
+ case 364:
+ break;
+ /* linux_recvmmsg */
+ case 365:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "userland struct l_mmsghdr *";
+ break;
+ case 2:
+ p = "l_uint";
+ break;
+ case 3:
+ p = "l_uint";
+ break;
+ case 4:
+ p = "userland struct l_timespec *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_accept4 */
+ case 366:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "l_uintptr_t";
+ break;
+ case 2:
+ p = "l_uintptr_t";
+ break;
+ case 3:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_fanotify_init */
+ case 367:
+ break;
+ /* linux_fanotify_mark */
+ case 368:
+ break;
+ /* linux_prlimit64 */
+ case 369:
+ switch(ndx) {
+ case 0:
+ p = "l_pid_t";
+ break;
+ case 1:
+ p = "l_uint";
+ break;
+ case 2:
+ p = "userland struct rlimit *";
+ break;
+ case 3:
+ p = "userland struct rlimit *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_name_to_handle_at */
+ case 370:
+ break;
+ /* linux_open_by_handle_at */
+ case 371:
+ break;
+ /* linux_clock_adjtime */
+ case 372:
+ break;
+ /* linux_syncfs */
+ case 373:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_sendmmsg */
+ case 374:
+ switch(ndx) {
+ case 0:
+ p = "l_int";
+ break;
+ case 1:
+ p = "userland struct l_mmsghdr *";
+ break;
+ case 2:
+ p = "l_uint";
+ break;
+ case 3:
+ p = "l_uint";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* linux_setns */
+ case 375:
+ break;
+ /* linux_process_vm_readv */
+ case 376:
+ break;
+ /* linux_process_vm_writev */
+ case 377:
+ break;
+ /* linux_set_tls */
+ case 405:
+ switch(ndx) {
+ case 0:
+ p = "userland void*";
+ break;
+ default:
+ break;
+ };
+ break;
+ default:
+ break;
+ };
+ if (p != NULL)
+ strlcpy(desc, p, descsz);
+}
+static void
+systrace_return_setargdesc(int sysnum, int ndx, char *desc, size_t descsz)
+{
+ const char *p = NULL;
+ switch (sysnum) {
+#define nosys linux_nosys
+ /* linux_exit */
+ case 1:
+ if (ndx == 0 || ndx == 1)
+ p = "void";
+ break;
+ /* linux_fork */
+ case 2:
+ /* read */
+ case 3:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* write */
+ case 4:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_open */
+ case 5:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* close */
+ case 6:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_creat */
+ case 8:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_link */
+ case 9:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_unlink */
+ case 10:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_execve */
+ case 11:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_chdir */
+ case 12:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_mknod */
+ case 14:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_chmod */
+ case 15:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_lchown16 */
+ case 16:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_lseek */
+ case 19:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_getpid */
+ case 20:
+ /* linux_mount */
+ case 21:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_setuid16 */
+ case 23:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_getuid16 */
+ case 24:
+ /* linux_pause */
+ case 29:
+ /* linux_access */
+ case 33:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_nice */
+ case 34:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* sync */
+ case 36:
+ /* linux_kill */
+ case 37:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_rename */
+ case 38:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_mkdir */
+ case 39:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_rmdir */
+ case 40:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* dup */
+ case 41:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_pipe */
+ case 42:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_times */
+ case 43:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_brk */
+ case 45:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_setgid16 */
+ case 46:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_getgid16 */
+ case 47:
+ /* linux_geteuid16 */
+ case 49:
+ /* linux_getegid16 */
+ case 50:
+ /* acct */
+ case 51:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_umount */
+ case 52:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_ioctl */
+ case 54:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_fcntl */
+ case 55:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* setpgid */
+ case 57:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* umask */
+ case 60:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* chroot */
+ case 61:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_ustat */
+ case 62:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* dup2 */
+ case 63:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_getppid */
+ case 64:
+ /* getpgrp */
+ case 65:
+ /* setsid */
+ case 66:
+ /* linux_sigaction */
+ case 67:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_setreuid16 */
+ case 70:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_setregid16 */
+ case 71:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_sigsuspend */
+ case 72:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_sigpending */
+ case 73:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_sethostname */
+ case 74:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_setrlimit */
+ case 75:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* getrusage */
+ case 77:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_gettimeofday */
+ case 78:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_settimeofday */
+ case 79:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_getgroups16 */
+ case 80:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_setgroups16 */
+ case 81:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_symlink */
+ case 83:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_readlink */
+ case 85:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* swapon */
+ case 87:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_reboot */
+ case 88:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* munmap */
+ case 91:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_truncate */
+ case 92:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_ftruncate */
+ case 93:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* fchmod */
+ case 94:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* fchown */
+ case 95:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_getpriority */
+ case 96:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* setpriority */
+ case 97:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_statfs */
+ case 99:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_fstatfs */
+ case 100:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_syslog */
+ case 103:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_setitimer */
+ case 104:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_getitimer */
+ case 105:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_newstat */
+ case 106:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_newlstat */
+ case 107:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_newfstat */
+ case 108:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_vhangup */
+ case 111:
+ /* linux_wait4 */
+ case 114:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_swapoff */
+ case 115:
+ /* linux_sysinfo */
+ case 116:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* fsync */
+ case 118:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_sigreturn */
+ case 119:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_clone */
+ case 120:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_setdomainname */
+ case 121:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_newuname */
+ case 122:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_adjtimex */
+ case 124:
+ /* linux_mprotect */
+ case 125:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_sigprocmask */
+ case 126:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_init_module */
+ case 128:
+ /* linux_delete_module */
+ case 129:
+ /* linux_quotactl */
+ case 131:
+ /* getpgid */
+ case 132:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* fchdir */
+ case 133:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_bdflush */
+ case 134:
+ /* linux_sysfs */
+ case 135:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_personality */
+ case 136:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_setfsuid16 */
+ case 138:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_setfsgid16 */
+ case 139:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_llseek */
+ case 140:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_getdents */
+ case 141:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_select */
+ case 142:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* flock */
+ case 143:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_msync */
+ case 144:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* readv */
+ case 145:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* writev */
+ case 146:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_getsid */
+ case 147:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_fdatasync */
+ case 148:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_sysctl */
+ case 149:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* mlock */
+ case 150:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* munlock */
+ case 151:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* mlockall */
+ case 152:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* munlockall */
+ case 153:
+ /* linux_sched_setparam */
+ case 154:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_sched_getparam */
+ case 155:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_sched_setscheduler */
+ case 156:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_sched_getscheduler */
+ case 157:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* sched_yield */
+ case 158:
+ /* linux_sched_get_priority_max */
+ case 159:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_sched_get_priority_min */
+ case 160:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_sched_rr_get_interval */
+ case 161:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_nanosleep */
+ case 162:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_mremap */
+ case 163:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_setresuid16 */
+ case 164:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_getresuid16 */
+ case 165:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* poll */
+ case 168:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_setresgid16 */
+ case 170:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_getresgid16 */
+ case 171:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_prctl */
+ case 172:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_rt_sigreturn */
+ case 173:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_rt_sigaction */
+ case 174:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_rt_sigprocmask */
+ case 175:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_rt_sigpending */
+ case 176:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_rt_sigtimedwait */
+ case 177:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_rt_sigqueueinfo */
+ case 178:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_rt_sigsuspend */
+ case 179:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_pread */
+ case 180:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_pwrite */
+ case 181:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_chown16 */
+ case 182:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_getcwd */
+ case 183:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_capget */
+ case 184:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_capset */
+ case 185:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_sigaltstack */
+ case 186:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_sendfile */
+ case 187:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_vfork */
+ case 190:
+ /* linux_getrlimit */
+ case 191:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_mmap2 */
+ case 192:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_truncate64 */
+ case 193:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_ftruncate64 */
+ case 194:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_stat64 */
+ case 195:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_lstat64 */
+ case 196:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_fstat64 */
+ case 197:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_lchown */
+ case 198:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_getuid */
+ case 199:
+ /* linux_getgid */
+ case 200:
+ /* geteuid */
+ case 201:
+ /* getegid */
+ case 202:
+ /* setreuid */
+ case 203:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* setregid */
+ case 204:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_getgroups */
+ case 205:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_setgroups */
+ case 206:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* fchown */
+ case 207:
+ /* setresuid */
+ case 208:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* getresuid */
+ case 209:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* setresgid */
+ case 210:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* getresgid */
+ case 211:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_chown */
+ case 212:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* setuid */
+ case 213:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* setgid */
+ case 214:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_setfsuid */
+ case 215:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_setfsgid */
+ case 216:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_getdents64 */
+ case 217:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_pivot_root */
+ case 218:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_mincore */
+ case 219:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* madvise */
+ case 220:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_fcntl64 */
+ case 221:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_gettid */
+ case 224:
+ /* linux_setxattr */
+ case 226:
+ /* linux_lsetxattr */
+ case 227:
+ /* linux_fsetxattr */
+ case 228:
+ /* linux_getxattr */
+ case 229:
+ /* linux_lgetxattr */
+ case 230:
+ /* linux_fgetxattr */
+ case 231:
+ /* linux_listxattr */
+ case 232:
+ /* linux_llistxattr */
+ case 233:
+ /* linux_flistxattr */
+ case 234:
+ /* linux_removexattr */
+ case 235:
+ /* linux_lremovexattr */
+ case 236:
+ /* linux_fremovexattr */
+ case 237:
+ /* linux_tkill */
+ case 238:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_sendfile64 */
+ case 239:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_sys_futex */
+ case 240:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_sched_setaffinity */
+ case 241:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_sched_getaffinity */
+ case 242:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_exit_group */
+ case 248:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_lookup_dcookie */
+ case 249:
+ /* linux_epoll_create */
+ case 250:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_epoll_ctl */
+ case 251:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_epoll_wait */
+ case 252:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_remap_file_pages */
+ case 253:
+ /* linux_set_tid_address */
+ case 256:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_timer_create */
+ case 257:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_timer_settime */
+ case 258:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_timer_gettime */
+ case 259:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_timer_getoverrun */
+ case 260:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_timer_delete */
+ case 261:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_clock_settime */
+ case 262:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_clock_gettime */
+ case 263:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_clock_getres */
+ case 264:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_clock_nanosleep */
+ case 265:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_statfs64 */
+ case 266:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_fstatfs64 */
+ case 267:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_tgkill */
+ case 268:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_utimes */
+ case 269:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_fadvise64_64 */
+ case 270:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_mq_open */
+ case 274:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_mq_unlink */
+ case 275:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_mq_timedsend */
+ case 276:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_mq_timedreceive */
+ case 277:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_mq_notify */
+ case 278:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_mq_getsetattr */
+ case 279:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_waitid */
+ case 280:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_socket */
+ case 281:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_bind */
+ case 282:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_connect */
+ case 283:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_listen */
+ case 284:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_accept */
+ case 285:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_getsockname */
+ case 286:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_getpeername */
+ case 287:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_socketpair */
+ case 288:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_send */
+ case 289:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_sendto */
+ case 290:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_recv */
+ case 291:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_recvfrom */
+ case 292:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_shutdown */
+ case 293:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_setsockopt */
+ case 294:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_getsockopt */
+ case 295:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_sendmsg */
+ case 296:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_recvmsg */
+ case 297:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_semop */
+ case 298:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_semget */
+ case 299:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_semctl */
+ case 300:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_msgsnd */
+ case 301:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_msgrcv */
+ case 302:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_msgget */
+ case 303:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_msgctl */
+ case 304:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_shmat */
+ case 305:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_shmdt */
+ case 306:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_shmget */
+ case 307:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_shmctl */
+ case 308:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_add_key */
+ case 309:
+ /* linux_request_key */
+ case 310:
+ /* linux_keyctl */
+ case 311:
+ /* linux_ioprio_set */
+ case 314:
+ /* linux_ioprio_get */
+ case 315:
+ /* linux_inotify_init */
+ case 316:
+ /* linux_inotify_add_watch */
+ case 317:
+ /* linux_inotify_rm_watch */
+ case 318:
+ /* linux_mbind */
+ case 319:
+ /* linux_get_mempolicy */
+ case 320:
+ /* linux_set_mempolicy */
+ case 321:
+ /* linux_openat */
+ case 322:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_mkdirat */
+ case 323:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_mknodat */
+ case 324:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_fchownat */
+ case 325:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_futimesat */
+ case 326:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_fstatat64 */
+ case 327:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_unlinkat */
+ case 328:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_renameat */
+ case 329:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_linkat */
+ case 330:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_symlinkat */
+ case 331:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_readlinkat */
+ case 332:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_fchmodat */
+ case 333:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_faccessat */
+ case 334:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_pselect6 */
+ case 335:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_ppoll */
+ case 336:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_unshare */
+ case 337:
+ /* linux_set_robust_list */
+ case 338:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_get_robust_list */
+ case 339:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_splice */
+ case 340:
+ /* linux_sync_file_range */
+ case 341:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_tee */
+ case 342:
+ /* linux_vmsplice */
+ case 343:
+ /* linux_move_pages */
+ case 344:
+ /* linux_getcpu */
+ case 345:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_epoll_pwait */
+ case 346:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_kexec_load */
+ case 347:
+ /* linux_utimensat */
+ case 348:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_signalfd */
+ case 349:
+ /* linux_timerfd_create */
+ case 350:
+ /* linux_eventfd */
+ case 351:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_fallocate */
+ case 352:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_timerfd_settime */
+ case 353:
+ /* linux_timerfd_gettime */
+ case 354:
+ /* linux_signalfd4 */
+ case 355:
+ /* linux_eventfd2 */
+ case 356:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_epoll_create1 */
+ case 357:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_dup3 */
+ case 358:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_pipe2 */
+ case 359:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_inotify_init1 */
+ case 360:
+ /* linux_preadv */
+ case 361:
+ /* linux_pwritev */
+ case 362:
+ /* linux_rt_tsigqueueinfo */
+ case 363:
+ /* linux_perf_event_open */
+ case 364:
+ /* linux_recvmmsg */
+ case 365:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_accept4 */
+ case 366:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_fanotify_init */
+ case 367:
+ /* linux_fanotify_mark */
+ case 368:
+ /* linux_prlimit64 */
+ case 369:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_name_to_handle_at */
+ case 370:
+ /* linux_open_by_handle_at */
+ case 371:
+ /* linux_clock_adjtime */
+ case 372:
+ /* linux_syncfs */
+ case 373:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_sendmmsg */
+ case 374:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* linux_setns */
+ case 375:
+ /* linux_process_vm_readv */
+ case 376:
+ /* linux_process_vm_writev */
+ case 377:
+ /* linux_set_tls */
+ case 405:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ if (p != NULL)
+ strlcpy(desc, p, descsz);
+}
diff --git a/sys/arm/linux/syscalls.conf b/sys/arm/linux/syscalls.conf
new file mode 100644
index 000000000000..29f37920bba6
--- /dev/null
+++ b/sys/arm/linux/syscalls.conf
@@ -0,0 +1,11 @@
+# $FreeBSD$
+sysnames="linux_syscalls.c"
+sysproto="linux_proto.h"
+sysproto_h=_LINUX_SYSPROTO_H_
+syshdr="linux_syscall.h"
+syssw="linux_sysent.c"
+sysmk="/dev/null"
+syscallprefix="LINUX_SYS_"
+switchname="linux_sysent"
+namesname="linux_syscallnames"
+systrace="linux_systrace_args.c"
diff --git a/sys/arm/linux/syscalls.master b/sys/arm/linux/syscalls.master
new file mode 100644
index 000000000000..02a504459760
--- /dev/null
+++ b/sys/arm/linux/syscalls.master
@@ -0,0 +1,1928 @@
+ $FreeBSD$
+
+; Linux ABI system call name/number map, based on Linux file
+; arch/arm/kernel/calls.S
+
+#include <sys/param.h>
+#include <sys/sysent.h>
+#include <sys/sysproto.h>
+#include <compat/linux/linux_sysproto.h>
+#include <arm/linux/linux.h>
+#include <arm/linux/linux_proto.h>
+
+; Isn't pretty, but there seems to be no other way to trap nosys
+#define nosys linux_nosys
+
+0 AUE_NULL UNIMPL setup
+1 AUE_EXIT STD {
+ void linux_exit(
+ int rval
+ );
+ }
+2 AUE_FORK STD {
+ int linux_fork(void);
+ }
+3 AUE_NULL NOPROTO {
+ int read(
+ int fd,
+ char *buf,
+ u_int nbyte
+ );
+ }
+4 AUE_NULL NOPROTO {
+ int write(
+ int fd,
+ char *buf,
+ u_int nbyte
+ );
+ }
+5 AUE_OPEN_RWTC STD {
+ int linux_open(
+ char *path,
+ l_int flags,
+ l_int mode
+ );
+ }
+6 AUE_CLOSE NOPROTO {
+ int close(
+ int fd);
+ }
+7 AUE_NULL UNIMPL ; was linux_waitpid
+8 AUE_CREAT STD {
+ int linux_creat(
+ char *path,
+ l_int mode
+ );
+ }
+9 AUE_LINK STD {
+ int linux_link(
+ char *path,
+ char *to
+ );
+ }
+10 AUE_UNLINK STD {
+ int linux_unlink(
+ char *path
+ );
+ }
+11 AUE_EXECVE STD {
+ int linux_execve(
+ char *path,
+ char **argp,
+ char **envp
+ );
+ }
+12 AUE_CHDIR STD {
+ int linux_chdir(
+ char *path
+ );
+ }
+13 AUE_NULL UNIMPL ; was OBSOLETE(linux_time)
+14 AUE_MKNOD STD {
+ int linux_mknod(
+ char *path,
+ l_int mode,
+ l_dev_t dev
+ );
+ }
+15 AUE_CHMOD STD {
+ int linux_chmod(
+ char *path,
+ l_mode_t mode
+ );
+ }
+16 AUE_LCHOWN STD {
+ int linux_lchown16(
+ char *path,
+ l_uid16_t uid,
+ l_gid16_t gid);
+ }
+17 AUE_NULL UNIMPL ; was linux_break
+18 AUE_NULL UNIMPL ; was linux_stat
+19 AUE_LSEEK STD {
+ int linux_lseek(
+ l_uint fdes,
+ l_off_t off,
+ l_int whence
+ );
+ }
+20 AUE_GETPID STD {
+ int linux_getpid(void);
+ }
+21 AUE_MOUNT STD {
+ int linux_mount(
+ char *specialfile,
+ char *dir,
+ char *filesystemtype,
+ l_ulong rwflag,
+ void *data
+ );
+ }
+22 AUE_NULL UNIMPL ; was OBSOLETE(linux_oldumount)
+23 AUE_SETUID STD {
+ int linux_setuid16(
+ l_uid16_t uid);
+ }
+24 AUE_GETUID STD {
+ int linux_getuid16(void);
+ }
+25 AUE_NULL UNIMPL ; was OBSOLETE(linux_stime)
+26 AUE_PTRACE UNIMPL ptrace
+27 AUE_NULL UNIMPL ; was OBSOLETE(linux_alarm)
+28 AUE_NULL UNIMPL ; was linux_fstat
+29 AUE_NULL STD {
+ int linux_pause(void);
+ }
+30 AUE_NULL UNIMPL ; was OBSOLETE(linux_utime)
+31 AUE_NULL UNIMPL ; was linux_stty
+32 AUE_NULL UNIMPL ; was linux_getty
+33 AUE_ACCESS STD {
+ int linux_access(
+ char *path,
+ l_int amode
+ );
+ }
+34 AUE_NICE STD {
+ int linux_nice(
+ l_int inc
+ );
+ }
+35 AUE_NULL UNIMPL ; was linux_ftime
+36 AUE_SYNC NOPROTO {
+ int sync(void);
+ }
+37 AUE_KILL STD {
+ int linux_kill(
+ l_int pid,
+ l_int signum
+ );
+ }
+38 AUE_RENAME STD {
+ int linux_rename(
+ char *from,
+ char *to
+ );
+ }
+39 AUE_MKDIR STD {
+ int linux_mkdir(
+ char *path,
+ l_int mode
+ );
+ }
+40 AUE_RMDIR STD {
+ int linux_rmdir(
+ char *path
+ );
+ }
+41 AUE_DUP NOPROTO {
+ int dup(
+ u_int fd);
+ }
+42 AUE_PIPE STD {
+ int linux_pipe(
+ l_int *pipefds
+ );
+ }
+43 AUE_NULL STD {
+ int linux_times(
+ struct l_times_argv *buf
+ );
+ }
+44 AUE_NULL UNIMPL ; was linux_prof
+45 AUE_NULL STD {
+ int linux_brk(
+ l_ulong dsend);
+ }
+46 AUE_SETGID STD {
+ int linux_setgid16(
+ l_gid16_t gid);
+ }
+47 AUE_GETGID STD {
+ int linux_getgid16(void);
+ }
+48 AUE_NULL UNIMPL ; was linux_signal
+49 AUE_GETEUID STD {
+ int linux_geteuid16(void);
+ }
+50 AUE_GETEGID STD {
+ int linux_getegid16(void);
+ }
+51 AUE_ACCT NOPROTO {
+ int acct(
+ char *path
+ );
+ }
+52 AUE_UMOUNT STD {
+ int linux_umount(
+ char *path,
+ l_int flags
+ );
+ }
+53 AUE_NULL UNIMPL ; was linux_lock
+54 AUE_IOCTL STD {
+ int linux_ioctl(
+ l_uint fd,
+ l_uint cmd,
+ l_ulong arg
+ );
+ }
+55 AUE_FCNTL STD {
+ int linux_fcntl(
+ l_uint fd,
+ l_uint cmd,
+ l_ulong arg
+ );
+ }
+56 AUE_NULL UNIMPL ; was linux_mpx
+57 AUE_SETPGRP NOPROTO {
+ int setpgid(
+ int pid,
+ int pgid);
+ }
+58 AUE_NULL UNIMPL ; was linux_ulimit
+59 AUE_NULL UNIMPL ; was linux_olduname
+60 AUE_UMASK NOPROTO {
+ int umask(
+ int newmask
+ );
+ }
+61 AUE_CHROOT NOPROTO {
+ int chroot(
+ char *path
+ );
+ }
+62 AUE_NULL STD {
+ int linux_ustat(
+ l_dev_t dev,
+ struct l_ustat *ubuf
+ );
+ }
+63 AUE_DUP2 NOPROTO {
+ int dup2(
+ u_int from,
+ u_int to
+ );
+ }
+64 AUE_GETPPID STD {
+ int linux_getppid(void);
+ }
+65 AUE_GETPGRP NOPROTO {
+ int getpgrp(void);
+ }
+66 AUE_SETSID NOPROTO {
+ int setsid(void);
+ }
+67 AUE_NULL STD {
+ int linux_sigaction(
+ l_int sig,
+ l_osigaction_t *nsa,
+ l_osigaction_t *osa
+ );
+ }
+68 AUE_NULL UNIMPL ; was linux_sgetmask
+69 AUE_NULL UNIMPL ; was linux_ssetmask
+70 AUE_SETREUID STD {
+ int linux_setreuid16(
+ l_uid16_t ruid,
+ l_uid16_t euid);
+ }
+71 AUE_SETREGID STD {
+ int linux_setregid16(
+ l_gid16_t rgid,
+ l_gid16_t egid);
+ }
+72 AUE_NULL STD {
+ int linux_sigsuspend(
+ l_int hist0,
+ l_int hist1,
+ l_osigset_t mask
+ );
+ }
+73 AUE_NULL STD {
+ int linux_sigpending(
+ l_osigset_t *mask
+ );
+ }
+74 AUE_SYSCTL STD {
+ int linux_sethostname(
+ char *hostname,
+ u_int len
+ );
+ }
+75 AUE_SETRLIMIT STD {
+ int linux_setrlimit(
+ l_uint resource,
+ struct l_rlimit *rlim
+ );
+ }
+76 AUE_NULL UNIMPL ; was OBSOLETE(linux_old_getrlimit)
+77 AUE_GETRUSAGE NOPROTO {
+ int getrusage(
+ int who,
+ struct rusage *rusage
+ );
+ }
+78 AUE_GETTIMEOFDAY STD {
+ int linux_gettimeofday(
+ struct timeval32 *tp,
+ struct timezone *tzp
+ );
+ }
+79 AUE_SETTIMEOFDAY STD {
+ int linux_settimeofday(
+ struct timeval32 *tp,
+ struct timezone *tzp
+ );
+ }
+80 AUE_GETGROUPS STD {
+ int linux_getgroups16(
+ l_uint gidsetsize,
+ l_gid16_t *gidset
+ );
+ }
+81 AUE_SETGROUPS STD {
+ int linux_setgroups16(
+ l_uint gidsetsize,
+ l_gid16_t *gidset
+ );
+ }
+82 AUE_NULL UNIMPL ; was OBSOLETE(linux_old_select)
+83 AUE_SYMLINK STD {
+ int linux_symlink(
+ char *path,
+ char *to
+ );
+ }
+84 AUE_NULL UNIMPL ; was linux_lstat
+85 AUE_READLINK STD {
+ int linux_readlink(
+ char *name,
+ char *buf,
+ l_int count
+ );
+ }
+86 AUE_NULL UNIMPL uselib
+87 AUE_SWAPON NOPROTO {
+ int swapon(
+ char *name
+ );
+ }
+88 AUE_REBOOT STD {
+ int linux_reboot(
+ l_int magic1,
+ l_int magic2,
+ l_uint cmd,
+ void *arg
+ );
+ }
+89 AUE_NULL UNIMPL ; was OBSOLETE(linux_old_readdir)
+90 AUE_NULL UNIMPL ; was OBSOLETE(linux_old_mmap)
+91 AUE_MUNMAP NOPROTO {
+ int munmap(
+ caddr_t addr,
+ int len
+ );
+ }
+92 AUE_TRUNCATE STD {
+ int linux_truncate(
+ char *path,
+ l_ulong length
+ );
+ }
+93 AUE_FTRUNCATE STD {
+ int linux_ftruncate(
+ int fd,
+ long length
+ );
+ }
+94 AUE_FCHMOD NOPROTO {
+ int fchmod(
+ int fd,
+ int mode
+ );
+ }
+95 AUE_FCHOWN NOPROTO {
+ int fchown(
+ int fd,
+ int uid,
+ int gid);
+ }
+96 AUE_GETPRIORITY STD {
+ int linux_getpriority(
+ int which,
+ int who
+ );
+ }
+97 AUE_SETPRIORITY NOPROTO {
+ int setpriority(
+ int which,
+ int who,
+ int prio
+ );
+ }
+98 AUE_NULL UNIMPL ; was linux_profil
+99 AUE_STATFS STD {
+ int linux_statfs(
+ char *path,
+ struct l_statfs_buf *buf
+ );
+ }
+100 AUE_FSTATFS STD {
+ int linux_fstatfs(
+ l_uint fd,
+ struct l_statfs_buf *buf
+ );
+ }
+101 AUE_NULL UNIMPL ; was ioperm
+102 AUE_NULL UNIMPL ; was OBSOLETE(linux_socketcall)
+103 AUE_NULL STD {
+ int linux_syslog(
+ l_int type,
+ char *buf,
+ l_int len
+ );
+ }
+104 AUE_SETITIMER STD {
+ int linux_setitimer(
+ l_int which,
+ struct l_itimerval *itv,
+ struct l_itimerval *oitv
+ );
+ }
+105 AUE_GETITIMER STD {
+ int linux_getitimer(
+ l_int which,
+ struct l_itimerval *itv
+ );
+ }
+106 AUE_STAT STD {
+ int linux_newstat(
+ char *path,
+ struct l_newstat *buf
+ );
+ }
+107 AUE_LSTAT STD {
+ int linux_newlstat(
+ char *path,
+ struct l_newstat *buf
+ );
+ }
+108 AUE_FSTAT STD {
+ int linux_newfstat(
+ l_uint fd,
+ struct l_newstat *buf
+ );
+ }
+109 AUE_NULL UNIMPL ; was linux_uname
+110 AUE_NULL UNIMPL ; was linux_iopl
+111 AUE_NULL STD {
+ int linux_vhangup(void);
+ }
+112 AUE_NULL UNIMPL ;
+113 AUE_NULL UNIMPL ; was OBSOLETE(linux_syscall)
+114 AUE_WAIT4 STD {
+ int linux_wait4(
+ l_pid_t pid,
+ l_int *status,
+ l_int options,
+ void *rusage
+ );
+ }
+115 AUE_SWAPOFF STD {
+ int linux_swapoff(void);
+ }
+116 AUE_NULL STD {
+ int linux_sysinfo(
+ struct l_sysinfo *info
+ );
+ }
+117 AUE_NULL UNIMPL ; was OBSOLETE(linux_ipc)
+118 AUE_FSYNC NOPROTO {
+ int fsync(
+ int fd);
+ }
+119 AUE_SIGRETURN STD {
+ int linux_sigreturn(
+ struct l_sigframe *sfp
+ );
+ }
+120 AUE_RFORK STD {
+ int linux_clone(
+ l_int flags,
+ void *stack,
+ void *parent_tidptr,
+ void *tls,
+ void * child_tidptr
+ );
+ }
+121 AUE_SYSCTL STD {
+ int linux_setdomainname(
+ char *name,
+ int len
+ );
+ }
+122 AUE_NULL STD {
+ int linux_newuname(
+ struct l_new_utsname *buf
+ );
+ }
+123 AUE_NULL UNIMPL ; was linux_modify_ldt
+124 AUE_ADJTIME STD {
+ int linux_adjtimex(void);
+ }
+125 AUE_MPROTECT STD {
+ int linux_mprotect(
+ caddr_t addr,
+ int len,
+ int prot
+ );
+ }
+126 AUE_SIGPROCMASK STD {
+ int linux_sigprocmask(
+ l_int how,
+ l_osigset_t *mask,
+ l_osigset_t *omask
+ );
+ }
+127 AUE_NULL UNIMPL ; was linux_create_module
+128 AUE_NULL STD {
+ int linux_init_module(void);
+ }
+129 AUE_NULL STD {
+ int linux_delete_module(void);
+ }
+130 AUE_NULL UNIMPL ; was linux_get_kernel_syms
+131 AUE_QUOTACTL STD {
+ int linux_quotactl(void);
+ }
+132 AUE_GETPGID NOPROTO {
+ int getpgid(
+ int pid);
+ }
+133 AUE_FCHDIR NOPROTO {
+ int fchdir(
+ int fd);
+ }
+134 AUE_BDFLUSH STD {
+ int linux_bdflush(void);
+ }
+135 AUE_NULL STD {
+ int linux_sysfs(
+ l_int option,
+ l_ulong arg1,
+ l_ulong arg2
+ );
+ }
+136 AUE_PERSONALITY STD {
+ int linux_personality(
+ l_ulong per
+ );
+ }
+137 AUE_NULL UNIMPL ; was afs_syscall
+138 AUE_SETFSUID STD {
+ int linux_setfsuid16(
+ l_uid16_t uid);
+ }
+139 AUE_SETFSGID STD {
+ int linux_setfsgid16(
+ l_gid16_t gid);
+ }
+140 AUE_LSEEK STD {
+ int linux_llseek(
+ l_int fd,
+ l_ulong ohigh,
+ l_ulong olow,
+ l_loff_t *res,
+ l_uint whence
+ );
+ }
+141 AUE_GETDIRENTRIES STD {
+ int linux_getdents(
+ l_uint fd,
+ void *dent,
+ l_uint count
+ );
+ }
+; 142: newselect
+142 AUE_SELECT STD {
+ int linux_select(
+ l_int nfds,
+ l_fd_set *readfds,
+ l_fd_set *writefds,
+ l_fd_set *exceptfds,
+ struct l_timeval *timeout
+ );
+ }
+143 AUE_FLOCK NOPROTO {
+ int flock(
+ int fd,
+ int how
+ );
+ }
+144 AUE_MSYNC STD {
+ int linux_msync(
+ l_ulong addr,
+ l_size_t len,
+ l_int fl
+ );
+ }
+145 AUE_READV NOPROTO {
+ int readv(
+ int fd,
+ struct iovec *iovp,
+ u_int iovcnt
+ );
+ }
+146 AUE_WRITEV NOPROTO {
+ int writev(
+ int fd,
+ struct iovec *iovp,
+ u_int iovcnt
+ );
+ }
+147 AUE_GETSID STD {
+ int linux_getsid(
+ l_pid_t pid);
+ }
+148 AUE_NULL STD {
+ int linux_fdatasync(
+ l_uint fd);
+ }
+149 AUE_SYSCTL STD {
+ int linux_sysctl(
+ struct l___sysctl_args *args
+ );
+ }
+150 AUE_MLOCK NOPROTO {
+ int mlock(
+ const void *addr,
+ size_t len
+ );
+ }
+151 AUE_MUNLOCK NOPROTO {
+ int munlock(
+ const void *addr,
+ size_t len
+ );
+ }
+152 AUE_MLOCKALL NOPROTO {
+ int mlockall(
+ int how
+ );
+ }
+153 AUE_MUNLOCKALL NOPROTO {
+ int munlockall(void);
+ }
+154 AUE_SCHED_SETPARAM STD {
+ int linux_sched_setparam(
+ l_pid_t pid,
+ struct sched_param *param
+ );
+ }
+155 AUE_SCHED_GETPARAM STD {
+ int linux_sched_getparam(
+ l_pid_t pid,
+ struct sched_param *param
+ );
+ }
+156 AUE_SCHED_SETSCHEDULER STD {
+ int linux_sched_setscheduler(
+ l_pid_t pid,
+ l_int policy,
+ struct sched_param *param
+ );
+ }
+157 AUE_SCHED_GETSCHEDULER STD {
+ int linux_sched_getscheduler(
+ l_pid_t pid);
+ }
+158 AUE_NULL NOPROTO {
+ int sched_yield(void);
+ }
+159 AUE_SCHED_GET_PRIORITY_MAX STD {
+ int linux_sched_get_priority_max(
+ l_int policy
+ );
+ }
+160 AUE_SCHED_GET_PRIORITY_MIN STD {
+ int linux_sched_get_priority_min(
+ l_int policy
+ );
+ }
+161 AUE_SCHED_RR_GET_INTERVAL STD {
+ int linux_sched_rr_get_interval(
+ l_pid_t pid,
+ struct l_timespec *interval
+ );
+ }
+162 AUE_NULL STD {
+ int linux_nanosleep(
+ const struct l_timespec *rqtp,
+ struct l_timespec *rmtp
+ );
+ }
+163 AUE_NULL STD {
+ int linux_mremap(
+ l_ulong addr,
+ l_ulong old_len,
+ l_ulong new_len,
+ l_ulong flags,
+ l_ulong new_addr
+ );
+ }
+164 AUE_SETRESUID STD {
+ int linux_setresuid16(
+ l_uid16_t ruid,
+ l_uid16_t euid,
+ l_uid16_t suid);
+ }
+165 AUE_GETRESUID STD {
+ int linux_getresuid16(
+ l_uid16_t *ruid,
+ l_uid16_t *euid,
+ l_uid16_t *suid);
+ }
+166 AUE_NULL UNIMPL ; was linux_vm86
+167 AUE_NULL UNIMPL ; was linux_query_module
+168 AUE_POLL NOPROTO {
+ int poll(
+ struct pollfd* fds,
+ unsigned int nfds,
+ long timeout
+ );
+ }
+169 AUE_NULL UNIMPL ; was linux_nfsservctl
+170 AUE_SETRESGID STD {
+ int linux_setresgid16(
+ l_gid16_t rgid,
+ l_gid16_t egid,
+ l_gid16_t sgid);
+ }
+171 AUE_GETRESGID STD {
+ int linux_getresgid16(
+ l_gid16_t *rgid,
+ l_gid16_t *egid,
+ l_gid16_t *sgid);
+ }
+172 AUE_PRCTL STD {
+ int linux_prctl(
+ l_int option,
+ l_int arg2,
+ l_int arg3,
+ l_int arg4,
+ l_int arg5
+ );
+ }
+173 AUE_NULL STD {
+ int linux_rt_sigreturn(
+ struct l_ucontext *ucp
+ );
+ }
+174 AUE_NULL STD {
+ int linux_rt_sigaction(
+ l_int sig,
+ l_sigaction_t *act,
+ l_sigaction_t *oact,
+ l_size_t sigsetsize
+ );
+ }
+175 AUE_NULL STD {
+ int linux_rt_sigprocmask(
+ l_int how,
+ l_sigset_t *mask,
+ l_sigset_t *omask,
+ l_size_t sigsetsize
+ );
+ }
+176 AUE_NULL STD {
+ int linux_rt_sigpending(
+ l_sigset_t *set,
+ l_size_t sigsetsize
+ );
+ }
+177 AUE_NULL STD {
+ int linux_rt_sigtimedwait(
+ l_sigset_t *mask,
+ l_siginfo_t *ptr,
+ struct l_timeval *timeout,
+ l_size_t sigsetsize
+ );
+ }
+178 AUE_NULL STD {
+ int linux_rt_sigqueueinfo(
+ l_pid_t pid,
+ l_int sig,
+ l_siginfo_t *info
+ );
+ }
+179 AUE_NULL STD {
+ int linux_rt_sigsuspend(
+ l_sigset_t *newset,
+ l_size_t sigsetsize
+ );
+ }
+180 AUE_PREAD STD {
+ int linux_pread(
+ l_uint fd,
+ char *buf,
+ l_size_t nbyte,
+ l_loff_t offset
+ );
+ }
+181 AUE_PWRITE STD {
+ int linux_pwrite(
+ l_uint fd,
+ char *buf,
+ l_size_t nbyte,
+ l_loff_t offset
+ );
+ }
+182 AUE_CHOWN STD {
+ int linux_chown16(
+ char *path,
+ l_uid16_t uid,
+ l_gid16_t gid);
+ }
+183 AUE_GETCWD STD {
+ int linux_getcwd(
+ char *buf,
+ l_ulong bufsize
+ );
+ }
+184 AUE_CAPGET STD {
+ int linux_capget(
+ struct l_user_cap_header *hdrp,
+ struct l_user_cap_data *datap
+ );
+ }
+185 AUE_CAPSET STD {
+ int linux_capset(
+ struct l_user_cap_header *hdrp,
+ struct l_user_cap_data *datap
+ );
+ }
+186 AUE_NULL STD {
+ int linux_sigaltstack(
+ l_stack_t *uss,
+ l_stack_t *uoss
+ );
+ }
+187 AUE_SENDFILE STD {
+ int linux_sendfile(
+ l_int out,
+ l_int in,
+ l_long *offset,
+ l_size_t count
+ );
+ }
+188 AUE_NULL UNIMPL ; was getpmsg
+189 AUE_NULL UNIMPL ; was putpmsg
+190 AUE_VFORK STD {
+ int linux_vfork(void);
+ }
+; 191: ugetrlimit
+191 AUE_GETRLIMIT STD {
+ int linux_getrlimit(
+ l_uint resource,
+ struct l_rlimit *rlim
+ );
+ }
+192 AUE_MMAP STD {
+ int linux_mmap2(
+ l_ulong addr,
+ l_ulong len,
+ l_ulong prot,
+ l_ulong flags,
+ l_ulong fd,
+ l_ulong pgoff
+ );
+ }
+193 AUE_TRUNCATE STD {
+ int linux_truncate64(
+ char *path,
+ l_loff_t length
+ );
+ }
+194 AUE_FTRUNCATE STD {
+ int linux_ftruncate64(
+ l_uint fd,
+ l_loff_t length
+ );
+ }
+195 AUE_STAT STD {
+ int linux_stat64(
+ const char *filename,
+ struct l_stat64 *statbuf
+ );
+ }
+196 AUE_LSTAT STD {
+ int linux_lstat64(
+ const char *filename,
+ struct l_stat64 *statbuf
+ );
+ }
+197 AUE_FSTAT STD {
+ int linux_fstat64(
+ l_int fd,
+ struct l_stat64 *statbuf
+ );
+ }
+198 AUE_LCHOWN STD {
+ int linux_lchown(
+ char *path,
+ l_uid_t uid,
+ l_gid_t gid);
+ }
+199 AUE_GETUID STD {
+ int linux_getuid(void);
+ }
+200 AUE_GETGID STD {
+ int linux_getgid(void);
+ }
+201 AUE_GETEUID NOPROTO {
+ int geteuid(void);
+ }
+202 AUE_GETEGID NOPROTO {
+ int getegid(void);
+ }
+203 AUE_SETREUID NOPROTO {
+ int setreuid(
+ uid_t ruid,
+ uid_t euid);
+ }
+204 AUE_SETREGID NOPROTO {
+ int setregid(
+ gid_t rgid,
+ gid_t egid);
+ }
+205 AUE_GETGROUPS STD {
+ int linux_getgroups(
+ l_int gidsetsize,
+ l_gid_t *grouplist
+ );
+ }
+206 AUE_SETGROUPS STD {
+ int linux_setgroups(
+ l_int gidsetsize,
+ l_gid_t *grouplist
+ );
+ }
+207 AUE_FCHOWN NODEF fchown fchown fchown_args int
+208 AUE_SETRESUID NOPROTO {
+ int setresuid(
+ uid_t ruid,
+ uid_t euid,
+ uid_t suid);
+ }
+209 AUE_GETRESUID NOPROTO {
+ int getresuid(
+ uid_t *ruid,
+ uid_t *euid,
+ uid_t *suid);
+ }
+210 AUE_SETRESGID NOPROTO {
+ int setresgid(
+ gid_t rgid,
+ gid_t egid,
+ gid_t sgid);
+ }
+211 AUE_GETRESGID NOPROTO {
+ int getresgid(
+ gid_t *rgid,
+ gid_t *egid,
+ gid_t *sgid);
+ }
+212 AUE_CHOWN STD {
+ int linux_chown(
+ char *path,
+ l_uid_t uid,
+ l_gid_t gid);
+ }
+213 AUE_SETUID NOPROTO {
+ int setuid(
+ uid_t uid);
+ }
+214 AUE_SETGID NOPROTO {
+ int setgid(
+ gid_t gid);
+ }
+215 AUE_SETFSUID STD {
+ int linux_setfsuid(
+ l_uid_t uid);
+ }
+216 AUE_SETFSGID STD {
+ int linux_setfsgid(
+ l_gid_t gid);
+ }
+
+217 AUE_GETDIRENTRIES STD {
+ int linux_getdents64(
+ l_uint fd,
+ void *dirent,
+ l_uint count
+ );
+ }
+218 AUE_PIVOT_ROOT STD {
+ int linux_pivot_root(
+ char *new_root,
+ char *put_old);
+ }
+219 AUE_MINCORE STD {
+ int linux_mincore(
+ l_ulong start,
+ l_size_t len,
+ u_char *vec
+ );
+ }
+220 AUE_MADVISE NOPROTO {
+ int madvise(void *addr,
+ size_t len,
+ int behav
+ );
+ }
+221 AUE_FCNTL STD {
+ int linux_fcntl64(
+ l_uint fd,
+ l_uint cmd,
+ l_ulong arg
+ );
+ }
+222 AUE_NULL UNIMPL
+223 AUE_NULL UNIMPL
+224 AUE_NULL STD {
+ long linux_gettid(void);
+ }
+225 AUE_NULL UNIMPL linux_readahead
+226 AUE_NULL STD {
+ int linux_setxattr(void);
+ }
+227 AUE_NULL STD {
+ int linux_lsetxattr(void);
+ }
+228 AUE_NULL STD {
+ int linux_fsetxattr(void);
+ }
+229 AUE_NULL STD {
+ int linux_getxattr(void);
+ }
+230 AUE_NULL STD {
+ int linux_lgetxattr(void);
+ }
+231 AUE_NULL STD {
+ int linux_fgetxattr(void);
+ }
+232 AUE_NULL STD {
+ int linux_listxattr(void);
+ }
+233 AUE_NULL STD {
+ int linux_llistxattr(void);
+ }
+234 AUE_NULL STD {
+ int linux_flistxattr(void);
+ }
+235 AUE_NULL STD {
+ int linux_removexattr(void);
+ }
+236 AUE_NULL STD {
+ int linux_lremovexattr(void);
+ }
+237 AUE_NULL STD {
+ int linux_fremovexattr(void);
+ }
+238 AUE_NULL STD {
+ int linux_tkill(
+ int tid,
+ int sig
+ );
+ }
+239 AUE_SENDFILE STD {
+ int linux_sendfile64(
+ l_int out,
+ l_int in,
+ l_loff_t *offset,
+ l_size_t count
+ );
+ }
+240 AUE_NULL STD {
+ int linux_sys_futex(void *uaddr,
+ int op,
+ uint32_t val,
+ struct l_timespec *timeout,
+ uint32_t *uaddr2,
+ uint32_t val3
+ );
+ }
+241 AUE_NULL STD {
+ int linux_sched_setaffinity(
+ l_pid_t pid,
+ l_uint len,
+ l_ulong *user_mask_ptr
+ );
+ }
+242 AUE_NULL STD {
+ int linux_sched_getaffinity(
+ l_pid_t pid,
+ l_uint len,
+ l_ulong *user_mask_ptr
+ );
+ }
+243 AUE_NULL UNIMPL linux_io_setup
+244 AUE_NULL UNIMPL linux_io_destroy
+245 AUE_NULL UNIMPL linux_io_getevents
+246 AUE_NULL UNIMPL linux_io_submit
+247 AUE_NULL UNIMPL linux_io_cancel
+248 AUE_EXIT STD {
+ int linux_exit_group(
+ int error_code
+ );
+ }
+249 AUE_NULL STD {
+ int linux_lookup_dcookie(void);
+ }
+250 AUE_NULL STD {
+ int linux_epoll_create(
+ l_int size
+ );
+ }
+251 AUE_NULL STD {
+ int linux_epoll_ctl(
+ l_int epfd,
+ l_int op,
+ l_int fd,
+ struct epoll_event *event
+ );
+ }
+252 AUE_NULL STD {
+ int linux_epoll_wait(
+ l_int epfd,
+ struct epoll_event *events,
+ l_int maxevents,
+ l_int timeout
+ );
+ }
+253 AUE_NULL STD {
+ int linux_remap_file_pages(void);
+ }
+
+254 AUE_NULL UNIMPL ; was set_thread_area
+255 AUE_NULL UNIMPL ; was get_thread_area
+256 AUE_NULL STD {
+ int linux_set_tid_address(
+ int *tidptr
+ );
+ }
+257 AUE_NULL STD {
+ int linux_timer_create(
+ clockid_t clock_id,
+ struct sigevent *evp,
+ l_timer_t *timerid);
+ }
+258 AUE_NULL STD {
+ int linux_timer_settime(
+ l_timer_t timerid,
+ l_int flags,
+ const struct itimerspec *new,
+ struct itimerspec *old);
+ }
+259 AUE_NULL STD {
+ int linux_timer_gettime(
+ l_timer_t timerid,
+ struct itimerspec *setting
+ );
+ }
+260 AUE_NULL STD {
+ int linux_timer_getoverrun(
+ l_timer_t timerid);
+ }
+261 AUE_NULL STD {
+ int linux_timer_delete(
+ l_timer_t timerid);
+ }
+262 AUE_CLOCK_SETTIME STD {
+ int linux_clock_settime(
+ clockid_t which,
+ struct l_timespec *tp
+ );
+ }
+263 AUE_NULL STD {
+ int linux_clock_gettime(
+ clockid_t which,
+ struct l_timespec *tp
+ );
+ }
+264 AUE_NULL STD {
+ int linux_clock_getres(
+ clockid_t which,
+ struct l_timespec *tp
+ );
+ }
+265 AUE_NULL STD {
+ int linux_clock_nanosleep(
+ clockid_t which,
+ int flags,
+ struct l_timespec *rqtp,
+ struct l_timespec *rmtp
+ );
+ }
+266 AUE_STATFS STD {
+ int linux_statfs64(
+ char *path,
+ size_t bufsize,
+ struct l_statfs64_buf *buf
+ );
+ }
+267 AUE_FSTATFS STD {
+ int linux_fstatfs64(
+ l_uint fd,
+ size_t bufsize,
+ struct l_statfs64_buf *buf
+ );
+ }
+268 AUE_NULL STD {
+ int linux_tgkill(
+ int tgid,
+ int pid,
+ int sig
+ );
+ }
+269 AUE_UTIMES STD {
+ int linux_utimes(
+ char *fname,
+ struct l_timeval *tptr
+ );
+ }
+270 AUE_NULL STD {
+ int linux_fadvise64_64(
+ int fd,
+ l_loff_t offset,
+ l_loff_t len,
+ int advice
+ );
+ }
+271 AUE_NULL UNIMPL pciconfig_iobase
+272 AUE_NULL UNIMPL pciconfig_read
+273 AUE_NULL UNIMPL pciconfig_write
+; linux 2.6.6:
+274 AUE_NULL STD {
+ int linux_mq_open(
+ const char *name,
+ int oflag,
+ mode_t mode,
+ struct mq_attr *attr
+ );
+ }
+275 AUE_NULL STD {
+ int linux_mq_unlink(
+ const char *name
+ );
+ }
+276 AUE_NULL STD {
+ int linux_mq_timedsend(
+ l_mqd_t mqd,
+ const char *msg_ptr,
+ size_t msg_len,
+ unsigned int msg_prio,
+ const struct l_timespec *abs_timeout
+ );
+ }
+277 AUE_NULL STD {
+ int linux_mq_timedreceive(
+ l_mqd_t mqd,
+ char *msg_ptr,
+ size_t msg_len,
+ unsigned int msg_prio,
+ const struct l_timespec *abs_timeout
+ );
+ }
+278 AUE_NULL STD {
+ int linux_mq_notify(
+ l_mqd_t mqd,
+ const struct l_timespec *abs_timeout
+ );
+ }
+279 AUE_NULL STD {
+ int linux_mq_getsetattr(
+ l_mqd_t mqd,
+ const struct mq_attr *attr,
+ struct mq_attr *oattr
+ );
+ }
+280 AUE_WAIT6 STD {
+ int linux_waitid(
+ int idtype,
+ l_pid_t id,
+ l_siginfo_t *info,
+ int options,
+ void *rusage
+ );
+ }
+281 AUE_SOCKET STD {
+ int linux_socket(
+ l_int domain,
+ l_int type,
+ l_int protocol
+ );
+ }
+282 AUE_BIND STD {
+ int linux_bind(
+ l_int s,
+ l_uintptr_t name,
+ l_int namelen
+ );
+ }
+283 AUE_CONNECT STD {
+ int linux_connect(
+ l_int s,
+ l_uintptr_t name,
+ l_int namelen
+ );
+ }
+284 AUE_LISTEN STD {
+ int linux_listen(
+ l_int s,
+ l_int backlog
+ );
+ }
+285 AUE_ACCEPT STD {
+ int linux_accept(
+ l_int s,
+ l_uintptr_t addr,
+ l_uintptr_t namelen
+ );
+ }
+286 AUE_GETSOCKNAME STD {
+ int linux_getsockname(
+ l_int s,
+ l_uintptr_t addr,
+ l_uintptr_t namelen
+ );
+ }
+287 AUE_GETPEERNAME STD {
+ int linux_getpeername(
+ l_int s,
+ l_uintptr_t addr,
+ l_uintptr_t namelen
+ );
+ }
+288 AUE_SOCKETPAIR STD {
+ int linux_socketpair(
+ l_int domain,
+ l_int type,
+ l_int protocol,
+ l_uintptr_t rsv
+ );
+ }
+289 AUE_SEND STD {
+ int linux_send(
+ int s,
+ l_uintptr_t msg,
+ int len,
+ int flags
+ );
+ }
+290 AUE_SENDTO STD {
+ int linux_sendto(
+ l_int s,
+ l_uintptr_t msg,
+ l_int len,
+ l_int flags,
+ l_uintptr_t to,
+ l_int tolen
+ );
+ }
+291 AUE_RECV STD {
+ int linux_recv(
+ int s,
+ l_uintptr_t msg,
+ int len,
+ int flags
+ );
+ }
+292 AUE_RECVFROM STD {
+ int linux_recvfrom(
+ l_int s,
+ l_uintptr_t buf,
+ l_size_t len,
+ l_int flags,
+ l_uintptr_t from,
+ l_uintptr_t fromlen
+ );
+ }
+293 AUE_NULL STD {
+ int linux_shutdown(
+ l_int s,
+ l_int how
+ );
+ }
+294 AUE_SETSOCKOPT STD {
+ int linux_setsockopt(
+ l_int s,
+ l_int level,
+ l_int optname,
+ l_uintptr_t optval,
+ l_int optlen
+ );
+ }
+295 AUE_GETSOCKOPT STD {
+ int linux_getsockopt(
+ l_int s,
+ l_int level,
+ l_int optname,
+ l_uintptr_t optval,
+ l_uintptr_t optlen
+ );
+ }
+296 AUE_SENDMSG STD {
+ int linux_sendmsg(
+ l_int s,
+ l_uintptr_t msg,
+ l_int flags
+ );
+ }
+297 AUE_RECVMSG STD {
+ int linux_recvmsg(
+ l_int s,
+ l_uintptr_t msg,
+ l_int flags
+ );
+ }
+298 AUE_NULL STD {
+ int linux_semop(
+ l_int semid,
+ struct l_sembuf *tsops,
+ l_uint nsops
+ );
+ }
+299 AUE_NULL STD {
+ int linux_semget(l_key_t key,
+ l_int nsems,
+ l_int semflg
+ );
+ }
+300 AUE_NULL STD {
+ int linux_semctl(
+ l_int semid,
+ l_int semnum,
+ l_int cmd,
+ union l_semun arg
+ );
+ }
+301 AUE_NULL STD {
+ int linux_msgsnd(
+ l_int msqid,
+ struct l_msgbuf *msgp,
+ l_size_t msgsz,
+ l_int msgflg
+ );
+ }
+302 AUE_NULL STD {
+ int linux_msgrcv(
+ l_int msqid,
+ struct l_msgbuf *msgp,
+ l_size_t msgsz,
+ l_long msgtyp,
+ l_int msgflg
+ );
+ }
+303 AUE_NULL STD {
+ int linux_msgget(
+ l_key_t key,
+ l_int msgflg
+ );
+ }
+304 AUE_NULL STD {
+ int linux_msgctl(
+ l_int msqid,
+ l_int cmd,
+ struct l_msqid_ds *buf
+ );
+ }
+305 AUE_NULL STD {
+ int linux_shmat(
+ l_int shmid,
+ char *shmaddr,
+ l_int shmflg
+ );
+ }
+306 AUE_NULL STD {
+ int linux_shmdt(
+ char *shmaddr
+ );
+ }
+307 AUE_NULL STD {
+ int linux_shmget(
+ l_key_t key,
+ l_size_t size,
+ l_int shmflg
+ );
+ }
+308 AUE_NULL STD {
+ int linux_shmctl(
+ l_int shmid,
+ l_int cmd,
+ struct l_shmid_ds *buf
+ );
+ }
+; linux 2.6.11:
+309 AUE_NULL STD {
+ int linux_add_key(void);
+ }
+310 AUE_NULL STD {
+ int linux_request_key(void);
+ }
+311 AUE_NULL STD {
+ int linux_keyctl(void);
+ }
+312 AUE_NULL UNIMPL semtimedop
+313 AUE_NULL UNIMPL ; was vserver
+
+; linux 2.6.13:
+314 AUE_NULL STD {
+ int linux_ioprio_set(void);
+ }
+315 AUE_NULL STD {
+ int linux_ioprio_get(void);
+ }
+316 AUE_NULL STD {
+ int linux_inotify_init(void);
+ }
+317 AUE_NULL STD {
+ int linux_inotify_add_watch(void);
+ }
+318 AUE_NULL STD {
+ int linux_inotify_rm_watch(void);
+ }
+
+319 AUE_NULL STD {
+ int linux_mbind(void);
+ }
+320 AUE_NULL STD {
+ int linux_get_mempolicy(void);
+ }
+321 AUE_NULL STD {
+ int linux_set_mempolicy(void);
+ }
+
+; linux 2.6.16:
+322 AUE_OPEN_RWTC STD {
+ int linux_openat(
+ l_int dfd,
+ const char *filename,
+ l_int flags,
+ l_int mode
+ );
+ }
+323 AUE_MKDIRAT STD {
+ int linux_mkdirat(
+ l_int dfd,
+ const char *pathname,
+ l_int mode
+ );
+ }
+324 AUE_MKNODAT STD {
+ int linux_mknodat(
+ l_int dfd,
+ const char *filename,
+ l_int mode,
+ l_uint dev
+ );
+ }
+325 AUE_FCHOWNAT STD {
+ int linux_fchownat(
+ l_int dfd,
+ const char *filename,
+ l_uid16_t uid,
+ l_gid16_t gid,
+ l_int flag
+ );
+ }
+326 AUE_FUTIMESAT STD {
+ int linux_futimesat(
+ l_int dfd,
+ char *filename,
+ struct l_timeval *utimes
+ );
+ }
+327 AUE_FSTATAT STD {
+ int linux_fstatat64(
+ l_int dfd,
+ char *pathname,
+ struct l_stat64 *statbuf,
+ l_int flag
+ );
+ }
+328 AUE_UNLINKAT STD {
+ int linux_unlinkat(
+ l_int dfd,
+ const char *pathname,
+ l_int flag
+ );
+ }
+329 AUE_RENAMEAT STD {
+ int linux_renameat(
+ l_int olddfd,
+ const char *oldname,
+ l_int newdfd,
+ const char *newname
+ );
+ }
+330 AUE_LINKAT STD {
+ int linux_linkat(
+ l_int olddfd,
+ const char *oldname,
+ l_int newdfd,
+ const char *newname,
+ l_int flag
+ );
+ }
+331 AUE_SYMLINKAT STD {
+ int linux_symlinkat(
+ const char *oldname,
+ l_int newdfd,
+ const char *newname
+ );
+ }
+332 AUE_READLINKAT STD {
+ int linux_readlinkat(
+ l_int dfd,
+ const char *path,
+ char *buf,
+ l_int bufsiz
+ );
+ }
+333 AUE_FCHMODAT STD {
+ int linux_fchmodat(
+ l_int dfd,
+ const char *filename,
+ l_mode_t mode
+ );
+ }
+334 AUE_FACCESSAT STD {
+ int linux_faccessat(
+ l_int dfd,
+ const char *filename,
+ l_int amode
+ );
+ }
+335 AUE_SELECT STD {
+ int linux_pselect6(
+ l_int nfds,
+ l_fd_set *readfds,
+ l_fd_set *writefds,
+ l_fd_set *exceptfds,
+ struct l_timespec *tsp,
+ l_uintptr_t *sig
+ );
+ }
+336 AUE_POLL STD {
+ int linux_ppoll(
+ struct pollfd *fds,
+ uint32_t nfds,
+ struct l_timespec *tsp,
+ l_sigset_t *sset,
+ l_size_t ssize
+ );
+ }
+337 AUE_NULL STD {
+ int linux_unshare(void);
+ }
+; linux 2.6.17:
+338 AUE_NULL STD {
+ int linux_set_robust_list(
+ struct linux_robust_list_head *head,
+ l_size_t len
+ );
+ }
+339 AUE_NULL STD {
+ int linux_get_robust_list(
+ l_int pid,
+ struct linux_robust_list_head **head,
+ l_size_t *len
+ );
+ }
+340 AUE_NULL STD {
+ int linux_splice(void);
+ }
+341 AUE_NULL STD {
+ int linux_sync_file_range(
+ l_int fd,
+ l_loff_t offset,
+ l_loff_t nbytes,
+ unsigned int flags
+ );
+ }
+342 AUE_NULL STD {
+ int linux_tee(void);
+ }
+343 AUE_NULL STD {
+ int linux_vmsplice(void);
+ }
+; linux 2.6.18:
+344 AUE_NULL STD {
+ int linux_move_pages(void);
+ }
+; linux 2.6.19:
+345 AUE_NULL STD {
+ int linux_getcpu(
+ l_uint *cpu,
+ l_uint *node,
+ void *cache
+ );
+ }
+346 AUE_NULL STD {
+ int linux_epoll_pwait(
+ l_int epfd,
+ struct epoll_event *events,
+ l_int maxevents,
+ l_int timeout,
+ l_sigset_t *mask
+ );
+ }
+; linux 2.6.22:
+347 AUE_NULL STD {
+ int linux_kexec_load(void);
+ }
+348 AUE_FUTIMESAT STD {
+ int linux_utimensat(
+ l_int dfd,
+ const char *pathname,
+ const struct l_timespec *times,
+ l_int flags
+ );
+ }
+349 AUE_NULL STD {
+ int linux_signalfd(void);
+ }
+350 AUE_NULL STD {
+ int linux_timerfd_create(void);
+ }
+351 AUE_NULL STD {
+ int linux_eventfd(
+ l_uint initval
+ );
+ }
+; linux 2.6.23:
+352 AUE_NULL STD {
+ int linux_fallocate(
+ l_int fd,
+ l_int mode,
+ l_loff_t offset,
+ l_loff_t len
+ );
+ }
+; linux 2.6.25:
+353 AUE_NULL STD {
+ int linux_timerfd_settime(void);
+ }
+354 AUE_NULL STD {
+ int linux_timerfd_gettime(void);
+ }
+; linux 2.6.27:
+355 AUE_NULL STD {
+ int linux_signalfd4(void);
+ }
+356 AUE_NULL STD {
+ int linux_eventfd2(
+ l_uint initval,
+ l_int flags
+ );
+ }
+357 AUE_NULL STD {
+ int linux_epoll_create1(
+ l_int flags
+ );
+ }
+358 AUE_NULL STD {
+ int linux_dup3(
+ l_int oldfd,
+ l_int newfd,
+ l_int flags
+ );
+ }
+359 AUE_NULL STD {
+ int linux_pipe2(
+ l_int *pipefds,
+ l_int flags
+ );
+ }
+360 AUE_NULL STD {
+ int linux_inotify_init1(void);
+ }
+; linux 2.6.30:
+361 AUE_NULL STD {
+ int linux_preadv(void);
+ }
+362 AUE_NULL STD {
+ int linux_pwritev(void);
+ }
+; linux 2.6.31:
+363 AUE_NULL STD {
+ int linux_rt_tsigqueueinfo(void);
+ }
+364 AUE_NULL STD {
+ int linux_perf_event_open(void);
+ }
+; linux 2.6.33:
+365 AUE_NULL STD {
+ int linux_recvmmsg(
+ l_int s,
+ struct l_mmsghdr *msg,
+ l_uint vlen,
+ l_uint flags,
+ struct l_timespec *timeout
+ );
+ }
+366 AUE_ACCEPT STD {
+ int linux_accept4(
+ l_int s,
+ l_uintptr_t addr,
+ l_uintptr_t namelen,
+ int flags
+ );
+ }
+367 AUE_NULL STD {
+ int linux_fanotify_init(void);
+ }
+368 AUE_NULL STD {
+ int linux_fanotify_mark(void);
+ }
+; linux 2.6.36:
+369 AUE_NULL STD {
+ int linux_prlimit64(
+ l_pid_t pid,
+ l_uint resource,
+ struct rlimit *new,
+ struct rlimit *old);
+ }
+; later:
+370 AUE_NULL STD {
+ int linux_name_to_handle_at(void);
+ }
+371 AUE_NULL STD {
+ int linux_open_by_handle_at(void);
+ }
+372 AUE_NULL STD {
+ int linux_clock_adjtime(void);
+ }
+373 AUE_SYNC STD {
+ int linux_syncfs(
+ l_int fd);
+ }
+374 AUE_NULL STD {
+ int linux_sendmmsg(
+ l_int s,
+ struct l_mmsghdr *msg,
+ l_uint vlen,
+ l_uint flags
+ );
+ }
+375 AUE_NULL STD {
+ int linux_setns(void);
+ }
+376 AUE_NULL STD {
+ int linux_process_vm_readv(void);
+ }
+377 AUE_NULL STD {
+ int linux_process_vm_writev(void);
+ }
+378 AUE_NULL UNIMPL kcmp
+379 AUE_NULL UNIMPL finit_module
+
+380 AUE_NULL UNIMPL sys_sched_setattr
+381 AUE_NULL UNIMPL sys_sched_getattr
+382 AUE_NULL UNIMPL sys_renameat2
+383 AUE_NULL UNIMPL sys_seccomp
+384 AUE_NULL UNIMPL sys_getrandom
+385 AUE_NULL UNIMPL sys_memfd_create
+386 AUE_NULL UNIMPL sys_bpf
+387 AUE_NULL UNIMPL sys_execveat
+388 AUE_NULL UNIMPL sys_userfaultfd
+389 AUE_NULL UNIMPL sys_membarrier
+390 AUE_NULL UNIMPL sys_mlock2
+391 AUE_NULL UNIMPL sys_copy_file_range
+392 AUE_NULL UNIMPL sys_preadv2
+393 AUE_NULL UNIMPL sys_pwritev2
+394 AUE_NULL UNIMPL
+395 AUE_NULL UNIMPL
+396 AUE_NULL UNIMPL
+397 AUE_NULL UNIMPL
+398 AUE_NULL UNIMPL
+399 AUE_NULL UNIMPL
+
+; ARM specific
+; Effective number here is 0x000f0000
+; but we must not breaks syscall numbering
+
+400 AUE_NULL UNIMPL unimpl400
+401 AUE_NULL UNIMPL breakpoint
+402 AUE_NULL UNIMPL cacheflush
+403 AUE_NULL UNIMPL usr26
+404 AUE_NULL UNIMPL usr32
+405 AUE_NULL STD {
+ int linux_set_tls(
+ void* tls
+ );
+ }
+
+; please, keep this line at the end.
+406 AUE_NULL UNIMPL nosys
diff --git a/sys/arm/mv/a37x0_gpio.c b/sys/arm/mv/a37x0_gpio.c
new file mode 100644
index 000000000000..6e9613b1ef7d
--- /dev/null
+++ b/sys/arm/mv/a37x0_gpio.c
@@ -0,0 +1,342 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2018-2019, Rubicon Communications, LLC (Netgate)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <sys/gpio.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/gpio/gpiobusvar.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "gpio_if.h"
+#include "syscon_if.h"
+
+struct a37x0_gpio_softc {
+ device_t sc_busdev;
+ int sc_type;
+ uint32_t sc_max_pins;
+ uint32_t sc_npins;
+ struct syscon *syscon;
+};
+
+/* Memory regions. */
+#define A37X0_GPIO 0
+#define A37X0_INTR 1
+
+/* North Bridge / South Bridge. */
+#define A37X0_NB_GPIO 1
+#define A37X0_SB_GPIO 2
+
+#define A37X0_GPIO_WRITE(_sc, _off, _val) \
+ SYSCON_WRITE_4((_sc)->syscon, (_off), (_val))
+#define A37X0_GPIO_READ(_sc, _off) \
+ SYSCON_READ_4((_sc)->syscon, (_off))
+
+#define A37X0_GPIO_BIT(_p) (1U << ((_p) % 32))
+#define A37X0_GPIO_OUT_EN(_p) (0x0 + ((_p) / 32) * 4)
+#define A37X0_GPIO_LATCH(_p) (0x8 + ((_p) / 32) * 4)
+#define A37X0_GPIO_INPUT(_p) (0x10 + ((_p) / 32) * 4)
+#define A37X0_GPIO_OUTPUT(_p) (0x18 + ((_p) / 32) * 4)
+#define A37X0_GPIO_SEL 0x30
+
+static struct ofw_compat_data compat_data[] = {
+ { "marvell,armada3710-nb-pinctrl", A37X0_NB_GPIO },
+ { "marvell,armada3710-sb-pinctrl", A37X0_SB_GPIO },
+ { NULL, 0 }
+};
+
+static phandle_t
+a37x0_gpio_get_node(device_t bus, device_t dev)
+{
+
+ return (ofw_bus_get_node(bus));
+}
+
+static device_t
+a37x0_gpio_get_bus(device_t dev)
+{
+ struct a37x0_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ return (sc->sc_busdev);
+}
+
+static int
+a37x0_gpio_pin_max(device_t dev, int *maxpin)
+{
+ struct a37x0_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+ *maxpin = sc->sc_npins - 1;
+
+ return (0);
+}
+
+static int
+a37x0_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
+{
+ struct a37x0_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->sc_npins)
+ return (EINVAL);
+ snprintf(name, GPIOMAXNAME, "pin %d", pin);
+
+ return (0);
+}
+
+static int
+a37x0_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
+{
+ struct a37x0_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->sc_npins)
+ return (EINVAL);
+ *caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
+
+ return (0);
+}
+
+static int
+a37x0_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
+{
+ struct a37x0_gpio_softc *sc;
+ uint32_t reg;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->sc_npins)
+ return (EINVAL);
+ reg = A37X0_GPIO_READ(sc, A37X0_GPIO_OUT_EN(pin));
+ if ((reg & A37X0_GPIO_BIT(pin)) != 0)
+ *flags = GPIO_PIN_OUTPUT;
+ else
+ *flags = GPIO_PIN_INPUT;
+
+ return (0);
+}
+
+static int
+a37x0_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
+{
+ struct a37x0_gpio_softc *sc;
+ uint32_t reg;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->sc_npins)
+ return (EINVAL);
+
+ reg = A37X0_GPIO_READ(sc, A37X0_GPIO_OUT_EN(pin));
+ if (flags & GPIO_PIN_OUTPUT)
+ reg |= A37X0_GPIO_BIT(pin);
+ else
+ reg &= ~A37X0_GPIO_BIT(pin);
+ A37X0_GPIO_WRITE(sc, A37X0_GPIO_OUT_EN(pin), reg);
+
+ return (0);
+}
+
+static int
+a37x0_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
+{
+ struct a37x0_gpio_softc *sc;
+ uint32_t reg;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->sc_npins)
+ return (EINVAL);
+
+ reg = A37X0_GPIO_READ(sc, A37X0_GPIO_OUT_EN(pin));
+ if ((reg & A37X0_GPIO_BIT(pin)) != 0)
+ reg = A37X0_GPIO_READ(sc, A37X0_GPIO_OUTPUT(pin));
+ else
+ reg = A37X0_GPIO_READ(sc, A37X0_GPIO_INPUT(pin));
+ *val = ((reg & A37X0_GPIO_BIT(pin)) != 0) ? 1 : 0;
+
+ return (0);
+}
+
+static int
+a37x0_gpio_pin_set(device_t dev, uint32_t pin, unsigned int val)
+{
+ struct a37x0_gpio_softc *sc;
+ uint32_t reg;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->sc_npins)
+ return (EINVAL);
+
+ reg = A37X0_GPIO_READ(sc, A37X0_GPIO_OUTPUT(pin));
+ if (val != 0)
+ reg |= A37X0_GPIO_BIT(pin);
+ else
+ reg &= ~A37X0_GPIO_BIT(pin);
+ A37X0_GPIO_WRITE(sc, A37X0_GPIO_OUTPUT(pin), reg);
+
+ return (0);
+}
+
+static int
+a37x0_gpio_pin_toggle(device_t dev, uint32_t pin)
+{
+ struct a37x0_gpio_softc *sc;
+ uint32_t reg;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->sc_npins)
+ return (EINVAL);
+
+ reg = A37X0_GPIO_READ(sc, A37X0_GPIO_OUT_EN(pin));
+ if ((reg & A37X0_GPIO_BIT(pin)) == 0)
+ return (EINVAL);
+ reg = A37X0_GPIO_READ(sc, A37X0_GPIO_OUTPUT(pin));
+ reg ^= A37X0_GPIO_BIT(pin);
+ A37X0_GPIO_WRITE(sc, A37X0_GPIO_OUTPUT(pin), reg);
+
+ return (0);
+}
+
+static int
+a37x0_gpio_probe(device_t dev)
+{
+ const char *desc;
+ struct a37x0_gpio_softc *sc;
+
+ if (!OF_hasprop(ofw_bus_get_node(dev), "gpio-controller"))
+ return (ENXIO);
+
+ sc = device_get_softc(dev);
+ sc->sc_type = ofw_bus_search_compatible(
+ device_get_parent(dev), compat_data)->ocd_data;
+ switch (sc->sc_type) {
+ case A37X0_NB_GPIO:
+ sc->sc_max_pins = 36;
+ desc = "Armada 37x0 North Bridge GPIO Controller";
+ break;
+ case A37X0_SB_GPIO:
+ sc->sc_max_pins = 30;
+ desc = "Armada 37x0 South Bridge GPIO Controller";
+ break;
+ default:
+ return (ENXIO);
+ }
+ device_set_desc(dev, desc);
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+a37x0_gpio_attach(device_t dev)
+{
+ int err, ncells;
+ pcell_t *ranges;
+ struct a37x0_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ err = syscon_get_handle_default(dev, &sc->syscon);
+ if (err != 0) {
+ device_printf(dev, "Cannot get syscon handle from parent\n");
+ return (ENXIO);
+ }
+
+ /* Read and verify the "gpio-ranges" property. */
+ ncells = OF_getencprop_alloc(ofw_bus_get_node(dev), "gpio-ranges",
+ (void **)&ranges);
+ if (ncells == -1)
+ return (ENXIO);
+ if (ncells != sizeof(*ranges) * 4 || ranges[1] != 0 || ranges[2] != 0) {
+ OF_prop_free(ranges);
+ return (ENXIO);
+ }
+ sc->sc_npins = ranges[3];
+ OF_prop_free(ranges);
+
+ /* Check the number of pins in the DTS vs HW capabilities. */
+ if (sc->sc_npins > sc->sc_max_pins)
+ return (ENXIO);
+
+ sc->sc_busdev = gpiobus_attach_bus(dev);
+ if (sc->sc_busdev == NULL)
+ return (ENXIO);
+
+ return (0);
+}
+
+static int
+a37x0_gpio_detach(device_t dev)
+{
+
+ return (EBUSY);
+}
+
+static device_method_t a37x0_gpio_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, a37x0_gpio_probe),
+ DEVMETHOD(device_attach, a37x0_gpio_attach),
+ DEVMETHOD(device_detach, a37x0_gpio_detach),
+
+ /* GPIO interface */
+ DEVMETHOD(gpio_get_bus, a37x0_gpio_get_bus),
+ DEVMETHOD(gpio_pin_max, a37x0_gpio_pin_max),
+ DEVMETHOD(gpio_pin_getname, a37x0_gpio_pin_getname),
+ DEVMETHOD(gpio_pin_getcaps, a37x0_gpio_pin_getcaps),
+ DEVMETHOD(gpio_pin_getflags, a37x0_gpio_pin_getflags),
+ DEVMETHOD(gpio_pin_setflags, a37x0_gpio_pin_setflags),
+ DEVMETHOD(gpio_pin_get, a37x0_gpio_pin_get),
+ DEVMETHOD(gpio_pin_set, a37x0_gpio_pin_set),
+ DEVMETHOD(gpio_pin_toggle, a37x0_gpio_pin_toggle),
+
+ /* ofw_bus interface */
+ DEVMETHOD(ofw_bus_get_node, a37x0_gpio_get_node),
+
+ DEVMETHOD_END
+};
+
+static devclass_t a37x0_gpio_devclass;
+static driver_t a37x0_gpio_driver = {
+ "gpio",
+ a37x0_gpio_methods,
+ sizeof(struct a37x0_gpio_softc),
+};
+
+EARLY_DRIVER_MODULE(a37x0_gpio, simple_mfd, a37x0_gpio_driver,
+ a37x0_gpio_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LAST);
diff --git a/sys/arm/mv/a37x0_iic.c b/sys/arm/mv/a37x0_iic.c
new file mode 100644
index 000000000000..041b2b1678dd
--- /dev/null
+++ b/sys/arm/mv/a37x0_iic.c
@@ -0,0 +1,484 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2018, 2019 Rubicon Communications, LLC (Netgate)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Driver for Armada 37x0 i2c controller.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/bus.h>
+#include <machine/resource.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+
+#include <dev/iicbus/iicbus.h>
+#include <dev/iicbus/iiconf.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/mv/a37x0_iicreg.h>
+
+#include "iicbus_if.h"
+
+struct a37x0_iic_softc {
+ boolean_t sc_fast_mode;
+ bus_space_tag_t sc_bst;
+ bus_space_handle_t sc_bsh;
+ device_t sc_dev;
+ device_t sc_iicbus;
+ struct mtx sc_mtx;
+ struct resource *sc_mem_res;
+ struct resource *sc_irq_res;
+ void *sc_intrhand;
+};
+
+#define A37X0_IIC_WRITE(_sc, _off, _val) \
+ bus_space_write_4((_sc)->sc_bst, (_sc)->sc_bsh, _off, _val)
+#define A37X0_IIC_READ(_sc, _off) \
+ bus_space_read_4((_sc)->sc_bst, (_sc)->sc_bsh, _off)
+#define A37X0_IIC_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define A37X0_IIC_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+
+static struct ofw_compat_data compat_data[] = {
+ { "marvell,armada-3700-i2c", 1 },
+ { NULL, 0 }
+};
+
+#undef A37x0_IIC_DEBUG
+
+static void a37x0_iic_intr(void *);
+static int a37x0_iic_detach(device_t);
+
+static void
+a37x0_iic_rmw(struct a37x0_iic_softc *sc, uint32_t off, uint32_t mask,
+ uint32_t value)
+{
+ uint32_t reg;
+
+ mtx_assert(&sc->sc_mtx, MA_OWNED);
+ reg = A37X0_IIC_READ(sc, off);
+ reg &= ~mask;
+ reg |= value;
+ A37X0_IIC_WRITE(sc, off, reg);
+}
+
+static int
+a37x0_iic_wait_clear(struct a37x0_iic_softc *sc, uint32_t mask)
+{
+ int timeout;
+ uint32_t status;
+
+ mtx_assert(&sc->sc_mtx, MA_OWNED);
+ timeout = 1000;
+ do {
+ DELAY(10);
+ status = A37X0_IIC_READ(sc, A37X0_IIC_ISR);
+ if (--timeout == 0)
+ return (0);
+ } while ((status & mask) != 0);
+
+ return (1);
+}
+
+static int
+a37x0_iic_wait_set(struct a37x0_iic_softc *sc, uint32_t mask)
+{
+ int timeout;
+ uint32_t status;
+
+ mtx_assert(&sc->sc_mtx, MA_OWNED);
+ timeout = 1000;
+ do {
+ DELAY(10);
+ status = A37X0_IIC_READ(sc, A37X0_IIC_ISR);
+ if (--timeout == 0)
+ return (0);
+ } while ((status & mask) != mask);
+
+ return (1);
+}
+
+#ifdef A37x0_IIC_DEBUG
+static void
+a37x0_iic_regdump(struct a37x0_iic_softc *sc)
+{
+
+ mtx_assert(&sc->sc_mtx, MA_OWNED);
+ printf("%s: IBMR: %#x\n", __func__, A37X0_IIC_READ(sc, A37X0_IIC_IBMR));
+ printf("%s: ICR: %#x\n", __func__, A37X0_IIC_READ(sc, A37X0_IIC_ICR));
+ printf("%s: ISR: %#x\n", __func__, A37X0_IIC_READ(sc, A37X0_IIC_ISR));
+}
+#endif
+
+static void
+a37x0_iic_reset(struct a37x0_iic_softc *sc)
+{
+ uint32_t mode, reg;
+
+ mtx_assert(&sc->sc_mtx, MA_OWNED);
+
+ /* Disable the controller. */
+ reg = A37X0_IIC_READ(sc, A37X0_IIC_ICR);
+ mode = reg & ICR_MODE_MASK;
+ A37X0_IIC_WRITE(sc, A37X0_IIC_ICR, reg & ~ICR_IUE);
+ A37X0_IIC_WRITE(sc, A37X0_IIC_ICR, reg | ICR_UR);
+ DELAY(100);
+ A37X0_IIC_WRITE(sc, A37X0_IIC_ICR, reg & ~ICR_IUE);
+
+ /* Enable the controller. */
+ reg = A37X0_IIC_READ(sc, A37X0_IIC_ICR);
+ reg |= mode | ICR_IUE | ICR_GCD | ICR_SCLE;
+ A37X0_IIC_WRITE(sc, A37X0_IIC_ICR, reg);
+#ifdef A37x0_IIC_DEBUG
+ a37x0_iic_regdump(sc);
+#endif
+}
+
+static int
+a37x0_iic_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Marvell Armada 37x0 IIC controller");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+a37x0_iic_attach(device_t dev)
+{
+ int rid;
+ phandle_t node;
+ struct a37x0_iic_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+
+ rid = 0;
+ sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (!sc->sc_mem_res) {
+ device_printf(dev, "cannot allocate memory window\n");
+ return (ENXIO);
+ }
+
+ sc->sc_bst = rman_get_bustag(sc->sc_mem_res);
+ sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res);
+
+ rid = 0;
+ sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE | RF_SHAREABLE);
+ if (!sc->sc_irq_res) {
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+ device_printf(dev, "cannot allocate interrupt\n");
+ return (ENXIO);
+ }
+
+ /* Hook up our interrupt handler. */
+ if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+ NULL, a37x0_iic_intr, sc, &sc->sc_intrhand)) {
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+ device_printf(dev, "cannot setup the interrupt handler\n");
+ return (ENXIO);
+ }
+
+ mtx_init(&sc->sc_mtx, "a37x0_iic", NULL, MTX_DEF);
+
+ node = ofw_bus_get_node(dev);
+ if (OF_hasprop(node, "mrvl,i2c-fast-mode"))
+ sc->sc_fast_mode = true;
+
+ /* Enable the controller. */
+ A37X0_IIC_LOCK(sc);
+ a37x0_iic_reset(sc);
+ A37X0_IIC_UNLOCK(sc);
+
+ sc->sc_iicbus = device_add_child(dev, "iicbus", -1);
+ if (sc->sc_iicbus == NULL) {
+ a37x0_iic_detach(dev);
+ return (ENXIO);
+ }
+
+ /* Probe and attach the iicbus. */
+ return (bus_generic_attach(dev));
+}
+
+static int
+a37x0_iic_detach(device_t dev)
+{
+ struct a37x0_iic_softc *sc;
+
+ bus_generic_detach(dev);
+
+ sc = device_get_softc(dev);
+ if (sc->sc_iicbus != NULL)
+ device_delete_child(dev, sc->sc_iicbus);
+ mtx_destroy(&sc->sc_mtx);
+ if (sc->sc_intrhand)
+ bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand);
+ if (sc->sc_irq_res)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
+ if (sc->sc_mem_res)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+
+ return (0);
+}
+
+static void
+a37x0_iic_intr(void *arg)
+{
+ struct a37x0_iic_softc *sc;
+ uint32_t status;
+
+ /* Not used, the interrupts are not enabled. */
+ sc = (struct a37x0_iic_softc *)arg;
+ A37X0_IIC_LOCK(sc);
+ status = A37X0_IIC_READ(sc, A37X0_IIC_ISR);
+#ifdef A37x0_IIC_DEBUG
+ a37x0_iic_regdump(sc);
+#endif
+
+ /* Clear pending interrrupts. */
+ A37X0_IIC_WRITE(sc, A37X0_IIC_ISR, status);
+ A37X0_IIC_UNLOCK(sc);
+}
+
+static int
+a37x0_iic_stop(device_t dev)
+{
+ struct a37x0_iic_softc *sc;
+ uint32_t reg;
+
+ sc = device_get_softc(dev);
+ A37X0_IIC_LOCK(sc);
+ /* Clear the STOP condition. */
+ reg = A37X0_IIC_READ(sc, A37X0_IIC_ICR);
+ if (reg & (ICR_ACKNAK | ICR_STOP)) {
+ reg &= ~(ICR_START | ICR_ACKNAK | ICR_STOP);
+ A37X0_IIC_WRITE(sc, A37X0_IIC_ICR, reg);
+ }
+ /* Clear interrupts. */
+ reg = A37X0_IIC_READ(sc, A37X0_IIC_ISR);
+ A37X0_IIC_WRITE(sc, A37X0_IIC_ISR, reg);
+ A37X0_IIC_UNLOCK(sc);
+
+ return (IIC_NOERR);
+}
+
+static int
+a37x0_iic_start(device_t dev, u_char slave, int timeout)
+{
+ int rv;
+ struct a37x0_iic_softc *sc;
+ uint32_t reg, status;
+
+ sc = device_get_softc(dev);
+ A37X0_IIC_LOCK(sc);
+
+ /* Wait for the bus to be free before start a transaction. */
+ if (a37x0_iic_wait_clear(sc, ISR_IBB) == 0) {
+ A37X0_IIC_UNLOCK(sc);
+ return (IIC_ETIMEOUT);
+ }
+
+ /* Write the slave address. */
+ A37X0_IIC_WRITE(sc, A37X0_IIC_IDBR, slave);
+
+ /* Send Start condition (with slave address). */
+ reg = A37X0_IIC_READ(sc, A37X0_IIC_ICR);
+ reg &= ~(ICR_STOP | ICR_ACKNAK);
+ A37X0_IIC_WRITE(sc, A37X0_IIC_ICR, reg | ICR_START | ICR_TB);
+
+ rv = IIC_NOERR;
+ if (a37x0_iic_wait_set(sc, ISR_ITE) == 0)
+ rv = IIC_ETIMEOUT;
+ if (rv == IIC_NOERR) {
+ status = A37X0_IIC_READ(sc, A37X0_IIC_ISR);
+ A37X0_IIC_WRITE(sc, A37X0_IIC_ISR, status | ISR_ITE);
+ if (a37x0_iic_wait_clear(sc, ISR_ACKNAK) == 0)
+ rv = IIC_ENOACK;
+ }
+
+ A37X0_IIC_UNLOCK(sc);
+ if (rv != IIC_NOERR)
+ a37x0_iic_stop(dev);
+
+ return (rv);
+}
+
+static int
+a37x0_iic_bus_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
+{
+ struct a37x0_iic_softc *sc;
+ uint32_t busfreq;
+
+ sc = device_get_softc(dev);
+ A37X0_IIC_LOCK(sc);
+ a37x0_iic_reset(sc);
+ if (sc->sc_iicbus == NULL)
+ busfreq = 100000;
+ else
+ busfreq = IICBUS_GET_FREQUENCY(sc->sc_iicbus, speed);
+ a37x0_iic_rmw(sc, A37X0_IIC_ICR, ICR_MODE_MASK,
+ (busfreq > 100000) ? ICR_FAST_MODE : 0);
+ A37X0_IIC_UNLOCK(sc);
+
+ return (IIC_ENOADDR);
+}
+
+static int
+a37x0_iic_read(device_t dev, char *buf, int len, int *read, int last, int delay)
+{
+ int rv;
+ struct a37x0_iic_softc *sc;
+ uint32_t reg, status;
+
+ sc = device_get_softc(dev);
+ A37X0_IIC_LOCK(sc);
+ reg = A37X0_IIC_READ(sc, A37X0_IIC_ISR);
+ if ((reg & (ISR_UB | ISR_IBB)) != ISR_UB) {
+ A37X0_IIC_UNLOCK(sc);
+ return (IIC_EBUSERR);
+ }
+
+ *read = 0;
+ rv = IIC_NOERR;
+ while (*read < len) {
+ reg = A37X0_IIC_READ(sc, A37X0_IIC_ICR);
+ reg &= ~(ICR_START | ICR_STOP | ICR_ACKNAK);
+ if (*read == len - 1)
+ reg |= ICR_ACKNAK | ICR_STOP;
+ A37X0_IIC_WRITE(sc, A37X0_IIC_ICR, reg | ICR_TB);
+ if (a37x0_iic_wait_set(sc, ISR_IRF) == 0) {
+ rv = IIC_ETIMEOUT;
+ break;
+ }
+ *buf++ = A37X0_IIC_READ(sc, A37X0_IIC_IDBR);
+ (*read)++;
+ status = A37X0_IIC_READ(sc, A37X0_IIC_ISR);
+ A37X0_IIC_WRITE(sc, A37X0_IIC_ISR, status | ISR_IRF);
+ }
+ A37X0_IIC_UNLOCK(sc);
+
+ return (rv);
+}
+
+static int
+a37x0_iic_write(device_t dev, const char *buf, int len, int *sent, int timeout)
+{
+ int rv;
+ struct a37x0_iic_softc *sc;
+ uint32_t reg, status;
+
+ sc = device_get_softc(dev);
+ A37X0_IIC_LOCK(sc);
+ reg = A37X0_IIC_READ(sc, A37X0_IIC_ISR);
+ if ((reg & (ISR_UB | ISR_IBB)) != ISR_UB) {
+ A37X0_IIC_UNLOCK(sc);
+ return (IIC_EBUSERR);
+ }
+
+ rv = IIC_NOERR;
+ *sent = 0;
+ while (*sent < len) {
+ A37X0_IIC_WRITE(sc, A37X0_IIC_IDBR, *buf++);
+ reg = A37X0_IIC_READ(sc, A37X0_IIC_ICR);
+ reg &= ~(ICR_START | ICR_STOP | ICR_ACKNAK);
+ if (*sent == len - 1)
+ reg |= ICR_STOP;
+ A37X0_IIC_WRITE(sc, A37X0_IIC_ICR, reg | ICR_TB);
+ if (a37x0_iic_wait_set(sc, ISR_ITE) == 0) {
+ rv = IIC_ETIMEOUT;
+ break;
+ }
+ (*sent)++;
+ status = A37X0_IIC_READ(sc, A37X0_IIC_ISR);
+ A37X0_IIC_WRITE(sc, A37X0_IIC_ISR, status | ISR_ITE);
+ if (a37x0_iic_wait_clear(sc, ISR_ACKNAK) == 0) {
+ rv = IIC_ENOACK;
+ break;
+ }
+ }
+ A37X0_IIC_UNLOCK(sc);
+
+ return (rv);
+}
+
+static phandle_t
+a37x0_iic_get_node(device_t bus, device_t dev)
+{
+
+ return (ofw_bus_get_node(bus));
+}
+
+static device_method_t a37x0_iic_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, a37x0_iic_probe),
+ DEVMETHOD(device_attach, a37x0_iic_attach),
+ DEVMETHOD(device_detach, a37x0_iic_detach),
+
+ /* iicbus interface */
+ DEVMETHOD(iicbus_reset, a37x0_iic_bus_reset),
+ DEVMETHOD(iicbus_callback, iicbus_null_callback),
+ DEVMETHOD(iicbus_transfer, iicbus_transfer_gen),
+ DEVMETHOD(iicbus_repeated_start, a37x0_iic_start),
+ DEVMETHOD(iicbus_start, a37x0_iic_start),
+ DEVMETHOD(iicbus_stop, a37x0_iic_stop),
+ DEVMETHOD(iicbus_read, a37x0_iic_read),
+ DEVMETHOD(iicbus_write, a37x0_iic_write),
+
+ /* ofw_bus interface */
+ DEVMETHOD(ofw_bus_get_node, a37x0_iic_get_node),
+
+ DEVMETHOD_END
+};
+
+static devclass_t a37x0_iic_devclass;
+
+static driver_t a37x0_iic_driver = {
+ "iichb",
+ a37x0_iic_methods,
+ sizeof(struct a37x0_iic_softc),
+};
+
+DRIVER_MODULE(iicbus, a37x0_iic, iicbus_driver, iicbus_devclass, 0, 0);
+DRIVER_MODULE(a37x0_iic, simplebus, a37x0_iic_driver, a37x0_iic_devclass, 0, 0);
diff --git a/sys/arm/mv/a37x0_iicreg.h b/sys/arm/mv/a37x0_iicreg.h
new file mode 100644
index 000000000000..37ea0ceefee4
--- /dev/null
+++ b/sys/arm/mv/a37x0_iicreg.h
@@ -0,0 +1,69 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2018, 2019 Rubicon Communications, LLC (Netgate)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _A37X0_IICREG_H_
+#define _A37X0_IICREG_H_
+
+#define A37X0_IIC_IBMR 0x00
+#define A37X0_IIC_IDBR 0x04
+#define A37X0_IIC_ICR 0x08
+#define ICR_START (1 << 0)
+#define ICR_STOP (1 << 1)
+#define ICR_ACKNAK (1 << 2)
+#define ICR_TB (1 << 3)
+#define ICR_MA (1 << 4)
+#define ICR_SCLE (1 << 5)
+#define ICR_IUE (1 << 6)
+#define ICR_GCD (1 << 7)
+#define ICR_ITEIE (1 << 8)
+#define ICR_IRFIE (1 << 9)
+#define ICR_BEIE (1 << 10)
+#define ICR_SSDIE (1 << 11)
+#define ICR_ALDIE (1 << 12)
+#define ICR_SADIE (1 << 13)
+#define ICR_UR (1 << 14)
+#define ICR_FAST_MODE (1 << 16)
+#define ICR_HIGH_SPEED (1 << 17)
+#define ICR_MODE_MASK (ICR_FAST_MODE | ICR_HIGH_SPEED)
+#define ICR_INIT \
+ (ICR_BEIE | ICR_IRFIE | ICR_ITEIE | ICR_GCD | ICR_SCLE)
+#define A37X0_IIC_ISR 0x0c
+#define ISR_RWM (1 << 0)
+#define ISR_ACKNAK (1 << 1)
+#define ISR_UB (1 << 2)
+#define ISR_IBB (1 << 3)
+#define ISR_SSD (1 << 4)
+#define ISR_ALD (1 << 5)
+#define ISR_ITE (1 << 6)
+#define ISR_IRF (1 << 7)
+#define ISR_GCAD (1 << 8)
+#define ISR_SAD (1 << 9)
+#define ISR_BED (1 << 10)
+
+#endif /* _A37X0_IICREG_H_ */
diff --git a/sys/arm/mv/a37x0_spi.c b/sys/arm/mv/a37x0_spi.c
new file mode 100644
index 000000000000..1fdb6735dd7f
--- /dev/null
+++ b/sys/arm/mv/a37x0_spi.c
@@ -0,0 +1,494 @@
+/*-
+ * Copyright (c) 2018, 2019 Rubicon Communications, LLC (Netgate)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/intr.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/spibus/spi.h>
+#include <dev/spibus/spibusvar.h>
+
+#include "spibus_if.h"
+
+struct a37x0_spi_softc {
+ device_t sc_dev;
+ struct mtx sc_mtx;
+ struct resource *sc_mem_res;
+ struct resource *sc_irq_res;
+ struct spi_command *sc_cmd;
+ bus_space_tag_t sc_bst;
+ bus_space_handle_t sc_bsh;
+ uint32_t sc_len;
+ uint32_t sc_maxfreq;
+ uint32_t sc_read;
+ uint32_t sc_flags;
+ uint32_t sc_written;
+ void *sc_intrhand;
+};
+
+#define A37X0_SPI_WRITE(_sc, _off, _val) \
+ bus_space_write_4((_sc)->sc_bst, (_sc)->sc_bsh, (_off), (_val))
+#define A37X0_SPI_READ(_sc, _off) \
+ bus_space_read_4((_sc)->sc_bst, (_sc)->sc_bsh, (_off))
+#define A37X0_SPI_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define A37X0_SPI_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+
+#define A37X0_SPI_BUSY (1 << 0)
+/*
+ * While the A3700 utils from Marvell usually sets the QSF clock to 200MHz,
+ * there is no guarantee that it is correct without the proper clock framework
+ * to retrieve the actual TBG and PLL settings.
+ */
+#define A37X0_SPI_CLOCK 200000000 /* QSF Clock 200MHz */
+
+#define A37X0_SPI_CONTROL 0x0
+#define A37X0_SPI_CS_SHIFT 16
+#define A37X0_SPI_CS_MASK (0xf << A37X0_SPI_CS_SHIFT)
+#define A37X0_SPI_CONF 0x4
+#define A37X0_SPI_WFIFO_THRS_SHIFT 28
+#define A37X0_SPI_RFIFO_THRS_SHIFT 24
+#define A37X0_SPI_AUTO_CS_EN (1 << 20)
+#define A37X0_SPI_DMA_WR_EN (1 << 19)
+#define A37X0_SPI_DMA_RD_EN (1 << 18)
+#define A37X0_SPI_FIFO_MODE (1 << 17)
+#define A37X0_SPI_SRST (1 << 16)
+#define A37X0_SPI_XFER_START (1 << 15)
+#define A37X0_SPI_XFER_STOP (1 << 14)
+#define A37X0_SPI_INSTR_PIN (1 << 13)
+#define A37X0_SPI_ADDR_PIN (1 << 12)
+#define A37X0_SPI_DATA_PIN_MASK 0x3
+#define A37X0_SPI_DATA_PIN_SHIFT 10
+#define A37X0_SPI_FIFO_FLUSH (1 << 9)
+#define A37X0_SPI_RW_EN (1 << 8)
+#define A37X0_SPI_CLK_POL (1 << 7)
+#define A37X0_SPI_CLK_PHASE (1 << 6)
+#define A37X0_SPI_BYTE_LEN (1 << 5)
+#define A37X0_SPI_PSC_MASK 0x1f
+#define A37X0_SPI_DATA_OUT 0x8
+#define A37X0_SPI_DATA_IN 0xc
+#define A37X0_SPI_INTR_STAT 0x28
+#define A37X0_SPI_INTR_MASK 0x2c
+#define A37X0_SPI_RDY (1 << 1)
+#define A37X0_SPI_XFER_DONE (1 << 0)
+
+static struct ofw_compat_data compat_data[] = {
+ { "marvell,armada-3700-spi", 1 },
+ { NULL, 0 }
+};
+
+static void a37x0_spi_intr(void *);
+
+static int
+a37x0_spi_wait(struct a37x0_spi_softc *sc, int timeout, uint32_t reg,
+ uint32_t mask)
+{
+ int i;
+
+ for (i = 0; i < timeout; i++) {
+ if ((A37X0_SPI_READ(sc, reg) & mask) == 0)
+ return (0);
+ DELAY(100);
+ }
+
+ return (ETIMEDOUT);
+}
+
+static int
+a37x0_spi_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+ device_set_desc(dev, "Armada 37x0 SPI controller");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+a37x0_spi_attach(device_t dev)
+{
+ int err, rid;
+ pcell_t maxfreq;
+ struct a37x0_spi_softc *sc;
+ uint32_t reg;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+
+ rid = 0;
+ sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (!sc->sc_mem_res) {
+ device_printf(dev, "cannot allocate memory window\n");
+ return (ENXIO);
+ }
+
+ sc->sc_bst = rman_get_bustag(sc->sc_mem_res);
+ sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res);
+
+ rid = 0;
+ sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (!sc->sc_irq_res) {
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+ device_printf(dev, "cannot allocate interrupt\n");
+ return (ENXIO);
+ }
+
+ /* Make sure that no CS is asserted. */
+ reg = A37X0_SPI_READ(sc, A37X0_SPI_CONTROL);
+ A37X0_SPI_WRITE(sc, A37X0_SPI_CONTROL, reg & ~A37X0_SPI_CS_MASK);
+
+ /* Reset FIFO. */
+ reg = A37X0_SPI_READ(sc, A37X0_SPI_CONF);
+ A37X0_SPI_WRITE(sc, A37X0_SPI_CONF, reg | A37X0_SPI_FIFO_FLUSH);
+ err = a37x0_spi_wait(sc, 20, A37X0_SPI_CONF, A37X0_SPI_FIFO_FLUSH);
+ if (err != 0) {
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+ device_printf(dev, "cannot flush the controller fifo.\n");
+ return (ENXIO);
+ }
+
+ /* Reset the Controller. */
+ reg = A37X0_SPI_READ(sc, A37X0_SPI_CONF);
+ A37X0_SPI_WRITE(sc, A37X0_SPI_CONF, reg | A37X0_SPI_SRST);
+ DELAY(1000);
+ /* Enable the single byte IO, disable FIFO. */
+ reg &= ~(A37X0_SPI_FIFO_MODE | A37X0_SPI_BYTE_LEN);
+ A37X0_SPI_WRITE(sc, A37X0_SPI_CONF, reg);
+
+ /* Disable and clear interrupts. */
+ A37X0_SPI_WRITE(sc, A37X0_SPI_INTR_MASK, 0);
+ reg = A37X0_SPI_READ(sc, A37X0_SPI_INTR_STAT);
+ A37X0_SPI_WRITE(sc, A37X0_SPI_INTR_STAT, reg);
+
+ /* Hook up our interrupt handler. */
+ if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+ NULL, a37x0_spi_intr, sc, &sc->sc_intrhand)) {
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+ device_printf(dev, "cannot setup the interrupt handler\n");
+ return (ENXIO);
+ }
+
+ mtx_init(&sc->sc_mtx, "a37x0_spi", NULL, MTX_DEF);
+
+ /* Read the controller max-frequency. */
+ if (OF_getencprop(ofw_bus_get_node(dev), "spi-max-frequency", &maxfreq,
+ sizeof(maxfreq)) == -1)
+ maxfreq = 0;
+ sc->sc_maxfreq = maxfreq;
+
+ device_add_child(dev, "spibus", -1);
+
+ /* Probe and attach the spibus when interrupts are available. */
+ return (bus_delayed_attach_children(dev));
+}
+
+static int
+a37x0_spi_detach(device_t dev)
+{
+ int err;
+ struct a37x0_spi_softc *sc;
+
+ if ((err = device_delete_children(dev)) != 0)
+ return (err);
+ sc = device_get_softc(dev);
+ mtx_destroy(&sc->sc_mtx);
+ if (sc->sc_intrhand)
+ bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand);
+ if (sc->sc_irq_res)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
+ if (sc->sc_mem_res)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+
+ return (0);
+}
+
+static __inline void
+a37x0_spi_rx_byte(struct a37x0_spi_softc *sc)
+{
+ struct spi_command *cmd;
+ uint32_t read;
+ uint8_t *p;
+
+ if (sc->sc_read == sc->sc_len)
+ return;
+ cmd = sc->sc_cmd;
+ p = (uint8_t *)cmd->rx_cmd;
+ read = sc->sc_read++;
+ if (read >= cmd->rx_cmd_sz) {
+ p = (uint8_t *)cmd->rx_data;
+ read -= cmd->rx_cmd_sz;
+ }
+ p[read] = A37X0_SPI_READ(sc, A37X0_SPI_DATA_IN) & 0xff;
+}
+
+static __inline void
+a37x0_spi_tx_byte(struct a37x0_spi_softc *sc)
+{
+ struct spi_command *cmd;
+ uint32_t written;
+ uint8_t *p;
+
+ if (sc->sc_written == sc->sc_len)
+ return;
+ cmd = sc->sc_cmd;
+ p = (uint8_t *)cmd->tx_cmd;
+ written = sc->sc_written++;
+ if (written >= cmd->tx_cmd_sz) {
+ p = (uint8_t *)cmd->tx_data;
+ written -= cmd->tx_cmd_sz;
+ }
+ A37X0_SPI_WRITE(sc, A37X0_SPI_DATA_OUT, p[written]);
+}
+
+static __inline void
+a37x0_spi_set_clock(struct a37x0_spi_softc *sc, uint32_t clock)
+{
+ uint32_t psc, reg;
+
+ if (sc->sc_maxfreq > 0 && clock > sc->sc_maxfreq)
+ clock = sc->sc_maxfreq;
+ psc = A37X0_SPI_CLOCK / clock;
+ if ((A37X0_SPI_CLOCK % clock) > 0)
+ psc++;
+ reg = A37X0_SPI_READ(sc, A37X0_SPI_CONF);
+ reg &= ~A37X0_SPI_PSC_MASK;
+ reg |= psc & A37X0_SPI_PSC_MASK;
+ A37X0_SPI_WRITE(sc, A37X0_SPI_CONF, reg);
+}
+
+static __inline void
+a37x0_spi_set_pins(struct a37x0_spi_softc *sc, uint32_t npins)
+{
+ uint32_t reg;
+
+ /* Sets single, dual or quad SPI mode. */
+ reg = A37X0_SPI_READ(sc, A37X0_SPI_CONF);
+ reg &= ~(A37X0_SPI_DATA_PIN_MASK << A37X0_SPI_DATA_PIN_SHIFT);
+ reg |= (npins / 2) << A37X0_SPI_DATA_PIN_SHIFT;
+ reg |= A37X0_SPI_INSTR_PIN | A37X0_SPI_ADDR_PIN;
+ A37X0_SPI_WRITE(sc, A37X0_SPI_CONF, reg);
+}
+
+static __inline void
+a37x0_spi_set_mode(struct a37x0_spi_softc *sc, uint32_t mode)
+{
+ uint32_t reg;
+
+ reg = A37X0_SPI_READ(sc, A37X0_SPI_CONF);
+ switch (mode) {
+ case 0:
+ reg &= ~(A37X0_SPI_CLK_PHASE | A37X0_SPI_CLK_POL);
+ break;
+ case 1:
+ reg &= ~A37X0_SPI_CLK_POL;
+ reg |= A37X0_SPI_CLK_PHASE;
+ break;
+ case 2:
+ reg &= ~A37X0_SPI_CLK_PHASE;
+ reg |= A37X0_SPI_CLK_POL;
+ break;
+ case 3:
+ reg |= (A37X0_SPI_CLK_PHASE | A37X0_SPI_CLK_POL);
+ break;
+ }
+ A37X0_SPI_WRITE(sc, A37X0_SPI_CONF, reg);
+}
+
+static void
+a37x0_spi_intr(void *arg)
+{
+ struct a37x0_spi_softc *sc;
+ uint32_t status;
+
+ sc = (struct a37x0_spi_softc *)arg;
+ A37X0_SPI_LOCK(sc);
+
+ /* Filter stray interrupts. */
+ if ((sc->sc_flags & A37X0_SPI_BUSY) == 0) {
+ A37X0_SPI_UNLOCK(sc);
+ return;
+ }
+
+ status = A37X0_SPI_READ(sc, A37X0_SPI_INTR_STAT);
+ if (status & A37X0_SPI_XFER_DONE)
+ a37x0_spi_rx_byte(sc);
+
+ /* Clear the interrupt status. */
+ A37X0_SPI_WRITE(sc, A37X0_SPI_INTR_STAT, status);
+
+ /* Check for end of transfer. */
+ if (sc->sc_written == sc->sc_len && sc->sc_read == sc->sc_len)
+ wakeup(sc->sc_dev);
+ else
+ a37x0_spi_tx_byte(sc);
+
+ A37X0_SPI_UNLOCK(sc);
+}
+
+static int
+a37x0_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
+{
+ int timeout;
+ struct a37x0_spi_softc *sc;
+ uint32_t clock, cs, mode, reg;
+
+ KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz,
+ ("TX/RX command sizes should be equal"));
+ KASSERT(cmd->tx_data_sz == cmd->rx_data_sz,
+ ("TX/RX data sizes should be equal"));
+
+ /* Get the proper data for this child. */
+ spibus_get_cs(child, &cs);
+ cs &= ~SPIBUS_CS_HIGH;
+ if (cs > 3) {
+ device_printf(dev,
+ "Invalid CS %d requested by %s\n", cs,
+ device_get_nameunit(child));
+ return (EINVAL);
+ }
+ spibus_get_clock(child, &clock);
+ if (clock == 0) {
+ device_printf(dev,
+ "Invalid clock %uHz requested by %s\n", clock,
+ device_get_nameunit(child));
+ return (EINVAL);
+ }
+ spibus_get_mode(child, &mode);
+ if (mode > 3) {
+ device_printf(dev,
+ "Invalid mode %u requested by %s\n", mode,
+ device_get_nameunit(child));
+ return (EINVAL);
+ }
+
+ sc = device_get_softc(dev);
+ A37X0_SPI_LOCK(sc);
+
+ /* Wait until the controller is free. */
+ while (sc->sc_flags & A37X0_SPI_BUSY)
+ mtx_sleep(dev, &sc->sc_mtx, 0, "a37x0_spi", 0);
+
+ /* Now we have control over SPI controller. */
+ sc->sc_flags = A37X0_SPI_BUSY;
+
+ /* Set transfer mode and clock. */
+ a37x0_spi_set_mode(sc, mode);
+ a37x0_spi_set_pins(sc, 1);
+ a37x0_spi_set_clock(sc, clock);
+
+ /* Set CS. */
+ A37X0_SPI_WRITE(sc, A37X0_SPI_CONTROL, 1 << (A37X0_SPI_CS_SHIFT + cs));
+
+ /* Save a pointer to the SPI command. */
+ sc->sc_cmd = cmd;
+ sc->sc_read = 0;
+ sc->sc_written = 0;
+ sc->sc_len = cmd->tx_cmd_sz + cmd->tx_data_sz;
+
+ /* Clear interrupts. */
+ reg = A37X0_SPI_READ(sc, A37X0_SPI_INTR_STAT);
+ A37X0_SPI_WRITE(sc, A37X0_SPI_INTR_STAT, reg);
+
+ while ((sc->sc_len - sc->sc_written) > 0) {
+ /*
+ * Write to start the transmission and read the byte
+ * back when ready.
+ */
+ a37x0_spi_tx_byte(sc);
+ timeout = 1000;
+ while (--timeout > 0) {
+ reg = A37X0_SPI_READ(sc, A37X0_SPI_CONTROL);
+ if (reg & A37X0_SPI_XFER_DONE)
+ break;
+ DELAY(1);
+ }
+ if (timeout == 0)
+ break;
+ a37x0_spi_rx_byte(sc);
+ }
+
+ /* Stop the controller. */
+ reg = A37X0_SPI_READ(sc, A37X0_SPI_CONTROL);
+ A37X0_SPI_WRITE(sc, A37X0_SPI_CONTROL, reg & ~A37X0_SPI_CS_MASK);
+ A37X0_SPI_WRITE(sc, A37X0_SPI_INTR_MASK, 0);
+
+ /* Release the controller and wakeup the next thread waiting for it. */
+ sc->sc_flags = 0;
+ wakeup_one(dev);
+ A37X0_SPI_UNLOCK(sc);
+
+ return ((timeout == 0) ? EIO : 0);
+}
+
+static phandle_t
+a37x0_spi_get_node(device_t bus, device_t dev)
+{
+
+ return (ofw_bus_get_node(bus));
+}
+
+static device_method_t a37x0_spi_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, a37x0_spi_probe),
+ DEVMETHOD(device_attach, a37x0_spi_attach),
+ DEVMETHOD(device_detach, a37x0_spi_detach),
+
+ /* SPI interface */
+ DEVMETHOD(spibus_transfer, a37x0_spi_transfer),
+
+ /* ofw_bus interface */
+ DEVMETHOD(ofw_bus_get_node, a37x0_spi_get_node),
+
+ DEVMETHOD_END
+};
+
+static devclass_t a37x0_spi_devclass;
+
+static driver_t a37x0_spi_driver = {
+ "spi",
+ a37x0_spi_methods,
+ sizeof(struct a37x0_spi_softc),
+};
+
+DRIVER_MODULE(a37x0_spi, simplebus, a37x0_spi_driver, a37x0_spi_devclass, 0, 0);
diff --git a/sys/arm/mv/armada/thermal.c b/sys/arm/mv/armada/thermal.c
new file mode 100644
index 000000000000..a2f6ed78c487
--- /dev/null
+++ b/sys/arm/mv/armada/thermal.c
@@ -0,0 +1,315 @@
+/*-
+ * Copyright (c) 2017 Semihalf.
+ * Copyright (c) 2017 Stormshield.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/rman.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/resource.h>
+
+#include <machine/fdt.h>
+
+#include <dev/ofw/ofw_bus_subr.h>
+
+#define READOUT_TO_C(temp) ((temp) / 1000)
+
+#define STAT_RID 0
+#define CTRL_RID 1
+
+#define TSEN_STAT_READOUT_VALID 0x1
+
+#define A380_TSEN_CTRL_RESET (1 << 8)
+
+struct armada_thermal_softc;
+
+typedef struct armada_thermal_data {
+ /* Initialize the sensor */
+ void (*tsen_init)(struct armada_thermal_softc *);
+
+ /* Test for a valid sensor value */
+ boolean_t (*is_valid)(struct armada_thermal_softc *);
+
+ /* Formula coefficients: temp = (b + m * reg) / div */
+ u_long coef_b;
+ u_long coef_m;
+ u_long coef_div;
+
+ boolean_t inverted;
+
+ /* Shift and mask to access the sensor temperature */
+ u_int temp_shift;
+ u_int temp_mask;
+ u_int is_valid_shift;
+} armada_tdata_t;
+
+static boolean_t armada_tsen_readout_valid(struct armada_thermal_softc *);
+static int armada_tsen_get_temp(struct armada_thermal_softc *, u_long *);
+static void armada380_tsen_init(struct armada_thermal_softc *);
+static void armada_temp_update(void *);
+
+static const armada_tdata_t armada380_tdata = {
+ .tsen_init = armada380_tsen_init,
+ .is_valid = armada_tsen_readout_valid,
+ .is_valid_shift = 10,
+ .temp_shift = 0,
+ .temp_mask = 0x3ff,
+ .coef_b = 1172499100UL,
+ .coef_m = 2000096UL,
+ .coef_div = 4201,
+ .inverted = TRUE,
+};
+
+static int armada_thermal_probe(device_t);
+static int armada_thermal_attach(device_t);
+static int armada_thermal_detach(device_t);
+
+static device_method_t armada_thermal_methods[] = {
+ DEVMETHOD(device_probe, armada_thermal_probe),
+ DEVMETHOD(device_attach, armada_thermal_attach),
+ DEVMETHOD(device_detach, armada_thermal_detach),
+
+ DEVMETHOD_END
+};
+
+struct armada_thermal_softc {
+ device_t dev;
+
+ struct resource *stat_res;
+ struct resource *ctrl_res;
+
+ struct callout temp_upd;
+ struct mtx temp_upd_mtx;
+
+ const armada_tdata_t *tdata;
+
+ u_long chip_temperature;
+};
+
+static driver_t armada_thermal_driver = {
+ "armada_thermal",
+ armada_thermal_methods,
+ sizeof(struct armada_thermal_softc)
+};
+
+static devclass_t armada_thermal_devclass;
+
+DRIVER_MODULE(armada_thermal, simplebus, armada_thermal_driver,
+ armada_thermal_devclass, 0, 0);
+DRIVER_MODULE(armada_thermal, ofwbus, armada_thermal_driver,
+ armada_thermal_devclass, 0, 0);
+
+static int
+armada_thermal_probe(device_t dev)
+{
+ struct armada_thermal_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_is_compatible(dev, "marvell,armada380-thermal")) {
+ device_set_desc(dev, "Armada380 Thermal Control");
+ sc->tdata = &armada380_tdata;
+
+ return (BUS_PROBE_DEFAULT);
+ }
+
+ return (ENXIO);
+}
+
+static int
+armada_thermal_attach(device_t dev)
+{
+ struct armada_thermal_softc *sc;
+ const armada_tdata_t *tdata;
+ struct sysctl_ctx_list *sctx;
+ struct sysctl_oid_list *schildren;
+ int timeout;
+ int rid;
+
+ sc = device_get_softc(dev);
+
+ /* Allocate CTRL and STAT register spaces */
+ rid = STAT_RID;
+ sc->stat_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &rid, RF_ACTIVE);
+ if (sc->stat_res == NULL) {
+ device_printf(dev,
+ "Could not allocate memory for the status register\n");
+ return (ENXIO);
+ }
+
+ rid = CTRL_RID;
+ sc->ctrl_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &rid, RF_ACTIVE);
+ if (sc->ctrl_res == NULL) {
+ device_printf(dev,
+ "Could not allocate memory for the control register\n");
+ bus_release_resource(dev, SYS_RES_MEMORY,
+ rman_get_rid(sc->stat_res), sc->stat_res);
+ sc->stat_res = NULL;
+ return (ENXIO);
+ }
+
+ /* Now initialize the sensor */
+ tdata = sc->tdata;
+ tdata->tsen_init(sc);
+ /* Set initial temperature value */
+ for (timeout = 1000; timeout > 0; timeout--) {
+ if (armada_tsen_get_temp(sc, &sc->chip_temperature) == 0)
+ break;
+ DELAY(10);
+ }
+ if (timeout <= 0) {
+ bus_release_resource(dev, SYS_RES_MEMORY,
+ rman_get_rid(sc->stat_res), sc->stat_res);
+ sc->stat_res = NULL;
+ bus_release_resource(dev, SYS_RES_MEMORY,
+ rman_get_rid(sc->ctrl_res), sc->ctrl_res);
+ sc->ctrl_res = NULL;
+ return (ENXIO);
+ }
+ /* Initialize mutex */
+ mtx_init(&sc->temp_upd_mtx, "Armada Thermal", NULL, MTX_DEF);
+ /* Set up the temperature update callout */
+ callout_init_mtx(&sc->temp_upd, &sc->temp_upd_mtx, 0);
+ /* Schedule callout */
+ callout_reset(&sc->temp_upd, hz, armada_temp_update, sc);
+
+ sctx = device_get_sysctl_ctx(dev);
+ schildren = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
+ SYSCTL_ADD_LONG(sctx, schildren, OID_AUTO, "temperature",
+ CTLFLAG_RD, &sc->chip_temperature, "SoC temperature");
+
+ return (0);
+}
+
+static int
+armada_thermal_detach(device_t dev)
+{
+ struct armada_thermal_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (!device_is_attached(dev))
+ return (0);
+
+ callout_drain(&sc->temp_upd);
+
+ sc->chip_temperature = 0;
+
+ bus_release_resource(dev, SYS_RES_MEMORY,
+ rman_get_rid(sc->stat_res), sc->stat_res);
+ sc->stat_res = NULL;
+
+ bus_release_resource(dev, SYS_RES_MEMORY,
+ rman_get_rid(sc->ctrl_res), sc->ctrl_res);
+ sc->ctrl_res = NULL;
+
+ return (0);
+}
+
+static boolean_t
+armada_tsen_readout_valid(struct armada_thermal_softc *sc)
+{
+ const armada_tdata_t *tdata;
+ uint32_t tsen_stat;
+ boolean_t is_valid;
+
+ tdata = sc->tdata;
+ tsen_stat = bus_read_4(sc->stat_res, 0);
+
+ tsen_stat >>= tdata->is_valid_shift;
+ is_valid = ((tsen_stat & TSEN_STAT_READOUT_VALID) != 0);
+
+ return (is_valid);
+}
+
+static int
+armada_tsen_get_temp(struct armada_thermal_softc *sc, u_long *temp)
+{
+ const armada_tdata_t *tdata;
+ uint32_t reg;
+ u_long tmp;
+ u_long m, b, div;
+
+ tdata = sc->tdata;
+ /* Check if the readout is valid */
+ if ((tdata->is_valid != NULL) && !tdata->is_valid(sc))
+ return (EIO);
+
+ reg = bus_read_4(sc->stat_res, 0);
+ reg = (reg >> tdata->temp_shift) & tdata->temp_mask;
+
+ /* Get formula coefficients */
+ b = tdata->coef_b;
+ m = tdata->coef_m;
+ div = tdata->coef_div;
+
+ if (tdata->inverted)
+ tmp = ((m * reg) - b) / div;
+ else
+ tmp = (b - (m * reg)) / div;
+
+ *temp = READOUT_TO_C(tmp);
+
+ return (0);
+}
+
+static void
+armada380_tsen_init(struct armada_thermal_softc *sc)
+{
+ uint32_t tsen_ctrl;
+
+ tsen_ctrl = bus_read_4(sc->ctrl_res, 0);
+ if ((tsen_ctrl & A380_TSEN_CTRL_RESET) == 0) {
+ tsen_ctrl |= A380_TSEN_CTRL_RESET;
+ bus_write_4(sc->ctrl_res, 0, tsen_ctrl);
+ DELAY(10000);
+ }
+}
+
+static void
+armada_temp_update(void *arg)
+{
+ struct armada_thermal_softc *sc;
+
+ sc = arg;
+ /* Update temperature value, keel old if the readout is not valid */
+ (void)armada_tsen_get_temp(sc, &sc->chip_temperature);
+
+ callout_reset(&sc->temp_upd, hz, armada_temp_update, sc);
+}
diff --git a/sys/arm/mv/armada/wdt.c b/sys/arm/mv/armada/wdt.c
new file mode 100644
index 000000000000..920ff4d506bb
--- /dev/null
+++ b/sys/arm/mv/armada/wdt.c
@@ -0,0 +1,378 @@
+/*-
+ * Copyright (c) 2006 Benno Rice.
+ * Copyright (C) 2007-2008 MARVELL INTERNATIONAL LTD.
+ * All rights reserved.
+ *
+ * Adapted to Marvell SoC by Semihalf.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * from: FreeBSD: //depot/projects/arm/src/sys/arm/xscale/pxa2x0/pxa2x0_timer.c, rev 1
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/eventhandler.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/kdb.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+#include <machine/bus.h>
+#include <machine/cpu.h>
+
+#include <arm/mv/mvreg.h>
+#include <arm/mv/mvvar.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#define INITIAL_TIMECOUNTER (0xffffffff)
+#define MAX_WATCHDOG_TICKS (0xffffffff)
+#define WD_RST_OUT_EN 0x00000002
+
+#define MV_CLOCK_SRC_ARMV7 25000000 /* Timers' 25MHz mode */
+
+struct mv_wdt_config {
+ enum soc_family wdt_soc;
+ uint32_t wdt_timer;
+ void (*wdt_enable)(void);
+ void (*wdt_disable)(void);
+ unsigned int wdt_clock_src;
+};
+
+static void mv_wdt_enable_armv5(void);
+static void mv_wdt_enable_armada_38x(void);
+static void mv_wdt_enable_armada_xp(void);
+
+static void mv_wdt_disable_armv5(void);
+static void mv_wdt_disable_armada_38x(void);
+static void mv_wdt_disable_armada_xp(void);
+
+static struct mv_wdt_config mv_wdt_armada_38x_config = {
+ .wdt_soc = MV_SOC_ARMADA_38X,
+ .wdt_timer = 4,
+ .wdt_enable = &mv_wdt_enable_armada_38x,
+ .wdt_disable = &mv_wdt_disable_armada_38x,
+ .wdt_clock_src = MV_CLOCK_SRC_ARMV7,
+};
+
+static struct mv_wdt_config mv_wdt_armada_xp_config = {
+ .wdt_soc = MV_SOC_ARMADA_XP,
+ .wdt_timer = 2,
+ .wdt_enable = &mv_wdt_enable_armada_xp,
+ .wdt_disable = &mv_wdt_disable_armada_xp,
+ .wdt_clock_src = MV_CLOCK_SRC_ARMV7,
+};
+
+static struct mv_wdt_config mv_wdt_armv5_config = {
+ .wdt_soc = MV_SOC_ARMV5,
+ .wdt_timer = 2,
+ .wdt_enable = &mv_wdt_enable_armv5,
+ .wdt_disable = &mv_wdt_disable_armv5,
+ .wdt_clock_src = 0,
+};
+
+struct mv_wdt_softc {
+ struct resource * wdt_res;
+ struct mtx wdt_mtx;
+ struct mv_wdt_config * wdt_config;
+};
+
+static struct resource_spec mv_wdt_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+static struct ofw_compat_data mv_wdt_compat[] = {
+ {"marvell,armada-380-wdt", (uintptr_t)&mv_wdt_armada_38x_config},
+ {"marvell,armada-xp-wdt", (uintptr_t)&mv_wdt_armada_xp_config},
+ {"marvell,orion-wdt", (uintptr_t)&mv_wdt_armv5_config},
+ {NULL, (uintptr_t)NULL}
+};
+
+static struct mv_wdt_softc *wdt_softc = NULL;
+int timers_initialized = 0;
+
+static int mv_wdt_probe(device_t);
+static int mv_wdt_attach(device_t);
+
+static uint32_t mv_get_timer_control(void);
+static void mv_set_timer_control(uint32_t);
+static void mv_set_timer(uint32_t, uint32_t);
+
+static void mv_watchdog_event(void *, unsigned int, int *);
+
+static device_method_t mv_wdt_methods[] = {
+ DEVMETHOD(device_probe, mv_wdt_probe),
+ DEVMETHOD(device_attach, mv_wdt_attach),
+ { 0, 0 }
+};
+
+static driver_t mv_wdt_driver = {
+ "wdt",
+ mv_wdt_methods,
+ sizeof(struct mv_wdt_softc),
+};
+
+static devclass_t mv_wdt_devclass;
+
+DRIVER_MODULE(wdt, simplebus, mv_wdt_driver, mv_wdt_devclass, 0, 0);
+static int
+mv_wdt_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_search_compatible(dev, mv_wdt_compat)->ocd_data)
+ return (ENXIO);
+
+ device_set_desc(dev, "Marvell Watchdog Timer");
+ return (0);
+}
+
+static int
+mv_wdt_attach(device_t dev)
+{
+ struct mv_wdt_softc *sc;
+ int error;
+
+ if (wdt_softc != NULL)
+ return (ENXIO);
+
+ sc = device_get_softc(dev);
+ wdt_softc = sc;
+
+ error = bus_alloc_resources(dev, mv_wdt_spec, &sc->wdt_res);
+ if (error) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ mtx_init(&sc->wdt_mtx, "watchdog", NULL, MTX_DEF);
+
+ sc->wdt_config = (struct mv_wdt_config *)
+ ofw_bus_search_compatible(dev, mv_wdt_compat)->ocd_data;
+
+ if (sc->wdt_config->wdt_clock_src == 0)
+ sc->wdt_config->wdt_clock_src = get_tclk();
+
+ if (wdt_softc->wdt_config->wdt_disable != NULL)
+ wdt_softc->wdt_config->wdt_disable();
+ EVENTHANDLER_REGISTER(watchdog_list, mv_watchdog_event, sc, 0);
+
+ return (0);
+}
+
+static __inline uint32_t
+mv_get_timer_control(void)
+{
+
+ return (bus_read_4(wdt_softc->wdt_res, CPU_TIMER_CONTROL));
+}
+
+static __inline void
+mv_set_timer_control(uint32_t val)
+{
+
+ bus_write_4(wdt_softc->wdt_res, CPU_TIMER_CONTROL, val);
+}
+
+static __inline void
+mv_set_timer(uint32_t timer, uint32_t val)
+{
+
+ bus_write_4(wdt_softc->wdt_res, CPU_TIMER0 + timer * 0x8, val);
+}
+static void
+mv_wdt_enable_armv5(void)
+{
+ uint32_t val, irq_cause, irq_mask;
+
+ irq_cause = read_cpu_ctrl(BRIDGE_IRQ_CAUSE);
+ irq_cause &= IRQ_TIMER_WD_CLR;
+ write_cpu_ctrl(BRIDGE_IRQ_CAUSE, irq_cause);
+
+ irq_mask = read_cpu_ctrl(BRIDGE_IRQ_MASK);
+ irq_mask |= IRQ_TIMER_WD_MASK;
+ write_cpu_ctrl(BRIDGE_IRQ_MASK, irq_mask);
+
+ val = read_cpu_ctrl(RSTOUTn_MASK);
+ val |= WD_RST_OUT_EN;
+ write_cpu_ctrl(RSTOUTn_MASK, val);
+
+ val = mv_get_timer_control();
+ val |= CPU_TIMER2_EN | CPU_TIMER2_AUTO;
+ mv_set_timer_control(val);
+}
+
+static inline void
+mv_wdt_enable_armada_38x_xp_helper()
+{
+ uint32_t val, irq_cause;
+
+ irq_cause = read_cpu_ctrl(BRIDGE_IRQ_CAUSE);
+ irq_cause &= IRQ_TIMER_WD_CLR;
+ write_cpu_ctrl(BRIDGE_IRQ_CAUSE, irq_cause);
+
+ val = read_cpu_mp_clocks(WD_RSTOUTn_MASK);
+ val |= (WD_GLOBAL_MASK | WD_CPU0_MASK);
+ write_cpu_mp_clocks(WD_RSTOUTn_MASK, val);
+
+ val = read_cpu_misc(RSTOUTn_MASK_ARMV7);
+ val &= ~RSTOUTn_MASK_WD;
+ write_cpu_misc(RSTOUTn_MASK_ARMV7, val);
+}
+
+static void
+mv_wdt_enable_armada_38x(void)
+{
+ uint32_t val, irq_cause;
+
+ irq_cause = read_cpu_ctrl(BRIDGE_IRQ_CAUSE);
+ irq_cause &= IRQ_TIMER_WD_CLR;
+ write_cpu_ctrl(BRIDGE_IRQ_CAUSE, irq_cause);
+
+ mv_wdt_enable_armada_38x_xp_helper();
+
+ val = mv_get_timer_control();
+ val |= CPU_TIMER_WD_EN | CPU_TIMER_WD_AUTO | CPU_TIMER_WD_25MHZ_EN;
+ mv_set_timer_control(val);
+}
+
+static void
+mv_wdt_enable_armada_xp(void)
+{
+ uint32_t val, irq_cause;
+ irq_cause = read_cpu_ctrl(BRIDGE_IRQ_CAUSE_ARMADAXP);
+ irq_cause &= IRQ_TIMER_WD_CLR_ARMADAXP;
+ write_cpu_ctrl(BRIDGE_IRQ_CAUSE_ARMADAXP, irq_cause);
+
+ mv_wdt_enable_armada_38x_xp_helper();
+
+ val = mv_get_timer_control();
+ val |= CPU_TIMER2_EN | CPU_TIMER2_AUTO | CPU_TIMER_WD_25MHZ_EN;
+ mv_set_timer_control(val);
+}
+
+static void
+mv_wdt_disable_armv5(void)
+{
+ uint32_t val, irq_cause, irq_mask;
+
+ val = mv_get_timer_control();
+ val &= ~(CPU_TIMER2_EN | CPU_TIMER2_AUTO);
+ mv_set_timer_control(val);
+
+ val = read_cpu_ctrl(RSTOUTn_MASK);
+ val &= ~WD_RST_OUT_EN;
+ write_cpu_ctrl(RSTOUTn_MASK, val);
+
+ irq_mask = read_cpu_ctrl(BRIDGE_IRQ_MASK);
+ irq_mask &= ~(IRQ_TIMER_WD_MASK);
+ write_cpu_ctrl(BRIDGE_IRQ_MASK, irq_mask);
+
+ irq_cause = read_cpu_ctrl(BRIDGE_IRQ_CAUSE);
+ irq_cause &= IRQ_TIMER_WD_CLR;
+ write_cpu_ctrl(BRIDGE_IRQ_CAUSE, irq_cause);
+}
+
+static __inline void
+mv_wdt_disable_armada_38x_xp_helper(void)
+{
+ uint32_t val;
+
+ val = read_cpu_mp_clocks(WD_RSTOUTn_MASK);
+ val &= ~(WD_GLOBAL_MASK | WD_CPU0_MASK);
+ write_cpu_mp_clocks(WD_RSTOUTn_MASK, val);
+
+ val = read_cpu_misc(RSTOUTn_MASK_ARMV7);
+ val |= RSTOUTn_MASK_WD;
+ write_cpu_misc(RSTOUTn_MASK_ARMV7, RSTOUTn_MASK_WD);
+}
+
+static void
+mv_wdt_disable_armada_38x(void)
+{
+ uint32_t val;
+
+ val = mv_get_timer_control();
+ val &= ~(CPU_TIMER_WD_EN | CPU_TIMER_WD_AUTO);
+ mv_set_timer_control(val);
+
+ mv_wdt_disable_armada_38x_xp_helper();
+}
+
+static void
+mv_wdt_disable_armada_xp(void)
+{
+ uint32_t val;
+
+ val = mv_get_timer_control();
+ val &= ~(CPU_TIMER2_EN | CPU_TIMER2_AUTO);
+ mv_set_timer_control(val);
+
+ mv_wdt_disable_armada_38x_xp_helper();
+}
+
+/*
+ * Watchdog event handler.
+ */
+static void
+mv_watchdog_event(void *arg, unsigned int cmd, int *error)
+{
+ struct mv_wdt_softc *sc;
+ uint64_t ns;
+ uint64_t ticks;
+
+ sc = arg;
+ mtx_lock(&sc->wdt_mtx);
+ if (cmd == 0) {
+ if (wdt_softc->wdt_config->wdt_disable != NULL)
+ wdt_softc->wdt_config->wdt_disable();
+ } else {
+ /*
+ * Watchdog timeout is in nanosecs, calculation according to
+ * watchdog(9)
+ */
+ ns = (uint64_t)1 << (cmd & WD_INTERVAL);
+ ticks = (uint64_t)(ns * sc->wdt_config->wdt_clock_src) / 1000000000;
+ if (ticks > MAX_WATCHDOG_TICKS) {
+ if (wdt_softc->wdt_config->wdt_disable != NULL)
+ wdt_softc->wdt_config->wdt_disable();
+ }
+ else {
+ mv_set_timer(wdt_softc->wdt_config->wdt_timer, ticks);
+ if (wdt_softc->wdt_config->wdt_enable != NULL)
+ wdt_softc->wdt_config->wdt_enable();
+ *error = 0;
+ }
+ }
+ mtx_unlock(&sc->wdt_mtx);
+}
diff --git a/sys/arm/mv/armada38x/armada38x.c b/sys/arm/mv/armada38x/armada38x.c
new file mode 100644
index 000000000000..47282a7b0411
--- /dev/null
+++ b/sys/arm/mv/armada38x/armada38x.c
@@ -0,0 +1,226 @@
+/*-
+ * Copyright (c) 2015 Semihalf.
+ * Copyright (c) 2015 Stormshield.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <machine/fdt.h>
+
+#include <arm/mv/mvwin.h>
+#include <arm/mv/mvreg.h>
+#include <arm/mv/mvvar.h>
+
+int armada38x_open_bootrom_win(void);
+int armada38x_scu_enable(void);
+int armada38x_win_set_iosync_barrier(void);
+int armada38x_mbus_optimization(void);
+static uint64_t get_sar_value_armada38x(void);
+
+static int hw_clockrate;
+SYSCTL_INT(_hw, OID_AUTO, clockrate, CTLFLAG_RD,
+ &hw_clockrate, 0, "CPU instruction clock rate");
+
+static uint64_t
+get_sar_value_armada38x(void)
+{
+ uint32_t sar_low, sar_high;
+
+ sar_high = 0;
+ sar_low = bus_space_read_4(fdtbus_bs_tag, MV_MISC_BASE,
+ SAMPLE_AT_RESET_ARMADA38X);
+ return (((uint64_t)sar_high << 32) | sar_low);
+}
+
+uint32_t
+get_tclk_armada38x(void)
+{
+ uint32_t sar;
+
+ /*
+ * On Armada38x TCLK can be configured to 250 MHz or 200 MHz.
+ * Current setting is read from Sample At Reset register.
+ */
+ sar = (uint32_t)get_sar_value_armada38x();
+ sar = (sar & TCLK_MASK_ARMADA38X) >> TCLK_SHIFT_ARMADA38X;
+ if (sar == 0)
+ return (TCLK_250MHZ);
+ else
+ return (TCLK_200MHZ);
+}
+
+uint32_t
+get_cpu_freq_armada38x(void)
+{
+ uint32_t sar;
+
+ static const uint32_t cpu_frequencies[] = {
+ 0, 0, 0, 0,
+ 1066, 0, 0, 0,
+ 1332, 0, 0, 0,
+ 1600, 0, 0, 0,
+ 1866, 0, 0, 2000
+ };
+
+ sar = (uint32_t)get_sar_value_armada38x();
+ sar = (sar & A38X_CPU_DDR_CLK_MASK) >> A38X_CPU_DDR_CLK_SHIFT;
+ if (sar >= nitems(cpu_frequencies))
+ return (0);
+
+ hw_clockrate = cpu_frequencies[sar];
+
+ return (hw_clockrate * 1000 * 1000);
+}
+
+int
+armada38x_win_set_iosync_barrier(void)
+{
+ bus_space_handle_t vaddr_iowind;
+ int rv;
+
+ rv = bus_space_map(fdtbus_bs_tag, (bus_addr_t)MV_MBUS_BRIDGE_BASE,
+ MV_CPU_SUBSYS_REGS_LEN, 0, &vaddr_iowind);
+ if (rv != 0)
+ return (rv);
+
+ /* Set Sync Barrier flags for all Mbus internal units */
+ bus_space_write_4(fdtbus_bs_tag, vaddr_iowind, MV_SYNC_BARRIER_CTRL,
+ MV_SYNC_BARRIER_CTRL_ALL);
+
+ bus_space_barrier(fdtbus_bs_tag, vaddr_iowind, 0,
+ MV_CPU_SUBSYS_REGS_LEN, BUS_SPACE_BARRIER_WRITE);
+ bus_space_unmap(fdtbus_bs_tag, vaddr_iowind, MV_CPU_SUBSYS_REGS_LEN);
+
+ return (rv);
+}
+
+int
+armada38x_open_bootrom_win(void)
+{
+ bus_space_handle_t vaddr_iowind;
+ uint32_t val;
+ int rv;
+
+ rv = bus_space_map(fdtbus_bs_tag, (bus_addr_t)MV_MBUS_BRIDGE_BASE,
+ MV_CPU_SUBSYS_REGS_LEN, 0, &vaddr_iowind);
+ if (rv != 0)
+ return (rv);
+
+ val = (MV_BOOTROM_WIN_SIZE & IO_WIN_SIZE_MASK) << IO_WIN_SIZE_SHIFT;
+ val |= (MBUS_BOOTROM_ATTR & IO_WIN_ATTR_MASK) << IO_WIN_ATTR_SHIFT;
+ val |= (MBUS_BOOTROM_TGT_ID & IO_WIN_TGT_MASK) << IO_WIN_TGT_SHIFT;
+ /* Enable window and Sync Barrier */
+ val |= (0x1 & IO_WIN_SYNC_MASK) << IO_WIN_SYNC_SHIFT;
+ val |= (0x1 & IO_WIN_ENA_MASK) << IO_WIN_ENA_SHIFT;
+
+ /* Configure IO Window Control Register */
+ bus_space_write_4(fdtbus_bs_tag, vaddr_iowind, IO_WIN_9_CTRL_OFFSET,
+ val);
+ /* Configure IO Window Base Register */
+ bus_space_write_4(fdtbus_bs_tag, vaddr_iowind, IO_WIN_9_BASE_OFFSET,
+ MV_BOOTROM_MEM_ADDR);
+
+ bus_space_barrier(fdtbus_bs_tag, vaddr_iowind, 0, MV_CPU_SUBSYS_REGS_LEN,
+ BUS_SPACE_BARRIER_WRITE);
+ bus_space_unmap(fdtbus_bs_tag, vaddr_iowind, MV_CPU_SUBSYS_REGS_LEN);
+
+ return (rv);
+}
+
+int
+armada38x_mbus_optimization(void)
+{
+ bus_space_handle_t vaddr_iowind;
+ int rv;
+
+ rv = bus_space_map(fdtbus_bs_tag, (bus_addr_t)MV_MBUS_CTRL_BASE,
+ MV_MBUS_CTRL_REGS_LEN, 0, &vaddr_iowind);
+ if (rv != 0)
+ return (rv);
+
+ /*
+ * MBUS Units Priority Control Register - Prioritize XOR,
+ * PCIe and GbEs (ID=4,6,3,7,8) DRAM access
+ * GbE is High and others are Medium.
+ */
+ bus_space_write_4(fdtbus_bs_tag, vaddr_iowind, 0, 0x19180);
+
+ /*
+ * Fabric Units Priority Control Register -
+ * Prioritize CPUs requests.
+ */
+ bus_space_write_4(fdtbus_bs_tag, vaddr_iowind, 0x4, 0x3000A);
+
+ /*
+ * MBUS Units Prefetch Control Register -
+ * Pre-fetch enable for all IO masters.
+ */
+ bus_space_write_4(fdtbus_bs_tag, vaddr_iowind, 0x8, 0xFFFF);
+
+ /*
+ * Fabric Units Prefetch Control Register -
+ * Enable the CPUs Instruction and Data prefetch.
+ */
+ bus_space_write_4(fdtbus_bs_tag, vaddr_iowind, 0xc, 0x303);
+
+ bus_space_barrier(fdtbus_bs_tag, vaddr_iowind, 0, MV_MBUS_CTRL_REGS_LEN,
+ BUS_SPACE_BARRIER_WRITE);
+
+ bus_space_unmap(fdtbus_bs_tag, vaddr_iowind, MV_MBUS_CTRL_REGS_LEN);
+
+ return (rv);
+}
+
+int
+armada38x_scu_enable(void)
+{
+ bus_space_handle_t vaddr_scu;
+ int rv;
+ uint32_t val;
+
+ rv = bus_space_map(fdtbus_bs_tag, (bus_addr_t)MV_SCU_BASE,
+ MV_SCU_REGS_LEN, 0, &vaddr_scu);
+ if (rv != 0)
+ return (rv);
+
+ /* Enable SCU */
+ val = bus_space_read_4(fdtbus_bs_tag, vaddr_scu, MV_SCU_REG_CTRL);
+ if (!(val & MV_SCU_ENABLE)) {
+ /* Enable SCU Speculative linefills to L2 */
+ val |= MV_SCU_SL_L2_ENABLE;
+
+ bus_space_write_4(fdtbus_bs_tag, vaddr_scu, 0,
+ val | MV_SCU_ENABLE);
+ }
+
+ bus_space_unmap(fdtbus_bs_tag, vaddr_scu, MV_SCU_REGS_LEN);
+ return (0);
+}
diff --git a/sys/arm/mv/armada38x/armada38x_mp.c b/sys/arm/mv/armada38x/armada38x_mp.c
new file mode 100644
index 000000000000..cbcee38d2e2e
--- /dev/null
+++ b/sys/arm/mv/armada38x/armada38x_mp.c
@@ -0,0 +1,148 @@
+/*-
+ * Copyright (c) 2015 Semihalf.
+ * Copyright (c) 2015 Stormshield.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/smp.h>
+
+#include <machine/smp.h>
+#include <machine/fdt.h>
+#include <machine/intr.h>
+#include <machine/platformvar.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/mv/mvreg.h>
+
+#include "pmsu.h"
+
+static int cpu_reset_deassert(void);
+void mv_a38x_platform_mp_setmaxid(platform_t plate);
+void mv_a38x_platform_mp_start_ap(platform_t plate);
+
+static int
+cpu_reset_deassert(void)
+{
+ bus_space_handle_t vaddr;
+ uint32_t reg;
+ int rv;
+
+ rv = bus_space_map(fdtbus_bs_tag, (bus_addr_t)MV_CPU_RESET_BASE,
+ MV_CPU_RESET_REGS_LEN, 0, &vaddr);
+ if (rv != 0)
+ return (rv);
+
+ /* CPU1 is held at reset by default - clear assert bit to release it */
+ reg = bus_space_read_4(fdtbus_bs_tag, vaddr, CPU_RESET_OFFSET(1));
+ reg &= ~CPU_RESET_ASSERT;
+
+ bus_space_write_4(fdtbus_bs_tag, vaddr, CPU_RESET_OFFSET(1), reg);
+
+ bus_space_unmap(fdtbus_bs_tag, vaddr, MV_CPU_RESET_REGS_LEN);
+
+ return (0);
+}
+
+static int
+platform_cnt_cpus(void)
+{
+ bus_space_handle_t vaddr_scu;
+ phandle_t cpus_node, child;
+ char device_type[16];
+ int fdt_cpu_count = 0;
+ int reg_cpu_count = 0;
+ uint32_t val;
+ int rv;
+
+ cpus_node = OF_finddevice("/cpus");
+ if (cpus_node == -1) {
+ /* Default is one core */
+ mp_ncpus = 1;
+ return (0);
+ }
+
+ /* Get number of 'cpu' nodes from FDT */
+ for (child = OF_child(cpus_node); child != 0; child = OF_peer(child)) {
+ /* Check if child is a CPU */
+ memset(device_type, 0, sizeof(device_type));
+ rv = OF_getprop(child, "device_type", device_type,
+ sizeof(device_type) - 1);
+ if (rv < 0)
+ continue;
+ if (strcmp(device_type, "cpu") != 0)
+ continue;
+
+ fdt_cpu_count++;
+ }
+
+ /* Get number of CPU cores from SCU register to cross-check with FDT */
+ rv = bus_space_map(fdtbus_bs_tag, (bus_addr_t)MV_SCU_BASE,
+ MV_SCU_REGS_LEN, 0, &vaddr_scu);
+ if (rv != 0) {
+ /* Default is one core */
+ mp_ncpus = 1;
+ return (0);
+ }
+
+ val = bus_space_read_4(fdtbus_bs_tag, vaddr_scu, MV_SCU_REG_CONFIG);
+ bus_space_unmap(fdtbus_bs_tag, vaddr_scu, MV_SCU_REGS_LEN);
+ reg_cpu_count = (val & SCU_CFG_REG_NCPU_MASK) + 1;
+
+ /* Set mp_ncpus to number of cpus in FDT unless SOC contains only one */
+ mp_ncpus = min(reg_cpu_count, fdt_cpu_count);
+ /* mp_ncpus must be at least 1 */
+ mp_ncpus = max(1, mp_ncpus);
+
+ return (mp_ncpus);
+}
+
+void
+mv_a38x_platform_mp_setmaxid(platform_t plate)
+{
+
+ /* Armada38x family supports maximum 2 cores */
+ mp_ncpus = platform_cnt_cpus();
+ mp_maxid = mp_ncpus - 1;
+}
+
+void
+mv_a38x_platform_mp_start_ap(platform_t plate)
+{
+ int rv;
+
+ /* Write secondary entry address to PMSU register */
+ rv = pmsu_boot_secondary_cpu();
+ if (rv != 0)
+ return;
+
+ /* Release CPU1 from reset */
+ cpu_reset_deassert();
+}
diff --git a/sys/arm/mv/armada38x/armada38x_pl310.c b/sys/arm/mv/armada38x/armada38x_pl310.c
new file mode 100644
index 000000000000..a96386e877a7
--- /dev/null
+++ b/sys/arm/mv/armada38x/armada38x_pl310.c
@@ -0,0 +1,82 @@
+/*-
+ * Copyright (c) 2017 Stormshield.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * The machine-dependent part of the arm/pl310 driver for Armada 38x SoCs.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <machine/bus.h>
+#include <machine/pl310.h>
+#include <machine/platform.h>
+#include <machine/platformvar.h>
+
+#include "armada38x_pl310.h"
+#include "platform_pl310_if.h"
+
+void
+mv_a38x_platform_pl310_init(platform_t plat, struct pl310_softc *sc)
+{
+ uint32_t reg;
+
+ /*
+ * Enable power saving modes:
+ * - Dynamic Gating stops the clock when the controller is idle.
+ */
+ reg = pl310_read4(sc, PL310_POWER_CTRL);
+ reg |= POWER_CTRL_ENABLE_GATING;
+ pl310_write4(sc, PL310_POWER_CTRL, reg);
+
+ pl310_write4(sc, PL310_PREFETCH_CTRL, PREFETCH_CTRL_DL |
+ PREFETCH_CTRL_DATA_PREFETCH | PREFETCH_CTRL_INCR_DL |
+ PREFETCH_CTRL_DL_ON_WRAP);
+
+ /* Disable L2 cache sync for IO coherent operation */
+ sc->sc_io_coherent = true;
+}
+
+void
+mv_a38x_platform_pl310_write_ctrl(platform_t plat, struct pl310_softc *sc, uint32_t val)
+{
+
+ pl310_write4(sc, PL310_CTRL, val);
+}
+
+void
+mv_a38x_platform_pl310_write_debug(platform_t plat, struct pl310_softc *sc, uint32_t val)
+{
+
+ pl310_write4(sc, PL310_DEBUG_CTRL, val);
+}
diff --git a/sys/arm/mv/armada38x/armada38x_pl310.h b/sys/arm/mv/armada38x/armada38x_pl310.h
new file mode 100644
index 000000000000..d0a0a0231444
--- /dev/null
+++ b/sys/arm/mv/armada38x/armada38x_pl310.h
@@ -0,0 +1,37 @@
+/*-
+ * Copyright (c) 2017 Semihalf.
+ * Copyright (c) 2017 Stormshield.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef ARMADA38X_PL310_H
+#define ARMADA38X_PL310_H
+#include <machine/platformvar.h>
+
+void mv_a38x_platform_pl310_init(platform_t plat, struct pl310_softc *sc);
+void mv_a38x_platform_pl310_write_ctrl(platform_t plat, struct pl310_softc *sc, uint32_t val);
+void mv_a38x_platform_pl310_write_debug(platform_t plat, struct pl310_softc *sc, uint32_t val);
+
+#endif
diff --git a/sys/arm/mv/armada38x/armada38x_rtc.c b/sys/arm/mv/armada38x/armada38x_rtc.c
new file mode 100644
index 000000000000..ff89094194a7
--- /dev/null
+++ b/sys/arm/mv/armada38x/armada38x_rtc.c
@@ -0,0 +1,368 @@
+/*-
+ * Copyright (c) 2015 Semihalf.
+ * Copyright (c) 2015 Stormshield.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/time.h>
+#include <sys/proc.h>
+#include <sys/conf.h>
+#include <sys/rman.h>
+#include <sys/clock.h>
+#include <sys/systm.h>
+#include <sys/mutex.h>
+#include <sys/types.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/resource.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "clock_if.h"
+
+#define RTC_RES_US 1000000
+#define HALF_OF_SEC_NS 500000000
+
+#define RTC_STATUS 0x0
+#define RTC_TIME 0xC
+#define RTC_TEST_CONFIG 0x1C
+#define RTC_IRQ_1_CONFIG 0x4
+#define RTC_IRQ_2_CONFIG 0x8
+#define RTC_ALARM_1 0x10
+#define RTC_ALARM_2 0x14
+#define RTC_CLOCK_CORR 0x18
+
+#define RTC_NOMINAL_TIMING 0x2000
+#define RTC_NOMINAL_TIMING_MASK 0x7fff
+
+#define RTC_STATUS_ALARM1_MASK 0x1
+#define RTC_STATUS_ALARM2_MASK 0x2
+
+#define MV_RTC_LOCK(sc) mtx_lock_spin(&(sc)->mutex)
+#define MV_RTC_UNLOCK(sc) mtx_unlock_spin(&(sc)->mutex)
+
+#define A38X_RTC_BRIDGE_TIMING_CTRL 0x0
+#define A38X_RTC_WRCLK_PERIOD_SHIFT 0
+#define A38X_RTC_WRCLK_PERIOD_MASK 0x00000003FF
+#define A38X_RTC_WRCLK_PERIOD_MAX 0x3FF
+#define A38X_RTC_READ_OUTPUT_DELAY_SHIFT 26
+#define A38X_RTC_READ_OUTPUT_DELAY_MASK 0x007C000000
+#define A38X_RTC_READ_OUTPUT_DELAY_MAX 0x1F
+
+#define A8K_RTC_BRIDGE_TIMING_CTRL0 0x0
+#define A8K_RTC_WRCLK_PERIOD_SHIFT 0
+#define A8K_RTC_WRCLK_PERIOD_MASK 0x000000FFFF
+#define A8K_RTC_WRCLK_PERIOD_VAL 0x3FF
+#define A8K_RTC_WRCLK_SETUP_SHIFT 16
+#define A8K_RTC_WRCLK_SETUP_MASK 0x00FFFF0000
+#define A8K_RTC_WRCLK_SETUP_VAL 29
+#define A8K_RTC_BRIDGE_TIMING_CTRL1 0x4
+#define A8K_RTC_READ_OUTPUT_DELAY_SHIFT 0
+#define A8K_RTC_READ_OUTPUT_DELAY_MASK 0x000000FFFF
+#define A8K_RTC_READ_OUTPUT_DELAY_VAL 0x3F
+
+#define RTC_RES 0
+#define RTC_SOC_RES 1
+
+static struct resource_spec res_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_MEMORY, 1, RF_ACTIVE },
+ { -1, 0 }
+};
+
+struct mv_rtc_softc {
+ device_t dev;
+ struct resource *res[2];
+ struct mtx mutex;
+ int rtc_type;
+};
+
+static int mv_rtc_probe(device_t dev);
+static int mv_rtc_attach(device_t dev);
+static int mv_rtc_detach(device_t dev);
+
+static int mv_rtc_gettime(device_t dev, struct timespec *ts);
+static int mv_rtc_settime(device_t dev, struct timespec *ts);
+
+static inline uint32_t mv_rtc_reg_read(struct mv_rtc_softc *sc,
+ bus_size_t off);
+static inline int mv_rtc_reg_write(struct mv_rtc_softc *sc, bus_size_t off,
+ uint32_t val);
+static inline void mv_rtc_configure_bus_a38x(struct mv_rtc_softc *sc);
+static inline void mv_rtc_configure_bus_a8k(struct mv_rtc_softc *sc);
+
+static device_method_t mv_rtc_methods[] = {
+ DEVMETHOD(device_probe, mv_rtc_probe),
+ DEVMETHOD(device_attach, mv_rtc_attach),
+ DEVMETHOD(device_detach, mv_rtc_detach),
+
+ DEVMETHOD(clock_gettime, mv_rtc_gettime),
+ DEVMETHOD(clock_settime, mv_rtc_settime),
+
+ { 0, 0 },
+};
+
+static driver_t mv_rtc_driver = {
+ "rtc",
+ mv_rtc_methods,
+ sizeof(struct mv_rtc_softc),
+};
+
+#define RTC_A38X 1
+#define RTC_A8K 2
+
+static struct ofw_compat_data mv_rtc_compat[] = {
+ {"marvell,armada-380-rtc", RTC_A38X},
+ {"marvell,armada-8k-rtc", RTC_A8K},
+ {NULL, 0},
+};
+
+static devclass_t mv_rtc_devclass;
+
+DRIVER_MODULE(a38x_rtc, simplebus, mv_rtc_driver, mv_rtc_devclass, 0, 0);
+
+static void
+mv_rtc_reset(device_t dev)
+{
+ struct mv_rtc_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ /* Reset Test register */
+ mv_rtc_reg_write(sc, RTC_TEST_CONFIG, 0);
+ DELAY(500000);
+
+ /* Reset Time register */
+ mv_rtc_reg_write(sc, RTC_TIME, 0);
+ DELAY(62);
+
+ /* Reset Status register */
+ mv_rtc_reg_write(sc, RTC_STATUS, (RTC_STATUS_ALARM1_MASK | RTC_STATUS_ALARM2_MASK));
+ DELAY(62);
+
+ /* Turn off Int1 and Int2 sources & clear the Alarm count */
+ mv_rtc_reg_write(sc, RTC_IRQ_1_CONFIG, 0);
+ mv_rtc_reg_write(sc, RTC_IRQ_2_CONFIG, 0);
+ mv_rtc_reg_write(sc, RTC_ALARM_1, 0);
+ mv_rtc_reg_write(sc, RTC_ALARM_2, 0);
+
+ /* Setup nominal register access timing */
+ mv_rtc_reg_write(sc, RTC_CLOCK_CORR, RTC_NOMINAL_TIMING);
+
+ /* Reset Time register */
+ mv_rtc_reg_write(sc, RTC_TIME, 0);
+ DELAY(10);
+
+ /* Reset Status register */
+ mv_rtc_reg_write(sc, RTC_STATUS, (RTC_STATUS_ALARM1_MASK | RTC_STATUS_ALARM2_MASK));
+ DELAY(50);
+}
+
+static int
+mv_rtc_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_search_compatible(dev, mv_rtc_compat)->ocd_data)
+ return (ENXIO);
+
+ device_set_desc(dev, "Marvell Integrated RTC");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+mv_rtc_attach(device_t dev)
+{
+ struct mv_rtc_softc *sc;
+ int unit, ret;
+
+ unit = device_get_unit(dev);
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ sc->rtc_type = ofw_bus_search_compatible(dev, mv_rtc_compat)->ocd_data;
+
+ mtx_init(&sc->mutex, device_get_nameunit(dev), NULL, MTX_SPIN);
+
+ ret = bus_alloc_resources(dev, res_spec, sc->res);
+ if (ret != 0) {
+ device_printf(dev, "could not allocate resources\n");
+ mtx_destroy(&sc->mutex);
+ return (ENXIO);
+ }
+
+ switch (sc->rtc_type) {
+ case RTC_A38X:
+ mv_rtc_configure_bus_a38x(sc);
+ break;
+ case RTC_A8K:
+ mv_rtc_configure_bus_a8k(sc);
+ break;
+ default:
+ panic("Unknown RTC type: %d", sc->rtc_type);
+ }
+ clock_register(dev, RTC_RES_US);
+
+ return (0);
+}
+
+static int
+mv_rtc_detach(device_t dev)
+{
+ struct mv_rtc_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ mtx_destroy(&sc->mutex);
+
+ bus_release_resources(dev, res_spec, sc->res);
+
+ return (0);
+}
+
+static int
+mv_rtc_gettime(device_t dev, struct timespec *ts)
+{
+ struct mv_rtc_softc *sc;
+ uint32_t val, val_check;
+
+ sc = device_get_softc(dev);
+
+ MV_RTC_LOCK(sc);
+ /*
+ * According to HW Errata, if more than one second is detected
+ * between two time reads, then at least one of the reads gave
+ * an invalid value.
+ */
+ do {
+ val = mv_rtc_reg_read(sc, RTC_TIME);
+ DELAY(100);
+ val_check = mv_rtc_reg_read(sc, RTC_TIME);
+ } while ((val_check - val) > 1);
+
+ MV_RTC_UNLOCK(sc);
+
+ ts->tv_sec = val_check;
+ /* RTC resolution is 1 sec */
+ ts->tv_nsec = 0;
+
+ return (0);
+}
+
+static int
+mv_rtc_settime(device_t dev, struct timespec *ts)
+{
+ struct mv_rtc_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ /* RTC resolution is 1 sec */
+ if (ts->tv_nsec >= HALF_OF_SEC_NS)
+ ts->tv_sec++;
+ ts->tv_nsec = 0;
+
+ MV_RTC_LOCK(sc);
+
+ if ((mv_rtc_reg_read(sc, RTC_CLOCK_CORR) & RTC_NOMINAL_TIMING_MASK) !=
+ RTC_NOMINAL_TIMING) {
+ /* RTC was not resetted yet */
+ mv_rtc_reset(dev);
+ }
+
+ /*
+ * According to errata FE-3124064, Write to RTC TIME register
+ * may fail. As a workaround, before writing to RTC TIME register,
+ * issue a dummy write of 0x0 twice to RTC Status register.
+ */
+ mv_rtc_reg_write(sc, RTC_STATUS, 0x0);
+ mv_rtc_reg_write(sc, RTC_STATUS, 0x0);
+ mv_rtc_reg_write(sc, RTC_TIME, ts->tv_sec);
+ MV_RTC_UNLOCK(sc);
+
+ return (0);
+}
+
+static inline uint32_t
+mv_rtc_reg_read(struct mv_rtc_softc *sc, bus_size_t off)
+{
+
+ return (bus_read_4(sc->res[RTC_RES], off));
+}
+
+/*
+ * According to the datasheet, the OS should wait 5us after every
+ * register write to the RTC hard macro so that the required update
+ * can occur without holding off the system bus
+ */
+static inline int
+mv_rtc_reg_write(struct mv_rtc_softc *sc, bus_size_t off, uint32_t val)
+{
+
+ bus_write_4(sc->res[RTC_RES], off, val);
+ DELAY(5);
+
+ return (0);
+}
+
+static inline void
+mv_rtc_configure_bus_a38x(struct mv_rtc_softc *sc)
+{
+ int val;
+
+ val = bus_read_4(sc->res[RTC_SOC_RES], A38X_RTC_BRIDGE_TIMING_CTRL);
+ val &= ~(A38X_RTC_WRCLK_PERIOD_MASK | A38X_RTC_READ_OUTPUT_DELAY_MASK);
+ val |= A38X_RTC_WRCLK_PERIOD_MAX << A38X_RTC_WRCLK_PERIOD_SHIFT;
+ val |= A38X_RTC_READ_OUTPUT_DELAY_MAX << A38X_RTC_READ_OUTPUT_DELAY_SHIFT;
+ bus_write_4(sc->res[RTC_SOC_RES], A38X_RTC_BRIDGE_TIMING_CTRL, val);
+}
+
+static inline void
+mv_rtc_configure_bus_a8k(struct mv_rtc_softc *sc)
+{
+ int val;
+
+ val = bus_read_4(sc->res[RTC_SOC_RES], A8K_RTC_BRIDGE_TIMING_CTRL0);
+ val &= ~(A8K_RTC_WRCLK_PERIOD_MASK | A8K_RTC_WRCLK_SETUP_MASK);
+ val |= A8K_RTC_WRCLK_PERIOD_VAL << A8K_RTC_WRCLK_PERIOD_SHIFT;
+ val |= A8K_RTC_WRCLK_SETUP_VAL << A8K_RTC_WRCLK_SETUP_SHIFT;
+ bus_write_4(sc->res[RTC_SOC_RES], A8K_RTC_BRIDGE_TIMING_CTRL1, val);
+
+ val = bus_read_4(sc->res[RTC_SOC_RES], A8K_RTC_BRIDGE_TIMING_CTRL0);
+ val &= ~A8K_RTC_READ_OUTPUT_DELAY_MASK;
+ val |= A8K_RTC_READ_OUTPUT_DELAY_VAL << A8K_RTC_READ_OUTPUT_DELAY_SHIFT;
+ bus_write_4(sc->res[RTC_SOC_RES], A8K_RTC_BRIDGE_TIMING_CTRL1, val);
+}
diff --git a/sys/arm/mv/armada38x/files.armada38x b/sys/arm/mv/armada38x/files.armada38x
new file mode 100644
index 000000000000..35b64b86d82f
--- /dev/null
+++ b/sys/arm/mv/armada38x/files.armada38x
@@ -0,0 +1,11 @@
+# $FreeBSD$
+arm/mv/mpic.c standard
+
+arm/mv/armada/thermal.c optional fdt
+arm/mv/armada/wdt.c optional fdt
+
+arm/mv/armada38x/armada38x.c standard
+arm/mv/armada38x/armada38x_mp.c optional smp
+arm/mv/armada38x/pmsu.c standard
+arm/mv/armada38x/armada38x_rtc.c optional mv_rtc fdt
+arm/mv/armada38x/armada38x_pl310.c optional pl310
diff --git a/sys/arm/mv/armada38x/pmsu.c b/sys/arm/mv/armada38x/pmsu.c
new file mode 100644
index 000000000000..7df368d0c051
--- /dev/null
+++ b/sys/arm/mv/armada38x/pmsu.c
@@ -0,0 +1,155 @@
+/*-
+ * Copyright (c) 2015 Semihalf.
+ * Copyright (c) 2015 Stormshield.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/rman.h>
+#include <sys/types.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/resource.h>
+#include <sys/systm.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/cpu.h>
+#include <machine/fdt.h>
+#include <machine/smp.h>
+
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/mv/mvreg.h>
+
+#include "pmsu.h"
+
+static struct resource_spec pmsu_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+struct pmsu_softc {
+ device_t dev;
+ struct resource *res;
+};
+
+static int pmsu_probe(device_t dev);
+static int pmsu_attach(device_t dev);
+static int pmsu_detach(device_t dev);
+
+static device_method_t pmsu_methods[] = {
+ DEVMETHOD(device_probe, pmsu_probe),
+ DEVMETHOD(device_attach, pmsu_attach),
+ DEVMETHOD(device_detach, pmsu_detach),
+ { 0, 0 }
+};
+
+static driver_t pmsu_driver = {
+ "pmsu",
+ pmsu_methods,
+ sizeof(struct pmsu_softc)
+};
+
+static devclass_t pmsu_devclass;
+
+DRIVER_MODULE(pmsu, simplebus, pmsu_driver, pmsu_devclass, 0, 0);
+DRIVER_MODULE(pmsu, ofwbus, pmsu_driver, pmsu_devclass, 0, 0);
+
+static int
+pmsu_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "marvell,armada-380-pmsu"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Power Management Service Unit");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+pmsu_attach(device_t dev)
+{
+ struct pmsu_softc *sc;
+ int err;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ err = bus_alloc_resources(dev, pmsu_spec, &sc->res);
+ if (err != 0) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static int
+pmsu_detach(device_t dev)
+{
+ struct pmsu_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ bus_release_resources(dev, pmsu_spec, &sc->res);
+
+ return (0);
+}
+
+#ifdef SMP
+int
+pmsu_boot_secondary_cpu(void)
+{
+ bus_space_handle_t vaddr;
+ int rv;
+
+ rv = bus_space_map(fdtbus_bs_tag, (bus_addr_t)MV_PMSU_BASE, MV_PMSU_REGS_LEN,
+ 0, &vaddr);
+ if (rv != 0)
+ return (rv);
+
+ /* Boot cpu1 */
+ bus_space_write_4(fdtbus_bs_tag, vaddr, PMSU_BOOT_ADDR_REDIRECT_OFFSET(1),
+ pmap_kextract((vm_offset_t)mpentry));
+
+ dcache_wbinv_poc_all();
+ dsb();
+ sev();
+
+ bus_space_unmap(fdtbus_bs_tag, vaddr, MV_PMSU_REGS_LEN);
+
+ return (0);
+}
+#endif
diff --git a/sys/arm/mv/armada38x/pmsu.h b/sys/arm/mv/armada38x/pmsu.h
new file mode 100644
index 000000000000..530cbbd6f097
--- /dev/null
+++ b/sys/arm/mv/armada38x/pmsu.h
@@ -0,0 +1,35 @@
+/*-
+ * Copyright (c) 2015 Semihalf.
+ * Copyright (c) 2015 Stormshield.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "opt_global.h"
+
+#ifdef SMP
+/* Boot secondary core using PMSU */
+int pmsu_boot_secondary_cpu(void);
+#endif
diff --git a/sys/arm/mv/armada38x/std.armada38x b/sys/arm/mv/armada38x/std.armada38x
new file mode 100644
index 000000000000..66c8add0393f
--- /dev/null
+++ b/sys/arm/mv/armada38x/std.armada38x
@@ -0,0 +1,6 @@
+# $FreeBSD$
+files "../mv/files.arm7"
+cpu CPU_CORTEXA
+machine arm armv7
+
+makeoptions CONF_CFLAGS="-march=armv7a"
diff --git a/sys/arm/mv/armadaxp/armadaxp.c b/sys/arm/mv/armadaxp/armadaxp.c
new file mode 100644
index 000000000000..bfd118d62705
--- /dev/null
+++ b/sys/arm/mv/armadaxp/armadaxp.c
@@ -0,0 +1,321 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2011 Semihalf.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * From: FreeBSD: src/sys/arm/mv/kirkwood/sheevaplug.c,v 1.2 2010/06/13 13:28:53
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <machine/armreg.h>
+#include <machine/bus.h>
+#include <machine/cpu.h>
+
+#include <arm/mv/mvwin.h>
+#include <arm/mv/mvreg.h>
+#include <arm/mv/mvvar.h>
+
+#include <dev/ofw/openfirm.h>
+
+#include <machine/fdt.h>
+
+#define CPU_FREQ_FIELD(sar) (((0x01 & (sar >> 52)) << 3) | \
+ (0x07 & (sar >> 21)))
+#define FAB_FREQ_FIELD(sar) (((0x01 & (sar >> 51)) << 4) | \
+ (0x0F & (sar >> 24)))
+
+static uint32_t count_l2clk(void);
+void armadaxp_l2_init(void);
+void armadaxp_init_coher_fabric(void);
+int platform_get_ncpus(void);
+static uint64_t get_sar_value_armadaxp(void);
+
+#define ARMADAXP_L2_BASE (MV_BASE + 0x8000)
+#define ARMADAXP_L2_CTRL 0x100
+#define L2_ENABLE (1 << 0)
+#define ARMADAXP_L2_AUX_CTRL 0x104
+#define L2_WBWT_MODE_MASK (3 << 0)
+#define L2_WBWT_MODE_PAGE 0
+#define L2_WBWT_MODE_WB 1
+#define L2_WBWT_MODE_WT 2
+#define L2_REP_STRAT_MASK (3 << 27)
+#define L2_REP_STRAT_LSFR (1 << 27)
+#define L2_REP_STRAT_SEMIPLRU (3 << 27)
+
+#define ARMADAXP_L2_CNTR_CTRL 0x200
+#define ARMADAXP_L2_CNTR_CONF(x) (0x204 + (x) * 0xc)
+#define ARMADAXP_L2_CNTR2_VAL_LOW (0x208 + (x) * 0xc)
+#define ARMADAXP_L2_CNTR2_VAL_HI (0x20c + (x) * 0xc)
+
+#define ARMADAXP_L2_INT_CAUSE 0x220
+
+#define ARMADAXP_L2_SYNC_BARRIER 0x700
+#define ARMADAXP_L2_INV_WAY 0x778
+#define ARMADAXP_L2_CLEAN_WAY 0x7BC
+#define ARMADAXP_L2_FLUSH_PHYS 0x7F0
+#define ARMADAXP_L2_FLUSH_WAY 0x7FC
+
+#define MV_COHERENCY_FABRIC_BASE (MV_MBUS_BRIDGE_BASE + 0x200)
+#define COHER_FABRIC_CTRL 0x00
+#define COHER_FABRIC_CONF 0x04
+#define COHER_FABRIC_CFU 0x28
+#define COHER_FABRIC_CIB_CTRL 0x80
+
+struct vco_freq_ratio {
+ uint8_t vco_cpu; /* VCO to CLK0(CPU) clock ratio */
+ uint8_t vco_l2c; /* VCO to NB(L2 cache) clock ratio */
+ uint8_t vco_hcl; /* VCO to HCLK(DDR controller) clock ratio */
+ uint8_t vco_ddr; /* VCO to DR(DDR memory) clock ratio */
+};
+
+static struct vco_freq_ratio freq_conf_table[] = {
+/*00*/ { 1, 1, 4, 2 },
+/*01*/ { 1, 2, 2, 2 },
+/*02*/ { 2, 2, 6, 3 },
+/*03*/ { 2, 2, 3, 3 },
+/*04*/ { 1, 2, 3, 3 },
+/*05*/ { 1, 2, 4, 2 },
+/*06*/ { 1, 1, 2, 2 },
+/*07*/ { 2, 3, 6, 6 },
+/*08*/ { 2, 3, 5, 5 },
+/*09*/ { 1, 2, 6, 3 },
+/*10*/ { 2, 4, 10, 5 },
+/*11*/ { 1, 3, 6, 6 },
+/*12*/ { 1, 2, 5, 5 },
+/*13*/ { 1, 3, 6, 3 },
+/*14*/ { 1, 2, 5, 5 },
+/*15*/ { 2, 2, 5, 5 },
+/*16*/ { 1, 1, 3, 3 },
+/*17*/ { 2, 5, 10, 10 },
+/*18*/ { 1, 3, 8, 4 },
+/*19*/ { 1, 1, 2, 1 },
+/*20*/ { 2, 3, 6, 3 },
+/*21*/ { 1, 2, 8, 4 },
+/*22*/ { 2, 5, 10, 5 }
+};
+
+static uint16_t cpu_clock_table[] = {
+ 1000, 1066, 1200, 1333, 1500, 1666, 1800, 2000, 600, 667, 800, 1600,
+ 2133, 2200, 2400 };
+
+static uint64_t
+get_sar_value_armadaxp(void)
+{
+ uint32_t sar_low, sar_high;
+
+ sar_high = bus_space_read_4(fdtbus_bs_tag, MV_MISC_BASE,
+ SAMPLE_AT_RESET_HI);
+ sar_low = bus_space_read_4(fdtbus_bs_tag, MV_MISC_BASE,
+ SAMPLE_AT_RESET_LO);
+ return (((uint64_t)sar_high << 32) | sar_low);
+}
+
+uint32_t
+get_tclk_armadaxp(void)
+{
+ uint32_t cputype;
+
+ cputype = cp15_midr_get();
+ cputype &= CPU_ID_CPU_MASK;
+
+ if (cputype == CPU_ID_MV88SV584X_V7)
+ return (TCLK_250MHZ);
+ else
+ return (TCLK_200MHZ);
+}
+
+uint32_t
+get_cpu_freq_armadaxp(void)
+{
+
+ return (0);
+}
+
+static uint32_t
+count_l2clk(void)
+{
+ uint64_t sar_reg;
+ uint32_t freq_vco, freq_l2clk;
+ uint8_t sar_cpu_freq, sar_fab_freq, array_size;
+
+ /* Get value of the SAR register and process it */
+ sar_reg = get_sar_value_armadaxp();
+ sar_cpu_freq = CPU_FREQ_FIELD(sar_reg);
+ sar_fab_freq = FAB_FREQ_FIELD(sar_reg);
+
+ /* Check if CPU frequency field has correct value */
+ array_size = nitems(cpu_clock_table);
+ if (sar_cpu_freq >= array_size)
+ panic("Reserved value in cpu frequency configuration field: "
+ "%d", sar_cpu_freq);
+
+ /* Check if fabric frequency field has correct value */
+ array_size = nitems(freq_conf_table);
+ if (sar_fab_freq >= array_size)
+ panic("Reserved value in fabric frequency configuration field: "
+ "%d", sar_fab_freq);
+
+ /* Get CPU clock frequency */
+ freq_vco = cpu_clock_table[sar_cpu_freq] *
+ freq_conf_table[sar_fab_freq].vco_cpu;
+
+ /* Get L2CLK clock frequency */
+ freq_l2clk = freq_vco / freq_conf_table[sar_fab_freq].vco_l2c;
+
+ /* Round L2CLK value to integer MHz */
+ if (((freq_vco % freq_conf_table[sar_fab_freq].vco_l2c) * 10 /
+ freq_conf_table[sar_fab_freq].vco_l2c) >= 5)
+ freq_l2clk++;
+
+ return (freq_l2clk * 1000000);
+}
+
+uint32_t
+get_l2clk(void)
+{
+ static uint32_t l2clk_freq = 0;
+
+ /* If get_l2clk is called first time get L2CLK value from register */
+ if (l2clk_freq == 0)
+ l2clk_freq = count_l2clk();
+
+ return (l2clk_freq);
+}
+
+static uint32_t
+read_coher_fabric(uint32_t reg)
+{
+
+ return (bus_space_read_4(fdtbus_bs_tag, MV_COHERENCY_FABRIC_BASE, reg));
+}
+
+static void
+write_coher_fabric(uint32_t reg, uint32_t val)
+{
+
+ bus_space_write_4(fdtbus_bs_tag, MV_COHERENCY_FABRIC_BASE, reg, val);
+}
+
+int
+platform_get_ncpus(void)
+{
+#if !defined(SMP)
+ return (1);
+#else
+ return ((read_coher_fabric(COHER_FABRIC_CONF) & 0xf) + 1);
+#endif
+}
+
+void
+armadaxp_init_coher_fabric(void)
+{
+ uint32_t val, cpus, mask;
+
+ cpus = platform_get_ncpus();
+ mask = (1 << cpus) - 1;
+ val = read_coher_fabric(COHER_FABRIC_CTRL);
+ val |= (mask << 24);
+ write_coher_fabric(COHER_FABRIC_CTRL, val);
+
+ val = read_coher_fabric(COHER_FABRIC_CONF);
+ val |= (mask << 24);
+ val |= (1 << 15);
+ write_coher_fabric(COHER_FABRIC_CONF, val);
+}
+
+#define ALL_WAYS 0xffffffff
+
+/* L2 cache configuration registers */
+static uint32_t
+read_l2_cache(uint32_t reg)
+{
+
+ return (bus_space_read_4(fdtbus_bs_tag, ARMADAXP_L2_BASE, reg));
+}
+
+static void
+write_l2_cache(uint32_t reg, uint32_t val)
+{
+
+ bus_space_write_4(fdtbus_bs_tag, ARMADAXP_L2_BASE, reg, val);
+}
+
+static void
+armadaxp_l2_idcache_inv_all(void)
+{
+ write_l2_cache(ARMADAXP_L2_INV_WAY, ALL_WAYS);
+}
+
+void
+armadaxp_l2_init(void)
+{
+ u_int32_t reg;
+
+ /* Set L2 policy */
+ reg = read_l2_cache(ARMADAXP_L2_AUX_CTRL);
+ reg &= ~(L2_WBWT_MODE_MASK);
+ reg &= ~(L2_REP_STRAT_MASK);
+ reg |= L2_REP_STRAT_SEMIPLRU;
+ reg |= L2_WBWT_MODE_WT;
+ write_l2_cache(ARMADAXP_L2_AUX_CTRL, reg);
+
+ /* Invalidate l2 cache */
+ armadaxp_l2_idcache_inv_all();
+
+ /* Clear pending L2 interrupts */
+ write_l2_cache(ARMADAXP_L2_INT_CAUSE, 0x1ff);
+
+ /* Enable l2 cache */
+ reg = read_l2_cache(ARMADAXP_L2_CTRL);
+ write_l2_cache(ARMADAXP_L2_CTRL, reg | L2_ENABLE);
+
+ /*
+ * For debug purposes
+ * Configure and enable counter
+ */
+ write_l2_cache(ARMADAXP_L2_CNTR_CONF(0), 0xf0000 | (4 << 2));
+ write_l2_cache(ARMADAXP_L2_CNTR_CONF(1), 0xf0000 | (2 << 2));
+ write_l2_cache(ARMADAXP_L2_CNTR_CTRL, 0x303);
+
+ /*
+ * Enable Cache maintenance operation propagation in coherency fabric
+ * Change point of coherency and point of unification to DRAM.
+ */
+ reg = read_coher_fabric(COHER_FABRIC_CFU);
+ reg |= (1 << 17) | (1 << 18);
+ write_coher_fabric(COHER_FABRIC_CFU, reg);
+
+ /* Coherent IO Bridge initialization */
+ reg = read_coher_fabric(COHER_FABRIC_CIB_CTRL);
+ reg &= ~(7 << 16);
+ reg |= (7 << 16);
+ write_coher_fabric(COHER_FABRIC_CIB_CTRL, reg);
+}
diff --git a/sys/arm/mv/armadaxp/armadaxp_mp.c b/sys/arm/mv/armadaxp/armadaxp_mp.c
new file mode 100644
index 000000000000..d6adf8b9ef97
--- /dev/null
+++ b/sys/arm/mv/armadaxp/armadaxp_mp.c
@@ -0,0 +1,183 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2011 Semihalf.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/smp.h>
+
+#include <vm/vm.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_extern.h>
+#include <vm/pmap.h>
+
+#include <dev/fdt/fdt_common.h>
+
+#include <machine/cpu.h>
+#include <machine/smp.h>
+#include <machine/fdt.h>
+#include <machine/armreg.h>
+
+#include <arm/mv/mvwin.h>
+
+#include <machine/platformvar.h>
+
+#define MV_AXP_CPU_DIVCLK_BASE (MV_BASE + 0x18700)
+#define CPU_DIVCLK_CTRL0 0x00
+#define CPU_DIVCLK_CTRL2_RATIO_FULL0 0x08
+#define CPU_DIVCLK_CTRL2_RATIO_FULL1 0x0c
+#define CPU_DIVCLK_MASK(x) (~(0xff << (8 * (x))))
+
+#define CPU_PMU(x) (MV_BASE + 0x22100 + (0x100 * (x)))
+#define CPU_PMU_BOOT 0x24
+
+#define MP (MV_BASE + 0x20800)
+#define MP_SW_RESET(x) ((x) * 8)
+
+#define CPU_RESUME_CONTROL (0x20988)
+
+void armadaxp_init_coher_fabric(void);
+int platform_get_ncpus(void);
+
+void mv_axp_platform_mp_setmaxid(platform_t plat);
+void mv_axp_platform_mp_start_ap(platform_t plat);
+
+/* Coherency Fabric registers */
+static uint32_t
+read_cpu_clkdiv(uint32_t reg)
+{
+
+ return (bus_space_read_4(fdtbus_bs_tag, MV_AXP_CPU_DIVCLK_BASE, reg));
+}
+
+static void
+write_cpu_clkdiv(uint32_t reg, uint32_t val)
+{
+
+ bus_space_write_4(fdtbus_bs_tag, MV_AXP_CPU_DIVCLK_BASE, reg, val);
+}
+
+void
+mv_axp_platform_mp_setmaxid(platform_t plat)
+{
+
+ mp_ncpus = platform_get_ncpus();
+ mp_maxid = mp_ncpus - 1;
+}
+
+void mptramp(void);
+void mptramp_end(void);
+extern vm_offset_t mptramp_pmu_boot;
+
+void
+mv_axp_platform_mp_start_ap(platform_t plat)
+{
+ uint32_t reg, *src, *dst, cpu_num, div_val, cputype;
+ vm_offset_t pmu_boot_off;
+ /*
+ * Initialization procedure depends on core revision,
+ * in this step CHIP ID is checked to choose proper procedure
+ */
+ cputype = cp15_midr_get();
+ cputype &= CPU_ID_CPU_MASK;
+
+ /*
+ * Set the PA of CPU0 Boot Address Redirect register used in
+ * mptramp according to the actual SoC registers' base address.
+ */
+ pmu_boot_off = (CPU_PMU(0) - MV_BASE) + CPU_PMU_BOOT;
+ mptramp_pmu_boot = fdt_immr_pa + pmu_boot_off;
+ dst = pmap_mapdev(0xffff0000, PAGE_SIZE);
+ for (src = (uint32_t *)mptramp; src < (uint32_t *)mptramp_end;
+ src++, dst++) {
+ *dst = *src;
+ }
+ pmap_unmapdev((vm_offset_t)dst, PAGE_SIZE);
+ if (cputype == CPU_ID_MV88SV584X_V7) {
+ /* Core rev A0 */
+ div_val = read_cpu_clkdiv(CPU_DIVCLK_CTRL2_RATIO_FULL1);
+ div_val &= 0x3f;
+
+ for (cpu_num = 1; cpu_num < mp_ncpus; cpu_num++ ) {
+ reg = read_cpu_clkdiv(CPU_DIVCLK_CTRL2_RATIO_FULL1);
+ reg &= CPU_DIVCLK_MASK(cpu_num);
+ reg |= div_val << (cpu_num * 8);
+ write_cpu_clkdiv(CPU_DIVCLK_CTRL2_RATIO_FULL1, reg);
+ }
+ } else {
+ /* Core rev Z1 */
+ div_val = 0x01;
+
+ if (mp_ncpus > 1) {
+ reg = read_cpu_clkdiv(CPU_DIVCLK_CTRL2_RATIO_FULL0);
+ reg &= CPU_DIVCLK_MASK(3);
+ reg |= div_val << 24;
+ write_cpu_clkdiv(CPU_DIVCLK_CTRL2_RATIO_FULL0, reg);
+ }
+
+ for (cpu_num = 2; cpu_num < mp_ncpus; cpu_num++ ) {
+ reg = read_cpu_clkdiv(CPU_DIVCLK_CTRL2_RATIO_FULL1);
+ reg &= CPU_DIVCLK_MASK(cpu_num);
+ reg |= div_val << (cpu_num * 8);
+ write_cpu_clkdiv(CPU_DIVCLK_CTRL2_RATIO_FULL1, reg);
+ }
+ }
+
+ reg = read_cpu_clkdiv(CPU_DIVCLK_CTRL0);
+ reg |= ((0x1 << (mp_ncpus - 1)) - 1) << 21;
+ write_cpu_clkdiv(CPU_DIVCLK_CTRL0, reg);
+ reg = read_cpu_clkdiv(CPU_DIVCLK_CTRL0);
+ reg |= 0x01000000;
+ write_cpu_clkdiv(CPU_DIVCLK_CTRL0, reg);
+
+ DELAY(100);
+ reg &= ~(0xf << 21);
+ write_cpu_clkdiv(CPU_DIVCLK_CTRL0, reg);
+ DELAY(100);
+
+ bus_space_write_4(fdtbus_bs_tag, MV_BASE, CPU_RESUME_CONTROL, 0);
+
+ for (cpu_num = 1; cpu_num < mp_ncpus; cpu_num++ )
+ bus_space_write_4(fdtbus_bs_tag, CPU_PMU(cpu_num), CPU_PMU_BOOT,
+ pmap_kextract((vm_offset_t)mpentry));
+
+ dcache_wbinv_poc_all();
+
+ for (cpu_num = 1; cpu_num < mp_ncpus; cpu_num++ )
+ bus_space_write_4(fdtbus_bs_tag, MP, MP_SW_RESET(cpu_num), 0);
+
+ /* XXX: Temporary workaround for hangup after releasing AP's */
+ wmb();
+ DELAY(10);
+
+ armadaxp_init_coher_fabric();
+}
diff --git a/sys/arm/mv/armadaxp/files.armadaxp b/sys/arm/mv/armadaxp/files.armadaxp
new file mode 100644
index 000000000000..2ca0836e3a15
--- /dev/null
+++ b/sys/arm/mv/armadaxp/files.armadaxp
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+arm/mv/armadaxp/armadaxp.c standard
+arm/mv/mpic.c standard
+arm/mv/rtc.c standard
+arm/mv/armadaxp/armadaxp_mp.c optional smp
+arm/mv/armadaxp/mptramp.S optional smp
+
diff --git a/sys/arm/mv/armadaxp/mptramp.S b/sys/arm/mv/armadaxp/mptramp.S
new file mode 100644
index 000000000000..056f5889dd9b
--- /dev/null
+++ b/sys/arm/mv/armadaxp/mptramp.S
@@ -0,0 +1,61 @@
+/*-
+ * Copyright 2011 Semihalf
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <machine/asm.h>
+#include <machine/armreg.h>
+
+__FBSDID("$FreeBSD$");
+
+.global _C_LABEL(mptramp_pmu_boot)
+
+ASENTRY_NP(mptramp)
+ mov r0, #0
+ mcr p15, 0, r0, c7, c7, 0
+
+ mrs r3, cpsr
+ bic r3, r3, #(PSR_MODE)
+ orr r3, r3, #(PSR_SVC32_MODE)
+ msr cpsr_fsxc, r3
+
+ mrc p15, 0, r0, c0, c0, 5
+ and r0, #0x0f /* Get CPU ID */
+
+ /* Read boot address for CPU */
+ mov r1, #0x100
+ mul r2, r0, r1
+ ldr r1, mptramp_pmu_boot
+ add r0, r2, r1
+ ldr r1, [r0], #0x00
+
+ mov pc, r1
+
+_C_LABEL(mptramp_pmu_boot):
+ .word 0x0
+
+END(mptramp)
+
+ .global _C_LABEL(mptramp_end)
+_C_LABEL(mptramp_end):
diff --git a/sys/arm/mv/armadaxp/std.armadaxp b/sys/arm/mv/armadaxp/std.armadaxp
new file mode 100644
index 000000000000..b720e3de68bf
--- /dev/null
+++ b/sys/arm/mv/armadaxp/std.armadaxp
@@ -0,0 +1,2 @@
+# $FreeBSD$
+
diff --git a/sys/arm/mv/armadaxp/std.mv78x60 b/sys/arm/mv/armadaxp/std.mv78x60
new file mode 100644
index 000000000000..a46ab8cc1c88
--- /dev/null
+++ b/sys/arm/mv/armadaxp/std.mv78x60
@@ -0,0 +1,4 @@
+# $FreeBSD$
+
+include "../mv/std-pj4b.mv"
+include "../mv/armadaxp/std.armadaxp"
diff --git a/sys/arm/mv/files.arm7 b/sys/arm/mv/files.arm7
new file mode 100644
index 000000000000..6f93fd6edbc5
--- /dev/null
+++ b/sys/arm/mv/files.arm7
@@ -0,0 +1,36 @@
+# $FreeBSD$
+arm/mv/armada38x/armada38x.c standard
+arm/mv/armadaxp/armadaxp.c standard
+
+arm/mv/gpio.c optional gpio
+arm/mv/mv_common.c standard
+arm/mv/mv_armv7_machdep.c standard
+arm/mv/mv_pci_ctrl.c optional pci | fdt
+arm/mv/mv_pci.c optional pci
+arm/mv/timer.c standard
+
+arm/mv/mpic.c standard
+arm/mv/armada/thermal.c optional fdt
+arm/mv/armada/wdt.c optional fdt
+arm/mv/armada38x/armada38x_mp.c optional smp
+arm/mv/armada38x/pmsu.c standard
+arm/mv/armada38x/armada38x_rtc.c standard
+arm/mv/armada38x/armada38x_pl310.c optional pl310
+arm/mv/mv_spi.c optional mv_spi spibus
+dev/sdhci/sdhci_fdt.c optional sdhci
+
+arm/mv/rtc.c standard
+arm/mv/armadaxp/armadaxp_mp.c optional smp
+arm/mv/armadaxp/mptramp.S optional smp
+dev/cesa/cesa.c optional cesa
+dev/iicbus/twsi/mv_twsi.c optional twsi
+dev/mge/if_mge.c optional mge
+dev/neta/if_mvneta_fdt.c optional neta fdt
+dev/neta/if_mvneta.c optional neta mdio mii
+dev/mvs/mvs_soc.c optional mvs
+dev/uart/uart_dev_ns8250.c optional uart
+dev/uart/uart_dev_snps.c optional uart
+dev/usb/controller/ehci_mv.c optional ehci
+dev/usb/controller/generic_xhci.c optional xhci
+dev/usb/controller/generic_xhci_fdt.c optional xhci
+dev/ahci/ahci_mv_fdt.c optional ahci
diff --git a/sys/arm/mv/gpio.c b/sys/arm/mv/gpio.c
new file mode 100644
index 000000000000..7fd80cb7c3d7
--- /dev/null
+++ b/sys/arm/mv/gpio.c
@@ -0,0 +1,1213 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2006 Benno Rice.
+ * Copyright (C) 2008 MARVELL INTERNATIONAL LTD.
+ * Copyright (c) 2017 Semihalf.
+ * All rights reserved.
+ *
+ * Adapted and extended for Marvell SoCs by Semihalf.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * from: FreeBSD: //depot/projects/arm/src/sys/arm/xscale/pxa2x0/pxa2x0_gpio.c, rev 1
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/interrupt.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/queue.h>
+#include <sys/timetc.h>
+#include <sys/callout.h>
+#include <sys/gpio.h>
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <dev/gpio/gpiobusvar.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/mv/mvvar.h>
+#include <arm/mv/mvreg.h>
+
+#include "gpio_if.h"
+
+#define GPIO_MAX_INTR_COUNT 8
+#define GPIO_PINS_PER_REG 32
+#define GPIO_GENERIC_CAP (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \
+ GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL | \
+ GPIO_PIN_TRISTATE | GPIO_PIN_PULLUP | \
+ GPIO_PIN_PULLDOWN | GPIO_PIN_INVIN | \
+ GPIO_PIN_INVOUT)
+
+#define DEBOUNCE_CHECK_MS 1
+#define DEBOUNCE_LO_HI_MS 2
+#define DEBOUNCE_HI_LO_MS 2
+#define DEBOUNCE_CHECK_TICKS ((hz / 1000) * DEBOUNCE_CHECK_MS)
+
+struct mv_gpio_softc {
+ device_t dev;
+ device_t sc_busdev;
+ struct resource * mem_res;
+ int mem_rid;
+ struct resource * irq_res[GPIO_MAX_INTR_COUNT];
+ int irq_rid[GPIO_MAX_INTR_COUNT];
+ struct intr_event * gpio_events[MV_GPIO_MAX_NPINS];
+ void *ih_cookie[GPIO_MAX_INTR_COUNT];
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+ uint32_t offset;
+ struct mtx mutex;
+ uint8_t pin_num; /* number of GPIO pins */
+ uint8_t irq_num; /* number of real IRQs occupied by GPIO controller */
+ struct gpio_pin gpio_setup[MV_GPIO_MAX_NPINS];
+
+ /* Used for debouncing. */
+ uint32_t debounced_state_lo;
+ uint32_t debounced_state_hi;
+ struct callout **debounce_callouts;
+ int *debounce_counters;
+};
+
+struct mv_gpio_pindev {
+ device_t dev;
+ int pin;
+};
+
+static int mv_gpio_probe(device_t);
+static int mv_gpio_attach(device_t);
+static int mv_gpio_intr(device_t, void *);
+
+static void mv_gpio_double_edge_init(device_t, int);
+
+static int mv_gpio_debounce_setup(device_t, int);
+static int mv_gpio_debounce_prepare(device_t, int);
+static int mv_gpio_debounce_init(device_t, int);
+static void mv_gpio_debounce_start(device_t, int);
+static void mv_gpio_debounce(void *);
+static void mv_gpio_debounced_state_set(device_t, int, uint8_t);
+static uint32_t mv_gpio_debounced_state_get(device_t, int);
+
+static void mv_gpio_exec_intr_handlers(device_t, uint32_t, int);
+static void mv_gpio_intr_handler(device_t, int);
+static uint32_t mv_gpio_reg_read(device_t, uint32_t);
+static void mv_gpio_reg_write(device_t, uint32_t, uint32_t);
+static void mv_gpio_reg_set(device_t, uint32_t, uint32_t);
+static void mv_gpio_reg_clear(device_t, uint32_t, uint32_t);
+
+static void mv_gpio_blink(device_t, uint32_t, uint8_t);
+static void mv_gpio_polarity(device_t, uint32_t, uint8_t, uint8_t);
+static void mv_gpio_level(device_t, uint32_t, uint8_t);
+static void mv_gpio_edge(device_t, uint32_t, uint8_t);
+static void mv_gpio_out_en(device_t, uint32_t, uint8_t);
+static void mv_gpio_int_ack(struct mv_gpio_pindev *);
+static void mv_gpio_value_set(device_t, uint32_t, uint8_t);
+static uint32_t mv_gpio_value_get(device_t, uint32_t, uint8_t);
+
+static void mv_gpio_intr_mask(struct mv_gpio_pindev *);
+static void mv_gpio_intr_unmask(struct mv_gpio_pindev *);
+
+void mv_gpio_finish_intrhandler(struct mv_gpio_pindev *);
+int mv_gpio_setup_intrhandler(device_t, const char *,
+ driver_filter_t *, void (*)(void *), void *,
+ int, int, void **);
+int mv_gpio_configure(device_t, uint32_t, uint32_t, uint32_t);
+void mv_gpio_out(device_t, uint32_t, uint8_t, uint8_t);
+uint8_t mv_gpio_in(device_t, uint32_t);
+
+/*
+ * GPIO interface
+ */
+static device_t mv_gpio_get_bus(device_t);
+static int mv_gpio_pin_max(device_t, int *);
+static int mv_gpio_pin_getcaps(device_t, uint32_t, uint32_t *);
+static int mv_gpio_pin_getflags(device_t, uint32_t, uint32_t *);
+static int mv_gpio_pin_getname(device_t, uint32_t, char *);
+static int mv_gpio_pin_setflags(device_t, uint32_t, uint32_t);
+static int mv_gpio_pin_set(device_t, uint32_t, unsigned int);
+static int mv_gpio_pin_get(device_t, uint32_t, unsigned int *);
+static int mv_gpio_pin_toggle(device_t, uint32_t);
+static int mv_gpio_map_gpios(device_t, phandle_t, phandle_t,
+ int, pcell_t *, uint32_t *, uint32_t *);
+
+#define MV_GPIO_LOCK() mtx_lock_spin(&sc->mutex)
+#define MV_GPIO_UNLOCK() mtx_unlock_spin(&sc->mutex)
+#define MV_GPIO_ASSERT_LOCKED() mtx_assert(&sc->mutex, MA_OWNED)
+
+static device_method_t mv_gpio_methods[] = {
+ DEVMETHOD(device_probe, mv_gpio_probe),
+ DEVMETHOD(device_attach, mv_gpio_attach),
+
+ /* GPIO protocol */
+ DEVMETHOD(gpio_get_bus, mv_gpio_get_bus),
+ DEVMETHOD(gpio_pin_max, mv_gpio_pin_max),
+ DEVMETHOD(gpio_pin_getname, mv_gpio_pin_getname),
+ DEVMETHOD(gpio_pin_getflags, mv_gpio_pin_getflags),
+ DEVMETHOD(gpio_pin_getcaps, mv_gpio_pin_getcaps),
+ DEVMETHOD(gpio_pin_setflags, mv_gpio_pin_setflags),
+ DEVMETHOD(gpio_pin_get, mv_gpio_pin_get),
+ DEVMETHOD(gpio_pin_set, mv_gpio_pin_set),
+ DEVMETHOD(gpio_pin_toggle, mv_gpio_pin_toggle),
+ DEVMETHOD(gpio_map_gpios, mv_gpio_map_gpios),
+
+ DEVMETHOD_END
+};
+
+static driver_t mv_gpio_driver = {
+ "gpio",
+ mv_gpio_methods,
+ sizeof(struct mv_gpio_softc),
+};
+
+static devclass_t mv_gpio_devclass;
+
+EARLY_DRIVER_MODULE(mv_gpio, simplebus, mv_gpio_driver, mv_gpio_devclass, 0, 0,
+ BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LAST);
+
+struct ofw_compat_data compat_data[] = {
+ { "mrvl,gpio", 1 },
+ { "marvell,orion-gpio", 1 },
+ { NULL, 0 }
+};
+
+static int
+mv_gpio_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Marvell Integrated GPIO Controller");
+ return (0);
+}
+
+static int
+mv_gpio_setup_interrupts(struct mv_gpio_softc *sc, phandle_t node)
+{
+ phandle_t iparent;
+ pcell_t irq_cells;
+ int i, size;
+
+ /* Find root interrupt controller */
+ iparent = ofw_bus_find_iparent(node);
+ if (iparent == 0) {
+ device_printf(sc->dev, "No interrupt-parrent found. "
+ "Error in DTB\n");
+ return (ENXIO);
+ } else {
+ /* While at parent - store interrupt cells prop */
+ if (OF_searchencprop(OF_node_from_xref(iparent),
+ "#interrupt-cells", &irq_cells, sizeof(irq_cells)) == -1) {
+ device_printf(sc->dev, "DTB: Missing #interrupt-cells "
+ "property in interrupt parent node\n");
+ return (ENXIO);
+ }
+ }
+
+ size = OF_getproplen(node, "interrupts");
+ if (size != -1) {
+ size = size / sizeof(pcell_t);
+ size = size / irq_cells;
+ sc->irq_num = size;
+ device_printf(sc->dev, "%d IRQs available\n", sc->irq_num);
+ } else {
+ device_printf(sc->dev, "ERROR: no interrupts entry found!\n");
+ return (ENXIO);
+ }
+
+ for (i = 0; i < sc->irq_num; i++) {
+ sc->irq_rid[i] = i;
+ sc->irq_res[i] = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ,
+ &sc->irq_rid[i], RF_ACTIVE);
+ if (!sc->irq_res[i]) {
+ mtx_destroy(&sc->mutex);
+ device_printf(sc->dev,
+ "could not allocate gpio%d interrupt\n", i+1);
+ return (ENXIO);
+ }
+ }
+
+ device_printf(sc->dev, "Disable interrupts (offset = %x + EDGE(0x18)\n", sc->offset);
+ /* Disable all interrupts */
+ bus_space_write_4(sc->bst, sc->bsh, sc->offset + GPIO_INT_EDGE_MASK, 0);
+ device_printf(sc->dev, "Disable interrupts (offset = %x + LEV(0x1C))\n", sc->offset);
+ bus_space_write_4(sc->bst, sc->bsh, sc->offset + GPIO_INT_LEV_MASK, 0);
+
+ for (i = 0; i < sc->irq_num; i++) {
+ device_printf(sc->dev, "Setup intr %d\n", i);
+ if (bus_setup_intr(sc->dev, sc->irq_res[i],
+ INTR_TYPE_MISC,
+ (driver_filter_t *)mv_gpio_intr, NULL,
+ sc, &sc->ih_cookie[i]) != 0) {
+ mtx_destroy(&sc->mutex);
+ bus_release_resource(sc->dev, SYS_RES_IRQ,
+ sc->irq_rid[i], sc->irq_res[i]);
+ device_printf(sc->dev, "could not set up intr %d\n", i);
+ return (ENXIO);
+ }
+ }
+
+ /* Clear interrupt status. */
+ device_printf(sc->dev, "Clear int status (offset = %x)\n", sc->offset);
+ bus_space_write_4(sc->bst, sc->bsh, sc->offset + GPIO_INT_CAUSE, 0);
+
+ sc->debounce_callouts = (struct callout **)malloc(sc->pin_num *
+ sizeof(struct callout *), M_DEVBUF, M_WAITOK | M_ZERO);
+ if (sc->debounce_callouts == NULL)
+ return (ENOMEM);
+
+ sc->debounce_counters = (int *)malloc(sc->pin_num * sizeof(int),
+ M_DEVBUF, M_WAITOK);
+ if (sc->debounce_counters == NULL)
+ return (ENOMEM);
+
+ return (0);
+}
+
+static int
+mv_gpio_attach(device_t dev)
+{
+ int i, rv;
+ struct mv_gpio_softc *sc;
+ phandle_t node;
+ pcell_t pincnt = 0;
+
+ sc = (struct mv_gpio_softc *)device_get_softc(dev);
+ if (sc == NULL)
+ return (ENXIO);
+
+ node = ofw_bus_get_node(dev);
+ sc->dev = dev;
+
+ if (OF_getencprop(node, "pin-count", &pincnt, sizeof(pcell_t)) >= 0 ||
+ OF_getencprop(node, "ngpios", &pincnt, sizeof(pcell_t)) >= 0) {
+ sc->pin_num = MIN(pincnt, MV_GPIO_MAX_NPINS);
+ if (bootverbose)
+ device_printf(dev, "%d pins available\n", sc->pin_num);
+ } else {
+ device_printf(dev, "ERROR: no pin-count or ngpios entry found!\n");
+ return (ENXIO);
+ }
+
+ if (OF_getencprop(node, "offset", &sc->offset, sizeof(sc->offset)) == -1)
+ sc->offset = 0;
+
+ /* Assign generic capabilities to every gpio pin */
+ for(i = 0; i < sc->pin_num; i++)
+ sc->gpio_setup[i].gp_caps = GPIO_GENERIC_CAP;
+
+ mtx_init(&sc->mutex, device_get_nameunit(dev), NULL, MTX_SPIN);
+
+ sc->mem_rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid,
+ RF_ACTIVE | RF_SHAREABLE );
+
+ if (!sc->mem_res) {
+ mtx_destroy(&sc->mutex);
+ device_printf(dev, "could not allocate memory window\n");
+ return (ENXIO);
+ }
+
+ sc->bst = rman_get_bustag(sc->mem_res);
+ sc->bsh = rman_get_bushandle(sc->mem_res);
+
+ rv = mv_gpio_setup_interrupts(sc, node);
+ if (rv != 0)
+ return (rv);
+
+ sc->sc_busdev = gpiobus_attach_bus(dev);
+ if (sc->sc_busdev == NULL) {
+ mtx_destroy(&sc->mutex);
+ bus_release_resource(dev, SYS_RES_IRQ,
+ sc->irq_rid[i], sc->irq_res[i]);
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static int
+mv_gpio_intr(device_t dev, void *arg)
+{
+ uint32_t int_cause, gpio_val;
+ struct mv_gpio_softc *sc;
+ sc = (struct mv_gpio_softc *)device_get_softc(dev);
+
+ MV_GPIO_LOCK();
+
+ /*
+ * According to documentation, edge sensitive interrupts are asserted
+ * when unmasked GPIO_INT_CAUSE register bits are set.
+ */
+ int_cause = mv_gpio_reg_read(dev, GPIO_INT_CAUSE);
+ int_cause &= mv_gpio_reg_read(dev, GPIO_INT_EDGE_MASK);
+
+ /*
+ * Level sensitive interrupts are asserted when unmasked GPIO_DATA_IN
+ * register bits are set.
+ */
+ gpio_val = mv_gpio_reg_read(dev, GPIO_DATA_IN);
+ gpio_val &= mv_gpio_reg_read(dev, GPIO_INT_LEV_MASK);
+
+ mv_gpio_exec_intr_handlers(dev, int_cause | gpio_val, 0);
+
+ MV_GPIO_UNLOCK();
+
+ return (FILTER_HANDLED);
+}
+
+/*
+ * GPIO interrupt handling
+ */
+
+void
+mv_gpio_finish_intrhandler(struct mv_gpio_pindev *s)
+{
+ /* When we acheive full interrupt support
+ * This function will be opposite to
+ * mv_gpio_setup_intrhandler
+ */
+
+ /* Now it exists only to remind that
+ * there should be place to free mv_gpio_pindev
+ * allocated by mv_gpio_setup_intrhandler
+ */
+ free(s, M_DEVBUF);
+}
+
+int
+mv_gpio_setup_intrhandler(device_t dev, const char *name, driver_filter_t *filt,
+ void (*hand)(void *), void *arg, int pin, int flags, void **cookiep)
+{
+ struct intr_event *event;
+ int error;
+ struct mv_gpio_pindev *s;
+ struct mv_gpio_softc *sc;
+ sc = (struct mv_gpio_softc *)device_get_softc(dev);
+ s = malloc(sizeof(struct mv_gpio_pindev), M_DEVBUF, M_NOWAIT | M_ZERO);
+
+ if (pin < 0 || pin >= sc->pin_num)
+ return (ENXIO);
+ event = sc->gpio_events[pin];
+ if (event == NULL) {
+ MV_GPIO_LOCK();
+ if (sc->gpio_setup[pin].gp_flags & MV_GPIO_IN_DEBOUNCE) {
+ error = mv_gpio_debounce_init(dev, pin);
+ if (error != 0) {
+ MV_GPIO_UNLOCK();
+ return (error);
+ }
+ } else if (sc->gpio_setup[pin].gp_flags & MV_GPIO_IN_IRQ_DOUBLE_EDGE)
+ mv_gpio_double_edge_init(dev, pin);
+ MV_GPIO_UNLOCK();
+ error = intr_event_create(&event, (void *)s, 0, pin,
+ (void (*)(void *))mv_gpio_intr_mask,
+ (void (*)(void *))mv_gpio_intr_unmask,
+ (void (*)(void *))mv_gpio_int_ack,
+ NULL,
+ "gpio%d:", pin);
+ if (error != 0)
+ return (error);
+ sc->gpio_events[pin] = event;
+ }
+
+ intr_event_add_handler(event, name, filt, hand, arg,
+ intr_priority(flags), flags, cookiep);
+ return (0);
+}
+
+static void
+mv_gpio_intr_mask(struct mv_gpio_pindev *s)
+{
+ struct mv_gpio_softc *sc;
+ sc = (struct mv_gpio_softc *)device_get_softc(s->dev);
+
+ if (s->pin >= sc->pin_num)
+ return;
+
+ MV_GPIO_LOCK();
+
+ if (sc->gpio_setup[s->pin].gp_flags & (MV_GPIO_IN_IRQ_EDGE |
+ MV_GPIO_IN_IRQ_DOUBLE_EDGE))
+ mv_gpio_edge(s->dev, s->pin, 0);
+ else
+ mv_gpio_level(s->dev, s->pin, 0);
+
+ /*
+ * The interrupt has to be acknowledged before scheduling an interrupt
+ * thread. This way we allow for interrupt source to trigger again
+ * (which can happen with shared IRQs e.g. PCI) while processing the
+ * current event.
+ */
+ mv_gpio_int_ack(s);
+
+ MV_GPIO_UNLOCK();
+
+ return;
+}
+
+static void
+mv_gpio_intr_unmask(struct mv_gpio_pindev *s)
+{
+ struct mv_gpio_softc *sc;
+ sc = (struct mv_gpio_softc *)device_get_softc(s->dev);
+
+ if (s->pin >= sc->pin_num)
+ return;
+
+ MV_GPIO_LOCK();
+
+ if (sc->gpio_setup[s->pin].gp_flags & (MV_GPIO_IN_IRQ_EDGE |
+ MV_GPIO_IN_IRQ_DOUBLE_EDGE))
+ mv_gpio_edge(s->dev, s->pin, 1);
+ else
+ mv_gpio_level(s->dev, s->pin, 1);
+
+ MV_GPIO_UNLOCK();
+
+ return;
+}
+
+static void
+mv_gpio_exec_intr_handlers(device_t dev, uint32_t status, int high)
+{
+ int i, pin;
+ struct mv_gpio_softc *sc;
+ sc = (struct mv_gpio_softc *)device_get_softc(dev);
+
+ MV_GPIO_ASSERT_LOCKED();
+
+ i = 0;
+ while (status != 0) {
+ if (status & 1) {
+ pin = (high ? (i + GPIO_PINS_PER_REG) : i);
+ if (sc->gpio_setup[pin].gp_flags & MV_GPIO_IN_DEBOUNCE)
+ mv_gpio_debounce_start(dev, pin);
+ else if (sc->gpio_setup[pin].gp_flags & MV_GPIO_IN_IRQ_DOUBLE_EDGE) {
+ mv_gpio_polarity(dev, pin, 0, 1);
+ mv_gpio_intr_handler(dev, pin);
+ } else
+ mv_gpio_intr_handler(dev, pin);
+ }
+ status >>= 1;
+ i++;
+ }
+}
+
+static void
+mv_gpio_intr_handler(device_t dev, int pin)
+{
+ struct intr_irqsrc isrc;
+ struct mv_gpio_softc *sc;
+ sc = (struct mv_gpio_softc *)device_get_softc(dev);
+
+ MV_GPIO_ASSERT_LOCKED();
+
+#ifdef INTR_SOLO
+ isrc.isrc_filter = NULL;
+#endif
+ isrc.isrc_event = sc->gpio_events[pin];
+
+ if (isrc.isrc_event == NULL ||
+ CK_SLIST_EMPTY(&isrc.isrc_event->ie_handlers))
+ return;
+
+ intr_isrc_dispatch(&isrc, NULL);
+}
+
+int
+mv_gpio_configure(device_t dev, uint32_t pin, uint32_t flags, uint32_t mask)
+{
+ int error;
+ struct mv_gpio_softc *sc;
+ sc = (struct mv_gpio_softc *)device_get_softc(dev);
+ error = 0;
+
+ if (pin >= sc->pin_num)
+ return (EINVAL);
+
+ /* check flags consistency */
+ if (((flags & mask) & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) ==
+ (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT))
+ return (EINVAL);
+
+ if (mask & MV_GPIO_IN_DEBOUNCE) {
+ if (sc->irq_num == 0)
+ return (EINVAL);
+ error = mv_gpio_debounce_prepare(dev, pin);
+ if (error != 0)
+ return (error);
+ }
+
+ MV_GPIO_LOCK();
+
+ if ((mask & flags) & GPIO_PIN_INPUT)
+ mv_gpio_out_en(dev, pin, 0);
+ if ((mask & flags) & GPIO_PIN_OUTPUT) {
+ if ((flags & mask) & GPIO_PIN_OPENDRAIN)
+ mv_gpio_value_set(dev, pin, 0);
+ else
+ mv_gpio_value_set(dev, pin, 1);
+ mv_gpio_out_en(dev, pin, 1);
+ }
+
+ if (mask & MV_GPIO_OUT_BLINK)
+ mv_gpio_blink(dev, pin, flags & MV_GPIO_OUT_BLINK);
+ if (mask & MV_GPIO_IN_POL_LOW)
+ mv_gpio_polarity(dev, pin, flags & MV_GPIO_IN_POL_LOW, 0);
+ if (mask & MV_GPIO_IN_DEBOUNCE) {
+ error = mv_gpio_debounce_setup(dev, pin);
+ if (error) {
+ MV_GPIO_UNLOCK();
+ return (error);
+ }
+ }
+
+ sc->gpio_setup[pin].gp_flags &= ~(mask);
+ sc->gpio_setup[pin].gp_flags |= (flags & mask);
+
+ MV_GPIO_UNLOCK();
+
+ return (0);
+}
+
+static void
+mv_gpio_double_edge_init(device_t dev, int pin)
+{
+ uint8_t raw_read;
+ struct mv_gpio_softc *sc;
+ sc = (struct mv_gpio_softc *)device_get_softc(dev);
+
+ MV_GPIO_ASSERT_LOCKED();
+
+ raw_read = (mv_gpio_value_get(dev, pin, 1) ? 1 : 0);
+
+ if (raw_read)
+ mv_gpio_polarity(dev, pin, 1, 0);
+ else
+ mv_gpio_polarity(dev, pin, 0, 0);
+}
+
+static int
+mv_gpio_debounce_setup(device_t dev, int pin)
+{
+ struct callout *c;
+ struct mv_gpio_softc *sc;
+
+ sc = (struct mv_gpio_softc *)device_get_softc(dev);
+
+ MV_GPIO_ASSERT_LOCKED();
+
+ c = sc->debounce_callouts[pin];
+ if (c == NULL)
+ return (ENXIO);
+
+ if (callout_active(c))
+ callout_deactivate(c);
+
+ callout_stop(c);
+
+ return (0);
+}
+
+static int
+mv_gpio_debounce_prepare(device_t dev, int pin)
+{
+ struct callout *c;
+ struct mv_gpio_softc *sc;
+
+ sc = (struct mv_gpio_softc *)device_get_softc(dev);
+
+ c = sc->debounce_callouts[pin];
+ if (c == NULL) {
+ c = (struct callout *)malloc(sizeof(struct callout),
+ M_DEVBUF, M_WAITOK);
+ sc->debounce_callouts[pin] = c;
+ if (c == NULL)
+ return (ENOMEM);
+ callout_init(c, 1);
+ }
+
+ return (0);
+}
+
+static int
+mv_gpio_debounce_init(device_t dev, int pin)
+{
+ uint8_t raw_read;
+ int *cnt;
+ struct mv_gpio_softc *sc;
+
+ sc = (struct mv_gpio_softc *)device_get_softc(dev);
+
+ MV_GPIO_ASSERT_LOCKED();
+
+ cnt = &sc->debounce_counters[pin];
+ raw_read = (mv_gpio_value_get(dev, pin, 1) ? 1 : 0);
+ if (raw_read) {
+ mv_gpio_polarity(dev, pin, 1, 0);
+ *cnt = DEBOUNCE_HI_LO_MS / DEBOUNCE_CHECK_MS;
+ } else {
+ mv_gpio_polarity(dev, pin, 0, 0);
+ *cnt = DEBOUNCE_LO_HI_MS / DEBOUNCE_CHECK_MS;
+ }
+
+ mv_gpio_debounced_state_set(dev, pin, raw_read);
+
+ return (0);
+}
+
+static void
+mv_gpio_debounce_start(device_t dev, int pin)
+{
+ struct callout *c;
+ struct mv_gpio_pindev s = {dev, pin};
+ struct mv_gpio_pindev *sd;
+ struct mv_gpio_softc *sc;
+ sc = (struct mv_gpio_softc *)device_get_softc(dev);
+
+ MV_GPIO_ASSERT_LOCKED();
+
+ c = sc->debounce_callouts[pin];
+ if (c == NULL) {
+ mv_gpio_int_ack(&s);
+ return;
+ }
+
+ if (callout_pending(c) || callout_active(c)) {
+ mv_gpio_int_ack(&s);
+ return;
+ }
+
+ sd = (struct mv_gpio_pindev *)malloc(sizeof(struct mv_gpio_pindev),
+ M_DEVBUF, M_WAITOK);
+ if (sd == NULL) {
+ mv_gpio_int_ack(&s);
+ return;
+ }
+ sd->pin = pin;
+ sd->dev = dev;
+
+ callout_reset(c, DEBOUNCE_CHECK_TICKS, mv_gpio_debounce, sd);
+}
+
+static void
+mv_gpio_debounce(void *arg)
+{
+ uint8_t raw_read, last_state;
+ int pin;
+ device_t dev;
+ int *debounce_counter;
+ struct mv_gpio_softc *sc;
+ struct mv_gpio_pindev *s;
+
+ s = (struct mv_gpio_pindev *)arg;
+ dev = s->dev;
+ pin = s->pin;
+ sc = (struct mv_gpio_softc *)device_get_softc(dev);
+
+ MV_GPIO_LOCK();
+
+ raw_read = (mv_gpio_value_get(dev, pin, 1) ? 1 : 0);
+ last_state = (mv_gpio_debounced_state_get(dev, pin) ? 1 : 0);
+ debounce_counter = &sc->debounce_counters[pin];
+
+ if (raw_read == last_state) {
+ if (last_state)
+ *debounce_counter = DEBOUNCE_HI_LO_MS /
+ DEBOUNCE_CHECK_MS;
+ else
+ *debounce_counter = DEBOUNCE_LO_HI_MS /
+ DEBOUNCE_CHECK_MS;
+
+ callout_reset(sc->debounce_callouts[pin],
+ DEBOUNCE_CHECK_TICKS, mv_gpio_debounce, arg);
+ } else {
+ *debounce_counter = *debounce_counter - 1;
+ if (*debounce_counter != 0)
+ callout_reset(sc->debounce_callouts[pin],
+ DEBOUNCE_CHECK_TICKS, mv_gpio_debounce, arg);
+ else {
+ mv_gpio_debounced_state_set(dev, pin, raw_read);
+
+ if (last_state)
+ *debounce_counter = DEBOUNCE_HI_LO_MS /
+ DEBOUNCE_CHECK_MS;
+ else
+ *debounce_counter = DEBOUNCE_LO_HI_MS /
+ DEBOUNCE_CHECK_MS;
+
+ if (((sc->gpio_setup[pin].gp_flags & MV_GPIO_IN_POL_LOW) &&
+ (raw_read == 0)) ||
+ (((sc->gpio_setup[pin].gp_flags & MV_GPIO_IN_POL_LOW) == 0) &&
+ raw_read) ||
+ (sc->gpio_setup[pin].gp_flags & MV_GPIO_IN_IRQ_DOUBLE_EDGE))
+ mv_gpio_intr_handler(dev, pin);
+
+ /* Toggle polarity for next edge. */
+ mv_gpio_polarity(dev, pin, 0, 1);
+
+ free(arg, M_DEVBUF);
+ callout_deactivate(sc->debounce_callouts[pin]);
+ }
+ }
+
+ MV_GPIO_UNLOCK();
+}
+
+static void
+mv_gpio_debounced_state_set(device_t dev, int pin, uint8_t new_state)
+{
+ uint32_t *old_state;
+ struct mv_gpio_softc *sc;
+ sc = (struct mv_gpio_softc *)device_get_softc(dev);
+
+ MV_GPIO_ASSERT_LOCKED();
+
+ if (pin >= GPIO_PINS_PER_REG) {
+ old_state = &sc->debounced_state_hi;
+ pin -= GPIO_PINS_PER_REG;
+ } else
+ old_state = &sc->debounced_state_lo;
+
+ if (new_state)
+ *old_state |= (1 << pin);
+ else
+ *old_state &= ~(1 << pin);
+}
+
+static uint32_t
+mv_gpio_debounced_state_get(device_t dev, int pin)
+{
+ uint32_t *state;
+ struct mv_gpio_softc *sc;
+ sc = (struct mv_gpio_softc *)device_get_softc(dev);
+
+ MV_GPIO_ASSERT_LOCKED();
+
+ if (pin >= GPIO_PINS_PER_REG) {
+ state = &sc->debounced_state_hi;
+ pin -= GPIO_PINS_PER_REG;
+ } else
+ state = &sc->debounced_state_lo;
+
+ return (*state & (1 << pin));
+}
+
+void
+mv_gpio_out(device_t dev, uint32_t pin, uint8_t val, uint8_t enable)
+{
+ struct mv_gpio_softc *sc;
+ sc = (struct mv_gpio_softc *)device_get_softc(dev);
+
+ MV_GPIO_LOCK();
+
+ mv_gpio_value_set(dev, pin, val);
+ mv_gpio_out_en(dev, pin, enable);
+
+ MV_GPIO_UNLOCK();
+}
+
+uint8_t
+mv_gpio_in(device_t dev, uint32_t pin)
+{
+ uint8_t state;
+ struct mv_gpio_softc *sc;
+ sc = (struct mv_gpio_softc *)device_get_softc(dev);
+
+ MV_GPIO_ASSERT_LOCKED();
+
+ if (sc->gpio_setup[pin].gp_flags & MV_GPIO_IN_DEBOUNCE) {
+ if (sc->gpio_setup[pin].gp_flags & MV_GPIO_IN_POL_LOW)
+ state = (mv_gpio_debounced_state_get(dev, pin) ? 0 : 1);
+ else
+ state = (mv_gpio_debounced_state_get(dev, pin) ? 1 : 0);
+ } else if (sc->gpio_setup[pin].gp_flags & MV_GPIO_IN_IRQ_DOUBLE_EDGE) {
+ if (sc->gpio_setup[pin].gp_flags & MV_GPIO_IN_POL_LOW)
+ state = (mv_gpio_value_get(dev, pin, 1) ? 0 : 1);
+ else
+ state = (mv_gpio_value_get(dev, pin, 1) ? 1 : 0);
+ } else
+ state = (mv_gpio_value_get(dev, pin, 0) ? 1 : 0);
+
+ return (state);
+}
+
+static uint32_t
+mv_gpio_reg_read(device_t dev, uint32_t reg)
+{
+ struct mv_gpio_softc *sc;
+ sc = (struct mv_gpio_softc *)device_get_softc(dev);
+
+ return (bus_space_read_4(sc->bst, sc->bsh, sc->offset + reg));
+}
+
+static void
+mv_gpio_reg_write(device_t dev, uint32_t reg, uint32_t val)
+{
+ struct mv_gpio_softc *sc;
+ sc = (struct mv_gpio_softc *)device_get_softc(dev);
+
+ bus_space_write_4(sc->bst, sc->bsh, sc->offset + reg, val);
+}
+
+static void
+mv_gpio_reg_set(device_t dev, uint32_t reg, uint32_t pin)
+{
+ uint32_t reg_val;
+
+ reg_val = mv_gpio_reg_read(dev, reg);
+ reg_val |= GPIO(pin);
+ mv_gpio_reg_write(dev, reg, reg_val);
+}
+
+static void
+mv_gpio_reg_clear(device_t dev, uint32_t reg, uint32_t pin)
+{
+ uint32_t reg_val;
+
+ reg_val = mv_gpio_reg_read(dev, reg);
+ reg_val &= ~(GPIO(pin));
+ mv_gpio_reg_write(dev, reg, reg_val);
+}
+
+static void
+mv_gpio_out_en(device_t dev, uint32_t pin, uint8_t enable)
+{
+ uint32_t reg;
+ struct mv_gpio_softc *sc;
+ sc = (struct mv_gpio_softc *)device_get_softc(dev);
+
+ if (pin >= sc->pin_num)
+ return;
+
+ reg = GPIO_DATA_OUT_EN_CTRL;
+
+ if (enable)
+ mv_gpio_reg_clear(dev, reg, pin);
+ else
+ mv_gpio_reg_set(dev, reg, pin);
+}
+
+static void
+mv_gpio_blink(device_t dev, uint32_t pin, uint8_t enable)
+{
+ uint32_t reg;
+ struct mv_gpio_softc *sc;
+ sc = (struct mv_gpio_softc *)device_get_softc(dev);
+
+ if (pin >= sc->pin_num)
+ return;
+
+ reg = GPIO_BLINK_EN;
+
+ if (enable)
+ mv_gpio_reg_set(dev, reg, pin);
+ else
+ mv_gpio_reg_clear(dev, reg, pin);
+}
+
+static void
+mv_gpio_polarity(device_t dev, uint32_t pin, uint8_t enable, uint8_t toggle)
+{
+ uint32_t reg, reg_val;
+ struct mv_gpio_softc *sc;
+ sc = (struct mv_gpio_softc *)device_get_softc(dev);
+
+ if (pin >= sc->pin_num)
+ return;
+
+ reg = GPIO_DATA_IN_POLAR;
+
+ if (toggle) {
+ reg_val = mv_gpio_reg_read(dev, reg) & GPIO(pin);
+ if (reg_val)
+ mv_gpio_reg_clear(dev, reg, pin);
+ else
+ mv_gpio_reg_set(dev, reg, pin);
+ } else if (enable)
+ mv_gpio_reg_set(dev, reg, pin);
+ else
+ mv_gpio_reg_clear(dev, reg, pin);
+}
+
+static void
+mv_gpio_level(device_t dev, uint32_t pin, uint8_t enable)
+{
+ uint32_t reg;
+ struct mv_gpio_softc *sc;
+ sc = (struct mv_gpio_softc *)device_get_softc(dev);
+
+ if (pin >= sc->pin_num)
+ return;
+
+ reg = GPIO_INT_LEV_MASK;
+
+ if (enable)
+ mv_gpio_reg_set(dev, reg, pin);
+ else
+ mv_gpio_reg_clear(dev, reg, pin);
+}
+
+static void
+mv_gpio_edge(device_t dev, uint32_t pin, uint8_t enable)
+{
+ uint32_t reg;
+ struct mv_gpio_softc *sc;
+ sc = (struct mv_gpio_softc *)device_get_softc(dev);
+
+ if (pin >= sc->pin_num)
+ return;
+
+ reg = GPIO_INT_EDGE_MASK;
+
+ if (enable)
+ mv_gpio_reg_set(dev, reg, pin);
+ else
+ mv_gpio_reg_clear(dev, reg, pin);
+}
+
+static void
+mv_gpio_int_ack(struct mv_gpio_pindev *s)
+{
+ uint32_t reg, pin;
+ struct mv_gpio_softc *sc;
+ sc = (struct mv_gpio_softc *)device_get_softc(s->dev);
+ pin = s->pin;
+
+ if (pin >= sc->pin_num)
+ return;
+
+ reg = GPIO_INT_CAUSE;
+
+ mv_gpio_reg_clear(s->dev, reg, pin);
+}
+
+static uint32_t
+mv_gpio_value_get(device_t dev, uint32_t pin, uint8_t exclude_polar)
+{
+ uint32_t reg, polar_reg, reg_val, polar_reg_val;
+ struct mv_gpio_softc *sc;
+ sc = (struct mv_gpio_softc *)device_get_softc(dev);
+
+ if (pin >= sc->pin_num)
+ return (0);
+
+ reg = GPIO_DATA_IN;
+ polar_reg = GPIO_DATA_IN_POLAR;
+
+ reg_val = mv_gpio_reg_read(dev, reg);
+
+ if (exclude_polar) {
+ polar_reg_val = mv_gpio_reg_read(dev, polar_reg);
+ return ((reg_val & GPIO(pin)) ^ (polar_reg_val & GPIO(pin)));
+ } else
+ return (reg_val & GPIO(pin));
+}
+
+static void
+mv_gpio_value_set(device_t dev, uint32_t pin, uint8_t val)
+{
+ uint32_t reg;
+ struct mv_gpio_softc *sc;
+ sc = (struct mv_gpio_softc *)device_get_softc(dev);
+
+ MV_GPIO_ASSERT_LOCKED();
+
+ if (pin >= sc->pin_num)
+ return;
+
+ reg = GPIO_DATA_OUT;
+
+ if (val)
+ mv_gpio_reg_set(dev, reg, pin);
+ else
+ mv_gpio_reg_clear(dev, reg, pin);
+}
+
+/*
+ * GPIO interface methods
+ */
+
+static int
+mv_gpio_pin_max(device_t dev, int *maxpin)
+{
+ struct mv_gpio_softc *sc;
+ if (maxpin == NULL)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+ *maxpin = sc->pin_num;
+
+ return (0);
+}
+
+static int
+mv_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
+{
+ struct mv_gpio_softc *sc = device_get_softc(dev);
+ if (caps == NULL)
+ return (EINVAL);
+
+ if (pin >= sc->pin_num)
+ return (EINVAL);
+
+ MV_GPIO_LOCK();
+ *caps = sc->gpio_setup[pin].gp_caps;
+ MV_GPIO_UNLOCK();
+
+ return (0);
+}
+
+static int
+mv_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
+{
+ struct mv_gpio_softc *sc = device_get_softc(dev);
+ if (flags == NULL)
+ return (EINVAL);
+
+ if (pin >= sc->pin_num)
+ return (EINVAL);
+
+ MV_GPIO_LOCK();
+ *flags = sc->gpio_setup[pin].gp_flags;
+ MV_GPIO_UNLOCK();
+
+ return (0);
+}
+
+static int
+mv_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
+{
+ struct mv_gpio_softc *sc = device_get_softc(dev);
+ if (name == NULL)
+ return (EINVAL);
+
+ if (pin >= sc->pin_num)
+ return (EINVAL);
+
+ MV_GPIO_LOCK();
+ memcpy(name, sc->gpio_setup[pin].gp_name, GPIOMAXNAME);
+ MV_GPIO_UNLOCK();
+
+ return (0);
+}
+
+static int
+mv_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
+{
+ int ret;
+ struct mv_gpio_softc *sc = device_get_softc(dev);
+ if (pin >= sc->pin_num)
+ return (EINVAL);
+
+ /* Check for unwanted flags. */
+ if ((flags & sc->gpio_setup[pin].gp_caps) != flags)
+ return (EINVAL);
+
+ ret = mv_gpio_configure(dev, pin, flags, ~0);
+
+ return (ret);
+}
+
+static int
+mv_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
+{
+ struct mv_gpio_softc *sc = device_get_softc(dev);
+ if (pin >= sc->pin_num)
+ return (EINVAL);
+
+ MV_GPIO_LOCK();
+ mv_gpio_value_set(dev, pin, value);
+ MV_GPIO_UNLOCK();
+
+ return (0);
+}
+
+static int
+mv_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *value)
+{
+ struct mv_gpio_softc *sc = device_get_softc(dev);
+ if (value == NULL)
+ return (EINVAL);
+
+ if (pin >= sc->pin_num)
+ return (EINVAL);
+
+ MV_GPIO_LOCK();
+ *value = mv_gpio_in(dev, pin);
+ MV_GPIO_UNLOCK();
+
+ return (0);
+}
+
+static int
+mv_gpio_pin_toggle(device_t dev, uint32_t pin)
+{
+ struct mv_gpio_softc *sc = device_get_softc(dev);
+ uint32_t value;
+ if (pin >= sc->pin_num)
+ return (EINVAL);
+
+ MV_GPIO_LOCK();
+ value = mv_gpio_in(dev, pin);
+ value = (~value) & 1;
+ mv_gpio_value_set(dev, pin, value);
+ MV_GPIO_UNLOCK();
+
+ return (0);
+}
+
+static device_t
+mv_gpio_get_bus(device_t dev)
+{
+ struct mv_gpio_softc *sc = device_get_softc(dev);
+
+ return (sc->sc_busdev);
+}
+
+static int
+mv_gpio_map_gpios(device_t bus, phandle_t dev, phandle_t gparent, int gcells,
+ pcell_t *gpios, uint32_t *pin, uint32_t *flags)
+{
+ struct mv_gpio_softc *sc = device_get_softc(bus);
+
+ if (gpios[0] >= sc->pin_num)
+ return (EINVAL);
+
+ *pin = gpios[0];
+ *flags = gpios[1];
+ mv_gpio_configure(bus, *pin, *flags, ~0);
+
+ return (0);
+}
diff --git a/sys/arm/mv/ic.c b/sys/arm/mv/ic.c
new file mode 100644
index 000000000000..ed99ee646aec
--- /dev/null
+++ b/sys/arm/mv/ic.c
@@ -0,0 +1,315 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2006 Benno Rice.
+ * Copyright (C) 2007-2008 MARVELL INTERNATIONAL LTD.
+ * All rights reserved.
+ *
+ * Adapted and extended to Marvell SoCs by Semihalf.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * from: FreeBSD: //depot/projects/arm/src/sys/arm/xscale/pxa2x0/pxa2x0_icu.c, rev 1
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/ktr.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/mv/mvreg.h>
+#include <arm/mv/mvvar.h>
+
+struct mv_ic_softc {
+ struct resource * ic_res[1];
+ bus_space_tag_t ic_bst;
+ bus_space_handle_t ic_bsh;
+ int ic_high_regs;
+ int ic_error_regs;
+};
+
+static struct resource_spec mv_ic_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+static struct mv_ic_softc *mv_ic_sc = NULL;
+
+static int mv_ic_probe(device_t);
+static int mv_ic_attach(device_t);
+
+uint32_t mv_ic_get_cause(void);
+uint32_t mv_ic_get_mask(void);
+void mv_ic_set_mask(uint32_t);
+uint32_t mv_ic_get_cause_hi(void);
+uint32_t mv_ic_get_mask_hi(void);
+void mv_ic_set_mask_hi(uint32_t);
+uint32_t mv_ic_get_cause_error(void);
+uint32_t mv_ic_get_mask_error(void);
+void mv_ic_set_mask_error(uint32_t);
+static void arm_mask_irq_all(void);
+
+static int
+mv_ic_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "mrvl,pic"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Marvell Integrated Interrupt Controller");
+ return (0);
+}
+
+static int
+mv_ic_attach(device_t dev)
+{
+ struct mv_ic_softc *sc;
+ uint32_t dev_id, rev_id;
+ int error;
+
+ sc = (struct mv_ic_softc *)device_get_softc(dev);
+
+ if (mv_ic_sc != NULL)
+ return (ENXIO);
+ mv_ic_sc = sc;
+
+ soc_id(&dev_id, &rev_id);
+
+ sc->ic_high_regs = 0;
+ sc->ic_error_regs = 0;
+
+ if (dev_id == MV_DEV_88F6281 ||
+ dev_id == MV_DEV_88F6282 ||
+ dev_id == MV_DEV_MV78100 ||
+ dev_id == MV_DEV_MV78100_Z0)
+ sc->ic_high_regs = 1;
+
+ if (dev_id == MV_DEV_MV78100 || dev_id == MV_DEV_MV78100_Z0)
+ sc->ic_error_regs = 1;
+
+ error = bus_alloc_resources(dev, mv_ic_spec, sc->ic_res);
+ if (error) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ sc->ic_bst = rman_get_bustag(sc->ic_res[0]);
+ sc->ic_bsh = rman_get_bushandle(sc->ic_res[0]);
+
+ /* Mask all interrupts */
+ arm_mask_irq_all();
+
+ return (0);
+}
+
+static device_method_t mv_ic_methods[] = {
+ DEVMETHOD(device_probe, mv_ic_probe),
+ DEVMETHOD(device_attach, mv_ic_attach),
+ { 0, 0 }
+};
+
+static driver_t mv_ic_driver = {
+ "ic",
+ mv_ic_methods,
+ sizeof(struct mv_ic_softc),
+};
+
+static devclass_t mv_ic_devclass;
+
+DRIVER_MODULE(ic, simplebus, mv_ic_driver, mv_ic_devclass, 0, 0);
+
+int
+arm_get_next_irq(int last)
+{
+ u_int filt, irq;
+ int next;
+
+ filt = ~((last >= 0) ? (2 << last) - 1 : 0);
+ irq = mv_ic_get_cause() & mv_ic_get_mask();
+ if (irq & filt) {
+ next = ffs(irq & filt) - 1;
+ goto out;
+ }
+ if (mv_ic_sc->ic_high_regs) {
+ filt = ~((last >= 32) ? (2 << (last - 32)) - 1 : 0);
+ irq = mv_ic_get_cause_hi() & mv_ic_get_mask_hi();
+ if (irq & filt) {
+ next = ffs(irq & filt) + 31;
+ goto out;
+ }
+ }
+ if (mv_ic_sc->ic_error_regs) {
+ filt = ~((last >= 64) ? (2 << (last - 64)) - 1 : 0);
+ irq = mv_ic_get_cause_error() & mv_ic_get_mask_error();
+ if (irq & filt) {
+ next = ffs(irq & filt) + 63;
+ goto out;
+ }
+ }
+ next = -1;
+
+ out:
+ CTR3(KTR_INTR, "%s: last=%d, next=%d", __func__, last, next);
+ return (next);
+}
+
+static void
+arm_mask_irq_all(void)
+{
+
+ mv_ic_set_mask(0);
+
+ if (mv_ic_sc->ic_high_regs)
+ mv_ic_set_mask_hi(0);
+
+ if (mv_ic_sc->ic_error_regs)
+ mv_ic_set_mask_error(0);
+}
+
+void
+arm_mask_irq(uintptr_t nb)
+{
+ uint32_t mr;
+
+ if (nb < 32) {
+ mr = mv_ic_get_mask();
+ mr &= ~(1 << nb);
+ mv_ic_set_mask(mr);
+
+ } else if ((nb < 64) && mv_ic_sc->ic_high_regs) {
+ mr = mv_ic_get_mask_hi();
+ mr &= ~(1 << (nb - 32));
+ mv_ic_set_mask_hi(mr);
+
+ } else if ((nb < 96) && mv_ic_sc->ic_error_regs) {
+ mr = mv_ic_get_mask_error();
+ mr &= ~(1 << (nb - 64));
+ mv_ic_set_mask_error(mr);
+ }
+}
+
+void
+arm_unmask_irq(uintptr_t nb)
+{
+ uint32_t mr;
+
+ if (nb < 32) {
+ mr = mv_ic_get_mask();
+ mr |= (1 << nb);
+ mv_ic_set_mask(mr);
+
+ } else if ((nb < 64) && mv_ic_sc->ic_high_regs) {
+ mr = mv_ic_get_mask_hi();
+ mr |= (1 << (nb - 32));
+ mv_ic_set_mask_hi(mr);
+
+ } else if ((nb < 96) && mv_ic_sc->ic_error_regs) {
+ mr = mv_ic_get_mask_error();
+ mr |= (1 << (nb - 64));
+ mv_ic_set_mask_error(mr);
+ }
+}
+
+void
+mv_ic_set_mask(uint32_t val)
+{
+
+ bus_space_write_4(mv_ic_sc->ic_bst, mv_ic_sc->ic_bsh,
+ IRQ_MASK, val);
+}
+
+uint32_t
+mv_ic_get_mask(void)
+{
+
+ return (bus_space_read_4(mv_ic_sc->ic_bst,
+ mv_ic_sc->ic_bsh, IRQ_MASK));
+}
+
+uint32_t
+mv_ic_get_cause(void)
+{
+
+ return (bus_space_read_4(mv_ic_sc->ic_bst,
+ mv_ic_sc->ic_bsh, IRQ_CAUSE));
+}
+
+void
+mv_ic_set_mask_hi(uint32_t val)
+{
+
+ bus_space_write_4(mv_ic_sc->ic_bst, mv_ic_sc->ic_bsh,
+ IRQ_MASK_HI, val);
+}
+
+uint32_t
+mv_ic_get_mask_hi(void)
+{
+
+ return (bus_space_read_4(mv_ic_sc->ic_bst,
+ mv_ic_sc->ic_bsh, IRQ_MASK_HI));
+}
+
+uint32_t
+mv_ic_get_cause_hi(void)
+{
+
+ return (bus_space_read_4(mv_ic_sc->ic_bst,
+ mv_ic_sc->ic_bsh, IRQ_CAUSE_HI));
+}
+
+void
+mv_ic_set_mask_error(uint32_t val)
+{
+
+ bus_space_write_4(mv_ic_sc->ic_bst, mv_ic_sc->ic_bsh,
+ IRQ_MASK_ERROR, val);
+}
+
+uint32_t
+mv_ic_get_mask_error(void)
+{
+
+ return (bus_space_read_4(mv_ic_sc->ic_bst,
+ mv_ic_sc->ic_bsh, IRQ_MASK_ERROR));
+}
+
+uint32_t
+mv_ic_get_cause_error(void)
+{
+
+ return (bus_space_read_4(mv_ic_sc->ic_bst,
+ mv_ic_sc->ic_bsh, IRQ_CAUSE_ERROR));
+}
diff --git a/sys/arm/mv/mpic.c b/sys/arm/mv/mpic.c
new file mode 100644
index 000000000000..e235dcd07c7c
--- /dev/null
+++ b/sys/arm/mv/mpic.c
@@ -0,0 +1,609 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2006 Benno Rice.
+ * Copyright (C) 2007-2011 MARVELL INTERNATIONAL LTD.
+ * Copyright (c) 2012 Semihalf.
+ * All rights reserved.
+ *
+ * Developed by Semihalf.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * from: FreeBSD: //depot/projects/arm/src/sys/arm/xscale/pxa2x0/pxa2x0_icu.c, rev 1
+ * from: FreeBSD: src/sys/arm/mv/ic.c,v 1.5 2011/02/08 01:49:30
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/cpuset.h>
+#include <sys/ktr.h>
+#include <sys/kdb.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/proc.h>
+#include <sys/smp.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+#include <machine/smp.h>
+
+#include <arm/mv/mvvar.h>
+#include <arm/mv/mvreg.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/fdt/fdt_common.h>
+
+#include "pic_if.h"
+
+#ifdef DEBUG
+#define debugf(fmt, args...) do { printf("%s(): ", __func__); \
+ printf(fmt,##args); } while (0)
+#else
+#define debugf(fmt, args...)
+#endif
+
+#define MPIC_INT_LOCAL 3
+#define MPIC_INT_ERR 4
+#define MPIC_INT_MSI 96
+
+#define MPIC_IRQ_MASK 0x3ff
+
+#define MPIC_CTRL 0x0
+#define MPIC_SOFT_INT 0x4
+#define MPIC_SOFT_INT_DRBL1 (1 << 5)
+#define MPIC_ERR_CAUSE 0x20
+#define MPIC_ISE 0x30
+#define MPIC_ICE 0x34
+#define MPIC_INT_CTL(irq) (0x100 + (irq)*4)
+
+#define MPIC_INT_IRQ_FIQ_MASK(cpuid) (0x101 << (cpuid))
+#define MPIC_CTRL_NIRQS(ctrl) (((ctrl) >> 2) & 0x3ff)
+
+#define MPIC_IN_DRBL 0x08
+#define MPIC_IN_DRBL_MASK 0x0c
+#define MPIC_PPI_CAUSE 0x10
+#define MPIC_CTP 0x40
+#define MPIC_IIACK 0x44
+#define MPIC_ISM 0x48
+#define MPIC_ICM 0x4c
+#define MPIC_ERR_MASK 0x50
+#define MPIC_LOCAL_MASK 0x54
+#define MPIC_CPU(n) (n) * 0x100
+
+#define MPIC_PPI 32
+
+struct mv_mpic_irqsrc {
+ struct intr_irqsrc mmi_isrc;
+ u_int mmi_irq;
+};
+
+struct mv_mpic_softc {
+ device_t sc_dev;
+ struct resource * mpic_res[4];
+ bus_space_tag_t mpic_bst;
+ bus_space_handle_t mpic_bsh;
+ bus_space_tag_t cpu_bst;
+ bus_space_handle_t cpu_bsh;
+ bus_space_tag_t drbl_bst;
+ bus_space_handle_t drbl_bsh;
+ struct mtx mtx;
+ struct mv_mpic_irqsrc * mpic_isrcs;
+ int nirqs;
+ void * intr_hand;
+};
+
+static struct resource_spec mv_mpic_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_MEMORY, 1, RF_ACTIVE },
+ { SYS_RES_MEMORY, 2, RF_ACTIVE | RF_OPTIONAL },
+ { SYS_RES_IRQ, 0, RF_ACTIVE | RF_OPTIONAL },
+ { -1, 0 }
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"mrvl,mpic", true},
+ {"marvell,mpic", true},
+ {NULL, false}
+};
+
+static struct mv_mpic_softc *mv_mpic_sc = NULL;
+
+void mpic_send_ipi(int cpus, u_int ipi);
+
+static int mv_mpic_probe(device_t);
+static int mv_mpic_attach(device_t);
+uint32_t mv_mpic_get_cause(void);
+uint32_t mv_mpic_get_cause_err(void);
+uint32_t mv_mpic_get_msi(void);
+static void mpic_unmask_irq(uintptr_t nb);
+static void mpic_mask_irq(uintptr_t nb);
+static void mpic_mask_irq_err(uintptr_t nb);
+static void mpic_unmask_irq_err(uintptr_t nb);
+static boolean_t mpic_irq_is_percpu(uintptr_t);
+static int mpic_intr(void *arg);
+static void mpic_unmask_msi(void);
+void mpic_init_secondary(device_t);
+void mpic_ipi_send(device_t, struct intr_irqsrc*, cpuset_t, u_int);
+int mpic_ipi_read(int);
+void mpic_ipi_clear(int);
+
+#define MPIC_WRITE(softc, reg, val) \
+ bus_space_write_4((softc)->mpic_bst, (softc)->mpic_bsh, (reg), (val))
+#define MPIC_READ(softc, reg) \
+ bus_space_read_4((softc)->mpic_bst, (softc)->mpic_bsh, (reg))
+
+#define MPIC_CPU_WRITE(softc, reg, val) \
+ bus_space_write_4((softc)->cpu_bst, (softc)->cpu_bsh, (reg), (val))
+#define MPIC_CPU_READ(softc, reg) \
+ bus_space_read_4((softc)->cpu_bst, (softc)->cpu_bsh, (reg))
+
+#define MPIC_DRBL_WRITE(softc, reg, val) \
+ bus_space_write_4((softc)->drbl_bst, (softc)->drbl_bsh, (reg), (val))
+#define MPIC_DRBL_READ(softc, reg) \
+ bus_space_read_4((softc)->drbl_bst, (softc)->drbl_bsh, (reg))
+
+static int
+mv_mpic_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+ return (ENXIO);
+
+ device_set_desc(dev, "Marvell Integrated Interrupt Controller");
+ return (0);
+}
+
+static int
+mv_mpic_register_isrcs(struct mv_mpic_softc *sc)
+{
+ int error;
+ uint32_t irq;
+ struct intr_irqsrc *isrc;
+ const char *name;
+
+ sc->mpic_isrcs = malloc(sc->nirqs * sizeof (*sc->mpic_isrcs), M_DEVBUF,
+ M_WAITOK | M_ZERO);
+
+ name = device_get_nameunit(sc->sc_dev);
+ for (irq = 0; irq < sc->nirqs; irq++) {
+ sc->mpic_isrcs[irq].mmi_irq = irq;
+
+ isrc = &sc->mpic_isrcs[irq].mmi_isrc;
+ if (irq < MPIC_PPI) {
+ error = intr_isrc_register(isrc, sc->sc_dev,
+ INTR_ISRCF_PPI, "%s", name);
+ } else {
+ error = intr_isrc_register(isrc, sc->sc_dev, 0, "%s",
+ name);
+ }
+ if (error != 0) {
+ /* XXX call intr_isrc_deregister() */
+ device_printf(sc->sc_dev, "%s failed", __func__);
+ return (error);
+ }
+ }
+ return (0);
+}
+
+static int
+mv_mpic_attach(device_t dev)
+{
+ struct mv_mpic_softc *sc;
+ int error;
+ uint32_t val;
+ int cpu;
+
+ sc = (struct mv_mpic_softc *)device_get_softc(dev);
+
+ if (mv_mpic_sc != NULL)
+ return (ENXIO);
+ mv_mpic_sc = sc;
+
+ sc->sc_dev = dev;
+
+ mtx_init(&sc->mtx, "MPIC lock", NULL, MTX_SPIN);
+
+ error = bus_alloc_resources(dev, mv_mpic_spec, sc->mpic_res);
+ if (error) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+ if (sc->mpic_res[3] == NULL)
+ device_printf(dev, "No interrupt to use.\n");
+ else
+ bus_setup_intr(dev, sc->mpic_res[3], INTR_TYPE_CLK,
+ mpic_intr, NULL, sc, &sc->intr_hand);
+
+ sc->mpic_bst = rman_get_bustag(sc->mpic_res[0]);
+ sc->mpic_bsh = rman_get_bushandle(sc->mpic_res[0]);
+
+ sc->cpu_bst = rman_get_bustag(sc->mpic_res[1]);
+ sc->cpu_bsh = rman_get_bushandle(sc->mpic_res[1]);
+
+ if (sc->mpic_res[2] != NULL) {
+ /* This is required only if MSIs are used. */
+ sc->drbl_bst = rman_get_bustag(sc->mpic_res[2]);
+ sc->drbl_bsh = rman_get_bushandle(sc->mpic_res[2]);
+ }
+
+ MPIC_WRITE(mv_mpic_sc, MPIC_CTRL, 1);
+ MPIC_CPU_WRITE(mv_mpic_sc, MPIC_CTP, 0);
+
+ val = MPIC_READ(mv_mpic_sc, MPIC_CTRL);
+ sc->nirqs = MPIC_CTRL_NIRQS(val);
+
+ if (mv_mpic_register_isrcs(sc) != 0) {
+ device_printf(dev, "could not register PIC ISRCs\n");
+ bus_release_resources(dev, mv_mpic_spec, sc->mpic_res);
+ return (ENXIO);
+ }
+
+ OF_device_register_xref(OF_xref_from_node(ofw_bus_get_node(dev)), dev);
+
+ if (intr_pic_register(dev, OF_xref_from_device(dev)) == NULL) {
+ device_printf(dev, "could not register PIC\n");
+ bus_release_resources(dev, mv_mpic_spec, sc->mpic_res);
+ return (ENXIO);
+ }
+
+ mpic_unmask_msi();
+
+ /* Unmask CPU performance counters overflow irq */
+ for (cpu = 0; cpu < mp_ncpus; cpu++)
+ MPIC_CPU_WRITE(mv_mpic_sc, MPIC_CPU(cpu) + MPIC_LOCAL_MASK,
+ (1 << cpu) | MPIC_CPU_READ(mv_mpic_sc,
+ MPIC_CPU(cpu) + MPIC_LOCAL_MASK));
+
+ return (0);
+}
+
+static int
+mpic_intr(void *arg)
+{
+ struct mv_mpic_softc *sc;
+ uint32_t cause, irqsrc;
+ unsigned int irq;
+ u_int cpuid;
+
+ sc = arg;
+ cpuid = PCPU_GET(cpuid);
+ irq = 0;
+
+ for (cause = MPIC_CPU_READ(sc, MPIC_PPI_CAUSE); cause > 0;
+ cause >>= 1, irq++) {
+ if (cause & 1) {
+ irqsrc = MPIC_READ(sc, MPIC_INT_CTL(irq));
+ if ((irqsrc & MPIC_INT_IRQ_FIQ_MASK(cpuid)) == 0)
+ continue;
+ if (intr_isrc_dispatch(&sc->mpic_isrcs[irq].mmi_isrc,
+ curthread->td_intr_frame) != 0) {
+ mpic_mask_irq(irq);
+ device_printf(sc->sc_dev, "Stray irq %u "
+ "disabled\n", irq);
+ }
+ }
+ }
+
+ return (FILTER_HANDLED);
+}
+
+static void
+mpic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ u_int irq;
+
+ irq = ((struct mv_mpic_irqsrc *)isrc)->mmi_irq;
+ mpic_mask_irq(irq);
+}
+
+static void
+mpic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ u_int irq;
+
+ irq = ((struct mv_mpic_irqsrc *)isrc)->mmi_irq;
+ mpic_unmask_irq(irq);
+}
+
+static int
+mpic_map_intr(device_t dev, struct intr_map_data *data,
+ struct intr_irqsrc **isrcp)
+{
+ struct intr_map_data_fdt *daf;
+ struct mv_mpic_softc *sc;
+
+ if (data->type != INTR_MAP_DATA_FDT)
+ return (ENOTSUP);
+
+ sc = device_get_softc(dev);
+ daf = (struct intr_map_data_fdt *)data;
+
+ if (daf->ncells !=1 || daf->cells[0] >= sc->nirqs)
+ return (EINVAL);
+
+ *isrcp = &sc->mpic_isrcs[daf->cells[0]].mmi_isrc;
+ return (0);
+}
+
+static void
+mpic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+
+ mpic_disable_intr(dev, isrc);
+}
+
+static void
+mpic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+
+ mpic_enable_intr(dev, isrc);
+}
+
+static void
+mpic_post_filter(device_t dev, struct intr_irqsrc *isrc)
+{
+}
+
+static device_method_t mv_mpic_methods[] = {
+ DEVMETHOD(device_probe, mv_mpic_probe),
+ DEVMETHOD(device_attach, mv_mpic_attach),
+
+ DEVMETHOD(pic_disable_intr, mpic_disable_intr),
+ DEVMETHOD(pic_enable_intr, mpic_enable_intr),
+ DEVMETHOD(pic_map_intr, mpic_map_intr),
+ DEVMETHOD(pic_post_filter, mpic_post_filter),
+ DEVMETHOD(pic_post_ithread, mpic_post_ithread),
+ DEVMETHOD(pic_pre_ithread, mpic_pre_ithread),
+ DEVMETHOD(pic_init_secondary, mpic_init_secondary),
+ DEVMETHOD(pic_ipi_send, mpic_ipi_send),
+ { 0, 0 }
+};
+
+static driver_t mv_mpic_driver = {
+ "mpic",
+ mv_mpic_methods,
+ sizeof(struct mv_mpic_softc),
+};
+
+static devclass_t mv_mpic_devclass;
+
+EARLY_DRIVER_MODULE(mpic, simplebus, mv_mpic_driver, mv_mpic_devclass, 0, 0,
+ BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);
+
+static void
+mpic_unmask_msi(void)
+{
+
+ mpic_unmask_irq(MPIC_INT_MSI);
+}
+
+static void
+mpic_unmask_irq_err(uintptr_t nb)
+{
+ uint32_t mask;
+ uint8_t bit_off;
+
+ MPIC_WRITE(mv_mpic_sc, MPIC_ISE, MPIC_INT_ERR);
+ MPIC_CPU_WRITE(mv_mpic_sc, MPIC_ICM, MPIC_INT_ERR);
+
+ bit_off = nb - ERR_IRQ;
+ mask = MPIC_CPU_READ(mv_mpic_sc, MPIC_ERR_MASK);
+ mask |= (1 << bit_off);
+ MPIC_CPU_WRITE(mv_mpic_sc, MPIC_ERR_MASK, mask);
+}
+
+static void
+mpic_mask_irq_err(uintptr_t nb)
+{
+ uint32_t mask;
+ uint8_t bit_off;
+
+ bit_off = nb - ERR_IRQ;
+ mask = MPIC_CPU_READ(mv_mpic_sc, MPIC_ERR_MASK);
+ mask &= ~(1 << bit_off);
+ MPIC_CPU_WRITE(mv_mpic_sc, MPIC_ERR_MASK, mask);
+}
+
+static boolean_t
+mpic_irq_is_percpu(uintptr_t nb)
+{
+ if (nb < MPIC_PPI)
+ return TRUE;
+
+ return FALSE;
+}
+
+static void
+mpic_unmask_irq(uintptr_t nb)
+{
+
+#ifdef SMP
+ int cpu;
+
+ if (nb == MPIC_INT_LOCAL) {
+ for (cpu = 0; cpu < mp_ncpus; cpu++)
+ MPIC_CPU_WRITE(mv_mpic_sc,
+ MPIC_CPU(cpu) + MPIC_ICM, nb);
+ return;
+ }
+#endif
+ if (mpic_irq_is_percpu(nb))
+ MPIC_CPU_WRITE(mv_mpic_sc, MPIC_ICM, nb);
+ else if (nb < ERR_IRQ)
+ MPIC_WRITE(mv_mpic_sc, MPIC_ISE, nb);
+ else if (nb < MSI_IRQ)
+ mpic_unmask_irq_err(nb);
+
+ if (nb == 0)
+ MPIC_CPU_WRITE(mv_mpic_sc, MPIC_IN_DRBL_MASK, 0xffffffff);
+}
+
+static void
+mpic_mask_irq(uintptr_t nb)
+{
+
+#ifdef SMP
+ int cpu;
+
+ if (nb == MPIC_INT_LOCAL) {
+ for (cpu = 0; cpu < mp_ncpus; cpu++)
+ MPIC_CPU_WRITE(mv_mpic_sc,
+ MPIC_CPU(cpu) + MPIC_ISM, nb);
+ return;
+ }
+#endif
+ if (mpic_irq_is_percpu(nb))
+ MPIC_CPU_WRITE(mv_mpic_sc, MPIC_ISM, nb);
+ else if (nb < ERR_IRQ)
+ MPIC_WRITE(mv_mpic_sc, MPIC_ICE, nb);
+ else if (nb < MSI_IRQ)
+ mpic_mask_irq_err(nb);
+}
+
+uint32_t
+mv_mpic_get_cause(void)
+{
+
+ return (MPIC_CPU_READ(mv_mpic_sc, MPIC_IIACK));
+}
+
+uint32_t
+mv_mpic_get_cause_err(void)
+{
+ uint32_t err_cause;
+ uint8_t bit_off;
+
+ err_cause = MPIC_READ(mv_mpic_sc, MPIC_ERR_CAUSE);
+
+ if (err_cause)
+ bit_off = ffs(err_cause) - 1;
+ else
+ return (-1);
+
+ debugf("%s: irq:%x cause:%x\n", __func__, bit_off, err_cause);
+ return (ERR_IRQ + bit_off);
+}
+
+uint32_t
+mv_mpic_get_msi(void)
+{
+ uint32_t cause;
+ uint8_t bit_off;
+
+ KASSERT(mv_mpic_sc->drbl_bst != NULL, ("No doorbell in mv_mpic_get_msi"));
+ cause = MPIC_DRBL_READ(mv_mpic_sc, 0);
+
+ if (cause)
+ bit_off = ffs(cause) - 1;
+ else
+ return (-1);
+
+ debugf("%s: irq:%x cause:%x\n", __func__, bit_off, cause);
+
+ cause &= ~(1 << bit_off);
+ MPIC_DRBL_WRITE(mv_mpic_sc, 0, cause);
+
+ return (MSI_IRQ + bit_off);
+}
+
+int
+mv_msi_data(int irq, uint64_t *addr, uint32_t *data)
+{
+ u_long phys, base, size;
+ phandle_t node;
+ int error;
+
+ node = ofw_bus_get_node(mv_mpic_sc->sc_dev);
+
+ /* Get physical address of register space */
+ error = fdt_get_range(OF_parent(node), 0, &phys, &size);
+ if (error) {
+ printf("%s: Cannot get register physical address, err:%d",
+ __func__, error);
+ return (error);
+ }
+
+ /* Get offset of MPIC register space */
+ error = fdt_regsize(node, &base, &size);
+ if (error) {
+ printf("%s: Cannot get MPIC register offset, err:%d",
+ __func__, error);
+ return (error);
+ }
+
+ *addr = phys + base + MPIC_SOFT_INT;
+ *data = MPIC_SOFT_INT_DRBL1 | irq;
+
+ return (0);
+}
+
+void
+mpic_init_secondary(device_t dev)
+{
+}
+
+void
+mpic_ipi_send(device_t dev, struct intr_irqsrc *isrc, cpuset_t cpus, u_int ipi)
+{
+ uint32_t val, i;
+
+ val = 0x00000000;
+ for (i = 0; i < MAXCPU; i++)
+ if (CPU_ISSET(i, &cpus))
+ val |= (1 << (8 + i));
+ val |= ipi;
+ MPIC_WRITE(mv_mpic_sc, MPIC_SOFT_INT, val);
+}
+
+int
+mpic_ipi_read(int i __unused)
+{
+ uint32_t val;
+ int ipi;
+
+ val = MPIC_CPU_READ(mv_mpic_sc, MPIC_IN_DRBL);
+ if (val) {
+ ipi = ffs(val) - 1;
+ MPIC_CPU_WRITE(mv_mpic_sc, MPIC_IN_DRBL, ~(1 << ipi));
+ return (ipi);
+ }
+
+ return (0x3ff);
+}
+
+void
+mpic_ipi_clear(int ipi)
+{
+}
diff --git a/sys/arm/mv/mv_ap806_clock.c b/sys/arm/mv/mv_ap806_clock.c
new file mode 100644
index 000000000000..56041a48d312
--- /dev/null
+++ b/sys/arm/mv/mv_ap806_clock.c
@@ -0,0 +1,233 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2018 Rubicon Communications, LLC (Netgate)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/intr.h>
+
+#include <dev/extres/clk/clk_fixed.h>
+#include <dev/extres/syscon/syscon.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "syscon_if.h"
+
+static struct clk_fixed_def ap806_clk_cluster_0 = {
+ .clkdef.id = 0,
+ .clkdef.name = "ap806-cpu-cluster-0",
+ .freq = 0,
+};
+
+static struct clk_fixed_def ap806_clk_cluster_1 = {
+ .clkdef.id = 1,
+ .clkdef.name = "ap806-cpu-cluster-1",
+ .freq = 0,
+};
+
+static struct clk_fixed_def ap806_clk_fixed = {
+ .clkdef.id = 2,
+ .clkdef.name = "ap806-fixed",
+ .freq = 1200000000,
+};
+
+/* Thoses are the only exported clocks AFAICT */
+
+static const char *mss_parents[] = {"ap806-fixed"};
+static struct clk_fixed_def ap806_clk_mss = {
+ .clkdef.id = 3,
+ .clkdef.name = "ap806-mss",
+ .clkdef.parent_names = mss_parents,
+ .clkdef.parent_cnt = 1,
+ .mult = 1,
+ .div = 6,
+};
+
+static const char *sdio_parents[] = {"ap806-fixed"};
+static struct clk_fixed_def ap806_clk_sdio = {
+ .clkdef.id = 4,
+ .clkdef.name = "ap806-sdio",
+ .clkdef.parent_names = sdio_parents,
+ .clkdef.parent_cnt = 1,
+ .mult = 1,
+ .div = 3,
+};
+
+struct mv_ap806_clock_softc {
+ device_t dev;
+ struct syscon *syscon;
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"marvell,ap806-clock", 1},
+ {NULL, 0}
+};
+
+#define RD4(sc, reg) SYSCON_READ_4((sc)->syscon, (reg))
+#define WR4(sc, reg, val) SYSCON_WRITE_4((sc)->syscon, (reg), (val))
+
+static int
+mv_ap806_clock_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Marvell AP806 Clock Controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+mv_ap806_clock_attach(device_t dev)
+{
+ struct mv_ap806_clock_softc *sc;
+ struct clkdom *clkdom;
+ uint64_t clock_freq;
+ uint32_t reg;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ if (SYSCON_GET_HANDLE(sc->dev, &sc->syscon) != 0 ||
+ sc->syscon == NULL) {
+ device_printf(dev, "cannot get syscon for device\n");
+ return (ENXIO);
+ }
+
+ reg = RD4(sc, 0x400);
+ switch (reg & 0x1f) {
+ case 0x0:
+ case 0x1:
+ clock_freq = 2000000000;
+ break;
+ case 0x4:
+ clock_freq = 1600000000;
+ break;
+ case 0x6:
+ clock_freq = 1800000000;
+ break;
+ case 0x7:
+ clock_freq = 1800000000;
+ break;
+ case 0xb:
+ clock_freq = 1600000000;
+ break;
+ case 0xd:
+ clock_freq = 1600000000;
+ break;
+ case 0x13:
+ clock_freq = 1000000000;
+ break;
+ case 0x14:
+ clock_freq = 1333000000;
+ break;
+ case 0x17:
+ clock_freq = 1333000000;
+ break;
+ case 0x19:
+ clock_freq = 1200000000;
+ break;
+ case 0x1a:
+ clock_freq = 1400000000;
+ break;
+ case 0x1b:
+ clock_freq = 600000000;
+ break;
+ case 0x1c:
+ clock_freq = 800000000;
+ break;
+ case 0x1d:
+ clock_freq = 1000000000;
+ break;
+ default:
+ device_printf(dev, "Cannot guess clock freq with reg %x\n",
+ reg & 0x1f);
+ return (ENXIO);
+ break;
+ };
+
+ ap806_clk_cluster_0.freq = clock_freq;
+ ap806_clk_cluster_1.freq = clock_freq;
+ clkdom = clkdom_create(dev);
+
+ clknode_fixed_register(clkdom, &ap806_clk_cluster_0);
+ clknode_fixed_register(clkdom, &ap806_clk_cluster_1);
+ clknode_fixed_register(clkdom, &ap806_clk_fixed);
+ clknode_fixed_register(clkdom, &ap806_clk_mss);
+ clknode_fixed_register(clkdom, &ap806_clk_sdio);
+
+ clkdom_finit(clkdom);
+
+ if (bootverbose)
+ clkdom_dump(clkdom);
+ return (0);
+}
+
+static int
+mv_ap806_clock_detach(device_t dev)
+{
+
+ return (EBUSY);
+}
+
+static device_method_t mv_ap806_clock_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, mv_ap806_clock_probe),
+ DEVMETHOD(device_attach, mv_ap806_clock_attach),
+ DEVMETHOD(device_detach, mv_ap806_clock_detach),
+
+ DEVMETHOD_END
+};
+
+static devclass_t mv_ap806_clock_devclass;
+
+static driver_t mv_ap806_clock_driver = {
+ "mv_ap806_clock",
+ mv_ap806_clock_methods,
+ sizeof(struct mv_ap806_clock_softc),
+};
+
+EARLY_DRIVER_MODULE(mv_ap806_clock, simplebus, mv_ap806_clock_driver,
+ mv_ap806_clock_devclass, 0, 0, BUS_PASS_RESOURCE + BUS_PASS_ORDER_LATE);
diff --git a/sys/arm/mv/mv_ap806_gicp.c b/sys/arm/mv/mv_ap806_gicp.c
new file mode 100644
index 000000000000..da93a32f2b23
--- /dev/null
+++ b/sys/arm/mv/mv_ap806_gicp.c
@@ -0,0 +1,328 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2018 Rubicon Communications, LLC (Netgate)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/intr.h>
+
+#include <dev/fdt/simplebus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "pic_if.h"
+
+#define MV_AP806_GICP_MAX_NIRQS 207
+
+struct mv_ap806_gicp_softc {
+ device_t dev;
+ device_t parent;
+ struct resource *res;
+
+ ssize_t spi_ranges_cnt;
+ uint32_t *spi_ranges;
+ struct intr_map_data_fdt *parent_map_data;
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"marvell,ap806-gicp", 1},
+ {NULL, 0}
+};
+
+#define RD4(sc, reg) bus_read_4((sc)->res, (reg))
+#define WR4(sc, reg, val) bus_write_4((sc)->res, (reg), (val))
+
+static int
+mv_ap806_gicp_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Marvell GICP");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+mv_ap806_gicp_attach(device_t dev)
+{
+ struct mv_ap806_gicp_softc *sc;
+ phandle_t node, xref, intr_parent;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ node = ofw_bus_get_node(dev);
+
+ /* Look for our parent */
+ if ((intr_parent = ofw_bus_find_iparent(node)) == 0) {
+ device_printf(dev,
+ "Cannot find our parent interrupt controller\n");
+ return (ENXIO);
+ }
+ if ((sc->parent = OF_device_from_xref(intr_parent)) == NULL) {
+ device_printf(dev,
+ "cannot find parent interrupt controller device\n");
+ return (ENXIO);
+ }
+
+ sc->spi_ranges_cnt = OF_getencprop_alloc(node, "marvell,spi-ranges",
+ (void **)&sc->spi_ranges);
+
+ xref = OF_xref_from_node(node);
+ if (intr_pic_register(dev, xref) == NULL) {
+ device_printf(dev, "Cannot register GICP\n");
+ return (ENXIO);
+ }
+ /* Allocate GIC compatible mapping entry (3 cells) */
+ sc->parent_map_data = (struct intr_map_data_fdt *)intr_alloc_map_data(
+ INTR_MAP_DATA_FDT, sizeof(struct intr_map_data_fdt) +
+ + 3 * sizeof(phandle_t), M_WAITOK | M_ZERO);
+ OF_device_register_xref(xref, dev);
+
+ return (0);
+}
+
+static int
+mv_ap806_gicp_detach(device_t dev)
+{
+
+ return (EBUSY);
+}
+
+static struct intr_map_data *
+mv_ap806_gicp_convert_map_data(struct mv_ap806_gicp_softc *sc,
+ struct intr_map_data *data)
+{
+ struct intr_map_data_fdt *daf;
+ uint32_t i, irq_num, irq_type;
+
+ daf = (struct intr_map_data_fdt *)data;
+ if (daf->ncells != 2)
+ return (NULL);
+
+ irq_num = daf->cells[0];
+ irq_type = daf->cells[1];
+ if (irq_num >= MV_AP806_GICP_MAX_NIRQS)
+ return (NULL);
+
+ /* Construct GIC compatible mapping. */
+ sc->parent_map_data->ncells = 3;
+ sc->parent_map_data->cells[0] = 0; /* SPI */
+ sc->parent_map_data->cells[2] = irq_type;
+
+ /* Map the interrupt number to SPI number */
+ for (i = 0; i < sc->spi_ranges_cnt / 2; i += 2) {
+ if (irq_num < sc->spi_ranges[i + 1]) {
+ irq_num += sc->spi_ranges[i];
+ break;
+ }
+
+ irq_num -= sc->spi_ranges[i];
+ }
+
+ sc->parent_map_data->cells[1] = irq_num - 32;
+
+ return ((struct intr_map_data *)sc->parent_map_data);
+}
+
+static int
+mv_ap806_gicp_activate_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ struct mv_ap806_gicp_softc *sc;
+
+ sc = device_get_softc(dev);
+ data = mv_ap806_gicp_convert_map_data(sc, data);
+ if (data == NULL)
+ return (EINVAL);
+
+ return (PIC_ACTIVATE_INTR(sc->parent, isrc, res, data));
+}
+
+static void
+mv_ap806_gicp_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct mv_ap806_gicp_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ PIC_ENABLE_INTR(sc->parent, isrc);
+}
+
+static void
+mv_ap806_gicp_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct mv_ap806_gicp_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ PIC_DISABLE_INTR(sc->parent, isrc);
+}
+
+static int
+mv_ap806_gicp_map_intr(device_t dev, struct intr_map_data *data,
+ struct intr_irqsrc **isrcp)
+{
+ struct mv_ap806_gicp_softc *sc;
+ int ret;
+
+ sc = device_get_softc(dev);
+
+ if (data->type != INTR_MAP_DATA_FDT)
+ return (ENOTSUP);
+
+ data = mv_ap806_gicp_convert_map_data(sc, data);
+ if (data == NULL)
+ return (EINVAL);
+
+ ret = PIC_MAP_INTR(sc->parent, data, isrcp);
+ (*isrcp)->isrc_dev = sc->dev;
+ return(ret);
+}
+
+static int
+mv_ap806_gicp_deactivate_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ struct mv_ap806_gicp_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ data = mv_ap806_gicp_convert_map_data(sc, data);
+ if (data == NULL)
+ return (EINVAL);
+
+ return (PIC_DEACTIVATE_INTR(sc->parent, isrc, res, data));
+}
+
+static int
+mv_ap806_gicp_setup_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ struct mv_ap806_gicp_softc *sc;
+
+ sc = device_get_softc(dev);
+ data = mv_ap806_gicp_convert_map_data(sc, data);
+ if (data == NULL)
+ return (EINVAL);
+
+ return (PIC_SETUP_INTR(sc->parent, isrc, res, data));
+}
+
+static int
+mv_ap806_gicp_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ struct mv_ap806_gicp_softc *sc;
+
+ sc = device_get_softc(dev);
+ data = mv_ap806_gicp_convert_map_data(sc, data);
+ if (data == NULL)
+ return (EINVAL);
+
+ return (PIC_TEARDOWN_INTR(sc->parent, isrc, res, data));
+}
+
+static void
+mv_ap806_gicp_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct mv_ap806_gicp_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ PIC_PRE_ITHREAD(sc->parent, isrc);
+}
+
+static void
+mv_ap806_gicp_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct mv_ap806_gicp_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ PIC_POST_ITHREAD(sc->parent, isrc);
+}
+
+static void
+mv_ap806_gicp_post_filter(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct mv_ap806_gicp_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ PIC_POST_FILTER(sc->parent, isrc);
+}
+
+static device_method_t mv_ap806_gicp_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, mv_ap806_gicp_probe),
+ DEVMETHOD(device_attach, mv_ap806_gicp_attach),
+ DEVMETHOD(device_detach, mv_ap806_gicp_detach),
+
+ /* Interrupt controller interface */
+ DEVMETHOD(pic_activate_intr, mv_ap806_gicp_activate_intr),
+ DEVMETHOD(pic_disable_intr, mv_ap806_gicp_disable_intr),
+ DEVMETHOD(pic_enable_intr, mv_ap806_gicp_enable_intr),
+ DEVMETHOD(pic_map_intr, mv_ap806_gicp_map_intr),
+ DEVMETHOD(pic_deactivate_intr, mv_ap806_gicp_deactivate_intr),
+ DEVMETHOD(pic_setup_intr, mv_ap806_gicp_setup_intr),
+ DEVMETHOD(pic_teardown_intr, mv_ap806_gicp_teardown_intr),
+ DEVMETHOD(pic_post_filter, mv_ap806_gicp_post_filter),
+ DEVMETHOD(pic_post_ithread, mv_ap806_gicp_post_ithread),
+ DEVMETHOD(pic_pre_ithread, mv_ap806_gicp_pre_ithread),
+
+ DEVMETHOD_END
+};
+
+static devclass_t mv_ap806_gicp_devclass;
+
+static driver_t mv_ap806_gicp_driver = {
+ "mv_ap806_gicp",
+ mv_ap806_gicp_methods,
+ sizeof(struct mv_ap806_gicp_softc),
+};
+
+EARLY_DRIVER_MODULE(mv_ap806_gicp, simplebus, mv_ap806_gicp_driver,
+ mv_ap806_gicp_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
diff --git a/sys/arm/mv/mv_ap806_sei.c b/sys/arm/mv/mv_ap806_sei.c
new file mode 100644
index 000000000000..5022e6765d0a
--- /dev/null
+++ b/sys/arm/mv/mv_ap806_sei.c
@@ -0,0 +1,413 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2019 Michal Meloun <mmel@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+#include <machine/resource.h>
+
+#include <dev/fdt/simplebus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "pic_if.h"
+
+#define MV_AP806_SEI_LOCK(_sc) mtx_lock(&(_sc)->mtx)
+#define MV_AP806_SEI_UNLOCK(_sc) mtx_unlock(&(_sc)->mtx)
+#define MV_AP806_SEI_LOCK_INIT(_sc) mtx_init(&_sc->mtx, \
+ device_get_nameunit(_sc->dev), "mv_ap806_sei", MTX_DEF)
+#define MV_AP806_SEI_LOCK_DESTROY(_sc) mtx_destroy(&_sc->mtx);
+#define MV_AP806_SEI_ASSERT_LOCKED(_sc) mtx_assert(&_sc->mtx, MA_OWNED);
+#define MV_AP806_SEI_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED);
+
+#define MV_AP806_SEI_MAX_NIRQS 64
+#define GICP_SECR0 0x00
+#define GICP_SECR1 0x04
+#define GICP_SECR(i) (0x00 + (((i)/32) * 0x4))
+#define GICP_SECR_BIT(i) ((i) % 32)
+#define GICP_SEMR0 0x20
+#define GICP_SEMR1 0x24
+#define GICP_SEMR(i) (0x20 + (((i)/32) * 0x4))
+#define GICP_SEMR_BIT(i) ((i) % 32)
+
+struct mv_ap806_sei_irqsrc {
+ struct intr_irqsrc isrc;
+ u_int irq;
+};
+
+struct mv_ap806_sei_softc {
+ device_t dev;
+ struct resource *mem_res;
+ struct resource *irq_res;
+ void *irq_ih;
+ struct mtx mtx;
+
+ struct mv_ap806_sei_irqsrc *isrcs;
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"marvell,ap806-sei", 1},
+ {NULL, 0}
+};
+
+#define RD4(sc, reg) bus_read_4((sc)->mem_res, (reg))
+#define WR4(sc, reg, val) bus_write_4((sc)->mem_res, (reg), (val))
+
+static inline void
+mv_ap806_sei_isrc_mask(struct mv_ap806_sei_softc *sc,
+ struct mv_ap806_sei_irqsrc *sisrc, uint32_t val)
+{
+ uint32_t tmp;
+ int bit;
+
+ bit = GICP_SEMR_BIT(sisrc->irq);
+ MV_AP806_SEI_LOCK(sc);
+ tmp = RD4(sc, GICP_SEMR(sisrc->irq));
+ if (val != 0)
+ tmp |= 1 << bit;
+ else
+ tmp &= ~(1 << bit);
+ WR4(sc, GICP_SEMR(sisrc->irq), tmp);
+ MV_AP806_SEI_UNLOCK(sc);
+}
+
+static inline void
+mv_ap806_sei_isrc_eoi(struct mv_ap806_sei_softc *sc,
+ struct mv_ap806_sei_irqsrc *sisrc)
+{
+
+ WR4(sc, GICP_SECR(sisrc->irq), GICP_SECR_BIT(sisrc->irq));
+}
+
+static void
+mv_ap806_sei_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct mv_ap806_sei_softc *sc;
+ struct mv_ap806_sei_irqsrc *sisrc;
+
+ sc = device_get_softc(dev);
+ sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
+ mv_ap806_sei_isrc_mask(sc, sisrc, 0);
+}
+
+static void
+mv_ap806_sei_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct mv_ap806_sei_softc *sc;
+ struct mv_ap806_sei_irqsrc *sisrc;
+
+ sc = device_get_softc(dev);
+ sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
+ mv_ap806_sei_isrc_mask(sc, sisrc, 1);
+}
+
+static int
+mv_ap806_sei_map(device_t dev, struct intr_map_data *data, u_int *irqp)
+{
+ struct mv_ap806_sei_softc *sc;
+ struct intr_map_data_fdt *daf;
+ u_int irq;
+
+ sc = device_get_softc(dev);
+
+ if (data->type != INTR_MAP_DATA_FDT)
+ return (ENOTSUP);
+
+ daf = (struct intr_map_data_fdt *)data;
+ if (daf->ncells != 1 || daf->cells[0] >= MV_AP806_SEI_MAX_NIRQS)
+ return (EINVAL);
+ irq = daf->cells[0];
+ if (irqp != NULL)
+ *irqp = irq;
+
+ return(0);
+}
+
+static int
+mv_ap806_sei_map_intr(device_t dev, struct intr_map_data *data,
+ struct intr_irqsrc **isrcp)
+{
+ struct mv_ap806_sei_softc *sc;
+ u_int irq;
+ int rv;
+
+ sc = device_get_softc(dev);
+ rv = mv_ap806_sei_map(dev, data, &irq);
+ if (rv == 0)
+ *isrcp = &sc->isrcs[irq].isrc;
+
+ return (rv);
+}
+
+static int
+mv_ap806_sei_setup_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ struct mv_ap806_sei_softc *sc;
+ struct mv_ap806_sei_irqsrc *sisrc;
+ u_int irq;
+ int rv;
+
+ sc = device_get_softc(dev);
+ sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
+ if (data == NULL)
+ return (ENOTSUP);
+ rv = mv_ap806_sei_map(dev, data, &irq);
+ if (rv != 0)
+ return (rv);
+ if (irq != sisrc->irq)
+ return (EINVAL);
+ mv_ap806_sei_isrc_mask(sc, sisrc, 0);
+ return (0);
+}
+
+static int
+mv_ap806_sei_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ struct mv_ap806_sei_softc *sc;
+ struct mv_ap806_sei_irqsrc *sisrc;
+
+ sc = device_get_softc(dev);
+ sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
+
+ mv_ap806_sei_isrc_mask(sc, sisrc, 1);
+ return (0);
+}
+
+static void
+mv_ap806_sei_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct mv_ap806_sei_softc *sc;
+ struct mv_ap806_sei_irqsrc *sisrc;
+
+ sc = device_get_softc(dev);
+ sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
+
+ mv_ap806_sei_isrc_mask(sc, sisrc, 1);
+ mv_ap806_sei_isrc_eoi(sc, sisrc);
+}
+
+static void
+mv_ap806_sei_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct mv_ap806_sei_softc *sc;
+ struct mv_ap806_sei_irqsrc *sisrc;
+
+ sc = device_get_softc(dev);
+ sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
+
+ mv_ap806_sei_isrc_mask(sc, sisrc, 1);
+}
+
+static void
+mv_ap806_sei_post_filter(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct mv_ap806_sei_softc *sc;
+ struct mv_ap806_sei_irqsrc *sisrc;
+
+ sc = device_get_softc(dev);
+ sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
+
+ mv_ap806_sei_isrc_mask(sc, sisrc, 1);
+ mv_ap806_sei_isrc_eoi(sc, sisrc);
+}
+
+/* ----------------------------------------------------------------------------
+ *
+ * B u s i n t e r f a c e
+ */
+static int
+mv_ap806_sei_intr(void *arg)
+{
+ struct mv_ap806_sei_softc *sc;
+ struct mv_ap806_sei_irqsrc *sirq;
+ struct trapframe *tf;
+ uint64_t cause;
+ u_int irq;
+
+ sc = (struct mv_ap806_sei_softc *)arg;
+ tf = curthread->td_intr_frame;
+ while (1) {
+ cause = RD4(sc, GICP_SECR1);
+ cause <<= 32;
+ cause |= RD4(sc, GICP_SECR0);
+
+ irq = ffsll(cause);
+ if (irq == 0) break;
+ irq--;
+ sirq = &sc->isrcs[irq];
+ if (intr_isrc_dispatch(&sirq->isrc, tf) != 0) {
+ mv_ap806_sei_isrc_mask(sc, sirq, 0);
+ mv_ap806_sei_isrc_eoi(sc, sirq);
+ device_printf(sc->dev,
+ "Stray irq %u disabled\n", irq);
+ }
+ }
+
+ return (FILTER_HANDLED);
+}
+
+static int
+mv_ap806_sei_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Marvell SEI");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+mv_ap806_sei_attach(device_t dev)
+{
+ struct mv_ap806_sei_softc *sc;
+ phandle_t xref, node;
+ uint32_t irq;
+ const char *name;
+ int rv, rid;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ node = ofw_bus_get_node(dev);
+ MV_AP806_SEI_LOCK_INIT(sc);
+
+ /* Allocate resources. */
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "Cannot allocate memory resources\n");
+ rv = ENXIO;
+ goto fail;
+ }
+
+ rid = 0;
+ sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
+ if (sc->irq_res == NULL) {
+ device_printf(dev, "Cannot allocate IRQ resources\n");
+ rv = ENXIO;
+ goto fail;
+ }
+
+ /* Mask all interrupts) */
+ WR4(sc, GICP_SEMR0, 0xFFFFFFFF);
+ WR4(sc, GICP_SEMR1, 0xFFFFFFFF);
+
+ /* Create all interrupt sources */
+ sc->isrcs = malloc(sizeof(*sc->isrcs) * MV_AP806_SEI_MAX_NIRQS,
+ M_DEVBUF, M_WAITOK | M_ZERO);
+ name = device_get_nameunit(sc->dev);
+ for (irq = 0; irq < MV_AP806_SEI_MAX_NIRQS; irq++) {
+ sc->isrcs[irq].irq = irq;
+ rv = intr_isrc_register(&sc->isrcs[irq].isrc,
+ sc->dev, 0, "%s,%u", name, irq);
+ if (rv != 0)
+ goto fail; /* XXX deregister ISRCs */
+ }
+ xref = OF_xref_from_node(node);;
+ if (intr_pic_register(dev, xref) == NULL) {
+ device_printf(dev, "Cannot register SEI\n");
+ rv = ENXIO;
+ goto fail;
+ }
+ if (bus_setup_intr(dev, sc->irq_res,INTR_TYPE_MISC | INTR_MPSAFE,
+ mv_ap806_sei_intr, NULL, sc, &sc->irq_ih)) {
+ device_printf(dev,
+ "Unable to register interrupt handler\n");
+ rv = ENXIO;
+ goto fail;
+ }
+
+ OF_device_register_xref(xref, dev);
+ return (0);
+
+fail:
+ if (sc->irq_ih != NULL)
+ bus_teardown_intr(dev, sc->irq_res, sc->irq_ih);
+ if (sc->irq_res != NULL)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+ if (sc->mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+ MV_AP806_SEI_LOCK_DESTROY(sc);
+ return (ENXIO);
+}
+
+static int
+mv_ap806_sei_detach(device_t dev)
+{
+
+ return (EBUSY);
+}
+
+static device_method_t mv_ap806_sei_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, mv_ap806_sei_probe),
+ DEVMETHOD(device_attach, mv_ap806_sei_attach),
+ DEVMETHOD(device_detach, mv_ap806_sei_detach),
+
+ /* Interrupt controller interface */
+ DEVMETHOD(pic_disable_intr, mv_ap806_sei_disable_intr),
+ DEVMETHOD(pic_enable_intr, mv_ap806_sei_enable_intr),
+ DEVMETHOD(pic_map_intr, mv_ap806_sei_map_intr),
+ DEVMETHOD(pic_setup_intr, mv_ap806_sei_setup_intr),
+ DEVMETHOD(pic_teardown_intr, mv_ap806_sei_teardown_intr),
+ DEVMETHOD(pic_post_filter, mv_ap806_sei_post_filter),
+ DEVMETHOD(pic_post_ithread, mv_ap806_sei_post_ithread),
+ DEVMETHOD(pic_pre_ithread, mv_ap806_sei_pre_ithread),
+
+ DEVMETHOD_END
+};
+
+static devclass_t mv_ap806_sei_devclass;
+
+static driver_t mv_ap806_sei_driver = {
+ "mv_ap806_sei",
+ mv_ap806_sei_methods,
+ sizeof(struct mv_ap806_sei_softc),
+};
+
+EARLY_DRIVER_MODULE(mv_ap806_sei, simplebus, mv_ap806_sei_driver,
+ mv_ap806_sei_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
diff --git a/sys/arm/mv/mv_armv7_machdep.c b/sys/arm/mv/mv_armv7_machdep.c
new file mode 100644
index 000000000000..3ecd69f435a6
--- /dev/null
+++ b/sys/arm/mv/mv_armv7_machdep.c
@@ -0,0 +1,482 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2017 Semihalf.
+ * Copyright (c) 1994-1998 Mark Brinicombe.
+ * Copyright (c) 1994 Brini.
+ * All rights reserved.
+ *
+ * This code is derived from software written for Brini by Mark Brinicombe
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Brini.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: FreeBSD: //depot/projects/arm/src/sys/arm/at91/kb920x_machdep.c, rev 45
+ */
+
+#include "opt_ddb.h"
+#include "opt_platform.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#define _ARM32_BUS_DMA_PRIVATE
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/devmap.h>
+#include <sys/kernel.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <arm/arm/mpcore_timervar.h>
+#include <arm/arm/nexusvar.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+#include <machine/machdep.h>
+#include <machine/platform.h>
+#include <machine/platformvar.h>
+#include <machine/pte-v6.h>
+
+#include <arm/mv/mvreg.h>
+#include <arm/mv/mvvar.h>
+#include <arm/mv/mvwin.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "opt_platform.h"
+#include "platform_if.h"
+
+#if defined(SOC_MV_ARMADA38X)
+#include "platform_pl310_if.h"
+#include "armada38x/armada38x_pl310.h"
+#endif
+
+static int platform_mpp_init(void);
+int armada38x_win_set_iosync_barrier(void);
+int armada38x_scu_enable(void);
+int armada38x_open_bootrom_win(void);
+int armada38x_mbus_optimization(void);
+
+static vm_offset_t mv_platform_lastaddr(platform_t plate);
+static int mv_platform_probe_and_attach(platform_t plate);
+static void mv_platform_gpio_init(platform_t plate);
+static void mv_cpu_reset(platform_t plat);
+
+static void mv_a38x_platform_late_init(platform_t plate);
+static int mv_a38x_platform_devmap_init(platform_t plate);
+static void mv_axp_platform_late_init(platform_t plate);
+static int mv_axp_platform_devmap_init(platform_t plate);
+
+void armadaxp_init_coher_fabric(void);
+void armadaxp_l2_init(void);
+
+#ifdef SMP
+void mv_a38x_platform_mp_setmaxid(platform_t plate);
+void mv_a38x_platform_mp_start_ap(platform_t plate);
+void mv_axp_platform_mp_setmaxid(platform_t plate);
+void mv_axp_platform_mp_start_ap(platform_t plate);
+#endif
+
+#define MPP_PIN_MAX 68
+#define MPP_PIN_CELLS 2
+#define MPP_PINS_PER_REG 8
+#define MPP_SEL(pin,func) (((func) & 0xf) << \
+ (((pin) % MPP_PINS_PER_REG) * 4))
+
+static void
+mv_busdma_tag_init(void *arg __unused)
+{
+ phandle_t node;
+ bus_dma_tag_t dmat;
+
+ /*
+ * If this platform has coherent DMA, create the parent DMA tag to pass
+ * down the coherent flag to all busses and devices on the platform,
+ * otherwise return without doing anything. By default create tag
+ * for all A38x-based platforms only.
+ */
+ if ((node = OF_finddevice("/")) == -1){
+ printf("no tree\n");
+ return;
+ }
+
+ if (ofw_bus_node_is_compatible(node, "marvell,armada380") == 0)
+ return;
+
+ bus_dma_tag_create(NULL, /* No parent tag */
+ 1, 0, /* alignment, bounds */
+ BUS_SPACE_MAXADDR, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ BUS_SPACE_MAXSIZE, /* maxsize */
+ BUS_SPACE_UNRESTRICTED, /* nsegments */
+ BUS_SPACE_MAXSIZE, /* maxsegsize */
+ BUS_DMA_COHERENT, /* flags */
+ NULL, NULL, /* lockfunc, lockarg */
+ &dmat);
+
+ nexus_set_dma_tag(dmat);
+
+}
+SYSINIT(mv_busdma_tag, SI_SUB_DRIVERS, SI_ORDER_ANY, mv_busdma_tag_init, NULL);
+
+static int
+platform_mpp_init(void)
+{
+ pcell_t pinmap[MPP_PIN_MAX * MPP_PIN_CELLS];
+ int mpp[MPP_PIN_MAX];
+ uint32_t ctrl_val, ctrl_offset;
+ pcell_t reg[4];
+ u_long start, size;
+ phandle_t node;
+ pcell_t pin_cells, *pinmap_ptr, pin_count;
+ ssize_t len;
+ int par_addr_cells, par_size_cells;
+ int tuple_size, tuples, rv, pins, i, j;
+ int mpp_pin, mpp_function;
+
+ /*
+ * Try to access the MPP node directly i.e. through /aliases/mpp.
+ */
+ if ((node = OF_finddevice("mpp")) != -1)
+ if (ofw_bus_node_is_compatible(node, "mrvl,mpp"))
+ goto moveon;
+ /*
+ * Find the node the long way.
+ */
+ if ((node = OF_finddevice("/")) == -1)
+ return (ENXIO);
+
+ if ((node = fdt_find_compatible(node, "simple-bus", 0)) == 0)
+ return (ENXIO);
+
+ if ((node = fdt_find_compatible(node, "mrvl,mpp", 0)) == 0)
+ /*
+ * No MPP node. Fall back to how MPP got set by the
+ * first-stage loader and try to continue booting.
+ */
+ return (0);
+moveon:
+ /*
+ * Process 'reg' prop.
+ */
+ if ((rv = fdt_addrsize_cells(OF_parent(node), &par_addr_cells,
+ &par_size_cells)) != 0)
+ return(ENXIO);
+
+ tuple_size = sizeof(pcell_t) * (par_addr_cells + par_size_cells);
+ len = OF_getprop(node, "reg", reg, sizeof(reg));
+ tuples = len / tuple_size;
+ if (tuple_size <= 0)
+ return (EINVAL);
+
+ rv = fdt_data_to_res(reg, par_addr_cells, par_size_cells,
+ &start, &size);
+ if (rv != 0)
+ return (rv);
+ start += fdt_immr_va;
+
+ /*
+ * Process 'pin-count' and 'pin-map' props.
+ */
+ if (OF_getencprop(node, "pin-count", &pin_count, sizeof(pin_count)) <= 0)
+ return (ENXIO);
+ if (pin_count > MPP_PIN_MAX)
+ return (ERANGE);
+
+ if (OF_getencprop(node, "#pin-cells", &pin_cells, sizeof(pin_cells)) <= 0)
+ pin_cells = MPP_PIN_CELLS;
+ if (pin_cells > MPP_PIN_CELLS)
+ return (ERANGE);
+ tuple_size = sizeof(pcell_t) * pin_cells;
+
+ bzero(pinmap, sizeof(pinmap));
+ len = OF_getencprop(node, "pin-map", pinmap, sizeof(pinmap));
+ if (len <= 0)
+ return (ERANGE);
+ if (len % tuple_size)
+ return (ERANGE);
+ pins = len / tuple_size;
+ if (pins > pin_count)
+ return (ERANGE);
+ /*
+ * Fill out a "mpp[pin] => function" table. All pins unspecified in
+ * the 'pin-map' property are defaulted to 0 function i.e. GPIO.
+ */
+ bzero(mpp, sizeof(mpp));
+ pinmap_ptr = pinmap;
+ for (i = 0; i < pins; i++) {
+ mpp_pin = *pinmap_ptr;
+ mpp_function = *(pinmap_ptr + 1);
+ mpp[mpp_pin] = mpp_function;
+ pinmap_ptr += pin_cells;
+ }
+
+ /*
+ * Prepare and program MPP control register values.
+ */
+ ctrl_offset = 0;
+ for (i = 0; i < pin_count;) {
+ ctrl_val = 0;
+
+ for (j = 0; j < MPP_PINS_PER_REG; j++) {
+ if (i + j == pin_count - 1)
+ break;
+ ctrl_val |= MPP_SEL(i + j, mpp[i + j]);
+ }
+ i += MPP_PINS_PER_REG;
+ bus_space_write_4(fdtbus_bs_tag, start, ctrl_offset,
+ ctrl_val);
+
+ ctrl_offset += 4;
+ }
+
+ return (0);
+}
+
+static vm_offset_t
+mv_platform_lastaddr(platform_t plat)
+{
+
+ return (fdt_immr_va);
+}
+
+static int
+mv_platform_probe_and_attach(platform_t plate)
+{
+
+ if (fdt_immr_addr(MV_BASE) != 0)
+ while (1);
+ return (0);
+}
+
+static void
+mv_platform_gpio_init(platform_t plate)
+{
+
+ /*
+ * Re-initialise MPP. It is important to call this prior to using
+ * console as the physical connection can be routed via MPP.
+ */
+ if (platform_mpp_init() != 0)
+ while (1);
+}
+
+static void
+mv_a38x_platform_late_init(platform_t plate)
+{
+
+ /*
+ * Re-initialise decode windows
+ */
+ if (mv_check_soc_family() == MV_SOC_UNSUPPORTED)
+ panic("Unsupported SoC family\n");
+
+ if (soc_decode_win() != 0)
+ printf("WARNING: could not re-initialise decode windows! "
+ "Running with existing settings...\n");
+
+ /* Configure timers' base frequency */
+ arm_tmr_change_frequency(get_cpu_freq() / 2);
+
+ /*
+ * Workaround for Marvell Armada38X family HW issue
+ * between Cortex-A9 CPUs and on-chip devices that may
+ * cause hang on heavy load.
+ * To avoid that, map all registers including PCIe IO
+ * as strongly ordered instead of device memory.
+ */
+ pmap_remap_vm_attr(VM_MEMATTR_DEVICE, VM_MEMATTR_SO);
+
+ /* Set IO Sync Barrier bit for all Mbus devices */
+ if (armada38x_win_set_iosync_barrier() != 0)
+ printf("WARNING: could not map CPU Subsystem registers\n");
+ if (armada38x_mbus_optimization() != 0)
+ printf("WARNING: could not enable mbus optimization\n");
+ if (armada38x_scu_enable() != 0)
+ printf("WARNING: could not enable SCU\n");
+#ifdef SMP
+ /* Open window to bootROM memory - needed for SMP */
+ if (armada38x_open_bootrom_win() != 0)
+ printf("WARNING: could not open window to bootROM\n");
+#endif
+}
+
+static void
+mv_axp_platform_late_init(platform_t plate)
+{
+ phandle_t node;
+ /*
+ * Re-initialise decode windows
+ */
+ if (soc_decode_win() != 0)
+ printf("WARNING: could not re-initialise decode windows! "
+ "Running with existing settings...\n");
+ if ((node = OF_finddevice("/")) == -1)
+ return;
+
+#if !defined(SMP)
+ /* For SMP case it should be initialized after APs are booted */
+ armadaxp_init_coher_fabric();
+#endif
+ armadaxp_l2_init();
+}
+
+#define FDT_DEVMAP_MAX (MV_WIN_CPU_MAX_ARMV7 + 2)
+static struct devmap_entry fdt_devmap[FDT_DEVMAP_MAX] = {
+ { 0, 0, 0, }
+};
+
+static int
+platform_sram_devmap(struct devmap_entry *map)
+{
+
+ return (ENOENT);
+}
+
+/*
+ * Construct devmap table with DT-derived config data.
+ */
+static int
+mv_a38x_platform_devmap_init(platform_t plat)
+{
+ phandle_t root, child;
+ int i;
+
+ i = 0;
+ devmap_register_table(&fdt_devmap[0]);
+
+ if ((root = OF_finddevice("/")) == -1)
+ return (ENXIO);
+
+ /*
+ * IMMR range.
+ */
+ fdt_devmap[i].pd_va = fdt_immr_va;
+ fdt_devmap[i].pd_pa = fdt_immr_pa;
+ fdt_devmap[i].pd_size = fdt_immr_size;
+ i++;
+
+ /*
+ * SRAM range.
+ */
+ if (i < FDT_DEVMAP_MAX)
+ if (platform_sram_devmap(&fdt_devmap[i]) == 0)
+ i++;
+
+ /*
+ * PCI range(s).
+ * PCI range(s) and localbus.
+ */
+ for (child = OF_child(root); child != 0; child = OF_peer(child)) {
+ if (mv_fdt_is_type(child, "pci") ||
+ mv_fdt_is_type(child, "pciep")) {
+ /*
+ * Check space: each PCI node will consume 2 devmap
+ * entries.
+ */
+ if (i + 1 >= FDT_DEVMAP_MAX)
+ return (ENOMEM);
+
+ if (mv_pci_devmap(child, &fdt_devmap[i], MV_PCI_VA_IO_BASE,
+ MV_PCI_VA_MEM_BASE) != 0)
+ return (ENXIO);
+ i += 2;
+ }
+ }
+
+ return (0);
+}
+
+static int
+mv_axp_platform_devmap_init(platform_t plate)
+{
+ vm_paddr_t cur_immr_pa;
+
+ /*
+ * Acquire SoC registers' base passed by u-boot and fill devmap
+ * accordingly. DTB is going to be modified basing on this data
+ * later.
+ */
+ __asm __volatile("mrc p15, 4, %0, c15, c0, 0" : "=r" (cur_immr_pa));
+ cur_immr_pa = (cur_immr_pa << 13) & 0xff000000;
+ if (cur_immr_pa != 0)
+ fdt_immr_pa = cur_immr_pa;
+
+ mv_a38x_platform_devmap_init(plate);
+
+ return (0);
+}
+
+static void
+mv_cpu_reset(platform_t plat)
+{
+
+ write_cpu_misc(RSTOUTn_MASK_ARMV7, SOFT_RST_OUT_EN_ARMV7);
+ write_cpu_misc(SYSTEM_SOFT_RESET_ARMV7, SYS_SOFT_RST_ARMV7);
+}
+
+#if defined(SOC_MV_ARMADA38X)
+static platform_method_t mv_a38x_methods[] = {
+ PLATFORMMETHOD(platform_devmap_init, mv_a38x_platform_devmap_init),
+ PLATFORMMETHOD(platform_cpu_reset, mv_cpu_reset),
+ PLATFORMMETHOD(platform_lastaddr, mv_platform_lastaddr),
+ PLATFORMMETHOD(platform_attach, mv_platform_probe_and_attach),
+ PLATFORMMETHOD(platform_gpio_init, mv_platform_gpio_init),
+ PLATFORMMETHOD(platform_late_init, mv_a38x_platform_late_init),
+ PLATFORMMETHOD(platform_pl310_init, mv_a38x_platform_pl310_init),
+ PLATFORMMETHOD(platform_pl310_write_ctrl, mv_a38x_platform_pl310_write_ctrl),
+ PLATFORMMETHOD(platform_pl310_write_debug, mv_a38x_platform_pl310_write_debug),
+#ifdef SMP
+ PLATFORMMETHOD(platform_mp_start_ap, mv_a38x_platform_mp_start_ap),
+ PLATFORMMETHOD(platform_mp_setmaxid, mv_a38x_platform_mp_setmaxid),
+#endif
+
+ PLATFORMMETHOD_END,
+};
+FDT_PLATFORM_DEF(mv_a38x, "mv_a38x", 0, "marvell,armada380", 100);
+#endif
+
+static platform_method_t mv_axp_methods[] = {
+ PLATFORMMETHOD(platform_devmap_init, mv_axp_platform_devmap_init),
+ PLATFORMMETHOD(platform_cpu_reset, mv_cpu_reset),
+ PLATFORMMETHOD(platform_lastaddr, mv_platform_lastaddr),
+ PLATFORMMETHOD(platform_attach, mv_platform_probe_and_attach),
+ PLATFORMMETHOD(platform_gpio_init, mv_platform_gpio_init),
+ PLATFORMMETHOD(platform_late_init, mv_axp_platform_late_init),
+#ifdef SMP
+ PLATFORMMETHOD(platform_mp_start_ap, mv_axp_platform_mp_start_ap),
+ PLATFORMMETHOD(platform_mp_setmaxid, mv_axp_platform_mp_setmaxid),
+#endif
+
+ PLATFORMMETHOD_END,
+};
+FDT_PLATFORM_DEF(mv_axp, "mv_axp", 0, "marvell,armadaxp", 100);
diff --git a/sys/arm/mv/mv_common.c b/sys/arm/mv/mv_common.c
new file mode 100644
index 000000000000..9d2bb17cf309
--- /dev/null
+++ b/sys/arm/mv/mv_common.c
@@ -0,0 +1,2909 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (C) 2008-2011 MARVELL INTERNATIONAL LTD.
+ * All rights reserved.
+ *
+ * Developed by Semihalf.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of MARVELL nor the names of contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/kdb.h>
+#include <sys/reboot.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+#include <machine/vmparam.h>
+#include <machine/intr.h>
+
+#include <arm/mv/mvreg.h>
+#include <arm/mv/mvvar.h>
+#include <arm/mv/mvwin.h>
+
+MALLOC_DEFINE(M_IDMA, "idma", "idma dma test memory");
+
+#define IDMA_DEBUG
+#undef IDMA_DEBUG
+
+#define MAX_CPU_WIN 5
+
+#ifdef DEBUG
+#define debugf(fmt, args...) do { printf("%s(): ", __func__); \
+ printf(fmt,##args); } while (0)
+#else
+#define debugf(fmt, args...)
+#endif
+
+#ifdef DEBUG
+#define MV_DUMP_WIN 1
+#else
+#define MV_DUMP_WIN 0
+#endif
+
+struct soc_node_spec;
+
+static enum soc_family soc_family;
+
+static int mv_win_cesa_attr_armv5(int eng_sel);
+static int mv_win_cesa_attr_armada38x(int eng_sel);
+static int mv_win_cesa_attr_armadaxp(int eng_sel);
+
+uint32_t read_cpu_ctrl_armv5(uint32_t reg);
+uint32_t read_cpu_ctrl_armv7(uint32_t reg);
+
+void write_cpu_ctrl_armv5(uint32_t reg, uint32_t val);
+void write_cpu_ctrl_armv7(uint32_t reg, uint32_t val);
+
+static int win_eth_can_remap(int i);
+
+static int decode_win_cesa_valid(void);
+static int decode_win_usb_valid(void);
+static int decode_win_usb3_valid(void);
+static int decode_win_eth_valid(void);
+static int decode_win_pcie_valid(void);
+static int decode_win_sata_valid(void);
+static int decode_win_sdhci_valid(void);
+
+static int decode_win_idma_valid(void);
+static int decode_win_xor_valid(void);
+
+static void decode_win_cpu_setup(void);
+static int decode_win_sdram_fixup(void);
+static void decode_win_cesa_setup(u_long);
+static void decode_win_a38x_cesa_setup(u_long);
+static void decode_win_usb_setup(u_long);
+static void decode_win_usb3_setup(u_long);
+static void decode_win_eth_setup(u_long);
+static void decode_win_neta_setup(u_long);
+static void decode_win_sata_setup(u_long);
+static void decode_win_ahci_setup(u_long);
+static void decode_win_sdhci_setup(u_long);
+
+static void decode_win_idma_setup(u_long);
+static void decode_win_xor_setup(u_long);
+
+static void decode_win_cesa_dump(u_long);
+static void decode_win_a38x_cesa_dump(u_long);
+static void decode_win_usb_dump(u_long);
+static void decode_win_usb3_dump(u_long);
+static void decode_win_eth_dump(u_long base);
+static void decode_win_neta_dump(u_long base);
+static void decode_win_idma_dump(u_long base);
+static void decode_win_xor_dump(u_long base);
+static void decode_win_ahci_dump(u_long base);
+static void decode_win_sdhci_dump(u_long);
+static void decode_win_pcie_dump(u_long);
+
+static uint32_t win_cpu_cr_read(int);
+static uint32_t win_cpu_armv5_cr_read(int);
+static uint32_t win_cpu_armv7_cr_read(int);
+static uint32_t win_cpu_br_read(int);
+static uint32_t win_cpu_armv5_br_read(int);
+static uint32_t win_cpu_armv7_br_read(int);
+static uint32_t win_cpu_remap_l_read(int);
+static uint32_t win_cpu_armv5_remap_l_read(int);
+static uint32_t win_cpu_armv7_remap_l_read(int);
+static uint32_t win_cpu_remap_h_read(int);
+static uint32_t win_cpu_armv5_remap_h_read(int);
+static uint32_t win_cpu_armv7_remap_h_read(int);
+
+static void win_cpu_cr_write(int, uint32_t);
+static void win_cpu_armv5_cr_write(int, uint32_t);
+static void win_cpu_armv7_cr_write(int, uint32_t);
+static void win_cpu_br_write(int, uint32_t);
+static void win_cpu_armv5_br_write(int, uint32_t);
+static void win_cpu_armv7_br_write(int, uint32_t);
+static void win_cpu_remap_l_write(int, uint32_t);
+static void win_cpu_armv5_remap_l_write(int, uint32_t);
+static void win_cpu_armv7_remap_l_write(int, uint32_t);
+static void win_cpu_remap_h_write(int, uint32_t);
+static void win_cpu_armv5_remap_h_write(int, uint32_t);
+static void win_cpu_armv7_remap_h_write(int, uint32_t);
+
+static uint32_t ddr_br_read(int);
+static uint32_t ddr_sz_read(int);
+static uint32_t ddr_armv5_br_read(int);
+static uint32_t ddr_armv5_sz_read(int);
+static uint32_t ddr_armv7_br_read(int);
+static uint32_t ddr_armv7_sz_read(int);
+static void ddr_br_write(int, uint32_t);
+static void ddr_sz_write(int, uint32_t);
+static void ddr_armv5_br_write(int, uint32_t);
+static void ddr_armv5_sz_write(int, uint32_t);
+static void ddr_armv7_br_write(int, uint32_t);
+static void ddr_armv7_sz_write(int, uint32_t);
+
+static int fdt_get_ranges(const char *, void *, int, int *, int *);
+int gic_decode_fdt(phandle_t iparent, pcell_t *intr, int *interrupt,
+ int *trig, int *pol);
+
+static int win_cpu_from_dt(void);
+static int fdt_win_setup(void);
+
+static int fdt_win_process_child(phandle_t, struct soc_node_spec *, const char*);
+
+static void soc_identify(uint32_t, uint32_t);
+
+static uint32_t dev_mask = 0;
+static int cpu_wins_no = 0;
+static int eth_port = 0;
+static int usb_port = 0;
+static boolean_t platform_io_coherent = false;
+
+static struct decode_win cpu_win_tbl[MAX_CPU_WIN];
+
+const struct decode_win *cpu_wins = cpu_win_tbl;
+
+typedef void (*decode_win_setup_t)(u_long);
+typedef void (*dump_win_t)(u_long);
+typedef int (*valid_t)(void);
+
+/*
+ * The power status of device feature is only supported on
+ * Kirkwood and Discovery SoCs.
+ */
+#if defined(SOC_MV_KIRKWOOD) || defined(SOC_MV_DISCOVERY)
+#define SOC_MV_POWER_STAT_SUPPORTED 1
+#else
+#define SOC_MV_POWER_STAT_SUPPORTED 0
+#endif
+
+struct soc_node_spec {
+ const char *compat;
+ decode_win_setup_t decode_handler;
+ dump_win_t dump_handler;
+ valid_t valid_handler;
+};
+
+static struct soc_node_spec soc_nodes[] = {
+ { "mrvl,ge", &decode_win_eth_setup, &decode_win_eth_dump, &decode_win_eth_valid},
+ { "marvell,armada-370-neta", &decode_win_neta_setup,
+ &decode_win_neta_dump, NULL },
+ { "mrvl,usb-ehci", &decode_win_usb_setup, &decode_win_usb_dump, &decode_win_usb_valid},
+ { "marvell,orion-ehci", &decode_win_usb_setup, &decode_win_usb_dump, &decode_win_usb_valid },
+ { "marvell,armada-380-xhci", &decode_win_usb3_setup,
+ &decode_win_usb3_dump, &decode_win_usb3_valid },
+ { "marvell,armada-380-ahci", &decode_win_ahci_setup,
+ &decode_win_ahci_dump, NULL },
+ { "marvell,armada-380-sdhci", &decode_win_sdhci_setup,
+ &decode_win_sdhci_dump, &decode_win_sdhci_valid},
+ { "mrvl,sata", &decode_win_sata_setup, NULL, &decode_win_sata_valid},
+ { "mrvl,xor", &decode_win_xor_setup, &decode_win_xor_dump, &decode_win_xor_valid},
+ { "mrvl,idma", &decode_win_idma_setup, &decode_win_idma_dump, &decode_win_idma_valid},
+ { "mrvl,cesa", &decode_win_cesa_setup, &decode_win_cesa_dump, &decode_win_cesa_valid},
+ { "mrvl,pcie", &decode_win_pcie_setup, &decode_win_pcie_dump, &decode_win_pcie_valid},
+ { "marvell,armada-38x-crypto", &decode_win_a38x_cesa_setup,
+ &decode_win_a38x_cesa_dump, &decode_win_cesa_valid},
+ { NULL, NULL, NULL, NULL },
+};
+
+#define SOC_NODE_PCIE_ENTRY_IDX 11
+
+typedef uint32_t(*read_cpu_ctrl_t)(uint32_t);
+typedef void(*write_cpu_ctrl_t)(uint32_t, uint32_t);
+typedef uint32_t (*win_read_t)(int);
+typedef void (*win_write_t)(int, uint32_t);
+typedef int (*win_cesa_attr_t)(int);
+typedef uint32_t (*get_t)(void);
+
+struct decode_win_spec {
+ read_cpu_ctrl_t read_cpu_ctrl;
+ write_cpu_ctrl_t write_cpu_ctrl;
+ win_read_t cr_read;
+ win_read_t br_read;
+ win_read_t remap_l_read;
+ win_read_t remap_h_read;
+ win_write_t cr_write;
+ win_write_t br_write;
+ win_write_t remap_l_write;
+ win_write_t remap_h_write;
+ uint32_t mv_win_cpu_max;
+ win_cesa_attr_t win_cesa_attr;
+ int win_cesa_target;
+ win_read_t ddr_br_read;
+ win_read_t ddr_sz_read;
+ win_write_t ddr_br_write;
+ win_write_t ddr_sz_write;
+ get_t get_tclk;
+ get_t get_cpu_freq;
+};
+
+struct decode_win_spec *soc_decode_win_spec;
+
+static struct decode_win_spec decode_win_specs[] =
+{
+ {
+ &read_cpu_ctrl_armv7,
+ &write_cpu_ctrl_armv7,
+ &win_cpu_armv7_cr_read,
+ &win_cpu_armv7_br_read,
+ &win_cpu_armv7_remap_l_read,
+ &win_cpu_armv7_remap_h_read,
+ &win_cpu_armv7_cr_write,
+ &win_cpu_armv7_br_write,
+ &win_cpu_armv7_remap_l_write,
+ &win_cpu_armv7_remap_h_write,
+ MV_WIN_CPU_MAX_ARMV7,
+ &mv_win_cesa_attr_armada38x,
+ MV_WIN_CESA_TARGET_ARMADA38X,
+ &ddr_armv7_br_read,
+ &ddr_armv7_sz_read,
+ &ddr_armv7_br_write,
+ &ddr_armv7_sz_write,
+ &get_tclk_armada38x,
+ &get_cpu_freq_armada38x,
+ },
+ {
+ &read_cpu_ctrl_armv7,
+ &write_cpu_ctrl_armv7,
+ &win_cpu_armv7_cr_read,
+ &win_cpu_armv7_br_read,
+ &win_cpu_armv7_remap_l_read,
+ &win_cpu_armv7_remap_h_read,
+ &win_cpu_armv7_cr_write,
+ &win_cpu_armv7_br_write,
+ &win_cpu_armv7_remap_l_write,
+ &win_cpu_armv7_remap_h_write,
+ MV_WIN_CPU_MAX_ARMV7,
+ &mv_win_cesa_attr_armadaxp,
+ MV_WIN_CESA_TARGET_ARMADAXP,
+ &ddr_armv7_br_read,
+ &ddr_armv7_sz_read,
+ &ddr_armv7_br_write,
+ &ddr_armv7_sz_write,
+ &get_tclk_armadaxp,
+ &get_cpu_freq_armadaxp,
+ },
+ {
+ &read_cpu_ctrl_armv5,
+ &write_cpu_ctrl_armv5,
+ &win_cpu_armv5_cr_read,
+ &win_cpu_armv5_br_read,
+ &win_cpu_armv5_remap_l_read,
+ &win_cpu_armv5_remap_h_read,
+ &win_cpu_armv5_cr_write,
+ &win_cpu_armv5_br_write,
+ &win_cpu_armv5_remap_l_write,
+ &win_cpu_armv5_remap_h_write,
+ MV_WIN_CPU_MAX,
+ &mv_win_cesa_attr_armv5,
+ MV_WIN_CESA_TARGET,
+ &ddr_armv5_br_read,
+ &ddr_armv5_sz_read,
+ &ddr_armv5_br_write,
+ &ddr_armv5_sz_write,
+ NULL,
+ NULL,
+ },
+};
+
+struct fdt_pm_mask_entry {
+ char *compat;
+ uint32_t mask;
+};
+
+static struct fdt_pm_mask_entry fdt_pm_mask_table[] = {
+ { "mrvl,ge", CPU_PM_CTRL_GE(0) },
+ { "mrvl,ge", CPU_PM_CTRL_GE(1) },
+ { "mrvl,usb-ehci", CPU_PM_CTRL_USB(0) },
+ { "mrvl,usb-ehci", CPU_PM_CTRL_USB(1) },
+ { "mrvl,usb-ehci", CPU_PM_CTRL_USB(2) },
+ { "mrvl,xor", CPU_PM_CTRL_XOR },
+ { "mrvl,sata", CPU_PM_CTRL_SATA },
+ { NULL, 0 }
+};
+
+static __inline int
+pm_is_disabled(uint32_t mask)
+{
+#if SOC_MV_POWER_STAT_SUPPORTED
+ return (soc_power_ctrl_get(mask) == mask ? 0 : 1);
+#else
+ return (0);
+#endif
+}
+
+/*
+ * Disable device using power management register.
+ * 1 - Device Power On
+ * 0 - Device Power Off
+ * Mask can be set in loader.
+ * EXAMPLE:
+ * loader> set hw.pm-disable-mask=0x2
+ *
+ * Common mask:
+ * |-------------------------------|
+ * | Device | Kirkwood | Discovery |
+ * |-------------------------------|
+ * | USB0 | 0x00008 | 0x020000 |
+ * |-------------------------------|
+ * | USB1 | - | 0x040000 |
+ * |-------------------------------|
+ * | USB2 | - | 0x080000 |
+ * |-------------------------------|
+ * | GE0 | 0x00001 | 0x000002 |
+ * |-------------------------------|
+ * | GE1 | - | 0x000004 |
+ * |-------------------------------|
+ * | IDMA | - | 0x100000 |
+ * |-------------------------------|
+ * | XOR | 0x10000 | 0x200000 |
+ * |-------------------------------|
+ * | CESA | 0x20000 | 0x400000 |
+ * |-------------------------------|
+ * | SATA | 0x04000 | 0x004000 |
+ * --------------------------------|
+ * This feature can be used only on Kirkwood and Discovery
+ * machines.
+ */
+
+static int mv_win_cesa_attr_armv5(int eng_sel)
+{
+
+ return MV_WIN_CESA_ATTR(eng_sel);
+}
+
+static int mv_win_cesa_attr_armada38x(int eng_sel)
+{
+
+ return MV_WIN_CESA_ATTR_ARMADA38X(eng_sel);
+}
+
+static int mv_win_cesa_attr_armadaxp(int eng_sel)
+{
+
+ return MV_WIN_CESA_ATTR_ARMADAXP(eng_sel);
+}
+
+enum soc_family
+mv_check_soc_family()
+{
+ uint32_t dev, rev;
+
+ soc_id(&dev, &rev);
+ switch (dev) {
+ case MV_DEV_MV78230:
+ case MV_DEV_MV78260:
+ case MV_DEV_MV78460:
+ soc_decode_win_spec = &decode_win_specs[MV_SOC_ARMADA_XP];
+ soc_family = MV_SOC_ARMADA_XP;
+ break;
+ case MV_DEV_88F6828:
+ case MV_DEV_88F6820:
+ case MV_DEV_88F6810:
+ soc_decode_win_spec = &decode_win_specs[MV_SOC_ARMADA_38X];
+ soc_family = MV_SOC_ARMADA_38X;
+ break;
+ case MV_DEV_88F5181:
+ case MV_DEV_88F5182:
+ case MV_DEV_88F5281:
+ case MV_DEV_88F6281:
+ case MV_DEV_88RC8180:
+ case MV_DEV_88RC9480:
+ case MV_DEV_88RC9580:
+ case MV_DEV_88F6781:
+ case MV_DEV_88F6282:
+ case MV_DEV_MV78100_Z0:
+ case MV_DEV_MV78100:
+ case MV_DEV_MV78160:
+ soc_decode_win_spec = &decode_win_specs[MV_SOC_ARMV5];
+ soc_family = MV_SOC_ARMV5;
+ break;
+ default:
+ soc_family = MV_SOC_UNSUPPORTED;
+ return (MV_SOC_UNSUPPORTED);
+ }
+
+ soc_identify(dev, rev);
+
+ return (soc_family);
+}
+
+static __inline void
+pm_disable_device(int mask)
+{
+#ifdef DIAGNOSTIC
+ uint32_t reg;
+
+ reg = soc_power_ctrl_get(CPU_PM_CTRL_ALL);
+ printf("Power Management Register: 0%x\n", reg);
+
+ reg &= ~mask;
+ soc_power_ctrl_set(reg);
+ printf("Device %x is disabled\n", mask);
+
+ reg = soc_power_ctrl_get(CPU_PM_CTRL_ALL);
+ printf("Power Management Register: 0%x\n", reg);
+#endif
+}
+
+int
+mv_fdt_is_type(phandle_t node, const char *typestr)
+{
+#define FDT_TYPE_LEN 64
+ char type[FDT_TYPE_LEN];
+
+ if (OF_getproplen(node, "device_type") <= 0)
+ return (0);
+
+ if (OF_getprop(node, "device_type", type, FDT_TYPE_LEN) < 0)
+ return (0);
+
+ if (strncasecmp(type, typestr, FDT_TYPE_LEN) == 0)
+ /* This fits. */
+ return (1);
+
+ return (0);
+#undef FDT_TYPE_LEN
+}
+
+int
+mv_fdt_pm(phandle_t node)
+{
+ uint32_t cpu_pm_ctrl;
+ int i, ena, compat;
+
+ ena = 1;
+ cpu_pm_ctrl = read_cpu_ctrl(CPU_PM_CTRL);
+ for (i = 0; fdt_pm_mask_table[i].compat != NULL; i++) {
+ if (dev_mask & (1 << i))
+ continue;
+
+ compat = ofw_bus_node_is_compatible(node,
+ fdt_pm_mask_table[i].compat);
+#if defined(SOC_MV_KIRKWOOD)
+ if (compat && (cpu_pm_ctrl & fdt_pm_mask_table[i].mask)) {
+ dev_mask |= (1 << i);
+ ena = 0;
+ break;
+ } else if (compat) {
+ dev_mask |= (1 << i);
+ break;
+ }
+#else
+ if (compat && (~cpu_pm_ctrl & fdt_pm_mask_table[i].mask)) {
+ dev_mask |= (1 << i);
+ ena = 0;
+ break;
+ } else if (compat) {
+ dev_mask |= (1 << i);
+ break;
+ }
+#endif
+ }
+
+ return (ena);
+}
+
+uint32_t
+read_cpu_ctrl(uint32_t reg)
+{
+
+ if (soc_decode_win_spec->read_cpu_ctrl != NULL)
+ return (soc_decode_win_spec->read_cpu_ctrl(reg));
+ return (-1);
+}
+
+uint32_t
+read_cpu_ctrl_armv5(uint32_t reg)
+{
+
+ return (bus_space_read_4(fdtbus_bs_tag, MV_CPU_CONTROL_BASE, reg));
+}
+
+uint32_t
+read_cpu_ctrl_armv7(uint32_t reg)
+{
+
+ return (bus_space_read_4(fdtbus_bs_tag, MV_CPU_CONTROL_BASE_ARMV7, reg));
+}
+
+void
+write_cpu_ctrl(uint32_t reg, uint32_t val)
+{
+
+ if (soc_decode_win_spec->write_cpu_ctrl != NULL)
+ soc_decode_win_spec->write_cpu_ctrl(reg, val);
+}
+
+void
+write_cpu_ctrl_armv5(uint32_t reg, uint32_t val)
+{
+
+ bus_space_write_4(fdtbus_bs_tag, MV_CPU_CONTROL_BASE, reg, val);
+}
+
+void
+write_cpu_ctrl_armv7(uint32_t reg, uint32_t val)
+{
+
+ bus_space_write_4(fdtbus_bs_tag, MV_CPU_CONTROL_BASE_ARMV7, reg, val);
+}
+
+uint32_t
+read_cpu_mp_clocks(uint32_t reg)
+{
+
+ return (bus_space_read_4(fdtbus_bs_tag, MV_MP_CLOCKS_BASE, reg));
+}
+
+void
+write_cpu_mp_clocks(uint32_t reg, uint32_t val)
+{
+
+ bus_space_write_4(fdtbus_bs_tag, MV_MP_CLOCKS_BASE, reg, val);
+}
+
+uint32_t
+read_cpu_misc(uint32_t reg)
+{
+
+ return (bus_space_read_4(fdtbus_bs_tag, MV_MISC_BASE, reg));
+}
+
+void
+write_cpu_misc(uint32_t reg, uint32_t val)
+{
+
+ bus_space_write_4(fdtbus_bs_tag, MV_MISC_BASE, reg, val);
+}
+
+uint32_t
+cpu_extra_feat(void)
+{
+ uint32_t dev, rev;
+ uint32_t ef = 0;
+
+ soc_id(&dev, &rev);
+
+ switch (dev) {
+ case MV_DEV_88F6281:
+ case MV_DEV_88F6282:
+ case MV_DEV_88RC8180:
+ case MV_DEV_MV78100_Z0:
+ case MV_DEV_MV78100:
+ __asm __volatile("mrc p15, 1, %0, c15, c1, 0" : "=r" (ef));
+ break;
+ case MV_DEV_88F5182:
+ case MV_DEV_88F5281:
+ __asm __volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (ef));
+ break;
+ default:
+ if (bootverbose)
+ printf("This ARM Core does not support any extra features\n");
+ }
+
+ return (ef);
+}
+
+/*
+ * Get the power status of device. This feature is only supported on
+ * Kirkwood and Discovery SoCs.
+ */
+uint32_t
+soc_power_ctrl_get(uint32_t mask)
+{
+
+#if SOC_MV_POWER_STAT_SUPPORTED
+ if (mask != CPU_PM_CTRL_NONE)
+ mask &= read_cpu_ctrl(CPU_PM_CTRL);
+
+ return (mask);
+#else
+ return (mask);
+#endif
+}
+
+/*
+ * Set the power status of device. This feature is only supported on
+ * Kirkwood and Discovery SoCs.
+ */
+void
+soc_power_ctrl_set(uint32_t mask)
+{
+
+#if !defined(SOC_MV_ORION)
+ if (mask != CPU_PM_CTRL_NONE)
+ write_cpu_ctrl(CPU_PM_CTRL, mask);
+#endif
+}
+
+void
+soc_id(uint32_t *dev, uint32_t *rev)
+{
+ uint64_t mv_pcie_base = MV_PCIE_BASE;
+ phandle_t node;
+
+ /*
+ * Notice: system identifiers are available in the registers range of
+ * PCIE controller, so using this function is only allowed (and
+ * possible) after the internal registers range has been mapped in via
+ * devmap_bootstrap().
+ */
+ *dev = 0;
+ *rev = 0;
+ if ((node = OF_finddevice("/")) == -1)
+ return;
+ if (ofw_bus_node_is_compatible(node, "marvell,armada380"))
+ mv_pcie_base = MV_PCIE_BASE_ARMADA38X;
+
+ *dev = bus_space_read_4(fdtbus_bs_tag, mv_pcie_base, 0) >> 16;
+ *rev = bus_space_read_4(fdtbus_bs_tag, mv_pcie_base, 8) & 0xff;
+}
+
+static void
+soc_identify(uint32_t d, uint32_t r)
+{
+ uint32_t size, mode, freq;
+ const char *dev;
+ const char *rev;
+
+ printf("SOC: ");
+ if (bootverbose)
+ printf("(0x%4x:0x%02x) ", d, r);
+
+ rev = "";
+ switch (d) {
+ case MV_DEV_88F5181:
+ dev = "Marvell 88F5181";
+ if (r == 3)
+ rev = "B1";
+ break;
+ case MV_DEV_88F5182:
+ dev = "Marvell 88F5182";
+ if (r == 2)
+ rev = "A2";
+ break;
+ case MV_DEV_88F5281:
+ dev = "Marvell 88F5281";
+ if (r == 4)
+ rev = "D0";
+ else if (r == 5)
+ rev = "D1";
+ else if (r == 6)
+ rev = "D2";
+ break;
+ case MV_DEV_88F6281:
+ dev = "Marvell 88F6281";
+ if (r == 0)
+ rev = "Z0";
+ else if (r == 2)
+ rev = "A0";
+ else if (r == 3)
+ rev = "A1";
+ break;
+ case MV_DEV_88RC8180:
+ dev = "Marvell 88RC8180";
+ break;
+ case MV_DEV_88RC9480:
+ dev = "Marvell 88RC9480";
+ break;
+ case MV_DEV_88RC9580:
+ dev = "Marvell 88RC9580";
+ break;
+ case MV_DEV_88F6781:
+ dev = "Marvell 88F6781";
+ if (r == 2)
+ rev = "Y0";
+ break;
+ case MV_DEV_88F6282:
+ dev = "Marvell 88F6282";
+ if (r == 0)
+ rev = "A0";
+ else if (r == 1)
+ rev = "A1";
+ break;
+ case MV_DEV_88F6828:
+ dev = "Marvell 88F6828";
+ break;
+ case MV_DEV_88F6820:
+ dev = "Marvell 88F6820";
+ break;
+ case MV_DEV_88F6810:
+ dev = "Marvell 88F6810";
+ break;
+ case MV_DEV_MV78100_Z0:
+ dev = "Marvell MV78100 Z0";
+ break;
+ case MV_DEV_MV78100:
+ dev = "Marvell MV78100";
+ break;
+ case MV_DEV_MV78160:
+ dev = "Marvell MV78160";
+ break;
+ case MV_DEV_MV78260:
+ dev = "Marvell MV78260";
+ break;
+ case MV_DEV_MV78460:
+ dev = "Marvell MV78460";
+ break;
+ default:
+ dev = "UNKNOWN";
+ break;
+ }
+
+ printf("%s", dev);
+ if (*rev != '\0')
+ printf(" rev %s", rev);
+ printf(", TClock %dMHz", get_tclk() / 1000 / 1000);
+ freq = get_cpu_freq();
+ if (freq != 0)
+ printf(", Frequency %dMHz", freq / 1000 / 1000);
+ printf("\n");
+
+ mode = read_cpu_ctrl(CPU_CONFIG);
+ printf(" Instruction cache prefetch %s, data cache prefetch %s\n",
+ (mode & CPU_CONFIG_IC_PREF) ? "enabled" : "disabled",
+ (mode & CPU_CONFIG_DC_PREF) ? "enabled" : "disabled");
+
+ switch (d) {
+ case MV_DEV_88F6281:
+ case MV_DEV_88F6282:
+ mode = read_cpu_ctrl(CPU_L2_CONFIG) & CPU_L2_CONFIG_MODE;
+ printf(" 256KB 4-way set-associative %s unified L2 cache\n",
+ mode ? "write-through" : "write-back");
+ break;
+ case MV_DEV_MV78100:
+ mode = read_cpu_ctrl(CPU_CONTROL);
+ size = mode & CPU_CONTROL_L2_SIZE;
+ mode = mode & CPU_CONTROL_L2_MODE;
+ printf(" %s set-associative %s unified L2 cache\n",
+ size ? "256KB 4-way" : "512KB 8-way",
+ mode ? "write-through" : "write-back");
+ break;
+ default:
+ break;
+ }
+}
+
+#ifdef KDB
+static void
+mv_enter_debugger(void *dummy)
+{
+
+ if (boothowto & RB_KDB)
+ kdb_enter(KDB_WHY_BOOTFLAGS, "Boot flags requested debugger");
+}
+SYSINIT(mv_enter_debugger, SI_SUB_CPU, SI_ORDER_ANY, mv_enter_debugger, NULL);
+#endif
+
+int
+soc_decode_win(void)
+{
+ uint32_t dev, rev;
+ int mask, err;
+
+ mask = 0;
+ TUNABLE_INT_FETCH("hw.pm-disable-mask", &mask);
+
+ if (mask != 0)
+ pm_disable_device(mask);
+
+ /* Retrieve data about physical addresses from device tree. */
+ if ((err = win_cpu_from_dt()) != 0)
+ return (err);
+
+ /* Retrieve our ID: some windows facilities vary between SoC models */
+ soc_id(&dev, &rev);
+
+ if (soc_family == MV_SOC_ARMADA_XP)
+ if ((err = decode_win_sdram_fixup()) != 0)
+ return(err);
+
+ decode_win_cpu_setup();
+ if (MV_DUMP_WIN)
+ soc_dump_decode_win();
+
+ eth_port = 0;
+ usb_port = 0;
+ if ((err = fdt_win_setup()) != 0)
+ return (err);
+
+ return (0);
+}
+
+/**************************************************************************
+ * Decode windows registers accessors
+ **************************************************************************/
+WIN_REG_IDX_RD(win_cpu_armv5, cr, MV_WIN_CPU_CTRL_ARMV5, MV_MBUS_BRIDGE_BASE)
+WIN_REG_IDX_RD(win_cpu_armv5, br, MV_WIN_CPU_BASE_ARMV5, MV_MBUS_BRIDGE_BASE)
+WIN_REG_IDX_RD(win_cpu_armv5, remap_l, MV_WIN_CPU_REMAP_LO_ARMV5, MV_MBUS_BRIDGE_BASE)
+WIN_REG_IDX_RD(win_cpu_armv5, remap_h, MV_WIN_CPU_REMAP_HI_ARMV5, MV_MBUS_BRIDGE_BASE)
+WIN_REG_IDX_WR(win_cpu_armv5, cr, MV_WIN_CPU_CTRL_ARMV5, MV_MBUS_BRIDGE_BASE)
+WIN_REG_IDX_WR(win_cpu_armv5, br, MV_WIN_CPU_BASE_ARMV5, MV_MBUS_BRIDGE_BASE)
+WIN_REG_IDX_WR(win_cpu_armv5, remap_l, MV_WIN_CPU_REMAP_LO_ARMV5, MV_MBUS_BRIDGE_BASE)
+WIN_REG_IDX_WR(win_cpu_armv5, remap_h, MV_WIN_CPU_REMAP_HI_ARMV5, MV_MBUS_BRIDGE_BASE)
+
+WIN_REG_IDX_RD(win_cpu_armv7, cr, MV_WIN_CPU_CTRL_ARMV7, MV_MBUS_BRIDGE_BASE)
+WIN_REG_IDX_RD(win_cpu_armv7, br, MV_WIN_CPU_BASE_ARMV7, MV_MBUS_BRIDGE_BASE)
+WIN_REG_IDX_RD(win_cpu_armv7, remap_l, MV_WIN_CPU_REMAP_LO_ARMV7, MV_MBUS_BRIDGE_BASE)
+WIN_REG_IDX_RD(win_cpu_armv7, remap_h, MV_WIN_CPU_REMAP_HI_ARMV7, MV_MBUS_BRIDGE_BASE)
+WIN_REG_IDX_WR(win_cpu_armv7, cr, MV_WIN_CPU_CTRL_ARMV7, MV_MBUS_BRIDGE_BASE)
+WIN_REG_IDX_WR(win_cpu_armv7, br, MV_WIN_CPU_BASE_ARMV7, MV_MBUS_BRIDGE_BASE)
+WIN_REG_IDX_WR(win_cpu_armv7, remap_l, MV_WIN_CPU_REMAP_LO_ARMV7, MV_MBUS_BRIDGE_BASE)
+WIN_REG_IDX_WR(win_cpu_armv7, remap_h, MV_WIN_CPU_REMAP_HI_ARMV7, MV_MBUS_BRIDGE_BASE)
+
+static uint32_t
+win_cpu_cr_read(int i)
+{
+
+ if (soc_decode_win_spec->cr_read != NULL)
+ return (soc_decode_win_spec->cr_read(i));
+ return (-1);
+}
+
+static uint32_t
+win_cpu_br_read(int i)
+{
+
+ if (soc_decode_win_spec->br_read != NULL)
+ return (soc_decode_win_spec->br_read(i));
+ return (-1);
+}
+
+static uint32_t
+win_cpu_remap_l_read(int i)
+{
+
+ if (soc_decode_win_spec->remap_l_read != NULL)
+ return (soc_decode_win_spec->remap_l_read(i));
+ return (-1);
+}
+
+static uint32_t
+win_cpu_remap_h_read(int i)
+{
+
+ if (soc_decode_win_spec->remap_h_read != NULL)
+ return soc_decode_win_spec->remap_h_read(i);
+ return (-1);
+}
+
+static void
+win_cpu_cr_write(int i, uint32_t val)
+{
+
+ if (soc_decode_win_spec->cr_write != NULL)
+ soc_decode_win_spec->cr_write(i, val);
+}
+
+static void
+win_cpu_br_write(int i, uint32_t val)
+{
+
+ if (soc_decode_win_spec->br_write != NULL)
+ soc_decode_win_spec->br_write(i, val);
+}
+
+static void
+win_cpu_remap_l_write(int i, uint32_t val)
+{
+
+ if (soc_decode_win_spec->remap_l_write != NULL)
+ soc_decode_win_spec->remap_l_write(i, val);
+}
+
+static void
+win_cpu_remap_h_write(int i, uint32_t val)
+{
+
+ if (soc_decode_win_spec->remap_h_write != NULL)
+ soc_decode_win_spec->remap_h_write(i, val);
+}
+
+WIN_REG_BASE_IDX_RD(win_cesa, cr, MV_WIN_CESA_CTRL)
+WIN_REG_BASE_IDX_RD(win_cesa, br, MV_WIN_CESA_BASE)
+WIN_REG_BASE_IDX_WR(win_cesa, cr, MV_WIN_CESA_CTRL)
+WIN_REG_BASE_IDX_WR(win_cesa, br, MV_WIN_CESA_BASE)
+
+WIN_REG_BASE_IDX_RD(win_usb, cr, MV_WIN_USB_CTRL)
+WIN_REG_BASE_IDX_RD(win_usb, br, MV_WIN_USB_BASE)
+WIN_REG_BASE_IDX_WR(win_usb, cr, MV_WIN_USB_CTRL)
+WIN_REG_BASE_IDX_WR(win_usb, br, MV_WIN_USB_BASE)
+
+WIN_REG_BASE_IDX_RD(win_usb3, cr, MV_WIN_USB3_CTRL)
+WIN_REG_BASE_IDX_RD(win_usb3, br, MV_WIN_USB3_BASE)
+WIN_REG_BASE_IDX_WR(win_usb3, cr, MV_WIN_USB3_CTRL)
+WIN_REG_BASE_IDX_WR(win_usb3, br, MV_WIN_USB3_BASE)
+
+WIN_REG_BASE_IDX_RD(win_eth, br, MV_WIN_ETH_BASE)
+WIN_REG_BASE_IDX_RD(win_eth, sz, MV_WIN_ETH_SIZE)
+WIN_REG_BASE_IDX_RD(win_eth, har, MV_WIN_ETH_REMAP)
+WIN_REG_BASE_IDX_WR(win_eth, br, MV_WIN_ETH_BASE)
+WIN_REG_BASE_IDX_WR(win_eth, sz, MV_WIN_ETH_SIZE)
+WIN_REG_BASE_IDX_WR(win_eth, har, MV_WIN_ETH_REMAP)
+
+WIN_REG_BASE_RD(win_eth, bare, 0x290)
+WIN_REG_BASE_RD(win_eth, epap, 0x294)
+WIN_REG_BASE_WR(win_eth, bare, 0x290)
+WIN_REG_BASE_WR(win_eth, epap, 0x294)
+
+WIN_REG_BASE_IDX_RD(win_pcie, cr, MV_WIN_PCIE_CTRL);
+WIN_REG_BASE_IDX_RD(win_pcie, br, MV_WIN_PCIE_BASE);
+WIN_REG_BASE_IDX_RD(win_pcie, remap, MV_WIN_PCIE_REMAP);
+WIN_REG_BASE_IDX_WR(win_pcie, cr, MV_WIN_PCIE_CTRL);
+WIN_REG_BASE_IDX_WR(win_pcie, br, MV_WIN_PCIE_BASE);
+WIN_REG_BASE_IDX_WR(win_pcie, remap, MV_WIN_PCIE_REMAP);
+WIN_REG_BASE_IDX_RD(pcie_bar, br, MV_PCIE_BAR_BASE);
+WIN_REG_BASE_IDX_RD(pcie_bar, brh, MV_PCIE_BAR_BASE_H);
+WIN_REG_BASE_IDX_RD(pcie_bar, cr, MV_PCIE_BAR_CTRL);
+WIN_REG_BASE_IDX_WR(pcie_bar, br, MV_PCIE_BAR_BASE);
+WIN_REG_BASE_IDX_WR(pcie_bar, brh, MV_PCIE_BAR_BASE_H);
+WIN_REG_BASE_IDX_WR(pcie_bar, cr, MV_PCIE_BAR_CTRL);
+
+WIN_REG_BASE_IDX_RD(win_sata, cr, MV_WIN_SATA_CTRL);
+WIN_REG_BASE_IDX_RD(win_sata, br, MV_WIN_SATA_BASE);
+WIN_REG_BASE_IDX_WR(win_sata, cr, MV_WIN_SATA_CTRL);
+WIN_REG_BASE_IDX_WR(win_sata, br, MV_WIN_SATA_BASE);
+
+WIN_REG_BASE_IDX_RD(win_sata_armada38x, sz, MV_WIN_SATA_SIZE_ARMADA38X);
+WIN_REG_BASE_IDX_WR(win_sata_armada38x, sz, MV_WIN_SATA_SIZE_ARMADA38X);
+WIN_REG_BASE_IDX_RD(win_sata_armada38x, cr, MV_WIN_SATA_CTRL_ARMADA38X);
+WIN_REG_BASE_IDX_WR(win_sata_armada38x, cr, MV_WIN_SATA_CTRL_ARMADA38X);
+WIN_REG_BASE_IDX_WR(win_sata_armada38x, br, MV_WIN_SATA_BASE_ARMADA38X);
+
+WIN_REG_BASE_IDX_RD(win_sdhci, cr, MV_WIN_SDHCI_CTRL);
+WIN_REG_BASE_IDX_RD(win_sdhci, br, MV_WIN_SDHCI_BASE);
+WIN_REG_BASE_IDX_WR(win_sdhci, cr, MV_WIN_SDHCI_CTRL);
+WIN_REG_BASE_IDX_WR(win_sdhci, br, MV_WIN_SDHCI_BASE);
+
+#ifndef SOC_MV_DOVE
+WIN_REG_IDX_RD(ddr_armv5, br, MV_WIN_DDR_BASE, MV_DDR_CADR_BASE)
+WIN_REG_IDX_RD(ddr_armv5, sz, MV_WIN_DDR_SIZE, MV_DDR_CADR_BASE)
+WIN_REG_IDX_WR(ddr_armv5, br, MV_WIN_DDR_BASE, MV_DDR_CADR_BASE)
+WIN_REG_IDX_WR(ddr_armv5, sz, MV_WIN_DDR_SIZE, MV_DDR_CADR_BASE)
+
+WIN_REG_IDX_RD(ddr_armv7, br, MV_WIN_DDR_BASE, MV_DDR_CADR_BASE_ARMV7)
+WIN_REG_IDX_RD(ddr_armv7, sz, MV_WIN_DDR_SIZE, MV_DDR_CADR_BASE_ARMV7)
+WIN_REG_IDX_WR(ddr_armv7, br, MV_WIN_DDR_BASE, MV_DDR_CADR_BASE_ARMV7)
+WIN_REG_IDX_WR(ddr_armv7, sz, MV_WIN_DDR_SIZE, MV_DDR_CADR_BASE_ARMV7)
+
+static inline uint32_t
+ddr_br_read(int i)
+{
+
+ if (soc_decode_win_spec->ddr_br_read != NULL)
+ return (soc_decode_win_spec->ddr_br_read(i));
+ return (-1);
+}
+
+static inline uint32_t
+ddr_sz_read(int i)
+{
+
+ if (soc_decode_win_spec->ddr_sz_read != NULL)
+ return (soc_decode_win_spec->ddr_sz_read(i));
+ return (-1);
+}
+
+static inline void
+ddr_br_write(int i, uint32_t val)
+{
+
+ if (soc_decode_win_spec->ddr_br_write != NULL)
+ soc_decode_win_spec->ddr_br_write(i, val);
+}
+
+static inline void
+ddr_sz_write(int i, uint32_t val)
+{
+
+ if (soc_decode_win_spec->ddr_sz_write != NULL)
+ soc_decode_win_spec->ddr_sz_write(i, val);
+}
+#else
+/*
+ * On 88F6781 (Dove) SoC DDR Controller is accessed through
+ * single MBUS <-> AXI bridge. In this case we provide emulated
+ * ddr_br_read() and ddr_sz_read() functions to keep compatibility
+ * with common decoding windows setup code.
+ */
+
+static inline uint32_t ddr_br_read(int i)
+{
+ uint32_t mmap;
+
+ /* Read Memory Address Map Register for CS i */
+ mmap = bus_space_read_4(fdtbus_bs_tag, MV_DDR_CADR_BASE + (i * 0x10), 0);
+
+ /* Return CS i base address */
+ return (mmap & 0xFF000000);
+}
+
+static inline uint32_t ddr_sz_read(int i)
+{
+ uint32_t mmap, size;
+
+ /* Read Memory Address Map Register for CS i */
+ mmap = bus_space_read_4(fdtbus_bs_tag, MV_DDR_CADR_BASE + (i * 0x10), 0);
+
+ /* Extract size of CS space in 64kB units */
+ size = (1 << ((mmap >> 16) & 0x0F));
+
+ /* Return CS size and enable/disable status */
+ return (((size - 1) << 16) | (mmap & 0x01));
+}
+#endif
+
+/**************************************************************************
+ * Decode windows helper routines
+ **************************************************************************/
+void
+soc_dump_decode_win(void)
+{
+ int i;
+
+ for (i = 0; i < soc_decode_win_spec->mv_win_cpu_max; i++) {
+ printf("CPU window#%d: c 0x%08x, b 0x%08x", i,
+ win_cpu_cr_read(i),
+ win_cpu_br_read(i));
+
+ if (win_cpu_can_remap(i))
+ printf(", rl 0x%08x, rh 0x%08x",
+ win_cpu_remap_l_read(i),
+ win_cpu_remap_h_read(i));
+
+ printf("\n");
+ }
+ printf("Internal regs base: 0x%08x\n",
+ bus_space_read_4(fdtbus_bs_tag, MV_INTREGS_BASE, 0));
+
+ for (i = 0; i < MV_WIN_DDR_MAX; i++)
+ printf("DDR CS#%d: b 0x%08x, s 0x%08x\n", i,
+ ddr_br_read(i), ddr_sz_read(i));
+}
+
+/**************************************************************************
+ * CPU windows routines
+ **************************************************************************/
+int
+win_cpu_can_remap(int i)
+{
+ uint32_t dev, rev;
+
+ soc_id(&dev, &rev);
+
+ /* Depending on the SoC certain windows have remap capability */
+ if ((dev == MV_DEV_88F5182 && i < 2) ||
+ (dev == MV_DEV_88F5281 && i < 4) ||
+ (dev == MV_DEV_88F6281 && i < 4) ||
+ (dev == MV_DEV_88F6282 && i < 4) ||
+ (dev == MV_DEV_88F6828 && i < 20) ||
+ (dev == MV_DEV_88F6820 && i < 20) ||
+ (dev == MV_DEV_88F6810 && i < 20) ||
+ (dev == MV_DEV_88RC8180 && i < 2) ||
+ (dev == MV_DEV_88F6781 && i < 4) ||
+ (dev == MV_DEV_MV78100_Z0 && i < 8) ||
+ ((dev & MV_DEV_FAMILY_MASK) == MV_DEV_DISCOVERY && i < 8))
+ return (1);
+
+ return (0);
+}
+
+/* XXX This should check for overlapping remap fields too.. */
+int
+decode_win_overlap(int win, int win_no, const struct decode_win *wintab)
+{
+ const struct decode_win *tab;
+ int i;
+
+ tab = wintab;
+
+ for (i = 0; i < win_no; i++, tab++) {
+ if (i == win)
+ /* Skip self */
+ continue;
+
+ if ((tab->base + tab->size - 1) < (wintab + win)->base)
+ continue;
+
+ else if (((wintab + win)->base + (wintab + win)->size - 1) <
+ tab->base)
+ continue;
+ else
+ return (i);
+ }
+
+ return (-1);
+}
+
+int
+decode_win_cpu_set(int target, int attr, vm_paddr_t base, uint32_t size,
+ vm_paddr_t remap)
+{
+ uint32_t br, cr;
+ int win, i;
+
+ if (remap == ~0) {
+ win = soc_decode_win_spec->mv_win_cpu_max - 1;
+ i = -1;
+ } else {
+ win = 0;
+ i = 1;
+ }
+
+ while ((win >= 0) && (win < soc_decode_win_spec->mv_win_cpu_max)) {
+ cr = win_cpu_cr_read(win);
+ if ((cr & MV_WIN_CPU_ENABLE_BIT) == 0)
+ break;
+ if ((cr & ((0xff << MV_WIN_CPU_ATTR_SHIFT) |
+ (0x1f << MV_WIN_CPU_TARGET_SHIFT))) ==
+ ((attr << MV_WIN_CPU_ATTR_SHIFT) |
+ (target << MV_WIN_CPU_TARGET_SHIFT)))
+ break;
+ win += i;
+ }
+ if ((win < 0) || (win >= soc_decode_win_spec->mv_win_cpu_max) ||
+ ((remap != ~0) && (win_cpu_can_remap(win) == 0)))
+ return (-1);
+
+ br = base & 0xffff0000;
+ win_cpu_br_write(win, br);
+
+ if (win_cpu_can_remap(win)) {
+ if (remap != ~0) {
+ win_cpu_remap_l_write(win, remap & 0xffff0000);
+ win_cpu_remap_h_write(win, 0);
+ } else {
+ /*
+ * Remap function is not used for a given window
+ * (capable of remapping) - set remap field with the
+ * same value as base.
+ */
+ win_cpu_remap_l_write(win, base & 0xffff0000);
+ win_cpu_remap_h_write(win, 0);
+ }
+ }
+
+ cr = ((size - 1) & 0xffff0000) | (attr << MV_WIN_CPU_ATTR_SHIFT) |
+ (target << MV_WIN_CPU_TARGET_SHIFT) | MV_WIN_CPU_ENABLE_BIT;
+ win_cpu_cr_write(win, cr);
+
+ return (0);
+}
+
+static void
+decode_win_cpu_setup(void)
+{
+ int i;
+
+ /* Disable all CPU windows */
+ for (i = 0; i < soc_decode_win_spec->mv_win_cpu_max; i++) {
+ win_cpu_cr_write(i, 0);
+ win_cpu_br_write(i, 0);
+ if (win_cpu_can_remap(i)) {
+ win_cpu_remap_l_write(i, 0);
+ win_cpu_remap_h_write(i, 0);
+ }
+ }
+
+ for (i = 0; i < cpu_wins_no; i++)
+ if (cpu_wins[i].target > 0)
+ decode_win_cpu_set(cpu_wins[i].target,
+ cpu_wins[i].attr, cpu_wins[i].base,
+ cpu_wins[i].size, cpu_wins[i].remap);
+
+}
+
+static int
+decode_win_sdram_fixup(void)
+{
+ struct mem_region mr[FDT_MEM_REGIONS];
+ uint8_t window_valid[MV_WIN_DDR_MAX];
+ int mr_cnt, err, i, j;
+ uint32_t valid_win_num = 0;
+
+ /* Grab physical memory regions information from device tree. */
+ err = fdt_get_mem_regions(mr, &mr_cnt, NULL);
+ if (err != 0)
+ return (err);
+
+ for (i = 0; i < MV_WIN_DDR_MAX; i++)
+ window_valid[i] = 0;
+
+ /* Try to match entries from device tree with settings from u-boot */
+ for (i = 0; i < mr_cnt; i++) {
+ for (j = 0; j < MV_WIN_DDR_MAX; j++) {
+ if (ddr_is_active(j) &&
+ (ddr_base(j) == mr[i].mr_start) &&
+ (ddr_size(j) == mr[i].mr_size)) {
+ window_valid[j] = 1;
+ valid_win_num++;
+ }
+ }
+ }
+
+ if (mr_cnt != valid_win_num)
+ return (EINVAL);
+
+ /* Destroy windows without corresponding device tree entry */
+ for (j = 0; j < MV_WIN_DDR_MAX; j++) {
+ if (ddr_is_active(j) && (window_valid[j] != 1)) {
+ printf("Disabling SDRAM decoding window: %d\n", j);
+ ddr_disable(j);
+ }
+ }
+
+ return (0);
+}
+/*
+ * Check if we're able to cover all active DDR banks.
+ */
+static int
+decode_win_can_cover_ddr(int max)
+{
+ int i, c;
+
+ c = 0;
+ for (i = 0; i < MV_WIN_DDR_MAX; i++)
+ if (ddr_is_active(i))
+ c++;
+
+ if (c > max) {
+ printf("Unable to cover all active DDR banks: "
+ "%d, available windows: %d\n", c, max);
+ return (0);
+ }
+
+ return (1);
+}
+
+/**************************************************************************
+ * DDR windows routines
+ **************************************************************************/
+int
+ddr_is_active(int i)
+{
+
+ if (ddr_sz_read(i) & 0x1)
+ return (1);
+
+ return (0);
+}
+
+void
+ddr_disable(int i)
+{
+
+ ddr_sz_write(i, 0);
+ ddr_br_write(i, 0);
+}
+
+uint32_t
+ddr_base(int i)
+{
+
+ return (ddr_br_read(i) & 0xff000000);
+}
+
+uint32_t
+ddr_size(int i)
+{
+
+ return ((ddr_sz_read(i) | 0x00ffffff) + 1);
+}
+
+uint32_t
+ddr_attr(int i)
+{
+ uint32_t dev, rev, attr;
+
+ soc_id(&dev, &rev);
+ if (dev == MV_DEV_88RC8180)
+ return ((ddr_sz_read(i) & 0xf0) >> 4);
+ if (dev == MV_DEV_88F6781)
+ return (0);
+
+ attr = (i == 0 ? 0xe :
+ (i == 1 ? 0xd :
+ (i == 2 ? 0xb :
+ (i == 3 ? 0x7 : 0xff))));
+ if (platform_io_coherent)
+ attr |= 0x10;
+
+ return (attr);
+}
+
+uint32_t
+ddr_target(int i)
+{
+ uint32_t dev, rev;
+
+ soc_id(&dev, &rev);
+ if (dev == MV_DEV_88RC8180) {
+ i = (ddr_sz_read(i) & 0xf0) >> 4;
+ return (i == 0xe ? 0xc :
+ (i == 0xd ? 0xd :
+ (i == 0xb ? 0xe :
+ (i == 0x7 ? 0xf : 0xc))));
+ }
+
+ /*
+ * On SOCs other than 88RC8180 Mbus unit ID for
+ * DDR SDRAM controller is always 0x0.
+ */
+ return (0);
+}
+
+/**************************************************************************
+ * CESA windows routines
+ **************************************************************************/
+static int
+decode_win_cesa_valid(void)
+{
+
+ return (decode_win_can_cover_ddr(MV_WIN_CESA_MAX));
+}
+
+static void
+decode_win_cesa_dump(u_long base)
+{
+ int i;
+
+ for (i = 0; i < MV_WIN_CESA_MAX; i++)
+ printf("CESA window#%d: c 0x%08x, b 0x%08x\n", i,
+ win_cesa_cr_read(base, i), win_cesa_br_read(base, i));
+}
+
+/*
+ * Set CESA decode windows.
+ */
+static void
+decode_win_cesa_setup(u_long base)
+{
+ uint32_t br, cr;
+ uint64_t size;
+ int i, j;
+
+ for (i = 0; i < MV_WIN_CESA_MAX; i++) {
+ win_cesa_cr_write(base, i, 0);
+ win_cesa_br_write(base, i, 0);
+ }
+
+ /* Only access to active DRAM banks is required */
+ for (i = 0; i < MV_WIN_DDR_MAX; i++) {
+ if (ddr_is_active(i)) {
+ br = ddr_base(i);
+
+ size = ddr_size(i);
+ /*
+ * Armada 38x SoC's equipped with 4GB DRAM
+ * suffer freeze during CESA operation, if
+ * MBUS window opened at given DRAM CS reaches
+ * end of the address space. Apply a workaround
+ * by setting the window size to the closest possible
+ * value, i.e. divide it by 2.
+ */
+ if ((soc_family == MV_SOC_ARMADA_38X) &&
+ (size + ddr_base(i) == 0x100000000ULL))
+ size /= 2;
+
+ cr = (((size - 1) & 0xffff0000) |
+ (ddr_attr(i) << IO_WIN_ATTR_SHIFT) |
+ (ddr_target(i) << IO_WIN_TGT_SHIFT) |
+ IO_WIN_ENA_MASK);
+
+ /* Set the first free CESA window */
+ for (j = 0; j < MV_WIN_CESA_MAX; j++) {
+ if (win_cesa_cr_read(base, j) & 0x1)
+ continue;
+
+ win_cesa_br_write(base, j, br);
+ win_cesa_cr_write(base, j, cr);
+ break;
+ }
+ }
+ }
+}
+
+static void
+decode_win_a38x_cesa_setup(u_long base)
+{
+ decode_win_cesa_setup(base);
+ decode_win_cesa_setup(base + MV_WIN_CESA_OFFSET);
+}
+
+static void
+decode_win_a38x_cesa_dump(u_long base)
+{
+ decode_win_cesa_dump(base);
+ decode_win_cesa_dump(base + MV_WIN_CESA_OFFSET);
+}
+
+/**************************************************************************
+ * USB windows routines
+ **************************************************************************/
+static int
+decode_win_usb_valid(void)
+{
+
+ return (decode_win_can_cover_ddr(MV_WIN_USB_MAX));
+}
+
+static void
+decode_win_usb_dump(u_long base)
+{
+ int i;
+
+ if (pm_is_disabled(CPU_PM_CTRL_USB(usb_port - 1)))
+ return;
+
+ for (i = 0; i < MV_WIN_USB_MAX; i++)
+ printf("USB window#%d: c 0x%08x, b 0x%08x\n", i,
+ win_usb_cr_read(base, i), win_usb_br_read(base, i));
+}
+
+/*
+ * Set USB decode windows.
+ */
+static void
+decode_win_usb_setup(u_long base)
+{
+ uint32_t br, cr;
+ int i, j;
+
+ if (pm_is_disabled(CPU_PM_CTRL_USB(usb_port)))
+ return;
+
+ usb_port++;
+
+ for (i = 0; i < MV_WIN_USB_MAX; i++) {
+ win_usb_cr_write(base, i, 0);
+ win_usb_br_write(base, i, 0);
+ }
+
+ /* Only access to active DRAM banks is required */
+ for (i = 0; i < MV_WIN_DDR_MAX; i++) {
+ if (ddr_is_active(i)) {
+ br = ddr_base(i);
+ /*
+ * XXX for 6281 we should handle Mbus write
+ * burst limit field in the ctrl reg
+ */
+ cr = (((ddr_size(i) - 1) & 0xffff0000) |
+ (ddr_attr(i) << 8) |
+ (ddr_target(i) << 4) | 1);
+
+ /* Set the first free USB window */
+ for (j = 0; j < MV_WIN_USB_MAX; j++) {
+ if (win_usb_cr_read(base, j) & 0x1)
+ continue;
+
+ win_usb_br_write(base, j, br);
+ win_usb_cr_write(base, j, cr);
+ break;
+ }
+ }
+ }
+}
+
+/**************************************************************************
+ * USB3 windows routines
+ **************************************************************************/
+static int
+decode_win_usb3_valid(void)
+{
+
+ return (decode_win_can_cover_ddr(MV_WIN_USB3_MAX));
+}
+
+static void
+decode_win_usb3_dump(u_long base)
+{
+ int i;
+
+ for (i = 0; i < MV_WIN_USB3_MAX; i++)
+ printf("USB3.0 window#%d: c 0x%08x, b 0x%08x\n", i,
+ win_usb3_cr_read(base, i), win_usb3_br_read(base, i));
+}
+
+/*
+ * Set USB3 decode windows
+ */
+static void
+decode_win_usb3_setup(u_long base)
+{
+ uint32_t br, cr;
+ int i, j;
+
+ for (i = 0; i < MV_WIN_USB3_MAX; i++) {
+ win_usb3_cr_write(base, i, 0);
+ win_usb3_br_write(base, i, 0);
+ }
+
+ /* Only access to active DRAM banks is required */
+ for (i = 0; i < MV_WIN_DDR_MAX; i++) {
+ if (ddr_is_active(i)) {
+ br = ddr_base(i);
+ cr = (((ddr_size(i) - 1) &
+ (IO_WIN_SIZE_MASK << IO_WIN_SIZE_SHIFT)) |
+ (ddr_attr(i) << IO_WIN_ATTR_SHIFT) |
+ (ddr_target(i) << IO_WIN_TGT_SHIFT) |
+ IO_WIN_ENA_MASK);
+
+ /* Set the first free USB3.0 window */
+ for (j = 0; j < MV_WIN_USB3_MAX; j++) {
+ if (win_usb3_cr_read(base, j) & IO_WIN_ENA_MASK)
+ continue;
+
+ win_usb3_br_write(base, j, br);
+ win_usb3_cr_write(base, j, cr);
+ break;
+ }
+ }
+ }
+}
+
+/**************************************************************************
+ * ETH windows routines
+ **************************************************************************/
+
+static int
+win_eth_can_remap(int i)
+{
+
+ /* ETH encode windows 0-3 have remap capability */
+ if (i < 4)
+ return (1);
+
+ return (0);
+}
+
+static int
+eth_bare_read(uint32_t base, int i)
+{
+ uint32_t v;
+
+ v = win_eth_bare_read(base);
+ v &= (1 << i);
+
+ return (v >> i);
+}
+
+static void
+eth_bare_write(uint32_t base, int i, int val)
+{
+ uint32_t v;
+
+ v = win_eth_bare_read(base);
+ v &= ~(1 << i);
+ v |= (val << i);
+ win_eth_bare_write(base, v);
+}
+
+static void
+eth_epap_write(uint32_t base, int i, int val)
+{
+ uint32_t v;
+
+ v = win_eth_epap_read(base);
+ v &= ~(0x3 << (i * 2));
+ v |= (val << (i * 2));
+ win_eth_epap_write(base, v);
+}
+
+static void
+decode_win_eth_dump(u_long base)
+{
+ int i;
+
+ if (pm_is_disabled(CPU_PM_CTRL_GE(eth_port - 1)))
+ return;
+
+ for (i = 0; i < MV_WIN_ETH_MAX; i++) {
+ printf("ETH window#%d: b 0x%08x, s 0x%08x", i,
+ win_eth_br_read(base, i),
+ win_eth_sz_read(base, i));
+
+ if (win_eth_can_remap(i))
+ printf(", ha 0x%08x",
+ win_eth_har_read(base, i));
+
+ printf("\n");
+ }
+ printf("ETH windows: bare 0x%08x, epap 0x%08x\n",
+ win_eth_bare_read(base),
+ win_eth_epap_read(base));
+}
+
+#define MV_WIN_ETH_DDR_TRGT(n) ddr_target(n)
+
+static void
+decode_win_eth_setup(u_long base)
+{
+ uint32_t br, sz;
+ int i, j;
+
+ if (pm_is_disabled(CPU_PM_CTRL_GE(eth_port)))
+ return;
+
+ eth_port++;
+
+ /* Disable, clear and revoke protection for all ETH windows */
+ for (i = 0; i < MV_WIN_ETH_MAX; i++) {
+ eth_bare_write(base, i, 1);
+ eth_epap_write(base, i, 0);
+ win_eth_br_write(base, i, 0);
+ win_eth_sz_write(base, i, 0);
+ if (win_eth_can_remap(i))
+ win_eth_har_write(base, i, 0);
+ }
+
+ /* Only access to active DRAM banks is required */
+ for (i = 0; i < MV_WIN_DDR_MAX; i++)
+ if (ddr_is_active(i)) {
+ br = ddr_base(i) | (ddr_attr(i) << 8) | MV_WIN_ETH_DDR_TRGT(i);
+ sz = ((ddr_size(i) - 1) & 0xffff0000);
+
+ /* Set the first free ETH window */
+ for (j = 0; j < MV_WIN_ETH_MAX; j++) {
+ if (eth_bare_read(base, j) == 0)
+ continue;
+
+ win_eth_br_write(base, j, br);
+ win_eth_sz_write(base, j, sz);
+
+ /* XXX remapping ETH windows not supported */
+
+ /* Set protection RW */
+ eth_epap_write(base, j, 0x3);
+
+ /* Enable window */
+ eth_bare_write(base, j, 0);
+ break;
+ }
+ }
+}
+
+static void
+decode_win_neta_dump(u_long base)
+{
+
+ decode_win_eth_dump(base + MV_WIN_NETA_OFFSET);
+}
+
+static void
+decode_win_neta_setup(u_long base)
+{
+
+ decode_win_eth_setup(base + MV_WIN_NETA_OFFSET);
+}
+
+static int
+decode_win_eth_valid(void)
+{
+
+ return (decode_win_can_cover_ddr(MV_WIN_ETH_MAX));
+}
+
+/**************************************************************************
+ * PCIE windows routines
+ **************************************************************************/
+static void
+decode_win_pcie_dump(u_long base)
+{
+ int i;
+
+ printf("PCIE windows base 0x%08lx\n", base);
+ for (i = 0; i < MV_WIN_PCIE_MAX; i++)
+ printf("PCIE window#%d: cr 0x%08x br 0x%08x remap 0x%08x\n",
+ i, win_pcie_cr_read(base, i),
+ win_pcie_br_read(base, i), win_pcie_remap_read(base, i));
+
+ for (i = 0; i < MV_PCIE_BAR_MAX; i++)
+ printf("PCIE bar#%d: cr 0x%08x br 0x%08x brh 0x%08x\n",
+ i, pcie_bar_cr_read(base, i),
+ pcie_bar_br_read(base, i), pcie_bar_brh_read(base, i));
+}
+
+void
+decode_win_pcie_setup(u_long base)
+{
+ uint32_t size = 0, ddrbase = ~0;
+ uint32_t cr, br;
+ int i, j;
+
+ for (i = 0; i < MV_PCIE_BAR_MAX; i++) {
+ pcie_bar_br_write(base, i,
+ MV_PCIE_BAR_64BIT | MV_PCIE_BAR_PREFETCH_EN);
+ if (i < 3)
+ pcie_bar_brh_write(base, i, 0);
+ if (i > 0)
+ pcie_bar_cr_write(base, i, 0);
+ }
+
+ for (i = 0; i < MV_WIN_PCIE_MAX; i++) {
+ win_pcie_cr_write(base, i, 0);
+ win_pcie_br_write(base, i, 0);
+ win_pcie_remap_write(base, i, 0);
+ }
+
+ /* On End-Point only set BAR size to 1MB regardless of DDR size */
+ if ((bus_space_read_4(fdtbus_bs_tag, base, MV_PCIE_CONTROL)
+ & MV_PCIE_ROOT_CMPLX) == 0) {
+ pcie_bar_cr_write(base, 1, 0xf0000 | 1);
+ return;
+ }
+
+ for (i = 0; i < MV_WIN_DDR_MAX; i++) {
+ if (ddr_is_active(i)) {
+ /* Map DDR to BAR 1 */
+ cr = (ddr_size(i) - 1) & 0xffff0000;
+ size += ddr_size(i) & 0xffff0000;
+ cr |= (ddr_attr(i) << 8) | (ddr_target(i) << 4) | 1;
+ br = ddr_base(i);
+ if (br < ddrbase)
+ ddrbase = br;
+
+ /* Use the first available PCIE window */
+ for (j = 0; j < MV_WIN_PCIE_MAX; j++) {
+ if (win_pcie_cr_read(base, j) != 0)
+ continue;
+
+ win_pcie_br_write(base, j, br);
+ win_pcie_cr_write(base, j, cr);
+ break;
+ }
+ }
+ }
+
+ /*
+ * Upper 16 bits in BAR register is interpreted as BAR size
+ * (in 64 kB units) plus 64kB, so subtract 0x10000
+ * form value passed to register to get correct value.
+ */
+ size -= 0x10000;
+ pcie_bar_cr_write(base, 1, size | 1);
+ pcie_bar_br_write(base, 1, ddrbase |
+ MV_PCIE_BAR_64BIT | MV_PCIE_BAR_PREFETCH_EN);
+ pcie_bar_br_write(base, 0, fdt_immr_pa |
+ MV_PCIE_BAR_64BIT | MV_PCIE_BAR_PREFETCH_EN);
+}
+
+static int
+decode_win_pcie_valid(void)
+{
+
+ return (decode_win_can_cover_ddr(MV_WIN_PCIE_MAX));
+}
+
+/**************************************************************************
+ * IDMA windows routines
+ **************************************************************************/
+#if defined(SOC_MV_ORION) || defined(SOC_MV_DISCOVERY)
+static int
+idma_bare_read(u_long base, int i)
+{
+ uint32_t v;
+
+ v = win_idma_bare_read(base);
+ v &= (1 << i);
+
+ return (v >> i);
+}
+
+static void
+idma_bare_write(u_long base, int i, int val)
+{
+ uint32_t v;
+
+ v = win_idma_bare_read(base);
+ v &= ~(1 << i);
+ v |= (val << i);
+ win_idma_bare_write(base, v);
+}
+
+/*
+ * Sets channel protection 'val' for window 'w' on channel 'c'
+ */
+static void
+idma_cap_write(u_long base, int c, int w, int val)
+{
+ uint32_t v;
+
+ v = win_idma_cap_read(base, c);
+ v &= ~(0x3 << (w * 2));
+ v |= (val << (w * 2));
+ win_idma_cap_write(base, c, v);
+}
+
+/*
+ * Set protection 'val' on all channels for window 'w'
+ */
+static void
+idma_set_prot(u_long base, int w, int val)
+{
+ int c;
+
+ for (c = 0; c < MV_IDMA_CHAN_MAX; c++)
+ idma_cap_write(base, c, w, val);
+}
+
+static int
+win_idma_can_remap(int i)
+{
+
+ /* IDMA decode windows 0-3 have remap capability */
+ if (i < 4)
+ return (1);
+
+ return (0);
+}
+
+void
+decode_win_idma_setup(u_long base)
+{
+ uint32_t br, sz;
+ int i, j;
+
+ if (pm_is_disabled(CPU_PM_CTRL_IDMA))
+ return;
+ /*
+ * Disable and clear all IDMA windows, revoke protection for all channels
+ */
+ for (i = 0; i < MV_WIN_IDMA_MAX; i++) {
+ idma_bare_write(base, i, 1);
+ win_idma_br_write(base, i, 0);
+ win_idma_sz_write(base, i, 0);
+ if (win_idma_can_remap(i) == 1)
+ win_idma_har_write(base, i, 0);
+ }
+ for (i = 0; i < MV_IDMA_CHAN_MAX; i++)
+ win_idma_cap_write(base, i, 0);
+
+ /*
+ * Set up access to all active DRAM banks
+ */
+ for (i = 0; i < MV_WIN_DDR_MAX; i++)
+ if (ddr_is_active(i)) {
+ br = ddr_base(i) | (ddr_attr(i) << 8) | ddr_target(i);
+ sz = ((ddr_size(i) - 1) & 0xffff0000);
+
+ /* Place DDR entries in non-remapped windows */
+ for (j = 0; j < MV_WIN_IDMA_MAX; j++)
+ if (win_idma_can_remap(j) != 1 &&
+ idma_bare_read(base, j) == 1) {
+ /* Configure window */
+ win_idma_br_write(base, j, br);
+ win_idma_sz_write(base, j, sz);
+
+ /* Set protection RW on all channels */
+ idma_set_prot(base, j, 0x3);
+
+ /* Enable window */
+ idma_bare_write(base, j, 0);
+ break;
+ }
+ }
+
+ /*
+ * Remaining targets -- from statically defined table
+ */
+ for (i = 0; i < idma_wins_no; i++)
+ if (idma_wins[i].target > 0) {
+ br = (idma_wins[i].base & 0xffff0000) |
+ (idma_wins[i].attr << 8) | idma_wins[i].target;
+ sz = ((idma_wins[i].size - 1) & 0xffff0000);
+
+ /* Set the first free IDMA window */
+ for (j = 0; j < MV_WIN_IDMA_MAX; j++) {
+ if (idma_bare_read(base, j) == 0)
+ continue;
+
+ /* Configure window */
+ win_idma_br_write(base, j, br);
+ win_idma_sz_write(base, j, sz);
+ if (win_idma_can_remap(j) &&
+ idma_wins[j].remap >= 0)
+ win_idma_har_write(base, j,
+ idma_wins[j].remap);
+
+ /* Set protection RW on all channels */
+ idma_set_prot(base, j, 0x3);
+
+ /* Enable window */
+ idma_bare_write(base, j, 0);
+ break;
+ }
+ }
+}
+
+int
+decode_win_idma_valid(void)
+{
+ const struct decode_win *wintab;
+ int c, i, j, rv;
+ uint32_t b, e, s;
+
+ if (idma_wins_no > MV_WIN_IDMA_MAX) {
+ printf("IDMA windows: too many entries: %d\n", idma_wins_no);
+ return (0);
+ }
+ for (i = 0, c = 0; i < MV_WIN_DDR_MAX; i++)
+ if (ddr_is_active(i))
+ c++;
+
+ if (idma_wins_no > (MV_WIN_IDMA_MAX - c)) {
+ printf("IDMA windows: too many entries: %d, available: %d\n",
+ idma_wins_no, MV_WIN_IDMA_MAX - c);
+ return (0);
+ }
+
+ wintab = idma_wins;
+ rv = 1;
+ for (i = 0; i < idma_wins_no; i++, wintab++) {
+ if (wintab->target == 0) {
+ printf("IDMA window#%d: DDR target window is not "
+ "supposed to be reprogrammed!\n", i);
+ rv = 0;
+ }
+
+ if (wintab->remap >= 0 && win_cpu_can_remap(i) != 1) {
+ printf("IDMA window#%d: not capable of remapping, but "
+ "val 0x%08x defined\n", i, wintab->remap);
+ rv = 0;
+ }
+
+ s = wintab->size;
+ b = wintab->base;
+ e = b + s - 1;
+ if (s > (0xFFFFFFFF - b + 1)) {
+ /* XXX this boundary check should account for 64bit and
+ * remapping.. */
+ printf("IDMA window#%d: no space for size 0x%08x at "
+ "0x%08x\n", i, s, b);
+ rv = 0;
+ continue;
+ }
+
+ j = decode_win_overlap(i, idma_wins_no, &idma_wins[0]);
+ if (j >= 0) {
+ printf("IDMA window#%d: (0x%08x - 0x%08x) overlaps "
+ "with #%d (0x%08x - 0x%08x)\n", i, b, e, j,
+ idma_wins[j].base,
+ idma_wins[j].base + idma_wins[j].size - 1);
+ rv = 0;
+ }
+ }
+
+ return (rv);
+}
+
+void
+decode_win_idma_dump(u_long base)
+{
+ int i;
+
+ if (pm_is_disabled(CPU_PM_CTRL_IDMA))
+ return;
+
+ for (i = 0; i < MV_WIN_IDMA_MAX; i++) {
+ printf("IDMA window#%d: b 0x%08x, s 0x%08x", i,
+ win_idma_br_read(base, i), win_idma_sz_read(base, i));
+
+ if (win_idma_can_remap(i))
+ printf(", ha 0x%08x", win_idma_har_read(base, i));
+
+ printf("\n");
+ }
+ for (i = 0; i < MV_IDMA_CHAN_MAX; i++)
+ printf("IDMA channel#%d: ap 0x%08x\n", i,
+ win_idma_cap_read(base, i));
+ printf("IDMA windows: bare 0x%08x\n", win_idma_bare_read(base));
+}
+#else
+
+/* Provide dummy functions to satisfy the build for SoCs not equipped with IDMA */
+int
+decode_win_idma_valid(void)
+{
+
+ return (1);
+}
+
+void
+decode_win_idma_setup(u_long base)
+{
+}
+
+void
+decode_win_idma_dump(u_long base)
+{
+}
+#endif
+
+/**************************************************************************
+ * XOR windows routines
+ **************************************************************************/
+#if defined(SOC_MV_KIRKWOOD) || defined(SOC_MV_DISCOVERY)
+static int
+xor_ctrl_read(u_long base, int i, int c, int e)
+{
+ uint32_t v;
+ v = win_xor_ctrl_read(base, c, e);
+ v &= (1 << i);
+
+ return (v >> i);
+}
+
+static void
+xor_ctrl_write(u_long base, int i, int c, int e, int val)
+{
+ uint32_t v;
+
+ v = win_xor_ctrl_read(base, c, e);
+ v &= ~(1 << i);
+ v |= (val << i);
+ win_xor_ctrl_write(base, c, e, v);
+}
+
+/*
+ * Set channel protection 'val' for window 'w' on channel 'c'
+ */
+static void
+xor_chan_write(u_long base, int c, int e, int w, int val)
+{
+ uint32_t v;
+
+ v = win_xor_ctrl_read(base, c, e);
+ v &= ~(0x3 << (w * 2 + 16));
+ v |= (val << (w * 2 + 16));
+ win_xor_ctrl_write(base, c, e, v);
+}
+
+/*
+ * Set protection 'val' on all channels for window 'w' on engine 'e'
+ */
+static void
+xor_set_prot(u_long base, int w, int e, int val)
+{
+ int c;
+
+ for (c = 0; c < MV_XOR_CHAN_MAX; c++)
+ xor_chan_write(base, c, e, w, val);
+}
+
+static int
+win_xor_can_remap(int i)
+{
+
+ /* XOR decode windows 0-3 have remap capability */
+ if (i < 4)
+ return (1);
+
+ return (0);
+}
+
+static int
+xor_max_eng(void)
+{
+ uint32_t dev, rev;
+
+ soc_id(&dev, &rev);
+ switch (dev) {
+ case MV_DEV_88F6281:
+ case MV_DEV_88F6282:
+ case MV_DEV_MV78130:
+ case MV_DEV_MV78160:
+ case MV_DEV_MV78230:
+ case MV_DEV_MV78260:
+ case MV_DEV_MV78460:
+ return (2);
+ case MV_DEV_MV78100:
+ case MV_DEV_MV78100_Z0:
+ return (1);
+ default:
+ return (0);
+ }
+}
+
+static void
+xor_active_dram(u_long base, int c, int e, int *window)
+{
+ uint32_t br, sz;
+ int i, m, w;
+
+ /*
+ * Set up access to all active DRAM banks
+ */
+ m = xor_max_eng();
+ for (i = 0; i < m; i++)
+ if (ddr_is_active(i)) {
+ br = ddr_base(i) | (ddr_attr(i) << 8) |
+ ddr_target(i);
+ sz = ((ddr_size(i) - 1) & 0xffff0000);
+
+ /* Place DDR entries in non-remapped windows */
+ for (w = 0; w < MV_WIN_XOR_MAX; w++)
+ if (win_xor_can_remap(w) != 1 &&
+ (xor_ctrl_read(base, w, c, e) == 0) &&
+ w > *window) {
+ /* Configure window */
+ win_xor_br_write(base, w, e, br);
+ win_xor_sz_write(base, w, e, sz);
+
+ /* Set protection RW on all channels */
+ xor_set_prot(base, w, e, 0x3);
+
+ /* Enable window */
+ xor_ctrl_write(base, w, c, e, 1);
+ (*window)++;
+ break;
+ }
+ }
+}
+
+void
+decode_win_xor_setup(u_long base)
+{
+ uint32_t br, sz;
+ int i, j, z, e = 1, m, window;
+
+ if (pm_is_disabled(CPU_PM_CTRL_XOR))
+ return;
+
+ /*
+ * Disable and clear all XOR windows, revoke protection for all
+ * channels
+ */
+ m = xor_max_eng();
+ for (j = 0; j < m; j++, e--) {
+ /* Number of non-remaped windows */
+ window = MV_XOR_NON_REMAP - 1;
+
+ for (i = 0; i < MV_WIN_XOR_MAX; i++) {
+ win_xor_br_write(base, i, e, 0);
+ win_xor_sz_write(base, i, e, 0);
+ }
+
+ if (win_xor_can_remap(i) == 1)
+ win_xor_har_write(base, i, e, 0);
+
+ for (i = 0; i < MV_XOR_CHAN_MAX; i++) {
+ win_xor_ctrl_write(base, i, e, 0);
+ xor_active_dram(base, i, e, &window);
+ }
+
+ /*
+ * Remaining targets -- from a statically defined table
+ */
+ for (i = 0; i < xor_wins_no; i++)
+ if (xor_wins[i].target > 0) {
+ br = (xor_wins[i].base & 0xffff0000) |
+ (xor_wins[i].attr << 8) |
+ xor_wins[i].target;
+ sz = ((xor_wins[i].size - 1) & 0xffff0000);
+
+ /* Set the first free XOR window */
+ for (z = 0; z < MV_WIN_XOR_MAX; z++) {
+ if (xor_ctrl_read(base, z, 0, e) &&
+ xor_ctrl_read(base, z, 1, e))
+ continue;
+
+ /* Configure window */
+ win_xor_br_write(base, z, e, br);
+ win_xor_sz_write(base, z, e, sz);
+ if (win_xor_can_remap(z) &&
+ xor_wins[z].remap >= 0)
+ win_xor_har_write(base, z, e,
+ xor_wins[z].remap);
+
+ /* Set protection RW on all channels */
+ xor_set_prot(base, z, e, 0x3);
+
+ /* Enable window */
+ xor_ctrl_write(base, z, 0, e, 1);
+ xor_ctrl_write(base, z, 1, e, 1);
+ break;
+ }
+ }
+ }
+}
+
+int
+decode_win_xor_valid(void)
+{
+ const struct decode_win *wintab;
+ int c, i, j, rv;
+ uint32_t b, e, s;
+
+ if (xor_wins_no > MV_WIN_XOR_MAX) {
+ printf("XOR windows: too many entries: %d\n", xor_wins_no);
+ return (0);
+ }
+ for (i = 0, c = 0; i < MV_WIN_DDR_MAX; i++)
+ if (ddr_is_active(i))
+ c++;
+
+ if (xor_wins_no > (MV_WIN_XOR_MAX - c)) {
+ printf("XOR windows: too many entries: %d, available: %d\n",
+ xor_wins_no, MV_WIN_IDMA_MAX - c);
+ return (0);
+ }
+
+ wintab = xor_wins;
+ rv = 1;
+ for (i = 0; i < xor_wins_no; i++, wintab++) {
+ if (wintab->target == 0) {
+ printf("XOR window#%d: DDR target window is not "
+ "supposed to be reprogrammed!\n", i);
+ rv = 0;
+ }
+
+ if (wintab->remap >= 0 && win_cpu_can_remap(i) != 1) {
+ printf("XOR window#%d: not capable of remapping, but "
+ "val 0x%08x defined\n", i, wintab->remap);
+ rv = 0;
+ }
+
+ s = wintab->size;
+ b = wintab->base;
+ e = b + s - 1;
+ if (s > (0xFFFFFFFF - b + 1)) {
+ /*
+ * XXX this boundary check should account for 64bit
+ * and remapping..
+ */
+ printf("XOR window#%d: no space for size 0x%08x at "
+ "0x%08x\n", i, s, b);
+ rv = 0;
+ continue;
+ }
+
+ j = decode_win_overlap(i, xor_wins_no, &xor_wins[0]);
+ if (j >= 0) {
+ printf("XOR window#%d: (0x%08x - 0x%08x) overlaps "
+ "with #%d (0x%08x - 0x%08x)\n", i, b, e, j,
+ xor_wins[j].base,
+ xor_wins[j].base + xor_wins[j].size - 1);
+ rv = 0;
+ }
+ }
+
+ return (rv);
+}
+
+void
+decode_win_xor_dump(u_long base)
+{
+ int i, j;
+ int e = 1;
+
+ if (pm_is_disabled(CPU_PM_CTRL_XOR))
+ return;
+
+ for (j = 0; j < xor_max_eng(); j++, e--) {
+ for (i = 0; i < MV_WIN_XOR_MAX; i++) {
+ printf("XOR window#%d: b 0x%08x, s 0x%08x", i,
+ win_xor_br_read(base, i, e), win_xor_sz_read(base, i, e));
+
+ if (win_xor_can_remap(i))
+ printf(", ha 0x%08x", win_xor_har_read(base, i, e));
+
+ printf("\n");
+ }
+ for (i = 0; i < MV_XOR_CHAN_MAX; i++)
+ printf("XOR control#%d: 0x%08x\n", i,
+ win_xor_ctrl_read(base, i, e));
+ }
+}
+
+#else
+/* Provide dummy functions to satisfy the build for SoCs not equipped with XOR */
+static int
+decode_win_xor_valid(void)
+{
+
+ return (1);
+}
+
+static void
+decode_win_xor_setup(u_long base)
+{
+}
+
+static void
+decode_win_xor_dump(u_long base)
+{
+}
+#endif
+
+/**************************************************************************
+ * SATA windows routines
+ **************************************************************************/
+static void
+decode_win_sata_setup(u_long base)
+{
+ uint32_t cr, br;
+ int i, j;
+
+ if (pm_is_disabled(CPU_PM_CTRL_SATA))
+ return;
+
+ for (i = 0; i < MV_WIN_SATA_MAX; i++) {
+ win_sata_cr_write(base, i, 0);
+ win_sata_br_write(base, i, 0);
+ }
+
+ for (i = 0; i < MV_WIN_DDR_MAX; i++)
+ if (ddr_is_active(i)) {
+ cr = ((ddr_size(i) - 1) & 0xffff0000) |
+ (ddr_attr(i) << 8) | (ddr_target(i) << 4) | 1;
+ br = ddr_base(i);
+
+ /* Use the first available SATA window */
+ for (j = 0; j < MV_WIN_SATA_MAX; j++) {
+ if ((win_sata_cr_read(base, j) & 1) != 0)
+ continue;
+
+ win_sata_br_write(base, j, br);
+ win_sata_cr_write(base, j, cr);
+ break;
+ }
+ }
+}
+
+/*
+ * Configure AHCI decoding windows
+ */
+static void
+decode_win_ahci_setup(u_long base)
+{
+ uint32_t br, cr, sz;
+ int i, j;
+
+ for (i = 0; i < MV_WIN_SATA_MAX_ARMADA38X; i++) {
+ win_sata_armada38x_cr_write(base, i, 0);
+ win_sata_armada38x_br_write(base, i, 0);
+ win_sata_armada38x_sz_write(base, i, 0);
+ }
+
+ for (i = 0; i < MV_WIN_DDR_MAX; i++) {
+ if (ddr_is_active(i)) {
+ cr = (ddr_attr(i) << IO_WIN_ATTR_SHIFT) |
+ (ddr_target(i) << IO_WIN_TGT_SHIFT) |
+ IO_WIN_ENA_MASK;
+ br = ddr_base(i);
+ sz = (ddr_size(i) - 1) &
+ (IO_WIN_SIZE_MASK << IO_WIN_SIZE_SHIFT);
+
+ /* Use first available SATA window */
+ for (j = 0; j < MV_WIN_SATA_MAX_ARMADA38X; j++) {
+ if (win_sata_armada38x_cr_read(base, j) & IO_WIN_ENA_MASK)
+ continue;
+
+ /* BASE is set to DRAM base (0x00000000) */
+ win_sata_armada38x_br_write(base, j, br);
+ /* CTRL targets DRAM ctrl with 0x0E or 0x0D */
+ win_sata_armada38x_cr_write(base, j, cr);
+ /* SIZE is set to 16MB - max value */
+ win_sata_armada38x_sz_write(base, j, sz);
+ break;
+ }
+ }
+ }
+}
+
+static void
+decode_win_ahci_dump(u_long base)
+{
+ int i;
+
+ for (i = 0; i < MV_WIN_SATA_MAX_ARMADA38X; i++)
+ printf("SATA window#%d: cr 0x%08x, br 0x%08x, sz 0x%08x\n", i,
+ win_sata_armada38x_cr_read(base, i), win_sata_br_read(base, i),
+ win_sata_armada38x_sz_read(base,i));
+}
+
+static int
+decode_win_sata_valid(void)
+{
+ uint32_t dev, rev;
+
+ soc_id(&dev, &rev);
+ if (dev == MV_DEV_88F5281)
+ return (1);
+
+ return (decode_win_can_cover_ddr(MV_WIN_SATA_MAX));
+}
+
+static void
+decode_win_sdhci_setup(u_long base)
+{
+ uint32_t cr, br;
+ int i, j;
+
+ for (i = 0; i < MV_WIN_SDHCI_MAX; i++) {
+ win_sdhci_cr_write(base, i, 0);
+ win_sdhci_br_write(base, i, 0);
+ }
+
+ for (i = 0; i < MV_WIN_DDR_MAX; i++)
+ if (ddr_is_active(i)) {
+ br = ddr_base(i);
+ cr = (((ddr_size(i) - 1) &
+ (IO_WIN_SIZE_MASK << IO_WIN_SIZE_SHIFT)) |
+ (ddr_attr(i) << IO_WIN_ATTR_SHIFT) |
+ (ddr_target(i) << IO_WIN_TGT_SHIFT) |
+ IO_WIN_ENA_MASK);
+
+ /* Use the first available SDHCI window */
+ for (j = 0; j < MV_WIN_SDHCI_MAX; j++) {
+ if (win_sdhci_cr_read(base, j) & IO_WIN_ENA_MASK)
+ continue;
+
+ win_sdhci_cr_write(base, j, cr);
+ win_sdhci_br_write(base, j, br);
+ break;
+ }
+ }
+}
+
+static void
+decode_win_sdhci_dump(u_long base)
+{
+ int i;
+
+ for (i = 0; i < MV_WIN_SDHCI_MAX; i++)
+ printf("SDHCI window#%d: c 0x%08x, b 0x%08x\n", i,
+ win_sdhci_cr_read(base, i), win_sdhci_br_read(base, i));
+}
+
+static int
+decode_win_sdhci_valid(void)
+{
+
+ return (decode_win_can_cover_ddr(MV_WIN_SDHCI_MAX));
+}
+
+/**************************************************************************
+ * FDT parsing routines.
+ **************************************************************************/
+
+static int
+fdt_get_ranges(const char *nodename, void *buf, int size, int *tuples,
+ int *tuplesize)
+{
+ phandle_t node;
+ pcell_t addr_cells, par_addr_cells, size_cells;
+ int len, tuple_size, tuples_count;
+
+ node = OF_finddevice(nodename);
+ if (node == -1)
+ return (EINVAL);
+
+ if ((fdt_addrsize_cells(node, &addr_cells, &size_cells)) != 0)
+ return (ENXIO);
+
+ par_addr_cells = fdt_parent_addr_cells(node);
+ if (par_addr_cells > 2)
+ return (ERANGE);
+
+ tuple_size = sizeof(pcell_t) * (addr_cells + par_addr_cells +
+ size_cells);
+
+ /* Note the OF_getprop_alloc() cannot be used at this early stage. */
+ len = OF_getprop(node, "ranges", buf, size);
+
+ /*
+ * XXX this does not handle the empty 'ranges;' case, which is
+ * legitimate and should be allowed.
+ */
+ tuples_count = len / tuple_size;
+ if (tuples_count <= 0)
+ return (ERANGE);
+
+ if (par_addr_cells > 2 || addr_cells > 2 || size_cells > 2)
+ return (ERANGE);
+
+ *tuples = tuples_count;
+ *tuplesize = tuple_size;
+ return (0);
+}
+
+static int
+win_cpu_from_dt(void)
+{
+ pcell_t ranges[48];
+ phandle_t node;
+ int i, entry_size, err, t, tuple_size, tuples;
+ u_long sram_base, sram_size;
+
+ t = 0;
+ /* Retrieve 'ranges' property of '/localbus' node. */
+ if ((err = fdt_get_ranges("/localbus", ranges, sizeof(ranges),
+ &tuples, &tuple_size)) == 0) {
+ /*
+ * Fill CPU decode windows table.
+ */
+ bzero((void *)&cpu_win_tbl, sizeof(cpu_win_tbl));
+
+ entry_size = tuple_size / sizeof(pcell_t);
+ cpu_wins_no = tuples;
+
+ /* Check range */
+ if (tuples > nitems(cpu_win_tbl)) {
+ debugf("too many tuples to fit into cpu_win_tbl\n");
+ return (ENOMEM);
+ }
+
+ for (i = 0, t = 0; t < tuples; i += entry_size, t++) {
+ cpu_win_tbl[t].target = 1;
+ cpu_win_tbl[t].attr = fdt32_to_cpu(ranges[i + 1]);
+ cpu_win_tbl[t].base = fdt32_to_cpu(ranges[i + 2]);
+ cpu_win_tbl[t].size = fdt32_to_cpu(ranges[i + 3]);
+ cpu_win_tbl[t].remap = ~0;
+ debugf("target = 0x%0x attr = 0x%0x base = 0x%0x "
+ "size = 0x%0x remap = 0x%0x\n",
+ cpu_win_tbl[t].target,
+ cpu_win_tbl[t].attr, cpu_win_tbl[t].base,
+ cpu_win_tbl[t].size, cpu_win_tbl[t].remap);
+ }
+ }
+
+ /*
+ * Retrieve CESA SRAM data.
+ */
+ if ((node = OF_finddevice("sram")) != -1)
+ if (ofw_bus_node_is_compatible(node, "mrvl,cesa-sram"))
+ goto moveon;
+
+ if ((node = OF_finddevice("/")) == -1)
+ return (ENXIO);
+
+ if ((node = fdt_find_compatible(node, "mrvl,cesa-sram", 0)) == 0)
+ /* SRAM block is not always present. */
+ return (0);
+moveon:
+ sram_base = sram_size = 0;
+ if (fdt_regsize(node, &sram_base, &sram_size) != 0)
+ return (EINVAL);
+
+ /* Check range */
+ if (t >= nitems(cpu_win_tbl)) {
+ debugf("cannot fit CESA tuple into cpu_win_tbl\n");
+ return (ENOMEM);
+ }
+
+ cpu_win_tbl[t].target = soc_decode_win_spec->win_cesa_target;
+ if (soc_family == MV_SOC_ARMADA_38X)
+ cpu_win_tbl[t].attr = soc_decode_win_spec->win_cesa_attr(0);
+ else
+ cpu_win_tbl[t].attr = soc_decode_win_spec->win_cesa_attr(1);
+ cpu_win_tbl[t].base = sram_base;
+ cpu_win_tbl[t].size = sram_size;
+ cpu_win_tbl[t].remap = ~0;
+ cpu_wins_no++;
+ debugf("sram: base = 0x%0lx size = 0x%0lx\n", sram_base, sram_size);
+
+ /* Check if there is a second CESA node */
+ while ((node = OF_peer(node)) != 0) {
+ if (ofw_bus_node_is_compatible(node, "mrvl,cesa-sram")) {
+ if (fdt_regsize(node, &sram_base, &sram_size) != 0)
+ return (EINVAL);
+ break;
+ }
+ }
+
+ if (node == 0)
+ return (0);
+
+ t++;
+ if (t >= nitems(cpu_win_tbl)) {
+ debugf("cannot fit CESA tuple into cpu_win_tbl\n");
+ return (ENOMEM);
+ }
+
+ /* Configure window for CESA1 */
+ cpu_win_tbl[t].target = soc_decode_win_spec->win_cesa_target;
+ cpu_win_tbl[t].attr = soc_decode_win_spec->win_cesa_attr(1);
+ cpu_win_tbl[t].base = sram_base;
+ cpu_win_tbl[t].size = sram_size;
+ cpu_win_tbl[t].remap = ~0;
+ cpu_wins_no++;
+ debugf("sram: base = 0x%0lx size = 0x%0lx\n", sram_base, sram_size);
+
+ return (0);
+}
+
+static int
+fdt_win_process(phandle_t child)
+{
+ int i, ret;
+
+ for (i = 0; soc_nodes[i].compat != NULL; i++) {
+ /* Setup only for enabled devices */
+ if (ofw_bus_node_status_okay(child) == 0)
+ continue;
+
+ if (!ofw_bus_node_is_compatible(child, soc_nodes[i].compat))
+ continue;
+
+ ret = fdt_win_process_child(child, &soc_nodes[i], "reg");
+ if (ret != 0)
+ return (ret);
+ }
+
+ return (0);
+}
+
+static int
+fdt_win_process_child(phandle_t child, struct soc_node_spec *soc_node,
+ const char* mimo_reg_source)
+{
+ int addr_cells, size_cells;
+ pcell_t reg[8];
+ u_long size, base;
+
+ if (fdt_addrsize_cells(OF_parent(child), &addr_cells,
+ &size_cells))
+ return (ENXIO);
+
+ if ((sizeof(pcell_t) * (addr_cells + size_cells)) > sizeof(reg))
+ return (ENOMEM);
+ if (OF_getprop(child, mimo_reg_source, &reg, sizeof(reg)) <= 0)
+ return (EINVAL);
+
+ if (addr_cells <= 2)
+ base = fdt_data_get(&reg[0], addr_cells);
+ else
+ base = fdt_data_get(&reg[addr_cells - 2], 2);
+ size = fdt_data_get(&reg[addr_cells], size_cells);
+
+ if (soc_node->valid_handler != NULL)
+ if (!soc_node->valid_handler())
+ return (EINVAL);
+
+ base = (base & 0x000fffff) | fdt_immr_va;
+ if (soc_node->decode_handler != NULL)
+ soc_node->decode_handler(base);
+ else
+ return (ENXIO);
+
+ if (MV_DUMP_WIN && (soc_node->dump_handler != NULL))
+ soc_node->dump_handler(base);
+
+ return (0);
+}
+
+static int
+fdt_win_setup(void)
+{
+ phandle_t node, child, sb;
+ phandle_t child_pci;
+ int err;
+
+ sb = 0;
+ node = OF_finddevice("/");
+ if (node == -1)
+ panic("fdt_win_setup: no root node");
+
+ /* Allow for coherent transactions on the A38x MBUS */
+ if (ofw_bus_node_is_compatible(node, "marvell,armada380"))
+ platform_io_coherent = true;
+
+ /*
+ * Traverse through all children of root and simple-bus nodes.
+ * For each found device retrieve decode windows data (if applicable).
+ */
+ child = OF_child(node);
+ while (child != 0) {
+ /* Lookup for callback and run */
+ err = fdt_win_process(child);
+ if (err != 0)
+ return (err);
+
+ /* Process Marvell Armada-XP/38x PCIe controllers */
+ if (ofw_bus_node_is_compatible(child, "marvell,armada-370-pcie")) {
+ child_pci = OF_child(child);
+ while (child_pci != 0) {
+ err = fdt_win_process_child(child_pci,
+ &soc_nodes[SOC_NODE_PCIE_ENTRY_IDX],
+ "assigned-addresses");
+ if (err != 0)
+ return (err);
+
+ child_pci = OF_peer(child_pci);
+ }
+ }
+
+ /*
+ * Once done with root-level children let's move down to
+ * simple-bus and its children.
+ */
+ child = OF_peer(child);
+ if ((child == 0) && (node == OF_finddevice("/"))) {
+ sb = node = fdt_find_compatible(node, "simple-bus", 0);
+ if (node == 0)
+ return (ENXIO);
+ child = OF_child(node);
+ }
+ /*
+ * Next, move one more level down to internal-regs node (if
+ * it is present) and its children. This node also have
+ * "simple-bus" compatible.
+ */
+ if ((child == 0) && (node == sb)) {
+ node = fdt_find_compatible(node, "simple-bus", 0);
+ if (node == 0)
+ return (0);
+ child = OF_child(node);
+ }
+ }
+
+ return (0);
+}
+
+static void
+fdt_fixup_busfreq(phandle_t root)
+{
+ phandle_t sb;
+ pcell_t freq;
+
+ freq = cpu_to_fdt32(get_tclk());
+
+ /*
+ * Fix bus speed in cpu node
+ */
+ if ((sb = OF_finddevice("cpu")) != -1)
+ if (fdt_is_compatible_strict(sb, "ARM,88VS584"))
+ OF_setprop(sb, "bus-frequency", (void *)&freq,
+ sizeof(freq));
+
+ /*
+ * This fixup sets the simple-bus bus-frequency property.
+ */
+ if ((sb = fdt_find_compatible(root, "simple-bus", 1)) != 0)
+ OF_setprop(sb, "bus-frequency", (void *)&freq, sizeof(freq));
+}
+
+static void
+fdt_fixup_ranges(phandle_t root)
+{
+ phandle_t node;
+ pcell_t par_addr_cells, addr_cells, size_cells;
+ pcell_t ranges[3], reg[2], *rangesptr;
+ int len, tuple_size, tuples_count;
+ uint32_t base;
+
+ /* Fix-up SoC ranges according to real fdt_immr_pa */
+ if ((node = fdt_find_compatible(root, "simple-bus", 1)) != 0) {
+ if (fdt_addrsize_cells(node, &addr_cells, &size_cells) == 0 &&
+ ((par_addr_cells = fdt_parent_addr_cells(node)) <= 2)) {
+ tuple_size = sizeof(pcell_t) * (par_addr_cells +
+ addr_cells + size_cells);
+ len = OF_getprop(node, "ranges", ranges,
+ sizeof(ranges));
+ tuples_count = len / tuple_size;
+ /* Unexpected settings are not supported */
+ if (tuples_count != 1)
+ goto fixup_failed;
+
+ rangesptr = &ranges[0];
+ rangesptr += par_addr_cells;
+ base = fdt_data_get((void *)rangesptr, addr_cells);
+ *rangesptr = cpu_to_fdt32(fdt_immr_pa);
+ if (OF_setprop(node, "ranges", (void *)&ranges[0],
+ sizeof(ranges)) < 0)
+ goto fixup_failed;
+ }
+ }
+
+ /* Fix-up PCIe reg according to real PCIe registers' PA */
+ if ((node = fdt_find_compatible(root, "mrvl,pcie", 1)) != 0) {
+ if (fdt_addrsize_cells(OF_parent(node), &par_addr_cells,
+ &size_cells) == 0) {
+ tuple_size = sizeof(pcell_t) * (par_addr_cells +
+ size_cells);
+ len = OF_getprop(node, "reg", reg, sizeof(reg));
+ tuples_count = len / tuple_size;
+ /* Unexpected settings are not supported */
+ if (tuples_count != 1)
+ goto fixup_failed;
+
+ base = fdt_data_get((void *)&reg[0], par_addr_cells);
+ base &= ~0xFF000000;
+ base |= fdt_immr_pa;
+ reg[0] = cpu_to_fdt32(base);
+ if (OF_setprop(node, "reg", (void *)&reg[0],
+ sizeof(reg)) < 0)
+ goto fixup_failed;
+ }
+ }
+ /* Fix-up succeeded. May return and continue */
+ return;
+
+fixup_failed:
+ while (1) {
+ /*
+ * In case of any error while fixing ranges just hang.
+ * 1. No message can be displayed yet since console
+ * is not initialized.
+ * 2. Going further will cause failure on bus_space_map()
+ * relying on the wrong ranges or data abort when
+ * accessing PCIe registers.
+ */
+ }
+}
+
+struct fdt_fixup_entry fdt_fixup_table[] = {
+ { "mrvl,DB-88F6281", &fdt_fixup_busfreq },
+ { "mrvl,DB-78460", &fdt_fixup_busfreq },
+ { "mrvl,DB-78460", &fdt_fixup_ranges },
+ { NULL, NULL }
+};
+
+uint32_t
+get_tclk(void)
+{
+
+ if (soc_decode_win_spec->get_tclk != NULL)
+ return soc_decode_win_spec->get_tclk();
+ else
+ return -1;
+}
+
+uint32_t
+get_cpu_freq(void)
+{
+
+ if (soc_decode_win_spec->get_cpu_freq != NULL)
+ return soc_decode_win_spec->get_cpu_freq();
+ else
+ return -1;
+}
diff --git a/sys/arm/mv/mv_cp110_clock.c b/sys/arm/mv/mv_cp110_clock.c
new file mode 100644
index 000000000000..84519b870d9a
--- /dev/null
+++ b/sys/arm/mv/mv_cp110_clock.c
@@ -0,0 +1,370 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2018 Rubicon Communications, LLC (Netgate)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/intr.h>
+
+#include <dev/extres/clk/clk_fixed.h>
+#include <dev/extres/clk/clk_gate.h>
+#include <dev/extres/syscon/syscon.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/mv/mv_cp110_clock.h>
+
+#include "clkdev_if.h"
+#include "syscon_if.h"
+
+/* Clocks */
+static struct clk_fixed_def cp110_clk_pll_0 = {
+ .clkdef.id = CP110_PLL_0,
+ .freq = 1000000000,
+};
+
+static const char *clk_parents_0[] = {"cp110-pll0-0"};
+static const char *clk_parents_1[] = {"cp110-pll0-1"};
+
+static struct clk_fixed_def cp110_clk_ppv2_core = {
+ .clkdef.id = CP110_PPV2_CORE,
+ .clkdef.parent_cnt = 1,
+ .mult = 1,
+ .div = 3,
+};
+
+static struct clk_fixed_def cp110_clk_x2core = {
+ .clkdef.id = CP110_X2CORE,
+ .clkdef.parent_cnt = 1,
+ .mult = 1,
+ .div = 2,
+};
+
+static const char *core_parents_0[] = {"cp110-x2core-0"};
+static const char *core_parents_1[] = {"cp110-x2core-1"};
+
+static struct clk_fixed_def cp110_clk_core = {
+ .clkdef.id = CP110_CORE,
+ .clkdef.parent_cnt = 1,
+ .mult = 1,
+ .div = 2,
+};
+
+static struct clk_fixed_def cp110_clk_sdio = {
+ .clkdef.id = CP110_SDIO,
+ .clkdef.parent_cnt = 1,
+ .mult = 2,
+ .div = 5,
+};
+
+/* Gates */
+
+static struct cp110_gate cp110_gates[] = {
+ CCU_GATE(CP110_GATE_AUDIO, "cp110-gate-audio", 0)
+ CCU_GATE(CP110_GATE_COMM_UNIT, "cp110-gate-comm_unit", 1)
+ /* CCU_GATE(CP110_GATE_NAND, "cp110-gate-nand", 2) */
+ CCU_GATE(CP110_GATE_PPV2, "cp110-gate-ppv2", 3)
+ CCU_GATE(CP110_GATE_SDIO, "cp110-gate-sdio", 4)
+ CCU_GATE(CP110_GATE_MG, "cp110-gate-mg", 5)
+ CCU_GATE(CP110_GATE_MG_CORE, "cp110-gate-mg_core", 6)
+ CCU_GATE(CP110_GATE_XOR1, "cp110-gate-xor1", 7)
+ CCU_GATE(CP110_GATE_XOR0, "cp110-gate-xor0", 8)
+ CCU_GATE(CP110_GATE_GOP_DP, "cp110-gate-gop_dp", 9)
+ CCU_GATE(CP110_GATE_PCIE_X1_0, "cp110-gate-pcie_x10", 11)
+ CCU_GATE(CP110_GATE_PCIE_X1_1, "cp110-gate-pcie_x11", 12)
+ CCU_GATE(CP110_GATE_PCIE_X4, "cp110-gate-pcie_x4", 13)
+ CCU_GATE(CP110_GATE_PCIE_XOR, "cp110-gate-pcie_xor", 14)
+ CCU_GATE(CP110_GATE_SATA, "cp110-gate-sata", 15)
+ CCU_GATE(CP110_GATE_SATA_USB, "cp110-gate-sata_usb", 16)
+ CCU_GATE(CP110_GATE_MAIN, "cp110-gate-main", 17)
+ CCU_GATE(CP110_GATE_SDMMC_GOP, "cp110-gate-sdmmc_gop", 18)
+ CCU_GATE(CP110_GATE_SLOW_IO, "cp110-gate-slow_io", 21)
+ CCU_GATE(CP110_GATE_USB3H0, "cp110-gate-usb3h0", 22)
+ CCU_GATE(CP110_GATE_USB3H1, "cp110-gate-usb3h1", 23)
+ CCU_GATE(CP110_GATE_USB3DEV, "cp110-gate-usb3dev", 24)
+ CCU_GATE(CP110_GATE_EIP150, "cp110-gate-eip150", 25)
+ CCU_GATE(CP110_GATE_EIP197, "cp110-gate-eip197", 26)
+};
+
+struct mv_cp110_clock_softc {
+ device_t dev;
+ struct syscon *syscon;
+ struct mtx mtx;
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"marvell,cp110-clock", 1},
+ {NULL, 0}
+};
+
+#define RD4(sc, reg) SYSCON_READ_4((sc)->syscon, (reg))
+#define WR4(sc, reg, val) SYSCON_WRITE_4((sc)->syscon, (reg), (val))
+
+static char *
+mv_cp110_clock_name(device_t dev, const char *name)
+{
+ char *clkname = NULL;
+ int unit;
+
+ unit = device_get_unit(dev);
+ if (asprintf(&clkname, M_DEVBUF, "%s-%d", name, unit) <= 0)
+ panic("Cannot generate unique clock name for %s\n", name);
+ return (clkname);
+}
+
+static int
+mv_cp110_clock_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Marvell CP110 Clock Controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+cp110_ofw_map(struct clkdom *clkdom, uint32_t ncells,
+ phandle_t *cells, struct clknode **clk)
+{
+ int id = 0;
+
+ if (ncells != 2)
+ return (ENXIO);
+
+ id = cells[1];
+ if (cells[0] == 1)
+ id += CP110_MAX_CLOCK;
+
+ *clk = clknode_find_by_id(clkdom, id);
+
+ return (0);
+}
+
+static int
+mv_cp110_clock_attach(device_t dev)
+{
+ struct mv_cp110_clock_softc *sc;
+ struct clkdom *clkdom;
+ struct clk_gate_def def;
+ char *pll0_name;
+ int unit, i;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ if (SYSCON_GET_HANDLE(sc->dev, &sc->syscon) != 0 ||
+ sc->syscon == NULL) {
+ device_printf(dev, "cannot get syscon for device\n");
+ return (ENXIO);
+ }
+
+ unit = device_get_unit(dev);
+ if (unit > 1) {
+ device_printf(dev, "Bogus cp110-system-controller unit %d\n", unit);
+ return (ENXIO);
+ }
+
+ mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ clkdom = clkdom_create(dev);
+ clkdom_set_ofw_mapper(clkdom, cp110_ofw_map);
+
+ pll0_name = mv_cp110_clock_name(dev, "cp110-pll0");
+ cp110_clk_pll_0.clkdef.name = pll0_name;
+ clknode_fixed_register(clkdom, &cp110_clk_pll_0);
+
+ cp110_clk_ppv2_core.clkdef.name = mv_cp110_clock_name(dev, "cp110-ppv2");
+ cp110_clk_ppv2_core.clkdef.parent_names = (unit == 0) ? clk_parents_0 : clk_parents_1;
+ clknode_fixed_register(clkdom, &cp110_clk_ppv2_core);
+
+ cp110_clk_x2core.clkdef.name = mv_cp110_clock_name(dev, "cp110-x2core");
+ cp110_clk_x2core.clkdef.parent_names = (unit == 0) ? clk_parents_0 : clk_parents_1;
+ clknode_fixed_register(clkdom, &cp110_clk_x2core);
+
+ cp110_clk_core.clkdef.name = mv_cp110_clock_name(dev, "cp110-core");
+ cp110_clk_core.clkdef.parent_names = (unit == 0) ? core_parents_0 : core_parents_1;
+ clknode_fixed_register(clkdom, &cp110_clk_core);
+
+ /* NAND missing */
+
+ cp110_clk_sdio.clkdef.name = mv_cp110_clock_name(dev, "cp110-sdio");
+ cp110_clk_sdio.clkdef.parent_names = (unit == 0) ? clk_parents_0 : clk_parents_1;
+ clknode_fixed_register(clkdom, &cp110_clk_sdio);
+
+ for (i = 0; i < nitems(cp110_gates); i++) {
+ if (cp110_gates[i].name == NULL)
+ continue;
+
+ memset(&def, 0, sizeof(def));
+ def.clkdef.id = CP110_MAX_CLOCK + i;
+ def.clkdef.name = mv_cp110_clock_name(dev, cp110_gates[i].name);
+ def.clkdef.parent_cnt = 1;
+ def.offset = CP110_CLOCK_GATING_OFFSET;
+ def.shift = cp110_gates[i].shift;
+ def.mask = 1;
+ def.on_value = 1;
+ def.off_value = 0;
+
+ switch (i) {
+ case CP110_GATE_MG:
+ case CP110_GATE_GOP_DP:
+ case CP110_GATE_PPV2:
+ def.clkdef.parent_names = &cp110_clk_ppv2_core.clkdef.name;
+ break;
+ case CP110_GATE_SDIO:
+ def.clkdef.parent_names = &cp110_clk_sdio.clkdef.name;
+ break;
+ case CP110_GATE_MAIN:
+ case CP110_GATE_PCIE_XOR:
+ case CP110_GATE_PCIE_X4:
+ case CP110_GATE_EIP150:
+ case CP110_GATE_EIP197:
+ def.clkdef.parent_names = &cp110_clk_x2core.clkdef.name;
+ break;
+ default:
+ def.clkdef.parent_names = &cp110_clk_core.clkdef.name;
+ break;
+ }
+
+ clknode_gate_register(clkdom, &def);
+ }
+
+ clkdom_finit(clkdom);
+
+ if (bootverbose)
+ clkdom_dump(clkdom);
+
+ return (0);
+}
+
+static int
+mv_cp110_clock_detach(device_t dev)
+{
+
+ return (EBUSY);
+}
+
+static int
+mv_cp110_clock_write_4(device_t dev, bus_addr_t addr, uint32_t val)
+{
+ struct mv_cp110_clock_softc *sc;
+
+ sc = device_get_softc(dev);
+ WR4(sc, addr, val);
+ return (0);
+}
+
+static int
+mv_cp110_clock_read_4(device_t dev, bus_addr_t addr, uint32_t *val)
+{
+ struct mv_cp110_clock_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ *val = RD4(sc, addr);
+ return (0);
+}
+
+static int
+mv_cp110_clock_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set)
+{
+ struct mv_cp110_clock_softc *sc;
+ uint32_t reg;
+
+ sc = device_get_softc(dev);
+
+ reg = RD4(sc, addr);
+ reg &= ~clr;
+ reg |= set;
+ WR4(sc, addr, reg);
+
+ return (0);
+}
+
+static void
+mv_cp110_clock_device_lock(device_t dev)
+{
+ struct mv_cp110_clock_softc *sc;
+
+ sc = device_get_softc(dev);
+ mtx_lock(&sc->mtx);
+}
+
+static void
+mv_cp110_clock_device_unlock(device_t dev)
+{
+ struct mv_cp110_clock_softc *sc;
+
+ sc = device_get_softc(dev);
+ mtx_unlock(&sc->mtx);
+}
+
+static device_method_t mv_cp110_clock_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, mv_cp110_clock_probe),
+ DEVMETHOD(device_attach, mv_cp110_clock_attach),
+ DEVMETHOD(device_detach, mv_cp110_clock_detach),
+
+ /* clkdev interface */
+ DEVMETHOD(clkdev_write_4, mv_cp110_clock_write_4),
+ DEVMETHOD(clkdev_read_4, mv_cp110_clock_read_4),
+ DEVMETHOD(clkdev_modify_4, mv_cp110_clock_modify_4),
+ DEVMETHOD(clkdev_device_lock, mv_cp110_clock_device_lock),
+ DEVMETHOD(clkdev_device_unlock, mv_cp110_clock_device_unlock),
+
+ DEVMETHOD_END
+};
+
+static devclass_t mv_cp110_clock_devclass;
+
+static driver_t mv_cp110_clock_driver = {
+ "mv_cp110_clock",
+ mv_cp110_clock_methods,
+ sizeof(struct mv_cp110_clock_softc),
+};
+
+EARLY_DRIVER_MODULE(mv_cp110_clock, simplebus, mv_cp110_clock_driver,
+ mv_cp110_clock_devclass, 0, 0, BUS_PASS_RESOURCE + BUS_PASS_ORDER_LATE);
diff --git a/sys/arm/mv/mv_cp110_clock.h b/sys/arm/mv/mv_cp110_clock.h
new file mode 100644
index 000000000000..7c67254817e4
--- /dev/null
+++ b/sys/arm/mv/mv_cp110_clock.h
@@ -0,0 +1,82 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2018 Rubicon Communications, LLC (Netgate)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MV_CP110_SYSCON_H_
+#define _MV_CP110_SYSCON_H_
+
+enum mv_cp110_clk_id {
+ CP110_PLL_0 = 0,
+ CP110_PPV2_CORE,
+ CP110_X2CORE,
+ CP110_CORE,
+ CP110_NAND,
+ CP110_SDIO,
+ CP110_MAX_CLOCK
+};
+
+/* Gates */
+#define CP110_CLOCK_GATING_OFFSET 0x220
+
+struct cp110_gate {
+ const char *name;
+ uint32_t shift;
+};
+
+#define CCU_GATE(idx, clkname, s) \
+ [idx] = { \
+ .name = clkname, \
+ .shift = s, \
+ },
+
+#define CP110_GATE_AUDIO 0
+#define CP110_GATE_COMM_UNIT 1
+#define CP110_GATE_NAND 2
+#define CP110_GATE_PPV2 3
+#define CP110_GATE_SDIO 4
+#define CP110_GATE_MG 5
+#define CP110_GATE_MG_CORE 6
+#define CP110_GATE_XOR1 7
+#define CP110_GATE_XOR0 8
+#define CP110_GATE_GOP_DP 9
+#define CP110_GATE_PCIE_X1_0 11
+#define CP110_GATE_PCIE_X1_1 12
+#define CP110_GATE_PCIE_X4 13
+#define CP110_GATE_PCIE_XOR 14
+#define CP110_GATE_SATA 15
+#define CP110_GATE_SATA_USB 16
+#define CP110_GATE_MAIN 17
+#define CP110_GATE_SDMMC_GOP 18
+#define CP110_GATE_SLOW_IO 21
+#define CP110_GATE_USB3H0 22
+#define CP110_GATE_USB3H1 23
+#define CP110_GATE_USB3DEV 24
+#define CP110_GATE_EIP150 25
+#define CP110_GATE_EIP197 26
+
+#endif
diff --git a/sys/arm/mv/mv_cp110_icu.c b/sys/arm/mv/mv_cp110_icu.c
new file mode 100644
index 000000000000..12dd6989e339
--- /dev/null
+++ b/sys/arm/mv/mv_cp110_icu.c
@@ -0,0 +1,354 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2018 Rubicon Communications, LLC (Netgate)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/intr.h>
+
+#include <dev/fdt/simplebus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dt-bindings/interrupt-controller/irq.h>
+#include "pic_if.h"
+
+#define ICU_GRP_NSR 0x0
+#define ICU_GRP_SR 0x1
+#define ICU_GRP_SEI 0x4
+#define ICU_GRP_REI 0x5
+
+#define ICU_SETSPI_NSR_AL 0x10
+#define ICU_SETSPI_NSR_AH 0x14
+#define ICU_CLRSPI_NSR_AL 0x18
+#define ICU_CLRSPI_NSR_AH 0x1c
+#define ICU_INT_CFG(x) (0x100 + (x) * 4)
+#define ICU_INT_ENABLE (1 << 24)
+#define ICU_INT_EDGE (1 << 28)
+#define ICU_INT_GROUP_SHIFT 29
+#define ICU_INT_MASK 0x3ff
+
+#define MV_CP110_ICU_MAX_NIRQS 207
+
+struct mv_cp110_icu_softc {
+ device_t dev;
+ device_t parent;
+ struct resource *res;
+ struct intr_map_data_fdt *parent_map_data;
+};
+
+static struct resource_spec mv_cp110_icu_res_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE | RF_SHAREABLE },
+ { -1, 0 }
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"marvell,cp110-icu-nsr", 1},
+ {"marvell,cp110-icu-sei", 2},
+ {NULL, 0}
+};
+
+#define RD4(sc, reg) bus_read_4((sc)->res, (reg))
+#define WR4(sc, reg, val) bus_write_4((sc)->res, (reg), (val))
+
+static int
+mv_cp110_icu_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Marvell Interrupt Consolidation Unit");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+mv_cp110_icu_attach(device_t dev)
+{
+ struct mv_cp110_icu_softc *sc;
+ phandle_t node, msi_parent;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ node = ofw_bus_get_node(dev);
+
+ if (OF_getencprop(node, "msi-parent", &msi_parent,
+ sizeof(phandle_t)) <= 0) {
+ device_printf(dev, "cannot find msi-parent property\n");
+ return (ENXIO);
+ }
+
+ if ((sc->parent = OF_device_from_xref(msi_parent)) == NULL) {
+ device_printf(dev, "cannot find msi-parent device\n");
+ return (ENXIO);
+ }
+ if (bus_alloc_resources(dev, mv_cp110_icu_res_spec, &sc->res) != 0) {
+ device_printf(dev, "cannot allocate resources for device\n");
+ return (ENXIO);
+ }
+
+ if (intr_pic_register(dev, OF_xref_from_node(node)) == NULL) {
+ device_printf(dev, "Cannot register ICU\n");
+ goto fail;
+ }
+
+ /* Allocate GICP compatible mapping entry (2 cells) */
+ sc->parent_map_data = (struct intr_map_data_fdt *)intr_alloc_map_data(
+ INTR_MAP_DATA_FDT, sizeof(struct intr_map_data_fdt) +
+ + 3 * sizeof(phandle_t), M_WAITOK | M_ZERO);
+ return (0);
+
+fail:
+ bus_release_resources(dev, mv_cp110_icu_res_spec, &sc->res);
+ return (ENXIO);
+}
+
+static struct intr_map_data *
+mv_cp110_icu_convert_map_data(struct mv_cp110_icu_softc *sc, struct intr_map_data *data)
+{
+ struct intr_map_data_fdt *daf;
+ uint32_t reg, irq_no, irq_type;
+
+ daf = (struct intr_map_data_fdt *)data;
+ if (daf->ncells != 2)
+ return (NULL);
+ irq_no = daf->cells[0];
+ irq_type = daf->cells[1];
+ if (irq_no >= MV_CP110_ICU_MAX_NIRQS)
+ return (NULL);
+ if (irq_type != IRQ_TYPE_LEVEL_HIGH &&
+ irq_type != IRQ_TYPE_EDGE_RISING)
+ return (NULL);
+
+ /* We rely on fact that ICU->GIC mapping is preset by bootstrap. */
+ reg = RD4(sc, ICU_INT_CFG(irq_no));
+
+ /* Construct GICP compatible mapping. */
+ sc->parent_map_data->ncells = 2;
+ sc->parent_map_data->cells[0] = reg & ICU_INT_MASK;
+ sc->parent_map_data->cells[1] = irq_type;
+
+ return ((struct intr_map_data *)sc->parent_map_data);
+}
+
+static int
+mv_cp110_icu_detach(device_t dev)
+{
+
+ return (EBUSY);
+}
+
+static int
+mv_cp110_icu_activate_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ struct mv_cp110_icu_softc *sc;
+
+ sc = device_get_softc(dev);
+ data = mv_cp110_icu_convert_map_data(sc, data);
+ if (data == NULL)
+ return (EINVAL);
+ return (PIC_ACTIVATE_INTR(sc->parent, isrc, res, data));
+}
+
+static void
+mv_cp110_icu_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct mv_cp110_icu_softc *sc;
+ sc = device_get_softc(dev);
+
+ PIC_ENABLE_INTR(sc->parent, isrc);
+}
+
+static void
+mv_cp110_icu_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct mv_cp110_icu_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ PIC_DISABLE_INTR(sc->parent, isrc);
+}
+
+static int
+mv_cp110_icu_map_intr(device_t dev, struct intr_map_data *data,
+ struct intr_irqsrc **isrcp)
+{
+ struct mv_cp110_icu_softc *sc;
+ struct intr_map_data_fdt *daf;
+ uint32_t reg, irq_no, irq_type;
+ int ret;
+
+ sc = device_get_softc(dev);
+
+ if (data->type != INTR_MAP_DATA_FDT)
+ return (ENOTSUP);
+
+ /* Parse original */
+ daf = (struct intr_map_data_fdt *)data;
+ if (daf->ncells != 2)
+ return (EINVAL);
+ irq_no = daf->cells[0];
+ irq_type = daf->cells[1];
+ data = mv_cp110_icu_convert_map_data(sc, data);
+ if (data == NULL)
+ return (EINVAL);
+
+ reg = RD4(sc, ICU_INT_CFG(irq_no));
+ reg |= ICU_INT_ENABLE;
+ if (irq_type == IRQ_TYPE_LEVEL_HIGH)
+ reg &= ~ICU_INT_EDGE;
+ else
+ reg |= ICU_INT_EDGE;
+ WR4(sc, ICU_INT_CFG(irq_no), reg);
+
+ ret = PIC_MAP_INTR(sc->parent, data, isrcp);
+ (*isrcp)->isrc_dev = sc->dev;
+ return (ret);
+}
+
+static int
+mv_cp110_icu_deactivate_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ struct mv_cp110_icu_softc *sc;
+
+ sc = device_get_softc(dev);
+ data = mv_cp110_icu_convert_map_data(sc, data);
+ if (data == NULL)
+ return (EINVAL);
+
+ return (PIC_DEACTIVATE_INTR(sc->parent, isrc, res, data));
+}
+
+static int
+mv_cp110_icu_setup_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ struct mv_cp110_icu_softc *sc;
+
+ sc = device_get_softc(dev);
+ data = mv_cp110_icu_convert_map_data(sc, data);
+ if (data == NULL)
+ return (EINVAL);
+
+ return (PIC_SETUP_INTR(sc->parent, isrc, res, data));
+}
+
+static int
+mv_cp110_icu_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ struct mv_cp110_icu_softc *sc;
+
+ sc = device_get_softc(dev);
+ data = mv_cp110_icu_convert_map_data(sc, data);
+ if (data == NULL)
+ return (EINVAL);
+
+ return (PIC_TEARDOWN_INTR(sc->parent, isrc, res, data));
+}
+
+static void
+mv_cp110_icu_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct mv_cp110_icu_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ PIC_PRE_ITHREAD(sc->parent, isrc);
+}
+
+static void
+mv_cp110_icu_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct mv_cp110_icu_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ PIC_POST_ITHREAD(sc->parent, isrc);
+}
+
+static void
+mv_cp110_icu_post_filter(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct mv_cp110_icu_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ PIC_POST_FILTER(sc->parent, isrc);
+}
+
+static device_method_t mv_cp110_icu_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, mv_cp110_icu_probe),
+ DEVMETHOD(device_attach, mv_cp110_icu_attach),
+ DEVMETHOD(device_detach, mv_cp110_icu_detach),
+
+ /* Interrupt controller interface */
+ DEVMETHOD(pic_activate_intr, mv_cp110_icu_activate_intr),
+ DEVMETHOD(pic_disable_intr, mv_cp110_icu_disable_intr),
+ DEVMETHOD(pic_enable_intr, mv_cp110_icu_enable_intr),
+ DEVMETHOD(pic_map_intr, mv_cp110_icu_map_intr),
+ DEVMETHOD(pic_deactivate_intr, mv_cp110_icu_deactivate_intr),
+ DEVMETHOD(pic_setup_intr, mv_cp110_icu_setup_intr),
+ DEVMETHOD(pic_teardown_intr, mv_cp110_icu_teardown_intr),
+ DEVMETHOD(pic_post_filter, mv_cp110_icu_post_filter),
+ DEVMETHOD(pic_post_ithread, mv_cp110_icu_post_ithread),
+ DEVMETHOD(pic_pre_ithread, mv_cp110_icu_pre_ithread),
+
+ DEVMETHOD_END
+};
+
+static devclass_t mv_cp110_icu_devclass;
+
+static driver_t mv_cp110_icu_driver = {
+ "mv_cp110_icu",
+ mv_cp110_icu_methods,
+ sizeof(struct mv_cp110_icu_softc),
+};
+
+EARLY_DRIVER_MODULE(mv_cp110_icu, mv_cp110_icu_bus, mv_cp110_icu_driver,
+ mv_cp110_icu_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LAST);
diff --git a/sys/arm/mv/mv_cp110_icu_bus.c b/sys/arm/mv/mv_cp110_icu_bus.c
new file mode 100644
index 000000000000..c1c5f4ae7891
--- /dev/null
+++ b/sys/arm/mv/mv_cp110_icu_bus.c
@@ -0,0 +1,78 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2019 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <machine/bus.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/extres/syscon/syscon.h>
+#include <dev/fdt/simple_mfd.h>
+
+static struct ofw_compat_data compat_data[] = {
+ {"marvell,cp110-icu", 1},
+ {NULL, 0}
+};
+
+static int
+mv_cp110_icu_bus_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Marvell Interrupt Consolidation Unit Bus");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static device_method_t mv_cp110_icu_bus_methods[] = {
+ DEVMETHOD(device_probe, mv_cp110_icu_bus_probe),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(mv_cp110_icu_bus, mv_cp110_icu_bus_driver,
+ mv_cp110_icu_bus_methods, sizeof(struct simple_mfd_softc),
+ simple_mfd_driver);
+
+static devclass_t mv_cp110_icu_bus_devclass;
+EARLY_DRIVER_MODULE(mv_cp110_icu_bus, simplebus, mv_cp110_icu_bus_driver,
+ mv_cp110_icu_bus_devclass, 0, 0, BUS_PASS_INTERRUPT);
+MODULE_VERSION(mv_cp110_icu_bus, 1);
diff --git a/sys/arm/mv/mv_pci.c b/sys/arm/mv/mv_pci.c
new file mode 100644
index 000000000000..4c8da7dc7582
--- /dev/null
+++ b/sys/arm/mv/mv_pci.c
@@ -0,0 +1,1284 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 2008 MARVELL INTERNATIONAL LTD.
+ * Copyright (c) 2010 The FreeBSD Foundation
+ * Copyright (c) 2010-2015 Semihalf
+ * All rights reserved.
+ *
+ * Developed by Semihalf.
+ *
+ * Portions of this software were developed by Semihalf
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of MARVELL nor the names of contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Marvell integrated PCI/PCI-Express controller driver.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/queue.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/endian.h>
+#include <sys/devmap.h>
+
+#include <machine/fdt.h>
+#include <machine/intr.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/ofw_pci.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcib_private.h>
+
+#include "ofw_bus_if.h"
+#include "pcib_if.h"
+
+#include <machine/resource.h>
+#include <machine/bus.h>
+
+#include <arm/mv/mvreg.h>
+#include <arm/mv/mvvar.h>
+#include <arm/mv/mvwin.h>
+
+#ifdef DEBUG
+#define debugf(fmt, args...) do { printf(fmt,##args); } while (0)
+#else
+#define debugf(fmt, args...)
+#endif
+
+/*
+ * Code and data related to fdt-based PCI configuration.
+ *
+ * This stuff used to be in dev/fdt/fdt_pci.c and fdt_common.h, but it was
+ * always Marvell-specific so that was deleted and the code now lives here.
+ */
+
+struct mv_pci_range {
+ u_long base_pci;
+ u_long base_parent;
+ u_long len;
+};
+
+#define FDT_RANGES_CELLS ((3 + 3 + 2) * 2)
+#define PCI_SPACE_LEN 0x00400000
+
+static void
+mv_pci_range_dump(struct mv_pci_range *range)
+{
+#ifdef DEBUG
+ printf("\n");
+ printf(" base_pci = 0x%08lx\n", range->base_pci);
+ printf(" base_par = 0x%08lx\n", range->base_parent);
+ printf(" len = 0x%08lx\n", range->len);
+#endif
+}
+
+static int
+mv_pci_ranges_decode(phandle_t node, struct mv_pci_range *io_space,
+ struct mv_pci_range *mem_space)
+{
+ pcell_t ranges[FDT_RANGES_CELLS];
+ struct mv_pci_range *pci_space;
+ pcell_t addr_cells, size_cells, par_addr_cells;
+ pcell_t *rangesptr;
+ pcell_t cell0, cell1, cell2;
+ int tuple_size, tuples, i, rv, offset_cells, len;
+ int portid, is_io_space;
+
+ /*
+ * Retrieve 'ranges' property.
+ */
+ if ((fdt_addrsize_cells(node, &addr_cells, &size_cells)) != 0)
+ return (EINVAL);
+ if (addr_cells != 3 || size_cells != 2)
+ return (ERANGE);
+
+ par_addr_cells = fdt_parent_addr_cells(node);
+ if (par_addr_cells > 3)
+ return (ERANGE);
+
+ len = OF_getproplen(node, "ranges");
+ if (len > sizeof(ranges))
+ return (ENOMEM);
+
+ if (OF_getprop(node, "ranges", ranges, sizeof(ranges)) <= 0)
+ return (EINVAL);
+
+ tuple_size = sizeof(pcell_t) * (addr_cells + par_addr_cells +
+ size_cells);
+ tuples = len / tuple_size;
+
+ /*
+ * Initialize the ranges so that we don't have to worry about
+ * having them all defined in the FDT. In particular, it is
+ * perfectly fine not to want I/O space on PCI buses.
+ */
+ bzero(io_space, sizeof(*io_space));
+ bzero(mem_space, sizeof(*mem_space));
+
+ rangesptr = &ranges[0];
+ offset_cells = 0;
+ for (i = 0; i < tuples; i++) {
+ cell0 = fdt_data_get((void *)rangesptr, 1);
+ rangesptr++;
+ cell1 = fdt_data_get((void *)rangesptr, 1);
+ rangesptr++;
+ cell2 = fdt_data_get((void *)rangesptr, 1);
+ rangesptr++;
+ portid = fdt_data_get((void *)(rangesptr+1), 1);
+
+ if (cell0 & 0x02000000) {
+ pci_space = mem_space;
+ is_io_space = 0;
+ } else if (cell0 & 0x01000000) {
+ pci_space = io_space;
+ is_io_space = 1;
+ } else {
+ rv = ERANGE;
+ goto out;
+ }
+
+ if (par_addr_cells == 3) {
+ /*
+ * This is a PCI subnode 'ranges'. Skip cell0 and
+ * cell1 of this entry and only use cell2.
+ */
+ offset_cells = 2;
+ rangesptr += offset_cells;
+ }
+
+ if ((par_addr_cells - offset_cells) > 2) {
+ rv = ERANGE;
+ goto out;
+ }
+ pci_space->base_parent = fdt_data_get((void *)rangesptr,
+ par_addr_cells - offset_cells);
+ rangesptr += par_addr_cells - offset_cells;
+
+ if (size_cells > 2) {
+ rv = ERANGE;
+ goto out;
+ }
+ pci_space->len = fdt_data_get((void *)rangesptr, size_cells);
+ rangesptr += size_cells;
+
+ pci_space->base_pci = cell2;
+
+ if (pci_space->len == 0) {
+ pci_space->len = PCI_SPACE_LEN;
+ pci_space->base_parent = fdt_immr_va +
+ PCI_SPACE_LEN * ( 2 * portid + is_io_space);
+ }
+ }
+ rv = 0;
+out:
+ return (rv);
+}
+
+static int
+mv_pci_ranges(phandle_t node, struct mv_pci_range *io_space,
+ struct mv_pci_range *mem_space)
+{
+ int err;
+
+ debugf("Processing PCI node: %x\n", node);
+ if ((err = mv_pci_ranges_decode(node, io_space, mem_space)) != 0) {
+ debugf("could not decode parent PCI node 'ranges'\n");
+ return (err);
+ }
+
+ debugf("Post fixup dump:\n");
+ mv_pci_range_dump(io_space);
+ mv_pci_range_dump(mem_space);
+ return (0);
+}
+
+int
+mv_pci_devmap(phandle_t node, struct devmap_entry *devmap, vm_offset_t io_va,
+ vm_offset_t mem_va)
+{
+ struct mv_pci_range io_space, mem_space;
+ int error;
+
+ if ((error = mv_pci_ranges_decode(node, &io_space, &mem_space)) != 0)
+ return (error);
+
+ devmap->pd_va = (io_va ? io_va : io_space.base_parent);
+ devmap->pd_pa = io_space.base_parent;
+ devmap->pd_size = io_space.len;
+ devmap++;
+
+ devmap->pd_va = (mem_va ? mem_va : mem_space.base_parent);
+ devmap->pd_pa = mem_space.base_parent;
+ devmap->pd_size = mem_space.len;
+ return (0);
+}
+
+/*
+ * Code and data related to the Marvell pcib driver.
+ */
+
+#define PCI_CFG_ENA (1U << 31)
+#define PCI_CFG_BUS(bus) (((bus) & 0xff) << 16)
+#define PCI_CFG_DEV(dev) (((dev) & 0x1f) << 11)
+#define PCI_CFG_FUN(fun) (((fun) & 0x7) << 8)
+#define PCI_CFG_PCIE_REG(reg) ((reg) & 0xfc)
+
+#define PCI_REG_CFG_ADDR 0x0C78
+#define PCI_REG_CFG_DATA 0x0C7C
+
+#define PCIE_REG_CFG_ADDR 0x18F8
+#define PCIE_REG_CFG_DATA 0x18FC
+#define PCIE_REG_CONTROL 0x1A00
+#define PCIE_CTRL_LINK1X 0x00000001
+#define PCIE_REG_STATUS 0x1A04
+#define PCIE_REG_IRQ_MASK 0x1910
+
+#define PCIE_CONTROL_ROOT_CMPLX (1 << 1)
+#define PCIE_CONTROL_HOT_RESET (1 << 24)
+
+#define PCIE_LINK_TIMEOUT 1000000
+
+#define PCIE_STATUS_LINK_DOWN 1
+#define PCIE_STATUS_DEV_OFFS 16
+
+/* Minimum PCI Memory and I/O allocations taken from PCI spec (in bytes) */
+#define PCI_MIN_IO_ALLOC 4
+#define PCI_MIN_MEM_ALLOC 16
+
+#define BITS_PER_UINT32 (NBBY * sizeof(uint32_t))
+
+struct mv_pcib_softc {
+ device_t sc_dev;
+
+ struct rman sc_mem_rman;
+ bus_addr_t sc_mem_base;
+ bus_addr_t sc_mem_size;
+ uint32_t sc_mem_map[MV_PCI_MEM_SLICE_SIZE /
+ (PCI_MIN_MEM_ALLOC * BITS_PER_UINT32)];
+ int sc_win_target;
+ int sc_mem_win_attr;
+
+ struct rman sc_io_rman;
+ bus_addr_t sc_io_base;
+ bus_addr_t sc_io_size;
+ uint32_t sc_io_map[MV_PCI_IO_SLICE_SIZE /
+ (PCI_MIN_IO_ALLOC * BITS_PER_UINT32)];
+ int sc_io_win_attr;
+
+ struct resource *sc_res;
+ bus_space_handle_t sc_bsh;
+ bus_space_tag_t sc_bst;
+ int sc_rid;
+
+ struct mtx sc_msi_mtx;
+ uint32_t sc_msi_bitmap;
+
+ int sc_busnr; /* Host bridge bus number */
+ int sc_devnr; /* Host bridge device number */
+ int sc_type;
+ int sc_mode; /* Endpoint / Root Complex */
+
+ int sc_msi_supported;
+ int sc_skip_enable_procedure;
+ int sc_enable_find_root_slot;
+ struct ofw_bus_iinfo sc_pci_iinfo;
+
+ int ap_segment; /* PCI domain */
+};
+
+/* Local forward prototypes */
+static int mv_pcib_decode_win(phandle_t, struct mv_pcib_softc *);
+static void mv_pcib_hw_cfginit(void);
+static uint32_t mv_pcib_hw_cfgread(struct mv_pcib_softc *, u_int, u_int,
+ u_int, u_int, int);
+static void mv_pcib_hw_cfgwrite(struct mv_pcib_softc *, u_int, u_int,
+ u_int, u_int, uint32_t, int);
+static int mv_pcib_init(struct mv_pcib_softc *, int, int);
+static int mv_pcib_init_all_bars(struct mv_pcib_softc *, int, int, int, int);
+static void mv_pcib_init_bridge(struct mv_pcib_softc *, int, int, int);
+static inline void pcib_write_irq_mask(struct mv_pcib_softc *, uint32_t);
+static void mv_pcib_enable(struct mv_pcib_softc *, uint32_t);
+static int mv_pcib_mem_init(struct mv_pcib_softc *);
+
+/* Forward prototypes */
+static int mv_pcib_probe(device_t);
+static int mv_pcib_attach(device_t);
+
+static struct resource *mv_pcib_alloc_resource(device_t, device_t, int, int *,
+ rman_res_t, rman_res_t, rman_res_t, u_int);
+static int mv_pcib_release_resource(device_t, device_t, int, int,
+ struct resource *);
+static int mv_pcib_read_ivar(device_t, device_t, int, uintptr_t *);
+static int mv_pcib_write_ivar(device_t, device_t, int, uintptr_t);
+
+static int mv_pcib_maxslots(device_t);
+static uint32_t mv_pcib_read_config(device_t, u_int, u_int, u_int, u_int, int);
+static void mv_pcib_write_config(device_t, u_int, u_int, u_int, u_int,
+ uint32_t, int);
+static int mv_pcib_route_interrupt(device_t, device_t, int);
+
+static int mv_pcib_alloc_msi(device_t, device_t, int, int, int *);
+static int mv_pcib_map_msi(device_t, device_t, int, uint64_t *, uint32_t *);
+static int mv_pcib_release_msi(device_t, device_t, int, int *);
+
+/*
+ * Bus interface definitions.
+ */
+static device_method_t mv_pcib_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, mv_pcib_probe),
+ DEVMETHOD(device_attach, mv_pcib_attach),
+
+ /* Bus interface */
+ DEVMETHOD(bus_read_ivar, mv_pcib_read_ivar),
+ DEVMETHOD(bus_write_ivar, mv_pcib_write_ivar),
+ DEVMETHOD(bus_alloc_resource, mv_pcib_alloc_resource),
+ DEVMETHOD(bus_release_resource, mv_pcib_release_resource),
+ DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
+ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
+ DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
+
+ /* pcib interface */
+ DEVMETHOD(pcib_maxslots, mv_pcib_maxslots),
+ DEVMETHOD(pcib_read_config, mv_pcib_read_config),
+ DEVMETHOD(pcib_write_config, mv_pcib_write_config),
+ DEVMETHOD(pcib_route_interrupt, mv_pcib_route_interrupt),
+ DEVMETHOD(pcib_request_feature, pcib_request_feature_allow),
+
+ DEVMETHOD(pcib_alloc_msi, mv_pcib_alloc_msi),
+ DEVMETHOD(pcib_release_msi, mv_pcib_release_msi),
+ DEVMETHOD(pcib_map_msi, mv_pcib_map_msi),
+
+ /* OFW bus interface */
+ DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat),
+ DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model),
+ DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name),
+ DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node),
+ DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type),
+
+ DEVMETHOD_END
+};
+
+static driver_t mv_pcib_driver = {
+ "pcib",
+ mv_pcib_methods,
+ sizeof(struct mv_pcib_softc),
+};
+
+devclass_t pcib_devclass;
+
+DRIVER_MODULE(mv_pcib, ofwbus, mv_pcib_driver, pcib_devclass, 0, 0);
+DRIVER_MODULE(mv_pcib, pcib_ctrl, mv_pcib_driver, pcib_devclass, 0, 0);
+
+static struct mtx pcicfg_mtx;
+
+static int
+mv_pcib_probe(device_t self)
+{
+ phandle_t node;
+
+ node = ofw_bus_get_node(self);
+ if (!mv_fdt_is_type(node, "pci"))
+ return (ENXIO);
+
+ if (!(ofw_bus_is_compatible(self, "mrvl,pcie") ||
+ ofw_bus_is_compatible(self, "mrvl,pci") ||
+ ofw_bus_node_is_compatible(
+ OF_parent(node), "marvell,armada-370-pcie")))
+ return (ENXIO);
+
+ if (!ofw_bus_status_okay(self))
+ return (ENXIO);
+
+ device_set_desc(self, "Marvell Integrated PCI/PCI-E Controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+mv_pcib_attach(device_t self)
+{
+ struct mv_pcib_softc *sc;
+ phandle_t node, parnode;
+ uint32_t val, reg0;
+ int err, bus, devfn, port_id;
+
+ sc = device_get_softc(self);
+ sc->sc_dev = self;
+
+ node = ofw_bus_get_node(self);
+ parnode = OF_parent(node);
+
+ if (OF_getencprop(node, "marvell,pcie-port", &(port_id),
+ sizeof(port_id)) <= 0) {
+ /* If port ID does not exist in the FDT set value to 0 */
+ if (!OF_hasprop(node, "marvell,pcie-port"))
+ port_id = 0;
+ else
+ return(ENXIO);
+ }
+
+ sc->ap_segment = port_id;
+
+ if (ofw_bus_node_is_compatible(node, "mrvl,pcie")) {
+ sc->sc_type = MV_TYPE_PCIE;
+ sc->sc_win_target = MV_WIN_PCIE_TARGET(port_id);
+ sc->sc_mem_win_attr = MV_WIN_PCIE_MEM_ATTR(port_id);
+ sc->sc_io_win_attr = MV_WIN_PCIE_IO_ATTR(port_id);
+ sc->sc_skip_enable_procedure = 1;
+ } else if (ofw_bus_node_is_compatible(parnode, "marvell,armada-370-pcie")) {
+ sc->sc_type = MV_TYPE_PCIE;
+ sc->sc_win_target = MV_WIN_PCIE_TARGET_ARMADA38X(port_id);
+ sc->sc_mem_win_attr = MV_WIN_PCIE_MEM_ATTR_ARMADA38X(port_id);
+ sc->sc_io_win_attr = MV_WIN_PCIE_IO_ATTR_ARMADA38X(port_id);
+ sc->sc_enable_find_root_slot = 1;
+ } else if (ofw_bus_node_is_compatible(node, "mrvl,pci")) {
+ sc->sc_type = MV_TYPE_PCI;
+ sc->sc_win_target = MV_WIN_PCI_TARGET;
+ sc->sc_mem_win_attr = MV_WIN_PCI_MEM_ATTR;
+ sc->sc_io_win_attr = MV_WIN_PCI_IO_ATTR;
+ } else
+ return (ENXIO);
+
+ /*
+ * Retrieve our mem-mapped registers range.
+ */
+ sc->sc_rid = 0;
+ sc->sc_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &sc->sc_rid,
+ RF_ACTIVE);
+ if (sc->sc_res == NULL) {
+ device_printf(self, "could not map memory\n");
+ return (ENXIO);
+ }
+ sc->sc_bst = rman_get_bustag(sc->sc_res);
+ sc->sc_bsh = rman_get_bushandle(sc->sc_res);
+
+ val = bus_space_read_4(sc->sc_bst, sc->sc_bsh, PCIE_REG_CONTROL);
+ sc->sc_mode = (val & PCIE_CONTROL_ROOT_CMPLX ? MV_MODE_ROOT :
+ MV_MODE_ENDPOINT);
+
+ /*
+ * Get PCI interrupt info.
+ */
+ if (sc->sc_mode == MV_MODE_ROOT)
+ ofw_bus_setup_iinfo(node, &sc->sc_pci_iinfo, sizeof(pcell_t));
+
+ /*
+ * Configure decode windows for PCI(E) access.
+ */
+ if (mv_pcib_decode_win(node, sc) != 0)
+ return (ENXIO);
+
+ mv_pcib_hw_cfginit();
+
+ /*
+ * Enable PCIE device.
+ */
+ mv_pcib_enable(sc, port_id);
+
+ /*
+ * Memory management.
+ */
+ err = mv_pcib_mem_init(sc);
+ if (err)
+ return (err);
+
+ /*
+ * Preliminary bus enumeration to find first linked devices and set
+ * appropriate bus number from which should start the actual enumeration
+ */
+ for (bus = 0; bus < PCI_BUSMAX; bus++) {
+ for (devfn = 0; devfn < mv_pcib_maxslots(self); devfn++) {
+ reg0 = mv_pcib_read_config(self, bus, devfn, devfn & 0x7, 0x0, 4);
+ if (reg0 == (~0U))
+ continue; /* no device */
+ else {
+ sc->sc_busnr = bus; /* update bus number */
+ break;
+ }
+ }
+ }
+
+ if (sc->sc_mode == MV_MODE_ROOT) {
+ err = mv_pcib_init(sc, sc->sc_busnr,
+ mv_pcib_maxslots(sc->sc_dev));
+ if (err)
+ goto error;
+
+ device_add_child(self, "pci", -1);
+ } else {
+ sc->sc_devnr = 1;
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh,
+ PCIE_REG_STATUS, 1 << PCIE_STATUS_DEV_OFFS);
+ device_add_child(self, "pci_ep", -1);
+ }
+
+ mtx_init(&sc->sc_msi_mtx, "msi_mtx", NULL, MTX_DEF);
+ return (bus_generic_attach(self));
+
+error:
+ /* XXX SYS_RES_ should be released here */
+ rman_fini(&sc->sc_mem_rman);
+ rman_fini(&sc->sc_io_rman);
+
+ return (err);
+}
+
+static void
+mv_pcib_enable(struct mv_pcib_softc *sc, uint32_t unit)
+{
+ uint32_t val;
+ int timeout;
+
+ if (sc->sc_skip_enable_procedure)
+ goto pcib_enable_root_mode;
+
+ /*
+ * Check if PCIE device is enabled.
+ */
+ if ((sc->sc_skip_enable_procedure == 0) &&
+ (read_cpu_ctrl(CPU_CONTROL) & CPU_CONTROL_PCIE_DISABLE(unit))) {
+ write_cpu_ctrl(CPU_CONTROL, read_cpu_ctrl(CPU_CONTROL) &
+ ~(CPU_CONTROL_PCIE_DISABLE(unit)));
+
+ timeout = PCIE_LINK_TIMEOUT;
+ val = bus_space_read_4(sc->sc_bst, sc->sc_bsh,
+ PCIE_REG_STATUS);
+ while (((val & PCIE_STATUS_LINK_DOWN) == 1) && (timeout > 0)) {
+ DELAY(1000);
+ timeout -= 1000;
+ val = bus_space_read_4(sc->sc_bst, sc->sc_bsh,
+ PCIE_REG_STATUS);
+ }
+ }
+
+pcib_enable_root_mode:
+ if (sc->sc_mode == MV_MODE_ROOT) {
+ /*
+ * Enable PCI bridge.
+ */
+ val = bus_space_read_4(sc->sc_bst, sc->sc_bsh, PCIR_COMMAND);
+ val |= PCIM_CMD_SERRESPEN | PCIM_CMD_BUSMASTEREN |
+ PCIM_CMD_MEMEN | PCIM_CMD_PORTEN;
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, PCIR_COMMAND, val);
+ }
+}
+
+static int
+mv_pcib_mem_init(struct mv_pcib_softc *sc)
+{
+ int err;
+
+ /*
+ * Memory management.
+ */
+ sc->sc_mem_rman.rm_type = RMAN_ARRAY;
+ err = rman_init(&sc->sc_mem_rman);
+ if (err)
+ return (err);
+
+ sc->sc_io_rman.rm_type = RMAN_ARRAY;
+ err = rman_init(&sc->sc_io_rman);
+ if (err) {
+ rman_fini(&sc->sc_mem_rman);
+ return (err);
+ }
+
+ err = rman_manage_region(&sc->sc_mem_rman, sc->sc_mem_base,
+ sc->sc_mem_base + sc->sc_mem_size - 1);
+ if (err)
+ goto error;
+
+ err = rman_manage_region(&sc->sc_io_rman, sc->sc_io_base,
+ sc->sc_io_base + sc->sc_io_size - 1);
+ if (err)
+ goto error;
+
+ return (0);
+
+error:
+ rman_fini(&sc->sc_mem_rman);
+ rman_fini(&sc->sc_io_rman);
+
+ return (err);
+}
+
+static inline uint32_t
+pcib_bit_get(uint32_t *map, uint32_t bit)
+{
+ uint32_t n = bit / BITS_PER_UINT32;
+
+ bit = bit % BITS_PER_UINT32;
+ return (map[n] & (1 << bit));
+}
+
+static inline void
+pcib_bit_set(uint32_t *map, uint32_t bit)
+{
+ uint32_t n = bit / BITS_PER_UINT32;
+
+ bit = bit % BITS_PER_UINT32;
+ map[n] |= (1 << bit);
+}
+
+static inline uint32_t
+pcib_map_check(uint32_t *map, uint32_t start, uint32_t bits)
+{
+ uint32_t i;
+
+ for (i = start; i < start + bits; i++)
+ if (pcib_bit_get(map, i))
+ return (0);
+
+ return (1);
+}
+
+static inline void
+pcib_map_set(uint32_t *map, uint32_t start, uint32_t bits)
+{
+ uint32_t i;
+
+ for (i = start; i < start + bits; i++)
+ pcib_bit_set(map, i);
+}
+
+/*
+ * The idea of this allocator is taken from ARM No-Cache memory
+ * management code (sys/arm/arm/vm_machdep.c).
+ */
+static bus_addr_t
+pcib_alloc(struct mv_pcib_softc *sc, uint32_t smask)
+{
+ uint32_t bits, bits_limit, i, *map, min_alloc, size;
+ bus_addr_t addr = 0;
+ bus_addr_t base;
+
+ if (smask & 1) {
+ base = sc->sc_io_base;
+ min_alloc = PCI_MIN_IO_ALLOC;
+ bits_limit = sc->sc_io_size / min_alloc;
+ map = sc->sc_io_map;
+ smask &= ~0x3;
+ } else {
+ base = sc->sc_mem_base;
+ min_alloc = PCI_MIN_MEM_ALLOC;
+ bits_limit = sc->sc_mem_size / min_alloc;
+ map = sc->sc_mem_map;
+ smask &= ~0xF;
+ }
+
+ size = ~smask + 1;
+ bits = size / min_alloc;
+
+ for (i = 0; i + bits <= bits_limit; i += bits)
+ if (pcib_map_check(map, i, bits)) {
+ pcib_map_set(map, i, bits);
+ addr = base + (i * min_alloc);
+ return (addr);
+ }
+
+ return (addr);
+}
+
+static int
+mv_pcib_init_bar(struct mv_pcib_softc *sc, int bus, int slot, int func,
+ int barno)
+{
+ uint32_t addr, bar;
+ int reg, width;
+
+ reg = PCIR_BAR(barno);
+
+ /*
+ * Need to init the BAR register with 0xffffffff before correct
+ * value can be read.
+ */
+ mv_pcib_write_config(sc->sc_dev, bus, slot, func, reg, ~0, 4);
+ bar = mv_pcib_read_config(sc->sc_dev, bus, slot, func, reg, 4);
+ if (bar == 0)
+ return (1);
+
+ /* Calculate BAR size: 64 or 32 bit (in 32-bit units) */
+ width = ((bar & 7) == 4) ? 2 : 1;
+
+ addr = pcib_alloc(sc, bar);
+ if (!addr)
+ return (-1);
+
+ if (bootverbose)
+ printf("PCI %u:%u:%u: reg %x: smask=%08x: addr=%08x\n",
+ bus, slot, func, reg, bar, addr);
+
+ mv_pcib_write_config(sc->sc_dev, bus, slot, func, reg, addr, 4);
+ if (width == 2)
+ mv_pcib_write_config(sc->sc_dev, bus, slot, func, reg + 4,
+ 0, 4);
+
+ return (width);
+}
+
+static void
+mv_pcib_init_bridge(struct mv_pcib_softc *sc, int bus, int slot, int func)
+{
+ bus_addr_t io_base, mem_base;
+ uint32_t io_limit, mem_limit;
+ int secbus;
+
+ io_base = sc->sc_io_base;
+ io_limit = io_base + sc->sc_io_size - 1;
+ mem_base = sc->sc_mem_base;
+ mem_limit = mem_base + sc->sc_mem_size - 1;
+
+ /* Configure I/O decode registers */
+ mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_IOBASEL_1,
+ io_base >> 8, 1);
+ mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_IOBASEH_1,
+ io_base >> 16, 2);
+ mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_IOLIMITL_1,
+ io_limit >> 8, 1);
+ mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_IOLIMITH_1,
+ io_limit >> 16, 2);
+
+ /* Configure memory decode registers */
+ mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_MEMBASE_1,
+ mem_base >> 16, 2);
+ mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_MEMLIMIT_1,
+ mem_limit >> 16, 2);
+
+ /* Disable memory prefetch decode */
+ mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_PMBASEL_1,
+ 0x10, 2);
+ mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_PMBASEH_1,
+ 0x0, 4);
+ mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_PMLIMITL_1,
+ 0xF, 2);
+ mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_PMLIMITH_1,
+ 0x0, 4);
+
+ secbus = mv_pcib_read_config(sc->sc_dev, bus, slot, func,
+ PCIR_SECBUS_1, 1);
+
+ /* Configure buses behind the bridge */
+ mv_pcib_init(sc, secbus, PCI_SLOTMAX);
+}
+
+static int
+mv_pcib_init(struct mv_pcib_softc *sc, int bus, int maxslot)
+{
+ int slot, func, maxfunc, error;
+ uint8_t hdrtype, command, class, subclass;
+
+ for (slot = 0; slot <= maxslot; slot++) {
+ maxfunc = 0;
+ for (func = 0; func <= maxfunc; func++) {
+ hdrtype = mv_pcib_read_config(sc->sc_dev, bus, slot,
+ func, PCIR_HDRTYPE, 1);
+
+ if ((hdrtype & PCIM_HDRTYPE) > PCI_MAXHDRTYPE)
+ continue;
+
+ if (func == 0 && (hdrtype & PCIM_MFDEV))
+ maxfunc = PCI_FUNCMAX;
+
+ command = mv_pcib_read_config(sc->sc_dev, bus, slot,
+ func, PCIR_COMMAND, 1);
+ command &= ~(PCIM_CMD_MEMEN | PCIM_CMD_PORTEN);
+ mv_pcib_write_config(sc->sc_dev, bus, slot, func,
+ PCIR_COMMAND, command, 1);
+
+ error = mv_pcib_init_all_bars(sc, bus, slot, func,
+ hdrtype);
+
+ if (error)
+ return (error);
+
+ command |= PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN |
+ PCIM_CMD_PORTEN;
+ mv_pcib_write_config(sc->sc_dev, bus, slot, func,
+ PCIR_COMMAND, command, 1);
+
+ /* Handle PCI-PCI bridges */
+ class = mv_pcib_read_config(sc->sc_dev, bus, slot,
+ func, PCIR_CLASS, 1);
+ subclass = mv_pcib_read_config(sc->sc_dev, bus, slot,
+ func, PCIR_SUBCLASS, 1);
+
+ if (class != PCIC_BRIDGE ||
+ subclass != PCIS_BRIDGE_PCI)
+ continue;
+
+ mv_pcib_init_bridge(sc, bus, slot, func);
+ }
+ }
+
+ /* Enable all ABCD interrupts */
+ pcib_write_irq_mask(sc, (0xF << 24));
+
+ return (0);
+}
+
+static int
+mv_pcib_init_all_bars(struct mv_pcib_softc *sc, int bus, int slot,
+ int func, int hdrtype)
+{
+ int maxbar, bar, i;
+
+ maxbar = (hdrtype & PCIM_HDRTYPE) ? 0 : 6;
+ bar = 0;
+
+ /* Program the base address registers */
+ while (bar < maxbar) {
+ i = mv_pcib_init_bar(sc, bus, slot, func, bar);
+ bar += i;
+ if (i < 0) {
+ device_printf(sc->sc_dev,
+ "PCI IO/Memory space exhausted\n");
+ return (ENOMEM);
+ }
+ }
+
+ return (0);
+}
+
+static struct resource *
+mv_pcib_alloc_resource(device_t dev, device_t child, int type, int *rid,
+ rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
+{
+ struct mv_pcib_softc *sc = device_get_softc(dev);
+ struct rman *rm = NULL;
+ struct resource *res;
+
+ switch (type) {
+ case SYS_RES_IOPORT:
+ rm = &sc->sc_io_rman;
+ break;
+ case SYS_RES_MEMORY:
+ rm = &sc->sc_mem_rman;
+ break;
+#ifdef PCI_RES_BUS
+ case PCI_RES_BUS:
+ return (pci_domain_alloc_bus(sc->ap_segment, child, rid, start,
+ end, count, flags));
+#endif
+ default:
+ return (BUS_ALLOC_RESOURCE(device_get_parent(dev), dev,
+ type, rid, start, end, count, flags));
+ }
+
+ if (RMAN_IS_DEFAULT_RANGE(start, end)) {
+ start = sc->sc_mem_base;
+ end = sc->sc_mem_base + sc->sc_mem_size - 1;
+ count = sc->sc_mem_size;
+ }
+
+ if ((start < sc->sc_mem_base) || (start + count - 1 != end) ||
+ (end > sc->sc_mem_base + sc->sc_mem_size - 1))
+ return (NULL);
+
+ res = rman_reserve_resource(rm, start, end, count, flags, child);
+ if (res == NULL)
+ return (NULL);
+
+ rman_set_rid(res, *rid);
+ rman_set_bustag(res, fdtbus_bs_tag);
+ rman_set_bushandle(res, start);
+
+ if (flags & RF_ACTIVE)
+ if (bus_activate_resource(child, type, *rid, res)) {
+ rman_release_resource(res);
+ return (NULL);
+ }
+
+ return (res);
+}
+
+static int
+mv_pcib_release_resource(device_t dev, device_t child, int type, int rid,
+ struct resource *res)
+{
+#ifdef PCI_RES_BUS
+ struct mv_pcib_softc *sc = device_get_softc(dev);
+
+ if (type == PCI_RES_BUS)
+ return (pci_domain_release_bus(sc->ap_segment, child, rid, res));
+#endif
+ if (type != SYS_RES_IOPORT && type != SYS_RES_MEMORY)
+ return (BUS_RELEASE_RESOURCE(device_get_parent(dev), child,
+ type, rid, res));
+
+ return (rman_release_resource(res));
+}
+
+static int
+mv_pcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
+{
+ struct mv_pcib_softc *sc = device_get_softc(dev);
+
+ switch (which) {
+ case PCIB_IVAR_BUS:
+ *result = sc->sc_busnr;
+ return (0);
+ case PCIB_IVAR_DOMAIN:
+ *result = device_get_unit(dev);
+ return (0);
+ }
+
+ return (ENOENT);
+}
+
+static int
+mv_pcib_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
+{
+ struct mv_pcib_softc *sc = device_get_softc(dev);
+
+ switch (which) {
+ case PCIB_IVAR_BUS:
+ sc->sc_busnr = value;
+ return (0);
+ }
+
+ return (ENOENT);
+}
+
+static inline void
+pcib_write_irq_mask(struct mv_pcib_softc *sc, uint32_t mask)
+{
+
+ if (sc->sc_type != MV_TYPE_PCIE)
+ return;
+
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, PCIE_REG_IRQ_MASK, mask);
+}
+
+static void
+mv_pcib_hw_cfginit(void)
+{
+ static int opened = 0;
+
+ if (opened)
+ return;
+
+ mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN);
+ opened = 1;
+}
+
+static uint32_t
+mv_pcib_hw_cfgread(struct mv_pcib_softc *sc, u_int bus, u_int slot,
+ u_int func, u_int reg, int bytes)
+{
+ uint32_t addr, data, ca, cd;
+
+ ca = (sc->sc_type != MV_TYPE_PCI) ?
+ PCIE_REG_CFG_ADDR : PCI_REG_CFG_ADDR;
+ cd = (sc->sc_type != MV_TYPE_PCI) ?
+ PCIE_REG_CFG_DATA : PCI_REG_CFG_DATA;
+ addr = PCI_CFG_ENA | PCI_CFG_BUS(bus) | PCI_CFG_DEV(slot) |
+ PCI_CFG_FUN(func) | PCI_CFG_PCIE_REG(reg);
+
+ mtx_lock_spin(&pcicfg_mtx);
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, ca, addr);
+
+ data = ~0;
+ switch (bytes) {
+ case 1:
+ data = bus_space_read_1(sc->sc_bst, sc->sc_bsh,
+ cd + (reg & 3));
+ break;
+ case 2:
+ data = le16toh(bus_space_read_2(sc->sc_bst, sc->sc_bsh,
+ cd + (reg & 2)));
+ break;
+ case 4:
+ data = le32toh(bus_space_read_4(sc->sc_bst, sc->sc_bsh,
+ cd));
+ break;
+ }
+ mtx_unlock_spin(&pcicfg_mtx);
+ return (data);
+}
+
+static void
+mv_pcib_hw_cfgwrite(struct mv_pcib_softc *sc, u_int bus, u_int slot,
+ u_int func, u_int reg, uint32_t data, int bytes)
+{
+ uint32_t addr, ca, cd;
+
+ ca = (sc->sc_type != MV_TYPE_PCI) ?
+ PCIE_REG_CFG_ADDR : PCI_REG_CFG_ADDR;
+ cd = (sc->sc_type != MV_TYPE_PCI) ?
+ PCIE_REG_CFG_DATA : PCI_REG_CFG_DATA;
+ addr = PCI_CFG_ENA | PCI_CFG_BUS(bus) | PCI_CFG_DEV(slot) |
+ PCI_CFG_FUN(func) | PCI_CFG_PCIE_REG(reg);
+
+ mtx_lock_spin(&pcicfg_mtx);
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, ca, addr);
+
+ switch (bytes) {
+ case 1:
+ bus_space_write_1(sc->sc_bst, sc->sc_bsh,
+ cd + (reg & 3), data);
+ break;
+ case 2:
+ bus_space_write_2(sc->sc_bst, sc->sc_bsh,
+ cd + (reg & 2), htole16(data));
+ break;
+ case 4:
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh,
+ cd, htole32(data));
+ break;
+ }
+ mtx_unlock_spin(&pcicfg_mtx);
+}
+
+static int
+mv_pcib_maxslots(device_t dev)
+{
+ struct mv_pcib_softc *sc = device_get_softc(dev);
+
+ return ((sc->sc_type != MV_TYPE_PCI) ? 1 : PCI_SLOTMAX);
+}
+
+static int
+mv_pcib_root_slot(device_t dev, u_int bus, u_int slot, u_int func)
+{
+ struct mv_pcib_softc *sc = device_get_softc(dev);
+ uint32_t vendor, device;
+
+ /* On platforms other than Armada38x, root link is always at slot 0 */
+ if (!sc->sc_enable_find_root_slot)
+ return (slot == 0);
+
+ vendor = mv_pcib_hw_cfgread(sc, bus, slot, func, PCIR_VENDOR,
+ PCIR_VENDOR_LENGTH);
+ device = mv_pcib_hw_cfgread(sc, bus, slot, func, PCIR_DEVICE,
+ PCIR_DEVICE_LENGTH) & MV_DEV_FAMILY_MASK;
+
+ return (vendor == PCI_VENDORID_MRVL && device == MV_DEV_ARMADA38X);
+}
+
+static uint32_t
+mv_pcib_read_config(device_t dev, u_int bus, u_int slot, u_int func,
+ u_int reg, int bytes)
+{
+ struct mv_pcib_softc *sc = device_get_softc(dev);
+
+ /* Return ~0 if link is inactive or trying to read from Root */
+ if ((bus_space_read_4(sc->sc_bst, sc->sc_bsh, PCIE_REG_STATUS) &
+ PCIE_STATUS_LINK_DOWN) || mv_pcib_root_slot(dev, bus, slot, func))
+ return (~0U);
+
+ return (mv_pcib_hw_cfgread(sc, bus, slot, func, reg, bytes));
+}
+
+static void
+mv_pcib_write_config(device_t dev, u_int bus, u_int slot, u_int func,
+ u_int reg, uint32_t val, int bytes)
+{
+ struct mv_pcib_softc *sc = device_get_softc(dev);
+
+ /* Return if link is inactive or trying to write to Root */
+ if ((bus_space_read_4(sc->sc_bst, sc->sc_bsh, PCIE_REG_STATUS) &
+ PCIE_STATUS_LINK_DOWN) || mv_pcib_root_slot(dev, bus, slot, func))
+ return;
+
+ mv_pcib_hw_cfgwrite(sc, bus, slot, func, reg, val, bytes);
+}
+
+static int
+mv_pcib_route_interrupt(device_t bus, device_t dev, int pin)
+{
+ struct mv_pcib_softc *sc;
+ struct ofw_pci_register reg;
+ uint32_t pintr, mintr[4];
+ int icells;
+ phandle_t iparent;
+
+ sc = device_get_softc(bus);
+ pintr = pin;
+
+ /* Fabricate imap information in case this isn't an OFW device */
+ bzero(&reg, sizeof(reg));
+ reg.phys_hi = (pci_get_bus(dev) << OFW_PCI_PHYS_HI_BUSSHIFT) |
+ (pci_get_slot(dev) << OFW_PCI_PHYS_HI_DEVICESHIFT) |
+ (pci_get_function(dev) << OFW_PCI_PHYS_HI_FUNCTIONSHIFT);
+
+ icells = ofw_bus_lookup_imap(ofw_bus_get_node(dev), &sc->sc_pci_iinfo,
+ &reg, sizeof(reg), &pintr, sizeof(pintr), mintr, sizeof(mintr),
+ &iparent);
+ if (icells > 0)
+ return (ofw_bus_map_intr(dev, iparent, icells, mintr));
+
+ /* Maybe it's a real interrupt, not an intpin */
+ if (pin > 4)
+ return (pin);
+
+ device_printf(bus, "could not route pin %d for device %d.%d\n",
+ pin, pci_get_slot(dev), pci_get_function(dev));
+ return (PCI_INVALID_IRQ);
+}
+
+static int
+mv_pcib_decode_win(phandle_t node, struct mv_pcib_softc *sc)
+{
+ struct mv_pci_range io_space, mem_space;
+ device_t dev;
+ int error;
+
+ dev = sc->sc_dev;
+
+ if ((error = mv_pci_ranges(node, &io_space, &mem_space)) != 0) {
+ device_printf(dev, "could not retrieve 'ranges' data\n");
+ return (error);
+ }
+
+ /* Configure CPU decoding windows */
+ error = decode_win_cpu_set(sc->sc_win_target,
+ sc->sc_io_win_attr, io_space.base_parent, io_space.len, ~0);
+ if (error < 0) {
+ device_printf(dev, "could not set up CPU decode "
+ "window for PCI IO\n");
+ return (ENXIO);
+ }
+ error = decode_win_cpu_set(sc->sc_win_target,
+ sc->sc_mem_win_attr, mem_space.base_parent, mem_space.len,
+ mem_space.base_parent);
+ if (error < 0) {
+ device_printf(dev, "could not set up CPU decode "
+ "windows for PCI MEM\n");
+ return (ENXIO);
+ }
+
+ sc->sc_io_base = io_space.base_parent;
+ sc->sc_io_size = io_space.len;
+
+ sc->sc_mem_base = mem_space.base_parent;
+ sc->sc_mem_size = mem_space.len;
+
+ return (0);
+}
+
+static int
+mv_pcib_map_msi(device_t dev, device_t child, int irq, uint64_t *addr,
+ uint32_t *data)
+{
+ struct mv_pcib_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (!sc->sc_msi_supported)
+ return (ENOTSUP);
+
+ irq = irq - MSI_IRQ;
+
+ /* validate parameters */
+ if (isclr(&sc->sc_msi_bitmap, irq)) {
+ device_printf(dev, "invalid MSI 0x%x\n", irq);
+ return (EINVAL);
+ }
+
+ mv_msi_data(irq, addr, data);
+
+ debugf("%s: irq: %d addr: %jx data: %x\n",
+ __func__, irq, *addr, *data);
+
+ return (0);
+}
+
+static int
+mv_pcib_alloc_msi(device_t dev, device_t child, int count,
+ int maxcount __unused, int *irqs)
+{
+ struct mv_pcib_softc *sc;
+ u_int start = 0, i;
+
+ sc = device_get_softc(dev);
+ if (!sc->sc_msi_supported)
+ return (ENOTSUP);
+
+ if (powerof2(count) == 0 || count > MSI_IRQ_NUM)
+ return (EINVAL);
+
+ mtx_lock(&sc->sc_msi_mtx);
+
+ for (start = 0; (start + count) < MSI_IRQ_NUM; start++) {
+ for (i = start; i < start + count; i++) {
+ if (isset(&sc->sc_msi_bitmap, i))
+ break;
+ }
+ if (i == start + count)
+ break;
+ }
+
+ if ((start + count) == MSI_IRQ_NUM) {
+ mtx_unlock(&sc->sc_msi_mtx);
+ return (ENXIO);
+ }
+
+ for (i = start; i < start + count; i++) {
+ setbit(&sc->sc_msi_bitmap, i);
+ *irqs++ = MSI_IRQ + i;
+ }
+ debugf("%s: start: %x count: %x\n", __func__, start, count);
+
+ mtx_unlock(&sc->sc_msi_mtx);
+ return (0);
+}
+
+static int
+mv_pcib_release_msi(device_t dev, device_t child, int count, int *irqs)
+{
+ struct mv_pcib_softc *sc;
+ u_int i;
+
+ sc = device_get_softc(dev);
+ if(!sc->sc_msi_supported)
+ return (ENOTSUP);
+
+ mtx_lock(&sc->sc_msi_mtx);
+
+ for (i = 0; i < count; i++)
+ clrbit(&sc->sc_msi_bitmap, irqs[i] - MSI_IRQ);
+
+ mtx_unlock(&sc->sc_msi_mtx);
+ return (0);
+}
diff --git a/sys/arm/mv/mv_pci_ctrl.c b/sys/arm/mv/mv_pci_ctrl.c
new file mode 100644
index 000000000000..0b51a5289c9e
--- /dev/null
+++ b/sys/arm/mv/mv_pci_ctrl.c
@@ -0,0 +1,349 @@
+/*-
+ * Copyright (c) 2016 Stormshield
+ * Copyright (c) 2016 Semihalf
+ * All rights reserved.
+ *
+ * Developed by Semihalf.
+ *
+ * Portions of this software were developed by Semihalf
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of MARVELL nor the names of contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Marvell integrated PCI/PCI-Express Bus Controller Driver.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+static int mv_pcib_ctrl_probe(device_t);
+static int mv_pcib_ctrl_attach(device_t);
+static device_t mv_pcib_ctrl_add_child(device_t, u_int, const char *, int);
+static const struct ofw_bus_devinfo * mv_pcib_ctrl_get_devinfo(device_t, device_t);
+static struct resource * mv_pcib_ctrl_alloc_resource(device_t, device_t, int,
+ int *, rman_res_t, rman_res_t, rman_res_t, u_int);
+void mv_pcib_ctrl_init(device_t, phandle_t);
+static int mv_pcib_ofw_bus_attach(device_t);
+
+struct mv_pcib_ctrl_range {
+ uint64_t bus;
+ uint64_t host;
+ uint64_t size;
+};
+
+typedef int (*get_rl_t)(device_t dev, phandle_t node, pcell_t acells,
+ pcell_t scells, struct resource_list *rl);
+
+struct mv_pcib_ctrl_softc {
+ pcell_t addr_cells;
+ pcell_t size_cells;
+ int nranges;
+ struct mv_pcib_ctrl_range *ranges;
+};
+
+struct mv_pcib_ctrl_devinfo {
+ struct ofw_bus_devinfo di_dinfo;
+ struct resource_list di_rl;
+};
+
+static int mv_pcib_ctrl_fill_ranges(phandle_t, struct mv_pcib_ctrl_softc *);
+
+/*
+ * Bus interface definitions
+ */
+static device_method_t mv_pcib_ctrl_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, mv_pcib_ctrl_probe),
+ DEVMETHOD(device_attach, mv_pcib_ctrl_attach),
+
+ /* Bus interface */
+ DEVMETHOD(bus_add_child, mv_pcib_ctrl_add_child),
+ DEVMETHOD(bus_alloc_resource, mv_pcib_ctrl_alloc_resource),
+ DEVMETHOD(bus_release_resource, bus_generic_release_resource),
+ DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
+ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
+
+ /* ofw_bus interface */
+ DEVMETHOD(ofw_bus_get_devinfo, mv_pcib_ctrl_get_devinfo),
+ DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat),
+ DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model),
+ DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name),
+ DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node),
+ DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type),
+
+ DEVMETHOD_END
+};
+
+static struct ofw_compat_data mv_pcib_ctrl_compat[] = {
+ {"mrvl,pcie-ctrl", (uintptr_t)&ofw_bus_reg_to_rl},
+ {"marvell,armada-370-pcie",
+ (uintptr_t)&ofw_bus_assigned_addresses_to_rl},
+ {NULL, (uintptr_t)NULL},
+};
+
+static driver_t mv_pcib_ctrl_driver = {
+ "pcib_ctrl",
+ mv_pcib_ctrl_methods,
+ sizeof(struct mv_pcib_ctrl_softc),
+};
+
+devclass_t pcib_ctrl_devclass;
+
+DRIVER_MODULE(pcib_ctrl, simplebus, mv_pcib_ctrl_driver, pcib_ctrl_devclass, 0, 0);
+
+MALLOC_DEFINE(M_PCIB_CTRL, "PCIe Bus Controller",
+ "Marvell Integrated PCIe Bus Controller");
+
+static int
+mv_pcib_ctrl_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_search_compatible(dev, mv_pcib_ctrl_compat)->ocd_data)
+ return (ENXIO);
+
+ device_set_desc(dev, "Marvell Integrated PCIe Bus Controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+mv_pcib_ctrl_attach(device_t dev)
+{
+ int err;
+
+ err = mv_pcib_ofw_bus_attach(dev);
+ if (err != 0)
+ return (err);
+
+ return (bus_generic_attach(dev));
+}
+
+static int
+mv_pcib_ofw_bus_attach(device_t dev)
+{
+ struct mv_pcib_ctrl_devinfo *di;
+ struct mv_pcib_ctrl_softc *sc;
+ device_t child;
+ phandle_t parent, node;
+ get_rl_t get_rl;
+
+ parent = ofw_bus_get_node(dev);
+ sc = device_get_softc(dev);
+ if (parent > 0) {
+ sc->addr_cells = 1;
+ if (OF_getencprop(parent, "#address-cells", &(sc->addr_cells),
+ sizeof(sc->addr_cells)) <= 0)
+ return(ENXIO);
+
+ sc->size_cells = 1;
+ if (OF_getencprop(parent, "#size-cells", &(sc->size_cells),
+ sizeof(sc->size_cells)) <= 0)
+ return(ENXIO);
+
+ for (node = OF_child(parent); node > 0; node = OF_peer(node)) {
+ di = malloc(sizeof(*di), M_PCIB_CTRL, M_WAITOK | M_ZERO);
+ if (ofw_bus_gen_setup_devinfo(&di->di_dinfo, node)) {
+ if (bootverbose) {
+ device_printf(dev,
+ "Could not set up devinfo for PCI\n");
+ }
+ free(di, M_PCIB_CTRL);
+ continue;
+ }
+
+ child = device_add_child(dev, NULL, -1);
+ if (child == NULL) {
+ if (bootverbose) {
+ device_printf(dev,
+ "Could not add child: %s\n",
+ di->di_dinfo.obd_name);
+ }
+ ofw_bus_gen_destroy_devinfo(&di->di_dinfo);
+ free(di, M_PCIB_CTRL);
+ continue;
+ }
+
+ resource_list_init(&di->di_rl);
+ get_rl = (get_rl_t) ofw_bus_search_compatible(dev,
+ mv_pcib_ctrl_compat)->ocd_data;
+ if (get_rl != NULL)
+ get_rl(child, node, sc->addr_cells,
+ sc->size_cells, &di->di_rl);
+
+ device_set_ivars(child, di);
+ }
+ }
+
+ if (mv_pcib_ctrl_fill_ranges(parent, sc) < 0) {
+ device_printf(dev, "could not get ranges\n");
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static device_t
+mv_pcib_ctrl_add_child(device_t dev, u_int order, const char *name, int unit)
+{
+ device_t cdev;
+ struct mv_pcib_ctrl_devinfo *di;
+
+ cdev = device_add_child_ordered(dev, order, name, unit);
+ if (cdev == NULL)
+ return (NULL);
+
+ di = malloc(sizeof(*di), M_DEVBUF, M_WAITOK | M_ZERO);
+ di->di_dinfo.obd_node = -1;
+ resource_list_init(&di->di_rl);
+ device_set_ivars(cdev, di);
+
+ return (cdev);
+}
+
+static struct resource *
+mv_pcib_ctrl_alloc_resource(device_t bus, device_t child, int type, int *rid,
+ rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
+{
+ struct mv_pcib_ctrl_devinfo *di;
+ struct resource_list_entry *rle;
+ struct mv_pcib_ctrl_softc *sc;
+ int i;
+
+ if (RMAN_IS_DEFAULT_RANGE(start, end)) {
+ if ((di = device_get_ivars(child)) == NULL)
+ return (NULL);
+ if (type != SYS_RES_MEMORY)
+ return (NULL);
+
+ /* Find defaults for this rid */
+ rle = resource_list_find(&di->di_rl, type, *rid);
+
+ if (rle == NULL)
+ return (NULL);
+
+ start = rle->start;
+ end = rle->end;
+ count = rle->count;
+ }
+
+ sc = device_get_softc(bus);
+ if (type == SYS_RES_MEMORY) {
+ /* Remap through ranges property */
+ for (i = 0; i < sc->nranges; i++) {
+ if (start >= sc->ranges[i].bus && end <
+ sc->ranges[i].bus + sc->ranges[i].size) {
+ start -= sc->ranges[i].bus;
+ start += sc->ranges[i].host;
+ end -= sc->ranges[i].bus;
+ end += sc->ranges[i].host;
+ break;
+ }
+ }
+
+ if (i == sc->nranges && sc->nranges != 0) {
+ device_printf(bus, "Could not map resource "
+ "%#llx-%#llx\n", start, end);
+ return (NULL);
+ }
+ }
+
+ return (bus_generic_alloc_resource(bus, child, type, rid, start, end,
+ count, flags));
+}
+
+static int
+mv_pcib_ctrl_fill_ranges(phandle_t node, struct mv_pcib_ctrl_softc *sc)
+{
+ int host_address_cells;
+ cell_t *base_ranges;
+ ssize_t nbase_ranges;
+ int err;
+ int i, j, k;
+
+ err = OF_searchencprop(OF_parent(node), "#address-cells",
+ &host_address_cells, sizeof(host_address_cells));
+ if (err <= 0)
+ return (-1);
+
+ nbase_ranges = OF_getproplen(node, "ranges");
+ if (nbase_ranges < 0)
+ return (-1);
+ sc->nranges = nbase_ranges / sizeof(cell_t) /
+ (sc->addr_cells + host_address_cells + sc->size_cells);
+ if (sc->nranges == 0)
+ return (0);
+
+ sc->ranges = malloc(sc->nranges * sizeof(sc->ranges[0]),
+ M_DEVBUF, M_WAITOK);
+ base_ranges = malloc(nbase_ranges, M_DEVBUF, M_WAITOK);
+ OF_getencprop(node, "ranges", base_ranges, nbase_ranges);
+
+ for (i = 0, j = 0; i < sc->nranges; i++) {
+ sc->ranges[i].bus = 0;
+ for (k = 0; k < sc->addr_cells; k++) {
+ sc->ranges[i].bus <<= 32;
+ sc->ranges[i].bus |= base_ranges[j++];
+ }
+ sc->ranges[i].host = 0;
+ for (k = 0; k < host_address_cells; k++) {
+ sc->ranges[i].host <<= 32;
+ sc->ranges[i].host |= base_ranges[j++];
+ }
+ sc->ranges[i].size = 0;
+ for (k = 0; k < sc->size_cells; k++) {
+ sc->ranges[i].size <<= 32;
+ sc->ranges[i].size |= base_ranges[j++];
+ }
+ }
+
+ free(base_ranges, M_DEVBUF);
+ return (sc->nranges);
+}
+
+static const struct ofw_bus_devinfo *
+mv_pcib_ctrl_get_devinfo(device_t bus __unused, device_t child)
+{
+ struct mv_pcib_ctrl_devinfo *di;
+
+ di = device_get_ivars(child);
+ return (&di->di_dinfo);
+}
diff --git a/sys/arm/mv/mv_spi.c b/sys/arm/mv/mv_spi.c
new file mode 100644
index 000000000000..256c8ff9161c
--- /dev/null
+++ b/sys/arm/mv/mv_spi.c
@@ -0,0 +1,408 @@
+/*-
+ * Copyright (c) 2017-2018, Rubicon Communications, LLC (Netgate)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/intr.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/spibus/spi.h>
+#include <dev/spibus/spibusvar.h>
+
+#include <arm/mv/mvvar.h>
+
+#include "spibus_if.h"
+
+struct mv_spi_softc {
+ device_t sc_dev;
+ struct mtx sc_mtx;
+ struct resource *sc_mem_res;
+ struct resource *sc_irq_res;
+ struct spi_command *sc_cmd;
+ bus_space_tag_t sc_bst;
+ bus_space_handle_t sc_bsh;
+ uint32_t sc_len;
+ uint32_t sc_read;
+ uint32_t sc_flags;
+ uint32_t sc_written;
+ void *sc_intrhand;
+};
+
+#define MV_SPI_BUSY 0x1
+#define MV_SPI_WRITE(_sc, _off, _val) \
+ bus_space_write_4((_sc)->sc_bst, (_sc)->sc_bsh, (_off), (_val))
+#define MV_SPI_READ(_sc, _off) \
+ bus_space_read_4((_sc)->sc_bst, (_sc)->sc_bsh, (_off))
+#define MV_SPI_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define MV_SPI_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+
+#define MV_SPI_CONTROL 0
+#define MV_SPI_CTRL_CS_MASK 7
+#define MV_SPI_CTRL_CS_SHIFT 2
+#define MV_SPI_CTRL_SMEMREADY (1 << 1)
+#define MV_SPI_CTRL_CS_ACTIVE (1 << 0)
+#define MV_SPI_CONF 0x4
+#define MV_SPI_CONF_MODE_SHIFT 12
+#define MV_SPI_CONF_MODE_MASK (3 << MV_SPI_CONF_MODE_SHIFT)
+#define MV_SPI_CONF_BYTELEN (1 << 5)
+#define MV_SPI_CONF_CLOCK_SPR_MASK 0xf
+#define MV_SPI_CONF_CLOCK_SPPR_MASK 1
+#define MV_SPI_CONF_CLOCK_SPPR_SHIFT 4
+#define MV_SPI_CONF_CLOCK_SPPRHI_MASK 3
+#define MV_SPI_CONF_CLOCK_SPPRHI_SHIFT 6
+#define MV_SPI_CONF_CLOCK_MASK \
+ ((MV_SPI_CONF_CLOCK_SPPRHI_MASK << MV_SPI_CONF_CLOCK_SPPRHI_SHIFT) | \
+ (MV_SPI_CONF_CLOCK_SPPR_MASK << MV_SPI_CONF_CLOCK_SPPR_SHIFT) | \
+ MV_SPI_CONF_CLOCK_SPR_MASK)
+#define MV_SPI_DATAOUT 0x8
+#define MV_SPI_DATAIN 0xc
+#define MV_SPI_INTR_STAT 0x10
+#define MV_SPI_INTR_MASK 0x14
+#define MV_SPI_INTR_SMEMREADY (1 << 0)
+
+static struct ofw_compat_data compat_data[] = {
+ {"marvell,armada-380-spi", 1},
+ {NULL, 0}
+};
+
+static void mv_spi_intr(void *);
+
+static int
+mv_spi_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Marvell SPI controller");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+mv_spi_attach(device_t dev)
+{
+ struct mv_spi_softc *sc;
+ int rid;
+ uint32_t reg;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+
+ rid = 0;
+ sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (!sc->sc_mem_res) {
+ device_printf(dev, "cannot allocate memory window\n");
+ return (ENXIO);
+ }
+
+ sc->sc_bst = rman_get_bustag(sc->sc_mem_res);
+ sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res);
+
+ rid = 0;
+ sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (!sc->sc_irq_res) {
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+ device_printf(dev, "cannot allocate interrupt\n");
+ return (ENXIO);
+ }
+
+ /* Deactivate the bus - just in case... */
+ reg = MV_SPI_READ(sc, MV_SPI_CONTROL);
+ MV_SPI_WRITE(sc, MV_SPI_CONTROL, reg & ~MV_SPI_CTRL_CS_ACTIVE);
+
+ /* Disable the two bytes FIFO. */
+ reg = MV_SPI_READ(sc, MV_SPI_CONF);
+ MV_SPI_WRITE(sc, MV_SPI_CONF, reg & ~MV_SPI_CONF_BYTELEN);
+
+ /* Clear and disable interrupts. */
+ MV_SPI_WRITE(sc, MV_SPI_INTR_MASK, 0);
+ MV_SPI_WRITE(sc, MV_SPI_INTR_STAT, 0);
+
+ /* Hook up our interrupt handler. */
+ if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+ NULL, mv_spi_intr, sc, &sc->sc_intrhand)) {
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+ device_printf(dev, "cannot setup the interrupt handler\n");
+ return (ENXIO);
+ }
+
+ mtx_init(&sc->sc_mtx, "mv_spi", NULL, MTX_DEF);
+
+ device_add_child(dev, "spibus", -1);
+
+ /* Probe and attach the spibus when interrupts are available. */
+ return (bus_delayed_attach_children(dev));
+}
+
+static int
+mv_spi_detach(device_t dev)
+{
+ struct mv_spi_softc *sc;
+
+ bus_generic_detach(dev);
+
+ sc = device_get_softc(dev);
+ mtx_destroy(&sc->sc_mtx);
+ if (sc->sc_intrhand)
+ bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand);
+ if (sc->sc_irq_res)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
+ if (sc->sc_mem_res)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+
+ return (0);
+}
+
+static __inline void
+mv_spi_rx_byte(struct mv_spi_softc *sc)
+{
+ struct spi_command *cmd;
+ uint32_t read;
+ uint8_t *p;
+
+ cmd = sc->sc_cmd;
+ p = (uint8_t *)cmd->rx_cmd;
+ read = sc->sc_read++;
+ if (read >= cmd->rx_cmd_sz) {
+ p = (uint8_t *)cmd->rx_data;
+ read -= cmd->rx_cmd_sz;
+ }
+ p[read] = MV_SPI_READ(sc, MV_SPI_DATAIN) & 0xff;
+}
+
+static __inline void
+mv_spi_tx_byte(struct mv_spi_softc *sc)
+{
+ struct spi_command *cmd;
+ uint32_t written;
+ uint8_t *p;
+
+ cmd = sc->sc_cmd;
+ p = (uint8_t *)cmd->tx_cmd;
+ written = sc->sc_written++;
+ if (written >= cmd->tx_cmd_sz) {
+ p = (uint8_t *)cmd->tx_data;
+ written -= cmd->tx_cmd_sz;
+ }
+ MV_SPI_WRITE(sc, MV_SPI_DATAOUT, p[written]);
+}
+
+static void
+mv_spi_intr(void *arg)
+{
+ struct mv_spi_softc *sc;
+
+ sc = (struct mv_spi_softc *)arg;
+ MV_SPI_LOCK(sc);
+
+ /* Filter stray interrupts. */
+ if ((sc->sc_flags & MV_SPI_BUSY) == 0) {
+ MV_SPI_UNLOCK(sc);
+ return;
+ }
+
+ /* RX */
+ mv_spi_rx_byte(sc);
+
+ /* TX */
+ mv_spi_tx_byte(sc);
+
+ /* Check for end of transfer. */
+ if (sc->sc_written == sc->sc_len && sc->sc_read == sc->sc_len)
+ wakeup(sc->sc_dev);
+
+ MV_SPI_UNLOCK(sc);
+}
+
+static int
+mv_spi_psc_calc(uint32_t clock, uint32_t *spr, uint32_t *sppr)
+{
+ uint32_t divider, tclk;
+
+ tclk = get_tclk_armada38x();
+ for (*spr = 2; *spr <= 15; (*spr)++) {
+ for (*sppr = 0; *sppr <= 7; (*sppr)++) {
+ divider = *spr * (1 << *sppr);
+ if (tclk / divider <= clock)
+ return (0);
+ }
+ }
+
+ return (EINVAL);
+}
+
+static int
+mv_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
+{
+ struct mv_spi_softc *sc;
+ uint32_t clock, cs, mode, reg, spr, sppr;
+ int resid, timeout;
+
+ KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz,
+ ("TX/RX command sizes should be equal"));
+ KASSERT(cmd->tx_data_sz == cmd->rx_data_sz,
+ ("TX/RX data sizes should be equal"));
+
+ /* Get the proper chip select, mode and clock for this transfer. */
+ spibus_get_cs(child, &cs);
+ cs &= ~SPIBUS_CS_HIGH;
+ spibus_get_mode(child, &mode);
+ if (mode > 3) {
+ device_printf(dev,
+ "Invalid mode %u requested by %s\n", mode,
+ device_get_nameunit(child));
+ return (EINVAL);
+ }
+ spibus_get_clock(child, &clock);
+ if (clock == 0 || mv_spi_psc_calc(clock, &spr, &sppr) != 0) {
+ device_printf(dev,
+ "Invalid clock %uHz requested by %s\n", clock,
+ device_get_nameunit(child));
+ return (EINVAL);
+ }
+
+ sc = device_get_softc(dev);
+ MV_SPI_LOCK(sc);
+
+ /* Wait until the controller is free. */
+ while (sc->sc_flags & MV_SPI_BUSY)
+ mtx_sleep(dev, &sc->sc_mtx, 0, "mv_spi", 0);
+
+ /* Now we have control over SPI controller. */
+ sc->sc_flags = MV_SPI_BUSY;
+
+ /* Save a pointer to the SPI command. */
+ sc->sc_cmd = cmd;
+ sc->sc_read = 0;
+ sc->sc_written = 0;
+ sc->sc_len = cmd->tx_cmd_sz + cmd->tx_data_sz;
+
+ /* Set SPI Mode and Clock. */
+ reg = MV_SPI_READ(sc, MV_SPI_CONF);
+ reg &= ~(MV_SPI_CONF_MODE_MASK | MV_SPI_CONF_CLOCK_MASK);
+ reg |= mode << MV_SPI_CONF_MODE_SHIFT;
+ reg |= spr & MV_SPI_CONF_CLOCK_SPR_MASK;
+ reg |= (sppr & MV_SPI_CONF_CLOCK_SPPR_MASK) <<
+ MV_SPI_CONF_CLOCK_SPPR_SHIFT;
+ reg |= (sppr & MV_SPI_CONF_CLOCK_SPPRHI_MASK) <<
+ MV_SPI_CONF_CLOCK_SPPRHI_SHIFT;
+ MV_SPI_WRITE(sc, MV_SPI_CONTROL, reg);
+
+ /* Set CS number and assert CS. */
+ reg = (cs & MV_SPI_CTRL_CS_MASK) << MV_SPI_CTRL_CS_SHIFT;
+ MV_SPI_WRITE(sc, MV_SPI_CONTROL, reg);
+ reg = MV_SPI_READ(sc, MV_SPI_CONTROL);
+ MV_SPI_WRITE(sc, MV_SPI_CONTROL, reg | MV_SPI_CTRL_CS_ACTIVE);
+
+ while ((resid = sc->sc_len - sc->sc_written) > 0) {
+ MV_SPI_WRITE(sc, MV_SPI_INTR_STAT, 0);
+
+ /*
+ * Write to start the transmission and read the byte
+ * back when ready.
+ */
+ mv_spi_tx_byte(sc);
+ timeout = 1000;
+ while (--timeout > 0) {
+ reg = MV_SPI_READ(sc, MV_SPI_CONTROL);
+ if (reg & MV_SPI_CTRL_SMEMREADY)
+ break;
+ DELAY(1);
+ }
+ if (timeout == 0)
+ break;
+ mv_spi_rx_byte(sc);
+ }
+
+ /* Stop the controller. */
+ reg = MV_SPI_READ(sc, MV_SPI_CONTROL);
+ MV_SPI_WRITE(sc, MV_SPI_CONTROL, reg & ~MV_SPI_CTRL_CS_ACTIVE);
+ MV_SPI_WRITE(sc, MV_SPI_INTR_MASK, 0);
+ MV_SPI_WRITE(sc, MV_SPI_INTR_STAT, 0);
+
+ /* Release the controller and wakeup the next thread waiting for it. */
+ sc->sc_flags = 0;
+ wakeup_one(dev);
+ MV_SPI_UNLOCK(sc);
+
+ /*
+ * Check for transfer timeout. The SPI controller doesn't
+ * return errors.
+ */
+ return ((timeout == 0) ? EIO : 0);
+}
+
+static phandle_t
+mv_spi_get_node(device_t bus, device_t dev)
+{
+
+ return (ofw_bus_get_node(bus));
+}
+
+static device_method_t mv_spi_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, mv_spi_probe),
+ DEVMETHOD(device_attach, mv_spi_attach),
+ DEVMETHOD(device_detach, mv_spi_detach),
+
+ /* SPI interface */
+ DEVMETHOD(spibus_transfer, mv_spi_transfer),
+
+ /* ofw_bus interface */
+ DEVMETHOD(ofw_bus_get_node, mv_spi_get_node),
+
+ DEVMETHOD_END
+};
+
+static devclass_t mv_spi_devclass;
+
+static driver_t mv_spi_driver = {
+ "spi",
+ mv_spi_methods,
+ sizeof(struct mv_spi_softc),
+};
+
+DRIVER_MODULE(mv_spi, simplebus, mv_spi_driver, mv_spi_devclass, 0, 0);
diff --git a/sys/arm/mv/mv_thermal.c b/sys/arm/mv/mv_thermal.c
new file mode 100644
index 000000000000..ed47f25a40c1
--- /dev/null
+++ b/sys/arm/mv/mv_thermal.c
@@ -0,0 +1,381 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2018 Rubicon Communications, LLC (Netgate)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/intr.h>
+#include <dev/extres/syscon/syscon.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "syscon_if.h"
+
+#define CONTROL0 0 /* Offset in config->regs[] array */
+#define CONTROL0_TSEN_START (1 << 0)
+#define CONTROL0_TSEN_RESET (1 << 1)
+#define CONTROL0_TSEN_EN (1 << 2)
+#define CONTROL0_CHANNEL_SHIFT 13
+#define CONTROL0_CHANNEL_MASK 0xF
+#define CONTROL0_OSR_SHIFT 24
+#define CONTROL0_OSR_MAX 3 /* OSR = 512 * 4uS = ~2mS */
+#define CONTROL0_MODE_SHIFT 30
+#define CONTROL0_MODE_EXTERNAL 0x2
+#define CONTROL0_MODE_MASK 0x3
+
+#define CONTROL1 1 /* Offset in config->regs[] array */
+/* This doesn't seems to work */
+#define CONTROL1_TSEN_SENS_SHIFT 21
+#define CONTROL1_TSEN_SENS_MASK 0x7
+
+#define STATUS 2 /* Offset in config->regs[] array */
+#define STATUS_TEMP_MASK 0x3FF
+
+enum mv_thermal_type {
+ MV_AP806 = 1,
+ MV_CP110,
+};
+
+struct mv_thermal_config {
+ enum mv_thermal_type type;
+ int regs[3];
+ int ncpus;
+ int64_t calib_mul;
+ int64_t calib_add;
+ int64_t calib_div;
+ uint32_t valid_mask;
+ bool signed_value;
+};
+
+struct mv_thermal_softc {
+ device_t dev;
+ struct syscon *syscon;
+ struct mtx mtx;
+
+ struct mv_thermal_config *config;
+ int cur_sensor;
+};
+
+static struct mv_thermal_config mv_ap806_config = {
+ .type = MV_AP806,
+ .regs = {0x84, 0x88, 0x8C},
+ .ncpus = 4,
+ .calib_mul = 423,
+ .calib_add = -150000,
+ .calib_div = 100,
+ .valid_mask = (1 << 16),
+ .signed_value = true,
+};
+
+static struct mv_thermal_config mv_cp110_config = {
+ .type = MV_CP110,
+ .regs = {0x70, 0x74, 0x78},
+ .calib_mul = 2000096,
+ .calib_add = 1172499100,
+ .calib_div = 420100,
+ .valid_mask = (1 << 10),
+ .signed_value = false,
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"marvell,armada-ap806-thermal", (uintptr_t) &mv_ap806_config},
+ {"marvell,armada-cp110-thermal", (uintptr_t) &mv_cp110_config},
+ {NULL, 0}
+};
+
+#define RD4(sc, reg) \
+ SYSCON_READ_4((sc)->syscon, sc->config->regs[reg])
+#define WR4(sc, reg, val) \
+ SYSCON_WRITE_4((sc)->syscon, sc->config->regs[reg], (val))
+
+static inline int32_t sign_extend(uint32_t value, int index)
+{
+ uint8_t shift;
+
+ shift = 31 - index;
+ return ((int32_t)(value << shift) >> shift);
+}
+
+static int
+mv_thermal_wait_sensor(struct mv_thermal_softc *sc)
+{
+ uint32_t reg;
+ uint32_t timeout;
+
+ timeout = 100000;
+ while (--timeout > 0) {
+ reg = RD4(sc, STATUS);
+ if ((reg & sc->config->valid_mask) == sc->config->valid_mask)
+ break;
+ DELAY(100);
+ }
+ if (timeout == 0) {
+ return (ETIMEDOUT);
+ }
+
+ return (0);
+}
+
+static int
+mv_thermal_select_sensor(struct mv_thermal_softc *sc, int sensor)
+{
+ uint32_t reg;
+
+ if (sc->cur_sensor == sensor)
+ return (0);
+
+ /* Stop the current reading and reset the module */
+ reg = RD4(sc, CONTROL0);
+ reg &= ~(CONTROL0_TSEN_START | CONTROL0_TSEN_EN);
+ WR4(sc, CONTROL0, reg);
+
+ /* Switch to the selected sensor */
+ /*
+ * NOTE : Datasheet says to use CONTROL1 for selecting
+ * but when doing so the sensors >0 are never ready
+ * Do what Linux does using undocumented bits in CONTROL0
+ */
+ /* This reset automatically to the sensor 0 */
+ reg &= ~(CONTROL0_MODE_MASK << CONTROL0_MODE_SHIFT);
+ if (sensor) {
+ /* Select external sensor */
+ reg |= CONTROL0_MODE_EXTERNAL << CONTROL0_MODE_SHIFT;
+ reg &= ~(CONTROL0_CHANNEL_MASK << CONTROL0_CHANNEL_SHIFT);
+ reg |= (sensor - 1) << CONTROL0_CHANNEL_SHIFT;
+ }
+ WR4(sc, CONTROL0, reg);
+ sc->cur_sensor = sensor;
+
+ /* Start the reading */
+ reg = RD4(sc, CONTROL0);
+ reg |= CONTROL0_TSEN_START | CONTROL0_TSEN_EN;
+ WR4(sc, CONTROL0, reg);
+
+ return (mv_thermal_wait_sensor(sc));
+}
+
+static int
+mv_thermal_read_sensor(struct mv_thermal_softc *sc, int sensor, int *temp)
+{
+ uint32_t reg;
+ int64_t sample, rv;
+
+ rv = mv_thermal_select_sensor(sc, sensor);
+ if (rv != 0)
+ return (rv);
+
+ reg = RD4(sc, STATUS) & STATUS_TEMP_MASK;
+
+ if (sc->config->signed_value)
+ sample = sign_extend(reg, fls(STATUS_TEMP_MASK) - 1);
+ else
+ sample = reg;
+
+ *temp = ((sample * sc->config->calib_mul) - sc->config->calib_add) /
+ sc->config->calib_div;
+
+ return (0);
+}
+
+static int
+ap806_init(struct mv_thermal_softc *sc)
+{
+ uint32_t reg;
+
+ /* Start the temp capture/conversion */
+ reg = RD4(sc, CONTROL0);
+ reg &= ~CONTROL0_TSEN_RESET;
+ reg |= CONTROL0_TSEN_START | CONTROL0_TSEN_EN;
+
+ /* Sample every ~2ms */
+ reg |= CONTROL0_OSR_MAX << CONTROL0_OSR_SHIFT;
+
+ WR4(sc, CONTROL0, reg);
+
+ /* Since we just started the module wait for the sensor to be ready */
+ mv_thermal_wait_sensor(sc);
+
+ return (0);
+}
+
+static int
+cp110_init(struct mv_thermal_softc *sc)
+{
+ uint32_t reg;
+
+ reg = RD4(sc, CONTROL1);
+ reg &= (1 << 7);
+ reg |= (1 << 8);
+ WR4(sc, CONTROL1, reg);
+
+ /* Sample every ~2ms */
+ reg = RD4(sc, CONTROL0);
+ reg |= CONTROL0_OSR_MAX << CONTROL0_OSR_SHIFT;
+ WR4(sc, CONTROL0, reg);
+
+ return (0);
+}
+
+static int
+mv_thermal_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ struct mv_thermal_softc *sc;
+ device_t dev = arg1;
+ int sensor = arg2;
+ int val = 0;
+
+ sc = device_get_softc(dev);
+ mtx_lock(&(sc)->mtx);
+
+ if (mv_thermal_read_sensor(sc, sensor, &val) == 0) {
+ /* Convert to Kelvin */
+ val = val + 2732;
+ } else {
+ device_printf(dev, "Timeout waiting for sensor\n");
+ }
+
+ mtx_unlock(&(sc)->mtx);
+ return sysctl_handle_opaque(oidp, &val, sizeof(val), req);
+}
+
+static int
+mv_thermal_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Marvell Thermal Sensor Controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+mv_thermal_attach(device_t dev)
+{
+ struct mv_thermal_softc *sc;
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid_list *oid;
+ phandle_t node;
+ char name[255];
+ char desc[255];
+ int i;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ node = ofw_bus_get_node(dev);
+
+ sc->config = (struct mv_thermal_config *)
+ ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+
+ mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ if (SYSCON_GET_HANDLE(sc->dev, &sc->syscon) != 0 ||
+ sc->syscon == NULL) {
+ device_printf(dev, "cannot get syscon for device\n");
+ return (ENXIO);
+ }
+
+ sc->cur_sensor = -1;
+ switch (sc->config->type) {
+ case MV_AP806:
+ ap806_init(sc);
+ break;
+ case MV_CP110:
+ cp110_init(sc);
+ break;
+ }
+
+ ctx = device_get_sysctl_ctx(dev);
+ oid = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
+ /* There is always at least one sensor */
+ SYSCTL_ADD_PROC(ctx, oid, OID_AUTO, "internal",
+ CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
+ dev, 0, mv_thermal_sysctl,
+ "IK",
+ "Internal Temperature");
+
+ for (i = 0; i < sc->config->ncpus; i++) {
+ snprintf(name, sizeof(name), "cpu%d", i);
+ snprintf(desc, sizeof(desc), "CPU%d Temperature", i);
+ SYSCTL_ADD_PROC(ctx, oid, OID_AUTO, name,
+ CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
+ dev, i + 1, mv_thermal_sysctl,
+ "IK",
+ desc);
+ }
+
+ return (0);
+}
+
+static int
+mv_thermal_detach(device_t dev)
+{
+ struct mv_thermal_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ return (0);
+}
+
+static device_method_t mv_thermal_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, mv_thermal_probe),
+ DEVMETHOD(device_attach, mv_thermal_attach),
+ DEVMETHOD(device_detach, mv_thermal_detach),
+
+ DEVMETHOD_END
+};
+
+static devclass_t mv_thermal_devclass;
+
+static driver_t mv_thermal_driver = {
+ "mv_thermal",
+ mv_thermal_methods,
+ sizeof(struct mv_thermal_softc),
+};
+
+DRIVER_MODULE(mv_thermal, simplebus, mv_thermal_driver,
+ mv_thermal_devclass, 0, 0);
diff --git a/sys/arm/mv/mvebu_gpio.c b/sys/arm/mv/mvebu_gpio.c
new file mode 100644
index 000000000000..afc3f177ade3
--- /dev/null
+++ b/sys/arm/mv/mvebu_gpio.c
@@ -0,0 +1,862 @@
+/*-
+ * Copyright (c) 2020 Michal Meloun <mmel@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * ARMADA 8040 GPIO driver.
+ */
+#include "opt_platform.h"
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/gpio.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+#include <machine/resource.h>
+
+#include <dev/extres/syscon/syscon.h>
+
+#include <dev/gpio/gpiobusvar.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "pic_if.h"
+#include "syscon_if.h"
+
+#define GPIO_LOCK(_sc) mtx_lock(&(_sc)->mtx)
+#define GPIO_UNLOCK(_sc) mtx_unlock(&(_sc)->mtx)
+#define GPIO_LOCK_INIT(_sc) mtx_init(&_sc->mtx, \
+ device_get_nameunit(_sc->dev), "mvebu_gpio", MTX_DEF)
+#define GPIO_LOCK_DESTROY(_sc) mtx_destroy(&_sc->mtx);
+#define GPIO_ASSERT_LOCKED(_sc) mtx_assert(&_sc->mtx, MA_OWNED);
+#define GPIO_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED);
+
+#define GPIO_DATA_OUT 0x00
+#define GPIO_CONTROL 0x04
+#define GPIO_BLINK_ENA 0x08
+#define GPIO_DATA_IN_POL 0x0C
+#define GPIO_DATA_IN 0x10
+#define GPIO_INT_CAUSE 0x14
+#define GPIO_INT_MASK 0x18
+#define GPIO_INT_LEVEL_MASK 0x1C
+#define GPIO_CONTROL_SET 0x28
+#define GPIO_CONTROL_CLR 0x2C
+#define GPIO_DATA_SET 0x30
+#define GPIO_DATA_CLR 0x34
+
+#define GPIO_BIT(_p) ((_p) % 32)
+#define GPIO_REGNUM(_p) ((_p) / 32)
+
+#define MV_GPIO_MAX_NIRQS 4
+#define MV_GPIO_MAX_NPINS 32
+
+#define RD4(sc, reg) SYSCON_READ_4((sc)->syscon, (reg))
+#define WR4(sc, reg, val) SYSCON_WRITE_4((sc)->syscon, (reg), (val))
+
+struct mvebu_gpio_irqsrc {
+ struct intr_irqsrc isrc;
+ u_int irq;
+ bool is_level;
+ bool is_inverted;
+};
+
+struct mvebu_gpio_softc;
+struct mvebu_gpio_irq_cookie {
+ struct mvebu_gpio_softc *sc;
+ int bank_num;
+};
+
+struct mvebu_gpio_softc {
+ device_t dev;
+ device_t busdev;
+ struct mtx mtx;
+ struct syscon *syscon;
+ uint32_t offset;
+ struct resource *irq_res[MV_GPIO_MAX_NIRQS];
+ void *irq_ih[MV_GPIO_MAX_NIRQS];
+ struct mvebu_gpio_irq_cookie irq_cookies[MV_GPIO_MAX_NIRQS];
+ int gpio_npins;
+ struct gpio_pin gpio_pins[MV_GPIO_MAX_NPINS];
+ struct mvebu_gpio_irqsrc *isrcs;
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"marvell,armada-8k-gpio", 1},
+ {NULL, 0}
+};
+
+/* --------------------------------------------------------------------------
+ *
+ * GPIO
+ *
+ */
+static inline void
+gpio_write(struct mvebu_gpio_softc *sc, bus_size_t reg,
+ struct gpio_pin *pin, uint32_t val)
+{
+ uint32_t tmp;
+ int bit;
+
+ bit = GPIO_BIT(pin->gp_pin);
+ tmp = 0x100 << bit; /* mask */
+ tmp |= (val & 1) << bit; /* value */
+ SYSCON_WRITE_4(sc->syscon, sc->offset + GPIO_REGNUM(pin->gp_pin) + reg,
+ tmp);
+}
+
+static inline uint32_t
+gpio_read(struct mvebu_gpio_softc *sc, bus_size_t reg, struct gpio_pin *pin)
+{
+ int bit;
+ uint32_t val;
+
+ bit = GPIO_BIT(pin->gp_pin);
+ val = SYSCON_READ_4(sc->syscon,
+ sc->offset + GPIO_REGNUM(pin->gp_pin) + reg);
+ return (val >> bit) & 1;
+}
+
+static void
+mvebu_gpio_pin_configure(struct mvebu_gpio_softc *sc, struct gpio_pin *pin,
+ unsigned int flags)
+{
+
+ if ((flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) == 0)
+ return;
+
+ /* Manage input/output */
+ pin->gp_flags &= ~(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT);
+ if (flags & GPIO_PIN_OUTPUT) {
+ pin->gp_flags |= GPIO_PIN_OUTPUT;
+ gpio_write(sc, GPIO_CONTROL_SET, pin, 1);
+ } else {
+ pin->gp_flags |= GPIO_PIN_INPUT;
+ gpio_write(sc, GPIO_CONTROL_CLR, pin, 1);
+ }
+}
+
+static device_t
+mvebu_gpio_get_bus(device_t dev)
+{
+ struct mvebu_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+ return (sc->busdev);
+}
+
+static int
+mvebu_gpio_pin_max(device_t dev, int *maxpin)
+{
+ struct mvebu_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+ *maxpin = sc->gpio_npins - 1;
+ return (0);
+}
+
+static int
+mvebu_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
+{
+ struct mvebu_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->gpio_npins)
+ return (EINVAL);
+
+ *caps = sc->gpio_pins[pin].gp_caps;
+
+ return (0);
+}
+
+static int
+mvebu_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
+{
+ struct mvebu_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->gpio_npins)
+ return (EINVAL);
+
+ *flags = sc->gpio_pins[pin].gp_flags;
+
+ return (0);
+}
+
+static int
+mvebu_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
+{
+ struct mvebu_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->gpio_npins)
+ return (EINVAL);
+
+ memcpy(name, sc->gpio_pins[pin].gp_name, GPIOMAXNAME);
+
+ return (0);
+}
+
+static int
+mvebu_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
+{
+ struct mvebu_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->gpio_npins)
+ return (EINVAL);
+
+ mvebu_gpio_pin_configure(sc, &sc->gpio_pins[pin], flags);
+
+ return (0);
+}
+
+static int
+mvebu_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
+{
+ struct mvebu_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->gpio_npins)
+ return (EINVAL);
+
+ if (value != 0)
+ gpio_write(sc, GPIO_DATA_SET, &sc->gpio_pins[pin], 1);
+ else
+ gpio_write(sc, GPIO_DATA_CLR, &sc->gpio_pins[pin], 1);
+
+ return (0);
+}
+
+static int
+mvebu_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
+{
+ struct mvebu_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->gpio_npins)
+ return (EINVAL);
+
+ GPIO_LOCK(sc);
+ *val = gpio_read(sc, GPIO_DATA_IN, &sc->gpio_pins[pin]);
+ *val ^= gpio_read(sc, GPIO_DATA_IN_POL, &sc->gpio_pins[pin]);
+ GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+mvebu_gpio_pin_toggle(device_t dev, uint32_t pin)
+{
+ struct mvebu_gpio_softc *sc;
+ uint32_t val;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->gpio_npins)
+ return (EINVAL);
+
+ GPIO_LOCK(sc);
+ mvebu_gpio_pin_get(sc->dev, pin, &val);
+ if (val != 0)
+ gpio_write(sc, GPIO_DATA_CLR, &sc->gpio_pins[pin], 1);
+ else
+ gpio_write(sc, GPIO_DATA_SET, &sc->gpio_pins[pin], 1);
+ GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+/* --------------------------------------------------------------------------
+ *
+ * Interrupts
+ *
+ */
+static inline void
+intr_modify(struct mvebu_gpio_softc *sc, bus_addr_t reg,
+ struct mvebu_gpio_irqsrc *mgi, uint32_t val, uint32_t mask)
+{
+ int bit;
+
+ bit = GPIO_BIT(mgi->irq);
+ GPIO_LOCK(sc);
+ val = SYSCON_MODIFY_4(sc->syscon,
+ sc->offset + GPIO_REGNUM(mgi->irq) + reg, val, mask);
+ GPIO_UNLOCK(sc);
+}
+
+static inline void
+mvebu_gpio_isrc_mask(struct mvebu_gpio_softc *sc,
+ struct mvebu_gpio_irqsrc *mgi, uint32_t val)
+{
+
+ if (mgi->is_level)
+ intr_modify(sc, GPIO_INT_LEVEL_MASK, mgi, val, 1);
+ else
+ intr_modify(sc, GPIO_INT_MASK, mgi, val, 1);
+}
+
+static inline void
+mvebu_gpio_isrc_eoi(struct mvebu_gpio_softc *sc,
+ struct mvebu_gpio_irqsrc *mgi)
+{
+
+ if (!mgi->is_level)
+ intr_modify(sc, GPIO_INT_CAUSE, mgi, 1, 1);
+}
+
+static int
+mvebu_gpio_pic_attach(struct mvebu_gpio_softc *sc)
+{
+ int rv;
+ uint32_t irq;
+ const char *name;
+
+ sc->isrcs = malloc(sizeof(*sc->isrcs) * sc->gpio_npins, M_DEVBUF,
+ M_WAITOK | M_ZERO);
+
+ name = device_get_nameunit(sc->dev);
+ for (irq = 0; irq < sc->gpio_npins; irq++) {
+ sc->isrcs[irq].irq = irq;
+ sc->isrcs[irq].is_level = false;
+ sc->isrcs[irq].is_inverted = false;
+ rv = intr_isrc_register(&sc->isrcs[irq].isrc,
+ sc->dev, 0, "%s,%u", name, irq);
+ if (rv != 0)
+ return (rv); /* XXX deregister ISRCs */
+ }
+ if (intr_pic_register(sc->dev,
+ OF_xref_from_node(ofw_bus_get_node(sc->dev))) == NULL)
+ return (ENXIO);
+
+ return (0);
+}
+
+static int
+mvebu_gpio_pic_detach(struct mvebu_gpio_softc *sc)
+{
+
+ /*
+ * There has not been established any procedure yet
+ * how to detach PIC from living system correctly.
+ */
+ device_printf(sc->dev, "%s: not implemented yet\n", __func__);
+ return (EBUSY);
+}
+
+static void
+mvebu_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct mvebu_gpio_softc *sc;
+ struct mvebu_gpio_irqsrc *mgi;
+
+ sc = device_get_softc(dev);
+ mgi = (struct mvebu_gpio_irqsrc *)isrc;
+ mvebu_gpio_isrc_mask(sc, mgi, 0);
+}
+
+static void
+mvebu_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct mvebu_gpio_softc *sc;
+ struct mvebu_gpio_irqsrc *mgi;
+
+ sc = device_get_softc(dev);
+ mgi = (struct mvebu_gpio_irqsrc *)isrc;
+ mvebu_gpio_isrc_mask(sc, mgi, 1);
+}
+
+static int
+mvebu_gpio_pic_map_fdt(struct mvebu_gpio_softc *sc, u_int ncells,
+ pcell_t *cells, u_int *irqp, bool *invertedp, bool *levelp)
+{
+ bool inverted, level;
+
+ /*
+ * The first cell is the interrupt number.
+ * The second cell is used to specify flags:
+ * bits[3:0] trigger type and level flags:
+ * 1 = low-to-high edge triggered.
+ * 2 = high-to-low edge triggered.
+ * 4 = active high level-sensitive.
+ * 8 = active low level-sensitive.
+ */
+ if (ncells != 2 || cells[0] >= sc->gpio_npins)
+ return (EINVAL);
+
+ switch (cells[1]) {
+ case 1:
+ inverted = false;
+ level = false;
+ break;
+ case 2:
+ inverted = true;
+ level = false;
+ break;
+ case 4:
+ inverted = false;
+ level = true;
+ break;
+ case 8:
+ inverted = true;
+ level = true;
+ break;
+ default:
+ return (EINVAL);
+ }
+ *irqp = cells[0];
+ if (invertedp != NULL)
+ *invertedp = inverted;
+ if (levelp != NULL)
+ *levelp = level;
+ return (0);
+}
+
+static int
+mvebu_gpio_pic_map_gpio(struct mvebu_gpio_softc *sc, u_int gpio_pin_num,
+ u_int gpio_pin_flags, u_int intr_mode, u_int *irqp, bool *invertedp,
+ bool *levelp)
+{
+ bool inverted, level;
+
+ if (gpio_pin_num >= sc->gpio_npins)
+ return (EINVAL);
+
+ switch (intr_mode) {
+ case GPIO_INTR_LEVEL_LOW:
+ inverted = true;
+ level = true;
+ break;
+ case GPIO_INTR_LEVEL_HIGH:
+ inverted = false;
+ level = true;
+ break;
+ case GPIO_INTR_CONFORM:
+ case GPIO_INTR_EDGE_RISING:
+ inverted = false;
+ level = false;
+ break;
+ case GPIO_INTR_EDGE_FALLING:
+ inverted = true;
+ level = false;
+ break;
+ default:
+ return (EINVAL);
+ }
+ *irqp = gpio_pin_num;
+ if (invertedp != NULL)
+ *invertedp = inverted;
+ if (levelp != NULL)
+ *levelp = level;
+ return (0);
+}
+
+static int
+mvebu_gpio_pic_map_intr(device_t dev, struct intr_map_data *data,
+ struct intr_irqsrc **isrcp)
+{
+ int rv;
+ u_int irq;
+ struct mvebu_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (data->type == INTR_MAP_DATA_FDT) {
+ struct intr_map_data_fdt *daf;
+
+ daf = (struct intr_map_data_fdt *)data;
+ rv = mvebu_gpio_pic_map_fdt(sc, daf->ncells, daf->cells, &irq,
+ NULL, NULL);
+ } else if (data->type == INTR_MAP_DATA_GPIO) {
+ struct intr_map_data_gpio *dag;
+
+ dag = (struct intr_map_data_gpio *)data;
+ rv = mvebu_gpio_pic_map_gpio(sc, dag->gpio_pin_num,
+ dag->gpio_pin_flags, dag->gpio_intr_mode, &irq, NULL, NULL);
+ } else
+ return (ENOTSUP);
+
+ if (rv == 0)
+ *isrcp = &sc->isrcs[irq].isrc;
+ return (rv);
+}
+
+static void
+mvebu_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct mvebu_gpio_softc *sc;
+ struct mvebu_gpio_irqsrc *mgi;
+
+ sc = device_get_softc(dev);
+ mgi = (struct mvebu_gpio_irqsrc *)isrc;
+ if (mgi->is_level)
+ mvebu_gpio_isrc_eoi(sc, mgi);
+}
+
+static void
+mvebu_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct mvebu_gpio_softc *sc;
+ struct mvebu_gpio_irqsrc *mgi;
+
+ sc = device_get_softc(dev);
+ mgi = (struct mvebu_gpio_irqsrc *)isrc;
+ mvebu_gpio_isrc_mask(sc, mgi, 1);
+}
+
+static void
+mvebu_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct mvebu_gpio_softc *sc;
+ struct mvebu_gpio_irqsrc *mgi;
+
+ sc = device_get_softc(dev);
+ mgi = (struct mvebu_gpio_irqsrc *)isrc;
+
+ mvebu_gpio_isrc_mask(sc, mgi, 0);
+ if (mgi->is_level)
+ mvebu_gpio_isrc_eoi(sc, mgi);
+}
+
+static int
+mvebu_gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ u_int irq;
+ bool inverted, level;
+ int rv;
+ struct mvebu_gpio_softc *sc;
+ struct mvebu_gpio_irqsrc *mgi;
+
+ sc = device_get_softc(dev);
+ mgi = (struct mvebu_gpio_irqsrc *)isrc;
+
+ if (data == NULL)
+ return (ENOTSUP);
+
+ /* Get and check config for an interrupt. */
+ if (data->type == INTR_MAP_DATA_FDT) {
+ struct intr_map_data_fdt *daf;
+
+ daf = (struct intr_map_data_fdt *)data;
+ rv = mvebu_gpio_pic_map_fdt(sc, daf->ncells, daf->cells, &irq,
+ &inverted, &level);
+ } else if (data->type == INTR_MAP_DATA_GPIO) {
+ struct intr_map_data_gpio *dag;
+
+ dag = (struct intr_map_data_gpio *)data;
+ rv = mvebu_gpio_pic_map_gpio(sc, dag->gpio_pin_num,
+ dag->gpio_pin_flags, dag->gpio_intr_mode, &irq,
+ &inverted, &level);
+ } else
+ return (ENOTSUP);
+
+ if (rv != 0)
+ return (EINVAL);
+
+ /*
+ * If this is a setup for another handler,
+ * only check that its configuration match.
+ */
+ if (isrc->isrc_handlers != 0)
+ return (
+ mgi->is_level == level && mgi->is_inverted == inverted ?
+ 0 : EINVAL);
+
+ mgi->is_level = level;
+ mgi->is_inverted = inverted;
+ intr_modify(sc, GPIO_DATA_IN_POL, mgi, inverted ? 1 : 0, 1);
+ mvebu_gpio_pic_enable_intr(dev, isrc);
+
+ return (0);
+}
+
+static int
+mvebu_gpio_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ struct mvebu_gpio_softc *sc;
+ struct mvebu_gpio_irqsrc *mgi;
+
+ sc = device_get_softc(dev);
+ mgi = (struct mvebu_gpio_irqsrc *)isrc;
+
+ if (isrc->isrc_handlers == 0)
+ mvebu_gpio_isrc_mask(sc, mgi, 0);
+ return (0);
+}
+
+/* --------------------------------------------------------------------------
+ *
+ * Bus
+ *
+ */
+
+static int
+mvebu_gpio_intr(void *arg)
+{
+ u_int i, lvl, edge;
+ struct mvebu_gpio_softc *sc;
+ struct trapframe *tf;
+ struct mvebu_gpio_irqsrc *mgi;
+ struct mvebu_gpio_irq_cookie *cookie;
+
+ cookie = (struct mvebu_gpio_irq_cookie *)arg;
+ sc = cookie->sc;
+ tf = curthread->td_intr_frame;
+
+ for (i = 0; i < sc->gpio_npins; i++) {
+ lvl = gpio_read(sc, GPIO_DATA_IN, &sc->gpio_pins[i]);
+ lvl &= gpio_read(sc, GPIO_INT_LEVEL_MASK, &sc->gpio_pins[i]);
+ edge = gpio_read(sc, GPIO_DATA_IN, &sc->gpio_pins[i]);
+ edge &= gpio_read(sc, GPIO_INT_LEVEL_MASK, &sc->gpio_pins[i]);
+ if (edge == 0 || lvl == 0)
+ continue;
+
+ mgi = &sc->isrcs[i];
+ if (!mgi->is_level)
+ mvebu_gpio_isrc_eoi(sc, mgi);
+ if (intr_isrc_dispatch(&mgi->isrc, tf) != 0) {
+ mvebu_gpio_isrc_mask(sc, mgi, 0);
+ if (mgi->is_level)
+ mvebu_gpio_isrc_eoi(sc, mgi);
+ device_printf(sc->dev,
+ "Stray irq %u disabled\n", mgi->irq);
+ }
+ }
+ return (FILTER_HANDLED);
+}
+
+static int
+mvebu_gpio_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Marvell Integrated GPIO Controller");
+ return (0);
+}
+
+static int
+mvebu_gpio_detach(device_t dev)
+{
+ struct mvebu_gpio_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+
+ KASSERT(mtx_initialized(&sc->mtx), ("gpio mutex not initialized"));
+
+ for (i = 0; i < MV_GPIO_MAX_NIRQS; i++) {
+ if (sc->irq_ih[i] != NULL)
+ bus_teardown_intr(dev, sc->irq_res[i], sc->irq_ih[i]);
+ }
+
+ if (sc->isrcs != NULL)
+ mvebu_gpio_pic_detach(sc);
+
+ gpiobus_detach_bus(dev);
+
+ for (i = 0; i < MV_GPIO_MAX_NIRQS; i++) {
+ if (sc->irq_res[i] != NULL)
+ bus_release_resource(dev, SYS_RES_IRQ, 0,
+ sc->irq_res[i]);
+ }
+ GPIO_LOCK_DESTROY(sc);
+
+ return(0);
+}
+
+static int
+mvebu_gpio_attach(device_t dev)
+{
+ struct mvebu_gpio_softc *sc;
+ phandle_t node;
+ struct gpio_pin *pin;
+ pcell_t pincnt;
+ int i, rv, rid;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ node = ofw_bus_get_node(dev);
+
+ GPIO_LOCK_INIT(sc);
+
+ pincnt = 0;
+ rv = OF_getencprop(node, "ngpios", &pincnt, sizeof(pcell_t));
+ if (rv < 0) {
+ device_printf(dev,
+ "ERROR: no pin-count or ngpios entry found!\n");
+ return (ENXIO);
+ }
+
+ sc->gpio_npins = MIN(pincnt, MV_GPIO_MAX_NPINS);
+ if (bootverbose)
+ device_printf(dev,
+ "%d pins available\n", sc->gpio_npins);
+
+ rv = OF_getencprop(node, "offset", &sc->offset, sizeof(sc->offset));
+ if (rv == -1) {
+ device_printf(dev, "ERROR: no 'offset' property found!\n");
+ return (ENXIO);
+ }
+
+ if (SYSCON_GET_HANDLE(sc->dev, &sc->syscon) != 0 ||
+ sc->syscon == NULL) {
+ device_printf(dev, "ERROR: cannot get syscon handle!\n");
+ return (ENXIO);
+ }
+
+ /* Allocate interrupts. */
+ for (i = 0; i < MV_GPIO_MAX_NIRQS; i++) {
+ sc->irq_cookies[i].sc = sc;
+ sc->irq_cookies[i].bank_num = i;
+ rid = i;
+ sc->irq_res[i] = bus_alloc_resource_any(dev, SYS_RES_IRQ,
+ &rid, RF_ACTIVE);
+ if (sc->irq_res[i] == NULL)
+ break;
+ if ((bus_setup_intr(dev, sc->irq_res[i],
+ INTR_TYPE_MISC | INTR_MPSAFE, mvebu_gpio_intr, NULL,
+ &sc->irq_cookies[i], &sc->irq_ih[i]))) {
+ device_printf(dev,
+ "WARNING: unable to register interrupt handler\n");
+ mvebu_gpio_detach(dev);
+ return (ENXIO);
+ }
+ }
+
+ /* Init GPIO pins */
+ for (i = 0; i < sc->gpio_npins; i++) {
+ pin = sc->gpio_pins + i;
+ pin->gp_pin = i;
+ if (sc->irq_res[0] != NULL)
+ pin->gp_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT |
+ GPIO_INTR_LEVEL_LOW | GPIO_INTR_LEVEL_HIGH |
+ GPIO_INTR_EDGE_RISING | GPIO_INTR_EDGE_FALLING;
+ else
+ pin->gp_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
+ pin->gp_flags =
+ gpio_read(sc, GPIO_CONTROL, &sc->gpio_pins[i]) != 0 ?
+ GPIO_PIN_OUTPUT : GPIO_PIN_INPUT;
+ snprintf(pin->gp_name, GPIOMAXNAME, "gpio%d", i);
+
+ /* Init HW */
+ gpio_write(sc, GPIO_INT_MASK, pin, 0);
+ gpio_write(sc, GPIO_INT_LEVEL_MASK, pin, 0);
+ gpio_write(sc, GPIO_INT_CAUSE, pin, 1);
+ gpio_write(sc, GPIO_DATA_IN_POL, pin, 1);
+ gpio_write(sc, GPIO_BLINK_ENA, pin, 0);
+ }
+
+ if (sc->irq_res[0] != NULL) {
+ rv = mvebu_gpio_pic_attach(sc);
+ if (rv != 0) {
+ device_printf(dev, "WARNING: unable to attach PIC\n");
+ mvebu_gpio_detach(dev);
+ return (rv);
+ }
+ }
+
+ sc->busdev = gpiobus_attach_bus(dev);
+ if (sc->busdev == NULL) {
+ mvebu_gpio_detach(dev);
+ return (ENXIO);
+ }
+
+ return (bus_generic_attach(dev));
+}
+
+static int
+mvebu_gpio_map_gpios(device_t dev, phandle_t pdev, phandle_t gparent,
+ int gcells, pcell_t *gpios, uint32_t *pin, uint32_t *flags)
+{
+
+ if (gcells != 2)
+ return (ERANGE);
+ *pin = gpios[0];
+ *flags= gpios[1];
+ return (0);
+}
+
+static phandle_t
+mvebu_gpio_get_node(device_t bus, device_t dev)
+{
+
+ /* We only have one child, the GPIO bus, which needs our own node. */
+ return (ofw_bus_get_node(bus));
+}
+
+static device_method_t mvebu_gpio_methods[] = {
+ DEVMETHOD(device_probe, mvebu_gpio_probe),
+ DEVMETHOD(device_attach, mvebu_gpio_attach),
+ DEVMETHOD(device_detach, mvebu_gpio_detach),
+
+ /* Interrupt controller interface */
+ DEVMETHOD(pic_disable_intr, mvebu_gpio_pic_disable_intr),
+ DEVMETHOD(pic_enable_intr, mvebu_gpio_pic_enable_intr),
+ DEVMETHOD(pic_map_intr, mvebu_gpio_pic_map_intr),
+ DEVMETHOD(pic_setup_intr, mvebu_gpio_pic_setup_intr),
+ DEVMETHOD(pic_teardown_intr, mvebu_gpio_pic_teardown_intr),
+ DEVMETHOD(pic_post_filter, mvebu_gpio_pic_post_filter),
+ DEVMETHOD(pic_post_ithread, mvebu_gpio_pic_post_ithread),
+ DEVMETHOD(pic_pre_ithread, mvebu_gpio_pic_pre_ithread),
+
+ /* GPIO protocol */
+ DEVMETHOD(gpio_get_bus, mvebu_gpio_get_bus),
+ DEVMETHOD(gpio_pin_max, mvebu_gpio_pin_max),
+ DEVMETHOD(gpio_pin_getname, mvebu_gpio_pin_getname),
+ DEVMETHOD(gpio_pin_getflags, mvebu_gpio_pin_getflags),
+ DEVMETHOD(gpio_pin_getcaps, mvebu_gpio_pin_getcaps),
+ DEVMETHOD(gpio_pin_setflags, mvebu_gpio_pin_setflags),
+ DEVMETHOD(gpio_pin_get, mvebu_gpio_pin_get),
+ DEVMETHOD(gpio_pin_set, mvebu_gpio_pin_set),
+ DEVMETHOD(gpio_pin_toggle, mvebu_gpio_pin_toggle),
+ DEVMETHOD(gpio_map_gpios, mvebu_gpio_map_gpios),
+
+ /* ofw_bus interface */
+ DEVMETHOD(ofw_bus_get_node, mvebu_gpio_get_node),
+
+ DEVMETHOD_END
+};
+
+static devclass_t mvebu_gpio_devclass;
+static DEFINE_CLASS_0(gpio, mvebu_gpio_driver, mvebu_gpio_methods,
+ sizeof(struct mvebu_gpio_softc));
+EARLY_DRIVER_MODULE(mvebu_gpio, simplebus, mvebu_gpio_driver,
+ mvebu_gpio_devclass, NULL, NULL,
+ BUS_PASS_TIMER + BUS_PASS_ORDER_LAST);
diff --git a/sys/arm/mv/mvebu_pinctrl.c b/sys/arm/mv/mvebu_pinctrl.c
new file mode 100644
index 000000000000..930c6878370f
--- /dev/null
+++ b/sys/arm/mv/mvebu_pinctrl.c
@@ -0,0 +1,239 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2018 Rubicon Communications, LLC (Netgate)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/intr.h>
+
+#include <dev/extres/syscon/syscon.h>
+
+#include <dev/fdt/fdt_pinctrl.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "syscon_if.h"
+
+#define PINS_PER_REG 8
+#define BITS_PER_PIN 4
+#define PINS_MASK 0xf
+#define MAX_PIN_FUNC 5
+
+struct mv_pins {
+ const char *name;
+ const char *functions[MAX_PIN_FUNC];
+};
+
+struct mv_padconf {
+ const struct mv_pins *pins;
+ size_t npins;
+};
+
+const static struct mv_pins ap806_pins[] = {
+ {"mpp0", {"gpio", "sdio", NULL, "spi0"}},
+ {"mpp1", {"gpio", "sdio", NULL, "spi0"}},
+ {"mpp2", {"gpio", "sdio", NULL, "spi0"}},
+ {"mpp3", {"gpio", "sdio", NULL, "spi0"}},
+ {"mpp4", {"gpio", "sdio", NULL, "i2c0"}},
+ {"mpp5", {"gpio", "sdio", NULL, "i2c0"}},
+ {"mpp6", {"gpio", "sdio", NULL, NULL}},
+ {"mpp7", {"gpio", "sdio", NULL, "uart1"}},
+ {"mpp8", {"gpio", "sdio", NULL, "uart1"}},
+ {"mpp9", {"gpio", "sdio", NULL, "spi0"}},
+ {"mpp10", {"gpio", "sdio", NULL, NULL}},
+ {"mpp11", {"gpio", NULL, NULL, "uart0"}},
+ {"mpp12", {"gpio", "sdio", "sdio", NULL}},
+ {"mpp13", {"gpio", NULL, NULL}},
+ {"mpp14", {"gpio", NULL, NULL}},
+ {"mpp15", {"gpio", NULL, NULL}},
+ {"mpp16", {"gpio", NULL, NULL}},
+ {"mpp17", {"gpio", NULL, NULL}},
+ {"mpp18", {"gpio", NULL, NULL}},
+ {"mpp19", {"gpio", NULL, NULL, "uart0", "sdio"}},
+};
+
+const struct mv_padconf ap806_padconf = {
+ .npins = nitems(ap806_pins),
+ .pins = ap806_pins,
+};
+
+struct mv_pinctrl_softc {
+ device_t dev;
+ struct syscon *syscon;
+
+ struct mv_padconf *padconf;
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"marvell,ap806-pinctrl", (uintptr_t)&ap806_padconf},
+ {NULL, 0}
+};
+
+#define RD4(sc, reg) SYSCON_READ_4((sc)->syscon, (reg))
+#define WR4(sc, reg, val) SYSCON_WRITE_4((sc)->syscon, (reg), (val))
+
+static void
+mv_pinctrl_configure_pin(struct mv_pinctrl_softc *sc, uint32_t pin,
+ uint32_t function)
+{
+ uint32_t offset, shift, reg;
+
+ offset = (pin / PINS_PER_REG) * BITS_PER_PIN;
+ shift = (pin % PINS_PER_REG) * BITS_PER_PIN;
+ reg = RD4(sc, offset);
+ reg &= ~(PINS_MASK << shift);
+ reg |= function << shift;
+ WR4(sc, offset, reg);
+}
+
+static int
+mv_pinctrl_configure_pins(device_t dev, phandle_t cfgxref)
+{
+ struct mv_pinctrl_softc *sc;
+ phandle_t node;
+ char *function;
+ const char **pins;
+ int i, pin_num, pin_func, npins;
+
+ sc = device_get_softc(dev);
+ node = OF_node_from_xref(cfgxref);
+
+ if (OF_getprop_alloc(node, "marvell,function",
+ (void **)&function) == -1)
+ return (ENOMEM);
+
+ npins = ofw_bus_string_list_to_array(node, "marvell,pins", &pins);
+ if (npins == -1)
+ return (ENOMEM);
+
+ for (i = 0; i < npins; i++) {
+ for (pin_num = 0; pin_num < sc->padconf->npins; pin_num++) {
+ if (strcmp(pins[i], sc->padconf->pins[pin_num].name) == 0)
+ break;
+ }
+ if (pin_num == sc->padconf->npins)
+ continue;
+
+ for (pin_func = 0; pin_func < MAX_PIN_FUNC; pin_func++)
+ if (sc->padconf->pins[pin_num].functions[pin_func] &&
+ strcmp(function, sc->padconf->pins[pin_num].functions[pin_func]) == 0)
+ break;
+
+ if (pin_func == MAX_PIN_FUNC)
+ continue;
+
+ mv_pinctrl_configure_pin(sc, pin_num, pin_func);
+ }
+
+ OF_prop_free(pins);
+
+ return (0);
+}
+
+static int
+mv_pinctrl_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Marvell Pinctrl controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+mv_pinctrl_attach(device_t dev)
+{
+ struct mv_pinctrl_softc *sc;
+ phandle_t node;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ sc->padconf = (struct mv_padconf *)
+ ofw_bus_search_compatible(dev,compat_data)->ocd_data;
+
+ if (SYSCON_GET_HANDLE(sc->dev, &sc->syscon) != 0 ||
+ sc->syscon == NULL) {
+ device_printf(dev, "cannot get syscon for device\n");
+ return (ENXIO);
+ }
+
+ node = ofw_bus_get_node(dev);
+
+ fdt_pinctrl_register(dev, "marvell,pins");
+ fdt_pinctrl_configure_tree(dev);
+
+ return (0);
+}
+
+static int
+mv_pinctrl_detach(device_t dev)
+{
+
+ return (EBUSY);
+}
+
+static device_method_t mv_pinctrl_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, mv_pinctrl_probe),
+ DEVMETHOD(device_attach, mv_pinctrl_attach),
+ DEVMETHOD(device_detach, mv_pinctrl_detach),
+
+ /* fdt_pinctrl interface */
+ DEVMETHOD(fdt_pinctrl_configure,mv_pinctrl_configure_pins),
+
+ DEVMETHOD_END
+};
+
+static devclass_t mv_pinctrl_devclass;
+
+static driver_t mv_pinctrl_driver = {
+ "mv_pinctrl",
+ mv_pinctrl_methods,
+ sizeof(struct mv_pinctrl_softc),
+};
+
+EARLY_DRIVER_MODULE(mv_pinctrl, simplebus, mv_pinctrl_driver,
+ mv_pinctrl_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);
diff --git a/sys/arm/mv/mvreg.h b/sys/arm/mv/mvreg.h
new file mode 100644
index 000000000000..95af82b295f6
--- /dev/null
+++ b/sys/arm/mv/mvreg.h
@@ -0,0 +1,439 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (C) 2007-2011 MARVELL INTERNATIONAL LTD.
+ * All rights reserved.
+ *
+ * Developed by Semihalf.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of MARVELL nor the names of contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MVREG_H_
+#define _MVREG_H_
+
+#include <arm/mv/mvwin.h>
+
+#if defined(SOC_MV_DISCOVERY)
+#define IRQ_CAUSE_ERROR 0x0
+#define IRQ_CAUSE 0x4
+#define IRQ_CAUSE_HI 0x8
+#define IRQ_MASK_ERROR 0xC
+#define IRQ_MASK 0x10
+#define IRQ_MASK_HI 0x14
+#define IRQ_CAUSE_SELECT 0x18
+#define FIQ_MASK_ERROR 0x1C
+#define FIQ_MASK 0x20
+#define FIQ_MASK_HI 0x24
+#define FIQ_CAUSE_SELECT 0x28
+#define ENDPOINT_IRQ_MASK_ERROR(n) 0x2C
+#define ENDPOINT_IRQ_MASK(n) 0x30
+#define ENDPOINT_IRQ_MASK_HI(n) 0x34
+#define ENDPOINT_IRQ_CAUSE_SELECT 0x38
+#else
+#define IRQ_CAUSE 0x0
+#define IRQ_MASK 0x4
+#define FIQ_MASK 0x8
+#define ENDPOINT_IRQ_MASK(n) 0xC
+#define IRQ_CAUSE_HI 0x10
+#define IRQ_MASK_HI 0x14
+#define FIQ_MASK_HI 0x18
+#define ENDPOINT_IRQ_MASK_HI(n) 0x1C
+#define ENDPOINT_IRQ_MASK_ERROR(n) (-1)
+#define IRQ_CAUSE_ERROR (-1) /* Fake defines for unified */
+#define IRQ_MASK_ERROR (-1) /* interrupt controller code */
+#endif
+
+#define MAIN_IRQ_NUM 116
+#define ERR_IRQ_NUM 32
+#define ERR_IRQ (MAIN_IRQ_NUM)
+#define MSI_IRQ (ERR_IRQ + ERR_IRQ_NUM)
+
+#define MSI_IRQ_NUM 32
+
+#define IRQ_CPU_SELF 0x00000001
+#define BRIDGE_IRQ_CAUSE_ARMADAXP 0x68
+#define IRQ_TIMER0_ARMADAXP 0x00000001
+#define IRQ_TIMER1_ARMADAXP 0x00000002
+#define IRQ_TIMER_WD_ARMADAXP 0x00000004
+
+#define BRIDGE_IRQ_CAUSE 0x10
+#define IRQ_CPU_SELF 0x00000001
+#define IRQ_TIMER0 0x00000002
+#define IRQ_TIMER1 0x00000004
+#define IRQ_TIMER_WD 0x00000008
+
+#define BRIDGE_IRQ_MASK 0x14
+#define IRQ_CPU_MASK 0x00000001
+#define IRQ_TIMER0_MASK 0x00000002
+#define IRQ_TIMER1_MASK 0x00000004
+#define IRQ_TIMER_WD_MASK 0x00000008
+
+#define IRQ_CPU_SELF_CLR (~IRQ_CPU_SELF)
+#define IRQ_TIMER0_CLR (~IRQ_TIMER0)
+#define IRQ_TIMER_WD_CLR (~IRQ_TIMER_WD)
+
+#define IRQ_TIMER0_CLR_ARMADAXP (~IRQ_TIMER0_ARMADAXP)
+#define IRQ_TIMER_WD_CLR_ARMADAXP (~IRQ_TIMER_WD_ARMADAXP)
+
+/*
+ * System reset
+ */
+#define RSTOUTn_MASK_ARMV7 0x60
+#define SYSTEM_SOFT_RESET_ARMV7 0x64
+#define SOFT_RST_OUT_EN_ARMV7 0x00000001
+#define SYS_SOFT_RST_ARMV7 0x00000001
+
+#define RSTOUTn_MASK 0x8
+#define SOFT_RST_OUT_EN 0x00000004
+#define SYSTEM_SOFT_RESET 0xc
+#define SYS_SOFT_RST 0x00000001
+#define RSTOUTn_MASK_WD 0x400
+#define WD_RSTOUTn_MASK 0x4
+#define WD_GLOBAL_MASK 0x00000100
+#define WD_CPU0_MASK 0x00000001
+#define WD_RST_OUT_EN 0x00000002
+
+/*
+ * Power Control
+ */
+#if defined(SOC_MV_KIRKWOOD)
+#define CPU_PM_CTRL 0x18
+#else
+#define CPU_PM_CTRL 0x1C
+#endif
+#define CPU_PM_CTRL_NONE 0
+#define CPU_PM_CTRL_ALL ~0x0
+
+#if defined(SOC_MV_KIRKWOOD)
+#define CPU_PM_CTRL_GE0 (1 << 0)
+#define CPU_PM_CTRL_PEX0_PHY (1 << 1)
+#define CPU_PM_CTRL_PEX0 (1 << 2)
+#define CPU_PM_CTRL_USB0 (1 << 3)
+#define CPU_PM_CTRL_SDIO (1 << 4)
+#define CPU_PM_CTRL_TSU (1 << 5)
+#define CPU_PM_CTRL_DUNIT (1 << 6)
+#define CPU_PM_CTRL_RUNIT (1 << 7)
+#define CPU_PM_CTRL_XOR0 (1 << 8)
+#define CPU_PM_CTRL_AUDIO (1 << 9)
+#define CPU_PM_CTRL_SATA0 (1 << 14)
+#define CPU_PM_CTRL_SATA1 (1 << 15)
+#define CPU_PM_CTRL_XOR1 (1 << 16)
+#define CPU_PM_CTRL_CRYPTO (1 << 17)
+#define CPU_PM_CTRL_GE1 (1 << 19)
+#define CPU_PM_CTRL_TDM (1 << 20)
+#define CPU_PM_CTRL_XOR (CPU_PM_CTRL_XOR0 | CPU_PM_CTRL_XOR1)
+#define CPU_PM_CTRL_USB(u) (CPU_PM_CTRL_USB0)
+#define CPU_PM_CTRL_SATA (CPU_PM_CTRL_SATA0 | CPU_PM_CTRL_SATA1)
+#define CPU_PM_CTRL_GE(u) (CPU_PM_CTRL_GE1 * (u) | CPU_PM_CTRL_GE0 * \
+ (1 - (u)))
+#define CPU_PM_CTRL_IDMA (CPU_PM_CTRL_NONE)
+#elif defined(SOC_MV_DISCOVERY)
+#define CPU_PM_CTRL_GE0 (1 << 1)
+#define CPU_PM_CTRL_GE1 (1 << 2)
+#define CPU_PM_CTRL_PEX00 (1 << 5)
+#define CPU_PM_CTRL_PEX01 (1 << 6)
+#define CPU_PM_CTRL_PEX02 (1 << 7)
+#define CPU_PM_CTRL_PEX03 (1 << 8)
+#define CPU_PM_CTRL_PEX10 (1 << 9)
+#define CPU_PM_CTRL_PEX11 (1 << 10)
+#define CPU_PM_CTRL_PEX12 (1 << 11)
+#define CPU_PM_CTRL_PEX13 (1 << 12)
+#define CPU_PM_CTRL_SATA0_PHY (1 << 13)
+#define CPU_PM_CTRL_SATA0 (1 << 14)
+#define CPU_PM_CTRL_SATA1_PHY (1 << 15)
+#define CPU_PM_CTRL_SATA1 (1 << 16)
+#define CPU_PM_CTRL_USB0 (1 << 17)
+#define CPU_PM_CTRL_USB1 (1 << 18)
+#define CPU_PM_CTRL_USB2 (1 << 19)
+#define CPU_PM_CTRL_IDMA (1 << 20)
+#define CPU_PM_CTRL_XOR (1 << 21)
+#define CPU_PM_CTRL_CRYPTO (1 << 22)
+#define CPU_PM_CTRL_DEVICE (1 << 23)
+#define CPU_PM_CTRL_USB(u) (1 << (17 + (u)))
+#define CPU_PM_CTRL_SATA (CPU_PM_CTRL_SATA0 | CPU_PM_CTRL_SATA1)
+#define CPU_PM_CTRL_GE(u) (CPU_PM_CTRL_GE1 * (u) | CPU_PM_CTRL_GE0 * \
+ (1 - (u)))
+#else
+#define CPU_PM_CTRL_CRYPTO (CPU_PM_CTRL_NONE)
+#define CPU_PM_CTRL_IDMA (CPU_PM_CTRL_NONE)
+#define CPU_PM_CTRL_XOR (CPU_PM_CTRL_NONE)
+#define CPU_PM_CTRL_SATA (CPU_PM_CTRL_NONE)
+#define CPU_PM_CTRL_USB(u) (CPU_PM_CTRL_NONE)
+#define CPU_PM_CTRL_GE(u) (CPU_PM_CTRL_NONE)
+#endif
+
+/*
+ * Timers
+ */
+#define CPU_TIMERS_BASE 0x300
+#define CPU_TIMER_CONTROL 0x0
+#define CPU_TIMER0_EN 0x00000001
+#define CPU_TIMER0_AUTO 0x00000002
+#define CPU_TIMER1_EN 0x00000004
+#define CPU_TIMER1_AUTO 0x00000008
+#define CPU_TIMER2_EN 0x00000010
+#define CPU_TIMER2_AUTO 0x00000020
+#define CPU_TIMER_WD_EN 0x00000100
+#define CPU_TIMER_WD_AUTO 0x00000200
+/* 25MHz mode is Armada XP - specific */
+#define CPU_TIMER_WD_25MHZ_EN 0x00000400
+#define CPU_TIMER0_25MHZ_EN 0x00000800
+#define CPU_TIMER1_25MHZ_EN 0x00001000
+#define CPU_TIMER0_REL 0x10
+#define CPU_TIMER0 0x14
+
+/*
+ * SATA
+ */
+#define SATA_CHAN_NUM 2
+
+#define EDMA_REGISTERS_OFFSET 0x2000
+#define EDMA_REGISTERS_SIZE 0x2000
+#define SATA_EDMA_BASE(ch) (EDMA_REGISTERS_OFFSET + \
+ ((ch) * EDMA_REGISTERS_SIZE))
+
+/* SATAHC registers */
+#define SATA_CR 0x000 /* Configuration Reg. */
+#define SATA_CR_NODMABS (1 << 8)
+#define SATA_CR_NOEDMABS (1 << 9)
+#define SATA_CR_NOPRDPBS (1 << 10)
+#define SATA_CR_COALDIS(ch) (1 << (24 + ch))
+
+/* Interrupt Coalescing Threshold Reg. */
+#define SATA_ICTR 0x00C
+#define SATA_ICTR_MAX ((1 << 8) - 1)
+
+/* Interrupt Time Threshold Reg. */
+#define SATA_ITTR 0x010
+#define SATA_ITTR_MAX ((1 << 24) - 1)
+
+#define SATA_ICR 0x014 /* Interrupt Cause Reg. */
+#define SATA_ICR_DMADONE(ch) (1 << (ch))
+#define SATA_ICR_COAL (1 << 4)
+#define SATA_ICR_DEV(ch) (1 << (8 + ch))
+
+#define SATA_MICR 0x020 /* Main Interrupt Cause Reg. */
+#define SATA_MICR_ERR(ch) (1 << (2 * ch))
+#define SATA_MICR_DONE(ch) (1 << ((2 * ch) + 1))
+#define SATA_MICR_DMADONE(ch) (1 << (4 + ch))
+#define SATA_MICR_COAL (1 << 8)
+
+#define SATA_MIMR 0x024 /* Main Interrupt Mask Reg. */
+
+/* Shadow registers */
+#define SATA_SHADOWR_BASE(ch) (SATA_EDMA_BASE(ch) + 0x100)
+#define SATA_SHADOWR_CONTROL(ch) (SATA_EDMA_BASE(ch) + 0x120)
+
+/* SATA registers */
+#define SATA_SATA_SSTATUS(ch) (SATA_EDMA_BASE(ch) + 0x300)
+#define SATA_SATA_SERROR(ch) (SATA_EDMA_BASE(ch) + 0x304)
+#define SATA_SATA_SCONTROL(ch) (SATA_EDMA_BASE(ch) + 0x308)
+#define SATA_SATA_FISICR(ch) (SATA_EDMA_BASE(ch) + 0x364)
+
+/* EDMA registers */
+#define SATA_EDMA_CFG(ch) (SATA_EDMA_BASE(ch) + 0x000)
+#define SATA_EDMA_CFG_QL128 (1 << 19)
+#define SATA_EDMA_CFG_HQCACHE (1 << 22)
+
+#define SATA_EDMA_IECR(ch) (SATA_EDMA_BASE(ch) + 0x008)
+
+#define SATA_EDMA_IEMR(ch) (SATA_EDMA_BASE(ch) + 0x00C)
+#define SATA_EDMA_REQBAHR(ch) (SATA_EDMA_BASE(ch) + 0x010)
+#define SATA_EDMA_REQIPR(ch) (SATA_EDMA_BASE(ch) + 0x014)
+#define SATA_EDMA_REQOPR(ch) (SATA_EDMA_BASE(ch) + 0x018)
+#define SATA_EDMA_RESBAHR(ch) (SATA_EDMA_BASE(ch) + 0x01C)
+#define SATA_EDMA_RESIPR(ch) (SATA_EDMA_BASE(ch) + 0x020)
+#define SATA_EDMA_RESOPR(ch) (SATA_EDMA_BASE(ch) + 0x024)
+
+#define SATA_EDMA_CMD(ch) (SATA_EDMA_BASE(ch) + 0x028)
+#define SATA_EDMA_CMD_ENABLE (1 << 0)
+#define SATA_EDMA_CMD_DISABLE (1 << 1)
+#define SATA_EDMA_CMD_RESET (1 << 2)
+
+#define SATA_EDMA_STATUS(ch) (SATA_EDMA_BASE(ch) + 0x030)
+#define SATA_EDMA_STATUS_IDLE (1 << 7)
+
+/* Offset to extract input slot from REQIPR register */
+#define SATA_EDMA_REQIS_OFS 5
+
+/* Offset to extract input slot from RESOPR register */
+#define SATA_EDMA_RESOS_OFS 3
+
+/*
+ * GPIO
+ */
+#define GPIO_DATA_OUT 0x00
+#define GPIO_DATA_OUT_EN_CTRL 0x04
+#define GPIO_BLINK_EN 0x08
+#define GPIO_DATA_IN_POLAR 0x0c
+#define GPIO_DATA_IN 0x10
+#define GPIO_INT_CAUSE 0x14
+#define GPIO_INT_EDGE_MASK 0x18
+#define GPIO_INT_LEV_MASK 0x1c
+
+#define GPIO(n) (1 << (n))
+#define MV_GPIO_MAX_NPINS 64
+
+#define MV_GPIO_IN_NONE 0x0
+#define MV_GPIO_IN_POL_LOW (1 << 16)
+#define MV_GPIO_IN_IRQ_EDGE (2 << 16)
+#define MV_GPIO_IN_IRQ_LEVEL (4 << 16)
+#define MV_GPIO_IN_IRQ_DOUBLE_EDGE (8 << 16)
+#define MV_GPIO_IN_DEBOUNCE (16 << 16)
+#define MV_GPIO_OUT_NONE 0x0
+#define MV_GPIO_OUT_BLINK 0x1
+#define MV_GPIO_OUT_OPEN_DRAIN 0x2
+#define MV_GPIO_OUT_OPEN_SRC 0x4
+
+#if defined(SOC_MV_ORION)
+#define SAMPLE_AT_RESET 0x10
+#elif defined(SOC_MV_KIRKWOOD)
+#define SAMPLE_AT_RESET 0x30
+#endif
+#define SAMPLE_AT_RESET_ARMADA38X 0x400
+#define SAMPLE_AT_RESET_LO 0x30
+#define SAMPLE_AT_RESET_HI 0x34
+
+/*
+ * Clocks
+ */
+#if defined(SOC_MV_ORION)
+#define TCLK_MASK 0x00000300
+#define TCLK_SHIFT 0x08
+#elif defined(SOC_MV_DISCOVERY)
+#define TCLK_MASK 0x00000180
+#define TCLK_SHIFT 0x07
+#endif
+
+#define TCLK_MASK_ARMADA38X 0x00008000
+#define TCLK_SHIFT_ARMADA38X 15
+
+#define TCLK_100MHZ 100000000
+#define TCLK_125MHZ 125000000
+#define TCLK_133MHZ 133333333
+#define TCLK_150MHZ 150000000
+#define TCLK_166MHZ 166666667
+#define TCLK_200MHZ 200000000
+#define TCLK_250MHZ 250000000
+#define TCLK_300MHZ 300000000
+#define TCLK_667MHZ 667000000
+
+#define A38X_CPU_DDR_CLK_MASK 0x00007c00
+#define A38X_CPU_DDR_CLK_SHIFT 10
+
+/*
+ * CPU Cache Configuration
+ */
+
+#define CPU_CONFIG 0x00000000
+#define CPU_CONFIG_IC_PREF 0x00010000
+#define CPU_CONFIG_DC_PREF 0x00020000
+#define CPU_CONTROL 0x00000004
+#define CPU_CONTROL_L2_SIZE 0x00200000 /* Only on Discovery */
+#define CPU_CONTROL_L2_MODE 0x00020000 /* Only on Discovery */
+#define CPU_L2_CONFIG 0x00000028 /* Only on Kirkwood */
+#define CPU_L2_CONFIG_MODE 0x00000010 /* Only on Kirkwood */
+
+/*
+ * PCI Express port control (CPU Control registers)
+ */
+#define CPU_CONTROL_PCIE_DISABLE(n) (1 << (3 * (n)))
+
+/*
+ * Vendor ID
+ */
+#define PCI_VENDORID_MRVL 0x11AB
+#define PCI_VENDORID_MRVL2 0x1B4B
+
+/*
+ * Chip ID
+ */
+#define MV_DEV_88F5181 0x5181
+#define MV_DEV_88F5182 0x5182
+#define MV_DEV_88F5281 0x5281
+#define MV_DEV_88F6281 0x6281
+#define MV_DEV_88F6282 0x6282
+#define MV_DEV_88F6781 0x6781
+#define MV_DEV_88F6828 0x6828
+#define MV_DEV_88F6820 0x6820
+#define MV_DEV_88F6810 0x6810
+#define MV_DEV_MV78100_Z0 0x6381
+#define MV_DEV_MV78100 0x7810
+#define MV_DEV_MV78130 0x7813
+#define MV_DEV_MV78160 0x7816
+#define MV_DEV_MV78230 0x7823
+#define MV_DEV_MV78260 0x7826
+#define MV_DEV_MV78460 0x7846
+#define MV_DEV_88RC8180 0x8180
+#define MV_DEV_88RC9480 0x9480
+#define MV_DEV_88RC9580 0x9580
+
+#define MV_DEV_FAMILY_MASK 0xff00
+#define MV_DEV_DISCOVERY 0x7800
+#define MV_DEV_ARMADA38X 0x6800
+
+/*
+ * Doorbell register control
+ */
+#define MV_DRBL_PCIE_TO_CPU 0
+#define MV_DRBL_CPU_TO_PCIE 1
+
+#define MV_DRBL_CAUSE(d,u) (0x10 * (u) + 0x8 * (d))
+#define MV_DRBL_MASK(d,u) (0x10 * (u) + 0x8 * (d) + 0x4)
+#define MV_DRBL_MSG(m,d,u) (0x10 * (u) + 0x8 * (d) + 0x4 * (m) + 0x30)
+
+/*
+ * SCU
+ */
+#define MV_SCU_BASE (MV_BASE + 0xc000)
+#define MV_SCU_REGS_LEN 0x100
+#define MV_SCU_REG_CTRL 0x00
+#define MV_SCU_REG_CONFIG 0x04
+#define MV_SCU_ENABLE (1 << 0)
+#define MV_SCU_SL_L2_ENABLE (1 << 3)
+#define SCU_CFG_REG_NCPU_MASK 0x3
+
+/*
+ * PMSU
+ */
+#define MV_PMSU_BASE (MV_BASE + 0x22000)
+#define MV_PMSU_REGS_LEN 0x1000
+#define PMSU_BOOT_ADDR_REDIRECT_OFFSET(cpu) (((cpu) * 0x100) + 0x124)
+
+/*
+ * CPU RESET
+ */
+#define MV_CPU_RESET_BASE (MV_BASE + 0x20800)
+#define MV_CPU_RESET_REGS_LEN 0x8
+#define CPU_RESET_OFFSET(cpu) ((cpu) * 0x8)
+#define CPU_RESET_ASSERT 0x1
+
+#define MV_MBUS_CTRL_BASE (MV_BASE + 0x20420)
+#define MV_MBUS_CTRL_REGS_LEN 0x10
+
+#endif /* _MVREG_H_ */
diff --git a/sys/arm/mv/mvvar.h b/sys/arm/mv/mvvar.h
new file mode 100644
index 000000000000..c1e51fa6c62c
--- /dev/null
+++ b/sys/arm/mv/mvvar.h
@@ -0,0 +1,151 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2002, 2003 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Jason R. Thorpe for Wasabi Systems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the NetBSD Project by
+ * Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * from: FreeBSD: //depot/projects/arm/src/sys/arm/xscale/pxa2x0/pxa2x0var.h, rev 1
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MVVAR_H_
+#define _MVVAR_H_
+
+#include <sys/rman.h>
+#include <machine/bus.h>
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <dev/ofw/openfirm.h>
+
+#define MV_TYPE_PCI 0
+#define MV_TYPE_PCIE 1
+
+#define MV_MODE_ENDPOINT 0
+#define MV_MODE_ROOT 1
+
+enum soc_family{
+ MV_SOC_ARMADA_38X = 0x00,
+ MV_SOC_ARMADA_XP = 0x01,
+ MV_SOC_ARMV5 = 0x02,
+ MV_SOC_UNSUPPORTED = 0xff,
+};
+
+struct gpio_config {
+ int gc_gpio; /* GPIO number */
+ uint32_t gc_flags; /* GPIO flags */
+ int gc_output; /* GPIO output value */
+};
+
+struct decode_win {
+ int target; /* Mbus unit ID */
+ int attr; /* Attributes of the target interface */
+ vm_paddr_t base; /* Physical base addr */
+ uint32_t size;
+ vm_paddr_t remap;
+};
+
+extern const struct gpio_config mv_gpio_config[];
+extern const struct decode_win *cpu_wins;
+extern const struct decode_win *idma_wins;
+extern const struct decode_win *xor_wins;
+extern int idma_wins_no;
+extern int xor_wins_no;
+
+int soc_decode_win(void);
+void soc_id(uint32_t *dev, uint32_t *rev);
+void soc_dump_decode_win(void);
+uint32_t soc_power_ctrl_get(uint32_t mask);
+void soc_power_ctrl_set(uint32_t mask);
+
+int decode_win_cpu_set(int target, int attr, vm_paddr_t base, uint32_t size,
+ vm_paddr_t remap);
+int decode_win_overlap(int, int, const struct decode_win *);
+int win_cpu_can_remap(int);
+void decode_win_pcie_setup(u_long);
+
+void ddr_disable(int i);
+int ddr_is_active(int i);
+uint32_t ddr_base(int i);
+uint32_t ddr_size(int i);
+uint32_t ddr_attr(int i);
+uint32_t ddr_target(int i);
+
+uint32_t cpu_extra_feat(void);
+uint32_t get_tclk(void);
+uint32_t get_cpu_freq(void);
+uint32_t get_l2clk(void);
+uint32_t read_cpu_ctrl(uint32_t);
+void write_cpu_ctrl(uint32_t, uint32_t);
+
+uint32_t read_cpu_mp_clocks(uint32_t reg);
+void write_cpu_mp_clocks(uint32_t reg, uint32_t val);
+uint32_t read_cpu_misc(uint32_t reg);
+void write_cpu_misc(uint32_t reg, uint32_t val);
+
+int mv_pcib_bar_win_set(device_t dev, uint32_t base, uint32_t size,
+ uint32_t remap, int winno, int busno);
+int mv_pcib_cpu_win_remap(device_t dev, uint32_t remap, uint32_t size);
+
+void mv_mask_endpoint_irq(uintptr_t nb, int unit);
+void mv_unmask_endpoint_irq(uintptr_t nb, int unit);
+
+int mv_drbl_get_next_irq(int dir, int unit);
+void mv_drbl_mask_all(int unit);
+void mv_drbl_mask_irq(uint32_t irq, int dir, int unit);
+void mv_drbl_unmask_irq(uint32_t irq, int dir, int unit);
+void mv_drbl_set_mask(uint32_t val, int dir, int unit);
+uint32_t mv_drbl_get_mask(int dir, int unit);
+void mv_drbl_set_cause(uint32_t val, int dir, int unit);
+uint32_t mv_drbl_get_cause(int dir, int unit);
+void mv_drbl_set_msg(uint32_t val, int mnr, int dir, int unit);
+uint32_t mv_drbl_get_msg(int mnr, int dir, int unit);
+
+int mv_msi_data(int irq, uint64_t *addr, uint32_t *data);
+
+struct devmap_entry;
+
+int mv_pci_devmap(phandle_t, struct devmap_entry *, vm_offset_t,
+ vm_offset_t);
+int fdt_localbus_devmap(phandle_t, struct devmap_entry *, int, int *);
+enum soc_family mv_check_soc_family(void);
+
+int mv_fdt_is_type(phandle_t, const char *);
+int mv_fdt_pm(phandle_t);
+
+uint32_t get_tclk_armadaxp(void);
+uint32_t get_tclk_armada38x(void);
+uint32_t get_cpu_freq_armadaxp(void);
+uint32_t get_cpu_freq_armada38x(void);
+#endif /* _MVVAR_H_ */
diff --git a/sys/arm/mv/mvwin.h b/sys/arm/mv/mvwin.h
new file mode 100644
index 000000000000..967b626200c0
--- /dev/null
+++ b/sys/arm/mv/mvwin.h
@@ -0,0 +1,392 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (C) 2007-2011 MARVELL INTERNATIONAL LTD.
+ * All rights reserved.
+ *
+ * Developed by Semihalf.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of MARVELL nor the names of contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MVWIN_H_
+#define _MVWIN_H_
+
+/*
+ * Decode windows addresses.
+ *
+ * All decoding windows must be aligned to their size, which has to be
+ * a power of 2.
+ */
+
+/*
+ * SoC Integrated devices: 0xF1000000, 16 MB (VA == PA)
+ */
+
+/* SoC Regs */
+#define MV_PHYS_BASE 0xF1000000
+#define MV_SIZE (1024 * 1024) /* 1 MB */
+
+/* SRAM */
+#define MV_CESA_SRAM_BASE 0xF1100000
+
+/*
+ * External devices: 0x80000000, 1 GB (VA == PA)
+ * Includes Device Bus, PCI and PCIE.
+ */
+#if defined(SOC_MV_ORION)
+#define MV_PCI_PORTS 2 /* 1x PCI + 1x PCIE */
+#elif defined(SOC_MV_KIRKWOOD)
+#define MV_PCI_PORTS 1 /* 1x PCIE */
+#elif defined(SOC_MV_DISCOVERY)
+#define MV_PCI_PORTS 8 /* 8x PCIE */
+#else
+#define MV_PCI_PORTS 1 /* 1x PCIE -> worst case */
+#endif
+
+/* PCI/PCIE Memory */
+#define MV_PCI_MEM_PHYS_BASE 0x80000000
+#define MV_PCI_MEM_SIZE (512 * 1024 * 1024) /* 512 MB */
+#define MV_PCI_MEM_BASE MV_PCI_MEM_PHYS_BASE
+#define MV_PCI_MEM_SLICE_SIZE (MV_PCI_MEM_SIZE / MV_PCI_PORTS)
+/* PCI/PCIE I/O */
+#define MV_PCI_IO_PHYS_BASE 0xBF000000
+#define MV_PCI_IO_SIZE (16 * 1024 * 1024) /* 16 MB */
+#define MV_PCI_IO_BASE MV_PCI_IO_PHYS_BASE
+#define MV_PCI_IO_SLICE_SIZE (MV_PCI_IO_SIZE / MV_PCI_PORTS)
+#define MV_PCI_VA_MEM_BASE 0
+#define MV_PCI_VA_IO_BASE 0
+
+/*
+ * Device Bus (VA == PA)
+ */
+#define MV_DEV_BOOT_BASE 0xF9300000
+#define MV_DEV_BOOT_SIZE (1024 * 1024) /* 1 MB */
+
+#define MV_DEV_CS0_BASE 0xF9400000
+#define MV_DEV_CS0_SIZE (1024 * 1024) /* 1 MB */
+
+#define MV_DEV_CS1_BASE 0xF9500000
+#define MV_DEV_CS1_SIZE (32 * 1024 * 1024) /* 32 MB */
+
+#define MV_DEV_CS2_BASE 0xFB500000
+#define MV_DEV_CS2_SIZE (1024 * 1024) /* 1 MB */
+
+/*
+ * Integrated SoC peripherals addresses
+ */
+#define MV_BASE MV_PHYS_BASE /* VA == PA mapping */
+#define MV_DDR_CADR_BASE_ARMV7 (MV_BASE + 0x20180)
+#define MV_DDR_CADR_BASE (MV_BASE + 0x1500)
+#define MV_MPP_BASE (MV_BASE + 0x10000)
+
+#define MV_MISC_BASE (MV_BASE + 0x18200)
+#define MV_MBUS_BRIDGE_BASE (MV_BASE + 0x20000)
+#define MV_INTREGS_BASE (MV_MBUS_BRIDGE_BASE + 0x80)
+#define MV_MP_CLOCKS_BASE (MV_MBUS_BRIDGE_BASE + 0x700)
+
+#define MV_CPU_CONTROL_BASE_ARMV7 (MV_MBUS_BRIDGE_BASE + 0x1800)
+#define MV_CPU_CONTROL_BASE (MV_MBUS_BRIDGE_BASE + 0x100)
+
+#define MV_PCI_BASE (MV_BASE + 0x30000)
+#define MV_PCI_SIZE 0x2000
+
+#define MV_PCIE_BASE_ARMADA38X (MV_BASE + 0x80000)
+#define MV_PCIE_BASE (MV_BASE + 0x40000)
+#define MV_PCIE_SIZE 0x2000
+#define MV_SDIO_BASE (MV_BASE + 0x90000)
+#define MV_SDIO_SIZE 0x10000
+
+/*
+ * Decode windows definitions and macros
+ */
+#define MV_WIN_CPU_CTRL_ARMV7(n) (((n) < 8) ? 0x10 * (n) : 0x90 + (0x8 * ((n) - 8)))
+#define MV_WIN_CPU_BASE_ARMV7(n) ((((n) < 8) ? 0x10 * (n) : 0x90 + (0x8 * ((n) - 8))) + 0x4)
+#define MV_WIN_CPU_REMAP_LO_ARMV7(n) (0x10 * (n) + 0x008)
+#define MV_WIN_CPU_REMAP_HI_ARMV7(n) (0x10 * (n) + 0x00C)
+
+#define MV_WIN_CPU_CTRL_ARMV5(n) (0x10 * (n) + (((n) < 8) ? 0x000 : 0x880))
+#define MV_WIN_CPU_BASE_ARMV5(n) (0x10 * (n) + (((n) < 8) ? 0x004 : 0x884))
+#define MV_WIN_CPU_REMAP_LO_ARMV5(n) (0x10 * (n) + (((n) < 8) ? 0x008 : 0x888))
+#define MV_WIN_CPU_REMAP_HI_ARMV5(n) (0x10 * (n) + (((n) < 8) ? 0x00C : 0x88C))
+
+#if defined(SOC_MV_DISCOVERY)
+#define MV_WIN_CPU_MAX 14
+#else
+#define MV_WIN_CPU_MAX 8
+#endif
+#define MV_WIN_CPU_MAX_ARMV7 20
+
+#define MV_WIN_CPU_ATTR_SHIFT 8
+#define MV_WIN_CPU_TARGET_SHIFT 4
+#define MV_WIN_CPU_ENABLE_BIT 1
+
+#define MV_WIN_DDR_BASE(n) (0x8 * (n) + 0x0)
+#define MV_WIN_DDR_SIZE(n) (0x8 * (n) + 0x4)
+#define MV_WIN_DDR_MAX 4
+
+/*
+ * These values are valid only for peripherals decoding windows
+ * Bit in ATTR is zeroed according to CS bank number
+ */
+#define MV_WIN_DDR_ATTR(cs) (0x0F & ~(0x01 << (cs)))
+#define MV_WIN_DDR_TARGET 0x0
+
+#if defined(SOC_MV_DISCOVERY)
+#define MV_WIN_CESA_TARGET 9
+#define MV_WIN_CESA_ATTR(eng_sel) 1
+#else
+#define MV_WIN_CESA_TARGET 3
+#define MV_WIN_CESA_ATTR(eng_sel) 0
+#endif
+
+#define MV_WIN_CESA_TARGET_ARMADAXP 9
+/*
+ * Bits [2:3] of cesa attribute select engine:
+ * eng_sel:
+ * 1: engine1
+ * 2: engine0
+ */
+#define MV_WIN_CESA_ATTR_ARMADAXP(eng_sel) (1 | ((eng_sel) << 2))
+#define MV_WIN_CESA_TARGET_ARMADA38X 9
+/*
+ * Bits [1:0] = Data swapping
+ * 0x0 = Byte swap
+ * 0x1 = No swap
+ * 0x2 = Byte and word swap
+ * 0x3 = Word swap
+ * Bits [4:2] = CESA select:
+ * 0x6 = CESA0
+ * 0x5 = CESA1
+ */
+#define MV_WIN_CESA_ATTR_ARMADA38X(eng_sel) (0x11 | (1 << (3 - (eng_sel))))
+/* CESA TDMA address decoding registers */
+#define MV_WIN_CESA_CTRL(n) (0x8 * (n) + 0xA04)
+#define MV_WIN_CESA_BASE(n) (0x8 * (n) + 0xA00)
+#define MV_WIN_CESA_MAX 4
+
+#define MV_WIN_USB_CTRL(n) (0x10 * (n) + 0x320)
+#define MV_WIN_USB_BASE(n) (0x10 * (n) + 0x324)
+#define MV_WIN_USB_MAX 4
+
+#define MV_WIN_USB3_CTRL(n) (0x8 * (n) + 0x4000)
+#define MV_WIN_USB3_BASE(n) (0x8 * (n) + 0x4004)
+#define MV_WIN_USB3_MAX 8
+
+#define MV_WIN_NETA_OFFSET 0x2000
+#define MV_WIN_NETA_BASE(n) MV_WIN_ETH_BASE(n) + MV_WIN_NETA_OFFSET
+
+#define MV_WIN_CESA_OFFSET 0x2000
+
+#define MV_WIN_ETH_BASE(n) (0x8 * (n) + 0x200)
+#define MV_WIN_ETH_SIZE(n) (0x8 * (n) + 0x204)
+#define MV_WIN_ETH_REMAP(n) (0x4 * (n) + 0x280)
+#define MV_WIN_ETH_MAX 6
+
+#define MV_WIN_IDMA_BASE(n) (0x8 * (n) + 0xa00)
+#define MV_WIN_IDMA_SIZE(n) (0x8 * (n) + 0xa04)
+#define MV_WIN_IDMA_REMAP(n) (0x4 * (n) + 0xa60)
+#define MV_WIN_IDMA_CAP(n) (0x4 * (n) + 0xa70)
+#define MV_WIN_IDMA_MAX 8
+#define MV_IDMA_CHAN_MAX 4
+
+#define MV_WIN_XOR_BASE(n, m) (0x4 * (n) + 0xa50 + (m) * 0x100)
+#define MV_WIN_XOR_SIZE(n, m) (0x4 * (n) + 0xa70 + (m) * 0x100)
+#define MV_WIN_XOR_REMAP(n, m) (0x4 * (n) + 0xa90 + (m) * 0x100)
+#define MV_WIN_XOR_CTRL(n, m) (0x4 * (n) + 0xa40 + (m) * 0x100)
+#define MV_WIN_XOR_OVERR(n, m) (0x4 * (n) + 0xaa0 + (m) * 0x100)
+#define MV_WIN_XOR_MAX 8
+#define MV_XOR_CHAN_MAX 2
+#define MV_XOR_NON_REMAP 4
+
+#define MV_WIN_PCIE_TARGET_ARMADAXP(n) (4 + (4 * ((n) % 2)))
+#define MV_WIN_PCIE_MEM_ATTR_ARMADAXP(n) (0xE8 + (0x10 * ((n) / 2)))
+#define MV_WIN_PCIE_IO_ATTR_ARMADAXP(n) (0xE0 + (0x10 * ((n) / 2)))
+#define MV_WIN_PCIE_TARGET_ARMADA38X(n) ((n) == 0 ? 8 : 4)
+#define MV_WIN_PCIE_MEM_ATTR_ARMADA38X(n) ((n) < 2 ? 0xE8 : (0xD8 - (((n) % 2) * 0x20)))
+#define MV_WIN_PCIE_IO_ATTR_ARMADA38X(n) ((n) < 2 ? 0xE0 : (0xD0 - (((n) % 2) * 0x20)))
+#if defined(SOC_MV_DISCOVERY) || defined(SOC_MV_KIRKWOOD)
+#define MV_WIN_PCIE_TARGET(n) 4
+#define MV_WIN_PCIE_MEM_ATTR(n) 0xE8
+#define MV_WIN_PCIE_IO_ATTR(n) 0xE0
+#elif defined(SOC_MV_ORION)
+#define MV_WIN_PCIE_TARGET(n) 4
+#define MV_WIN_PCIE_MEM_ATTR(n) 0x59
+#define MV_WIN_PCIE_IO_ATTR(n) 0x51
+#else
+#define MV_WIN_PCIE_TARGET(n) (4 + (4 * ((n) % 2)))
+#define MV_WIN_PCIE_MEM_ATTR(n) (0xE8 + (0x10 * ((n) / 2)))
+#define MV_WIN_PCIE_IO_ATTR(n) (0xE0 + (0x10 * ((n) / 2)))
+#endif
+
+#define MV_WIN_PCI_TARGET 3
+#define MV_WIN_PCI_MEM_ATTR 0x59
+#define MV_WIN_PCI_IO_ATTR 0x51
+
+#define MV_WIN_PCIE_CTRL(n) (0x10 * (((n) < 5) ? (n) : \
+ (n) + 1) + 0x1820)
+#define MV_WIN_PCIE_BASE(n) (0x10 * (((n) < 5) ? (n) : \
+ (n) + 1) + 0x1824)
+#define MV_WIN_PCIE_REMAP(n) (0x10 * (((n) < 5) ? (n) : \
+ (n) + 1) + 0x182C)
+#define MV_WIN_PCIE_MAX 6
+
+#define MV_PCIE_BAR_CTRL(n) (0x04 * (n) + 0x1800)
+#define MV_PCIE_BAR_BASE(n) (0x08 * ((n) < 3 ? (n) : 4) + 0x0010)
+#define MV_PCIE_BAR_BASE_H(n) (0x08 * (n) + 0x0014)
+#define MV_PCIE_BAR_MAX 4
+#define MV_PCIE_BAR_64BIT (0x4)
+#define MV_PCIE_BAR_PREFETCH_EN (0x8)
+
+#define MV_PCIE_CONTROL (0x1a00)
+#define MV_PCIE_ROOT_CMPLX (1 << 1)
+
+#define MV_WIN_SATA_CTRL_ARMADA38X(n) (0x10 * (n) + 0x60)
+#define MV_WIN_SATA_BASE_ARMADA38X(n) (0x10 * (n) + 0x64)
+#define MV_WIN_SATA_SIZE_ARMADA38X(n) (0x10 * (n) + 0x68)
+#define MV_WIN_SATA_MAX_ARMADA38X 4
+#define MV_WIN_SATA_CTRL(n) (0x10 * (n) + 0x30)
+#define MV_WIN_SATA_BASE(n) (0x10 * (n) + 0x34)
+#define MV_WIN_SATA_MAX 4
+
+#define MV_WIN_SDHCI_CTRL(n) (0x8 * (n) + 0x4080)
+#define MV_WIN_SDHCI_BASE(n) (0x8 * (n) + 0x4084)
+#define MV_WIN_SDHCI_MAX 8
+
+#define MV_BOOTROM_MEM_ADDR 0xFFF00000
+#define MV_BOOTROM_WIN_SIZE 0xF
+#define MV_CPU_SUBSYS_REGS_LEN 0x100
+
+#define IO_WIN_9_CTRL_OFFSET 0x98
+#define IO_WIN_9_BASE_OFFSET 0x9C
+
+/* Mbus decoding unit IDs and attributes */
+#define MBUS_BOOTROM_TGT_ID 0x1
+#define MBUS_BOOTROM_ATTR 0x1D
+
+/* Internal Units Sync Barrier Control Register */
+#define MV_SYNC_BARRIER_CTRL 0x84
+#define MV_SYNC_BARRIER_CTRL_ALL 0xFFFF
+
+/* IO Window Control Register fields */
+#define IO_WIN_SIZE_SHIFT 16
+#define IO_WIN_SIZE_MASK 0xFFFF
+#define IO_WIN_COH_ATTR_MASK (0xF << 12)
+#define IO_WIN_ATTR_SHIFT 8
+#define IO_WIN_ATTR_MASK 0xFF
+#define IO_WIN_TGT_SHIFT 4
+#define IO_WIN_TGT_MASK 0xF
+#define IO_WIN_SYNC_SHIFT 1
+#define IO_WIN_SYNC_MASK 0x1
+#define IO_WIN_ENA_SHIFT 0
+#define IO_WIN_ENA_MASK 0x1
+
+#define WIN_REG_IDX_RD(pre,reg,off,base) \
+ static __inline uint32_t \
+ pre ## _ ## reg ## _read(int i) \
+ { \
+ return (bus_space_read_4(fdtbus_bs_tag, base, off(i))); \
+ }
+
+#define WIN_REG_IDX_RD2(pre,reg,off,base) \
+ static __inline uint32_t \
+ pre ## _ ## reg ## _read(int i, int j) \
+ { \
+ return (bus_space_read_4(fdtbus_bs_tag, base, off(i, j))); \
+ } \
+
+#define WIN_REG_BASE_IDX_RD(pre,reg,off) \
+ static __inline uint32_t \
+ pre ## _ ## reg ## _read(uint32_t base, int i) \
+ { \
+ return (bus_space_read_4(fdtbus_bs_tag, base, off(i))); \
+ }
+
+#define WIN_REG_BASE_IDX_RD2(pre,reg,off) \
+ static __inline uint32_t \
+ pre ## _ ## reg ## _read(uint32_t base, int i, int j) \
+ { \
+ return (bus_space_read_4(fdtbus_bs_tag, base, off(i, j))); \
+ }
+
+#define WIN_REG_IDX_WR(pre,reg,off,base) \
+ static __inline void \
+ pre ## _ ## reg ## _write(int i, uint32_t val) \
+ { \
+ bus_space_write_4(fdtbus_bs_tag, base, off(i), val); \
+ }
+
+#define WIN_REG_IDX_WR2(pre,reg,off,base) \
+ static __inline void \
+ pre ## _ ## reg ## _write(int i, int j, uint32_t val) \
+ { \
+ bus_space_write_4(fdtbus_bs_tag, base, off(i, j), val); \
+ }
+
+#define WIN_REG_BASE_IDX_WR(pre,reg,off) \
+ static __inline void \
+ pre ## _ ## reg ## _write(uint32_t base, int i, uint32_t val) \
+ { \
+ bus_space_write_4(fdtbus_bs_tag, base, off(i), val); \
+ }
+
+#define WIN_REG_BASE_IDX_WR2(pre,reg,off) \
+ static __inline void \
+ pre ## _ ## reg ## _write(uint32_t base, int i, int j, uint32_t val) \
+ { \
+ bus_space_write_4(fdtbus_bs_tag, base, off(i, j), val); \
+ }
+
+#define WIN_REG_RD(pre,reg,off,base) \
+ static __inline uint32_t \
+ pre ## _ ## reg ## _read(void) \
+ { \
+ return (bus_space_read_4(fdtbus_bs_tag, base, off)); \
+ }
+
+#define WIN_REG_BASE_RD(pre,reg,off) \
+ static __inline uint32_t \
+ pre ## _ ## reg ## _read(uint32_t base) \
+ { \
+ return (bus_space_read_4(fdtbus_bs_tag, base, off)); \
+ }
+
+#define WIN_REG_WR(pre,reg,off,base) \
+ static __inline void \
+ pre ## _ ## reg ## _write(uint32_t val) \
+ { \
+ bus_space_write_4(fdtbus_bs_tag, base, off, val); \
+ }
+
+#define WIN_REG_BASE_WR(pre,reg,off) \
+ static __inline void \
+ pre ## _ ## reg ## _write(uint32_t base, uint32_t val) \
+ { \
+ bus_space_write_4(fdtbus_bs_tag, base, off, val); \
+ }
+
+#endif /* _MVWIN_H_ */
diff --git a/sys/arm/mv/rtc.c b/sys/arm/mv/rtc.c
new file mode 100644
index 000000000000..61c879d525e3
--- /dev/null
+++ b/sys/arm/mv/rtc.c
@@ -0,0 +1,195 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (C) 2008 MARVELL INTERNATIONAL LTD.
+ * All rights reserved.
+ *
+ * Developed by Semihalf.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of MARVELL nor the names of contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/time.h>
+#include <sys/clock.h>
+#include <sys/resource.h>
+#include <sys/systm.h>
+#include <sys/rman.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "clock_if.h"
+
+#define MV_RTC_TIME_REG 0x00
+#define MV_RTC_DATE_REG 0x04
+#define YEAR_BASE 2000
+
+struct mv_rtc_softc {
+ device_t dev;
+ struct resource *res[1];
+};
+
+static struct resource_spec res_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+static int mv_rtc_probe(device_t dev);
+static int mv_rtc_attach(device_t dev);
+
+static int mv_rtc_gettime(device_t dev, struct timespec *ts);
+static int mv_rtc_settime(device_t dev, struct timespec *ts);
+
+static uint32_t mv_rtc_reg_read(struct mv_rtc_softc *sc, bus_size_t off);
+static int mv_rtc_reg_write(struct mv_rtc_softc *sc, bus_size_t off,
+ uint32_t val);
+
+static device_method_t mv_rtc_methods[] = {
+ DEVMETHOD(device_probe, mv_rtc_probe),
+ DEVMETHOD(device_attach, mv_rtc_attach),
+
+ DEVMETHOD(clock_gettime, mv_rtc_gettime),
+ DEVMETHOD(clock_settime, mv_rtc_settime),
+
+ { 0, 0 },
+};
+
+static driver_t mv_rtc_driver = {
+ "rtc",
+ mv_rtc_methods,
+ sizeof(struct mv_rtc_softc),
+};
+static devclass_t mv_rtc_devclass;
+
+DRIVER_MODULE(mv_rtc, simplebus, mv_rtc_driver, mv_rtc_devclass, 0, 0);
+
+static int
+mv_rtc_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "mrvl,rtc"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Marvell Integrated RTC");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+mv_rtc_attach(device_t dev)
+{
+ struct mv_rtc_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ clock_register(dev, 1000000);
+
+ if (bus_alloc_resources(dev, res_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static int
+mv_rtc_gettime(device_t dev, struct timespec *ts)
+{
+ struct clocktime ct;
+ struct mv_rtc_softc *sc;
+ uint32_t val;
+
+ sc = device_get_softc(dev);
+
+ val = mv_rtc_reg_read(sc, MV_RTC_TIME_REG);
+
+ ct.nsec = 0;
+ ct.sec = FROMBCD(val & 0x7f);
+ ct.min = FROMBCD((val & 0x7f00) >> 8);
+ ct.hour = FROMBCD((val & 0x3f0000) >> 16);
+ ct.dow = FROMBCD((val & 0x7000000) >> 24) - 1;
+
+ val = mv_rtc_reg_read(sc, MV_RTC_DATE_REG);
+
+ ct.day = FROMBCD(val & 0x7f);
+ ct.mon = FROMBCD((val & 0x1f00) >> 8);
+ ct.year = YEAR_BASE + FROMBCD((val & 0xff0000) >> 16);
+
+ return (clock_ct_to_ts(&ct, ts));
+}
+
+static int
+mv_rtc_settime(device_t dev, struct timespec *ts)
+{
+ struct clocktime ct;
+ struct mv_rtc_softc *sc;
+ uint32_t val;
+
+ sc = device_get_softc(dev);
+
+ /* Resolution: 1 sec */
+ if (ts->tv_nsec >= 500000000)
+ ts->tv_sec++;
+ ts->tv_nsec = 0;
+ clock_ts_to_ct(ts, &ct);
+
+ val = TOBCD(ct.sec) | (TOBCD(ct.min) << 8) |
+ (TOBCD(ct.hour) << 16) | (TOBCD( ct.dow + 1) << 24);
+ mv_rtc_reg_write(sc, MV_RTC_TIME_REG, val);
+
+ val = TOBCD(ct.day) | (TOBCD(ct.mon) << 8) |
+ (TOBCD(ct.year - YEAR_BASE) << 16);
+ mv_rtc_reg_write(sc, MV_RTC_DATE_REG, val);
+
+ return (0);
+}
+
+static uint32_t
+mv_rtc_reg_read(struct mv_rtc_softc *sc, bus_size_t off)
+{
+
+ return (bus_read_4(sc->res[0], off));
+}
+
+static int
+mv_rtc_reg_write(struct mv_rtc_softc *sc, bus_size_t off, uint32_t val)
+{
+
+ bus_write_4(sc->res[0], off, val);
+ return (0);
+}
diff --git a/sys/arm/mv/std-pj4b.mv b/sys/arm/mv/std-pj4b.mv
new file mode 100644
index 000000000000..a142ef75204e
--- /dev/null
+++ b/sys/arm/mv/std-pj4b.mv
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+files "../mv/files.arm7"
+cpu CPU_MV_PJ4B
+machine arm armv7
+makeoptions CONF_CFLAGS="-march=armv7a"
+
+# This was originally defined as "(KERNBASE-(1024*1024*1024))" but that
+# (in opt_global.h) clashed with the value emitted by genassym which
+# reduces the original macro text to its numeric value. The only way
+# to avoid that is to define it here as the numeric value genassym emits.
+options VM_MAXUSER_ADDRESS="0x80000000"
diff --git a/sys/arm/mv/timer.c b/sys/arm/mv/timer.c
new file mode 100644
index 000000000000..14052ac2ca25
--- /dev/null
+++ b/sys/arm/mv/timer.c
@@ -0,0 +1,568 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2006 Benno Rice.
+ * Copyright (C) 2007-2008 MARVELL INTERNATIONAL LTD.
+ * All rights reserved.
+ *
+ * Adapted to Marvell SoC by Semihalf.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * from: FreeBSD: //depot/projects/arm/src/sys/arm/xscale/pxa2x0/pxa2x0_timer.c, rev 1
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/eventhandler.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+#include <machine/machdep.h>
+
+#include <arm/mv/mvreg.h>
+#include <arm/mv/mvvar.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#define INITIAL_TIMECOUNTER (0xffffffff)
+#define MAX_WATCHDOG_TICKS (0xffffffff)
+
+#define MV_TMR 0x1
+#define MV_WDT 0x2
+#define MV_NONE 0x0
+
+#define MV_CLOCK_SRC_ARMV7 25000000 /* Timers' 25MHz mode */
+
+#define WATCHDOG_TIMER_ARMV5 2
+
+typedef void (*mv_watchdog_enable_t)(void);
+typedef void (*mv_watchdog_disable_t)(void);
+
+struct mv_timer_config {
+ enum soc_family soc_family;
+ mv_watchdog_enable_t watchdog_enable;
+ mv_watchdog_disable_t watchdog_disable;
+ unsigned int clock_src;
+ uint32_t bridge_irq_cause;
+ uint32_t irq_timer0_clr;
+ uint32_t irq_timer_wd_clr;
+};
+
+struct mv_timer_softc {
+ struct resource * timer_res[2];
+ bus_space_tag_t timer_bst;
+ bus_space_handle_t timer_bsh;
+ struct mtx timer_mtx;
+ struct eventtimer et;
+ boolean_t has_wdt;
+ struct mv_timer_config* config;
+};
+
+static struct resource_spec mv_timer_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE | RF_OPTIONAL },
+ { -1, 0 }
+};
+
+/* Interrupt is not required by MV_WDT devices */
+static struct ofw_compat_data mv_timer_compat[] = {
+ {"marvell,armada-380-timer", MV_NONE },
+ {"marvell,armada-xp-timer", MV_TMR | MV_WDT },
+ {"mrvl,timer", MV_TMR | MV_WDT },
+ {NULL, MV_NONE }
+};
+
+static struct mv_timer_softc *timer_softc = NULL;
+static int timers_initialized = 0;
+
+static int mv_timer_probe(device_t);
+static int mv_timer_attach(device_t);
+
+static int mv_hardclock(void *);
+static unsigned mv_timer_get_timecount(struct timecounter *);
+
+static uint32_t mv_get_timer_control(void);
+static void mv_set_timer_control(uint32_t);
+static uint32_t mv_get_timer(uint32_t);
+static void mv_set_timer(uint32_t, uint32_t);
+static void mv_set_timer_rel(uint32_t, uint32_t);
+static void mv_watchdog_event(void *, unsigned int, int *);
+static int mv_timer_start(struct eventtimer *et,
+ sbintime_t first, sbintime_t period);
+static int mv_timer_stop(struct eventtimer *et);
+static void mv_setup_timers(void);
+
+static void mv_watchdog_enable_armv5(void);
+static void mv_watchdog_enable_armadaxp(void);
+static void mv_watchdog_disable_armv5(void);
+static void mv_watchdog_disable_armadaxp(void);
+
+static void mv_delay(int usec, void* arg);
+
+static struct mv_timer_config timer_armadaxp_config =
+{
+ MV_SOC_ARMADA_XP,
+ &mv_watchdog_enable_armadaxp,
+ &mv_watchdog_disable_armadaxp,
+ MV_CLOCK_SRC_ARMV7,
+ BRIDGE_IRQ_CAUSE_ARMADAXP,
+ IRQ_TIMER0_CLR_ARMADAXP,
+ IRQ_TIMER_WD_CLR_ARMADAXP,
+};
+static struct mv_timer_config timer_armv5_config =
+{
+ MV_SOC_ARMV5,
+ &mv_watchdog_enable_armv5,
+ &mv_watchdog_disable_armv5,
+ 0,
+ BRIDGE_IRQ_CAUSE,
+ IRQ_TIMER0_CLR,
+ IRQ_TIMER_WD_CLR,
+};
+
+static struct ofw_compat_data mv_timer_soc_config[] = {
+ {"marvell,armada-xp-timer", (uintptr_t)&timer_armadaxp_config },
+ {"mrvl,timer", (uintptr_t)&timer_armv5_config },
+ {NULL, (uintptr_t)NULL },
+};
+
+static struct timecounter mv_timer_timecounter = {
+ .tc_get_timecount = mv_timer_get_timecount,
+ .tc_name = "CPUTimer1",
+ .tc_frequency = 0, /* This is assigned on the fly in the init sequence */
+ .tc_counter_mask = ~0u,
+ .tc_quality = 1000,
+};
+
+static int
+mv_timer_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, mv_timer_compat)->ocd_data == MV_NONE)
+ return (ENXIO);
+
+ device_set_desc(dev, "Marvell CPU Timer");
+ return (0);
+}
+
+static int
+mv_timer_attach(device_t dev)
+{
+ int error;
+ void *ihl;
+ struct mv_timer_softc *sc;
+ uint32_t irq_cause, irq_mask;
+
+ if (timer_softc != NULL)
+ return (ENXIO);
+
+ sc = (struct mv_timer_softc *)device_get_softc(dev);
+ timer_softc = sc;
+
+ sc->config = (struct mv_timer_config*)
+ ofw_bus_search_compatible(dev, mv_timer_soc_config)->ocd_data;
+
+ if (sc->config->clock_src == 0)
+ sc->config->clock_src = get_tclk();
+
+ error = bus_alloc_resources(dev, mv_timer_spec, sc->timer_res);
+ if (error) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ sc->timer_bst = rman_get_bustag(sc->timer_res[0]);
+ sc->timer_bsh = rman_get_bushandle(sc->timer_res[0]);
+
+ sc->has_wdt = ofw_bus_has_prop(dev, "mrvl,has-wdt");
+
+ mtx_init(&timer_softc->timer_mtx, "watchdog", NULL, MTX_DEF);
+
+ if (sc->has_wdt) {
+ if (sc->config->watchdog_disable)
+ sc->config->watchdog_disable();
+ EVENTHANDLER_REGISTER(watchdog_list, mv_watchdog_event, sc, 0);
+ }
+
+ if (ofw_bus_search_compatible(dev, mv_timer_compat)->ocd_data
+ == MV_WDT) {
+ /* Don't set timers for wdt-only entry. */
+ device_printf(dev, "only watchdog attached\n");
+ return (0);
+ } else if (sc->timer_res[1] == NULL) {
+ device_printf(dev, "no interrupt resource\n");
+ bus_release_resources(dev, mv_timer_spec, sc->timer_res);
+ return (ENXIO);
+ }
+
+ if (bus_setup_intr(dev, sc->timer_res[1], INTR_TYPE_CLK,
+ mv_hardclock, NULL, sc, &ihl) != 0) {
+ bus_release_resources(dev, mv_timer_spec, sc->timer_res);
+ device_printf(dev, "Could not setup interrupt.\n");
+ return (ENXIO);
+ }
+
+ mv_setup_timers();
+ if (sc->config->soc_family != MV_SOC_ARMADA_XP ) {
+ irq_cause = read_cpu_ctrl(sc->config->bridge_irq_cause);
+ irq_cause &= sc->config->irq_timer0_clr;
+
+ write_cpu_ctrl(sc->config->bridge_irq_cause, irq_cause);
+ irq_mask = read_cpu_ctrl(BRIDGE_IRQ_MASK);
+ irq_mask |= IRQ_TIMER0_MASK;
+ irq_mask &= ~IRQ_TIMER1_MASK;
+ write_cpu_ctrl(BRIDGE_IRQ_MASK, irq_mask);
+ }
+ sc->et.et_name = "CPUTimer0";
+ sc->et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT;
+ sc->et.et_quality = 1000;
+
+ sc->et.et_frequency = sc->config->clock_src;
+ sc->et.et_min_period = (0x00000002LLU << 32) / sc->et.et_frequency;
+ sc->et.et_max_period = (0xfffffffeLLU << 32) / sc->et.et_frequency;
+ sc->et.et_start = mv_timer_start;
+ sc->et.et_stop = mv_timer_stop;
+ sc->et.et_priv = sc;
+ et_register(&sc->et);
+ mv_timer_timecounter.tc_frequency = sc->config->clock_src;
+ tc_init(&mv_timer_timecounter);
+
+#ifdef PLATFORM
+ arm_set_delay(mv_delay, NULL);
+#endif
+ return (0);
+}
+
+static int
+mv_hardclock(void *arg)
+{
+ struct mv_timer_softc *sc;
+ uint32_t irq_cause;
+
+ irq_cause = read_cpu_ctrl(timer_softc->config->bridge_irq_cause);
+ irq_cause &= timer_softc->config->irq_timer0_clr;
+ write_cpu_ctrl(timer_softc->config->bridge_irq_cause, irq_cause);
+
+ sc = (struct mv_timer_softc *)arg;
+ if (sc->et.et_active)
+ sc->et.et_event_cb(&sc->et, sc->et.et_arg);
+
+ return (FILTER_HANDLED);
+}
+
+static device_method_t mv_timer_methods[] = {
+ DEVMETHOD(device_probe, mv_timer_probe),
+ DEVMETHOD(device_attach, mv_timer_attach),
+ { 0, 0 }
+};
+
+static driver_t mv_timer_driver = {
+ "timer",
+ mv_timer_methods,
+ sizeof(struct mv_timer_softc),
+};
+
+static devclass_t mv_timer_devclass;
+
+DRIVER_MODULE(timer_mv, simplebus, mv_timer_driver, mv_timer_devclass, 0, 0);
+
+static unsigned
+mv_timer_get_timecount(struct timecounter *tc)
+{
+
+ return (INITIAL_TIMECOUNTER - mv_get_timer(1));
+}
+
+static void
+mv_delay(int usec, void* arg)
+{
+ uint32_t val, val_temp;
+ int32_t nticks;
+
+ val = mv_get_timer(1);
+ nticks = ((timer_softc->config->clock_src / 1000000 + 1) * usec);
+
+ while (nticks > 0) {
+ val_temp = mv_get_timer(1);
+ if (val > val_temp)
+ nticks -= (val - val_temp);
+ else
+ nticks -= (val + (INITIAL_TIMECOUNTER - val_temp));
+
+ val = val_temp;
+ }
+}
+
+#ifndef PLATFORM
+void
+DELAY(int usec)
+{
+ uint32_t val;
+
+ if (!timers_initialized) {
+ for (; usec > 0; usec--)
+ for (val = 100; val > 0; val--)
+ __asm __volatile("nop" ::: "memory");
+ } else {
+ TSENTER();
+ mv_delay(usec, NULL);
+ TSEXIT();
+ }
+}
+#endif
+
+static uint32_t
+mv_get_timer_control(void)
+{
+
+ return (bus_space_read_4(timer_softc->timer_bst,
+ timer_softc->timer_bsh, CPU_TIMER_CONTROL));
+}
+
+static void
+mv_set_timer_control(uint32_t val)
+{
+
+ bus_space_write_4(timer_softc->timer_bst,
+ timer_softc->timer_bsh, CPU_TIMER_CONTROL, val);
+}
+
+static uint32_t
+mv_get_timer(uint32_t timer)
+{
+
+ return (bus_space_read_4(timer_softc->timer_bst,
+ timer_softc->timer_bsh, CPU_TIMER0 + timer * 0x8));
+}
+
+static void
+mv_set_timer(uint32_t timer, uint32_t val)
+{
+
+ bus_space_write_4(timer_softc->timer_bst,
+ timer_softc->timer_bsh, CPU_TIMER0 + timer * 0x8, val);
+}
+
+static void
+mv_set_timer_rel(uint32_t timer, uint32_t val)
+{
+
+ bus_space_write_4(timer_softc->timer_bst,
+ timer_softc->timer_bsh, CPU_TIMER0_REL + timer * 0x8, val);
+}
+
+static void
+mv_watchdog_enable_armv5(void)
+{
+ uint32_t val, irq_cause, irq_mask;
+
+ irq_cause = read_cpu_ctrl(timer_softc->config->bridge_irq_cause);
+ irq_cause &= timer_softc->config->irq_timer_wd_clr;
+ write_cpu_ctrl(timer_softc->config->bridge_irq_cause, irq_cause);
+
+ irq_mask = read_cpu_ctrl(BRIDGE_IRQ_MASK);
+ irq_mask |= IRQ_TIMER_WD_MASK;
+ write_cpu_ctrl(BRIDGE_IRQ_MASK, irq_mask);
+
+ val = read_cpu_ctrl(RSTOUTn_MASK);
+ val |= WD_RST_OUT_EN;
+ write_cpu_ctrl(RSTOUTn_MASK, val);
+
+ val = mv_get_timer_control();
+ val |= CPU_TIMER2_EN | CPU_TIMER2_AUTO;
+ mv_set_timer_control(val);
+}
+
+static void
+mv_watchdog_enable_armadaxp(void)
+{
+ uint32_t irq_cause, val;
+
+ irq_cause = read_cpu_ctrl(timer_softc->config->bridge_irq_cause);
+ irq_cause &= timer_softc->config->irq_timer_wd_clr;
+ write_cpu_ctrl(timer_softc->config->bridge_irq_cause, irq_cause);
+
+ val = read_cpu_mp_clocks(WD_RSTOUTn_MASK);
+ val |= (WD_GLOBAL_MASK | WD_CPU0_MASK);
+ write_cpu_mp_clocks(WD_RSTOUTn_MASK, val);
+
+ val = read_cpu_misc(RSTOUTn_MASK_ARMV7);
+ val &= ~RSTOUTn_MASK_WD;
+ write_cpu_misc(RSTOUTn_MASK_ARMV7, val);
+
+ val = mv_get_timer_control();
+ val |= CPU_TIMER2_EN | CPU_TIMER2_AUTO | CPU_TIMER_WD_25MHZ_EN;
+ mv_set_timer_control(val);
+}
+
+static void
+mv_watchdog_disable_armv5(void)
+{
+ uint32_t val, irq_cause,irq_mask;
+
+ val = mv_get_timer_control();
+ val &= ~(CPU_TIMER2_EN | CPU_TIMER2_AUTO);
+ mv_set_timer_control(val);
+
+ val = read_cpu_ctrl(RSTOUTn_MASK);
+ val &= ~WD_RST_OUT_EN;
+ write_cpu_ctrl(RSTOUTn_MASK, val);
+
+ irq_mask = read_cpu_ctrl(BRIDGE_IRQ_MASK);
+ irq_mask &= ~(IRQ_TIMER_WD_MASK);
+ write_cpu_ctrl(BRIDGE_IRQ_MASK, irq_mask);
+
+ irq_cause = read_cpu_ctrl(timer_softc->config->bridge_irq_cause);
+ irq_cause &= timer_softc->config->irq_timer_wd_clr;
+ write_cpu_ctrl(timer_softc->config->bridge_irq_cause, irq_cause);
+}
+
+static void
+mv_watchdog_disable_armadaxp(void)
+{
+ uint32_t val, irq_cause;
+
+ val = read_cpu_mp_clocks(WD_RSTOUTn_MASK);
+ val &= ~(WD_GLOBAL_MASK | WD_CPU0_MASK);
+ write_cpu_mp_clocks(WD_RSTOUTn_MASK, val);
+
+ val = read_cpu_misc(RSTOUTn_MASK_ARMV7);
+ val |= RSTOUTn_MASK_WD;
+ write_cpu_misc(RSTOUTn_MASK_ARMV7, RSTOUTn_MASK_WD);
+
+ irq_cause = read_cpu_ctrl(timer_softc->config->bridge_irq_cause);
+ irq_cause &= timer_softc->config->irq_timer_wd_clr;
+ write_cpu_ctrl(timer_softc->config->bridge_irq_cause, irq_cause);
+
+ val = mv_get_timer_control();
+ val &= ~(CPU_TIMER2_EN | CPU_TIMER2_AUTO);
+ mv_set_timer_control(val);
+}
+
+/*
+ * Watchdog event handler.
+ */
+static void
+mv_watchdog_event(void *arg, unsigned int cmd, int *error)
+{
+ uint64_t ns;
+ uint64_t ticks;
+
+ mtx_lock(&timer_softc->timer_mtx);
+ if (cmd == 0) {
+ if (timer_softc->config->watchdog_disable != NULL)
+ timer_softc->config->watchdog_disable();
+ } else {
+ /*
+ * Watchdog timeout is in nanosecs, calculation according to
+ * watchdog(9)
+ */
+ ns = (uint64_t)1 << (cmd & WD_INTERVAL);
+ ticks = (uint64_t)(ns * timer_softc->config->clock_src) / 1000000000;
+ if (ticks > MAX_WATCHDOG_TICKS) {
+ if (timer_softc->config->watchdog_disable != NULL)
+ timer_softc->config->watchdog_disable();
+ } else {
+ mv_set_timer(WATCHDOG_TIMER_ARMV5, ticks);
+ if (timer_softc->config->watchdog_enable != NULL)
+ timer_softc->config->watchdog_enable();
+ *error = 0;
+ }
+ }
+ mtx_unlock(&timer_softc->timer_mtx);
+}
+
+static int
+mv_timer_start(struct eventtimer *et, sbintime_t first, sbintime_t period)
+{
+ struct mv_timer_softc *sc;
+ uint32_t val, val1;
+
+ /* Calculate dividers. */
+ sc = (struct mv_timer_softc *)et->et_priv;
+ if (period != 0)
+ val = ((uint32_t)sc->et.et_frequency * period) >> 32;
+ else
+ val = 0;
+ if (first != 0)
+ val1 = ((uint32_t)sc->et.et_frequency * first) >> 32;
+ else
+ val1 = val;
+
+ /* Apply configuration. */
+ mv_set_timer_rel(0, val);
+ mv_set_timer(0, val1);
+ val = mv_get_timer_control();
+ val |= CPU_TIMER0_EN;
+ if (period != 0)
+ val |= CPU_TIMER0_AUTO;
+ else
+ val &= ~CPU_TIMER0_AUTO;
+ mv_set_timer_control(val);
+ return (0);
+}
+
+static int
+mv_timer_stop(struct eventtimer *et)
+{
+ uint32_t val;
+
+ val = mv_get_timer_control();
+ val &= ~(CPU_TIMER0_EN | CPU_TIMER0_AUTO);
+ mv_set_timer_control(val);
+ return (0);
+}
+
+static void
+mv_setup_timers(void)
+{
+ uint32_t val;
+
+ mv_set_timer_rel(1, INITIAL_TIMECOUNTER);
+ mv_set_timer(1, INITIAL_TIMECOUNTER);
+ val = mv_get_timer_control();
+ val &= ~(CPU_TIMER0_EN | CPU_TIMER0_AUTO);
+ val |= CPU_TIMER1_EN | CPU_TIMER1_AUTO;
+
+ if (timer_softc->config->soc_family == MV_SOC_ARMADA_XP) {
+ /* Enable 25MHz mode */
+ val |= CPU_TIMER0_25MHZ_EN | CPU_TIMER1_25MHZ_EN;
+ }
+
+ mv_set_timer_control(val);
+ timers_initialized = 1;
+}
diff --git a/sys/arm/nvidia/as3722.c b/sys/arm/nvidia/as3722.c
new file mode 100644
index 000000000000..60301c78a2fb
--- /dev/null
+++ b/sys/arm/nvidia/as3722.c
@@ -0,0 +1,411 @@
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * AS3722 PMIC driver
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/gpio.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/sx.h>
+
+#include <machine/bus.h>
+
+#include <dev/extres/regulator/regulator.h>
+#include <dev/fdt/fdt_pinctrl.h>
+#include <dev/gpio/gpiobusvar.h>
+#include <dev/iicbus/iiconf.h>
+#include <dev/iicbus/iicbus.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dt-bindings/mfd/as3722.h>
+
+#include "clock_if.h"
+#include "regdev_if.h"
+
+#include "as3722.h"
+
+static struct ofw_compat_data compat_data[] = {
+ {"ams,as3722", 1},
+ {NULL, 0},
+};
+
+#define LOCK(_sc) sx_xlock(&(_sc)->lock)
+#define UNLOCK(_sc) sx_xunlock(&(_sc)->lock)
+#define LOCK_INIT(_sc) sx_init(&(_sc)->lock, "as3722")
+#define LOCK_DESTROY(_sc) sx_destroy(&(_sc)->lock);
+#define ASSERT_LOCKED(_sc) sx_assert(&(_sc)->lock, SA_XLOCKED);
+#define ASSERT_UNLOCKED(_sc) sx_assert(&(_sc)->lock, SA_UNLOCKED);
+
+#define AS3722_DEVICE_ID 0x0C
+
+/*
+ * Raw register access function.
+ */
+int
+as3722_read(struct as3722_softc *sc, uint8_t reg, uint8_t *val)
+{
+ uint8_t addr;
+ int rv;
+ struct iic_msg msgs[2] = {
+ {0, IIC_M_WR, 1, &addr},
+ {0, IIC_M_RD, 1, val},
+ };
+
+ msgs[0].slave = sc->bus_addr;
+ msgs[1].slave = sc->bus_addr;
+ addr = reg;
+
+ rv = iicbus_transfer(sc->dev, msgs, 2);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Error when reading reg 0x%02X, rv: %d\n", reg, rv);
+ return (EIO);
+ }
+
+ return (0);
+}
+
+int as3722_read_buf(struct as3722_softc *sc, uint8_t reg, uint8_t *buf,
+ size_t size)
+{
+ uint8_t addr;
+ int rv;
+ struct iic_msg msgs[2] = {
+ {0, IIC_M_WR, 1, &addr},
+ {0, IIC_M_RD, size, buf},
+ };
+
+ msgs[0].slave = sc->bus_addr;
+ msgs[1].slave = sc->bus_addr;
+ addr = reg;
+
+ rv = iicbus_transfer(sc->dev, msgs, 2);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Error when reading reg 0x%02X, rv: %d\n", reg, rv);
+ return (EIO);
+ }
+
+ return (0);
+}
+
+int
+as3722_write(struct as3722_softc *sc, uint8_t reg, uint8_t val)
+{
+ uint8_t data[2];
+ int rv;
+
+ struct iic_msg msgs[1] = {
+ {0, IIC_M_WR, 2, data},
+ };
+
+ msgs[0].slave = sc->bus_addr;
+ data[0] = reg;
+ data[1] = val;
+
+ rv = iicbus_transfer(sc->dev, msgs, 1);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Error when writing reg 0x%02X, rv: %d\n", reg, rv);
+ return (EIO);
+ }
+ return (0);
+}
+
+int as3722_write_buf(struct as3722_softc *sc, uint8_t reg, uint8_t *buf,
+ size_t size)
+{
+ uint8_t data[1];
+ int rv;
+ struct iic_msg msgs[2] = {
+ {0, IIC_M_WR, 1, data},
+ {0, IIC_M_WR | IIC_M_NOSTART, size, buf},
+ };
+
+ msgs[0].slave = sc->bus_addr;
+ msgs[1].slave = sc->bus_addr;
+ data[0] = reg;
+
+ rv = iicbus_transfer(sc->dev, msgs, 2);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Error when writing reg 0x%02X, rv: %d\n", reg, rv);
+ return (EIO);
+ }
+ return (0);
+}
+
+int
+as3722_modify(struct as3722_softc *sc, uint8_t reg, uint8_t clear, uint8_t set)
+{
+ uint8_t val;
+ int rv;
+
+ rv = as3722_read(sc, reg, &val);
+ if (rv != 0)
+ return (rv);
+
+ val &= ~clear;
+ val |= set;
+
+ rv = as3722_write(sc, reg, val);
+ if (rv != 0)
+ return (rv);
+
+ return (0);
+}
+
+static int
+as3722_get_version(struct as3722_softc *sc)
+{
+ uint8_t reg;
+ int rv;
+
+ /* Verify AS3722 ID and version. */
+ rv = RD1(sc, AS3722_ASIC_ID1, &reg);
+ if (rv != 0)
+ return (ENXIO);
+
+ if (reg != AS3722_DEVICE_ID) {
+ device_printf(sc->dev, "Invalid chip ID is 0x%x\n", reg);
+ return (ENXIO);
+ }
+
+ rv = RD1(sc, AS3722_ASIC_ID2, &sc->chip_rev);
+ if (rv != 0)
+ return (ENXIO);
+
+ if (bootverbose)
+ device_printf(sc->dev, "AS3722 rev: 0x%x\n", sc->chip_rev);
+ return (0);
+}
+
+static int
+as3722_init(struct as3722_softc *sc)
+{
+ uint32_t reg;
+ int rv;
+
+ reg = 0;
+ if (sc->int_pullup)
+ reg |= AS3722_INT_PULL_UP;
+ if (sc->i2c_pullup)
+ reg |= AS3722_I2C_PULL_UP;
+
+ rv = RM1(sc, AS3722_IO_VOLTAGE,
+ AS3722_INT_PULL_UP | AS3722_I2C_PULL_UP, reg);
+ if (rv != 0)
+ return (ENXIO);
+
+ /* mask interrupts */
+ rv = WR1(sc, AS3722_INTERRUPT_MASK1, 0);
+ if (rv != 0)
+ return (ENXIO);
+ rv = WR1(sc, AS3722_INTERRUPT_MASK2, 0);
+ if (rv != 0)
+ return (ENXIO);
+ rv = WR1(sc, AS3722_INTERRUPT_MASK3, 0);
+ if (rv != 0)
+ return (ENXIO);
+ rv = WR1(sc, AS3722_INTERRUPT_MASK4, 0);
+ if (rv != 0)
+ return (ENXIO);
+ return (0);
+}
+
+static int
+as3722_parse_fdt(struct as3722_softc *sc, phandle_t node)
+{
+
+ sc->int_pullup =
+ OF_hasprop(node, "ams,enable-internal-int-pullup") ? 1 : 0;
+ sc->i2c_pullup =
+ OF_hasprop(node, "ams,enable-internal-i2c-pullup") ? 1 : 0;
+ return 0;
+}
+
+static void
+as3722_intr(void *arg)
+{
+ struct as3722_softc *sc;
+
+ sc = (struct as3722_softc *)arg;
+ /* XXX Finish temperature alarms. */
+}
+
+static int
+as3722_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+ return (ENXIO);
+
+ device_set_desc(dev, "AS3722 PMIC");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+as3722_attach(device_t dev)
+{
+ struct as3722_softc *sc;
+ const char *dname;
+ int dunit, rv, rid;
+ phandle_t node;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ sc->bus_addr = iicbus_get_addr(dev);
+ node = ofw_bus_get_node(sc->dev);
+ dname = device_get_name(dev);
+ dunit = device_get_unit(dev);
+ rv = 0;
+ LOCK_INIT(sc);
+
+ rid = 0;
+ sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (sc->irq_res == NULL) {
+ device_printf(dev, "Cannot allocate interrupt.\n");
+ rv = ENXIO;
+ goto fail;
+ }
+
+ rv = as3722_parse_fdt(sc, node);
+ if (rv != 0)
+ goto fail;
+ rv = as3722_get_version(sc);
+ if (rv != 0)
+ goto fail;
+ rv = as3722_init(sc);
+ if (rv != 0)
+ goto fail;
+ rv = as3722_regulator_attach(sc, node);
+ if (rv != 0)
+ goto fail;
+ rv = as3722_gpio_attach(sc, node);
+ if (rv != 0)
+ goto fail;
+ rv = as3722_rtc_attach(sc, node);
+ if (rv != 0)
+ goto fail;
+
+ fdt_pinctrl_register(dev, NULL);
+ fdt_pinctrl_configure_by_name(dev, "default");
+
+ /* Setup interrupt. */
+ rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+ NULL, as3722_intr, sc, &sc->irq_h);
+ if (rv) {
+ device_printf(dev, "Cannot setup interrupt.\n");
+ goto fail;
+ }
+ return (bus_generic_attach(dev));
+
+fail:
+ if (sc->irq_h != NULL)
+ bus_teardown_intr(dev, sc->irq_res, sc->irq_h);
+ if (sc->irq_res != NULL)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+ LOCK_DESTROY(sc);
+ return (rv);
+}
+
+static int
+as3722_detach(device_t dev)
+{
+ struct as3722_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (sc->irq_h != NULL)
+ bus_teardown_intr(dev, sc->irq_res, sc->irq_h);
+ if (sc->irq_res != NULL)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+ LOCK_DESTROY(sc);
+
+ return (bus_generic_detach(dev));
+}
+
+static phandle_t
+as3722_gpio_get_node(device_t bus, device_t dev)
+{
+
+ /* We only have one child, the GPIO bus, which needs our own node. */
+ return (ofw_bus_get_node(bus));
+}
+
+static device_method_t as3722_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, as3722_probe),
+ DEVMETHOD(device_attach, as3722_attach),
+ DEVMETHOD(device_detach, as3722_detach),
+
+ /* Regdev interface */
+ DEVMETHOD(regdev_map, as3722_regulator_map),
+
+ /* RTC interface */
+ DEVMETHOD(clock_gettime, as3722_rtc_gettime),
+ DEVMETHOD(clock_settime, as3722_rtc_settime),
+
+ /* GPIO protocol interface */
+ DEVMETHOD(gpio_get_bus, as3722_gpio_get_bus),
+ DEVMETHOD(gpio_pin_max, as3722_gpio_pin_max),
+ DEVMETHOD(gpio_pin_getname, as3722_gpio_pin_getname),
+ DEVMETHOD(gpio_pin_getflags, as3722_gpio_pin_getflags),
+ DEVMETHOD(gpio_pin_getcaps, as3722_gpio_pin_getcaps),
+ DEVMETHOD(gpio_pin_setflags, as3722_gpio_pin_setflags),
+ DEVMETHOD(gpio_pin_get, as3722_gpio_pin_get),
+ DEVMETHOD(gpio_pin_set, as3722_gpio_pin_set),
+ DEVMETHOD(gpio_pin_toggle, as3722_gpio_pin_toggle),
+ DEVMETHOD(gpio_map_gpios, as3722_gpio_map_gpios),
+
+ /* fdt_pinctrl interface */
+ DEVMETHOD(fdt_pinctrl_configure, as3722_pinmux_configure),
+
+ /* ofw_bus interface */
+ DEVMETHOD(ofw_bus_get_node, as3722_gpio_get_node),
+
+ DEVMETHOD_END
+};
+
+static devclass_t as3722_devclass;
+static DEFINE_CLASS_0(gpio, as3722_driver, as3722_methods,
+ sizeof(struct as3722_softc));
+EARLY_DRIVER_MODULE(as3722, iicbus, as3722_driver, as3722_devclass,
+ NULL, NULL, 74);
diff --git a/sys/arm/nvidia/as3722.h b/sys/arm/nvidia/as3722.h
new file mode 100644
index 000000000000..c559a8a27681
--- /dev/null
+++ b/sys/arm/nvidia/as3722.h
@@ -0,0 +1,323 @@
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _AS3722_H_
+
+#include <sys/clock.h>
+
+#define AS3722_SD0_VOLTAGE 0x00
+#define AS3722_SD_VSEL_MASK 0x7F /* For all SD */
+#define AS3722_SD0_VSEL_MIN 0x01
+#define AS3722_SD0_VSEL_MAX 0x5A
+#define AS3722_SD0_VSEL_LOW_VOL_MAX 0x6E
+
+#define AS3722_SD1_VOLTAGE 0x01
+#define AS3722_SD2_VOLTAGE 0x02
+#define AS3722_SD2_VSEL_MIN 0x01
+#define AS3722_SD2_VSEL_MAX 0x7F
+#define AS3722_SD3_VOLTAGE 0x03
+#define AS3722_SD4_VOLTAGE 0x04
+#define AS3722_SD5_VOLTAGE 0x05
+#define AS3722_SD6_VOLTAGE 0x06
+#define AS3722_GPIO0_CONTROL 0x08
+#define AS3722_GPIO_INVERT 0x80
+#define AS3722_GPIO_IOSF_MASK 0x0F
+#define AS3722_GPIO_IOSF_SHIFT 3
+#define AS3722_GPIO_MODE_MASK 0x07
+#define AS3722_GPIO_MODE_SHIFT 0
+
+#define AS3722_GPIO1_CONTROL 0x09
+#define AS3722_GPIO2_CONTROL 0x0A
+#define AS3722_GPIO3_CONTROL 0x0B
+#define AS3722_GPIO4_CONTROL 0x0C
+#define AS3722_GPIO5_CONTROL 0x0D
+#define AS3722_GPIO6_CONTROL 0x0E
+#define AS3722_GPIO7_CONTROL 0x0F
+#define AS3722_LDO0_VOLTAGE 0x10
+#define AS3722_LDO0_VSEL_MASK 0x1F
+#define AS3722_LDO0_VSEL_MIN 0x01
+#define AS3722_LDO0_VSEL_MAX 0x12
+#define AS3722_LDO0_NUM_VOLT 0x12
+
+#define AS3722_LDO1_VOLTAGE 0x11
+#define AS3722_LDO_VSEL_MASK 0x7F
+#define AS3722_LDO_VSEL_MIN 0x01
+#define AS3722_LDO_VSEL_MAX 0x7F
+#define AS3722_LDO_VSEL_DNU_MIN 0x25
+#define AS3722_LDO_VSEL_DNU_MAX 0x3F
+#define AS3722_LDO_NUM_VOLT 0x80
+
+#define AS3722_LDO2_VOLTAGE 0x12
+#define AS3722_LDO3_VOLTAGE 0x13
+#define AS3722_LDO3_VSEL_MASK 0x3F
+#define AS3722_LDO3_VSEL_MIN 0x01
+#define AS3722_LDO3_VSEL_MAX 0x2D
+#define AS3722_LDO3_NUM_VOLT 0x2D
+#define AS3722_LDO3_MODE_MASK (0x3 << 6)
+#define AS3722_LDO3_MODE_GET(x) (((x) >> 6) & 0x3)
+#define AS3722_LDO3_MODE(x) (((x) & 0x3) << 6)
+#define AS3722_LDO3_MODE_PMOS AS3722_LDO3_MODE(0)
+#define AS3722_LDO3_MODE_PMOS_TRACKING AS3722_LDO3_MODE(1)
+#define AS3722_LDO3_MODE_NMOS AS3722_LDO3_MODE(2)
+#define AS3722_LDO3_MODE_SWITCH AS3722_LDO3_MODE(3)
+
+#define AS3722_LDO4_VOLTAGE 0x14
+#define AS3722_LDO5_VOLTAGE 0x15
+#define AS3722_LDO6_VOLTAGE 0x16
+#define AS3722_LDO6_SEL_BYPASS 0x3F
+#define AS3722_LDO7_VOLTAGE 0x17
+#define AS3722_LDO9_VOLTAGE 0x19
+#define AS3722_LDO10_VOLTAGE 0x1A
+#define AS3722_LDO11_VOLTAGE 0x1B
+#define AS3722_LDO3_SETTINGS 0x1D
+#define AS3722_GPIO_DEB1 0x1E
+#define AS3722_GPIO_DEB2 0x1F
+#define AS3722_GPIO_SIGNAL_OUT 0x20
+#define AS3722_GPIO_SIGNAL_IN 0x21
+#define AS3722_REG_SEQU_MOD1 0x22
+#define AS3722_REG_SEQU_MOD2 0x23
+#define AS3722_REG_SEQU_MOD3 0x24
+#define AS3722_SD_PHSW_CTRL 0x27
+#define AS3722_SD_PHSW_STATUS 0x28
+
+#define AS3722_SD0_CONTROL 0x29
+#define AS3722_SD0_MODE_FAST (1 << 4)
+
+#define AS3722_SD1_CONTROL 0x2A
+#define AS3722_SD1_MODE_FAST (1 << 4)
+
+#define AS3722_SDMPH_CONTROL 0x2B
+#define AS3722_SD23_CONTROL 0x2C
+#define AS3722_SD3_MODE_FAST (1 << 6)
+#define AS3722_SD2_MODE_FAST (1 << 2)
+
+#define AS3722_SD4_CONTROL 0x2D
+#define AS3722_SD4_MODE_FAST (1 << 2)
+
+#define AS3722_SD5_CONTROL 0x2E
+#define AS3722_SD5_MODE_FAST (1 << 2)
+
+#define AS3722_SD6_CONTROL 0x2F
+#define AS3722_SD6_MODE_FAST (1 << 4)
+
+#define AS3722_SD_DVM 0x30
+#define AS3722_RESET_REASON 0x31
+#define AS3722_BATTERY_VOLTAGE_MONITOR 0x32
+#define AS3722_STARTUP_CONTROL 0x33
+#define AS3722_RESET_TIMER 0x34
+#define AS3722_REFERENCE_CONTROL 0x35
+#define AS3722_RESET_CONTROL 0x36
+#define AS3722_OVERTEMPERATURE_CONTROL 0x37
+#define AS3722_WATCHDOG_CONTROL 0x38
+#define AS3722_REG_STANDBY_MOD1 0x39
+#define AS3722_REG_STANDBY_MOD2 0x3A
+#define AS3722_REG_STANDBY_MOD3 0x3B
+#define AS3722_ENABLE_CTRL1 0x3C
+#define AS3722_SD3_EXT_ENABLE_MASK 0xC0
+#define AS3722_SD2_EXT_ENABLE_MASK 0x30
+#define AS3722_SD1_EXT_ENABLE_MASK 0x0C
+#define AS3722_SD0_EXT_ENABLE_MASK 0x03
+
+#define AS3722_ENABLE_CTRL2 0x3D
+#define AS3722_SD6_EXT_ENABLE_MASK 0x30
+#define AS3722_SD5_EXT_ENABLE_MASK 0x0C
+#define AS3722_SD4_EXT_ENABLE_MASK 0x03
+
+#define AS3722_ENABLE_CTRL3 0x3E
+#define AS3722_LDO3_EXT_ENABLE_MASK 0xC0
+#define AS3722_LDO2_EXT_ENABLE_MASK 0x30
+#define AS3722_LDO1_EXT_ENABLE_MASK 0x0C
+#define AS3722_LDO0_EXT_ENABLE_MASK 0x03
+
+#define AS3722_ENABLE_CTRL4 0x3F
+#define AS3722_LDO7_EXT_ENABLE_MASK 0xC0
+#define AS3722_LDO6_EXT_ENABLE_MASK 0x30
+#define AS3722_LDO5_EXT_ENABLE_MASK 0x0C
+#define AS3722_LDO4_EXT_ENABLE_MASK 0x03
+
+#define AS3722_ENABLE_CTRL5 0x40
+#define AS3722_LDO11_EXT_ENABLE_MASK 0xC0
+#define AS3722_LDO10_EXT_ENABLE_MASK 0x30
+#define AS3722_LDO9_EXT_ENABLE_MASK 0x0C
+
+#define AS3722_PWM_CONTROL_L 0x41
+#define AS3722_PWM_CONTROL_H 0x42
+#define AS3722_WATCHDOG_TIMER 0x46
+#define AS3722_WATCHDOG_SOFTWARE_SIGNAL 0x48
+#define AS3722_IO_VOLTAGE 0x49
+#define AS3722_I2C_PULL_UP (1 << 4)
+#define AS3722_INT_PULL_UP (1 << 5)
+
+#define AS3722_BATTERY_VOLTAGE_MONITOR2 0x4A
+#define AS3722_SD_CONTROL 0x4D
+#define AS3722_SDN_CTRL(x) (1 << (x))
+
+#define AS3722_LDO_CONTROL0 0x4E
+#define AS3722_LDO7_CTRL (1 << 7)
+#define AS3722_LDO6_CTRL (1 << 6)
+#define AS3722_LDO5_CTRL (1 << 5)
+#define AS3722_LDO4_CTRL (1 << 4)
+#define AS3722_LDO3_CTRL (1 << 3)
+#define AS3722_LDO2_CTRL (1 << 2)
+#define AS3722_LDO1_CTRL (1 << 1)
+#define AS3722_LDO0_CTRL (1 << 0)
+
+#define AS3722_LDO_CONTROL1 0x4F
+#define AS3722_LDO11_CTRL (1 << 3)
+#define AS3722_LDO10_CTRL (1 << 2)
+#define AS3722_LDO9_CTRL (1 << 1)
+
+#define AS3722_SD0_PROTECT 0x50
+#define AS3722_SD6_PROTECT 0x51
+#define AS3722_PWM_VCONTROL1 0x52
+#define AS3722_PWM_VCONTROL2 0x53
+#define AS3722_PWM_VCONTROL3 0x54
+#define AS3722_PWM_VCONTROL4 0x55
+#define AS3722_BB_CHARGER 0x57
+#define AS3722_CTRL_SEQU1 0x58
+#define AS3722_CTRL_SEQU2 0x59
+#define AS3722_OV_CURRENT 0x5A
+#define AS3722_OV_CURRENT_DEB 0x5B
+#define AS3722_SDLV_DEB 0x5C
+#define AS3722_OC_PG_CTRL 0x5D
+#define AS3722_OC_PG_CTRL2 0x5E
+#define AS3722_CTRL_STATUS 0x5F
+#define AS3722_RTC_CONTROL 0x60
+#define AS3722_RTC_AM_PM_MODE (1 << 7)
+#define AS3722_RTC_CLK32K_OUT_EN (1 << 5)
+#define AS3722_RTC_IRQ_MODE (1 << 3)
+#define AS3722_RTC_ON (1 << 2)
+#define AS3722_RTC_ALARM_WAKEUP_EN (1 << 1)
+#define AS3722_RTC_REP_WAKEUP_EN (1 << 0)
+
+#define AS3722_RTC_SECOND 0x61
+#define AS3722_RTC_MINUTE 0x62
+#define AS3722_RTC_HOUR 0x63
+#define AS3722_RTC_DAY 0x64
+#define AS3722_RTC_MONTH 0x65
+#define AS3722_RTC_YEAR 0x66
+#define AS3722_RTC_ALARM_SECOND 0x67
+#define AS3722_RTC_ALARM_MINUTE 0x68
+#define AS3722_RTC_ALARM_HOUR 0x69
+#define AS3722_RTC_ALARM_DAY 0x6A
+#define AS3722_RTC_ALARM_MONTH 0x6B
+#define AS3722_RTC_ALARM_YEAR 0x6C
+#define AS3722_SRAM 0x6D
+#define AS3722_RTC_ACCESS 0x6F
+#define AS3722_REG_STATUS 0x73
+#define AS3722_INTERRUPT_MASK1 0x74
+#define AS3722_INTERRUPT_MASK2 0x75
+#define AS3722_INTERRUPT_MASK3 0x76
+#define AS3722_INTERRUPT_MASK4 0x77
+#define AS3722_INTERRUPT_STATUS1 0x78
+#define AS3722_INTERRUPT_STATUS2 0x79
+#define AS3722_INTERRUPT_STATUS3 0x7A
+#define AS3722_INTERRUPT_STATUS4 0x7B
+#define AS3722_TEMP_STATUS 0x7D
+#define AS3722_ADC0_CONTROL 0x80
+#define AS3722_ADC1_CONTROL 0x81
+#define AS3722_ADC0_MSB_RESULT 0x82
+#define AS3722_ADC0_LSB_RESULT 0x83
+#define AS3722_ADC1_MSB_RESULT 0x84
+#define AS3722_ADC1_LSB_RESULT 0x85
+#define AS3722_ADC1_THRESHOLD_HI_MSB 0x86
+#define AS3722_ADC1_THRESHOLD_HI_LSB 0x87
+#define AS3722_ADC1_THRESHOLD_LO_MSB 0x88
+#define AS3722_ADC1_THRESHOLD_LO_LSB 0x89
+#define AS3722_ADC_CONFIGURATION 0x8A
+#define AS3722_ASIC_ID1 0x90
+#define AS3722_ASIC_ID2 0x91
+#define AS3722_LOCK 0x9E
+#define AS3722_FUSE7 0x9E
+#define AS3722_FUSE7_SD0_LOW_VOLTAGE (1 << 4)
+
+struct as3722_reg_sc;
+struct as3722_gpio_pin;
+
+struct as3722_softc {
+ device_t dev;
+ struct sx lock;
+ int bus_addr;
+ struct resource *irq_res;
+ void *irq_h;
+
+ uint8_t chip_rev;
+ int int_pullup;
+ int i2c_pullup;
+
+ /* Regulators. */
+ struct as3722_reg_sc **regs;
+ int nregs;
+
+ /* GPIO */
+ device_t gpio_busdev;
+ struct as3722_gpio_pin **gpio_pins;
+ int gpio_npins;
+ struct sx gpio_lock;
+
+};
+
+#define RD1(sc, reg, val) as3722_read(sc, reg, val)
+#define WR1(sc, reg, val) as3722_write(sc, reg, val)
+#define RM1(sc, reg, clr, set) as3722_modify(sc, reg, clr, set)
+
+int as3722_read(struct as3722_softc *sc, uint8_t reg, uint8_t *val);
+int as3722_write(struct as3722_softc *sc, uint8_t reg, uint8_t val);
+int as3722_modify(struct as3722_softc *sc, uint8_t reg, uint8_t clear,
+ uint8_t set);
+int as3722_read_buf(struct as3722_softc *sc, uint8_t reg, uint8_t *buf,
+ size_t size);
+int as3722_write_buf(struct as3722_softc *sc, uint8_t reg, uint8_t *buf,
+ size_t size);
+
+/* Regulators */
+int as3722_regulator_attach(struct as3722_softc *sc, phandle_t node);
+int as3722_regulator_map(device_t dev, phandle_t xref, int ncells,
+ pcell_t *cells, int *num);
+
+/* RTC */
+int as3722_rtc_attach(struct as3722_softc *sc, phandle_t node);
+int as3722_rtc_gettime(device_t dev, struct timespec *ts);
+int as3722_rtc_settime(device_t dev, struct timespec *ts);
+
+/* GPIO */
+device_t as3722_gpio_get_bus(device_t dev);
+int as3722_gpio_pin_max(device_t dev, int *maxpin);
+int as3722_gpio_pin_getname(device_t dev, uint32_t pin, char *name);
+int as3722_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags);
+int as3722_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps);
+int as3722_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags);
+int as3722_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value);
+int as3722_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val);
+int as3722_gpio_pin_toggle(device_t dev, uint32_t pin);
+int as3722_gpio_map_gpios(device_t dev, phandle_t pdev, phandle_t gparent,
+ int gcells, pcell_t *gpios, uint32_t *pin, uint32_t *flags);
+int as3722_gpio_attach(struct as3722_softc *sc, phandle_t node);
+int as3722_pinmux_configure(device_t dev, phandle_t cfgxref);
+
+#endif /* _AS3722_H_ */
diff --git a/sys/arm/nvidia/as3722_gpio.c b/sys/arm/nvidia/as3722_gpio.c
new file mode 100644
index 000000000000..1afdb8a94f8b
--- /dev/null
+++ b/sys/arm/nvidia/as3722_gpio.c
@@ -0,0 +1,572 @@
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/gpio.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/sx.h>
+
+#include <machine/bus.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/gpio/gpiobusvar.h>
+
+#include "as3722.h"
+
+MALLOC_DEFINE(M_AS3722_GPIO, "AS3722 gpio", "AS3722 GPIO");
+
+/* AS3722_GPIOx_CONTROL MODE and IOSF definition. */
+#define AS3722_IOSF_GPIO 0x00
+#define AS3722_IOSF_INTERRUPT_OUT 0x01
+#define AS3722_IOSF_VSUP_VBAT_LOW_UNDEBOUNCE_OUT 0x02
+#define AS3722_IOSF_GPIO_IN_INTERRUPT 0x03
+#define AS3722_IOSF_PWM_IN 0x04
+#define AS3722_IOSF_VOLTAGE_IN_STANDBY 0x05
+#define AS3722_IOSF_OC_PG_SD0 0x06
+#define AS3722_IOSF_POWERGOOD_OUT 0x07
+#define AS3722_IOSF_CLK32K_OUT 0x08
+#define AS3722_IOSF_WATCHDOG_IN 0x09
+#define AS3722_IOSF_SOFT_RESET_IN 0x0b
+#define AS3722_IOSF_PWM_OUT 0x0c
+#define AS3722_IOSF_VSUP_VBAT_LOW_DEBOUNCE_OUT 0x0d
+#define AS3722_IOSF_OC_PG_SD6 0x0e
+
+#define AS3722_MODE_INPUT 0
+#define AS3722_MODE_PUSH_PULL 1
+#define AS3722_MODE_OPEN_DRAIN 2
+#define AS3722_MODE_TRISTATE 3
+#define AS3722_MODE_INPUT_PULL_UP_LV 4
+#define AS3722_MODE_INPUT_PULL_DOWN 5
+#define AS3722_MODE_OPEN_DRAIN_LV 6
+#define AS3722_MODE_PUSH_PULL_LV 7
+
+#define NGPIO 8
+
+#define GPIO_LOCK(_sc) sx_slock(&(_sc)->gpio_lock)
+#define GPIO_UNLOCK(_sc) sx_unlock(&(_sc)->gpio_lock)
+#define GPIO_ASSERT(_sc) sx_assert(&(_sc)->gpio_lock, SA_LOCKED)
+
+#define AS3722_CFG_BIAS_DISABLE 0x0001
+#define AS3722_CFG_BIAS_PULL_UP 0x0002
+#define AS3722_CFG_BIAS_PULL_DOWN 0x0004
+#define AS3722_CFG_BIAS_HIGH_IMPEDANCE 0x0008
+#define AS3722_CFG_OPEN_DRAIN 0x0010
+
+static const struct {
+ const char *name;
+ int config; /* AS3722_CFG_ */
+} as3722_cfg_names[] = {
+ {"bias-disable", AS3722_CFG_BIAS_DISABLE},
+ {"bias-pull-up", AS3722_CFG_BIAS_PULL_UP},
+ {"bias-pull-down", AS3722_CFG_BIAS_PULL_DOWN},
+ {"bias-high-impedance", AS3722_CFG_BIAS_HIGH_IMPEDANCE},
+ {"drive-open-drain", AS3722_CFG_OPEN_DRAIN},
+};
+
+static struct {
+ const char *name;
+ int fnc_val;
+} as3722_fnc_table[] = {
+ {"gpio", AS3722_IOSF_GPIO},
+ {"interrupt-out", AS3722_IOSF_INTERRUPT_OUT},
+ {"vsup-vbat-low-undebounce-out", AS3722_IOSF_VSUP_VBAT_LOW_UNDEBOUNCE_OUT},
+ {"gpio-in-interrupt", AS3722_IOSF_GPIO_IN_INTERRUPT},
+ {"pwm-in", AS3722_IOSF_PWM_IN},
+ {"voltage-in-standby", AS3722_IOSF_VOLTAGE_IN_STANDBY},
+ {"oc-pg-sd0", AS3722_IOSF_OC_PG_SD0},
+ {"powergood-out", AS3722_IOSF_POWERGOOD_OUT},
+ {"clk32k-out", AS3722_IOSF_CLK32K_OUT},
+ {"watchdog-in", AS3722_IOSF_WATCHDOG_IN},
+ {"soft-reset-in", AS3722_IOSF_SOFT_RESET_IN},
+ {"pwm-out", AS3722_IOSF_PWM_OUT},
+ {"vsup-vbat-low-debounce-out", AS3722_IOSF_VSUP_VBAT_LOW_DEBOUNCE_OUT},
+ {"oc-pg-sd6", AS3722_IOSF_OC_PG_SD6},
+};
+
+struct as3722_pincfg {
+ char *function;
+ int flags;
+};
+
+struct as3722_gpio_pin {
+ int pin_caps;
+ uint8_t pin_ctrl_reg;
+ char pin_name[GPIOMAXNAME];
+ int pin_cfg_flags;
+};
+
+/* --------------------------------------------------------------------------
+ *
+ * Pinmux functions.
+ */
+static int
+as3722_pinmux_get_function(struct as3722_softc *sc, char *name)
+{
+ int i;
+
+ for (i = 0; i < nitems(as3722_fnc_table); i++) {
+ if (strcmp(as3722_fnc_table[i].name, name) == 0)
+ return (as3722_fnc_table[i].fnc_val);
+ }
+ return (-1);
+}
+
+static int
+as3722_pinmux_config_node(struct as3722_softc *sc, char *pin_name,
+ struct as3722_pincfg *cfg)
+{
+ uint8_t ctrl;
+ int rv, fnc, pin;
+
+ for (pin = 0; pin < sc->gpio_npins; pin++) {
+ if (strcmp(sc->gpio_pins[pin]->pin_name, pin_name) == 0)
+ break;
+ }
+ if (pin >= sc->gpio_npins) {
+ device_printf(sc->dev, "Unknown pin: %s\n", pin_name);
+ return (ENXIO);
+ }
+
+ ctrl = sc->gpio_pins[pin]->pin_ctrl_reg;
+ sc->gpio_pins[pin]->pin_cfg_flags = cfg->flags;
+ if (cfg->function != NULL) {
+ fnc = as3722_pinmux_get_function(sc, cfg->function);
+ if (fnc == -1) {
+ device_printf(sc->dev,
+ "Unknown function %s for pin %s\n", cfg->function,
+ sc->gpio_pins[pin]->pin_name);
+ return (ENXIO);
+ }
+ switch (fnc) {
+ case AS3722_IOSF_INTERRUPT_OUT:
+ case AS3722_IOSF_VSUP_VBAT_LOW_UNDEBOUNCE_OUT:
+ case AS3722_IOSF_OC_PG_SD0:
+ case AS3722_IOSF_POWERGOOD_OUT:
+ case AS3722_IOSF_CLK32K_OUT:
+ case AS3722_IOSF_PWM_OUT:
+ case AS3722_IOSF_OC_PG_SD6:
+ ctrl &= ~(AS3722_GPIO_MODE_MASK <<
+ AS3722_GPIO_MODE_SHIFT);
+ ctrl |= AS3722_MODE_PUSH_PULL << AS3722_GPIO_MODE_SHIFT;
+ /* XXX Handle flags (OC + pullup) */
+ break;
+ case AS3722_IOSF_GPIO_IN_INTERRUPT:
+ case AS3722_IOSF_PWM_IN:
+ case AS3722_IOSF_VOLTAGE_IN_STANDBY:
+ case AS3722_IOSF_WATCHDOG_IN:
+ case AS3722_IOSF_SOFT_RESET_IN:
+ ctrl &= ~(AS3722_GPIO_MODE_MASK <<
+ AS3722_GPIO_MODE_SHIFT);
+ ctrl |= AS3722_MODE_INPUT << AS3722_GPIO_MODE_SHIFT;
+ /* XXX Handle flags (pulldown + pullup) */
+
+ default:
+ break;
+ }
+ ctrl &= ~(AS3722_GPIO_IOSF_MASK << AS3722_GPIO_IOSF_SHIFT);
+ ctrl |= fnc << AS3722_GPIO_IOSF_SHIFT;
+ }
+ rv = 0;
+ if (ctrl != sc->gpio_pins[pin]->pin_ctrl_reg) {
+ rv = WR1(sc, AS3722_GPIO0_CONTROL + pin, ctrl);
+ sc->gpio_pins[pin]->pin_ctrl_reg = ctrl;
+ }
+ return (rv);
+}
+
+static int
+as3722_pinmux_read_node(struct as3722_softc *sc, phandle_t node,
+ struct as3722_pincfg *cfg, char **pins, int *lpins)
+{
+ int rv, i;
+
+ *lpins = OF_getprop_alloc(node, "pins", (void **)pins);
+ if (*lpins <= 0)
+ return (ENOENT);
+
+ /* Read function (mux) settings. */
+ rv = OF_getprop_alloc(node, "function", (void **)&cfg->function);
+ if (rv <= 0)
+ cfg->function = NULL;
+
+ /* Read boolean properties. */
+ for (i = 0; i < nitems(as3722_cfg_names); i++) {
+ if (OF_hasprop(node, as3722_cfg_names[i].name))
+ cfg->flags |= as3722_cfg_names[i].config;
+ }
+ return (0);
+}
+
+static int
+as3722_pinmux_process_node(struct as3722_softc *sc, phandle_t node)
+{
+ struct as3722_pincfg cfg;
+ char *pins, *pname;
+ int i, len, lpins, rv;
+
+ rv = as3722_pinmux_read_node(sc, node, &cfg, &pins, &lpins);
+ if (rv != 0)
+ return (rv);
+
+ len = 0;
+ pname = pins;
+ do {
+ i = strlen(pname) + 1;
+ rv = as3722_pinmux_config_node(sc, pname, &cfg);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot configure pin: %s: %d\n", pname, rv);
+ }
+ len += i;
+ pname += i;
+ } while (len < lpins);
+
+ if (pins != NULL)
+ OF_prop_free(pins);
+ if (cfg.function != NULL)
+ OF_prop_free(cfg.function);
+
+ return (rv);
+}
+
+int as3722_pinmux_configure(device_t dev, phandle_t cfgxref)
+{
+ struct as3722_softc *sc;
+ phandle_t node, cfgnode;
+ int rv;
+
+ sc = device_get_softc(dev);
+ cfgnode = OF_node_from_xref(cfgxref);
+
+ for (node = OF_child(cfgnode); node != 0; node = OF_peer(node)) {
+ if (!ofw_bus_node_status_okay(node))
+ continue;
+ rv = as3722_pinmux_process_node(sc, node);
+ if (rv != 0)
+ device_printf(dev, "Failed to process pinmux");
+ }
+ return (0);
+}
+
+/* --------------------------------------------------------------------------
+ *
+ * GPIO
+ */
+device_t
+as3722_gpio_get_bus(device_t dev)
+{
+ struct as3722_softc *sc;
+
+ sc = device_get_softc(dev);
+ return (sc->gpio_busdev);
+}
+
+int
+as3722_gpio_pin_max(device_t dev, int *maxpin)
+{
+
+ *maxpin = NGPIO - 1;
+ return (0);
+}
+
+int
+as3722_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
+{
+ struct as3722_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->gpio_npins)
+ return (EINVAL);
+ GPIO_LOCK(sc);
+ *caps = sc->gpio_pins[pin]->pin_caps;
+ GPIO_UNLOCK(sc);
+ return (0);
+}
+
+int
+as3722_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
+{
+ struct as3722_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->gpio_npins)
+ return (EINVAL);
+ GPIO_LOCK(sc);
+ memcpy(name, sc->gpio_pins[pin]->pin_name, GPIOMAXNAME);
+ GPIO_UNLOCK(sc);
+ return (0);
+}
+
+int
+as3722_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *out_flags)
+{
+ struct as3722_softc *sc;
+ uint8_t tmp, mode, iosf;
+ uint32_t flags;
+ bool inverted;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->gpio_npins)
+ return (EINVAL);
+
+ GPIO_LOCK(sc);
+ tmp = sc->gpio_pins[pin]->pin_ctrl_reg;
+ GPIO_UNLOCK(sc);
+ iosf = (tmp >> AS3722_GPIO_IOSF_SHIFT) & AS3722_GPIO_IOSF_MASK;
+ mode = (tmp >> AS3722_GPIO_MODE_SHIFT) & AS3722_GPIO_MODE_MASK;
+ inverted = (tmp & AS3722_GPIO_INVERT) != 0;
+ /* Is pin in GPIO mode ? */
+ if (iosf != AS3722_IOSF_GPIO)
+ return (ENXIO);
+
+ flags = 0;
+ switch (mode) {
+ case AS3722_MODE_INPUT:
+ flags = GPIO_PIN_INPUT;
+ break;
+ case AS3722_MODE_PUSH_PULL:
+ case AS3722_MODE_PUSH_PULL_LV:
+ flags = GPIO_PIN_OUTPUT;
+ break;
+ case AS3722_MODE_OPEN_DRAIN:
+ case AS3722_MODE_OPEN_DRAIN_LV:
+ flags = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_OPENDRAIN;
+ break;
+ case AS3722_MODE_TRISTATE:
+ flags = GPIO_PIN_TRISTATE;
+ break;
+ case AS3722_MODE_INPUT_PULL_UP_LV:
+ flags = GPIO_PIN_INPUT | GPIO_PIN_PULLUP;
+ break;
+
+ case AS3722_MODE_INPUT_PULL_DOWN:
+ flags = GPIO_PIN_OUTPUT | GPIO_PIN_PULLDOWN;
+ break;
+ }
+ if (inverted)
+ flags |= GPIO_PIN_INVIN | GPIO_PIN_INVOUT;
+ *out_flags = flags;
+ return (0);
+}
+
+static int
+as3722_gpio_get_mode(struct as3722_softc *sc, uint32_t pin, uint32_t gpio_flags)
+{
+ uint8_t ctrl;
+ int flags;
+
+ ctrl = sc->gpio_pins[pin]->pin_ctrl_reg;
+ flags = sc->gpio_pins[pin]->pin_cfg_flags;
+
+ /* Tristate mode. */
+ if (flags & AS3722_CFG_BIAS_HIGH_IMPEDANCE ||
+ gpio_flags & GPIO_PIN_TRISTATE)
+ return (AS3722_MODE_TRISTATE);
+
+ /* Open drain modes. */
+ if (flags & AS3722_CFG_OPEN_DRAIN || gpio_flags & GPIO_PIN_OPENDRAIN) {
+ /* Only pull up have effect */
+ if (flags & AS3722_CFG_BIAS_PULL_UP ||
+ gpio_flags & GPIO_PIN_PULLUP)
+ return (AS3722_MODE_OPEN_DRAIN_LV);
+ return (AS3722_MODE_OPEN_DRAIN);
+ }
+ /* Input modes. */
+ if (gpio_flags & GPIO_PIN_INPUT) {
+ /* Accept pull up or pull down. */
+ if (flags & AS3722_CFG_BIAS_PULL_UP ||
+ gpio_flags & GPIO_PIN_PULLUP)
+ return (AS3722_MODE_INPUT_PULL_UP_LV);
+
+ if (flags & AS3722_CFG_BIAS_PULL_DOWN ||
+ gpio_flags & GPIO_PIN_PULLDOWN)
+ return (AS3722_MODE_INPUT_PULL_DOWN);
+ return (AS3722_MODE_INPUT);
+ }
+ /*
+ * Output modes.
+ * Pull down is used as indicator of low voltage output.
+ */
+ if (flags & AS3722_CFG_BIAS_PULL_DOWN ||
+ gpio_flags & GPIO_PIN_PULLDOWN)
+ return (AS3722_MODE_PUSH_PULL_LV);
+ return (AS3722_MODE_PUSH_PULL);
+}
+
+int
+as3722_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
+{
+ struct as3722_softc *sc;
+ uint8_t ctrl, mode, iosf;
+ int rv;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->gpio_npins)
+ return (EINVAL);
+
+ GPIO_LOCK(sc);
+ ctrl = sc->gpio_pins[pin]->pin_ctrl_reg;
+ iosf = (ctrl >> AS3722_GPIO_IOSF_SHIFT) & AS3722_GPIO_IOSF_MASK;
+ /* Is pin in GPIO mode ? */
+ if (iosf != AS3722_IOSF_GPIO) {
+ GPIO_UNLOCK(sc);
+ return (ENXIO);
+ }
+ mode = as3722_gpio_get_mode(sc, pin, flags);
+ ctrl &= ~(AS3722_GPIO_MODE_MASK << AS3722_GPIO_MODE_SHIFT);
+ ctrl |= AS3722_MODE_PUSH_PULL << AS3722_GPIO_MODE_SHIFT;
+ rv = 0;
+ if (ctrl != sc->gpio_pins[pin]->pin_ctrl_reg) {
+ rv = WR1(sc, AS3722_GPIO0_CONTROL + pin, ctrl);
+ sc->gpio_pins[pin]->pin_ctrl_reg = ctrl;
+ }
+ GPIO_UNLOCK(sc);
+ return (rv);
+}
+
+int
+as3722_gpio_pin_set(device_t dev, uint32_t pin, uint32_t val)
+{
+ struct as3722_softc *sc;
+ uint8_t tmp;
+ int rv;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->gpio_npins)
+ return (EINVAL);
+
+ tmp = (val != 0) ? 1 : 0;
+ if (sc->gpio_pins[pin]->pin_ctrl_reg & AS3722_GPIO_INVERT)
+ tmp ^= 1;
+
+ GPIO_LOCK(sc);
+ rv = RM1(sc, AS3722_GPIO_SIGNAL_OUT, (1 << pin), (tmp << pin));
+ GPIO_UNLOCK(sc);
+ return (rv);
+}
+
+int
+as3722_gpio_pin_get(device_t dev, uint32_t pin, uint32_t *val)
+{
+ struct as3722_softc *sc;
+ uint8_t tmp, mode, ctrl;
+ int rv;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->gpio_npins)
+ return (EINVAL);
+
+ GPIO_LOCK(sc);
+ ctrl = sc->gpio_pins[pin]->pin_ctrl_reg;
+ mode = (ctrl >> AS3722_GPIO_MODE_SHIFT) & AS3722_GPIO_MODE_MASK;
+ if ((mode == AS3722_MODE_PUSH_PULL) ||
+ (mode == AS3722_MODE_PUSH_PULL_LV))
+ rv = RD1(sc, AS3722_GPIO_SIGNAL_OUT, &tmp);
+ else
+ rv = RD1(sc, AS3722_GPIO_SIGNAL_IN, &tmp);
+ GPIO_UNLOCK(sc);
+ if (rv != 0)
+ return (rv);
+
+ *val = tmp & (1 << pin) ? 1 : 0;
+ if (ctrl & AS3722_GPIO_INVERT)
+ *val ^= 1;
+ return (0);
+}
+
+int
+as3722_gpio_pin_toggle(device_t dev, uint32_t pin)
+{
+ struct as3722_softc *sc;
+ uint8_t tmp;
+ int rv;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->gpio_npins)
+ return (EINVAL);
+
+ GPIO_LOCK(sc);
+ rv = RD1(sc, AS3722_GPIO_SIGNAL_OUT, &tmp);
+ if (rv != 0) {
+ GPIO_UNLOCK(sc);
+ return (rv);
+ }
+ tmp ^= (1 <<pin);
+ rv = RM1(sc, AS3722_GPIO_SIGNAL_OUT, (1 << pin), tmp);
+ GPIO_UNLOCK(sc);
+ return (0);
+}
+
+int
+as3722_gpio_map_gpios(device_t dev, phandle_t pdev, phandle_t gparent,
+ int gcells, pcell_t *gpios, uint32_t *pin, uint32_t *flags)
+{
+
+ if (gcells != 2)
+ return (ERANGE);
+ *pin = gpios[0];
+ *flags= gpios[1];
+ return (0);
+}
+
+int
+as3722_gpio_attach(struct as3722_softc *sc, phandle_t node)
+{
+ struct as3722_gpio_pin *pin;
+ int i, rv;
+
+ sx_init(&sc->gpio_lock, "AS3722 GPIO lock");
+ sc->gpio_npins = NGPIO;
+ sc->gpio_pins = malloc(sizeof(struct as3722_gpio_pin *) *
+ sc->gpio_npins, M_AS3722_GPIO, M_WAITOK | M_ZERO);
+
+ sc->gpio_busdev = gpiobus_attach_bus(sc->dev);
+ if (sc->gpio_busdev == NULL)
+ return (ENXIO);
+ for (i = 0; i < sc->gpio_npins; i++) {
+ sc->gpio_pins[i] = malloc(sizeof(struct as3722_gpio_pin),
+ M_AS3722_GPIO, M_WAITOK | M_ZERO);
+ pin = sc->gpio_pins[i];
+ sprintf(pin->pin_name, "gpio%d", i);
+ pin->pin_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT |
+ GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL | GPIO_PIN_TRISTATE |
+ GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN | GPIO_PIN_INVIN |
+ GPIO_PIN_INVOUT;
+ rv = RD1(sc, AS3722_GPIO0_CONTROL + i, &pin->pin_ctrl_reg);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot read configuration for pin %s\n",
+ sc->gpio_pins[i]->pin_name);
+ }
+ }
+ return (0);
+}
diff --git a/sys/arm/nvidia/as3722_regulators.c b/sys/arm/nvidia/as3722_regulators.c
new file mode 100644
index 000000000000..3e463f0f5de7
--- /dev/null
+++ b/sys/arm/nvidia/as3722_regulators.c
@@ -0,0 +1,712 @@
+/*-
+ * Copyright 2016 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/gpio.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/sx.h>
+
+#include <machine/bus.h>
+
+#include <dev/extres/regulator/regulator.h>
+#include <dev/gpio/gpiobusvar.h>
+
+#include <dt-bindings/mfd/as3722.h>
+
+#include "as3722.h"
+
+MALLOC_DEFINE(M_AS3722_REG, "AS3722 regulator", "AS3722 power regulator");
+
+#define DIV_ROUND_UP(n,d) howmany(n, d)
+
+enum as3722_reg_id {
+ AS3722_REG_ID_SD0,
+ AS3722_REG_ID_SD1,
+ AS3722_REG_ID_SD2,
+ AS3722_REG_ID_SD3,
+ AS3722_REG_ID_SD4,
+ AS3722_REG_ID_SD5,
+ AS3722_REG_ID_SD6,
+ AS3722_REG_ID_LDO0,
+ AS3722_REG_ID_LDO1,
+ AS3722_REG_ID_LDO2,
+ AS3722_REG_ID_LDO3,
+ AS3722_REG_ID_LDO4,
+ AS3722_REG_ID_LDO5,
+ AS3722_REG_ID_LDO6,
+ AS3722_REG_ID_LDO7,
+ AS3722_REG_ID_LDO9,
+ AS3722_REG_ID_LDO10,
+ AS3722_REG_ID_LDO11,
+};
+
+/* Regulator HW definition. */
+struct reg_def {
+ intptr_t id; /* ID */
+ char *name; /* Regulator name */
+ char *supply_name; /* Source property name */
+ uint8_t volt_reg;
+ uint8_t volt_vsel_mask;
+ uint8_t enable_reg;
+ uint8_t enable_mask;
+ uint8_t ext_enable_reg;
+ uint8_t ext_enable_mask;
+ struct regulator_range *ranges;
+ int nranges;
+};
+
+struct as3722_reg_sc {
+ struct regnode *regnode;
+ struct as3722_softc *base_sc;
+ struct reg_def *def;
+ phandle_t xref;
+
+ struct regnode_std_param *param;
+ int ext_control;
+ int enable_tracking;
+
+ int enable_usec;
+};
+
+static struct regulator_range as3722_sd016_ranges[] = {
+ REG_RANGE_INIT(0x00, 0x00, 0, 0),
+ REG_RANGE_INIT(0x01, 0x5A, 610000, 10000),
+};
+
+static struct regulator_range as3722_sd0_lv_ranges[] = {
+ REG_RANGE_INIT(0x00, 0x00, 0, 0),
+ REG_RANGE_INIT(0x01, 0x6E, 410000, 10000),
+};
+
+static struct regulator_range as3722_sd_ranges[] = {
+ REG_RANGE_INIT(0x00, 0x00, 0, 0),
+ REG_RANGE_INIT(0x01, 0x40, 612500, 12500),
+ REG_RANGE_INIT(0x41, 0x70, 1425000, 25000),
+ REG_RANGE_INIT(0x71, 0x7F, 2650000, 50000),
+};
+
+static struct regulator_range as3722_ldo3_ranges[] = {
+ REG_RANGE_INIT(0x00, 0x00, 0, 0),
+ REG_RANGE_INIT(0x01, 0x2D, 620000, 20000),
+};
+
+static struct regulator_range as3722_ldo_ranges[] = {
+ REG_RANGE_INIT(0x00, 0x00, 0, 0),
+ REG_RANGE_INIT(0x01, 0x24, 825000, 25000),
+ REG_RANGE_INIT(0x40, 0x7F, 1725000, 25000),
+};
+
+static struct reg_def as3722s_def[] = {
+ {
+ .id = AS3722_REG_ID_SD0,
+ .name = "sd0",
+ .volt_reg = AS3722_SD0_VOLTAGE,
+ .volt_vsel_mask = AS3722_SD_VSEL_MASK,
+ .enable_reg = AS3722_SD_CONTROL,
+ .enable_mask = AS3722_SDN_CTRL(0),
+ .ext_enable_reg = AS3722_ENABLE_CTRL1,
+ .ext_enable_mask = AS3722_SD0_EXT_ENABLE_MASK,
+ .ranges = as3722_sd016_ranges,
+ .nranges = nitems(as3722_sd016_ranges),
+ },
+ {
+ .id = AS3722_REG_ID_SD1,
+ .name = "sd1",
+ .volt_reg = AS3722_SD1_VOLTAGE,
+ .volt_vsel_mask = AS3722_SD_VSEL_MASK,
+ .enable_reg = AS3722_SD_CONTROL,
+ .enable_mask = AS3722_SDN_CTRL(1),
+ .ext_enable_reg = AS3722_ENABLE_CTRL1,
+ .ext_enable_mask = AS3722_SD1_EXT_ENABLE_MASK,
+ .ranges = as3722_sd_ranges,
+ .nranges = nitems(as3722_sd_ranges),
+ },
+ {
+ .id = AS3722_REG_ID_SD2,
+ .name = "sd2",
+ .supply_name = "vsup-sd2",
+ .volt_reg = AS3722_SD2_VOLTAGE,
+ .volt_vsel_mask = AS3722_SD_VSEL_MASK,
+ .enable_reg = AS3722_SD_CONTROL,
+ .enable_mask = AS3722_SDN_CTRL(2),
+ .ext_enable_reg = AS3722_ENABLE_CTRL1,
+ .ext_enable_mask = AS3722_SD2_EXT_ENABLE_MASK,
+ .ranges = as3722_sd_ranges,
+ .nranges = nitems(as3722_sd_ranges),
+ },
+ {
+ .id = AS3722_REG_ID_SD3,
+ .name = "sd3",
+ .supply_name = "vsup-sd3",
+ .volt_reg = AS3722_SD3_VOLTAGE,
+ .volt_vsel_mask = AS3722_SD_VSEL_MASK,
+ .enable_reg = AS3722_SD_CONTROL,
+ .enable_mask = AS3722_SDN_CTRL(3),
+ .ext_enable_reg = AS3722_ENABLE_CTRL1,
+ .ext_enable_mask = AS3722_SD3_EXT_ENABLE_MASK,
+ .ranges = as3722_sd_ranges,
+ .nranges = nitems(as3722_sd_ranges),
+ },
+ {
+ .id = AS3722_REG_ID_SD4,
+ .name = "sd4",
+ .supply_name = "vsup-sd4",
+ .volt_reg = AS3722_SD4_VOLTAGE,
+ .volt_vsel_mask = AS3722_SD_VSEL_MASK,
+ .enable_reg = AS3722_SD_CONTROL,
+ .enable_mask = AS3722_SDN_CTRL(4),
+ .ext_enable_reg = AS3722_ENABLE_CTRL2,
+ .ext_enable_mask = AS3722_SD4_EXT_ENABLE_MASK,
+ .ranges = as3722_sd_ranges,
+ .nranges = nitems(as3722_sd_ranges),
+ },
+ {
+ .id = AS3722_REG_ID_SD5,
+ .name = "sd5",
+ .supply_name = "vsup-sd5",
+ .volt_reg = AS3722_SD5_VOLTAGE,
+ .volt_vsel_mask = AS3722_SD_VSEL_MASK,
+ .enable_reg = AS3722_SD_CONTROL,
+ .enable_mask = AS3722_SDN_CTRL(5),
+ .ext_enable_reg = AS3722_ENABLE_CTRL2,
+ .ext_enable_mask = AS3722_SD5_EXT_ENABLE_MASK,
+ .ranges = as3722_sd_ranges,
+ .nranges = nitems(as3722_sd_ranges),
+ },
+ {
+ .id = AS3722_REG_ID_SD6,
+ .name = "sd6",
+ .volt_reg = AS3722_SD6_VOLTAGE,
+ .volt_vsel_mask = AS3722_SD_VSEL_MASK,
+ .enable_reg = AS3722_SD_CONTROL,
+ .enable_mask = AS3722_SDN_CTRL(6),
+ .ext_enable_reg = AS3722_ENABLE_CTRL2,
+ .ext_enable_mask = AS3722_SD6_EXT_ENABLE_MASK,
+ .ranges = as3722_sd016_ranges,
+ .nranges = nitems(as3722_sd016_ranges),
+ },
+ {
+ .id = AS3722_REG_ID_LDO0,
+ .name = "ldo0",
+ .supply_name = "vin-ldo0",
+ .volt_reg = AS3722_LDO0_VOLTAGE,
+ .volt_vsel_mask = AS3722_LDO0_VSEL_MASK,
+ .enable_reg = AS3722_LDO_CONTROL0,
+ .enable_mask = AS3722_LDO0_CTRL,
+ .ext_enable_reg = AS3722_ENABLE_CTRL3,
+ .ext_enable_mask = AS3722_LDO0_EXT_ENABLE_MASK,
+ .ranges = as3722_ldo_ranges,
+ .nranges = nitems(as3722_ldo_ranges),
+ },
+ {
+ .id = AS3722_REG_ID_LDO1,
+ .name = "ldo1",
+ .supply_name = "vin-ldo1-6",
+ .volt_reg = AS3722_LDO1_VOLTAGE,
+ .volt_vsel_mask = AS3722_LDO_VSEL_MASK,
+ .enable_reg = AS3722_LDO_CONTROL0,
+ .enable_mask = AS3722_LDO1_CTRL,
+ .ext_enable_reg = AS3722_ENABLE_CTRL3,
+ .ext_enable_mask = AS3722_LDO1_EXT_ENABLE_MASK,
+ .ranges = as3722_ldo_ranges,
+ .nranges = nitems(as3722_ldo_ranges),
+ },
+ {
+ .id = AS3722_REG_ID_LDO2,
+ .name = "ldo2",
+ .supply_name = "vin-ldo2-5-7",
+ .volt_reg = AS3722_LDO2_VOLTAGE,
+ .volt_vsel_mask = AS3722_LDO_VSEL_MASK,
+ .enable_reg = AS3722_LDO_CONTROL0,
+ .enable_mask = AS3722_LDO2_CTRL,
+ .ext_enable_reg = AS3722_ENABLE_CTRL3,
+ .ext_enable_mask = AS3722_LDO2_EXT_ENABLE_MASK,
+ .ranges = as3722_ldo_ranges,
+ .nranges = nitems(as3722_ldo_ranges),
+ },
+ {
+ .id = AS3722_REG_ID_LDO3,
+ .name = "ldo3",
+ .supply_name = "vin-ldo3-4",
+ .volt_reg = AS3722_LDO3_VOLTAGE,
+ .volt_vsel_mask = AS3722_LDO3_VSEL_MASK,
+ .enable_reg = AS3722_LDO_CONTROL0,
+ .enable_mask = AS3722_LDO3_CTRL,
+ .ext_enable_reg = AS3722_ENABLE_CTRL3,
+ .ext_enable_mask = AS3722_LDO3_EXT_ENABLE_MASK,
+ .ranges = as3722_ldo3_ranges,
+ .nranges = nitems(as3722_ldo3_ranges),
+ },
+ {
+ .id = AS3722_REG_ID_LDO4,
+ .name = "ldo4",
+ .supply_name = "vin-ldo3-4",
+ .volt_reg = AS3722_LDO4_VOLTAGE,
+ .volt_vsel_mask = AS3722_LDO_VSEL_MASK,
+ .enable_reg = AS3722_LDO_CONTROL0,
+ .enable_mask = AS3722_LDO4_CTRL,
+ .ext_enable_reg = AS3722_ENABLE_CTRL4,
+ .ext_enable_mask = AS3722_LDO4_EXT_ENABLE_MASK,
+ .ranges = as3722_ldo_ranges,
+ .nranges = nitems(as3722_ldo_ranges),
+ },
+ {
+ .id = AS3722_REG_ID_LDO5,
+ .name = "ldo5",
+ .supply_name = "vin-ldo2-5-7",
+ .volt_reg = AS3722_LDO5_VOLTAGE,
+ .volt_vsel_mask = AS3722_LDO_VSEL_MASK,
+ .enable_reg = AS3722_LDO_CONTROL0,
+ .enable_mask = AS3722_LDO5_CTRL,
+ .ext_enable_reg = AS3722_ENABLE_CTRL4,
+ .ext_enable_mask = AS3722_LDO5_EXT_ENABLE_MASK,
+ .ranges = as3722_ldo_ranges,
+ .nranges = nitems(as3722_ldo_ranges),
+ },
+ {
+ .id = AS3722_REG_ID_LDO6,
+ .name = "ldo6",
+ .supply_name = "vin-ldo1-6",
+ .volt_reg = AS3722_LDO6_VOLTAGE,
+ .volt_vsel_mask = AS3722_LDO_VSEL_MASK,
+ .enable_reg = AS3722_LDO_CONTROL0,
+ .enable_mask = AS3722_LDO6_CTRL,
+ .ext_enable_reg = AS3722_ENABLE_CTRL4,
+ .ext_enable_mask = AS3722_LDO6_EXT_ENABLE_MASK,
+ .ranges = as3722_ldo_ranges,
+ .nranges = nitems(as3722_ldo_ranges),
+ },
+ {
+ .id = AS3722_REG_ID_LDO7,
+ .name = "ldo7",
+ .supply_name = "vin-ldo2-5-7",
+ .volt_reg = AS3722_LDO7_VOLTAGE,
+ .volt_vsel_mask = AS3722_LDO_VSEL_MASK,
+ .enable_reg = AS3722_LDO_CONTROL0,
+ .enable_mask = AS3722_LDO7_CTRL,
+ .ext_enable_reg = AS3722_ENABLE_CTRL4,
+ .ext_enable_mask = AS3722_LDO7_EXT_ENABLE_MASK,
+ .ranges = as3722_ldo_ranges,
+ .nranges = nitems(as3722_ldo_ranges),
+ },
+ {
+ .id = AS3722_REG_ID_LDO9,
+ .name = "ldo9",
+ .supply_name = "vin-ldo9-10",
+ .volt_reg = AS3722_LDO9_VOLTAGE,
+ .volt_vsel_mask = AS3722_LDO_VSEL_MASK,
+ .enable_reg = AS3722_LDO_CONTROL1,
+ .enable_mask = AS3722_LDO9_CTRL,
+ .ext_enable_reg = AS3722_ENABLE_CTRL5,
+ .ext_enable_mask = AS3722_LDO9_EXT_ENABLE_MASK,
+ .ranges = as3722_ldo_ranges,
+ .nranges = nitems(as3722_ldo_ranges),
+ },
+ {
+ .id = AS3722_REG_ID_LDO10,
+ .name = "ldo10",
+ .supply_name = "vin-ldo9-10",
+ .volt_reg = AS3722_LDO10_VOLTAGE,
+ .volt_vsel_mask = AS3722_LDO_VSEL_MASK,
+ .enable_reg = AS3722_LDO_CONTROL1,
+ .enable_mask = AS3722_LDO10_CTRL,
+ .ext_enable_reg = AS3722_ENABLE_CTRL5,
+ .ext_enable_mask = AS3722_LDO10_EXT_ENABLE_MASK,
+ .ranges = as3722_ldo_ranges,
+ .nranges = nitems(as3722_ldo_ranges),
+ },
+ {
+ .id = AS3722_REG_ID_LDO11,
+ .name = "ldo11",
+ .supply_name = "vin-ldo11",
+ .volt_reg = AS3722_LDO11_VOLTAGE,
+ .volt_vsel_mask = AS3722_LDO_VSEL_MASK,
+ .enable_reg = AS3722_LDO_CONTROL1,
+ .enable_mask = AS3722_LDO11_CTRL,
+ .ext_enable_reg = AS3722_ENABLE_CTRL5,
+ .ext_enable_mask = AS3722_LDO11_EXT_ENABLE_MASK,
+ .ranges = as3722_ldo_ranges,
+ .nranges = nitems(as3722_ldo_ranges),
+ },
+};
+
+struct as3722_regnode_init_def {
+ struct regnode_init_def reg_init_def;
+ int ext_control;
+ int enable_tracking;
+};
+
+static int as3722_regnode_init(struct regnode *regnode);
+static int as3722_regnode_enable(struct regnode *regnode, bool enable,
+ int *udelay);
+static int as3722_regnode_set_volt(struct regnode *regnode, int min_uvolt,
+ int max_uvolt, int *udelay);
+static int as3722_regnode_get_volt(struct regnode *regnode, int *uvolt);
+static regnode_method_t as3722_regnode_methods[] = {
+ /* Regulator interface */
+ REGNODEMETHOD(regnode_init, as3722_regnode_init),
+ REGNODEMETHOD(regnode_enable, as3722_regnode_enable),
+ REGNODEMETHOD(regnode_set_voltage, as3722_regnode_set_volt),
+ REGNODEMETHOD(regnode_get_voltage, as3722_regnode_get_volt),
+ REGNODEMETHOD_END
+};
+DEFINE_CLASS_1(as3722_regnode, as3722_regnode_class, as3722_regnode_methods,
+ sizeof(struct as3722_reg_sc), regnode_class);
+
+static int
+as3722_read_sel(struct as3722_reg_sc *sc, uint8_t *sel)
+{
+ int rv;
+
+ rv = RD1(sc->base_sc, sc->def->volt_reg, sel);
+ if (rv != 0)
+ return (rv);
+ *sel &= sc->def->volt_vsel_mask;
+ *sel >>= ffs(sc->def->volt_vsel_mask) - 1;
+ return (0);
+}
+
+static int
+as3722_write_sel(struct as3722_reg_sc *sc, uint8_t sel)
+{
+ int rv;
+
+ sel <<= ffs(sc->def->volt_vsel_mask) - 1;
+ sel &= sc->def->volt_vsel_mask;
+
+ rv = RM1(sc->base_sc, sc->def->volt_reg,
+ sc->def->volt_vsel_mask, sel);
+ if (rv != 0)
+ return (rv);
+ return (rv);
+}
+
+static bool
+as3722_sd0_is_low_voltage(struct as3722_reg_sc *sc)
+{
+ uint8_t val;
+ int rv;
+
+ rv = RD1(sc->base_sc, AS3722_FUSE7, &val);
+ if (rv != 0)
+ return (rv);
+ return (val & AS3722_FUSE7_SD0_LOW_VOLTAGE ? true : false);
+}
+
+static int
+as3722_reg_extreg_setup(struct as3722_reg_sc *sc, int ext_pwr_ctrl)
+{
+ uint8_t val;
+ int rv;
+
+ val = ext_pwr_ctrl << (ffs(sc->def->ext_enable_mask) - 1);
+ rv = RM1(sc->base_sc, sc->def->ext_enable_reg,
+ sc->def->ext_enable_mask, val);
+ return (rv);
+}
+
+static int
+as3722_reg_enable(struct as3722_reg_sc *sc)
+{
+ int rv;
+
+ rv = RM1(sc->base_sc, sc->def->enable_reg,
+ sc->def->enable_mask, sc->def->enable_mask);
+ return (rv);
+}
+
+static int
+as3722_reg_disable(struct as3722_reg_sc *sc)
+{
+ int rv;
+
+ rv = RM1(sc->base_sc, sc->def->enable_reg,
+ sc->def->enable_mask, 0);
+ return (rv);
+}
+
+static int
+as3722_regnode_init(struct regnode *regnode)
+{
+ struct as3722_reg_sc *sc;
+ int rv;
+
+ sc = regnode_get_softc(regnode);
+
+ sc->enable_usec = 500;
+ if (sc->def->id == AS3722_REG_ID_SD0) {
+ if (as3722_sd0_is_low_voltage(sc)) {
+ sc->def->ranges = as3722_sd0_lv_ranges;
+ sc->def->nranges = nitems(as3722_sd0_lv_ranges);
+ }
+ sc->enable_usec = 600;
+ } else if (sc->def->id == AS3722_REG_ID_LDO3) {
+ if (sc->enable_tracking) {
+ rv = RM1(sc->base_sc, sc->def->volt_reg,
+ AS3722_LDO3_MODE_MASK,
+ AS3722_LDO3_MODE_PMOS_TRACKING);
+ if (rv < 0) {
+ device_printf(sc->base_sc->dev,
+ "LDO3 tracking failed: %d\n", rv);
+ return (rv);
+ }
+ }
+ }
+
+ if (sc->ext_control) {
+ rv = as3722_reg_enable(sc);
+ if (rv < 0) {
+ device_printf(sc->base_sc->dev,
+ "Failed to enable %s regulator: %d\n",
+ sc->def->name, rv);
+ return (rv);
+ }
+ rv = as3722_reg_extreg_setup(sc, sc->ext_control);
+ if (rv < 0) {
+ device_printf(sc->base_sc->dev,
+ "%s ext control failed: %d", sc->def->name, rv);
+ return (rv);
+ }
+ }
+ return (0);
+}
+
+static void
+as3722_fdt_parse(struct as3722_softc *sc, phandle_t node, struct reg_def *def,
+struct as3722_regnode_init_def *init_def)
+{
+ int rv;
+ phandle_t parent, supply_node;
+ char prop_name[64]; /* Maximum OFW property name length. */
+
+ rv = regulator_parse_ofw_stdparam(sc->dev, node,
+ &init_def->reg_init_def);
+
+ rv = OF_getencprop(node, "ams,ext-control", &init_def->ext_control,
+ sizeof(init_def->ext_control));
+ if (rv <= 0)
+ init_def->ext_control = 0;
+ if (init_def->ext_control > 3) {
+ device_printf(sc->dev,
+ "Invalid value for ams,ext-control property: %d\n",
+ init_def->ext_control);
+ init_def->ext_control = 0;
+ }
+ if (OF_hasprop(node, "ams,enable-tracking"))
+ init_def->enable_tracking = 1;
+
+ /* Get parent supply. */
+ if (def->supply_name == NULL)
+ return;
+
+ parent = OF_parent(node);
+ snprintf(prop_name, sizeof(prop_name), "%s-supply",
+ def->supply_name);
+ rv = OF_getencprop(parent, prop_name, &supply_node,
+ sizeof(supply_node));
+ if (rv <= 0)
+ return;
+ supply_node = OF_node_from_xref(supply_node);
+ rv = OF_getprop_alloc(supply_node, "regulator-name",
+ (void **)&init_def->reg_init_def.parent_name);
+ if (rv <= 0)
+ init_def->reg_init_def.parent_name = NULL;
+}
+
+static struct as3722_reg_sc *
+as3722_attach(struct as3722_softc *sc, phandle_t node, struct reg_def *def)
+{
+ struct as3722_reg_sc *reg_sc;
+ struct as3722_regnode_init_def init_def;
+ struct regnode *regnode;
+
+ bzero(&init_def, sizeof(init_def));
+
+ as3722_fdt_parse(sc, node, def, &init_def);
+ init_def.reg_init_def.id = def->id;
+ init_def.reg_init_def.ofw_node = node;
+ regnode = regnode_create(sc->dev, &as3722_regnode_class,
+ &init_def.reg_init_def);
+ if (regnode == NULL) {
+ device_printf(sc->dev, "Cannot create regulator.\n");
+ return (NULL);
+ }
+ reg_sc = regnode_get_softc(regnode);
+
+ /* Init regulator softc. */
+ reg_sc->regnode = regnode;
+ reg_sc->base_sc = sc;
+ reg_sc->def = def;
+ reg_sc->xref = OF_xref_from_node(node);
+
+ reg_sc->param = regnode_get_stdparam(regnode);
+ reg_sc->ext_control = init_def.ext_control;
+ reg_sc->enable_tracking = init_def.enable_tracking;
+
+ regnode_register(regnode);
+ if (bootverbose) {
+ int volt, rv;
+ regnode_topo_slock();
+ rv = regnode_get_voltage(regnode, &volt);
+ if (rv == ENODEV) {
+ device_printf(sc->dev,
+ " Regulator %s: parent doesn't exist yet.\n",
+ regnode_get_name(regnode));
+ } else if (rv != 0) {
+ device_printf(sc->dev,
+ " Regulator %s: voltage: INVALID!!!\n",
+ regnode_get_name(regnode));
+ } else {
+ device_printf(sc->dev,
+ " Regulator %s: voltage: %d uV\n",
+ regnode_get_name(regnode), volt);
+ }
+ regnode_topo_unlock();
+ }
+
+ return (reg_sc);
+}
+
+int
+as3722_regulator_attach(struct as3722_softc *sc, phandle_t node)
+{
+ struct as3722_reg_sc *reg;
+ phandle_t child, rnode;
+ int i;
+
+ rnode = ofw_bus_find_child(node, "regulators");
+ if (rnode <= 0) {
+ device_printf(sc->dev, " Cannot find regulators subnode\n");
+ return (ENXIO);
+ }
+
+ sc->nregs = nitems(as3722s_def);
+ sc->regs = malloc(sizeof(struct as3722_reg_sc *) * sc->nregs,
+ M_AS3722_REG, M_WAITOK | M_ZERO);
+
+ /* Attach all known regulators if exist in DT. */
+ for (i = 0; i < sc->nregs; i++) {
+ child = ofw_bus_find_child(rnode, as3722s_def[i].name);
+ if (child == 0) {
+ if (bootverbose)
+ device_printf(sc->dev,
+ "Regulator %s missing in DT\n",
+ as3722s_def[i].name);
+ continue;
+ }
+ reg = as3722_attach(sc, child, as3722s_def + i);
+ if (reg == NULL) {
+ device_printf(sc->dev, "Cannot attach regulator: %s\n",
+ as3722s_def[i].name);
+ return (ENXIO);
+ }
+ sc->regs[i] = reg;
+ }
+ return (0);
+}
+
+int
+as3722_regulator_map(device_t dev, phandle_t xref, int ncells,
+ pcell_t *cells, int *num)
+{
+ struct as3722_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+ for (i = 0; i < sc->nregs; i++) {
+ if (sc->regs[i] == NULL)
+ continue;
+ if (sc->regs[i]->xref == xref) {
+ *num = sc->regs[i]->def->id;
+ return (0);
+ }
+ }
+ return (ENXIO);
+}
+
+static int
+as3722_regnode_enable(struct regnode *regnode, bool val, int *udelay)
+{
+ struct as3722_reg_sc *sc;
+ int rv;
+
+ sc = regnode_get_softc(regnode);
+
+ if (val)
+ rv = as3722_reg_enable(sc);
+ else
+ rv = as3722_reg_disable(sc);
+ *udelay = sc->enable_usec;
+ return (rv);
+}
+
+static int
+as3722_regnode_set_volt(struct regnode *regnode, int min_uvolt, int max_uvolt,
+ int *udelay)
+{
+ struct as3722_reg_sc *sc;
+ uint8_t sel;
+ int rv;
+
+ sc = regnode_get_softc(regnode);
+
+ *udelay = 0;
+ rv = regulator_range_volt_to_sel8(sc->def->ranges, sc->def->nranges,
+ min_uvolt, max_uvolt, &sel);
+ if (rv != 0)
+ return (rv);
+ rv = as3722_write_sel(sc, sel);
+ return (rv);
+
+}
+
+static int
+as3722_regnode_get_volt(struct regnode *regnode, int *uvolt)
+{
+ struct as3722_reg_sc *sc;
+ uint8_t sel;
+ int rv;
+
+ sc = regnode_get_softc(regnode);
+ rv = as3722_read_sel(sc, &sel);
+ if (rv != 0)
+ return (rv);
+
+ /* LDO6 have bypass. */
+ if (sc->def->id == AS3722_REG_ID_LDO6 && sel == AS3722_LDO6_SEL_BYPASS)
+ return (ENOENT);
+ rv = regulator_range_sel8_to_volt(sc->def->ranges, sc->def->nranges,
+ sel, uvolt);
+ return (rv);
+}
diff --git a/sys/arm/nvidia/as3722_rtc.c b/sys/arm/nvidia/as3722_rtc.c
new file mode 100644
index 000000000000..8f13104337f8
--- /dev/null
+++ b/sys/arm/nvidia/as3722_rtc.c
@@ -0,0 +1,115 @@
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/clock.h>
+#include <sys/kernel.h>
+
+#include <dev/ofw/ofw_bus.h>
+
+#include "clock_if.h"
+#include "as3722.h"
+
+#define AS3722_RTC_START_YEAR 2000
+
+int
+as3722_rtc_gettime(device_t dev, struct timespec *ts)
+{
+ struct as3722_softc *sc;
+ struct clocktime ct;
+ uint8_t buf[6];
+ int rv;
+
+ sc = device_get_softc(dev);
+
+ rv = as3722_read_buf(sc, AS3722_RTC_SECOND, buf, 6);
+ if (rv != 0) {
+ device_printf(sc->dev, "Failed to read RTC data\n");
+ return (rv);
+ }
+ ct.nsec = 0;
+ ct.sec = bcd2bin(buf[0] & 0x7F);
+ ct.min = bcd2bin(buf[1] & 0x7F);
+ ct.hour = bcd2bin(buf[2] & 0x3F);
+ ct.day = bcd2bin(buf[3] & 0x3F);
+ ct.mon = bcd2bin(buf[4] & 0x1F);
+ ct.year = bcd2bin(buf[5] & 0x7F) + AS3722_RTC_START_YEAR;
+ ct.dow = -1;
+
+ return clock_ct_to_ts(&ct, ts);
+}
+
+int
+as3722_rtc_settime(device_t dev, struct timespec *ts)
+{
+ struct as3722_softc *sc;
+ struct clocktime ct;
+ uint8_t buf[6];
+ int rv;
+
+ sc = device_get_softc(dev);
+ clock_ts_to_ct(ts, &ct);
+
+ if (ct.year < AS3722_RTC_START_YEAR)
+ return (EINVAL);
+
+ buf[0] = bin2bcd(ct.sec);
+ buf[1] = bin2bcd(ct.min);
+ buf[2] = bin2bcd(ct.hour);
+ buf[3] = bin2bcd(ct.day);
+ buf[4] = bin2bcd(ct.mon);
+ buf[5] = bin2bcd(ct.year - AS3722_RTC_START_YEAR);
+
+ rv = as3722_write_buf(sc, AS3722_RTC_SECOND, buf, 6);
+ if (rv != 0) {
+ device_printf(sc->dev, "Failed to write RTC data\n");
+ return (rv);
+ }
+ return (0);
+}
+
+int
+as3722_rtc_attach(struct as3722_softc *sc, phandle_t node)
+{
+ int rv;
+
+ /* Enable RTC, set 24 hours mode and alarms */
+ rv = RM1(sc, AS3722_RTC_CONTROL,
+ AS3722_RTC_ON | AS3722_RTC_ALARM_WAKEUP_EN | AS3722_RTC_AM_PM_MODE,
+ AS3722_RTC_ON | AS3722_RTC_ALARM_WAKEUP_EN);
+ if (rv < 0) {
+ device_printf(sc->dev, "Failed to initialize RTC controller\n");
+ return (ENXIO);
+ }
+ clock_register(sc->dev, 1000000);
+
+ return (0);
+}
diff --git a/sys/arm/nvidia/drm2/hdmi.c b/sys/arm/nvidia/drm2/hdmi.c
new file mode 100644
index 000000000000..1e7ed4a440e8
--- /dev/null
+++ b/sys/arm/nvidia/drm2/hdmi.c
@@ -0,0 +1,1229 @@
+/*
+ * Copyright (C) 2012 Avionic Design GmbH
+ *
+ * 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, sub license,
+ * 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 NON-INFRINGEMENT. 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <arm/nvidia/drm2/hdmi.h>
+
+#define EXPORT_SYMBOL(x)
+#ifndef BIT
+#define BIT(x) (1U << (x))
+#endif
+#define hdmi_log(fmt, ...) printf(fmt, ##__VA_ARGS__)
+
+static uint8_t hdmi_infoframe_checksum(uint8_t *ptr, size_t size)
+{
+ uint8_t csum = 0;
+ size_t i;
+
+ /* compute checksum */
+ for (i = 0; i < size; i++)
+ csum += ptr[i];
+
+ return 256 - csum;
+}
+
+static void hdmi_infoframe_set_checksum(void *buffer, size_t size)
+{
+ uint8_t *ptr = buffer;
+
+ ptr[3] = hdmi_infoframe_checksum(buffer, size);
+}
+
+/**
+ * hdmi_avi_infoframe_init() - initialize an HDMI AVI infoframe
+ * @frame: HDMI AVI infoframe
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int hdmi_avi_infoframe_init(struct hdmi_avi_infoframe *frame)
+{
+ memset(frame, 0, sizeof(*frame));
+
+ frame->type = HDMI_INFOFRAME_TYPE_AVI;
+ frame->version = 2;
+ frame->length = HDMI_AVI_INFOFRAME_SIZE;
+
+ return 0;
+}
+EXPORT_SYMBOL(hdmi_avi_infoframe_init);
+
+/**
+ * hdmi_avi_infoframe_pack() - write HDMI AVI infoframe to binary buffer
+ * @frame: HDMI AVI infoframe
+ * @buffer: destination buffer
+ * @size: size of buffer
+ *
+ * Packs the information contained in the @frame structure into a binary
+ * representation that can be written into the corresponding controller
+ * registers. Also computes the checksum as required by section 5.3.5 of
+ * the HDMI 1.4 specification.
+ *
+ * Returns the number of bytes packed into the binary buffer or a negative
+ * error code on failure.
+ */
+ssize_t hdmi_avi_infoframe_pack(struct hdmi_avi_infoframe *frame, void *buffer,
+ size_t size)
+{
+ uint8_t *ptr = buffer;
+ size_t length;
+
+ length = HDMI_INFOFRAME_HEADER_SIZE + frame->length;
+
+ if (size < length)
+ return -ENOSPC;
+
+ memset(buffer, 0, size);
+
+ ptr[0] = frame->type;
+ ptr[1] = frame->version;
+ ptr[2] = frame->length;
+ ptr[3] = 0; /* checksum */
+
+ /* start infoframe payload */
+ ptr += HDMI_INFOFRAME_HEADER_SIZE;
+
+ ptr[0] = ((frame->colorspace & 0x3) << 5) | (frame->scan_mode & 0x3);
+
+ /*
+ * Data byte 1, bit 4 has to be set if we provide the active format
+ * aspect ratio
+ */
+ if (frame->active_aspect & 0xf)
+ ptr[0] |= BIT(4);
+
+ /* Bit 3 and 2 indicate if we transmit horizontal/vertical bar data */
+ if (frame->top_bar || frame->bottom_bar)
+ ptr[0] |= BIT(3);
+
+ if (frame->left_bar || frame->right_bar)
+ ptr[0] |= BIT(2);
+
+ ptr[1] = ((frame->colorimetry & 0x3) << 6) |
+ ((frame->picture_aspect & 0x3) << 4) |
+ (frame->active_aspect & 0xf);
+
+ ptr[2] = ((frame->extended_colorimetry & 0x7) << 4) |
+ ((frame->quantization_range & 0x3) << 2) |
+ (frame->nups & 0x3);
+
+ if (frame->itc)
+ ptr[2] |= BIT(7);
+
+ ptr[3] = frame->video_code & 0x7f;
+
+ ptr[4] = ((frame->ycc_quantization_range & 0x3) << 6) |
+ ((frame->content_type & 0x3) << 4) |
+ (frame->pixel_repeat & 0xf);
+
+ ptr[5] = frame->top_bar & 0xff;
+ ptr[6] = (frame->top_bar >> 8) & 0xff;
+ ptr[7] = frame->bottom_bar & 0xff;
+ ptr[8] = (frame->bottom_bar >> 8) & 0xff;
+ ptr[9] = frame->left_bar & 0xff;
+ ptr[10] = (frame->left_bar >> 8) & 0xff;
+ ptr[11] = frame->right_bar & 0xff;
+ ptr[12] = (frame->right_bar >> 8) & 0xff;
+
+ hdmi_infoframe_set_checksum(buffer, length);
+
+ return length;
+}
+EXPORT_SYMBOL(hdmi_avi_infoframe_pack);
+
+/**
+ * hdmi_spd_infoframe_init() - initialize an HDMI SPD infoframe
+ * @frame: HDMI SPD infoframe
+ * @vendor: vendor string
+ * @product: product string
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int hdmi_spd_infoframe_init(struct hdmi_spd_infoframe *frame,
+ const char *vendor, const char *product)
+{
+ memset(frame, 0, sizeof(*frame));
+
+ frame->type = HDMI_INFOFRAME_TYPE_SPD;
+ frame->version = 1;
+ frame->length = HDMI_SPD_INFOFRAME_SIZE;
+
+ strncpy(frame->vendor, vendor, sizeof(frame->vendor));
+ strncpy(frame->product, product, sizeof(frame->product));
+
+ return 0;
+}
+EXPORT_SYMBOL(hdmi_spd_infoframe_init);
+
+/**
+ * hdmi_spd_infoframe_pack() - write HDMI SPD infoframe to binary buffer
+ * @frame: HDMI SPD infoframe
+ * @buffer: destination buffer
+ * @size: size of buffer
+ *
+ * Packs the information contained in the @frame structure into a binary
+ * representation that can be written into the corresponding controller
+ * registers. Also computes the checksum as required by section 5.3.5 of
+ * the HDMI 1.4 specification.
+ *
+ * Returns the number of bytes packed into the binary buffer or a negative
+ * error code on failure.
+ */
+ssize_t hdmi_spd_infoframe_pack(struct hdmi_spd_infoframe *frame, void *buffer,
+ size_t size)
+{
+ uint8_t *ptr = buffer;
+ size_t length;
+
+ length = HDMI_INFOFRAME_HEADER_SIZE + frame->length;
+
+ if (size < length)
+ return -ENOSPC;
+
+ memset(buffer, 0, size);
+
+ ptr[0] = frame->type;
+ ptr[1] = frame->version;
+ ptr[2] = frame->length;
+ ptr[3] = 0; /* checksum */
+
+ /* start infoframe payload */
+ ptr += HDMI_INFOFRAME_HEADER_SIZE;
+
+ memcpy(ptr, frame->vendor, sizeof(frame->vendor));
+ memcpy(ptr + 8, frame->product, sizeof(frame->product));
+
+ ptr[24] = frame->sdi;
+
+ hdmi_infoframe_set_checksum(buffer, length);
+
+ return length;
+}
+EXPORT_SYMBOL(hdmi_spd_infoframe_pack);
+
+/**
+ * hdmi_audio_infoframe_init() - initialize an HDMI audio infoframe
+ * @frame: HDMI audio infoframe
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int hdmi_audio_infoframe_init(struct hdmi_audio_infoframe *frame)
+{
+ memset(frame, 0, sizeof(*frame));
+
+ frame->type = HDMI_INFOFRAME_TYPE_AUDIO;
+ frame->version = 1;
+ frame->length = HDMI_AUDIO_INFOFRAME_SIZE;
+
+ return 0;
+}
+EXPORT_SYMBOL(hdmi_audio_infoframe_init);
+
+/**
+ * hdmi_audio_infoframe_pack() - write HDMI audio infoframe to binary buffer
+ * @frame: HDMI audio infoframe
+ * @buffer: destination buffer
+ * @size: size of buffer
+ *
+ * Packs the information contained in the @frame structure into a binary
+ * representation that can be written into the corresponding controller
+ * registers. Also computes the checksum as required by section 5.3.5 of
+ * the HDMI 1.4 specification.
+ *
+ * Returns the number of bytes packed into the binary buffer or a negative
+ * error code on failure.
+ */
+ssize_t hdmi_audio_infoframe_pack(struct hdmi_audio_infoframe *frame,
+ void *buffer, size_t size)
+{
+ unsigned char channels;
+ uint8_t *ptr = buffer;
+ size_t length;
+
+ length = HDMI_INFOFRAME_HEADER_SIZE + frame->length;
+
+ if (size < length)
+ return -ENOSPC;
+
+ memset(buffer, 0, size);
+
+ if (frame->channels >= 2)
+ channels = frame->channels - 1;
+ else
+ channels = 0;
+
+ ptr[0] = frame->type;
+ ptr[1] = frame->version;
+ ptr[2] = frame->length;
+ ptr[3] = 0; /* checksum */
+
+ /* start infoframe payload */
+ ptr += HDMI_INFOFRAME_HEADER_SIZE;
+
+ ptr[0] = ((frame->coding_type & 0xf) << 4) | (channels & 0x7);
+ ptr[1] = ((frame->sample_frequency & 0x7) << 2) |
+ (frame->sample_size & 0x3);
+ ptr[2] = frame->coding_type_ext & 0x1f;
+ ptr[3] = frame->channel_allocation;
+ ptr[4] = (frame->level_shift_value & 0xf) << 3;
+
+ if (frame->downmix_inhibit)
+ ptr[4] |= BIT(7);
+
+ hdmi_infoframe_set_checksum(buffer, length);
+
+ return length;
+}
+EXPORT_SYMBOL(hdmi_audio_infoframe_pack);
+
+/**
+ * hdmi_vendor_infoframe_init() - initialize an HDMI vendor infoframe
+ * @frame: HDMI vendor infoframe
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int hdmi_vendor_infoframe_init(struct hdmi_vendor_infoframe *frame)
+{
+ memset(frame, 0, sizeof(*frame));
+
+ frame->type = HDMI_INFOFRAME_TYPE_VENDOR;
+ frame->version = 1;
+
+ frame->oui = HDMI_IEEE_OUI;
+
+ /*
+ * 0 is a valid value for s3d_struct, so we use a special "not set"
+ * value
+ */
+ frame->s3d_struct = HDMI_3D_STRUCTURE_INVALID;
+
+ return 0;
+}
+EXPORT_SYMBOL(hdmi_vendor_infoframe_init);
+
+/**
+ * hdmi_vendor_infoframe_pack() - write a HDMI vendor infoframe to binary buffer
+ * @frame: HDMI infoframe
+ * @buffer: destination buffer
+ * @size: size of buffer
+ *
+ * Packs the information contained in the @frame structure into a binary
+ * representation that can be written into the corresponding controller
+ * registers. Also computes the checksum as required by section 5.3.5 of
+ * the HDMI 1.4 specification.
+ *
+ * Returns the number of bytes packed into the binary buffer or a negative
+ * error code on failure.
+ */
+ssize_t hdmi_vendor_infoframe_pack(struct hdmi_vendor_infoframe *frame,
+ void *buffer, size_t size)
+{
+ uint8_t *ptr = buffer;
+ size_t length;
+
+ /* empty info frame */
+ if (frame->vic == 0 && frame->s3d_struct == HDMI_3D_STRUCTURE_INVALID)
+ return -EINVAL;
+
+ /* only one of those can be supplied */
+ if (frame->vic != 0 && frame->s3d_struct != HDMI_3D_STRUCTURE_INVALID)
+ return -EINVAL;
+
+ /* for side by side (half) we also need to provide 3D_Ext_Data */
+ if (frame->s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF)
+ frame->length = 6;
+ else
+ frame->length = 5;
+
+ length = HDMI_INFOFRAME_HEADER_SIZE + frame->length;
+
+ if (size < length)
+ return -ENOSPC;
+
+ memset(buffer, 0, size);
+
+ ptr[0] = frame->type;
+ ptr[1] = frame->version;
+ ptr[2] = frame->length;
+ ptr[3] = 0; /* checksum */
+
+ /* HDMI OUI */
+ ptr[4] = 0x03;
+ ptr[5] = 0x0c;
+ ptr[6] = 0x00;
+
+ if (frame->vic) {
+ ptr[7] = 0x1 << 5; /* video format */
+ ptr[8] = frame->vic;
+ } else {
+ ptr[7] = 0x2 << 5; /* video format */
+ ptr[8] = (frame->s3d_struct & 0xf) << 4;
+ if (frame->s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF)
+ ptr[9] = (frame->s3d_ext_data & 0xf) << 4;
+ }
+
+ hdmi_infoframe_set_checksum(buffer, length);
+
+ return length;
+}
+EXPORT_SYMBOL(hdmi_vendor_infoframe_pack);
+
+/*
+ * hdmi_vendor_any_infoframe_pack() - write a vendor infoframe to binary buffer
+ */
+static ssize_t
+hdmi_vendor_any_infoframe_pack(union hdmi_vendor_any_infoframe *frame,
+ void *buffer, size_t size)
+{
+ /* we only know about HDMI vendor infoframes */
+ if (frame->any.oui != HDMI_IEEE_OUI)
+ return -EINVAL;
+
+ return hdmi_vendor_infoframe_pack(&frame->hdmi, buffer, size);
+}
+
+/**
+ * hdmi_infoframe_pack() - write a HDMI infoframe to binary buffer
+ * @frame: HDMI infoframe
+ * @buffer: destination buffer
+ * @size: size of buffer
+ *
+ * Packs the information contained in the @frame structure into a binary
+ * representation that can be written into the corresponding controller
+ * registers. Also computes the checksum as required by section 5.3.5 of
+ * the HDMI 1.4 specification.
+ *
+ * Returns the number of bytes packed into the binary buffer or a negative
+ * error code on failure.
+ */
+ssize_t
+hdmi_infoframe_pack(union hdmi_infoframe *frame, void *buffer, size_t size)
+{
+ ssize_t length;
+
+ switch (frame->any.type) {
+ case HDMI_INFOFRAME_TYPE_AVI:
+ length = hdmi_avi_infoframe_pack(&frame->avi, buffer, size);
+ break;
+ case HDMI_INFOFRAME_TYPE_SPD:
+ length = hdmi_spd_infoframe_pack(&frame->spd, buffer, size);
+ break;
+ case HDMI_INFOFRAME_TYPE_AUDIO:
+ length = hdmi_audio_infoframe_pack(&frame->audio, buffer, size);
+ break;
+ case HDMI_INFOFRAME_TYPE_VENDOR:
+ length = hdmi_vendor_any_infoframe_pack(&frame->vendor,
+ buffer, size);
+ break;
+ default:
+ printf("Bad infoframe type %d\n", frame->any.type);
+ length = -EINVAL;
+ }
+
+ return length;
+}
+EXPORT_SYMBOL(hdmi_infoframe_pack);
+
+static const char *hdmi_infoframe_type_get_name(enum hdmi_infoframe_type type)
+{
+ if (type < 0x80 || type > 0x9f)
+ return "Invalid";
+ switch (type) {
+ case HDMI_INFOFRAME_TYPE_VENDOR:
+ return "Vendor";
+ case HDMI_INFOFRAME_TYPE_AVI:
+ return "Auxiliary Video Information (AVI)";
+ case HDMI_INFOFRAME_TYPE_SPD:
+ return "Source Product Description (SPD)";
+ case HDMI_INFOFRAME_TYPE_AUDIO:
+ return "Audio";
+ }
+ return "Reserved";
+}
+
+static void hdmi_infoframe_log_header(struct hdmi_any_infoframe *frame)
+{
+ hdmi_log("HDMI infoframe: %s, version %u, length %u\n",
+ hdmi_infoframe_type_get_name(frame->type),
+ frame->version, frame->length);
+}
+
+static const char *hdmi_colorspace_get_name(enum hdmi_colorspace colorspace)
+{
+ switch (colorspace) {
+ case HDMI_COLORSPACE_RGB:
+ return "RGB";
+ case HDMI_COLORSPACE_YUV422:
+ return "YCbCr 4:2:2";
+ case HDMI_COLORSPACE_YUV444:
+ return "YCbCr 4:4:4";
+ case HDMI_COLORSPACE_YUV420:
+ return "YCbCr 4:2:0";
+ case HDMI_COLORSPACE_RESERVED4:
+ return "Reserved (4)";
+ case HDMI_COLORSPACE_RESERVED5:
+ return "Reserved (5)";
+ case HDMI_COLORSPACE_RESERVED6:
+ return "Reserved (6)";
+ case HDMI_COLORSPACE_IDO_DEFINED:
+ return "IDO Defined";
+ }
+ return "Invalid";
+}
+
+static const char *hdmi_scan_mode_get_name(enum hdmi_scan_mode scan_mode)
+{
+ switch (scan_mode) {
+ case HDMI_SCAN_MODE_NONE:
+ return "No Data";
+ case HDMI_SCAN_MODE_OVERSCAN:
+ return "Overscan";
+ case HDMI_SCAN_MODE_UNDERSCAN:
+ return "Underscan";
+ case HDMI_SCAN_MODE_RESERVED:
+ return "Reserved";
+ }
+ return "Invalid";
+}
+
+static const char *hdmi_colorimetry_get_name(enum hdmi_colorimetry colorimetry)
+{
+ switch (colorimetry) {
+ case HDMI_COLORIMETRY_NONE:
+ return "No Data";
+ case HDMI_COLORIMETRY_ITU_601:
+ return "ITU601";
+ case HDMI_COLORIMETRY_ITU_709:
+ return "ITU709";
+ case HDMI_COLORIMETRY_EXTENDED:
+ return "Extended";
+ }
+ return "Invalid";
+}
+
+static const char *
+hdmi_picture_aspect_get_name(enum hdmi_picture_aspect picture_aspect)
+{
+ switch (picture_aspect) {
+ case HDMI_PICTURE_ASPECT_NONE:
+ return "No Data";
+ case HDMI_PICTURE_ASPECT_4_3:
+ return "4:3";
+ case HDMI_PICTURE_ASPECT_16_9:
+ return "16:9";
+ case HDMI_PICTURE_ASPECT_RESERVED:
+ return "Reserved";
+ }
+ return "Invalid";
+}
+
+static const char *
+hdmi_active_aspect_get_name(enum hdmi_active_aspect active_aspect)
+{
+ if (active_aspect > 0xf)
+ return "Invalid";
+
+ switch (active_aspect) {
+ case HDMI_ACTIVE_ASPECT_16_9_TOP:
+ return "16:9 Top";
+ case HDMI_ACTIVE_ASPECT_14_9_TOP:
+ return "14:9 Top";
+ case HDMI_ACTIVE_ASPECT_16_9_CENTER:
+ return "16:9 Center";
+ case HDMI_ACTIVE_ASPECT_PICTURE:
+ return "Same as Picture";
+ case HDMI_ACTIVE_ASPECT_4_3:
+ return "4:3";
+ case HDMI_ACTIVE_ASPECT_16_9:
+ return "16:9";
+ case HDMI_ACTIVE_ASPECT_14_9:
+ return "14:9";
+ case HDMI_ACTIVE_ASPECT_4_3_SP_14_9:
+ return "4:3 SP 14:9";
+ case HDMI_ACTIVE_ASPECT_16_9_SP_14_9:
+ return "16:9 SP 14:9";
+ case HDMI_ACTIVE_ASPECT_16_9_SP_4_3:
+ return "16:9 SP 4:3";
+ }
+ return "Reserved";
+}
+
+static const char *
+hdmi_extended_colorimetry_get_name(enum hdmi_extended_colorimetry ext_col)
+{
+ switch (ext_col) {
+ case HDMI_EXTENDED_COLORIMETRY_XV_YCC_601:
+ return "xvYCC 601";
+ case HDMI_EXTENDED_COLORIMETRY_XV_YCC_709:
+ return "xvYCC 709";
+ case HDMI_EXTENDED_COLORIMETRY_S_YCC_601:
+ return "sYCC 601";
+ case HDMI_EXTENDED_COLORIMETRY_ADOBE_YCC_601:
+ return "Adobe YCC 601";
+ case HDMI_EXTENDED_COLORIMETRY_ADOBE_RGB:
+ return "Adobe RGB";
+ case HDMI_EXTENDED_COLORIMETRY_BT2020_CONST_LUM:
+ return "BT.2020 Constant Luminance";
+ case HDMI_EXTENDED_COLORIMETRY_BT2020:
+ return "BT.2020";
+ case HDMI_EXTENDED_COLORIMETRY_RESERVED:
+ return "Reserved";
+ }
+ return "Invalid";
+}
+
+static const char *
+hdmi_quantization_range_get_name(enum hdmi_quantization_range qrange)
+{
+ switch (qrange) {
+ case HDMI_QUANTIZATION_RANGE_DEFAULT:
+ return "Default";
+ case HDMI_QUANTIZATION_RANGE_LIMITED:
+ return "Limited";
+ case HDMI_QUANTIZATION_RANGE_FULL:
+ return "Full";
+ case HDMI_QUANTIZATION_RANGE_RESERVED:
+ return "Reserved";
+ }
+ return "Invalid";
+}
+
+static const char *hdmi_nups_get_name(enum hdmi_nups nups)
+{
+ switch (nups) {
+ case HDMI_NUPS_UNKNOWN:
+ return "Unknown Non-uniform Scaling";
+ case HDMI_NUPS_HORIZONTAL:
+ return "Horizontally Scaled";
+ case HDMI_NUPS_VERTICAL:
+ return "Vertically Scaled";
+ case HDMI_NUPS_BOTH:
+ return "Horizontally and Vertically Scaled";
+ }
+ return "Invalid";
+}
+
+static const char *
+hdmi_ycc_quantization_range_get_name(enum hdmi_ycc_quantization_range qrange)
+{
+ switch (qrange) {
+ case HDMI_YCC_QUANTIZATION_RANGE_LIMITED:
+ return "Limited";
+ case HDMI_YCC_QUANTIZATION_RANGE_FULL:
+ return "Full";
+ }
+ return "Invalid";
+}
+
+static const char *
+hdmi_content_type_get_name(enum hdmi_content_type content_type)
+{
+ switch (content_type) {
+ case HDMI_CONTENT_TYPE_GRAPHICS:
+ return "Graphics";
+ case HDMI_CONTENT_TYPE_PHOTO:
+ return "Photo";
+ case HDMI_CONTENT_TYPE_CINEMA:
+ return "Cinema";
+ case HDMI_CONTENT_TYPE_GAME:
+ return "Game";
+ }
+ return "Invalid";
+}
+
+/**
+ * hdmi_avi_infoframe_log() - log info of HDMI AVI infoframe
+ * @level: logging level
+ * @dev: device
+ * @frame: HDMI AVI infoframe
+ */
+static void hdmi_avi_infoframe_log(struct hdmi_avi_infoframe *frame)
+{
+ hdmi_infoframe_log_header((struct hdmi_any_infoframe *)frame);
+
+ hdmi_log(" colorspace: %s\n",
+ hdmi_colorspace_get_name(frame->colorspace));
+ hdmi_log(" scan mode: %s\n",
+ hdmi_scan_mode_get_name(frame->scan_mode));
+ hdmi_log(" colorimetry: %s\n",
+ hdmi_colorimetry_get_name(frame->colorimetry));
+ hdmi_log(" picture aspect: %s\n",
+ hdmi_picture_aspect_get_name(frame->picture_aspect));
+ hdmi_log(" active aspect: %s\n",
+ hdmi_active_aspect_get_name(frame->active_aspect));
+ hdmi_log(" itc: %s\n", frame->itc ? "IT Content" : "No Data");
+ hdmi_log(" extended colorimetry: %s\n",
+ hdmi_extended_colorimetry_get_name(frame->extended_colorimetry));
+ hdmi_log(" quantization range: %s\n",
+ hdmi_quantization_range_get_name(frame->quantization_range));
+ hdmi_log(" nups: %s\n", hdmi_nups_get_name(frame->nups));
+ hdmi_log(" video code: %u\n", frame->video_code);
+ hdmi_log(" ycc quantization range: %s\n",
+ hdmi_ycc_quantization_range_get_name(frame->ycc_quantization_range));
+ hdmi_log(" hdmi content type: %s\n",
+ hdmi_content_type_get_name(frame->content_type));
+ hdmi_log(" pixel repeat: %u\n", frame->pixel_repeat);
+ hdmi_log(" bar top %u, bottom %u, left %u, right %u\n",
+ frame->top_bar, frame->bottom_bar,
+ frame->left_bar, frame->right_bar);
+}
+
+static const char *hdmi_spd_sdi_get_name(enum hdmi_spd_sdi sdi)
+{
+;
+ switch (sdi) {
+ case HDMI_SPD_SDI_UNKNOWN:
+ return "Unknown";
+ case HDMI_SPD_SDI_DSTB:
+ return "Digital STB";
+ case HDMI_SPD_SDI_DVDP:
+ return "DVD Player";
+ case HDMI_SPD_SDI_DVHS:
+ return "D-VHS";
+ case HDMI_SPD_SDI_HDDVR:
+ return "HDD Videorecorder";
+ case HDMI_SPD_SDI_DVC:
+ return "DVC";
+ case HDMI_SPD_SDI_DSC:
+ return "DSC";
+ case HDMI_SPD_SDI_VCD:
+ return "Video CD";
+ case HDMI_SPD_SDI_GAME:
+ return "Game";
+ case HDMI_SPD_SDI_PC:
+ return "PC General";
+ case HDMI_SPD_SDI_BD:
+ return "Blu-Ray Disc (BD)";
+ case HDMI_SPD_SDI_SACD:
+ return "Super Audio CD";
+ case HDMI_SPD_SDI_HDDVD:
+ return "HD DVD";
+ case HDMI_SPD_SDI_PMP:
+ return "PMP";
+ }
+ return "Reserved";
+}
+
+/**
+ * hdmi_spd_infoframe_log() - log info of HDMI SPD infoframe
+ * @level: logging level
+ * @dev: device
+ * @frame: HDMI SPD infoframe
+ */
+static void hdmi_spd_infoframe_log(struct hdmi_spd_infoframe *frame)
+{
+ uint8_t buf[17];
+
+ hdmi_infoframe_log_header((struct hdmi_any_infoframe *)frame);
+
+ memset(buf, 0, sizeof(buf));
+
+ strncpy(buf, frame->vendor, 8);
+ hdmi_log(" vendor: %s\n", buf);
+ strncpy(buf, frame->product, 16);
+ hdmi_log(" product: %s\n", buf);
+ hdmi_log(" source device information: %s (0x%x)\n",
+ hdmi_spd_sdi_get_name(frame->sdi), frame->sdi);
+}
+
+static const char *
+hdmi_audio_coding_type_get_name(enum hdmi_audio_coding_type coding_type)
+{
+ switch (coding_type) {
+ case HDMI_AUDIO_CODING_TYPE_STREAM:
+ return "Refer to Stream Header";
+ case HDMI_AUDIO_CODING_TYPE_PCM:
+ return "PCM";
+ case HDMI_AUDIO_CODING_TYPE_AC3:
+ return "AC-3";
+ case HDMI_AUDIO_CODING_TYPE_MPEG1:
+ return "MPEG1";
+ case HDMI_AUDIO_CODING_TYPE_MP3:
+ return "MP3";
+ case HDMI_AUDIO_CODING_TYPE_MPEG2:
+ return "MPEG2";
+ case HDMI_AUDIO_CODING_TYPE_AAC_LC:
+ return "AAC";
+ case HDMI_AUDIO_CODING_TYPE_DTS:
+ return "DTS";
+ case HDMI_AUDIO_CODING_TYPE_ATRAC:
+ return "ATRAC";
+ case HDMI_AUDIO_CODING_TYPE_DSD:
+ return "One Bit Audio";
+ case HDMI_AUDIO_CODING_TYPE_EAC3:
+ return "Dolby Digital +";
+ case HDMI_AUDIO_CODING_TYPE_DTS_HD:
+ return "DTS-HD";
+ case HDMI_AUDIO_CODING_TYPE_MLP:
+ return "MAT (MLP)";
+ case HDMI_AUDIO_CODING_TYPE_DST:
+ return "DST";
+ case HDMI_AUDIO_CODING_TYPE_WMA_PRO:
+ return "WMA PRO";
+ case HDMI_AUDIO_CODING_TYPE_CXT:
+ return "Refer to CXT";
+ }
+ return "Invalid";
+}
+
+static const char *
+hdmi_audio_sample_size_get_name(enum hdmi_audio_sample_size sample_size)
+{
+ switch (sample_size) {
+ case HDMI_AUDIO_SAMPLE_SIZE_STREAM:
+ return "Refer to Stream Header";
+ case HDMI_AUDIO_SAMPLE_SIZE_16:
+ return "16 bit";
+ case HDMI_AUDIO_SAMPLE_SIZE_20:
+ return "20 bit";
+ case HDMI_AUDIO_SAMPLE_SIZE_24:
+ return "24 bit";
+ }
+ return "Invalid";
+}
+
+static const char *
+hdmi_audio_sample_frequency_get_name(enum hdmi_audio_sample_frequency freq)
+{
+ switch (freq) {
+ case HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM:
+ return "Refer to Stream Header";
+ case HDMI_AUDIO_SAMPLE_FREQUENCY_32000:
+ return "32 kHz";
+ case HDMI_AUDIO_SAMPLE_FREQUENCY_44100:
+ return "44.1 kHz (CD)";
+ case HDMI_AUDIO_SAMPLE_FREQUENCY_48000:
+ return "48 kHz";
+ case HDMI_AUDIO_SAMPLE_FREQUENCY_88200:
+ return "88.2 kHz";
+ case HDMI_AUDIO_SAMPLE_FREQUENCY_96000:
+ return "96 kHz";
+ case HDMI_AUDIO_SAMPLE_FREQUENCY_176400:
+ return "176.4 kHz";
+ case HDMI_AUDIO_SAMPLE_FREQUENCY_192000:
+ return "192 kHz";
+ }
+ return "Invalid";
+}
+
+static const char *
+hdmi_audio_coding_type_ext_get_name(enum hdmi_audio_coding_type_ext ctx)
+{
+
+ switch (ctx) {
+ case HDMI_AUDIO_CODING_TYPE_EXT_CT:
+ return "Refer to CT";
+ case HDMI_AUDIO_CODING_TYPE_EXT_HE_AAC:
+ return "HE AAC";
+ case HDMI_AUDIO_CODING_TYPE_EXT_HE_AAC_V2:
+ return "HE AAC v2";
+ case HDMI_AUDIO_CODING_TYPE_EXT_MPEG_SURROUND:
+ return "MPEG SURROUND";
+ case HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_HE_AAC:
+ return "MPEG-4 HE AAC";
+ case HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_HE_AAC_V2:
+ return "MPEG-4 HE AAC v2";
+ case HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_AAC_LC:
+ return "MPEG-4 AAC LC";
+ case HDMI_AUDIO_CODING_TYPE_EXT_DRA:
+ return "DRA";
+ case HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_HE_AAC_SURROUND:
+ return "MPEG-4 HE AAC + MPEG Surround";
+ case HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_AAC_LC_SURROUND:
+ return "MPEG-4 AAC LC + MPEG Surround";
+ }
+ return "Reserved";
+}
+
+/**
+ * hdmi_audio_infoframe_log() - log info of HDMI AUDIO infoframe
+ * @level: logging level
+ * @dev: device
+ * @frame: HDMI AUDIO infoframe
+ */
+static void hdmi_audio_infoframe_log(struct hdmi_audio_infoframe *frame)
+{
+ hdmi_infoframe_log_header((struct hdmi_any_infoframe *)frame);
+
+ if (frame->channels)
+ hdmi_log(" channels: %u\n", frame->channels - 1);
+ else
+ hdmi_log(" channels: Refer to stream header\n");
+ hdmi_log(" coding type: %s\n",
+ hdmi_audio_coding_type_get_name(frame->coding_type));
+ hdmi_log(" sample size: %s\n",
+ hdmi_audio_sample_size_get_name(frame->sample_size));
+ hdmi_log(" sample frequency: %s\n",
+ hdmi_audio_sample_frequency_get_name(frame->sample_frequency));
+ hdmi_log(" coding type ext: %s\n",
+ hdmi_audio_coding_type_ext_get_name(frame->coding_type_ext));
+ hdmi_log(" channel allocation: 0x%x\n",
+ frame->channel_allocation);
+ hdmi_log(" level shift value: %u dB\n",
+ frame->level_shift_value);
+ hdmi_log(" downmix inhibit: %s\n",
+ frame->downmix_inhibit ? "Yes" : "No");
+}
+
+static const char *
+hdmi_3d_structure_get_name(enum hdmi_3d_structure s3d_struct)
+{
+ if (s3d_struct < 0 || s3d_struct > 0xf)
+ return "Invalid";
+
+ switch (s3d_struct) {
+ case HDMI_3D_STRUCTURE_FRAME_PACKING:
+ return "Frame Packing";
+ case HDMI_3D_STRUCTURE_FIELD_ALTERNATIVE:
+ return "Field Alternative";
+ case HDMI_3D_STRUCTURE_LINE_ALTERNATIVE:
+ return "Line Alternative";
+ case HDMI_3D_STRUCTURE_SIDE_BY_SIDE_FULL:
+ return "Side-by-side (Full)";
+ case HDMI_3D_STRUCTURE_L_DEPTH:
+ return "L + Depth";
+ case HDMI_3D_STRUCTURE_L_DEPTH_GFX_GFX_DEPTH:
+ return "L + Depth + Graphics + Graphics-depth";
+ case HDMI_3D_STRUCTURE_TOP_AND_BOTTOM:
+ return "Top-and-Bottom";
+ case HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF:
+ return "Side-by-side (Half)";
+ default:
+ break;
+ }
+ return "Reserved";
+}
+
+/**
+ * hdmi_vendor_infoframe_log() - log info of HDMI VENDOR infoframe
+ * @level: logging level
+ * @dev: device
+ * @frame: HDMI VENDOR infoframe
+ */
+static void
+hdmi_vendor_any_infoframe_log(union hdmi_vendor_any_infoframe *frame)
+{
+ struct hdmi_vendor_infoframe *hvf = &frame->hdmi;
+
+ hdmi_infoframe_log_header((struct hdmi_any_infoframe *)frame);
+
+ if (frame->any.oui != HDMI_IEEE_OUI) {
+ hdmi_log(" not a HDMI vendor infoframe\n");
+ return;
+ }
+ if (hvf->vic == 0 && hvf->s3d_struct == HDMI_3D_STRUCTURE_INVALID) {
+ hdmi_log(" empty frame\n");
+ return;
+ }
+
+ if (hvf->vic)
+ hdmi_log(" HDMI VIC: %u\n", hvf->vic);
+ if (hvf->s3d_struct != HDMI_3D_STRUCTURE_INVALID) {
+ hdmi_log(" 3D structure: %s\n",
+ hdmi_3d_structure_get_name(hvf->s3d_struct));
+ if (hvf->s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF)
+ hdmi_log(" 3D extension data: %d\n",
+ hvf->s3d_ext_data);
+ }
+}
+
+/**
+ * hdmi_infoframe_log() - log info of HDMI infoframe
+ * @level: logging level
+ * @dev: device
+ * @frame: HDMI infoframe
+ */
+void hdmi_infoframe_log(union hdmi_infoframe *frame)
+{
+ switch (frame->any.type) {
+ case HDMI_INFOFRAME_TYPE_AVI:
+ hdmi_avi_infoframe_log(&frame->avi);
+ break;
+ case HDMI_INFOFRAME_TYPE_SPD:
+ hdmi_spd_infoframe_log(&frame->spd);
+ break;
+ case HDMI_INFOFRAME_TYPE_AUDIO:
+ hdmi_audio_infoframe_log(&frame->audio);
+ break;
+ case HDMI_INFOFRAME_TYPE_VENDOR:
+ hdmi_vendor_any_infoframe_log(&frame->vendor);
+ break;
+ }
+}
+EXPORT_SYMBOL(hdmi_infoframe_log);
+
+/**
+ * hdmi_avi_infoframe_unpack() - unpack binary buffer to a HDMI AVI infoframe
+ * @buffer: source buffer
+ * @frame: HDMI AVI infoframe
+ *
+ * Unpacks the information contained in binary @buffer into a structured
+ * @frame of the HDMI Auxiliary Video (AVI) information frame.
+ * Also verifies the checksum as required by section 5.3.5 of the HDMI 1.4
+ * specification.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+static int hdmi_avi_infoframe_unpack(struct hdmi_avi_infoframe *frame,
+ void *buffer)
+{
+ uint8_t *ptr = buffer;
+ int ret;
+
+ if (ptr[0] != HDMI_INFOFRAME_TYPE_AVI ||
+ ptr[1] != 2 ||
+ ptr[2] != HDMI_AVI_INFOFRAME_SIZE)
+ return -EINVAL;
+
+ if (hdmi_infoframe_checksum(buffer, HDMI_INFOFRAME_SIZE(AVI)) != 0)
+ return -EINVAL;
+
+ ret = hdmi_avi_infoframe_init(frame);
+ if (ret)
+ return ret;
+
+ ptr += HDMI_INFOFRAME_HEADER_SIZE;
+
+ frame->colorspace = (ptr[0] >> 5) & 0x3;
+ if (ptr[0] & 0x10)
+ frame->active_aspect = ptr[1] & 0xf;
+ if (ptr[0] & 0x8) {
+ frame->top_bar = (ptr[5] << 8) + ptr[6];
+ frame->bottom_bar = (ptr[7] << 8) + ptr[8];
+ }
+ if (ptr[0] & 0x4) {
+ frame->left_bar = (ptr[9] << 8) + ptr[10];
+ frame->right_bar = (ptr[11] << 8) + ptr[12];
+ }
+ frame->scan_mode = ptr[0] & 0x3;
+
+ frame->colorimetry = (ptr[1] >> 6) & 0x3;
+ frame->picture_aspect = (ptr[1] >> 4) & 0x3;
+ frame->active_aspect = ptr[1] & 0xf;
+
+ frame->itc = ptr[2] & 0x80 ? true : false;
+ frame->extended_colorimetry = (ptr[2] >> 4) & 0x7;
+ frame->quantization_range = (ptr[2] >> 2) & 0x3;
+ frame->nups = ptr[2] & 0x3;
+
+ frame->video_code = ptr[3] & 0x7f;
+ frame->ycc_quantization_range = (ptr[4] >> 6) & 0x3;
+ frame->content_type = (ptr[4] >> 4) & 0x3;
+
+ frame->pixel_repeat = ptr[4] & 0xf;
+
+ return 0;
+}
+
+/**
+ * hdmi_spd_infoframe_unpack() - unpack binary buffer to a HDMI SPD infoframe
+ * @buffer: source buffer
+ * @frame: HDMI SPD infoframe
+ *
+ * Unpacks the information contained in binary @buffer into a structured
+ * @frame of the HDMI Source Product Description (SPD) information frame.
+ * Also verifies the checksum as required by section 5.3.5 of the HDMI 1.4
+ * specification.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+static int hdmi_spd_infoframe_unpack(struct hdmi_spd_infoframe *frame,
+ void *buffer)
+{
+ uint8_t *ptr = buffer;
+ int ret;
+
+ if (ptr[0] != HDMI_INFOFRAME_TYPE_SPD ||
+ ptr[1] != 1 ||
+ ptr[2] != HDMI_SPD_INFOFRAME_SIZE) {
+ return -EINVAL;
+ }
+
+ if (hdmi_infoframe_checksum(buffer, HDMI_INFOFRAME_SIZE(SPD)) != 0)
+ return -EINVAL;
+
+ ptr += HDMI_INFOFRAME_HEADER_SIZE;
+
+ ret = hdmi_spd_infoframe_init(frame, ptr, ptr + 8);
+ if (ret)
+ return ret;
+
+ frame->sdi = ptr[24];
+
+ return 0;
+}
+
+/**
+ * hdmi_audio_infoframe_unpack() - unpack binary buffer to a HDMI AUDIO infoframe
+ * @buffer: source buffer
+ * @frame: HDMI Audio infoframe
+ *
+ * Unpacks the information contained in binary @buffer into a structured
+ * @frame of the HDMI Audio information frame.
+ * Also verifies the checksum as required by section 5.3.5 of the HDMI 1.4
+ * specification.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+static int hdmi_audio_infoframe_unpack(struct hdmi_audio_infoframe *frame,
+ void *buffer)
+{
+ uint8_t *ptr = buffer;
+ int ret;
+
+ if (ptr[0] != HDMI_INFOFRAME_TYPE_AUDIO ||
+ ptr[1] != 1 ||
+ ptr[2] != HDMI_AUDIO_INFOFRAME_SIZE) {
+ return -EINVAL;
+ }
+
+ if (hdmi_infoframe_checksum(buffer, HDMI_INFOFRAME_SIZE(AUDIO)) != 0)
+ return -EINVAL;
+
+ ret = hdmi_audio_infoframe_init(frame);
+ if (ret)
+ return ret;
+
+ ptr += HDMI_INFOFRAME_HEADER_SIZE;
+
+ frame->channels = ptr[0] & 0x7;
+ frame->coding_type = (ptr[0] >> 4) & 0xf;
+ frame->sample_size = ptr[1] & 0x3;
+ frame->sample_frequency = (ptr[1] >> 2) & 0x7;
+ frame->coding_type_ext = ptr[2] & 0x1f;
+ frame->channel_allocation = ptr[3];
+ frame->level_shift_value = (ptr[4] >> 3) & 0xf;
+ frame->downmix_inhibit = ptr[4] & 0x80 ? true : false;
+
+ return 0;
+}
+
+/**
+ * hdmi_vendor_infoframe_unpack() - unpack binary buffer to a HDMI vendor infoframe
+ * @buffer: source buffer
+ * @frame: HDMI Vendor infoframe
+ *
+ * Unpacks the information contained in binary @buffer into a structured
+ * @frame of the HDMI Vendor information frame.
+ * Also verifies the checksum as required by section 5.3.5 of the HDMI 1.4
+ * specification.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+static int
+hdmi_vendor_any_infoframe_unpack(union hdmi_vendor_any_infoframe *frame,
+ void *buffer)
+{
+ uint8_t *ptr = buffer;
+ size_t length;
+ int ret;
+ uint8_t hdmi_video_format;
+ struct hdmi_vendor_infoframe *hvf = &frame->hdmi;
+
+ if (ptr[0] != HDMI_INFOFRAME_TYPE_VENDOR ||
+ ptr[1] != 1 ||
+ (ptr[2] != 5 && ptr[2] != 6))
+ return -EINVAL;
+
+ length = ptr[2];
+
+ if (hdmi_infoframe_checksum(buffer,
+ HDMI_INFOFRAME_HEADER_SIZE + length) != 0)
+ return -EINVAL;
+
+ ptr += HDMI_INFOFRAME_HEADER_SIZE;
+
+ /* HDMI OUI */
+ if ((ptr[0] != 0x03) ||
+ (ptr[1] != 0x0c) ||
+ (ptr[2] != 0x00))
+ return -EINVAL;
+
+ hdmi_video_format = ptr[3] >> 5;
+
+ if (hdmi_video_format > 0x2)
+ return -EINVAL;
+
+ ret = hdmi_vendor_infoframe_init(hvf);
+ if (ret)
+ return ret;
+
+ hvf->length = length;
+
+ if (hdmi_video_format == 0x1) {
+ hvf->vic = ptr[4];
+ } else if (hdmi_video_format == 0x2) {
+ hvf->s3d_struct = ptr[4] >> 4;
+ if (hvf->s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF) {
+ if (length == 6)
+ hvf->s3d_ext_data = ptr[5] >> 4;
+ else
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * hdmi_infoframe_unpack() - unpack binary buffer to a HDMI infoframe
+ * @buffer: source buffer
+ * @frame: HDMI infoframe
+ *
+ * Unpacks the information contained in binary buffer @buffer into a structured
+ * @frame of a HDMI infoframe.
+ * Also verifies the checksum as required by section 5.3.5 of the HDMI 1.4
+ * specification.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int hdmi_infoframe_unpack(union hdmi_infoframe *frame, void *buffer)
+{
+ int ret;
+ uint8_t *ptr = buffer;
+
+ switch (ptr[0]) {
+ case HDMI_INFOFRAME_TYPE_AVI:
+ ret = hdmi_avi_infoframe_unpack(&frame->avi, buffer);
+ break;
+ case HDMI_INFOFRAME_TYPE_SPD:
+ ret = hdmi_spd_infoframe_unpack(&frame->spd, buffer);
+ break;
+ case HDMI_INFOFRAME_TYPE_AUDIO:
+ ret = hdmi_audio_infoframe_unpack(&frame->audio, buffer);
+ break;
+ case HDMI_INFOFRAME_TYPE_VENDOR:
+ ret = hdmi_vendor_any_infoframe_unpack(&frame->vendor, buffer);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(hdmi_infoframe_unpack);
diff --git a/sys/arm/nvidia/drm2/hdmi.h b/sys/arm/nvidia/drm2/hdmi.h
new file mode 100644
index 000000000000..df9831546428
--- /dev/null
+++ b/sys/arm/nvidia/drm2/hdmi.h
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2012 Avionic Design GmbH
+ *
+ * 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, sub license,
+ * 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 NON-INFRINGEMENT. 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _HDMI_H_
+#define _HDMI_H_
+
+enum hdmi_infoframe_type {
+ HDMI_INFOFRAME_TYPE_VENDOR = 0x81,
+ HDMI_INFOFRAME_TYPE_AVI = 0x82,
+ HDMI_INFOFRAME_TYPE_SPD = 0x83,
+ HDMI_INFOFRAME_TYPE_AUDIO = 0x84,
+};
+
+#define HDMI_IEEE_OUI 0x000c03
+#define HDMI_INFOFRAME_HEADER_SIZE 4
+#define HDMI_AVI_INFOFRAME_SIZE 13
+#define HDMI_SPD_INFOFRAME_SIZE 25
+#define HDMI_AUDIO_INFOFRAME_SIZE 10
+
+#define HDMI_INFOFRAME_SIZE(type) \
+ (HDMI_INFOFRAME_HEADER_SIZE + HDMI_ ## type ## _INFOFRAME_SIZE)
+
+struct hdmi_any_infoframe {
+ enum hdmi_infoframe_type type;
+ unsigned char version;
+ unsigned char length;
+};
+
+enum hdmi_colorspace {
+ HDMI_COLORSPACE_RGB,
+ HDMI_COLORSPACE_YUV422,
+ HDMI_COLORSPACE_YUV444,
+ HDMI_COLORSPACE_YUV420,
+ HDMI_COLORSPACE_RESERVED4,
+ HDMI_COLORSPACE_RESERVED5,
+ HDMI_COLORSPACE_RESERVED6,
+ HDMI_COLORSPACE_IDO_DEFINED,
+};
+
+enum hdmi_scan_mode {
+ HDMI_SCAN_MODE_NONE,
+ HDMI_SCAN_MODE_OVERSCAN,
+ HDMI_SCAN_MODE_UNDERSCAN,
+ HDMI_SCAN_MODE_RESERVED,
+};
+
+enum hdmi_colorimetry {
+ HDMI_COLORIMETRY_NONE,
+ HDMI_COLORIMETRY_ITU_601,
+ HDMI_COLORIMETRY_ITU_709,
+ HDMI_COLORIMETRY_EXTENDED,
+};
+
+enum hdmi_picture_aspect {
+ HDMI_PICTURE_ASPECT_NONE,
+ HDMI_PICTURE_ASPECT_4_3,
+ HDMI_PICTURE_ASPECT_16_9,
+ HDMI_PICTURE_ASPECT_RESERVED,
+};
+
+enum hdmi_active_aspect {
+ HDMI_ACTIVE_ASPECT_16_9_TOP = 2,
+ HDMI_ACTIVE_ASPECT_14_9_TOP = 3,
+ HDMI_ACTIVE_ASPECT_16_9_CENTER = 4,
+ HDMI_ACTIVE_ASPECT_PICTURE = 8,
+ HDMI_ACTIVE_ASPECT_4_3 = 9,
+ HDMI_ACTIVE_ASPECT_16_9 = 10,
+ HDMI_ACTIVE_ASPECT_14_9 = 11,
+ HDMI_ACTIVE_ASPECT_4_3_SP_14_9 = 13,
+ HDMI_ACTIVE_ASPECT_16_9_SP_14_9 = 14,
+ HDMI_ACTIVE_ASPECT_16_9_SP_4_3 = 15,
+};
+
+enum hdmi_extended_colorimetry {
+ HDMI_EXTENDED_COLORIMETRY_XV_YCC_601,
+ HDMI_EXTENDED_COLORIMETRY_XV_YCC_709,
+ HDMI_EXTENDED_COLORIMETRY_S_YCC_601,
+ HDMI_EXTENDED_COLORIMETRY_ADOBE_YCC_601,
+ HDMI_EXTENDED_COLORIMETRY_ADOBE_RGB,
+
+ /* The following EC values are only defined in CEA-861-F. */
+ HDMI_EXTENDED_COLORIMETRY_BT2020_CONST_LUM,
+ HDMI_EXTENDED_COLORIMETRY_BT2020,
+ HDMI_EXTENDED_COLORIMETRY_RESERVED,
+};
+
+enum hdmi_quantization_range {
+ HDMI_QUANTIZATION_RANGE_DEFAULT,
+ HDMI_QUANTIZATION_RANGE_LIMITED,
+ HDMI_QUANTIZATION_RANGE_FULL,
+ HDMI_QUANTIZATION_RANGE_RESERVED,
+};
+
+/* non-uniform picture scaling */
+enum hdmi_nups {
+ HDMI_NUPS_UNKNOWN,
+ HDMI_NUPS_HORIZONTAL,
+ HDMI_NUPS_VERTICAL,
+ HDMI_NUPS_BOTH,
+};
+
+enum hdmi_ycc_quantization_range {
+ HDMI_YCC_QUANTIZATION_RANGE_LIMITED,
+ HDMI_YCC_QUANTIZATION_RANGE_FULL,
+};
+
+enum hdmi_content_type {
+ HDMI_CONTENT_TYPE_GRAPHICS,
+ HDMI_CONTENT_TYPE_PHOTO,
+ HDMI_CONTENT_TYPE_CINEMA,
+ HDMI_CONTENT_TYPE_GAME,
+};
+
+struct hdmi_avi_infoframe {
+ enum hdmi_infoframe_type type;
+ unsigned char version;
+ unsigned char length;
+ enum hdmi_colorspace colorspace;
+ enum hdmi_scan_mode scan_mode;
+ enum hdmi_colorimetry colorimetry;
+ enum hdmi_picture_aspect picture_aspect;
+ enum hdmi_active_aspect active_aspect;
+ bool itc;
+ enum hdmi_extended_colorimetry extended_colorimetry;
+ enum hdmi_quantization_range quantization_range;
+ enum hdmi_nups nups;
+ unsigned char video_code;
+ enum hdmi_ycc_quantization_range ycc_quantization_range;
+ enum hdmi_content_type content_type;
+ unsigned char pixel_repeat;
+ unsigned short top_bar;
+ unsigned short bottom_bar;
+ unsigned short left_bar;
+ unsigned short right_bar;
+};
+
+int hdmi_avi_infoframe_init(struct hdmi_avi_infoframe *frame);
+ssize_t hdmi_avi_infoframe_pack(struct hdmi_avi_infoframe *frame, void *buffer,
+ size_t size);
+
+enum hdmi_spd_sdi {
+ HDMI_SPD_SDI_UNKNOWN,
+ HDMI_SPD_SDI_DSTB,
+ HDMI_SPD_SDI_DVDP,
+ HDMI_SPD_SDI_DVHS,
+ HDMI_SPD_SDI_HDDVR,
+ HDMI_SPD_SDI_DVC,
+ HDMI_SPD_SDI_DSC,
+ HDMI_SPD_SDI_VCD,
+ HDMI_SPD_SDI_GAME,
+ HDMI_SPD_SDI_PC,
+ HDMI_SPD_SDI_BD,
+ HDMI_SPD_SDI_SACD,
+ HDMI_SPD_SDI_HDDVD,
+ HDMI_SPD_SDI_PMP,
+};
+
+struct hdmi_spd_infoframe {
+ enum hdmi_infoframe_type type;
+ unsigned char version;
+ unsigned char length;
+ char vendor[8];
+ char product[16];
+ enum hdmi_spd_sdi sdi;
+};
+
+int hdmi_spd_infoframe_init(struct hdmi_spd_infoframe *frame,
+ const char *vendor, const char *product);
+ssize_t hdmi_spd_infoframe_pack(struct hdmi_spd_infoframe *frame, void *buffer,
+ size_t size);
+
+enum hdmi_audio_coding_type {
+ HDMI_AUDIO_CODING_TYPE_STREAM,
+ HDMI_AUDIO_CODING_TYPE_PCM,
+ HDMI_AUDIO_CODING_TYPE_AC3,
+ HDMI_AUDIO_CODING_TYPE_MPEG1,
+ HDMI_AUDIO_CODING_TYPE_MP3,
+ HDMI_AUDIO_CODING_TYPE_MPEG2,
+ HDMI_AUDIO_CODING_TYPE_AAC_LC,
+ HDMI_AUDIO_CODING_TYPE_DTS,
+ HDMI_AUDIO_CODING_TYPE_ATRAC,
+ HDMI_AUDIO_CODING_TYPE_DSD,
+ HDMI_AUDIO_CODING_TYPE_EAC3,
+ HDMI_AUDIO_CODING_TYPE_DTS_HD,
+ HDMI_AUDIO_CODING_TYPE_MLP,
+ HDMI_AUDIO_CODING_TYPE_DST,
+ HDMI_AUDIO_CODING_TYPE_WMA_PRO,
+ HDMI_AUDIO_CODING_TYPE_CXT,
+};
+
+enum hdmi_audio_sample_size {
+ HDMI_AUDIO_SAMPLE_SIZE_STREAM,
+ HDMI_AUDIO_SAMPLE_SIZE_16,
+ HDMI_AUDIO_SAMPLE_SIZE_20,
+ HDMI_AUDIO_SAMPLE_SIZE_24,
+};
+
+enum hdmi_audio_sample_frequency {
+ HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM,
+ HDMI_AUDIO_SAMPLE_FREQUENCY_32000,
+ HDMI_AUDIO_SAMPLE_FREQUENCY_44100,
+ HDMI_AUDIO_SAMPLE_FREQUENCY_48000,
+ HDMI_AUDIO_SAMPLE_FREQUENCY_88200,
+ HDMI_AUDIO_SAMPLE_FREQUENCY_96000,
+ HDMI_AUDIO_SAMPLE_FREQUENCY_176400,
+ HDMI_AUDIO_SAMPLE_FREQUENCY_192000,
+};
+
+enum hdmi_audio_coding_type_ext {
+ /* Refer to Audio Coding Type (CT) field in Data Byte 1 */
+ HDMI_AUDIO_CODING_TYPE_EXT_CT,
+
+ /*
+ * The next three CXT values are defined in CEA-861-E only.
+ * They do not exist in older versions, and in CEA-861-F they are
+ * defined as 'Not in use'.
+ */
+ HDMI_AUDIO_CODING_TYPE_EXT_HE_AAC,
+ HDMI_AUDIO_CODING_TYPE_EXT_HE_AAC_V2,
+ HDMI_AUDIO_CODING_TYPE_EXT_MPEG_SURROUND,
+
+ /* The following CXT values are only defined in CEA-861-F. */
+ HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_HE_AAC,
+ HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_HE_AAC_V2,
+ HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_AAC_LC,
+ HDMI_AUDIO_CODING_TYPE_EXT_DRA,
+ HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_HE_AAC_SURROUND,
+ HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_AAC_LC_SURROUND = 10,
+};
+
+struct hdmi_audio_infoframe {
+ enum hdmi_infoframe_type type;
+ unsigned char version;
+ unsigned char length;
+ unsigned char channels;
+ enum hdmi_audio_coding_type coding_type;
+ enum hdmi_audio_sample_size sample_size;
+ enum hdmi_audio_sample_frequency sample_frequency;
+ enum hdmi_audio_coding_type_ext coding_type_ext;
+ unsigned char channel_allocation;
+ unsigned char level_shift_value;
+ bool downmix_inhibit;
+
+};
+
+int hdmi_audio_infoframe_init(struct hdmi_audio_infoframe *frame);
+ssize_t hdmi_audio_infoframe_pack(struct hdmi_audio_infoframe *frame,
+ void *buffer, size_t size);
+
+enum hdmi_3d_structure {
+ HDMI_3D_STRUCTURE_INVALID = -1,
+ HDMI_3D_STRUCTURE_FRAME_PACKING = 0,
+ HDMI_3D_STRUCTURE_FIELD_ALTERNATIVE,
+ HDMI_3D_STRUCTURE_LINE_ALTERNATIVE,
+ HDMI_3D_STRUCTURE_SIDE_BY_SIDE_FULL,
+ HDMI_3D_STRUCTURE_L_DEPTH,
+ HDMI_3D_STRUCTURE_L_DEPTH_GFX_GFX_DEPTH,
+ HDMI_3D_STRUCTURE_TOP_AND_BOTTOM,
+ HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF = 8,
+};
+
+struct hdmi_vendor_infoframe {
+ enum hdmi_infoframe_type type;
+ unsigned char version;
+ unsigned char length;
+ unsigned int oui;
+ uint8_t vic;
+ enum hdmi_3d_structure s3d_struct;
+ unsigned int s3d_ext_data;
+};
+
+int hdmi_vendor_infoframe_init(struct hdmi_vendor_infoframe *frame);
+ssize_t hdmi_vendor_infoframe_pack(struct hdmi_vendor_infoframe *frame,
+ void *buffer, size_t size);
+
+union hdmi_vendor_any_infoframe {
+ struct {
+ enum hdmi_infoframe_type type;
+ unsigned char version;
+ unsigned char length;
+ unsigned int oui;
+ } any;
+ struct hdmi_vendor_infoframe hdmi;
+};
+
+/**
+ * union hdmi_infoframe - overall union of all abstract infoframe representations
+ * @any: generic infoframe
+ * @avi: avi infoframe
+ * @spd: spd infoframe
+ * @vendor: union of all vendor infoframes
+ * @audio: audio infoframe
+ *
+ * This is used by the generic pack function. This works since all infoframes
+ * have the same header which also indicates which type of infoframe should be
+ * packed.
+ */
+union hdmi_infoframe {
+ struct hdmi_any_infoframe any;
+ struct hdmi_avi_infoframe avi;
+ struct hdmi_spd_infoframe spd;
+ union hdmi_vendor_any_infoframe vendor;
+ struct hdmi_audio_infoframe audio;
+};
+
+ssize_t
+hdmi_infoframe_pack(union hdmi_infoframe *frame, void *buffer, size_t size);
+int hdmi_infoframe_unpack(union hdmi_infoframe *frame, void *buffer);
+void hdmi_infoframe_log(union hdmi_infoframe *frame);
+
+#endif /* _HDMI_H */
diff --git a/sys/arm/nvidia/drm2/tegra_bo.c b/sys/arm/nvidia/drm2/tegra_bo.c
new file mode 100644
index 000000000000..7479fd8bc8da
--- /dev/null
+++ b/sys/arm/nvidia/drm2/tegra_bo.c
@@ -0,0 +1,366 @@
+/*-
+ * Copyright (c) 2015 Michal Meloun
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+
+#include <machine/bus.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/drm2/drmP.h>
+#include <dev/drm2/drm_crtc_helper.h>
+#include <dev/drm2/drm_fb_helper.h>
+
+#include <arm/nvidia/drm2/tegra_drm.h>
+
+#include <sys/vmem.h>
+#include <sys/vmem.h>
+#include <vm/vm.h>
+#include <vm/vm_pageout.h>
+
+static void
+tegra_bo_destruct(struct tegra_bo *bo)
+{
+ vm_page_t m;
+ size_t size;
+ int i;
+
+ if (bo->cdev_pager == NULL)
+ return;
+
+ size = round_page(bo->gem_obj.size);
+ if (bo->vbase != 0)
+ pmap_qremove(bo->vbase, bo->npages);
+
+ VM_OBJECT_WLOCK(bo->cdev_pager);
+ for (i = 0; i < bo->npages; i++) {
+ m = bo->m[i];
+ vm_page_busy_acquire(m, 0);
+ cdev_pager_free_page(bo->cdev_pager, m);
+ m->flags &= ~PG_FICTITIOUS;
+ vm_page_unwire_noq(m);
+ vm_page_free(m);
+ }
+ VM_OBJECT_WUNLOCK(bo->cdev_pager);
+
+ vm_object_deallocate(bo->cdev_pager);
+ if (bo->vbase != 0)
+ vmem_free(kmem_arena, bo->vbase, size);
+}
+
+static void
+tegra_bo_free_object(struct drm_gem_object *gem_obj)
+{
+ struct tegra_bo *bo;
+
+ bo = container_of(gem_obj, struct tegra_bo, gem_obj);
+ drm_gem_free_mmap_offset(gem_obj);
+ drm_gem_object_release(gem_obj);
+
+ tegra_bo_destruct(bo);
+
+ free(bo->m, DRM_MEM_DRIVER);
+ free(bo, DRM_MEM_DRIVER);
+}
+
+static int
+tegra_bo_alloc_contig(size_t npages, u_long alignment, vm_memattr_t memattr,
+ vm_page_t **ret_page)
+{
+ vm_page_t m;
+ int pflags, tries, i;
+ vm_paddr_t low, high, boundary;
+
+ low = 0;
+ high = -1UL;
+ boundary = 0;
+ pflags = VM_ALLOC_NORMAL | VM_ALLOC_NOOBJ | VM_ALLOC_NOBUSY |
+ VM_ALLOC_WIRED | VM_ALLOC_ZERO;
+ tries = 0;
+retry:
+ m = vm_page_alloc_contig(NULL, 0, pflags, npages, low, high, alignment,
+ boundary, memattr);
+ if (m == NULL) {
+ if (tries < 3) {
+ if (!vm_page_reclaim_contig(pflags, npages, low, high,
+ alignment, boundary))
+ vm_wait(NULL);
+ tries++;
+ goto retry;
+ }
+ return (ENOMEM);
+ }
+
+ for (i = 0; i < npages; i++, m++) {
+ if ((m->flags & PG_ZERO) == 0)
+ pmap_zero_page(m);
+ m->valid = VM_PAGE_BITS_ALL;
+ (*ret_page)[i] = m;
+ }
+
+ return (0);
+}
+
+/* Initialize pager and insert all object pages to it*/
+static int
+tegra_bo_init_pager(struct tegra_bo *bo)
+{
+ vm_page_t m;
+ size_t size;
+ int i;
+
+ size = round_page(bo->gem_obj.size);
+
+ bo->pbase = VM_PAGE_TO_PHYS(bo->m[0]);
+ if (vmem_alloc(kmem_arena, size, M_WAITOK | M_BESTFIT, &bo->vbase))
+ return (ENOMEM);
+
+ VM_OBJECT_WLOCK(bo->cdev_pager);
+ for (i = 0; i < bo->npages; i++) {
+ m = bo->m[i];
+ /*
+ * XXX This is a temporary hack.
+ * We need pager suitable for paging (mmap) managed
+ * real (non-fictitious) pages.
+ * - managed pages are needed for clean module unload.
+ * - aliasing fictitious page to real one is bad,
+ * pmap cannot handle this situation without issues
+ * It expects that
+ * paddr = PHYS_TO_VM_PAGE(VM_PAGE_TO_PHYS(paddr))
+ * for every single page passed to pmap.
+ */
+ m->oflags &= ~VPO_UNMANAGED;
+ m->flags |= PG_FICTITIOUS;
+ if (vm_page_insert(m, bo->cdev_pager, i) != 0)
+ return (EINVAL);
+ }
+ VM_OBJECT_WUNLOCK(bo->cdev_pager);
+
+ pmap_qenter(bo->vbase, bo->m, bo->npages);
+ return (0);
+}
+
+/* Allocate memory for frame buffer */
+static int
+tegra_bo_alloc(struct drm_device *drm, struct tegra_bo *bo)
+{
+ size_t size;
+ int rv;
+
+ size = bo->gem_obj.size;
+
+ bo->npages = atop(size);
+ bo->m = malloc(sizeof(vm_page_t *) * bo->npages, DRM_MEM_DRIVER,
+ M_WAITOK | M_ZERO);
+
+ rv = tegra_bo_alloc_contig(bo->npages, PAGE_SIZE,
+ VM_MEMATTR_WRITE_COMBINING, &(bo->m));
+ if (rv != 0) {
+ DRM_WARNING("Cannot allocate memory for gem object.\n");
+ return (rv);
+ }
+ rv = tegra_bo_init_pager(bo);
+ if (rv != 0) {
+ DRM_WARNING("Cannot initialize gem object pager.\n");
+ return (rv);
+ }
+ return (0);
+}
+
+int
+tegra_bo_create(struct drm_device *drm, size_t size, struct tegra_bo **res_bo)
+{
+ struct tegra_bo *bo;
+ int rv;
+
+ if (size <= 0)
+ return (-EINVAL);
+
+ bo = malloc(sizeof(*bo), DRM_MEM_DRIVER, M_WAITOK | M_ZERO);
+
+ size = round_page(size);
+ rv = drm_gem_object_init(drm, &bo->gem_obj, size);
+ if (rv != 0) {
+ free(bo, DRM_MEM_DRIVER);
+ return (rv);
+ }
+ rv = drm_gem_create_mmap_offset(&bo->gem_obj);
+ if (rv != 0) {
+ drm_gem_object_release(&bo->gem_obj);
+ free(bo, DRM_MEM_DRIVER);
+ return (rv);
+ }
+
+ bo->cdev_pager = cdev_pager_allocate(&bo->gem_obj, OBJT_MGTDEVICE,
+ drm->driver->gem_pager_ops, size, 0, 0, NULL);
+ rv = tegra_bo_alloc(drm, bo);
+ if (rv != 0) {
+ tegra_bo_free_object(&bo->gem_obj);
+ return (rv);
+ }
+
+ *res_bo = bo;
+ return (0);
+}
+
+static int
+tegra_bo_create_with_handle(struct drm_file *file, struct drm_device *drm,
+ size_t size, uint32_t *handle, struct tegra_bo **res_bo)
+{
+ int rv;
+ struct tegra_bo *bo;
+
+ rv = tegra_bo_create(drm, size, &bo);
+ if (rv != 0)
+ return (rv);
+
+ rv = drm_gem_handle_create(file, &bo->gem_obj, handle);
+ if (rv != 0) {
+ tegra_bo_free_object(&bo->gem_obj);
+ drm_gem_object_release(&bo->gem_obj);
+ return (rv);
+ }
+
+ drm_gem_object_unreference_unlocked(&bo->gem_obj);
+
+ *res_bo = bo;
+ return (0);
+}
+
+static int
+tegra_bo_dumb_create(struct drm_file *file, struct drm_device *drm_dev,
+ struct drm_mode_create_dumb *args)
+{
+ struct tegra_drm *drm;
+ struct tegra_bo *bo;
+ int rv;
+
+ drm = container_of(drm_dev, struct tegra_drm, drm_dev);
+
+ args->pitch= (args->width * args->bpp + 7) / 8;
+ args->pitch = roundup(args->pitch, drm->pitch_align);
+ args->size = args->pitch * args->height;
+ rv = tegra_bo_create_with_handle(file, drm_dev, args->size,
+ &args->handle, &bo);
+
+ return (rv);
+}
+
+static int
+tegra_bo_dumb_map_offset(struct drm_file *file_priv,
+ struct drm_device *drm_dev, uint32_t handle, uint64_t *offset)
+{
+ struct drm_gem_object *gem_obj;
+ int rv;
+
+ DRM_LOCK(drm_dev);
+ gem_obj = drm_gem_object_lookup(drm_dev, file_priv, handle);
+ if (gem_obj == NULL) {
+ device_printf(drm_dev->dev, "Object not found\n");
+ DRM_UNLOCK(drm_dev);
+ return (-EINVAL);
+ }
+ rv = drm_gem_create_mmap_offset(gem_obj);
+ if (rv != 0)
+ goto fail;
+
+ *offset = DRM_GEM_MAPPING_OFF(gem_obj->map_list.key) |
+ DRM_GEM_MAPPING_KEY;
+
+ drm_gem_object_unreference(gem_obj);
+ DRM_UNLOCK(drm_dev);
+ return (0);
+
+fail:
+ drm_gem_object_unreference(gem_obj);
+ DRM_UNLOCK(drm_dev);
+ return (rv);
+}
+
+static int
+tegra_bo_dumb_destroy(struct drm_file *file_priv, struct drm_device *drm_dev,
+ unsigned int handle)
+{
+ int rv;
+
+ rv = drm_gem_handle_delete(file_priv, handle);
+ return (rv);
+}
+
+/*
+ * mmap support
+ */
+static int
+tegra_gem_pager_fault(vm_object_t vm_obj, vm_ooffset_t offset, int prot,
+ vm_page_t *mres)
+{
+
+#ifdef DRM_PAGER_DEBUG
+ DRM_DEBUG("object %p offset %jd prot %d mres %p\n",
+ vm_obj, (intmax_t)offset, prot, mres);
+#endif
+ return (VM_PAGER_FAIL);
+
+}
+
+static int
+tegra_gem_pager_ctor(void *handle, vm_ooffset_t size, vm_prot_t prot,
+ vm_ooffset_t foff, struct ucred *cred, u_short *color)
+{
+
+ if (color != NULL)
+ *color = 0;
+ return (0);
+}
+
+static void
+tegra_gem_pager_dtor(void *handle)
+{
+
+}
+
+static struct cdev_pager_ops tegra_gem_pager_ops = {
+ .cdev_pg_fault = tegra_gem_pager_fault,
+ .cdev_pg_ctor = tegra_gem_pager_ctor,
+ .cdev_pg_dtor = tegra_gem_pager_dtor
+};
+
+/* Fill up relevant fields in drm_driver ops */
+void
+tegra_bo_driver_register(struct drm_driver *drm_drv)
+{
+ drm_drv->gem_free_object = tegra_bo_free_object;
+ drm_drv->gem_pager_ops = &tegra_gem_pager_ops;
+ drm_drv->dumb_create = tegra_bo_dumb_create;
+ drm_drv->dumb_map_offset = tegra_bo_dumb_map_offset;
+ drm_drv->dumb_destroy = tegra_bo_dumb_destroy;
+}
diff --git a/sys/arm/nvidia/drm2/tegra_dc.c b/sys/arm/nvidia/drm2/tegra_dc.c
new file mode 100644
index 000000000000..a5e661dab6b2
--- /dev/null
+++ b/sys/arm/nvidia/drm2/tegra_dc.c
@@ -0,0 +1,1441 @@
+/*-
+ * Copyright (c) 2015 Michal Meloun
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/gpio.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/drm2/drmP.h>
+#include <dev/drm2/drm_crtc_helper.h>
+#include <dev/drm2/drm_fb_helper.h>
+#include <dev/drm2/drm_fixed.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/nvidia/drm2/tegra_dc_reg.h>
+#include <arm/nvidia/drm2/tegra_drm.h>
+#include <arm/nvidia/tegra_pmc.h>
+
+#include "tegra_drm_if.h"
+#include "tegra_dc_if.h"
+
+#define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, 4 * (_r), (_v))
+#define RD4(_sc, _r) bus_read_4((_sc)->mem_res, 4 * (_r))
+
+#define LOCK(_sc) mtx_lock(&(_sc)->mtx)
+#define UNLOCK(_sc) mtx_unlock(&(_sc)->mtx)
+#define SLEEP(_sc, timeout) \
+ mtx_sleep(sc, &sc->mtx, 0, "tegra_dc_wait", timeout);
+#define LOCK_INIT(_sc) \
+ mtx_init(&_sc->mtx, device_get_nameunit(_sc->dev), "tegra_dc", MTX_DEF)
+#define LOCK_DESTROY(_sc) mtx_destroy(&_sc->mtx)
+#define ASSERT_LOCKED(_sc) mtx_assert(&_sc->mtx, MA_OWNED)
+#define ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED)
+
+#define SYNCPT_VBLANK0 26
+#define SYNCPT_VBLANK1 27
+
+#define DC_MAX_PLANES 2 /* Maximum planes */
+
+/* DRM Formats supported by DC */
+/* XXXX expand me */
+static uint32_t dc_plane_formats[] = {
+ DRM_FORMAT_XBGR8888,
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_RGB565,
+ DRM_FORMAT_UYVY,
+ DRM_FORMAT_YUYV,
+ DRM_FORMAT_YUV420,
+ DRM_FORMAT_YUV422,
+};
+
+/* Complete description of one window (plane) */
+struct dc_window {
+ /* Source (in framebuffer) rectangle, in pixels */
+ u_int src_x;
+ u_int src_y;
+ u_int src_w;
+ u_int src_h;
+
+ /* Destination (on display) rectangle, in pixels */
+ u_int dst_x;
+ u_int dst_y;
+ u_int dst_w;
+ u_int dst_h;
+
+ /* Parsed pixel format */
+ u_int bits_per_pixel;
+ bool is_yuv; /* any YUV mode */
+ bool is_yuv_planar; /* planar YUV mode */
+ uint32_t color_mode; /* DC_WIN_COLOR_DEPTH */
+ uint32_t swap; /* DC_WIN_BYTE_SWAP */
+ uint32_t surface_kind; /* DC_WINBUF_SURFACE_KIND */
+ uint32_t block_height; /* DC_WINBUF_SURFACE_KIND */
+
+ /* Parsed flipping, rotation is not supported for pitched modes */
+ bool flip_x; /* inverted X-axis */
+ bool flip_y; /* inverted Y-axis */
+ bool transpose_xy; /* swap X and Y-axis */
+
+ /* Color planes base addresses and strides */
+ bus_size_t base[3];
+ uint32_t stride[3]; /* stride[2] isn't used by HW */
+};
+
+struct dc_softc {
+ device_t dev;
+ struct resource *mem_res;
+ struct resource *irq_res;
+ void *irq_ih;
+ struct mtx mtx;
+
+ clk_t clk_parent;
+ clk_t clk_dc;
+ hwreset_t hwreset_dc;
+
+ int pitch_align;
+
+ struct tegra_crtc tegra_crtc;
+ struct drm_pending_vblank_event *event;
+ struct drm_gem_object *cursor_gem;
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"nvidia,tegra124-dc", 1},
+ {NULL, 0},
+};
+
+/* Convert standard drm pixel format to tegra windows parameters. */
+static int
+dc_parse_drm_format(struct tegra_fb *fb, struct dc_window *win)
+{
+ struct tegra_bo *bo;
+ uint32_t cm;
+ uint32_t sw;
+ bool is_yuv, is_yuv_planar;
+ int nplanes, i;
+
+ switch (fb->drm_fb.pixel_format) {
+ case DRM_FORMAT_XBGR8888:
+ sw = BYTE_SWAP(NOSWAP);
+ cm = WIN_COLOR_DEPTH_R8G8B8A8;
+ is_yuv = false;
+ is_yuv_planar = false;
+ break;
+
+ case DRM_FORMAT_XRGB8888:
+ sw = BYTE_SWAP(NOSWAP);
+ cm = WIN_COLOR_DEPTH_B8G8R8A8;
+ is_yuv = false;
+ is_yuv_planar = false;
+ break;
+
+ case DRM_FORMAT_RGB565:
+ sw = BYTE_SWAP(NOSWAP);
+ cm = WIN_COLOR_DEPTH_B5G6R5;
+ is_yuv = false;
+ is_yuv_planar = false;
+ break;
+
+ case DRM_FORMAT_UYVY:
+ sw = BYTE_SWAP(NOSWAP);
+ cm = WIN_COLOR_DEPTH_YCbCr422;
+ is_yuv = true;
+ is_yuv_planar = false;
+ break;
+
+ case DRM_FORMAT_YUYV:
+ sw = BYTE_SWAP(SWAP2);
+ cm = WIN_COLOR_DEPTH_YCbCr422;
+ is_yuv = true;
+ is_yuv_planar = false;
+ break;
+
+ case DRM_FORMAT_YUV420:
+ sw = BYTE_SWAP(NOSWAP);
+ cm = WIN_COLOR_DEPTH_YCbCr420P;
+ is_yuv = true;
+ is_yuv_planar = true;
+ break;
+
+ case DRM_FORMAT_YUV422:
+ sw = BYTE_SWAP(NOSWAP);
+ cm = WIN_COLOR_DEPTH_YCbCr422P;
+ is_yuv = true;
+ is_yuv_planar = true;
+ break;
+
+ default:
+ /* Unsupported format */
+ return (-EINVAL);
+ }
+
+ /* Basic check of arguments. */
+ switch (fb->rotation) {
+ case 0:
+ case 180:
+ break;
+
+ case 90: /* Rotation is supported only */
+ case 270: /* for block linear surfaces */
+ if (!fb->block_linear)
+ return (-EINVAL);
+ break;
+
+ default:
+ return (-EINVAL);
+ }
+ /* XXX Add more checks (sizes, scaling...) */
+
+ if (win == NULL)
+ return (0);
+
+ win->surface_kind =
+ fb->block_linear ? SURFACE_KIND_BL_16B2: SURFACE_KIND_PITCH;
+ win->block_height = fb->block_height;
+ switch (fb->rotation) {
+ case 0: /* (0,0,0) */
+ win->transpose_xy = false;
+ win->flip_x = false;
+ win->flip_y = false;
+ break;
+
+ case 90: /* (1,0,1) */
+ win->transpose_xy = true;
+ win->flip_x = false;
+ win->flip_y = true;
+ break;
+
+ case 180: /* (0,1,1) */
+ win->transpose_xy = false;
+ win->flip_x = true;
+ win->flip_y = true;
+ break;
+
+ case 270: /* (1,1,0) */
+ win->transpose_xy = true;
+ win->flip_x = true;
+ win->flip_y = false;
+ break;
+ }
+ win->flip_x ^= fb->flip_x;
+ win->flip_y ^= fb->flip_y;
+
+ win->color_mode = cm;
+ win->swap = sw;
+ win->bits_per_pixel = fb->drm_fb.bits_per_pixel;
+ win->is_yuv = is_yuv;
+ win->is_yuv_planar = is_yuv_planar;
+
+ nplanes = drm_format_num_planes(fb->drm_fb.pixel_format);
+ for (i = 0; i < nplanes; i++) {
+ bo = fb->planes[i];
+ win->base[i] = bo->pbase + fb->drm_fb.offsets[i];
+ win->stride[i] = fb->drm_fb.pitches[i];
+ }
+ return (0);
+}
+
+/*
+ * Scaling functions.
+ *
+ * It's unclear if we want/must program the fractional portion
+ * (aka bias) of init_dda registers, mainly when mirrored axis
+ * modes are used.
+ * For now, we use 1.0 as recommended by TRM.
+ */
+static inline uint32_t
+dc_scaling_init(uint32_t start)
+{
+
+ return (1 << 12);
+}
+
+static inline uint32_t
+dc_scaling_incr(uint32_t src, uint32_t dst, uint32_t maxscale)
+{
+ uint32_t val;
+
+ val = (src - 1) << 12 ; /* 4.12 fixed float */
+ val /= (dst - 1);
+ if (val > (maxscale << 12))
+ val = maxscale << 12;
+ return val;
+}
+
+/* -------------------------------------------------------------------
+ *
+ * HW Access.
+ *
+ */
+
+/*
+ * Setup pixel clock.
+ * Minimal frequency is pixel clock, but output is free to select
+ * any higher.
+ */
+static int
+dc_setup_clk(struct dc_softc *sc, struct drm_crtc *crtc,
+ struct drm_display_mode *mode, uint32_t *div)
+{
+ uint64_t pclk, freq;
+ struct tegra_drm_encoder *output;
+ struct drm_encoder *encoder;
+ long rv;
+
+ pclk = mode->clock * 1000;
+
+ /* Find attached encoder */
+ output = NULL;
+ list_for_each_entry(encoder, &crtc->dev->mode_config.encoder_list,
+ head) {
+ if (encoder->crtc == crtc) {
+ output = container_of(encoder, struct tegra_drm_encoder,
+ encoder);
+ break;
+ }
+ }
+ if (output == NULL)
+ return (-ENODEV);
+
+ if (output->setup_clock == NULL)
+ panic("Output have not setup_clock function.\n");
+ rv = output->setup_clock(output, sc->clk_dc, pclk);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot setup pixel clock: %llu\n",
+ pclk);
+ return (rv);
+ }
+
+ rv = clk_get_freq(sc->clk_dc, &freq);
+ *div = (freq * 2 / pclk) - 2;
+
+ DRM_DEBUG_KMS("frequency: %llu, DC divider: %u\n", freq, *div);
+
+ return 0;
+}
+
+static void
+dc_setup_window(struct dc_softc *sc, unsigned int index, struct dc_window *win)
+{
+ uint32_t h_offset, v_offset, h_size, v_size, bpp;
+ uint32_t h_init_dda, v_init_dda, h_incr_dda, v_incr_dda;
+ uint32_t val;
+
+#ifdef DMR_DEBUG_WINDOW
+ printf("%s window: %d\n", __func__, index);
+ printf(" src: x: %d, y: %d, w: %d, h: %d\n",
+ win->src_x, win->src_y, win->src_w, win->src_h);
+ printf(" dst: x: %d, y: %d, w: %d, h: %d\n",
+ win->dst_x, win->dst_y, win->dst_w, win->dst_h);
+ printf(" bpp: %d, color_mode: %d, swap: %d\n",
+ win->bits_per_pixel, win->color_mode, win->swap);
+#endif
+
+ if (win->is_yuv)
+ bpp = win->is_yuv_planar ? 1 : 2;
+ else
+ bpp = (win->bits_per_pixel + 7) / 8;
+
+ if (!win->transpose_xy) {
+ h_size = win->src_w * bpp;
+ v_size = win->src_h;
+ } else {
+ h_size = win->src_h * bpp;
+ v_size = win->src_w;
+ }
+
+ h_offset = win->src_x * bpp;
+ v_offset = win->src_y;
+ if (win->flip_x) {
+ h_offset += win->src_w * bpp - 1;
+ }
+ if (win->flip_y)
+ v_offset += win->src_h - 1;
+
+ /* Adjust offsets for planar yuv modes */
+ if (win->is_yuv_planar) {
+ h_offset &= ~1;
+ if (win->flip_x )
+ h_offset |= 1;
+ v_offset &= ~1;
+ if (win->flip_y )
+ v_offset |= 1;
+ }
+
+ /* Setup scaling. */
+ if (!win->transpose_xy) {
+ h_init_dda = dc_scaling_init(win->src_x);
+ v_init_dda = dc_scaling_init(win->src_y);
+ h_incr_dda = dc_scaling_incr(win->src_w, win->dst_w, 4);
+ v_incr_dda = dc_scaling_incr(win->src_h, win->dst_h, 15);
+ } else {
+ h_init_dda = dc_scaling_init(win->src_y);
+ v_init_dda = dc_scaling_init(win->src_x);
+ h_incr_dda = dc_scaling_incr(win->src_h, win->dst_h, 4);
+ v_incr_dda = dc_scaling_incr(win->src_w, win->dst_w, 15);
+ }
+#ifdef DMR_DEBUG_WINDOW
+ printf("\n");
+ printf(" bpp: %d, size: h: %d v: %d, offset: h:%d v: %d\n",
+ bpp, h_size, v_size, h_offset, v_offset);
+ printf(" init_dda: h: %d v: %d, incr_dda: h: %d v: %d\n",
+ h_init_dda, v_init_dda, h_incr_dda, v_incr_dda);
+#endif
+
+ LOCK(sc);
+
+ /* Select target window */
+ val = WINDOW_A_SELECT << index;
+ WR4(sc, DC_CMD_DISPLAY_WINDOW_HEADER, val);
+
+ /* Sizes */
+ WR4(sc, DC_WIN_POSITION, WIN_POSITION(win->dst_x, win->dst_y));
+ WR4(sc, DC_WIN_SIZE, WIN_SIZE(win->dst_w, win->dst_h));
+ WR4(sc, DC_WIN_PRESCALED_SIZE, WIN_PRESCALED_SIZE(h_size, v_size));
+
+ /* DDA */
+ WR4(sc, DC_WIN_DDA_INCREMENT,
+ WIN_DDA_INCREMENT(h_incr_dda, v_incr_dda));
+ WR4(sc, DC_WIN_H_INITIAL_DDA, h_init_dda);
+ WR4(sc, DC_WIN_V_INITIAL_DDA, v_init_dda);
+
+ /* Color planes base addresses and strides */
+ WR4(sc, DC_WINBUF_START_ADDR, win->base[0]);
+ if (win->is_yuv_planar) {
+ WR4(sc, DC_WINBUF_START_ADDR_U, win->base[1]);
+ WR4(sc, DC_WINBUF_START_ADDR_V, win->base[2]);
+ WR4(sc, DC_WIN_LINE_STRIDE,
+ win->stride[1] << 16 | win->stride[0]);
+ } else {
+ WR4(sc, DC_WIN_LINE_STRIDE, win->stride[0]);
+ }
+
+ /* Offsets for rotation and axis flip */
+ WR4(sc, DC_WINBUF_ADDR_H_OFFSET, h_offset);
+ WR4(sc, DC_WINBUF_ADDR_V_OFFSET, v_offset);
+
+ /* Color format */
+ WR4(sc, DC_WIN_COLOR_DEPTH, win->color_mode);
+ WR4(sc, DC_WIN_BYTE_SWAP, win->swap);
+
+ /* Tiling */
+ val = win->surface_kind;
+ if (win->surface_kind == SURFACE_KIND_BL_16B2)
+ val |= SURFACE_KIND_BLOCK_HEIGHT(win->block_height);
+ WR4(sc, DC_WINBUF_SURFACE_KIND, val);
+
+ /* Color space coefs for YUV modes */
+ if (win->is_yuv) {
+ WR4(sc, DC_WINC_CSC_YOF, 0x00f0);
+ WR4(sc, DC_WINC_CSC_KYRGB, 0x012a);
+ WR4(sc, DC_WINC_CSC_KUR, 0x0000);
+ WR4(sc, DC_WINC_CSC_KVR, 0x0198);
+ WR4(sc, DC_WINC_CSC_KUG, 0x039b);
+ WR4(sc, DC_WINC_CSC_KVG, 0x032f);
+ WR4(sc, DC_WINC_CSC_KUB, 0x0204);
+ WR4(sc, DC_WINC_CSC_KVB, 0x0000);
+ }
+
+ val = WIN_ENABLE;
+ if (win->is_yuv)
+ val |= CSC_ENABLE;
+ else if (win->bits_per_pixel < 24)
+ val |= COLOR_EXPAND;
+ if (win->flip_y)
+ val |= V_DIRECTION;
+ if (win->flip_x)
+ val |= H_DIRECTION;
+ if (win->transpose_xy)
+ val |= SCAN_COLUMN;
+ WR4(sc, DC_WINC_WIN_OPTIONS, val);
+
+#ifdef DMR_DEBUG_WINDOW
+ /* Set underflow debug mode -> highlight missing pixels. */
+ WR4(sc, DC_WINBUF_UFLOW_CTRL, UFLOW_CTR_ENABLE);
+ WR4(sc, DC_WINBUF_UFLOW_DBG_PIXEL, 0xFFFF0000);
+#endif
+
+ UNLOCK(sc);
+}
+
+/* -------------------------------------------------------------------
+ *
+ * Plane functions.
+ *
+ */
+static int
+dc_plane_update(struct drm_plane *drm_plane, struct drm_crtc *drm_crtc,
+ struct drm_framebuffer *drm_fb,
+ int crtc_x, int crtc_y, unsigned int crtc_w, unsigned int crtc_h,
+ uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h)
+{
+ struct tegra_plane *plane;
+ struct tegra_crtc *crtc;
+ struct tegra_fb *fb;
+ struct dc_softc *sc;
+ struct dc_window win;
+ int rv;
+
+ plane = container_of(drm_plane, struct tegra_plane, drm_plane);
+ fb = container_of(drm_fb, struct tegra_fb, drm_fb);
+ crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc);
+ sc = device_get_softc(crtc->dev);
+
+ memset(&win, 0, sizeof(win));
+ win.src_x = src_x >> 16;
+ win.src_y = src_y >> 16;
+ win.src_w = src_w >> 16;
+ win.src_h = src_h >> 16;
+ win.dst_x = crtc_x;
+ win.dst_y = crtc_y;
+ win.dst_w = crtc_w;
+ win.dst_h = crtc_h;
+
+ rv = dc_parse_drm_format(fb, &win);
+ if (rv != 0) {
+ DRM_WARNING("unsupported pixel format %d\n",
+ fb->drm_fb.pixel_format);
+ return (rv);
+ }
+
+ dc_setup_window(sc, plane->index, &win);
+
+ WR4(sc, DC_CMD_STATE_CONTROL, WIN_A_UPDATE << plane->index);
+ WR4(sc, DC_CMD_STATE_CONTROL, WIN_A_ACT_REQ << plane->index);
+
+ return (0);
+}
+
+static int
+dc_plane_disable(struct drm_plane *drm_plane)
+{
+ struct tegra_plane *plane;
+ struct tegra_crtc *crtc;
+ struct dc_softc *sc;
+ uint32_t val, idx;
+
+ if (drm_plane->crtc == NULL)
+ return (0);
+ plane = container_of(drm_plane, struct tegra_plane, drm_plane);
+ crtc = container_of(drm_plane->crtc, struct tegra_crtc, drm_crtc);
+
+ sc = device_get_softc(crtc->dev);
+ idx = plane->index;
+
+ LOCK(sc);
+
+ WR4(sc, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT << idx);
+
+ val = RD4(sc, DC_WINC_WIN_OPTIONS);
+ val &= ~WIN_ENABLE;
+ WR4(sc, DC_WINC_WIN_OPTIONS, val);
+
+ UNLOCK(sc);
+
+ WR4(sc, DC_CMD_STATE_CONTROL, WIN_A_UPDATE << idx);
+ WR4(sc, DC_CMD_STATE_CONTROL, WIN_A_ACT_REQ << idx);
+
+ return (0);
+}
+
+static void
+dc_plane_destroy(struct drm_plane *plane)
+{
+
+ dc_plane_disable(plane);
+ drm_plane_cleanup(plane);
+ free(plane, DRM_MEM_KMS);
+}
+
+static const struct drm_plane_funcs dc_plane_funcs = {
+ .update_plane = dc_plane_update,
+ .disable_plane = dc_plane_disable,
+ .destroy = dc_plane_destroy,
+};
+
+/* -------------------------------------------------------------------
+ *
+ * CRTC helper functions.
+ *
+ */
+static void
+dc_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+ /* Empty function */
+}
+
+static bool
+dc_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted)
+{
+
+ return (true);
+}
+
+static int
+dc_set_base(struct dc_softc *sc, int x, int y, struct tegra_fb *fb)
+{
+ struct dc_window win;
+ int rv;
+
+ memset(&win, 0, sizeof(win));
+ win.src_x = x;
+ win.src_y = y;
+ win.src_w = fb->drm_fb.width;
+ win.src_h = fb->drm_fb.height;
+ win.dst_x = x;
+ win.dst_y = y;
+ win.dst_w = fb->drm_fb.width;
+ win.dst_h = fb->drm_fb.height;
+
+ rv = dc_parse_drm_format(fb, &win);
+ if (rv != 0) {
+ DRM_WARNING("unsupported pixel format %d\n",
+ fb->drm_fb.pixel_format);
+ return (rv);
+ }
+ dc_setup_window(sc, 0, &win);
+
+ return (0);
+}
+
+static int
+dc_crtc_mode_set(struct drm_crtc *drm_crtc, struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted, int x, int y,
+ struct drm_framebuffer *old_fb)
+{
+ struct dc_softc *sc;
+ struct tegra_crtc *crtc;
+ struct tegra_fb *fb;
+ struct dc_window win;
+ uint32_t div, h_ref_to_sync, v_ref_to_sync;
+ int rv;
+
+ crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc);
+ sc = device_get_softc(crtc->dev);
+ fb = container_of(drm_crtc->fb, struct tegra_fb, drm_fb);
+
+ h_ref_to_sync = 1;
+ v_ref_to_sync = 1;
+ /* Setup timing */
+ rv = dc_setup_clk(sc, drm_crtc, mode, &div);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot set pixel clock\n");
+ return (rv);
+ }
+
+ /* Timing */
+ WR4(sc, DC_DISP_DISP_TIMING_OPTIONS, 0);
+
+ WR4(sc, DC_DISP_REF_TO_SYNC,
+ (v_ref_to_sync << 16) |
+ h_ref_to_sync);
+
+ WR4(sc, DC_DISP_SYNC_WIDTH,
+ ((mode->vsync_end - mode->vsync_start) << 16) |
+ ((mode->hsync_end - mode->hsync_start) << 0));
+
+ WR4(sc, DC_DISP_BACK_PORCH,
+ ((mode->vtotal - mode->vsync_end) << 16) |
+ ((mode->htotal - mode->hsync_end) << 0));
+
+ WR4(sc, DC_DISP_FRONT_PORCH,
+ ((mode->vsync_start - mode->vdisplay) << 16) |
+ ((mode->hsync_start - mode->hdisplay) << 0));
+
+ WR4(sc, DC_DISP_DISP_ACTIVE,
+ (mode->vdisplay << 16) | mode->hdisplay);
+
+ WR4(sc, DC_DISP_DISP_INTERFACE_CONTROL, DISP_DATA_FORMAT(DF1P1C));
+
+ WR4(sc,DC_DISP_DISP_CLOCK_CONTROL,
+ SHIFT_CLK_DIVIDER(div) | PIXEL_CLK_DIVIDER(PCD1));
+
+ memset(&win, 0, sizeof(win));
+ win.src_x = x;
+ win.src_y = y;
+ win.src_w = mode->hdisplay;
+ win.src_h = mode->vdisplay;
+ win.dst_x = x;
+ win.dst_y = y;
+ win.dst_w = mode->hdisplay;
+ win.dst_h = mode->vdisplay;
+
+ rv = dc_parse_drm_format(fb, &win);
+ if (rv != 0) {
+ DRM_WARNING("unsupported pixel format %d\n",
+ drm_crtc->fb->pixel_format);
+ return (rv);
+ }
+
+ dc_setup_window(sc, 0, &win);
+
+ return (0);
+
+}
+
+static int
+dc_crtc_mode_set_base(struct drm_crtc *drm_crtc, int x, int y,
+ struct drm_framebuffer *old_fb)
+{
+ struct dc_softc *sc;
+ struct tegra_crtc *crtc;
+ struct tegra_fb *fb;
+ int rv;
+
+ crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc);
+ fb = container_of(drm_crtc->fb, struct tegra_fb, drm_fb);
+ sc = device_get_softc(crtc->dev);
+
+ rv = dc_set_base(sc, x, y, fb);
+
+ /* Commit */
+ WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE);
+ WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ);
+ return (rv);
+}
+
+static void
+dc_crtc_prepare(struct drm_crtc *drm_crtc)
+{
+
+ struct dc_softc *sc;
+ struct tegra_crtc *crtc;
+ uint32_t val;
+
+ crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc);
+ sc = device_get_softc(crtc->dev);
+
+ WR4(sc, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL, SYNCPT_CNTRL_NO_STALL);
+ /* XXX allocate syncpoint from host1x */
+ WR4(sc, DC_CMD_CONT_SYNCPT_VSYNC, SYNCPT_VSYNC_ENABLE |
+ (sc->tegra_crtc.nvidia_head == 0 ? SYNCPT_VBLANK0: SYNCPT_VBLANK1));
+
+ WR4(sc, DC_CMD_DISPLAY_POWER_CONTROL,
+ PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
+ PW4_ENABLE | PM0_ENABLE | PM1_ENABLE);
+
+ val = RD4(sc, DC_CMD_DISPLAY_COMMAND);
+ val |= DISPLAY_CTRL_MODE(CTRL_MODE_C_DISPLAY);
+ WR4(sc, DC_CMD_DISPLAY_COMMAND, val);
+
+ WR4(sc, DC_CMD_INT_MASK,
+ WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
+ WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT);
+
+ WR4(sc, DC_CMD_INT_ENABLE,
+ VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
+ WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT);
+}
+
+static void
+dc_crtc_commit(struct drm_crtc *drm_crtc)
+{
+ struct dc_softc *sc;
+ struct tegra_crtc *crtc;
+ uint32_t val;
+
+ crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc);
+ sc = device_get_softc(crtc->dev);
+
+ WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE);
+
+ val = RD4(sc, DC_CMD_INT_MASK);
+ val |= FRAME_END_INT;
+ WR4(sc, DC_CMD_INT_MASK, val);
+
+ val = RD4(sc, DC_CMD_INT_ENABLE);
+ val |= FRAME_END_INT;
+ WR4(sc, DC_CMD_INT_ENABLE, val);
+
+ WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ);
+}
+
+static void
+dc_crtc_load_lut(struct drm_crtc *crtc)
+{
+
+ /* empty function */
+}
+
+static const struct drm_crtc_helper_funcs dc_crtc_helper_funcs = {
+ .dpms = dc_crtc_dpms,
+ .mode_fixup = dc_crtc_mode_fixup,
+ .mode_set = dc_crtc_mode_set,
+ .mode_set_base = dc_crtc_mode_set_base,
+ .prepare = dc_crtc_prepare,
+ .commit = dc_crtc_commit,
+ .load_lut = dc_crtc_load_lut,
+};
+
+static int
+drm_crtc_index(struct drm_crtc *crtc)
+{
+ int idx;
+ struct drm_crtc *tmp;
+
+ idx = 0;
+ list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) {
+ if (tmp == crtc)
+ return (idx);
+ idx++;
+ }
+ panic("Cannot find CRTC");
+}
+
+/* -------------------------------------------------------------------
+ *
+ * Exported functions (mainly vsync related).
+ *
+ * XXX revisit this -> convert to bus methods?
+ */
+int
+tegra_dc_get_pipe(struct drm_crtc *drm_crtc)
+{
+ struct tegra_crtc *crtc;
+
+ crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc);
+ return (crtc->nvidia_head);
+}
+
+void
+tegra_dc_enable_vblank(struct drm_crtc *drm_crtc)
+{
+ struct dc_softc *sc;
+ struct tegra_crtc *crtc;
+ uint32_t val;
+
+ crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc);
+ sc = device_get_softc(crtc->dev);
+
+ LOCK(sc);
+ val = RD4(sc, DC_CMD_INT_MASK);
+ val |= VBLANK_INT;
+ WR4(sc, DC_CMD_INT_MASK, val);
+ UNLOCK(sc);
+}
+
+void
+tegra_dc_disable_vblank(struct drm_crtc *drm_crtc)
+{
+ struct dc_softc *sc;
+ struct tegra_crtc *crtc;
+ uint32_t val;
+
+ crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc);
+ sc = device_get_softc(crtc->dev);
+
+ LOCK(sc);
+ val = RD4(sc, DC_CMD_INT_MASK);
+ val &= ~VBLANK_INT;
+ WR4(sc, DC_CMD_INT_MASK, val);
+ UNLOCK(sc);
+}
+
+static void
+dc_finish_page_flip(struct dc_softc *sc)
+{
+ struct drm_crtc *drm_crtc;
+ struct drm_device *drm;
+ struct tegra_fb *fb;
+ struct tegra_bo *bo;
+ uint32_t base;
+ int idx;
+
+ drm_crtc = &sc->tegra_crtc.drm_crtc;
+ drm = drm_crtc->dev;
+ fb = container_of(drm_crtc->fb, struct tegra_fb, drm_fb);
+
+ mtx_lock(&drm->event_lock);
+
+ if (sc->event == NULL) {
+ mtx_unlock(&drm->event_lock);
+ return;
+ }
+
+ LOCK(sc);
+ /* Read active copy of WINBUF_START_ADDR */
+ WR4(sc, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT);
+ WR4(sc, DC_CMD_STATE_ACCESS, READ_MUX);
+ base = RD4(sc, DC_WINBUF_START_ADDR);
+ WR4(sc, DC_CMD_STATE_ACCESS, 0);
+ UNLOCK(sc);
+
+ /* Is already active */
+ bo = tegra_fb_get_plane(fb, 0);
+ if (base == (bo->pbase + fb->drm_fb.offsets[0])) {
+ idx = drm_crtc_index(drm_crtc);
+ drm_send_vblank_event(drm, idx, sc->event);
+ drm_vblank_put(drm, idx);
+ sc->event = NULL;
+ }
+
+ mtx_unlock(&drm->event_lock);
+}
+
+void
+tegra_dc_cancel_page_flip(struct drm_crtc *drm_crtc, struct drm_file *file)
+{
+ struct dc_softc *sc;
+ struct tegra_crtc *crtc;
+ struct drm_device *drm;
+
+ crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc);
+ sc = device_get_softc(crtc->dev);
+ drm = drm_crtc->dev;
+ mtx_lock(&drm->event_lock);
+
+ if ((sc->event != NULL) && (sc->event->base.file_priv == file)) {
+ sc->event->base.destroy(&sc->event->base);
+ drm_vblank_put(drm, drm_crtc_index(drm_crtc));
+ sc->event = NULL;
+ }
+ mtx_unlock(&drm->event_lock);
+}
+
+/* -------------------------------------------------------------------
+ *
+ * CRTC functions.
+ *
+ */
+static int
+dc_page_flip(struct drm_crtc *drm_crtc, struct drm_framebuffer *drm_fb,
+ struct drm_pending_vblank_event *event)
+{
+ struct dc_softc *sc;
+ struct tegra_crtc *crtc;
+ struct tegra_fb *fb;
+ struct drm_device *drm;
+
+ crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc);
+ sc = device_get_softc(crtc->dev);
+ fb = container_of(drm_crtc->fb, struct tegra_fb, drm_fb);
+ drm = drm_crtc->dev;
+
+ if (sc->event != NULL)
+ return (-EBUSY);
+
+ if (event != NULL) {
+ event->pipe = sc->tegra_crtc.nvidia_head;
+ sc->event = event;
+ drm_vblank_get(drm, event->pipe);
+ }
+
+ dc_set_base(sc, drm_crtc->x, drm_crtc->y, fb);
+ drm_crtc->fb = drm_fb;
+
+ /* Commit */
+ WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE);
+
+ return (0);
+}
+
+static int
+dc_cursor_set(struct drm_crtc *drm_crtc, struct drm_file *file,
+ uint32_t handle, uint32_t width, uint32_t height)
+{
+
+ struct dc_softc *sc;
+ struct tegra_crtc *crtc;
+ struct drm_gem_object *gem;
+ struct tegra_bo *bo;
+ int i;
+ uint32_t val, *src, *dst;
+
+ crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc);
+ sc = device_get_softc(crtc->dev);
+
+ if (width != height)
+ return (-EINVAL);
+
+ switch (width) {
+ case 32:
+ val = CURSOR_SIZE(C32x32);
+ break;
+ case 64:
+ val = CURSOR_SIZE(C64x64);
+ break;
+ case 128:
+ val = CURSOR_SIZE(C128x128);
+ break;
+ case 256:
+ val = CURSOR_SIZE(C256x256);
+ break;
+ default:
+ return (-EINVAL);
+ }
+
+ bo = NULL;
+ gem = NULL;
+ if (handle != 0) {
+ gem = drm_gem_object_lookup(drm_crtc->dev, file, handle);
+ if (gem == NULL)
+ return (-ENOENT);
+ bo = container_of(gem, struct tegra_bo, gem_obj);
+ }
+
+ if (sc->cursor_gem != NULL) {
+ drm_gem_object_unreference(sc->cursor_gem);
+ }
+ sc->cursor_gem = gem;
+
+ if (bo != NULL) {
+ /*
+ * Copy cursor into cache and convert it from ARGB to RGBA.
+ * XXXX - this is broken by design - client can write to BO at
+ * any time. We can dedicate other window for cursor or switch
+ * to sw cursor in worst case.
+ */
+ src = (uint32_t *)bo->vbase;
+ dst = (uint32_t *)crtc->cursor_vbase;
+ for (i = 0; i < width * height; i++)
+ dst[i] = (src[i] << 8) | (src[i] >> 24);
+
+ val |= CURSOR_CLIP(CC_DISPLAY);
+ val |= CURSOR_START_ADDR(crtc->cursor_pbase);
+ WR4(sc, DC_DISP_CURSOR_START_ADDR, val);
+
+ val = RD4(sc, DC_DISP_BLEND_CURSOR_CONTROL);
+ val &= ~CURSOR_DST_BLEND_FACTOR_SELECT(~0);
+ val &= ~CURSOR_SRC_BLEND_FACTOR_SELECT(~0);
+ val |= CURSOR_MODE_SELECT;
+ val |= CURSOR_DST_BLEND_FACTOR_SELECT(DST_NEG_K1_TIMES_SRC);
+ val |= CURSOR_SRC_BLEND_FACTOR_SELECT(SRC_BLEND_K1_TIMES_SRC);
+ val |= CURSOR_ALPHA(~0);
+ WR4(sc, DC_DISP_BLEND_CURSOR_CONTROL, val);
+
+ val = RD4(sc, DC_DISP_DISP_WIN_OPTIONS);
+ val |= CURSOR_ENABLE;
+ WR4(sc, DC_DISP_DISP_WIN_OPTIONS, val);
+ } else {
+ val = RD4(sc, DC_DISP_DISP_WIN_OPTIONS);
+ val &= ~CURSOR_ENABLE;
+ WR4(sc, DC_DISP_DISP_WIN_OPTIONS, val);
+ }
+
+ /* XXX This fixes cursor underflow issues, but why ? */
+ WR4(sc, DC_DISP_CURSOR_UNDERFLOW_CTRL, CURSOR_UFLOW_CYA);
+
+ WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_UPDATE | CURSOR_UPDATE );
+ WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | CURSOR_ACT_REQ);
+ return (0);
+}
+
+static int
+dc_cursor_move(struct drm_crtc *drm_crtc, int x, int y)
+{
+ struct dc_softc *sc;
+ struct tegra_crtc *crtc;
+
+ crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc);
+ sc = device_get_softc(crtc->dev);
+ WR4(sc, DC_DISP_CURSOR_POSITION, CURSOR_POSITION(x, y));
+
+ WR4(sc, DC_CMD_STATE_CONTROL, CURSOR_UPDATE);
+ WR4(sc, DC_CMD_STATE_CONTROL, CURSOR_ACT_REQ);
+
+ return (0);
+}
+
+static void
+dc_destroy(struct drm_crtc *crtc)
+{
+
+ drm_crtc_cleanup(crtc);
+ memset(crtc, 0, sizeof(*crtc));
+}
+
+static const struct drm_crtc_funcs dc_crtc_funcs = {
+ .page_flip = dc_page_flip,
+ .cursor_set = dc_cursor_set,
+ .cursor_move = dc_cursor_move,
+ .set_config = drm_crtc_helper_set_config,
+ .destroy = dc_destroy,
+};
+
+/* -------------------------------------------------------------------
+ *
+ * Bus and infrastructure.
+ *
+ */
+static int
+dc_init_planes(struct dc_softc *sc, struct tegra_drm *drm)
+{
+ int i, rv;
+ struct tegra_plane *plane;
+
+ rv = 0;
+ for (i = 0; i < DC_MAX_PLANES; i++) {
+ plane = malloc(sizeof(*plane), DRM_MEM_KMS, M_WAITOK | M_ZERO);
+ plane->index = i + 1;
+ rv = drm_plane_init(&drm->drm_dev, &plane->drm_plane,
+ 1 << sc->tegra_crtc.nvidia_head, &dc_plane_funcs,
+ dc_plane_formats, nitems(dc_plane_formats), false);
+ if (rv != 0) {
+ free(plane, DRM_MEM_KMS);
+ return (rv);
+ }
+ }
+ return 0;
+}
+
+static void
+dc_display_enable(device_t dev, bool enable)
+{
+ struct dc_softc *sc;
+ uint32_t val;
+
+ sc = device_get_softc(dev);
+
+ /* Set display mode */
+ val = enable ? CTRL_MODE_C_DISPLAY: CTRL_MODE_STOP;
+ WR4(sc, DC_CMD_DISPLAY_COMMAND, DISPLAY_CTRL_MODE(val));
+
+ /* and commit it*/
+ WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_UPDATE);
+ WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ);
+}
+
+static void
+dc_hdmi_enable(device_t dev, bool enable)
+{
+ struct dc_softc *sc;
+ uint32_t val;
+
+ sc = device_get_softc(dev);
+
+ val = RD4(sc, DC_DISP_DISP_WIN_OPTIONS);
+ if (enable)
+ val |= HDMI_ENABLE;
+ else
+ val &= ~HDMI_ENABLE;
+ WR4(sc, DC_DISP_DISP_WIN_OPTIONS, val);
+
+}
+
+static void
+dc_setup_timing(device_t dev, int h_pulse_start)
+{
+ struct dc_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ /* Setup display timing */
+ WR4(sc, DC_DISP_DISP_TIMING_OPTIONS, VSYNC_H_POSITION(1));
+ WR4(sc, DC_DISP_DISP_COLOR_CONTROL,
+ DITHER_CONTROL(DITHER_DISABLE) | BASE_COLOR_SIZE(SIZE_BASE888));
+
+ WR4(sc, DC_DISP_DISP_SIGNAL_OPTIONS0, H_PULSE2_ENABLE);
+ WR4(sc, DC_DISP_H_PULSE2_CONTROL,
+ PULSE_CONTROL_QUAL(QUAL_VACTIVE) | PULSE_CONTROL_LAST(LAST_END_A));
+
+ WR4(sc, DC_DISP_H_PULSE2_POSITION_A,
+ PULSE_START(h_pulse_start) | PULSE_END(h_pulse_start + 8));
+}
+
+static void
+dc_intr(void *arg)
+{
+ struct dc_softc *sc;
+ uint32_t status;
+
+ sc = arg;
+
+ /* Confirm interrupt */
+ status = RD4(sc, DC_CMD_INT_STATUS);
+ WR4(sc, DC_CMD_INT_STATUS, status);
+ if (status & VBLANK_INT) {
+ drm_handle_vblank(sc->tegra_crtc.drm_crtc.dev,
+ sc->tegra_crtc.nvidia_head);
+ dc_finish_page_flip(sc);
+ }
+}
+
+static int
+dc_init_client(device_t dev, device_t host1x, struct tegra_drm *drm)
+{
+ struct dc_softc *sc;
+ int rv;
+
+ sc = device_get_softc(dev);
+
+ if (drm->pitch_align < sc->pitch_align)
+ drm->pitch_align = sc->pitch_align;
+
+ drm_crtc_init(&drm->drm_dev, &sc->tegra_crtc.drm_crtc, &dc_crtc_funcs);
+ drm_mode_crtc_set_gamma_size(&sc->tegra_crtc.drm_crtc, 256);
+ drm_crtc_helper_add(&sc->tegra_crtc.drm_crtc, &dc_crtc_helper_funcs);
+
+ rv = dc_init_planes(sc, drm);
+ if (rv!= 0){
+ device_printf(dev, "Cannot init planes\n");
+ return (rv);
+ }
+
+ WR4(sc, DC_CMD_INT_TYPE,
+ WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
+ WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT);
+
+ WR4(sc, DC_CMD_INT_POLARITY,
+ WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
+ WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT);
+
+ WR4(sc, DC_CMD_INT_ENABLE, 0);
+ WR4(sc, DC_CMD_INT_MASK, 0);
+
+ rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+ NULL, dc_intr, sc, &sc->irq_ih);
+ if (rv != 0) {
+ device_printf(dev, "Cannot register interrupt handler\n");
+ return (rv);
+ }
+
+ /* allocate memory for cursor cache */
+ sc->tegra_crtc.cursor_vbase = kmem_alloc_contig(256 * 256 * 4,
+ M_WAITOK | M_ZERO, 0, -1UL, PAGE_SIZE, 0,
+ VM_MEMATTR_WRITE_COMBINING);
+ sc->tegra_crtc.cursor_pbase = vtophys(sc->tegra_crtc.cursor_vbase);
+ return (0);
+}
+
+static int
+dc_exit_client(device_t dev, device_t host1x, struct tegra_drm *drm)
+{
+ struct dc_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (sc->irq_ih != NULL)
+ bus_teardown_intr(dev, sc->irq_res, sc->irq_ih);
+ sc->irq_ih = NULL;
+
+ return (0);
+}
+
+static int
+get_fdt_resources(struct dc_softc *sc, phandle_t node)
+{
+ int rv;
+
+ rv = hwreset_get_by_ofw_name(sc->dev, 0, "dc", &sc->hwreset_dc);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'dc' reset\n");
+ return (rv);
+ }
+ rv = clk_get_by_ofw_name(sc->dev, 0, "parent", &sc->clk_parent);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'parent' clock\n");
+ return (rv);
+ }
+ rv = clk_get_by_ofw_name(sc->dev, 0, "dc", &sc->clk_dc);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'dc' clock\n");
+ return (rv);
+ }
+
+ rv = OF_getencprop(node, "nvidia,head", &sc->tegra_crtc.nvidia_head,
+ sizeof(sc->tegra_crtc.nvidia_head));
+ if (rv <= 0) {
+ device_printf(sc->dev,
+ "Cannot get 'nvidia,head' property\n");
+ return (rv);
+ }
+ return (0);
+}
+
+static int
+enable_fdt_resources(struct dc_softc *sc)
+{
+ int id, rv;
+
+ rv = clk_set_parent_by_clk(sc->clk_dc, sc->clk_parent);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot set parent for 'dc' clock\n");
+ return (rv);
+ }
+
+ id = (sc->tegra_crtc.nvidia_head == 0) ?
+ TEGRA_POWERGATE_DIS: TEGRA_POWERGATE_DISB;
+ rv = tegra_powergate_sequence_power_up(id, sc->clk_dc, sc->hwreset_dc);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot enable 'DIS' powergate\n");
+ return (rv);
+ }
+
+ return (0);
+}
+
+static int
+dc_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Tegra Display Controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+dc_attach(device_t dev)
+{
+ struct dc_softc *sc;
+ phandle_t node;
+ int rid, rv;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ sc->tegra_crtc.dev = dev;
+
+ node = ofw_bus_get_node(sc->dev);
+ LOCK_INIT(sc);
+
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "Cannot allocate memory resources\n");
+ goto fail;
+ }
+
+ rid = 0;
+ sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
+ if (sc->irq_res == NULL) {
+ device_printf(dev, "Cannot allocate IRQ resources\n");
+ goto fail;
+ }
+
+ rv = get_fdt_resources(sc, node);
+ if (rv != 0) {
+ device_printf(dev, "Cannot parse FDT resources\n");
+ goto fail;
+ }
+ rv = enable_fdt_resources(sc);
+ if (rv != 0) {
+ device_printf(dev, "Cannot enable FDT resources\n");
+ goto fail;
+ }
+
+ /*
+ * Tegra124
+ * - 64 for RGB modes
+ * - 128 for YUV planar modes
+ * - 256 for block linear modes
+ */
+ sc->pitch_align = 256;
+
+ rv = TEGRA_DRM_REGISTER_CLIENT(device_get_parent(sc->dev), sc->dev);
+ if (rv != 0) {
+ device_printf(dev, "Cannot register DRM device\n");
+ goto fail;
+ }
+
+ return (bus_generic_attach(dev));
+
+fail:
+ TEGRA_DRM_DEREGISTER_CLIENT(device_get_parent(sc->dev), sc->dev);
+ if (sc->irq_ih != NULL)
+ bus_teardown_intr(dev, sc->irq_res, sc->irq_ih);
+ if (sc->clk_parent != NULL)
+ clk_release(sc->clk_parent);
+ if (sc->clk_dc != NULL)
+ clk_release(sc->clk_dc);
+ if (sc->hwreset_dc != NULL)
+ hwreset_release(sc->hwreset_dc);
+ if (sc->irq_res != NULL)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+ if (sc->mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+ LOCK_DESTROY(sc);
+
+ return (ENXIO);
+}
+
+static int
+dc_detach(device_t dev)
+{
+ struct dc_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ TEGRA_DRM_DEREGISTER_CLIENT(device_get_parent(sc->dev), sc->dev);
+
+ if (sc->irq_ih != NULL)
+ bus_teardown_intr(dev, sc->irq_res, sc->irq_ih);
+ if (sc->clk_parent != NULL)
+ clk_release(sc->clk_parent);
+ if (sc->clk_dc != NULL)
+ clk_release(sc->clk_dc);
+ if (sc->hwreset_dc != NULL)
+ hwreset_release(sc->hwreset_dc);
+ if (sc->irq_res != NULL)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+ if (sc->mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+ LOCK_DESTROY(sc);
+
+ return (bus_generic_detach(dev));
+}
+
+static device_method_t tegra_dc_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, dc_probe),
+ DEVMETHOD(device_attach, dc_attach),
+ DEVMETHOD(device_detach, dc_detach),
+
+ /* tegra drm interface */
+ DEVMETHOD(tegra_drm_init_client, dc_init_client),
+ DEVMETHOD(tegra_drm_exit_client, dc_exit_client),
+
+ /* tegra dc interface */
+ DEVMETHOD(tegra_dc_display_enable, dc_display_enable),
+ DEVMETHOD(tegra_dc_hdmi_enable, dc_hdmi_enable),
+ DEVMETHOD(tegra_dc_setup_timing, dc_setup_timing),
+
+ DEVMETHOD_END
+};
+
+static devclass_t tegra_dc_devclass;
+DEFINE_CLASS_0(tegra_dc, tegra_dc_driver, tegra_dc_methods,
+ sizeof(struct dc_softc));
+DRIVER_MODULE(tegra_dc, host1x, tegra_dc_driver, tegra_dc_devclass, NULL, NULL);
diff --git a/sys/arm/nvidia/drm2/tegra_dc_if.m b/sys/arm/nvidia/drm2/tegra_dc_if.m
new file mode 100644
index 000000000000..5b3bd1437767
--- /dev/null
+++ b/sys/arm/nvidia/drm2/tegra_dc_if.m
@@ -0,0 +1,57 @@
+#-
+# Copyright (c) 2015 Michal Meloun
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+
+#include <machine/bus.h>
+
+INTERFACE tegra_dc;
+
+
+METHOD void write_4{
+ device_t dev;
+ bus_size_t offset;
+ uint32_t val;
+};
+METHOD uint32_t read_4{
+ device_t dev;
+ bus_size_t offset;
+};
+
+METHOD void display_enable{
+ device_t dev;
+ bool enable;
+};
+
+METHOD void hdmi_enable{
+ device_t dev;
+ bool enable;
+};
+
+METHOD void setup_timing{
+ device_t dev;
+ int h_pulse_start;
+};
diff --git a/sys/arm/nvidia/drm2/tegra_dc_reg.h b/sys/arm/nvidia/drm2/tegra_dc_reg.h
new file mode 100644
index 000000000000..377f3e210681
--- /dev/null
+++ b/sys/arm/nvidia/drm2/tegra_dc_reg.h
@@ -0,0 +1,398 @@
+/*-
+ * Copyright 1992-2015 Michal Meloun
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _TEGRA_DC_REG_H_
+#define _TEGRA_DC_REG_H_
+
+/*
+ * !!! WARNING !!!
+ * Tegra manual uses registers index (and not register addreses).
+ * We follow the TRM notation and index is converted to offset in
+ * WR4 / RD4 macros
+ */
+
+/* --------------------------- DC CMD -------------------------------------- */
+#define DC_CMD_GENERAL_INCR_SYNCPT 0x000
+#define DC_CMD_GENERAL_INCR_SYNCPT_CNTRL 0x001
+#define SYNCPT_CNTRL_NO_STALL (1 << 8)
+#define SYNCPT_CNTRL_SOFT_RESET (1 << 0)
+
+#define DC_CMD_GENERAL_INCR_SYNCPT_ERROR 0x002
+#define DC_CMD_WIN_A_INCR_SYNCPT 0x008
+#define DC_CMD_WIN_A_INCR_SYNCPT_CNTRL 0x009
+#define DC_CMD_WIN_A_INCR_SYNCPT_ERROR 0x00a
+#define DC_CMD_WIN_B_INCR_SYNCPT 0x010
+#define DC_CMD_WIN_B_INCR_SYNCPT_CNTRL 0x011
+#define DC_CMD_WIN_B_INCR_SYNCPT_ERROR 0x012
+#define DC_CMD_WIN_C_INCR_SYNCPT 0x018
+#define DC_CMD_WIN_C_INCR_SYNCPT_CNTRL 0x019
+#define DC_CMD_WIN_C_INCR_SYNCPT_ERROR 0x01a
+#define DC_CMD_CONT_SYNCPT_VSYNC 0x028
+#define SYNCPT_VSYNC_ENABLE (1 << 8)
+
+#define DC_CMD_CTXSW 0x030
+#define DC_CMD_DISPLAY_COMMAND_OPTION0 0x031
+#define DC_CMD_DISPLAY_COMMAND 0x032
+#define DISPLAY_CTRL_MODE(x) ((x) << 5)
+#define CTRL_MODE_STOP 0
+#define CTRL_MODE_C_DISPLAY 1
+#define CTRL_MODE_NC_DISPLAY 2
+
+#define DC_CMD_SIGNAL_RAISE 0x033
+#define DC_CMD_DISPLAY_POWER_CONTROL 0x036
+#define PM1_ENABLE (1 << 18)
+#define PM0_ENABLE (1 << 16)
+#define PW4_ENABLE (1 << 8)
+#define PW3_ENABLE (1 << 6)
+#define PW2_ENABLE (1 << 4)
+#define PW1_ENABLE (1 << 2)
+#define PW0_ENABLE (1 << 0)
+
+#define DC_CMD_INT_STATUS 0x037
+#define DC_CMD_INT_MASK 0x038
+#define DC_CMD_INT_ENABLE 0x039
+#define DC_CMD_INT_TYPE 0x03a
+#define DC_CMD_INT_POLARITY 0x03b
+#define WIN_T_UF_INT (1 << 25)
+#define WIN_D_UF_INT (1 << 24)
+#define HC_UF_INT (1 << 23)
+#define CMU_LUT_CONFLICT_INT (1 << 22)
+#define WIN_C_OF_INT (1 << 16)
+#define WIN_B_OF_INT (1 << 15)
+#define WIN_A_OF_INT (1 << 14)
+#define SSF_INT (1 << 13)
+#define MSF_INT (1 << 12)
+#define WIN_C_UF_INT (1 << 10)
+#define WIN_B_UF_INT (1 << 9)
+#define WIN_A_UF_INT (1 << 8)
+#define SPI_BUSY_INT (1 << 6)
+#define V_PULSE2_INT (1 << 5)
+#define V_PULSE3_INT (1 << 4)
+#define HBLANK_INT (1 << 3)
+#define VBLANK_INT (1 << 2)
+#define FRAME_END_INT (1 << 1)
+
+#define DC_CMD_STATE_ACCESS 0x040
+#define WRITE_MUX (1 << 2)
+#define READ_MUX (1 << 0)
+
+#define DC_CMD_STATE_CONTROL 0x041
+#define NC_HOST_TRIG (1 << 24)
+#define CURSOR_UPDATE (1 << 15)
+#define WIN_C_UPDATE (1 << 11)
+#define WIN_B_UPDATE (1 << 10)
+#define WIN_A_UPDATE (1 << 9)
+#define WIN_UPDATE(x) (1 << (9 + (x)))
+#define GENERAL_UPDATE (1 << 8)
+#define CURSOR_ACT_REQ (1 << 7)
+#define WIN_D_ACT_REQ (1 << 4)
+#define WIN_C_ACT_REQ (1 << 3)
+#define WIN_B_ACT_REQ (1 << 2)
+#define WIN_A_ACT_REQ (1 << 1)
+#define WIN_ACT_REQ(x) (1 << (1 + (x)))
+#define GENERAL_ACT_REQ (1 << 0)
+
+#define DC_CMD_DISPLAY_WINDOW_HEADER 0x042
+#define WINDOW_D_SELECT (1 << 7)
+#define WINDOW_C_SELECT (1 << 6)
+#define WINDOW_B_SELECT (1 << 5)
+#define WINDOW_A_SELECT (1 << 4)
+#define WINDOW_SELECT(x) (1 << (4 + (x)))
+
+#define DC_CMD_REG_ACT_CONTROL 0x043
+#define DC_CMD_WIN_D_INCR_SYNCPT 0x04c
+#define DC_CMD_WIN_D_INCR_SYNCPT_CNTRL 0x04d
+#define DC_CMD_WIN_D_INCR_SYNCPT_ERROR 0x04e
+
+/* ---------------------------- DC COM ------------------------------------- */
+
+/* --------------------------- DC DISP ------------------------------------- */
+
+#define DC_DISP_DISP_SIGNAL_OPTIONS0 0x400
+#define M1_ENABLE (1 << 26)
+#define M0_ENABLE (1 << 24)
+#define V_PULSE2_ENABLE (1 << 18)
+#define V_PULSE1_ENABLE (1 << 16)
+#define V_PULSE0_ENABLE (1 << 14)
+#define H_PULSE2_ENABLE (1 << 12)
+#define H_PULSE1_ENABLE (1 << 10)
+#define H_PULSE0_ENABLE (1 << 8)
+
+#define DC_DISP_DISP_SIGNAL_OPTIONS1 0x401
+
+#define DC_DISP_DISP_WIN_OPTIONS 0x402
+#define HDMI_ENABLE (1 << 30)
+#define DSI_ENABLE (1 << 29)
+#define SOR1_TIMING_CYA (1 << 27)
+#define SOR1_ENABLE (1 << 26)
+#define SOR_ENABLE (1 << 25)
+#define CURSOR_ENABLE (1 << 16)
+
+#define DC_DISP_DISP_TIMING_OPTIONS 0x405
+#define VSYNC_H_POSITION(x) (((x) & 0xfff) << 0)
+
+#define DC_DISP_REF_TO_SYNC 0x406
+#define DC_DISP_SYNC_WIDTH 0x407
+#define DC_DISP_BACK_PORCH 0x408
+#define DC_DISP_DISP_ACTIVE 0x409
+#define DC_DISP_FRONT_PORCH 0x40a
+#define DC_DISP_H_PULSE0_CONTROL 0x40b
+#define DC_DISP_H_PULSE0_POSITION_A 0x40c
+#define DC_DISP_H_PULSE0_POSITION_B 0x40d
+#define DC_DISP_H_PULSE0_POSITION_C 0x40e
+#define DC_DISP_H_PULSE0_POSITION_D 0x40f
+#define DC_DISP_H_PULSE1_CONTROL 0x410
+#define DC_DISP_H_PULSE1_POSITION_A 0x411
+#define DC_DISP_H_PULSE1_POSITION_B 0x412
+#define DC_DISP_H_PULSE1_POSITION_C 0x413
+#define DC_DISP_H_PULSE1_POSITION_D 0x414
+#define DC_DISP_H_PULSE2_CONTROL 0x415
+#define DC_DISP_H_PULSE2_POSITION_A 0x416
+#define DC_DISP_H_PULSE2_POSITION_B 0x417
+#define DC_DISP_H_PULSE2_POSITION_C 0x418
+#define DC_DISP_H_PULSE2_POSITION_D 0x419
+#define DC_DISP_V_PULSE0_CONTROL 0x41a
+#define DC_DISP_V_PULSE0_POSITION_A 0x41b
+#define DC_DISP_V_PULSE0_POSITION_B 0x41c
+#define DC_DISP_V_PULSE0_POSITION_C 0x41d
+#define DC_DISP_V_PULSE1_CONTROL 0x41e
+#define DC_DISP_V_PULSE1_POSITION_A 0x41f
+#define DC_DISP_V_PULSE1_POSITION_B 0x420
+#define DC_DISP_V_PULSE1_POSITION_C 0x421
+#define DC_DISP_V_PULSE2_CONTROL 0x422
+#define DC_DISP_V_PULSE2_POSITION_A 0x423
+#define DC_DISP_V_PULSE3_CONTROL 0x424
+#define PULSE_CONTROL_LAST(x) (((x) & 0x7f) << 8)
+#define LAST_START_A 0
+#define LAST_END_A 1
+#define LAST_START_B 2
+#define LAST_END_B 3
+#define LAST_START_C 4
+#define LAST_END_C 5
+#define LAST_START_D 6
+#define LAST_END_D 7
+#define PULSE_CONTROL_QUAL(x) (((x) & 0x3) << 8)
+#define QUAL_ALWAYS 0
+#define QUAL_VACTIVE 2
+#define QUAL_VACTIVE1 3
+#define PULSE_POLARITY (1 << 4)
+#define PULSE_MODE (1 << 3)
+
+#define DC_DISP_V_PULSE3_POSITION_A 0x425
+#define PULSE_END(x) (((x) & 0xfff) << 16)
+#define PULSE_START(x) (((x) & 0xfff) << 0)
+
+#define DC_DISP_DISP_CLOCK_CONTROL 0x42e
+#define PIXEL_CLK_DIVIDER(x) (((x) & 0xf) << 8)
+#define PCD1 0
+#define PCD1H 1
+#define PCD2 2
+#define PCD3 3
+#define PCD4 4
+#define PCD6 5
+#define PCD8 6
+#define PCD9 7
+#define PCD12 8
+#define PCD16 9
+#define PCD18 10
+#define PCD24 11
+#define PCD13 12
+#define SHIFT_CLK_DIVIDER(x) ((x) & 0xff)
+
+#define DC_DISP_DISP_INTERFACE_CONTROL 0x42f
+#define DISP_ORDER_BLUE_RED ( 1 << 9)
+#define DISP_ALIGNMENT_LSB ( 1 << 8)
+#define DISP_DATA_FORMAT(x) (((x) & 0xf) << 8)
+#define DF1P1C 0
+#define DF1P2C24B 1
+#define DF1P2C18B 2
+#define DF1P2C16B 3
+#define DF1S 4
+#define DF2S 5
+#define DF3S 6
+#define DFSPI 7
+#define DF1P3C24B 8
+#define DF2P1C18B 9
+#define DFDUAL1P1C18B 10
+
+#define DC_DISP_DISP_COLOR_CONTROL 0x430
+#define NON_BASE_COLOR (1 << 18)
+#define BLANK_COLOR (1 << 17)
+#define DISP_COLOR_SWAP (1 << 16)
+#define ORD_DITHER_ROTATION(x) (((x) & 0x3) << 12)
+#define DITHER_CONTROL(x) (((x) & 0x3) << 8)
+#define DITHER_DISABLE 0
+#define DITHER_ORDERED 2
+#define DITHER_TEMPORAL 3
+#define BASE_COLOR_SIZE(x) (((x) & 0xF) << 0)
+#define SIZE_BASE666 0
+#define SIZE_BASE111 1
+#define SIZE_BASE222 2
+#define SIZE_BASE333 3
+#define SIZE_BASE444 4
+#define SIZE_BASE555 5
+#define SIZE_BASE565 6
+#define SIZE_BASE332 7
+#define SIZE_BASE888 8
+
+#define DC_DISP_CURSOR_START_ADDR 0x43e
+#define CURSOR_CLIP(x) (((x) & 0x3) << 28)
+#define CC_DISPLAY 0
+#define CC_WA 1
+#define CC_WB 2
+#define CC_WC 3
+#define CURSOR_SIZE(x) (((x) & 0x3) << 24)
+#define C32x32 0
+#define C64x64 1
+#define C128x128 2
+#define C256x256 3
+#define CURSOR_START_ADDR(x) (((x) >> 10) & 0x3FFFFF)
+
+#define DC_DISP_CURSOR_POSITION 0x440
+#define CURSOR_POSITION(h, v) ((((h) & 0x3fff) << 0) | \
+ (((v) & 0x3fff) << 16))
+#define DC_DISP_CURSOR_UNDERFLOW_CTRL 0x4eb
+#define DC_DISP_BLEND_CURSOR_CONTROL 0x4f1
+#define CURSOR_MODE_SELECT (1 << 24)
+#define CURSOR_DST_BLEND_FACTOR_SELECT(x) (((x) & 0x3) << 16)
+#define DST_BLEND_ZERO 0
+#define DST_BLEND_K1 1
+#define DST_NEG_K1_TIMES_SRC 2
+#define CURSOR_SRC_BLEND_FACTOR_SELECT(x) (((x) & 0x3) << 8)
+#define SRC_BLEND_K1 0
+#define SRC_BLEND_K1_TIMES_SRC 1
+#define CURSOR_ALPHA(x) (((x) & 0xFF) << 0)
+
+#define DC_DISP_CURSOR_UFLOW_DBG_PIXEL 0x4f3
+#define CURSOR_UFLOW_CYA (1 << 7)
+#define CURSOR_UFLOW_CTRL_DBG_MODE (1 << 0)
+/* --------------------------- DC WIN ------------------------------------- */
+
+#define DC_WINC_COLOR_PALETTE 0x500
+#define DC_WINC_CSC_YOF 0x611
+#define DC_WINC_CSC_KYRGB 0x612
+#define DC_WINC_CSC_KUR 0x613
+#define DC_WINC_CSC_KVR 0x614
+#define DC_WINC_CSC_KUG 0x615
+#define DC_WINC_CSC_KVG 0x616
+#define DC_WINC_CSC_KUB 0x617
+#define DC_WINC_CSC_KVB 0x618
+
+#define DC_WINC_WIN_OPTIONS 0x700
+#define H_FILTER_MODE (1U << 31)
+#define WIN_ENABLE (1 << 30)
+#define INTERLACE_ENABLE (1 << 23)
+#define YUV_RANGE_EXPAND (1 << 22)
+#define DV_ENABLE (1 << 20)
+#define CSC_ENABLE (1 << 18)
+#define CP_ENABLE (1 << 16)
+#define V_FILTER_UV_ALIGN (1 << 14)
+#define V_FILTER_OPTIMIZE (1 << 12)
+#define V_FILTER_ENABLE (1 << 10)
+#define H_FILTER_ENABLE (1 << 8)
+#define COLOR_EXPAND (1 << 6)
+#define SCAN_COLUMN (1 << 4)
+#define V_DIRECTION (1 << 2)
+#define H_DIRECTION (1 << 0)
+
+#define DC_WIN_BYTE_SWAP 0x701
+#define BYTE_SWAP(x) (((x) & 0x7) << 0)
+#define NOSWAP 0
+#define SWAP2 1
+#define SWAP4 2
+#define SWAP4HW 3
+#define SWAP02 4
+#define SWAPLEFT 5
+
+#define DC_WIN_COLOR_DEPTH 0x703
+#define WIN_COLOR_DEPTH_P8 3
+#define WIN_COLOR_DEPTH_B4G4R4A4 4
+#define WIN_COLOR_DEPTH_B5G5R5A 5
+#define WIN_COLOR_DEPTH_B5G6R5 6
+#define WIN_COLOR_DEPTH_AB5G5R5 7
+#define WIN_COLOR_DEPTH_B8G8R8A8 12
+#define WIN_COLOR_DEPTH_R8G8B8A8 13
+#define WIN_COLOR_DEPTH_YCbCr422 16
+#define WIN_COLOR_DEPTH_YUV422 17
+#define WIN_COLOR_DEPTH_YCbCr420P 18
+#define WIN_COLOR_DEPTH_YUV420P 19
+#define WIN_COLOR_DEPTH_YCbCr422P 20
+#define WIN_COLOR_DEPTH_YUV422P 21
+#define WIN_COLOR_DEPTH_YCbCr422R 22
+#define WIN_COLOR_DEPTH_YUV422R 23
+#define WIN_COLOR_DEPTH_YCbCr422RA 24
+#define WIN_COLOR_DEPTH_YUV422RA 25
+
+#define DC_WIN_POSITION 0x704
+#define WIN_POSITION(h, v) ((((h) & 0x1fff) << 0) | \
+ (((v) & 0x1fff) << 16))
+
+#define DC_WIN_SIZE 0x705
+#define WIN_SIZE(h, v) ((((h) & 0x1fff) << 0) | \
+ (((v) & 0x1fff) << 16))
+
+#define DC_WIN_PRESCALED_SIZE 0x706
+#define WIN_PRESCALED_SIZE(h, v) ((((h) & 0x7fff) << 0) | \
+ (((v) & 0x1fff) << 16))
+
+#define DC_WIN_H_INITIAL_DDA 0x707
+#define DC_WIN_V_INITIAL_DDA 0x708
+#define DC_WIN_DDA_INCREMENT 0x709
+#define WIN_DDA_INCREMENT(h, v) ((((h) & 0xffff) << 0) | \
+ (((v) & 0xffff) << 16))
+#define DC_WIN_LINE_STRIDE 0x70a
+
+/* -------------------------- DC WINBUF ------------------------------------ */
+
+#define DC_WINBUF_START_ADDR 0x800
+#define DC_WINBUF_START_ADDR_NS 0x801
+#define DC_WINBUF_START_ADDR_U 0x802
+#define DC_WINBUF_START_ADDR_U_NS 0x803
+#define DC_WINBUF_START_ADDR_V 0x804
+#define DC_WINBUF_START_ADDR_V_NS 0x805
+#define DC_WINBUF_ADDR_H_OFFSET 0x806
+#define DC_WINBUF_ADDR_H_OFFSET_NS 0x807
+#define DC_WINBUF_ADDR_V_OFFSET 0x808
+#define DC_WINBUF_ADDR_V_OFFSET_NS 0x809
+#define DC_WINBUF_UFLOW_STATUS 0x80a
+#define DC_WINBUF_SURFACE_KIND 0x80b
+#define SURFACE_KIND_BLOCK_HEIGHT(x) (((x) & 0x7) << 4)
+#define SURFACE_KIND_PITCH 0
+#define SURFACE_KIND_TILED 1
+#define SURFACE_KIND_BL_16B2 2
+#define DC_WINBUF_SURFACE_WEIGHT 0x80c
+#define DC_WINBUF_START_ADDR_HI 0x80d
+#define DC_WINBUF_START_ADDR_HI_NS 0x80e
+#define DC_WINBUF_START_ADDR_U_HI 0x80f
+#define DC_WINBUF_START_ADDR_U_HI_NS 0x810
+#define DC_WINBUF_START_ADDR_V_HI 0x811
+#define DC_WINBUF_START_ADDR_V_HI_NS 0x812
+#define DC_WINBUF_UFLOW_CTRL 0x824
+#define UFLOW_CTR_ENABLE (1 << 0)
+#define DC_WINBUF_UFLOW_DBG_PIXEL 0x825
+
+#endif /* _TEGRA_DC_REG_H_ */
diff --git a/sys/arm/nvidia/drm2/tegra_drm.h b/sys/arm/nvidia/drm2/tegra_drm.h
new file mode 100644
index 000000000000..ada4f4434e65
--- /dev/null
+++ b/sys/arm/nvidia/drm2/tegra_drm.h
@@ -0,0 +1,124 @@
+/*-
+ * Copyright 1992-2015 Michal Meloun
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _TEGRA_DRM_H_
+#define _TEGRA_DRM_H_
+
+#include <dev/gpio/gpiobusvar.h>
+
+struct tegra_bo {
+ struct drm_gem_object gem_obj;
+ /* mapped memory buffer */
+ vm_paddr_t pbase;
+ vm_offset_t vbase;
+ size_t npages;
+ vm_page_t *m;
+ vm_object_t cdev_pager;
+};
+
+struct tegra_plane {
+ struct drm_plane drm_plane;
+ int index; /* Window index */
+};
+
+struct tegra_fb {
+ struct drm_framebuffer drm_fb;
+ struct drm_fb_helper fb_helper;
+ struct tegra_bo **planes; /* Attached planes */
+ int nplanes;
+
+ /* Surface and display geometry */
+ bool block_linear; /* Surface_kind */
+ uint32_t block_height;
+ int rotation; /* In degrees */
+ bool flip_x; /* Inverted X-axis */
+ bool flip_y; /* Inverted Y-axis */
+};
+
+struct tegra_crtc {
+ struct drm_crtc drm_crtc;
+ device_t dev;
+ int nvidia_head;
+ vm_paddr_t cursor_pbase; /* Cursor buffer */
+ vm_offset_t cursor_vbase;
+};
+
+struct tegra_drm_encoder {
+ device_t dev;
+
+ void *panel; /* XXX For LVDS panel */
+ device_t ddc;
+ struct edid *edid;
+
+ gpio_pin_t gpio_hpd;
+
+ struct drm_encoder encoder;
+ struct drm_connector connector;
+ int (*setup_clock)(struct tegra_drm_encoder *output,
+ clk_t clk, uint64_t pclk);
+};
+
+struct tegra_drm {
+ struct drm_device drm_dev;
+ struct tegra_fb *fb; /* Prime framebuffer */
+ int pitch_align;
+};
+
+/* tegra_drm_subr.c */
+int tegra_drm_encoder_attach(struct tegra_drm_encoder *output, phandle_t node);
+int tegra_drm_encoder_init(struct tegra_drm_encoder *output,
+ struct tegra_drm *drm);
+int tegra_drm_encoder_exit(struct tegra_drm_encoder *output,
+ struct tegra_drm *drm);
+enum drm_connector_status tegra_drm_connector_detect(
+ struct drm_connector *connector, bool force);
+int tegra_drm_connector_get_modes(struct drm_connector *connector);
+struct drm_encoder *tegra_drm_connector_best_encoder(
+ struct drm_connector *connector);
+
+/* tegra_dc.c */
+void tegra_dc_cancel_page_flip(struct drm_crtc *drm_crtc,
+ struct drm_file *file);
+void tegra_dc_enable_vblank(struct drm_crtc *drm_crtc);
+void tegra_dc_disable_vblank(struct drm_crtc *drm_crtc);
+int tegra_dc_get_pipe(struct drm_crtc *drm_crtc);
+
+/* tegra_fb.c */
+struct fb_info *tegra_drm_fb_getinfo(struct drm_device *drm);
+struct tegra_bo *tegra_fb_get_plane(struct tegra_fb *fb, int idx);
+int tegra_drm_fb_create(struct drm_device *drm, struct drm_file *file,
+ struct drm_mode_fb_cmd2 *cmd, struct drm_framebuffer **fb_res);
+int tegra_drm_fb_init(struct drm_device *drm);
+void tegra_drm_fb_destroy(struct drm_device *drm);
+
+/* tegra_bo.c */
+struct tegra_bo;
+int tegra_bo_create(struct drm_device *drm, size_t size,
+ struct tegra_bo **res_bo);
+void tegra_bo_driver_register(struct drm_driver *drm_drv);
+
+#endif /* _TEGRA_DRM_H_ */
diff --git a/sys/arm/nvidia/drm2/tegra_drm_if.m b/sys/arm/nvidia/drm2/tegra_drm_if.m
new file mode 100644
index 000000000000..822db79ce9fa
--- /dev/null
+++ b/sys/arm/nvidia/drm2/tegra_drm_if.m
@@ -0,0 +1,68 @@
+#-
+# Copyright (c) 2015 Michal Meloun
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+
+
+INTERFACE tegra_drm;
+HEADER {
+ struct tegra_drm;
+};
+
+
+/**
+ * Register client to host1x
+ */
+METHOD int register_client{
+ device_t host1x;
+ device_t client;
+};
+
+/**
+ * Deregister client to host1x
+ */
+METHOD int deregister_client{
+ device_t host1x;
+ device_t client;
+};
+
+/**
+ * Call client init method
+ */
+METHOD int init_client{
+ device_t client;
+ device_t host1x;
+ struct tegra_drm *drm;
+};
+
+/**
+ * Call client exit method
+ */
+METHOD int exit_client{
+ device_t client;
+ device_t host1x;
+ struct tegra_drm *drm;
+};
diff --git a/sys/arm/nvidia/drm2/tegra_drm_subr.c b/sys/arm/nvidia/drm2/tegra_drm_subr.c
new file mode 100644
index 000000000000..813da9c7b18c
--- /dev/null
+++ b/sys/arm/nvidia/drm2/tegra_drm_subr.c
@@ -0,0 +1,177 @@
+/*-
+ * Copyright (c) 2015 Michal Meloun
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+
+#include <machine/bus.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/drm2/drmP.h>
+#include <dev/drm2/drm_crtc.h>
+#include <dev/drm2/drm_crtc_helper.h>
+#include <dev/drm2/drm_edid.h>
+#include <dev/drm2/drm_fb_helper.h>
+#include <dev/gpio/gpiobusvar.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/nvidia/drm2/tegra_drm.h>
+
+#include <dt-bindings/gpio/gpio.h>
+
+int
+tegra_drm_connector_get_modes(struct drm_connector *connector)
+{
+ struct tegra_drm_encoder *output;
+ struct edid *edid = NULL;
+ int rv;
+
+ output = container_of(connector, struct tegra_drm_encoder,
+ connector);
+
+ /* Panel is first */
+ if (output->panel != NULL) {
+ /* XXX panel parsing */
+ return (0);
+ }
+
+ /* static EDID is second*/
+ edid = output->edid;
+
+ /* EDID from monitor is last */
+ if (edid == NULL)
+ edid = drm_get_edid(connector, output->ddc);
+
+ if (edid == NULL)
+ return (0);
+
+ /* Process EDID */
+ drm_mode_connector_update_edid_property(connector, edid);
+ rv = drm_add_edid_modes(connector, edid);
+ drm_edid_to_eld(connector, edid);
+ return (rv);
+}
+
+struct drm_encoder *
+tegra_drm_connector_best_encoder(struct drm_connector *connector)
+{
+ struct tegra_drm_encoder *output;
+
+ output = container_of(connector, struct tegra_drm_encoder,
+ connector);
+
+ return &(output->encoder);
+}
+
+enum drm_connector_status
+tegra_drm_connector_detect(struct drm_connector *connector, bool force)
+{
+ struct tegra_drm_encoder *output;
+ bool active;
+ int rv;
+
+ output = container_of(connector, struct tegra_drm_encoder,
+ connector);
+ if (output->gpio_hpd == NULL) {
+ return ((output->panel != NULL) ?
+ connector_status_connected:
+ connector_status_disconnected);
+ }
+
+ rv = gpio_pin_is_active(output->gpio_hpd, &active);
+ if (rv != 0) {
+ device_printf(output->dev, " GPIO read failed: %d\n", rv);
+ return (connector_status_unknown);
+ }
+
+ return (active ?
+ connector_status_connected : connector_status_disconnected);
+}
+
+int
+tegra_drm_encoder_attach(struct tegra_drm_encoder *output, phandle_t node)
+{
+ int rv;
+ phandle_t ddc;
+
+ /* XXX parse output panel here */
+
+ rv = OF_getencprop_alloc(node, "nvidia,edid",
+ (void **)&output->edid);
+
+ /* EDID exist but have invalid size */
+ if ((rv >= 0) && (rv != sizeof(struct edid))) {
+ device_printf(output->dev,
+ "Malformed \"nvidia,edid\" property\n");
+ if (output->edid != NULL)
+ free(output->edid, M_OFWPROP);
+ return (ENXIO);
+ }
+
+ gpio_pin_get_by_ofw_property(output->dev, node, "nvidia,hpd-gpio",
+ &output->gpio_hpd);
+ ddc = 0;
+ OF_getencprop(node, "nvidia,ddc-i2c-bus", &ddc, sizeof(ddc));
+ if (ddc > 0)
+ output->ddc = OF_device_from_xref(ddc);
+ if ((output->edid == NULL) && (output->ddc == NULL))
+ return (ENXIO);
+
+ if (output->gpio_hpd != NULL) {
+ output->connector.polled =
+// DRM_CONNECTOR_POLL_HPD;
+ DRM_CONNECTOR_POLL_DISCONNECT |
+ DRM_CONNECTOR_POLL_CONNECT;
+ }
+
+ return (0);
+}
+
+int tegra_drm_encoder_init(struct tegra_drm_encoder *output,
+ struct tegra_drm *drm)
+{
+
+ if (output->panel) {
+ /* attach panel */
+ }
+ return (0);
+}
+
+int tegra_drm_encoder_exit(struct tegra_drm_encoder *output,
+ struct tegra_drm *drm)
+{
+
+ if (output->panel) {
+ /* detach panel */
+ }
+ return (0);
+}
diff --git a/sys/arm/nvidia/drm2/tegra_fb.c b/sys/arm/nvidia/drm2/tegra_fb.c
new file mode 100644
index 000000000000..ce5fbfd4cc38
--- /dev/null
+++ b/sys/arm/nvidia/drm2/tegra_fb.c
@@ -0,0 +1,338 @@
+/*-
+ * Copyright (c) 2016 Michal Meloun
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+
+#include <machine/bus.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/drm2/drmP.h>
+#include <dev/drm2/drm_crtc_helper.h>
+#include <dev/drm2/drm_fb_helper.h>
+
+#include <arm/nvidia/drm2/tegra_drm.h>
+
+static void
+fb_destroy(struct drm_framebuffer *drm_fb)
+{
+ struct tegra_fb *fb;
+ struct tegra_bo *bo;
+ unsigned int i;
+
+ fb = container_of(drm_fb, struct tegra_fb, drm_fb);
+ for (i = 0; i < fb->nplanes; i++) {
+ bo = fb->planes[i];
+ if (bo != NULL)
+ drm_gem_object_unreference_unlocked(&bo->gem_obj);
+ }
+
+ drm_framebuffer_cleanup(drm_fb);
+ free(fb->planes, DRM_MEM_DRIVER);
+}
+
+static int
+fb_create_handle(struct drm_framebuffer *drm_fb, struct drm_file *file,
+ unsigned int *handle)
+{
+ struct tegra_fb *fb;
+ int rv;
+
+ fb = container_of(drm_fb, struct tegra_fb, drm_fb);
+ rv = drm_gem_handle_create(file, &fb->planes[0]->gem_obj, handle);
+ return (rv);
+}
+
+/* XXX Probably not needed */
+static int
+fb_dirty(struct drm_framebuffer *fb, struct drm_file *file_priv,
+unsigned flags, unsigned color, struct drm_clip_rect *clips, unsigned num_clips)
+{
+
+ return (0);
+}
+
+static const struct drm_framebuffer_funcs fb_funcs = {
+ .destroy = fb_destroy,
+ .create_handle = fb_create_handle,
+ .dirty = fb_dirty,
+};
+
+static int
+fb_alloc(struct drm_device *drm, struct drm_mode_fb_cmd2 *mode_cmd,
+ struct tegra_bo **planes, int num_planes, struct tegra_fb **res_fb)
+{
+ struct tegra_fb *fb;
+ int i;
+ int rv;
+
+ fb = malloc(sizeof(*fb), DRM_MEM_DRIVER, M_WAITOK | M_ZERO);
+ fb->planes = malloc(num_planes * sizeof(*fb->planes), DRM_MEM_DRIVER,
+ M_WAITOK | M_ZERO);
+ fb->nplanes = num_planes;
+
+ drm_helper_mode_fill_fb_struct(&fb->drm_fb, mode_cmd);
+ for (i = 0; i < fb->nplanes; i++)
+ fb->planes[i] = planes[i];
+ rv = drm_framebuffer_init(drm, &fb->drm_fb, &fb_funcs);
+ if (rv < 0) {
+ device_printf(drm->dev,
+ "Cannot initialize frame buffer %d\n", rv);
+ free(fb->planes, DRM_MEM_DRIVER);
+ return (rv);
+ }
+ *res_fb = fb;
+ return (0);
+}
+
+static int
+tegra_fb_probe(struct drm_fb_helper *helper,
+ struct drm_fb_helper_surface_size *sizes)
+{
+ u_int bpp, size;
+ struct tegra_drm *drm;
+ struct tegra_fb *fb;
+ struct fb_info *info;
+ struct tegra_bo *bo;
+ struct drm_mode_fb_cmd2 mode_cmd;
+ struct drm_device *drm_dev;
+ int rv;
+
+ if (helper->fb != NULL)
+ return (0);
+
+ DRM_DEBUG_KMS("surface: %d x %d (bpp: %d)\n", sizes->surface_width,
+ sizes->surface_height, sizes->surface_bpp);
+
+ drm_dev = helper->dev;
+ fb = container_of(helper, struct tegra_fb, fb_helper);
+ drm = container_of(drm_dev, struct tegra_drm, drm_dev);
+ bpp = (sizes->surface_bpp + 7) / 8;
+
+ /* Create mode_cmd */
+ memset(&mode_cmd, 0, sizeof(mode_cmd));
+ mode_cmd.width = sizes->surface_width;
+ mode_cmd.height = sizes->surface_height;
+ mode_cmd.pitches[0] = roundup(sizes->surface_width * bpp,
+ drm->pitch_align);
+ mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
+ sizes->surface_depth);
+ size = mode_cmd.pitches[0] * mode_cmd.height;
+
+ DRM_LOCK(drm_dev);
+ rv = tegra_bo_create(drm_dev, size, &bo);
+ DRM_UNLOCK(drm_dev);
+ if (rv != 0)
+ return (rv);
+
+ info = framebuffer_alloc();
+ if (info == NULL) {
+ device_printf(drm_dev->dev,
+ "Cannot allocate DRM framebuffer info.\n");
+ rv = -ENOMEM;
+ goto err_object;
+ }
+
+ rv = fb_alloc(drm_dev, &mode_cmd, &bo, 1, &fb);
+ if (rv != 0) {
+ device_printf(drm_dev->dev,
+ "Cannot allocate DRM framebuffer.\n");
+ goto err_fb;
+ }
+ helper->fb = &fb->drm_fb;
+ helper->fbdev = info;
+
+ /* Fill FB info */
+ info->fb_vbase = bo->vbase;
+ info->fb_pbase = bo->pbase;
+ info->fb_size = size;
+ info->fb_bpp = sizes->surface_bpp;
+ drm_fb_helper_fill_fix(info, fb->drm_fb.pitches[0], fb->drm_fb.depth);
+ drm_fb_helper_fill_var(info, helper, fb->drm_fb.width,
+ fb->drm_fb.height);
+
+ DRM_DEBUG_KMS("allocated %dx%d (s %dbits) fb size: %d, bo %p\n",
+ fb->drm_fb.width, fb->drm_fb.height, fb->drm_fb.depth,
+ size, bo);
+ return (1);
+err_fb:
+ drm_gem_object_unreference_unlocked(&bo->gem_obj);
+ framebuffer_release(info);
+err_object:
+ drm_gem_object_release(&bo->gem_obj);
+ return (rv);
+}
+
+static struct drm_fb_helper_funcs fb_helper_funcs = {
+ .fb_probe = tegra_fb_probe,
+};
+
+/*
+ * Exported functions
+ */
+struct fb_info *
+tegra_drm_fb_getinfo(struct drm_device *drm_dev)
+{
+ struct tegra_fb *fb;
+ struct tegra_drm *drm;
+
+ drm = container_of(drm_dev, struct tegra_drm, drm_dev);
+ fb = drm->fb;
+ if (fb == NULL)
+ return (NULL);
+ return (fb->fb_helper.fbdev);
+}
+
+struct tegra_bo *
+tegra_fb_get_plane(struct tegra_fb *fb, int idx)
+{
+
+ if (idx >= drm_format_num_planes(fb->drm_fb.pixel_format))
+ return (NULL);
+ if (idx >= fb->nplanes)
+ return (NULL);
+ return (fb->planes[idx]);
+}
+
+int
+tegra_drm_fb_init(struct drm_device *drm_dev)
+{
+ struct tegra_fb *fb;
+ struct tegra_drm *drm;
+ int rv;
+
+ drm = drm_dev->dev_private;
+ drm = container_of(drm_dev, struct tegra_drm, drm_dev);
+ fb = malloc(sizeof(*fb), DRM_MEM_DRIVER, M_WAITOK | M_ZERO);
+ drm->fb = fb;
+
+ fb->fb_helper.funcs = &fb_helper_funcs;
+ rv = drm_fb_helper_init(drm_dev, &fb->fb_helper,
+ drm_dev->mode_config.num_crtc, drm_dev->mode_config.num_connector);
+ if (rv != 0) {
+ device_printf(drm_dev->dev,
+ "Cannot initialize frame buffer %d\n", rv);
+ return (rv);
+ }
+
+ rv = drm_fb_helper_single_add_all_connectors(&fb->fb_helper);
+ if (rv != 0) {
+ device_printf(drm_dev->dev, "Cannot add all connectors: %d\n",
+ rv);
+ goto err_fini;
+ }
+
+ rv = drm_fb_helper_initial_config(&fb->fb_helper, 32);
+ if (rv != 0) {
+ device_printf(drm_dev->dev,
+ "Cannot set initial config: %d\n", rv);
+ goto err_fini;
+ }
+ /* XXXX Setup initial mode for FB */
+ /* drm_fb_helper_set_par(fb->fb_helper.fbdev); */
+ return 0;
+
+err_fini:
+ drm_fb_helper_fini(&fb->fb_helper);
+ return (rv);
+}
+
+int
+tegra_drm_fb_create(struct drm_device *drm, struct drm_file *file,
+ struct drm_mode_fb_cmd2 *cmd, struct drm_framebuffer **fb_res)
+{
+ int hsub, vsub, i;
+ int width, height, size, bpp;
+ struct tegra_bo *planes[4];
+ struct drm_gem_object *gem_obj;
+ struct tegra_fb *fb;
+ int rv, nplanes;
+
+ hsub = drm_format_horz_chroma_subsampling(cmd->pixel_format);
+ vsub = drm_format_vert_chroma_subsampling(cmd->pixel_format);
+
+ nplanes = drm_format_num_planes(cmd->pixel_format);
+ for (i = 0; i < nplanes; i++) {
+ width = cmd->width;
+ height = cmd->height;
+ if (i != 0) {
+ width /= hsub;
+ height /= vsub;
+ }
+ gem_obj = drm_gem_object_lookup(drm, file, cmd->handles[i]);
+ if (gem_obj == NULL) {
+ rv = -ENXIO;
+ goto fail;
+ }
+
+ bpp = drm_format_plane_cpp(cmd->pixel_format, i);
+ size = (height - 1) * cmd->pitches[i] +
+ width * bpp + cmd->offsets[i];
+ if (gem_obj->size < size) {
+ rv = -EINVAL;
+ goto fail;
+ }
+ planes[i] = container_of(gem_obj, struct tegra_bo, gem_obj);
+ }
+
+ rv = fb_alloc(drm, cmd, planes, nplanes, &fb);
+ if (rv != 0)
+ goto fail;
+
+ *fb_res = &fb->drm_fb;
+ return (0);
+
+fail:
+ while (i--)
+ drm_gem_object_unreference_unlocked(&planes[i]->gem_obj);
+ return (rv);
+}
+
+void
+tegra_drm_fb_destroy(struct drm_device *drm_dev)
+{
+ struct fb_info *info;
+ struct tegra_fb *fb;
+ struct tegra_drm *drm;
+
+ drm = container_of(drm_dev, struct tegra_drm, drm_dev);
+ fb = drm->fb;
+ if (fb == NULL)
+ return;
+ info = fb->fb_helper.fbdev;
+ drm_framebuffer_remove(&fb->drm_fb);
+ framebuffer_release(info);
+ drm_fb_helper_fini(&fb->fb_helper);
+ drm_framebuffer_cleanup(&fb->drm_fb);
+ free(fb, DRM_MEM_DRIVER);
+ drm->fb = NULL;
+}
diff --git a/sys/arm/nvidia/drm2/tegra_hdmi.c b/sys/arm/nvidia/drm2/tegra_hdmi.c
new file mode 100644
index 000000000000..0c9315ec31a0
--- /dev/null
+++ b/sys/arm/nvidia/drm2/tegra_hdmi.c
@@ -0,0 +1,1320 @@
+/*-
+ * Copyright (c) 2015 Michal Meloun
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/gpio.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/extres/regulator/regulator.h>
+#include <dev/drm2/drmP.h>
+#include <dev/drm2/drm_crtc.h>
+#include <dev/drm2/drm_crtc_helper.h>
+#include <dev/drm2/drm_fb_helper.h>
+#include <dev/gpio/gpiobusvar.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/nvidia/drm2/tegra_drm.h>
+#include <arm/nvidia/drm2/tegra_hdmi_reg.h>
+#include <arm/nvidia/drm2/tegra_dc_reg.h>
+#include <arm/nvidia/drm2/hdmi.h>
+
+#include "tegra_dc_if.h"
+#include "tegra_drm_if.h"
+
+#define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, 4 * (_r), (_v))
+#define RD4(_sc, _r) bus_read_4((_sc)->mem_res, 4 * (_r))
+
+/* HDA stream format verb. */
+#define AC_FMT_CHAN_GET(x) (((x) >> 0) & 0xf)
+#define AC_FMT_CHAN_BITS_GET(x) (((x) >> 4) & 0x7)
+#define AC_FMT_DIV_GET(x) (((x) >> 8) & 0x7)
+#define AC_FMT_MUL_GET(x) (((x) >> 11) & 0x7)
+#define AC_FMT_BASE_44K (1 << 14)
+#define AC_FMT_TYPE_NON_PCM (1 << 15)
+
+#define HDMI_REKEY_DEFAULT 56
+#define HDMI_ELD_BUFFER_SIZE 96
+
+#define HDMI_DC_CLOCK_MULTIPIER 2
+
+struct audio_reg {
+ uint32_t audio_clk;
+ bus_size_t acr_reg;
+ bus_size_t nval_reg;
+ bus_size_t aval_reg;
+};
+
+static const struct audio_reg audio_regs[] =
+{
+ {
+ .audio_clk = 32000,
+ .acr_reg = HDMI_NV_PDISP_HDMI_ACR_0320_SUBPACK_LOW,
+ .nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_0320,
+ .aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0320,
+ },
+ {
+ .audio_clk = 44100,
+ .acr_reg = HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW,
+ .nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_0441,
+ .aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0441,
+ },
+ {
+ .audio_clk = 88200,
+ .acr_reg = HDMI_NV_PDISP_HDMI_ACR_0882_SUBPACK_LOW,
+ .nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_0882,
+ .aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0882,
+ },
+ {
+ .audio_clk = 176400,
+ .acr_reg = HDMI_NV_PDISP_HDMI_ACR_1764_SUBPACK_LOW,
+ .nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_1764,
+ .aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_1764,
+ },
+ {
+ .audio_clk = 48000,
+ .acr_reg = HDMI_NV_PDISP_HDMI_ACR_0480_SUBPACK_LOW,
+ .nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_0480,
+ .aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0480,
+ },
+ {
+ .audio_clk = 96000,
+ .acr_reg = HDMI_NV_PDISP_HDMI_ACR_0960_SUBPACK_LOW,
+ .nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_0960,
+ .aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0960,
+ },
+ {
+ .audio_clk = 192000,
+ .acr_reg = HDMI_NV_PDISP_HDMI_ACR_1920_SUBPACK_LOW,
+ .nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_1920,
+ .aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_1920,
+ },
+};
+
+struct tmds_config {
+ uint32_t pclk;
+ uint32_t pll0;
+ uint32_t pll1;
+ uint32_t drive_c;
+ uint32_t pe_c;
+ uint32_t peak_c;
+ uint32_t pad_ctls;
+};
+
+static const struct tmds_config tegra124_tmds_config[] =
+{
+ { /* 480p/576p / 25.2MHz/27MHz */
+ .pclk = 27000000,
+ .pll0 = 0x01003010,
+ .pll1 = 0x00301B00,
+ .drive_c = 0x1F1F1F1F,
+ .pe_c = 0x00000000,
+ .peak_c = 0x03030303,
+ .pad_ctls = 0x800034BB,
+ },
+ { /* 720p/1080i / 74.25MHz */
+ .pclk = 74250000,
+ .pll0 = 0x01003110,
+ .pll1 = 0x00301500,
+ .drive_c = 0x2C2C2C2C,
+ .pe_c = 0x00000000,
+ .peak_c = 0x07070707,
+ .pad_ctls = 0x800034BB,
+ },
+ { /* 1080p / 148.5MHz */
+ .pclk = 148500000,
+ .pll0 = 0x01003310,
+ .pll1 = 0x00301500,
+ .drive_c = 0x33333333,
+ .pe_c = 0x00000000,
+ .peak_c = 0x0C0C0C0C,
+ .pad_ctls = 0x800034BB,
+ },
+ { /* 2216p / 297MHz */
+ .pclk = UINT_MAX,
+ .pll0 = 0x01003F10,
+ .pll1 = 0x00300F00,
+ .drive_c = 0x37373737,
+ .pe_c = 0x00000000,
+ .peak_c = 0x17171717,
+ .pad_ctls = 0x800036BB,
+ },
+};
+
+struct hdmi_softc {
+ device_t dev;
+ struct resource *mem_res;
+ struct resource *irq_res;
+ void *irq_ih;
+
+ clk_t clk_parent;
+ clk_t clk_hdmi;
+ hwreset_t hwreset_hdmi;
+ regulator_t supply_hdmi;
+ regulator_t supply_pll;
+ regulator_t supply_vdd;
+
+ uint64_t pclk;
+ boolean_t hdmi_mode;
+
+ int audio_src_type;
+ int audio_freq;
+ int audio_chans;
+
+ struct tegra_drm *drm;
+ struct tegra_drm_encoder output;
+
+ const struct tmds_config *tmds_config;
+ int n_tmds_configs;
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"nvidia,tegra124-hdmi", 1},
+ {NULL, 0},
+};
+
+/* These functions have been copied from newer version of drm_edid.c */
+/* ELD Header Block */
+#define DRM_ELD_HEADER_BLOCK_SIZE 4
+#define DRM_ELD_BASELINE_ELD_LEN 2 /* in dwords! */
+static int drm_eld_size(const uint8_t *eld)
+{
+ return DRM_ELD_HEADER_BLOCK_SIZE + eld[DRM_ELD_BASELINE_ELD_LEN] * 4;
+}
+
+static int
+drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame,
+ struct drm_display_mode *mode)
+{
+ int rv;
+
+ if (!frame || !mode)
+ return -EINVAL;
+
+ rv = hdmi_avi_infoframe_init(frame);
+ if (rv < 0)
+ return rv;
+
+ if (mode->flags & DRM_MODE_FLAG_DBLCLK)
+ frame->pixel_repeat = 1;
+
+ frame->video_code = drm_match_cea_mode(mode);
+
+ frame->picture_aspect = HDMI_PICTURE_ASPECT_NONE;
+#ifdef FREEBSD_NOTYET
+ /*
+ * Populate picture aspect ratio from either
+ * user input (if specified) or from the CEA mode list.
+ */
+ if (mode->picture_aspect_ratio == HDMI_PICTURE_ASPECT_4_3 ||
+ mode->picture_aspect_ratio == HDMI_PICTURE_ASPECT_16_9)
+ frame->picture_aspect = mode->picture_aspect_ratio;
+ else if (frame->video_code > 0)
+ frame->picture_aspect = drm_get_cea_aspect_ratio(
+ frame->video_code);
+#endif
+
+ frame->active_aspect = HDMI_ACTIVE_ASPECT_PICTURE;
+ frame->scan_mode = HDMI_SCAN_MODE_UNDERSCAN;
+
+ return 0;
+}
+/* --------------------------------------------------------------------- */
+
+static int
+hdmi_setup_clock(struct tegra_drm_encoder *output, clk_t clk, uint64_t pclk)
+{
+ struct hdmi_softc *sc;
+ uint64_t freq;
+ int rv;
+
+ sc = device_get_softc(output->dev);
+
+ /* Disable consumers clock for while. */
+ rv = clk_disable(sc->clk_hdmi);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot disable 'hdmi' clock\n");
+ return (rv);
+ }
+ rv = clk_disable(clk);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot disable display clock\n");
+ return (rv);
+ }
+
+ /* Set frequency for Display Controller PLL. */
+ freq = HDMI_DC_CLOCK_MULTIPIER * pclk;
+ rv = clk_set_freq(sc->clk_parent, freq, 0);
+ if (rv != 0) {
+ device_printf(output->dev,
+ "Cannot set display pixel frequency\n");
+ return (rv);
+ }
+
+ /* Reparent display controller */
+ rv = clk_set_parent_by_clk(clk, sc->clk_parent);
+ if (rv != 0) {
+ device_printf(output->dev, "Cannot set parent clock\n");
+ return (rv);
+ }
+ rv = clk_set_freq(clk, freq, 0);
+ if (rv != 0) {
+ device_printf(output->dev,
+ "Cannot set display controller frequency\n");
+ return (rv);
+ }
+ rv = clk_set_freq(sc->clk_hdmi, pclk, 0);
+ if (rv != 0) {
+ device_printf(output->dev,
+ "Cannot set display controller frequency\n");
+ return (rv);
+ }
+
+ /* And reenable consumers clock. */
+ rv = clk_enable(clk);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot enable display clock\n");
+ return (rv);
+ }
+ rv = clk_enable(sc->clk_hdmi);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot enable 'hdmi' clock\n");
+ return (rv);
+ }
+
+ rv = clk_get_freq(clk, &freq);
+ if (rv != 0) {
+ device_printf(output->dev,
+ "Cannot get display controller frequency\n");
+ return (rv);
+ }
+
+ DRM_DEBUG_KMS("DC frequency: %llu\n", freq);
+
+ return (0);
+}
+
+/* -------------------------------------------------------------------
+ *
+ * Infoframes.
+ *
+ */
+static void
+avi_setup_infoframe(struct hdmi_softc *sc, struct drm_display_mode *mode)
+{
+ struct hdmi_avi_infoframe frame;
+ uint8_t buf[17], *hdr, *pb;
+ ssize_t rv;
+
+ rv = drm_hdmi_avi_infoframe_from_display_mode(&frame, mode);
+ if (rv < 0) {
+ device_printf(sc->dev, "Cannot setup AVI infoframe: %zd\n", rv);
+ return;
+ }
+ rv = hdmi_avi_infoframe_pack(&frame, buf, sizeof(buf));
+ if (rv < 0) {
+ device_printf(sc->dev, "Cannot pack AVI infoframe: %zd\n", rv);
+ return;
+ }
+ hdr = buf + 0;
+ pb = buf + 3;
+ WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_HEADER,
+ (hdr[2] << 16) | (hdr[1] << 8) | (hdr[0] << 0));
+ WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_LOW,
+ (pb[3] << 24) |(pb[2] << 16) | (pb[1] << 8) | (pb[0] << 0));
+ WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_HIGH,
+ (pb[6] << 16) | (pb[5] << 8) | (pb[4] << 0));
+ WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_LOW,
+ (pb[10] << 24) |(pb[9] << 16) | (pb[8] << 8) | (pb[7] << 0));
+ WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_HIGH,
+ (pb[13] << 16) | (pb[12] << 8) | (pb[11] << 0));
+
+ WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL,
+ AVI_INFOFRAME_CTRL_ENABLE);
+}
+
+static void
+audio_setup_infoframe(struct hdmi_softc *sc)
+{
+ struct hdmi_audio_infoframe frame;
+ uint8_t buf[14], *hdr, *pb;
+ ssize_t rv;
+
+ rv = hdmi_audio_infoframe_init(&frame);
+ frame.channels = sc->audio_chans;
+ rv = hdmi_audio_infoframe_pack(&frame, buf, sizeof(buf));
+ if (rv < 0) {
+ device_printf(sc->dev, "Cannot pack audio infoframe\n");
+ return;
+ }
+ hdr = buf + 0;
+ pb = buf + 3;
+ WR4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER,
+ (hdr[2] << 16) | (hdr[1] << 8) | (hdr[0] << 0));
+ WR4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_LOW,
+ (pb[3] << 24) |(pb[2] << 16) | (pb[1] << 8) | (pb[0] << 0));
+ WR4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_HIGH,
+ (pb[5] << 8) | (pb[4] << 0));
+
+ WR4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL,
+ AUDIO_INFOFRAME_CTRL_ENABLE);
+}
+
+/* -------------------------------------------------------------------
+ *
+ * Audio
+ *
+ */
+static void
+init_hda_eld(struct hdmi_softc *sc)
+{
+ size_t size;
+ int i ;
+ uint32_t val;
+
+ size = drm_eld_size(sc->output.connector.eld);
+ for (i = 0; i < HDMI_ELD_BUFFER_SIZE; i++) {
+ val = i << 8;
+ if (i < size)
+ val |= sc->output.connector.eld[i];
+ WR4(sc, HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR, val);
+ }
+ WR4(sc,HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE,
+ SOR_AUDIO_HDA_PRESENSE_VALID | SOR_AUDIO_HDA_PRESENSE_PRESENT);
+}
+
+static int
+get_audio_regs(int freq, bus_size_t *acr_reg, bus_size_t *nval_reg,
+ bus_size_t *aval_reg)
+{
+ int i;
+ const struct audio_reg *reg;
+
+ for (i = 0; i < nitems(audio_regs) ; i++) {
+ reg = audio_regs + i;
+ if (reg->audio_clk == freq) {
+ if (acr_reg != NULL)
+ *acr_reg = reg->acr_reg;
+ if (nval_reg != NULL)
+ *nval_reg = reg->nval_reg;
+ if (aval_reg != NULL)
+ *aval_reg = reg->aval_reg;
+ return (0);
+ }
+ }
+ return (ERANGE);
+}
+
+#define FR_BITS 16
+#define TO_FFP(x) (((int64_t)(x)) << FR_BITS)
+#define TO_INT(x) ((int)((x) >> FR_BITS))
+static int
+get_hda_cts_n(uint32_t audio_freq_hz, uint32_t pixclk_freq_hz,
+ uint32_t *best_cts, uint32_t *best_n, uint32_t *best_a)
+{
+ int min_n;
+ int max_n;
+ int ideal_n;
+ int n;
+ int cts;
+ int aval;
+ int64_t err_f;
+ int64_t min_err_f;
+ int64_t cts_f;
+ int64_t aval_f;
+ int64_t half_f; /* constant 0.5 */
+ bool better_n;
+
+ /*
+ * All floats are in fixed I48.16 format.
+ *
+ * Ideal ACR interval is 1000 hz (1 ms);
+ * acceptable is 300 hz .. 1500 hz
+ */
+ min_n = 128 * audio_freq_hz / 1500;
+ max_n = 128 * audio_freq_hz / 300;
+ ideal_n = 128 * audio_freq_hz / 1000;
+ min_err_f = TO_FFP(100);
+ half_f = TO_FFP(1) / 2;
+
+ *best_n = 0;
+ *best_cts = 0;
+ *best_a = 0;
+
+ for (n = min_n; n <= max_n; n++) {
+ cts_f = TO_FFP(pixclk_freq_hz);
+ cts_f *= n;
+ cts_f /= 128 * audio_freq_hz;
+ cts = TO_INT(cts_f + half_f); /* round */
+ err_f = cts_f - TO_FFP(cts);
+ if (err_f < 0)
+ err_f = -err_f;
+ aval_f = TO_FFP(24000000);
+ aval_f *= n;
+ aval_f /= 128 * audio_freq_hz;
+ aval = TO_INT(aval_f); /* truncate */
+
+ better_n = abs(n - ideal_n) < abs((int)(*best_n) - ideal_n);
+ if (TO_FFP(aval) == aval_f &&
+ (err_f < min_err_f || (err_f == min_err_f && better_n))) {
+ min_err_f = err_f;
+ *best_n = (uint32_t)n;
+ *best_cts = (uint32_t)cts;
+ *best_a = (uint32_t)aval;
+
+ if (err_f == 0 && n == ideal_n)
+ break;
+ }
+ }
+ return (0);
+}
+#undef FR_BITS
+#undef TO_FFP
+#undef TO_INT
+
+static int
+audio_setup(struct hdmi_softc *sc)
+{
+ uint32_t val;
+ uint32_t audio_n;
+ uint32_t audio_cts;
+ uint32_t audio_aval;
+ uint64_t hdmi_freq;
+ bus_size_t aval_reg;
+ int rv;
+
+ if (!sc->hdmi_mode)
+ return (ENOTSUP);
+ rv = get_audio_regs(sc->audio_freq, NULL, NULL, &aval_reg);
+ if (rv != 0) {
+ device_printf(sc->dev, "Unsupported audio frequency.\n");
+ return (rv);
+ }
+
+ rv = clk_get_freq(sc->clk_hdmi, &hdmi_freq);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get hdmi frequency: %d\n", rv);
+ return (rv);
+ }
+
+ rv = get_hda_cts_n(sc->audio_freq, hdmi_freq, &audio_cts, &audio_n,
+ &audio_aval);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot compute audio coefs: %d\n", rv);
+ return (rv);
+ }
+
+ /* Audio infoframe. */
+ audio_setup_infoframe(sc);
+ /* Setup audio source */
+ WR4(sc, HDMI_NV_PDISP_SOR_AUDIO_CNTRL0,
+ SOR_AUDIO_CNTRL0_SOURCE_SELECT(sc->audio_src_type) |
+ SOR_AUDIO_CNTRL0_INJECT_NULLSMPL);
+
+ val = RD4(sc, HDMI_NV_PDISP_SOR_AUDIO_SPARE0);
+ val |= SOR_AUDIO_SPARE0_HBR_ENABLE;
+ WR4(sc, HDMI_NV_PDISP_SOR_AUDIO_SPARE0, val);
+
+ WR4(sc, HDMI_NV_PDISP_HDMI_ACR_CTRL, 0);
+
+ WR4(sc, HDMI_NV_PDISP_AUDIO_N,
+ AUDIO_N_RESETF |
+ AUDIO_N_GENERATE_ALTERNATE |
+ AUDIO_N_VALUE(audio_n - 1));
+
+ WR4(sc, HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_HIGH,
+ ACR_SUBPACK_N(audio_n) | ACR_ENABLE);
+
+ WR4(sc, HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW,
+ ACR_SUBPACK_CTS(audio_cts));
+
+ WR4(sc, HDMI_NV_PDISP_HDMI_SPARE,
+ SPARE_HW_CTS | SPARE_FORCE_SW_CTS | SPARE_CTS_RESET_VAL(1));
+
+ val = RD4(sc, HDMI_NV_PDISP_AUDIO_N);
+ val &= ~AUDIO_N_RESETF;
+ WR4(sc, HDMI_NV_PDISP_AUDIO_N, val);
+
+ WR4(sc, aval_reg, audio_aval);
+
+ return (0);
+}
+
+static void
+audio_disable(struct hdmi_softc *sc) {
+ uint32_t val;
+
+ /* Disable audio */
+ val = RD4(sc, HDMI_NV_PDISP_HDMI_GENERIC_CTRL);
+ val &= ~GENERIC_CTRL_AUDIO;
+ WR4(sc, HDMI_NV_PDISP_HDMI_GENERIC_CTRL, val);
+
+ /* Disable audio infoframes */
+ val = RD4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL);
+ val &= ~AUDIO_INFOFRAME_CTRL_ENABLE;
+ WR4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL, val);
+}
+
+static void
+audio_enable(struct hdmi_softc *sc) {
+ uint32_t val;
+
+ if (!sc->hdmi_mode)
+ audio_disable(sc);
+
+ /* Enable audio infoframes */
+ val = RD4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL);
+ val |= AUDIO_INFOFRAME_CTRL_ENABLE;
+ WR4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL, val);
+
+ /* Enable audio */
+ val = RD4(sc, HDMI_NV_PDISP_HDMI_GENERIC_CTRL);
+ val |= GENERIC_CTRL_AUDIO;
+ WR4(sc, HDMI_NV_PDISP_HDMI_GENERIC_CTRL, val);
+}
+
+/* -------------------------------------------------------------------
+ *
+ * HDMI.
+ *
+ */
+ /* Process format change notification from HDA */
+static void
+hda_intr(struct hdmi_softc *sc)
+{
+ uint32_t val;
+ int rv;
+
+ if (!sc->hdmi_mode)
+ return;
+
+ val = RD4(sc, HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH0);
+ if ((val & (1 << 30)) == 0) {
+ audio_disable(sc);
+ return;
+ }
+
+ /* XXX Move this to any header */
+ /* Keep in sync with HDA */
+ sc->audio_freq = val & 0x00FFFFFF;
+ sc->audio_chans = (val >> 24) & 0x0f;
+ DRM_DEBUG_KMS("%d channel(s) at %dHz\n", sc->audio_chans,
+ sc->audio_freq);
+
+ rv = audio_setup(sc);
+ if (rv != 0) {
+ audio_disable(sc);
+ return;
+ }
+
+ audio_enable(sc);
+}
+
+static void
+tmds_init(struct hdmi_softc *sc, const struct tmds_config *tmds)
+{
+
+ WR4(sc, HDMI_NV_PDISP_SOR_PLL0, tmds->pll0);
+ WR4(sc, HDMI_NV_PDISP_SOR_PLL1, tmds->pll1);
+ WR4(sc, HDMI_NV_PDISP_PE_CURRENT, tmds->pe_c);
+ WR4(sc, HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT, tmds->drive_c);
+ WR4(sc, HDMI_NV_PDISP_SOR_IO_PEAK_CURRENT, tmds->peak_c);
+ WR4(sc, HDMI_NV_PDISP_SOR_PAD_CTLS0, tmds->pad_ctls);
+}
+
+static int
+hdmi_sor_start(struct hdmi_softc *sc, struct drm_display_mode *mode)
+{
+ int i;
+ uint32_t val;
+
+ /* Enable TMDS macro */
+ val = RD4(sc, HDMI_NV_PDISP_SOR_PLL0);
+ val &= ~SOR_PLL0_PWR;
+ val &= ~SOR_PLL0_VCOPD;
+ val &= ~SOR_PLL0_PULLDOWN;
+ WR4(sc, HDMI_NV_PDISP_SOR_PLL0, val);
+ DELAY(10);
+
+ val = RD4(sc, HDMI_NV_PDISP_SOR_PLL0);
+ val &= ~SOR_PLL0_PDBG;
+ WR4(sc, HDMI_NV_PDISP_SOR_PLL0, val);
+
+ WR4(sc, HDMI_NV_PDISP_SOR_PWR, SOR_PWR_SETTING_NEW);
+ WR4(sc, HDMI_NV_PDISP_SOR_PWR, 0);
+
+ /* Wait until SOR is ready */
+ for (i = 1000; i > 0; i--) {
+ val = RD4(sc, HDMI_NV_PDISP_SOR_PWR);
+ if ((val & SOR_PWR_SETTING_NEW) == 0)
+ break;
+ DELAY(10);
+ }
+ if (i <= 0) {
+ device_printf(sc->dev, "Timeouted while enabling SOR power.\n");
+ return (ETIMEDOUT);
+ }
+
+ val = SOR_STATE2_ASY_OWNER(ASY_OWNER_HEAD0) |
+ SOR_STATE2_ASY_SUBOWNER(SUBOWNER_BOTH) |
+ SOR_STATE2_ASY_CRCMODE(ASY_CRCMODE_COMPLETE) |
+ SOR_STATE2_ASY_PROTOCOL(ASY_PROTOCOL_SINGLE_TMDS_A);
+ if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+ val |= SOR_STATE2_ASY_HSYNCPOL_NEG;
+ if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+ val |= SOR_STATE2_ASY_VSYNCPOL_NEG;
+ WR4(sc, HDMI_NV_PDISP_SOR_STATE2, val);
+
+ WR4(sc, HDMI_NV_PDISP_SOR_STATE1, SOR_STATE1_ASY_ORMODE_NORMAL |
+ SOR_STATE1_ASY_HEAD_OPMODE(ASY_HEAD_OPMODE_AWAKE));
+
+ WR4(sc, HDMI_NV_PDISP_SOR_STATE0, 0);
+ WR4(sc, HDMI_NV_PDISP_SOR_STATE0, SOR_STATE0_UPDATE);
+
+ val = RD4(sc, HDMI_NV_PDISP_SOR_STATE1);
+ val |= SOR_STATE1_ATTACHED;
+ WR4(sc, HDMI_NV_PDISP_SOR_STATE1, val);
+
+ WR4(sc, HDMI_NV_PDISP_SOR_STATE0, 0);
+
+ return 0;
+}
+
+static int
+hdmi_disable(struct hdmi_softc *sc)
+{
+ struct tegra_crtc *crtc;
+ device_t dc;
+ uint32_t val;
+
+ dc = NULL;
+ if (sc->output.encoder.crtc != NULL) {
+ crtc = container_of(sc->output.encoder.crtc, struct tegra_crtc,
+ drm_crtc);
+ dc = crtc->dev;
+ }
+
+ if (dc != NULL) {
+ TEGRA_DC_HDMI_ENABLE(dc, false);
+ TEGRA_DC_DISPLAY_ENABLE(dc, false);
+ }
+ audio_disable(sc);
+ val = RD4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL);
+ val &= ~AVI_INFOFRAME_CTRL_ENABLE;
+ WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL, val);
+
+ /* Disable interrupts */
+ WR4(sc, HDMI_NV_PDISP_INT_ENABLE, 0);
+ WR4(sc, HDMI_NV_PDISP_INT_MASK, 0);
+
+ return (0);
+}
+
+static int
+hdmi_enable(struct hdmi_softc *sc)
+{
+ uint64_t freq;
+ struct drm_display_mode *mode;
+ struct tegra_crtc *crtc;
+ uint32_t val, h_sync_width, h_back_porch, h_front_porch, h_pulse_start;
+ uint32_t h_max_ac_packet, div8_2;
+ device_t dc;
+ int i, rv;
+
+ mode = &sc->output.encoder.crtc->mode;
+ crtc = container_of(sc->output.encoder.crtc, struct tegra_crtc,
+ drm_crtc);
+ dc = crtc->dev;
+
+ /* Compute all timings first. */
+ sc->pclk = mode->clock * 1000;
+ h_sync_width = mode->hsync_end - mode->hsync_start;
+ h_back_porch = mode->htotal - mode->hsync_end;
+ h_front_porch = mode->hsync_start - mode->hdisplay;
+ h_pulse_start = 1 + h_sync_width + h_back_porch - 10;
+ h_max_ac_packet = (h_sync_width + h_back_porch + h_front_porch -
+ HDMI_REKEY_DEFAULT - 18) / 32;
+
+ /* Check if HDMI device is connected and detected. */
+ if (sc->output.connector.edid_blob_ptr == NULL) {
+ sc->hdmi_mode = false;
+ } else {
+ sc->hdmi_mode = drm_detect_hdmi_monitor(
+ (struct edid *)sc->output.connector.edid_blob_ptr->data);
+ }
+
+ /* Get exact HDMI pixel frequency. */
+ rv = clk_get_freq(sc->clk_hdmi, &freq);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot get 'hdmi' clock frequency\n");
+ return (rv);
+ }
+ DRM_DEBUG_KMS("HDMI frequency: %llu Hz\n", freq);
+
+ /* Wakeup SOR power */
+ val = RD4(sc, HDMI_NV_PDISP_SOR_PLL0);
+ val &= ~SOR_PLL0_PDBG;
+ WR4(sc, HDMI_NV_PDISP_SOR_PLL0, val);
+ DELAY(10);
+
+ val = RD4(sc, HDMI_NV_PDISP_SOR_PLL0);
+ val &= ~SOR_PLL0_PWR;
+ WR4(sc, HDMI_NV_PDISP_SOR_PLL0, val);
+
+ /* Setup timings */
+ TEGRA_DC_SETUP_TIMING(dc, h_pulse_start);
+ WR4(sc, HDMI_NV_PDISP_HDMI_VSYNC_WINDOW,
+ VSYNC_WINDOW_START(0x200) | VSYNC_WINDOW_END(0x210) |
+ VSYNC_WINDOW_ENABLE);
+
+ /* Setup video source and adjust video range */
+ val = 0;
+ if (crtc->nvidia_head != 0)
+ HDMI_SRC_DISPLAYB;
+ if ((mode->hdisplay != 640) || (mode->vdisplay != 480))
+ val |= ARM_VIDEO_RANGE_LIMITED;
+ WR4(sc, HDMI_NV_PDISP_INPUT_CONTROL, val);
+
+ /* Program SOR reference clock - it uses 8.2 fractional divisor */
+ div8_2 = (freq * 4) / 1000000;
+ val = SOR_REFCLK_DIV_INT(div8_2 >> 2) | SOR_REFCLK_DIV_FRAC(div8_2);
+ WR4(sc, HDMI_NV_PDISP_SOR_REFCLK, val);
+
+ /* Setup audio */
+ if (sc->hdmi_mode) {
+ rv = audio_setup(sc);
+ if (rv != 0)
+ sc->hdmi_mode = false;
+ }
+
+ /* Init HDA ELD */
+ init_hda_eld(sc);
+ val = HDMI_CTRL_REKEY(HDMI_REKEY_DEFAULT);
+ val |= HDMI_CTRL_MAX_AC_PACKET(h_max_ac_packet);
+ if (sc->hdmi_mode)
+ val |= HDMI_CTRL_ENABLE;
+ WR4(sc, HDMI_NV_PDISP_HDMI_CTRL, val);
+
+ /* Setup TMDS */
+ for (i = 0; i < sc->n_tmds_configs; i++) {
+ if (sc->pclk <= sc->tmds_config[i].pclk) {
+ tmds_init(sc, sc->tmds_config + i);
+ break;
+ }
+ }
+
+ /* Program sequencer. */
+ WR4(sc, HDMI_NV_PDISP_SOR_SEQ_CTL,
+ SOR_SEQ_PU_PC(0) | SOR_SEQ_PU_PC_ALT(0) |
+ SOR_SEQ_PD_PC(8) | SOR_SEQ_PD_PC_ALT(8));
+
+ val = SOR_SEQ_INST_WAIT_TIME(1) |
+ SOR_SEQ_INST_WAIT_UNITS(WAIT_UNITS_VSYNC) |
+ SOR_SEQ_INST_HALT |
+ SOR_SEQ_INST_DRIVE_PWM_OUT_LO;
+ WR4(sc, HDMI_NV_PDISP_SOR_SEQ_INST(0), val);
+ WR4(sc, HDMI_NV_PDISP_SOR_SEQ_INST(8), val);
+
+ val = RD4(sc,HDMI_NV_PDISP_SOR_CSTM);
+ val &= ~SOR_CSTM_LVDS_ENABLE;
+ val &= ~SOR_CSTM_ROTCLK(~0);
+ val |= SOR_CSTM_ROTCLK(2);
+ val &= ~SOR_CSTM_MODE(~0);
+ val |= SOR_CSTM_MODE(CSTM_MODE_TMDS);
+ val |= SOR_CSTM_PLLDIV;
+ WR4(sc, HDMI_NV_PDISP_SOR_CSTM, val);
+
+ TEGRA_DC_DISPLAY_ENABLE(dc, false);
+
+ rv = hdmi_sor_start(sc, mode);
+ if (rv != 0)
+ return (rv);
+
+ TEGRA_DC_HDMI_ENABLE(dc, true);
+ TEGRA_DC_DISPLAY_ENABLE(dc, true);
+
+ /* Enable HDA codec interrupt */
+ WR4(sc, HDMI_NV_PDISP_INT_MASK, INT_CODEC_SCRATCH0);
+ WR4(sc, HDMI_NV_PDISP_INT_ENABLE, INT_CODEC_SCRATCH0);
+
+ if (sc->hdmi_mode) {
+ avi_setup_infoframe(sc, mode);
+ audio_enable(sc);
+ }
+
+ return (0);
+}
+
+/* -------------------------------------------------------------------
+ *
+ * DRM Interface.
+ *
+ */
+static enum drm_mode_status
+hdmi_connector_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ struct tegra_drm_encoder *output;
+ struct hdmi_softc *sc;
+ int rv;
+ uint64_t freq;
+
+ output = container_of(connector, struct tegra_drm_encoder,
+ connector);
+ sc = device_get_softc(output->dev);
+
+ freq = HDMI_DC_CLOCK_MULTIPIER * mode->clock * 1000;
+ rv = clk_test_freq(sc->clk_parent, freq, 0);
+ DRM_DEBUG_KMS("Test HDMI frequency: %u kHz, rv: %d\n", mode->clock, rv);
+ if (rv != 0)
+ return (MODE_NOCLOCK);
+
+ return (MODE_OK);
+}
+
+static const struct drm_connector_helper_funcs hdmi_connector_helper_funcs = {
+ .get_modes = tegra_drm_connector_get_modes,
+ .mode_valid = hdmi_connector_mode_valid,
+ .best_encoder = tegra_drm_connector_best_encoder,
+};
+
+static const struct drm_connector_funcs hdmi_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
+ .detect = tegra_drm_connector_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = drm_connector_cleanup,
+};
+
+static const struct drm_encoder_funcs hdmi_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
+static void
+hdmi_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+
+ /* Empty function. */
+}
+
+static bool
+hdmi_encoder_mode_fixup(struct drm_encoder *encoder,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted)
+{
+
+ return (true);
+}
+
+static void
+hdmi_encoder_prepare(struct drm_encoder *encoder)
+{
+
+ /* Empty function. */
+}
+
+static void
+hdmi_encoder_commit(struct drm_encoder *encoder)
+{
+
+ /* Empty function. */
+}
+
+static void
+hdmi_encoder_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode, struct drm_display_mode *adjusted)
+{
+ struct tegra_drm_encoder *output;
+ struct hdmi_softc *sc;
+ int rv;
+
+ output = container_of(encoder, struct tegra_drm_encoder, encoder);
+ sc = device_get_softc(output->dev);
+ rv = hdmi_enable(sc);
+ if (rv != 0)
+ device_printf(sc->dev, "Cannot enable HDMI port\n");
+
+}
+
+static void
+hdmi_encoder_disable(struct drm_encoder *encoder)
+{
+ struct tegra_drm_encoder *output;
+ struct hdmi_softc *sc;
+ int rv;
+
+ output = container_of(encoder, struct tegra_drm_encoder, encoder);
+ sc = device_get_softc(output->dev);
+ if (sc == NULL)
+ return;
+ rv = hdmi_disable(sc);
+ if (rv != 0)
+ device_printf(sc->dev, "Cannot disable HDMI port\n");
+}
+
+static const struct drm_encoder_helper_funcs hdmi_encoder_helper_funcs = {
+ .dpms = hdmi_encoder_dpms,
+ .mode_fixup = hdmi_encoder_mode_fixup,
+ .prepare = hdmi_encoder_prepare,
+ .commit = hdmi_encoder_commit,
+ .mode_set = hdmi_encoder_mode_set,
+ .disable = hdmi_encoder_disable,
+};
+
+/* -------------------------------------------------------------------
+ *
+ * Bus and infrastructure.
+ *
+ */
+static int
+hdmi_init_client(device_t dev, device_t host1x, struct tegra_drm *drm)
+{
+ struct hdmi_softc *sc;
+ phandle_t node;
+ int rv;
+
+ sc = device_get_softc(dev);
+ node = ofw_bus_get_node(sc->dev);
+ sc->drm = drm;
+ sc->output.setup_clock = &hdmi_setup_clock;
+
+ rv = tegra_drm_encoder_attach(&sc->output, node);
+ if (rv != 0) {
+ device_printf(dev, "Cannot attach output connector\n");
+ return(ENXIO);
+ }
+
+ /* Connect this encoder + connector to DRM. */
+ drm_connector_init(&drm->drm_dev, &sc->output.connector,
+ &hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA);
+
+ drm_connector_helper_add(&sc->output.connector,
+ &hdmi_connector_helper_funcs);
+
+ sc->output.connector.dpms = DRM_MODE_DPMS_OFF;
+
+ drm_encoder_init(&drm->drm_dev, &sc->output.encoder,
+ &hdmi_encoder_funcs, DRM_MODE_ENCODER_TMDS);
+
+ drm_encoder_helper_add(&sc->output.encoder, &hdmi_encoder_helper_funcs);
+
+ drm_mode_connector_attach_encoder(&sc->output.connector,
+ &sc->output.encoder);
+
+ rv = tegra_drm_encoder_init(&sc->output, drm);
+ if (rv < 0) {
+ device_printf(sc->dev, "Unable to init HDMI output\n");
+ return (rv);
+ }
+ sc->output.encoder.possible_crtcs = 0x3;
+ return (0);
+}
+
+static int
+hdmi_exit_client(device_t dev, device_t host1x, struct tegra_drm *drm)
+{
+ struct hdmi_softc *sc;
+
+ sc = device_get_softc(dev);
+ tegra_drm_encoder_exit(&sc->output, drm);
+ return (0);
+}
+
+static int
+get_fdt_resources(struct hdmi_softc *sc, phandle_t node)
+{
+ int rv;
+
+ rv = regulator_get_by_ofw_property(sc->dev, 0, "hdmi-supply",
+ &sc->supply_hdmi);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'hdmi' regulator\n");
+ return (ENXIO);
+ }
+ rv = regulator_get_by_ofw_property(sc->dev,0, "pll-supply",
+ &sc->supply_pll);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'pll' regulator\n");
+ return (ENXIO);
+ }
+ rv = regulator_get_by_ofw_property(sc->dev, 0, "vdd-supply",
+ &sc->supply_vdd);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'vdd' regulator\n");
+ return (ENXIO);
+ }
+
+ rv = hwreset_get_by_ofw_name(sc->dev, 0, "hdmi", &sc->hwreset_hdmi);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'hdmi' reset\n");
+ return (ENXIO);
+ }
+ rv = clk_get_by_ofw_name(sc->dev, 0, "parent", &sc->clk_parent);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'parent' clock\n");
+ return (ENXIO);
+ }
+ rv = clk_get_by_ofw_name(sc->dev, 0, "hdmi", &sc->clk_hdmi);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'hdmi' clock\n");
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static int
+enable_fdt_resources(struct hdmi_softc *sc)
+{
+ int rv;
+
+ rv = clk_set_parent_by_clk(sc->clk_hdmi, sc->clk_parent);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot set parent for 'hdmi' clock\n");
+ return (rv);
+ }
+
+ /* 594 MHz is arbitrarily selected value */
+ rv = clk_set_freq(sc->clk_parent, 594000000, 0);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot set frequency for 'hdmi' parent clock\n");
+ return (rv);
+ }
+ rv = clk_set_freq(sc->clk_hdmi, 594000000 / 4, 0);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot set frequency for 'hdmi' parent clock\n");
+ return (rv);
+ }
+
+ rv = regulator_enable(sc->supply_hdmi);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot enable 'hdmi' regulator\n");
+ return (rv);
+ }
+ rv = regulator_enable(sc->supply_pll);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot enable 'pll' regulator\n");
+ return (rv);
+ }
+ rv = regulator_enable(sc->supply_vdd);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot enable 'vdd' regulator\n");
+ return (rv);
+ }
+
+ rv = clk_enable(sc->clk_hdmi);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot enable 'hdmi' clock\n");
+ return (rv);
+ }
+
+ rv = hwreset_deassert(sc->hwreset_hdmi);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot unreset 'hdmi' reset\n");
+ return (rv);
+ }
+ return (0);
+}
+
+static void
+hdmi_intr(void *arg)
+{
+ struct hdmi_softc *sc;
+ uint32_t status;
+
+ sc = arg;
+
+ /* Confirm interrupt */
+ status = RD4(sc, HDMI_NV_PDISP_INT_STATUS);
+ WR4(sc, HDMI_NV_PDISP_INT_STATUS, status);
+
+ /* process audio verb from HDA */
+ if (status & INT_CODEC_SCRATCH0)
+ hda_intr(sc);
+}
+
+static int
+hdmi_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Tegra HDMI");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+hdmi_attach(device_t dev)
+{
+ struct hdmi_softc *sc;
+ phandle_t node;
+ int rid, rv;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ sc->output.dev = sc->dev;
+ node = ofw_bus_get_node(sc->dev);
+
+ sc->audio_src_type = SOURCE_SELECT_AUTO;
+ sc->audio_freq = 44100;
+ sc->audio_chans = 2;
+ sc->hdmi_mode = false;
+
+ sc->tmds_config = tegra124_tmds_config;
+ sc->n_tmds_configs = nitems(tegra124_tmds_config);
+
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "Cannot allocate memory resources\n");
+ goto fail;
+ }
+
+ rid = 0;
+ sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
+ if (sc->irq_res == NULL) {
+ device_printf(dev, "Cannot allocate IRQ resources\n");
+ goto fail;
+ }
+
+ rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+ NULL, hdmi_intr, sc, &sc->irq_ih);
+ if (rv != 0) {
+ device_printf(dev,
+ "WARNING: unable to register interrupt handler\n");
+ goto fail;
+ }
+
+ rv = get_fdt_resources(sc, node);
+ if (rv != 0) {
+ device_printf(dev, "Cannot parse FDT resources\n");
+ goto fail;
+ }
+ rv = enable_fdt_resources(sc);
+ if (rv != 0) {
+ device_printf(dev, "Cannot enable FDT resources\n");
+ goto fail;
+ }
+
+ rv = TEGRA_DRM_REGISTER_CLIENT(device_get_parent(sc->dev), sc->dev);
+ if (rv != 0) {
+ device_printf(dev, "Cannot register DRM device\n");
+ goto fail;
+ }
+ return (bus_generic_attach(dev));
+
+fail:
+ TEGRA_DRM_DEREGISTER_CLIENT(device_get_parent(sc->dev), sc->dev);
+
+ if (sc->irq_ih != NULL)
+ bus_teardown_intr(dev, sc->irq_res, sc->irq_ih);
+ if (sc->clk_parent != NULL)
+ clk_release(sc->clk_parent);
+ if (sc->clk_hdmi != NULL)
+ clk_release(sc->clk_hdmi);
+ if (sc->hwreset_hdmi != NULL)
+ hwreset_release(sc->hwreset_hdmi);
+ if (sc->supply_hdmi != NULL)
+ regulator_release(sc->supply_hdmi);
+ if (sc->supply_pll != NULL)
+ regulator_release(sc->supply_pll);
+ if (sc->supply_vdd != NULL)
+ regulator_release(sc->supply_vdd);
+ if (sc->irq_res != NULL)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+ if (sc->mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+ return (ENXIO);
+}
+
+static int
+hdmi_detach(device_t dev)
+{
+ struct hdmi_softc *sc;
+ sc = device_get_softc(dev);
+
+ TEGRA_DRM_DEREGISTER_CLIENT(device_get_parent(sc->dev), sc->dev);
+
+ if (sc->irq_ih != NULL)
+ bus_teardown_intr(dev, sc->irq_res, sc->irq_ih);
+ if (sc->clk_parent != NULL)
+ clk_release(sc->clk_parent);
+ if (sc->clk_hdmi != NULL)
+ clk_release(sc->clk_hdmi);
+ if (sc->hwreset_hdmi != NULL)
+ hwreset_release(sc->hwreset_hdmi);
+ if (sc->supply_hdmi != NULL)
+ regulator_release(sc->supply_hdmi);
+ if (sc->supply_pll != NULL)
+ regulator_release(sc->supply_pll);
+ if (sc->supply_vdd != NULL)
+ regulator_release(sc->supply_vdd);
+ if (sc->irq_res != NULL)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+ if (sc->mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+ return (bus_generic_detach(dev));
+}
+
+static device_method_t tegra_hdmi_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, hdmi_probe),
+ DEVMETHOD(device_attach, hdmi_attach),
+ DEVMETHOD(device_detach, hdmi_detach),
+
+ /* tegra drm interface */
+ DEVMETHOD(tegra_drm_init_client, hdmi_init_client),
+ DEVMETHOD(tegra_drm_exit_client, hdmi_exit_client),
+
+ DEVMETHOD_END
+};
+
+static devclass_t tegra_hdmi_devclass;
+DEFINE_CLASS_0(tegra_hdmi, tegra_hdmi_driver, tegra_hdmi_methods,
+ sizeof(struct hdmi_softc));
+DRIVER_MODULE(tegra_hdmi, host1x, tegra_hdmi_driver,
+tegra_hdmi_devclass, 0, 0);
diff --git a/sys/arm/nvidia/drm2/tegra_hdmi_reg.h b/sys/arm/nvidia/drm2/tegra_hdmi_reg.h
new file mode 100644
index 000000000000..091eb2386773
--- /dev/null
+++ b/sys/arm/nvidia/drm2/tegra_hdmi_reg.h
@@ -0,0 +1,283 @@
+/*-
+ * Copyright 1992-2016 Michal Meloun
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _TEGRA_HDMI_REG_H_
+#define _TEGRA_HDMI_REG_H_
+
+/*
+ * !!! WARNING !!!
+ * Tegra manual uses registers index (and not register addreses).
+ * We follow the TRM notation and index is converted to offset in
+ * WR4 / RD4 macros
+ */
+#define HDMI_NV_PDISP_SOR_STATE0 0x001
+#define SOR_STATE0_UPDATE (1 << 0)
+
+#define HDMI_NV_PDISP_SOR_STATE1 0x002
+#define SOR_STATE1_ATTACHED (1 << 3)
+#define SOR_STATE1_ASY_ORMODE_NORMAL (1 << 2)
+#define SOR_STATE1_ASY_HEAD_OPMODE(x) (((x) & 0x3) << 0)
+#define ASY_HEAD_OPMODE_SLEEP 0
+#define ASY_HEAD_OPMODE_SNOOZE 1
+#define ASY_HEAD_OPMODE_AWAKE 2
+
+#define HDMI_NV_PDISP_SOR_STATE2 0x003
+#define SOR_STATE2_ASY_DEPOL_NEG (1 << 14)
+#define SOR_STATE2_ASY_VSYNCPOL_NEG (1 << 13)
+#define SOR_STATE2_ASY_HSYNCPOL_NEG (1 << 12)
+#define SOR_STATE2_ASY_PROTOCOL(x) (((x) & 0xf) << 8)
+#define ASY_PROTOCOL_SINGLE_TMDS_A 1
+#define ASY_PROTOCOL_CUSTOM 15
+#define SOR_STATE2_ASY_CRCMODE(x) (((x) & 0x3) << 6)
+#define ASY_CRCMODE_ACTIVE 0
+#define ASY_CRCMODE_COMPLETE 1
+#define ASY_CRCMODE_NON_ACTIVE 2
+#define SOR_STATE2_ASY_SUBOWNER(x) (((x) & 0x3) << 4)
+#define ASY_SUBOWNER_NONE 0
+#define ASY_SUBOWNER_SUBHEAD0 1
+#define ASY_SUBOWNER_SUBHEAD1 2
+#define SUBOWNER_BOTH 3
+#define SOR_STATE2_ASY_OWNER(x) (((x) & 0x3) << 0)
+#define ASY_OWNER_NONE 0
+#define ASY_OWNER_HEAD0 1
+
+#define HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL 0x01e
+#define AUDIO_INFOFRAME_CTRL_ENABLE (1 << 0)
+#define HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_STATUS 0x01f
+#define HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER 0x020
+#define HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_LOW 0x021
+#define HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_HIGH 0x022
+#define INFOFRAME_HEADER_LEN(x) (((x) & 0x0f) << 16)
+#define INFOFRAME_HEADER_VERSION(x) (((x) & 0xff) << 8)
+#define INFOFRAME_HEADER_TYPE(x) (((x) & 0xff) << 0)
+
+#define HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL 0x023
+#define AVI_INFOFRAME_CTRL_ENABLE (1 << 0)
+#define HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_STATUS 0x024
+#define HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_HEADER 0x025
+#define HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_LOW 0x026
+#define HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_HIGH 0x027
+#define HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_LOW 0x028
+#define HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_HIGH 0x029
+
+#define HDMI_NV_PDISP_HDMI_GENERIC_CTRL 0x02a
+#define GENERIC_CTRL_AUDIO (1 << 16)
+#define GENERIC_CTRL_HBLANK (1 << 12)
+#define GENERIC_CTRL_SINGLE (1 << 8)
+#define GENERIC_CTRL_OTHER (1 << 4)
+#define GENERIC_CTRL_ENABLE (1 << 0)
+#define HDMI_NV_PDISP_HDMI_GENERIC_STATUS 0x02b
+#define HDMI_NV_PDISP_HDMI_GENERIC_HEADER 0x02c
+#define HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK0_LOW 0x02d
+#define HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK0_HIGH 0x02e
+#define HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK1_LOW 0x02f
+#define HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK1_HIGH 0x030
+#define HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK2_LOW 0x031
+#define HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK2_HIGH 0x032
+#define HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK3_LOW 0x033
+#define HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK3_HIGH 0x034
+
+#define HDMI_NV_PDISP_HDMI_ACR_CTRL 0x035
+#define HDMI_NV_PDISP_HDMI_ACR_0320_SUBPACK_LOW 0x036
+#define HDMI_NV_PDISP_HDMI_ACR_0320_SUBPACK_HIGH 0x037
+#define HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW 0x038
+#define HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_HIGH 0x039
+#define HDMI_NV_PDISP_HDMI_ACR_0882_SUBPACK_LOW 0x03a
+#define HDMI_NV_PDISP_HDMI_ACR_0882_SUBPACK_HIGH 0x03b
+#define HDMI_NV_PDISP_HDMI_ACR_1764_SUBPACK_LOW 0x03c
+#define HDMI_NV_PDISP_HDMI_ACR_1764_SUBPACK_HIGH 0x03d
+#define HDMI_NV_PDISP_HDMI_ACR_0480_SUBPACK_LOW 0x03e
+#define HDMI_NV_PDISP_HDMI_ACR_0480_SUBPACK_HIGH 0x03f
+#define HDMI_NV_PDISP_HDMI_ACR_0960_SUBPACK_LOW 0x040
+#define HDMI_NV_PDISP_HDMI_ACR_0960_SUBPACK_HIGH 0x041
+#define HDMI_NV_PDISP_HDMI_ACR_1920_SUBPACK_LOW 0x042
+#define HDMI_NV_PDISP_HDMI_ACR_1920_SUBPACK_HIGH 0x043
+#define ACR_ENABLE (1U << 31)
+#define ACR_SUBPACK_CTS(x) (((x) & 0xffffff) << 8)
+#define ACR_SUBPACK_N(x) (((x) & 0xffffff) << 0)
+
+#define HDMI_NV_PDISP_HDMI_CTRL 0x044
+#define HDMI_CTRL_ENABLE (1 << 30)
+#define HDMI_CTRL_CA_SELECT (1 << 28)
+#define HDMI_CTRL_SS_SELECT (1 << 27)
+#define HDMI_CTRL_SF_SELECT (1 << 26)
+#define HDMI_CTRL_CC_SELECT (1 << 25)
+#define HDMI_CTRL_CT_SELECT (1 << 24)
+#define HDMI_CTRL_MAX_AC_PACKET(x) (((x) & 0x1f) << 16)
+#define HDMI_CTRL_SAMPLE_FLAT (1 << 12)
+#define HDMI_CTRL_AUDIO_LAYOUT_SELECT (1 << 10)
+#define HDMI_CTRL_AUDIO_LAYOUT (1 << 8)
+#define HDMI_CTRL_REKEY(x) (((x) & 0x7f) << 0)
+
+#define HDMI_NV_PDISP_HDMI_VSYNC_WINDOW 0x046
+#define VSYNC_WINDOW_ENABLE (1U << 31)
+#define VSYNC_WINDOW_START(x) (((x) & 0x3ff) << 16)
+#define VSYNC_WINDOW_END(x) (((x) & 0x3ff) << 0)
+
+#define HDMI_NV_PDISP_HDMI_SPARE 0x04f
+#define SPARE_ACR_PRIORITY (1U << 31)
+#define SPARE_CTS_RESET_VAL(x) (((x) & 0x7) << 16)
+#define SPARE_SUPRESS_SP_B (1 << 2)
+#define SPARE_FORCE_SW_CTS (1 << 1)
+#define SPARE_HW_CTS (1 << 0)
+
+#define HDMI_NV_PDISP_SOR_PWR 0x055
+#define SOR_PWR_SETTING_NEW (1U << 31)
+#define SOR_PWR_SAFE_STATE_PU (1 << 16)
+#define SOR_PWR_NORMAL_START_ALT (1 << 1)
+#define SOR_PWR_NORMAL_STATE_PU (1 << 0)
+
+#define HDMI_NV_PDISP_SOR_PLL0 0x057
+#define SOR_PLL0_TX_REG_LOAD(x) (((x) & 0xf) << 28)
+#define SOR_PLL0_ICHPMP(x) (((x) & 0xf) << 24)
+#define SOR_PLL0_FILTER(x) (((x) & 0xf) << 16)
+#define SOR_PLL0_BG_V17_S(x) (((x) & 0xf) << 12)
+#define SOR_PLL0_VCOCAP(x) (((x) & 0xf) << 8)
+#define SOR_PLL0_PULLDOWN (1 << 5)
+#define SOR_PLL0_RESISTORSEL (1 << 4)
+#define SOR_PLL0_PDPORT (1 << 3)
+#define SOR_PLL0_VCOPD (1 << 2)
+#define SOR_PLL0_PDBG (1 << 1)
+#define SOR_PLL0_PWR (1 << 0)
+
+#define HDMI_NV_PDISP_SOR_PLL1 0x058
+#define SOR_PLL1_S_D_PIN_PE (1 << 30)
+#define SOR_PLL1_HALF_FULL_PE (1 << 29)
+#define SOR_PLL1_PE_EN (1 << 28)
+#define SOR_PLL1_LOADADJ(x) (((x) & 0xf) << 20)
+#define SOR_PLL1_TMDS_TERMADJ(x) (((x) & 0xf) << 9)
+#define SOR_PLL1_TMDS_TERM (1 << 8)
+
+#define HDMI_NV_PDISP_SOR_CSTM 0x05a
+#define SOR_CSTM_ROTAT(x) (((x) & 0xf) << 28)
+#define SOR_CSTM_ROTCLK(x) (((x) & 0xf) << 24)
+#define SOR_CSTM_PLLDIV (1 << 21)
+#define SOR_CSTM_BALANCED (1 << 19)
+#define SOR_CSTM_NEW_MODE (1 << 18)
+#define SOR_CSTM_DUP_SYNC (1 << 17)
+#define SOR_CSTM_LVDS_ENABLE (1 << 16)
+#define SOR_CSTM_LINKACTB (1 << 15)
+#define SOR_CSTM_LINKACTA (1 << 14)
+#define SOR_CSTM_MODE(x) (((x) & 0x3) << 12)
+#define CSTM_MODE_LVDS 0
+#define CSTM_MODE_TMDS 1
+
+#define HDMI_NV_PDISP_SOR_SEQ_CTL 0x05f
+#define SOR_SEQ_SWITCH (1 << 30)
+#define SOR_SEQ_STATUS (1 << 28)
+#define SOR_SEQ_PC(x) (((x) & 0xf) << 16)
+#define SOR_SEQ_PD_PC_ALT(x) (((x) & 0xf) << 12)
+#define SOR_SEQ_PD_PC(x) (((x) & 0xf) << 8)
+#define SOR_SEQ_PU_PC_ALT(x) (((x) & 0xf) << 4)
+#define SOR_SEQ_PU_PC(x) (((x) & 0xf) << 0)
+
+#define HDMI_NV_PDISP_SOR_SEQ_INST(x) (0x060 + (x))
+#define SOR_SEQ_INST_PLL_PULLDOWN (1U << 31)
+#define SOR_SEQ_INST_POWERDOWN_MACRO (1 << 30)
+#define SOR_SEQ_INST_ASSERT_PLL_RESETV (1 << 29)
+#define SOR_SEQ_INST_BLANK_V (1 << 28)
+#define SOR_SEQ_INST_BLANK_H (1 << 27)
+#define SOR_SEQ_INST_BLANK_DE (1 << 26)
+#define SOR_SEQ_INST_BLACK_DATA (1 << 25)
+#define SOR_SEQ_INST_TRISTATE_IOS (1 << 24)
+#define SOR_SEQ_INST_DRIVE_PWM_OUT_LO (1 << 23)
+#define SOR_SEQ_INST_PIN_B_HIGH (1 << 22)
+#define SOR_SEQ_INST_PIN_A_HIGH (1 << 21)
+#define SOR_SEQ_INST_HALT (1 << 15)
+#define SOR_SEQ_INST_WAIT_UNITS(x) (((x) & 0x3) << 12)
+#define WAIT_UNITS_US 0
+#define WAIT_UNITS_MS 1
+#define WAIT_UNITS_VSYNC 2
+#define SOR_SEQ_INST_WAIT_TIME(x) (((x) & 0x3ff) << 0)
+
+#define HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT 0x07e
+
+#define HDMI_NV_PDISP_AUDIO_N 0x08c
+#define AUDIO_N_LOOKUP (1 << 28)
+#define AUDIO_N_GENERATE_ALTERNATE (1 << 24)
+#define AUDIO_N_RESETF (1 << 20)
+#define AUDIO_N_VALUE(x) (((x) & 0xfffff) << 0)
+
+#define HDMI_NV_PDISP_SOR_REFCLK 0x095
+#define SOR_REFCLK_DIV_INT(x) (((x) & 0xff) << 8)
+#define SOR_REFCLK_DIV_FRAC(x) (((x) & 0x03) << 6)
+
+#define HDMI_NV_PDISP_INPUT_CONTROL 0x097
+#define ARM_VIDEO_RANGE_LIMITED (1 << 1)
+#define HDMI_SRC_DISPLAYB (1 << 0)
+
+#define HDMI_NV_PDISP_PE_CURRENT 0x099
+#define HDMI_NV_PDISP_SOR_AUDIO_CNTRL0 0x0ac
+#define SOR_AUDIO_CNTRL0_INJECT_NULLSMPL (1 << 29)
+#define SOR_AUDIO_CNTRL0_SOURCE_SELECT(x) (((x) & 0x03) << 20)
+#define SOURCE_SELECT_AUTO 0
+#define SOURCE_SELECT_SPDIF 1
+#define SOURCE_SELECT_HDAL 2
+#define SOR_AUDIO_CNTRL0_AFIFO_FLUSH (1 << 12)
+
+#define HDMI_NV_PDISP_SOR_AUDIO_SPARE0 0x0ae
+#define SOR_AUDIO_SPARE0_HBR_ENABLE (1 << 27)
+
+#define HDMI_NV_PDISP_SOR_AUDIO_NVAL_0320 0x0af
+#define HDMI_NV_PDISP_SOR_AUDIO_NVAL_0441 0x0b0
+#define HDMI_NV_PDISP_SOR_AUDIO_NVAL_0882 0x0b1
+#define HDMI_NV_PDISP_SOR_AUDIO_NVAL_1764 0x0b2
+#define HDMI_NV_PDISP_SOR_AUDIO_NVAL_0480 0x0b3
+#define HDMI_NV_PDISP_SOR_AUDIO_NVAL_0960 0x0b4
+#define HDMI_NV_PDISP_SOR_AUDIO_NVAL_1920 0x0b5
+#define HDMI_NV_PDISP_SOR_AUDIO_HDA_SCRATCH0 0x0b6
+#define HDMI_NV_PDISP_SOR_AUDIO_HDA_SCRATCH1 0x0b7
+#define HDMI_NV_PDISP_SOR_AUDIO_HDA_SCRATCH2 0x0b8
+#define HDMI_NV_PDISP_SOR_AUDIO_HDA_SCRATCH3 0x0b9
+#define HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH0 0x0ba
+#define HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH1 0x0bb
+#define HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR 0x0bc
+#define HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE 0x0bd
+#define SOR_AUDIO_HDA_PRESENSE_VALID (1 << 1)
+#define SOR_AUDIO_HDA_PRESENSE_PRESENT (1 << 0)
+
+#define HDMI_NV_PDISP_SOR_AUDIO_AVAL_0320 0x0bf
+#define HDMI_NV_PDISP_SOR_AUDIO_AVAL_0441 0x0c0
+#define HDMI_NV_PDISP_SOR_AUDIO_AVAL_0882 0x0c1
+#define HDMI_NV_PDISP_SOR_AUDIO_AVAL_1764 0x0c2
+#define HDMI_NV_PDISP_SOR_AUDIO_AVAL_0480 0x0c3
+#define HDMI_NV_PDISP_SOR_AUDIO_AVAL_0960 0x0c4
+#define HDMI_NV_PDISP_SOR_AUDIO_AVAL_1920 0x0c5
+#define HDMI_NV_PDISP_SOR_AUDIO_AVAL_DEFAULT 0x0c6
+
+#define HDMI_NV_PDISP_INT_STATUS 0x0cc
+#define INT_SCRATCH (1 << 3)
+#define INT_CP_REQUEST (1 << 2)
+#define INT_CODEC_SCRATCH1 (1 << 1)
+#define INT_CODEC_SCRATCH0 (1 << 0)
+
+#define HDMI_NV_PDISP_INT_MASK 0x0cd
+#define HDMI_NV_PDISP_INT_ENABLE 0x0ce
+#define HDMI_NV_PDISP_SOR_IO_PEAK_CURRENT 0x0d1
+#define HDMI_NV_PDISP_SOR_PAD_CTLS0 0x0d2
+
+#endif /* _TEGRA_HDMI_REG_H_ */
diff --git a/sys/arm/nvidia/drm2/tegra_host1x.c b/sys/arm/nvidia/drm2/tegra_host1x.c
new file mode 100644
index 000000000000..6d76beb83cd5
--- /dev/null
+++ b/sys/arm/nvidia/drm2/tegra_host1x.c
@@ -0,0 +1,645 @@
+/*-
+ * Copyright (c) 2015 Michal Meloun
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/clock.h>
+#include <sys/kernel.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+
+#include <sys/module.h>
+#include <sys/resource.h>
+#include <sys/sx.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/drm2/drmP.h>
+#include <dev/drm2/drm_crtc_helper.h>
+#include <dev/drm2/drm_fb_helper.h>
+#include <dev/fdt/simplebus.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/nvidia/drm2/tegra_drm.h>
+
+#include "fb_if.h"
+#include "tegra_drm_if.h"
+
+#define WR4(_sc, _r, _v) bus_rite_4((_sc)->mem_res, (_r), (_v))
+#define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r))
+
+#define LOCK(_sc) sx_xlock(&(_sc)->lock)
+#define UNLOCK(_sc) sx_xunlock(&(_sc)->lock)
+#define SLEEP(_sc, timeout) sx_sleep(sc, &sc->lock, 0, "host1x", timeout);
+#define LOCK_INIT(_sc) sx_init(&_sc->lock, "host1x")
+#define LOCK_DESTROY(_sc) sx_destroy(&_sc->lock)
+#define ASSERT_LOCKED(_sc) sx_assert(&_sc->lock, SA_LOCKED)
+#define ASSERT_UNLOCKED(_sc) sx_assert(&_sc->lock, SA_UNLOCKED)
+
+static struct ofw_compat_data compat_data[] = {
+ {"nvidia,tegra124-host1x", 1},
+ {NULL, 0}
+};
+
+#define DRIVER_NAME "tegra"
+#define DRIVER_DESC "NVIDIA Tegra TK1"
+#define DRIVER_DATE "20151101"
+#define DRIVER_MAJOR 0
+#define DRIVER_MINOR 0
+#define DRIVER_PATCHLEVEL 0
+
+struct client_info;
+TAILQ_HEAD(client_list, client_info);
+typedef struct client_list client_list_t;
+
+struct client_info {
+ TAILQ_ENTRY(client_info) list_e;
+ device_t client;
+ int activated;
+};
+
+struct host1x_softc {
+ struct simplebus_softc simplebus_sc; /* must be first */
+ device_t dev;
+ struct sx lock;
+ int attach_done;
+
+ struct resource *mem_res;
+ struct resource *syncpt_irq_res;
+ void *syncpt_irq_h;
+ struct resource *gen_irq_res;
+ void *gen_irq_h;
+
+ clk_t clk;
+ hwreset_t reset;
+ struct intr_config_hook irq_hook;
+
+ int drm_inited;
+ client_list_t clients;
+
+ struct tegra_drm *tegra_drm;
+};
+
+static void
+host1x_output_poll_changed(struct drm_device *drm_dev)
+{
+ struct tegra_drm *drm;
+
+ drm = container_of(drm_dev, struct tegra_drm, drm_dev);
+ if (drm->fb != NULL)
+ drm_fb_helper_hotplug_event(&drm->fb->fb_helper);
+}
+
+static const struct drm_mode_config_funcs mode_config_funcs = {
+ .fb_create = tegra_drm_fb_create,
+ .output_poll_changed = host1x_output_poll_changed,
+};
+
+static int
+host1x_drm_init(struct host1x_softc *sc)
+{
+ struct client_info *entry;
+ int rv;
+
+ LOCK(sc);
+
+ TAILQ_FOREACH(entry, &sc->clients, list_e) {
+ if (entry->activated)
+ continue;
+ rv = TEGRA_DRM_INIT_CLIENT(entry->client, sc->dev,
+ sc->tegra_drm);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot init DRM client %s: %d\n",
+ device_get_name(entry->client), rv);
+ return (rv);
+ }
+ entry->activated = 1;
+ }
+ UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+host1x_drm_exit(struct host1x_softc *sc)
+{
+ struct client_info *entry;
+ int rv;
+#ifdef FREEBSD_NOTYET
+ struct drm_device *dev, *tmp;
+#endif
+ LOCK(sc);
+ if (!sc->drm_inited) {
+ UNLOCK(sc);
+ return (0);
+ }
+ TAILQ_FOREACH_REVERSE(entry, &sc->clients, client_list, list_e) {
+ if (!entry->activated)
+ continue;
+ rv = TEGRA_DRM_EXIT_CLIENT(entry->client, sc->dev,
+ sc->tegra_drm);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot exit DRM client %s: %d\n",
+ device_get_name(entry->client), rv);
+ }
+ entry->activated = 0;
+ }
+
+#ifdef FREEBSD_NOTYET
+ list_for_each_entry_safe(dev, tmp, &driver->device_list, driver_item)
+ drm_put_dev(dev);
+#endif
+ sc->drm_inited = 0;
+ UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+host1x_drm_load(struct drm_device *drm_dev, unsigned long flags)
+{
+ struct host1x_softc *sc;
+ int rv;
+
+ sc = device_get_softc(drm_dev->dev);
+
+ drm_mode_config_init(drm_dev);
+ drm_dev->mode_config.min_width = 32;
+ drm_dev->mode_config.min_height = 32;
+ drm_dev->mode_config.max_width = 4096;
+ drm_dev->mode_config.max_height = 4096;
+ drm_dev->mode_config.funcs = &mode_config_funcs;
+
+ rv = host1x_drm_init(sc);
+ if (rv != 0)
+ goto fail_host1x;
+
+ drm_dev->irq_enabled = true;
+ drm_dev->max_vblank_count = 0xffffffff;
+ drm_dev->vblank_disable_allowed = true;
+
+ rv = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc);
+ if (rv != 0)
+ goto fail_vblank;
+
+ drm_mode_config_reset(drm_dev);
+
+ rv = tegra_drm_fb_init(drm_dev);
+ if (rv != 0)
+ goto fail_fb;
+ drm_kms_helper_poll_init(drm_dev);
+
+ return (0);
+
+fail_fb:
+ tegra_drm_fb_destroy(drm_dev);
+ drm_vblank_cleanup(drm_dev);
+fail_vblank:
+ host1x_drm_exit(sc);
+fail_host1x:
+ drm_mode_config_cleanup(drm_dev);
+
+ return (rv);
+}
+
+static int
+host1x_drm_unload(struct drm_device *drm_dev)
+{
+ struct host1x_softc *sc;
+ int rv;
+
+ sc = device_get_softc(drm_dev->dev);
+
+ drm_kms_helper_poll_fini(drm_dev);
+ tegra_drm_fb_destroy(drm_dev);
+ drm_mode_config_cleanup(drm_dev);
+
+ rv = host1x_drm_exit(sc);
+ if (rv < 0)
+ return (rv);
+ return (0);
+}
+
+static int
+host1x_drm_open(struct drm_device *drm_dev, struct drm_file *filp)
+{
+
+ return (0);
+}
+
+static void
+tegra_drm_preclose(struct drm_device *drm, struct drm_file *file)
+{
+ struct drm_crtc *crtc;
+
+ list_for_each_entry(crtc, &drm->mode_config.crtc_list, head)
+ tegra_dc_cancel_page_flip(crtc, file);
+}
+
+static void
+host1x_drm_lastclose(struct drm_device *drm_dev)
+{
+
+ struct tegra_drm *drm;
+
+ drm = container_of(drm_dev, struct tegra_drm, drm_dev);
+ if (drm->fb != NULL)
+ drm_fb_helper_restore_fbdev_mode(&drm->fb->fb_helper);
+}
+
+static int
+host1x_drm_enable_vblank(struct drm_device *drm_dev, int pipe)
+{
+ struct drm_crtc *crtc;
+
+ list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) {
+ if (pipe == tegra_dc_get_pipe(crtc)) {
+ tegra_dc_enable_vblank(crtc);
+ return (0);
+ }
+ }
+ return (-ENODEV);
+}
+
+static void
+host1x_drm_disable_vblank(struct drm_device *drm_dev, int pipe)
+{
+ struct drm_crtc *crtc;
+
+ list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) {
+ if (pipe == tegra_dc_get_pipe(crtc)) {
+ tegra_dc_disable_vblank(crtc);
+ return;
+ }
+ }
+}
+
+static struct drm_ioctl_desc host1x_drm_ioctls[] = {
+};
+
+struct drm_driver tegra_drm_driver = {
+ .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
+ .load = host1x_drm_load,
+ .unload = host1x_drm_unload,
+ .open = host1x_drm_open,
+ .preclose = tegra_drm_preclose,
+ .lastclose = host1x_drm_lastclose,
+
+ .get_vblank_counter = drm_vblank_count,
+ .enable_vblank = host1x_drm_enable_vblank,
+ .disable_vblank = host1x_drm_disable_vblank,
+
+ /* Fields filled by tegra_bo_driver_register()
+ .gem_free_object
+ .gem_pager_ops
+ .dumb_create
+ .dumb_map_offset
+ .dumb_destroy
+ */
+ .ioctls = host1x_drm_ioctls,
+ .num_ioctls = nitems(host1x_drm_ioctls),
+
+ .name = DRIVER_NAME,
+ .desc = DRIVER_DESC,
+ .date = DRIVER_DATE,
+ .major = DRIVER_MAJOR,
+ .minor = DRIVER_MINOR,
+ .patchlevel = DRIVER_PATCHLEVEL,
+};
+
+/*
+ * ----------------- Device methods -------------------------
+ */
+static void
+host1x_irq_hook(void *arg)
+{
+ struct host1x_softc *sc;
+ int rv;
+
+ sc = arg;
+ config_intrhook_disestablish(&sc->irq_hook);
+
+ tegra_bo_driver_register(&tegra_drm_driver);
+ rv = drm_get_platform_dev(sc->dev, &sc->tegra_drm->drm_dev,
+ &tegra_drm_driver);
+ if (rv != 0) {
+ device_printf(sc->dev, "drm_get_platform_dev(): %d\n", rv);
+ return;
+ }
+
+ sc->drm_inited = 1;
+}
+
+static struct fb_info *
+host1x_fb_helper_getinfo(device_t dev)
+{
+ struct host1x_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (sc->tegra_drm == NULL)
+ return (NULL);
+ return (tegra_drm_fb_getinfo(&sc->tegra_drm->drm_dev));
+}
+
+static int
+host1x_register_client(device_t dev, device_t client)
+{
+ struct host1x_softc *sc;
+ struct client_info *entry;
+
+ sc = device_get_softc(dev);
+
+ entry = malloc(sizeof(struct client_info), M_DEVBUF, M_WAITOK | M_ZERO);
+ entry->client = client;
+ entry->activated = 0;
+
+ LOCK(sc);
+ TAILQ_INSERT_TAIL(&sc->clients, entry, list_e);
+ UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+host1x_deregister_client(device_t dev, device_t client)
+{
+ struct host1x_softc *sc;
+ struct client_info *entry;
+
+ sc = device_get_softc(dev);
+
+ LOCK(sc);
+ TAILQ_FOREACH(entry, &sc->clients, list_e) {
+ if (entry->client == client) {
+ if (entry->activated)
+ panic("Tegra DRM: Attempt to deregister "
+ "activated client");
+ TAILQ_REMOVE(&sc->clients, entry, list_e);
+ free(entry, M_DEVBUF);
+ UNLOCK(sc);
+ return (0);
+ }
+ }
+ UNLOCK(sc);
+
+ return (0);
+}
+
+static void
+host1x_gen_intr(void *arg)
+{
+ struct host1x_softc *sc;
+
+ sc = (struct host1x_softc *)arg;
+ LOCK(sc);
+ UNLOCK(sc);
+}
+
+static void
+host1x_syncpt_intr(void *arg)
+{
+ struct host1x_softc *sc;
+
+ sc = (struct host1x_softc *)arg;
+ LOCK(sc);
+ UNLOCK(sc);
+}
+
+static void
+host1x_new_pass(device_t dev)
+{
+ struct host1x_softc *sc;
+ int rv, rid;
+ phandle_t node;
+
+ /*
+ * We attach during BUS_PASS_BUS (because we must overcome simplebus),
+ * but some of our FDT resources are not ready until BUS_PASS_DEFAULT
+ */
+ sc = device_get_softc(dev);
+ if (sc->attach_done || bus_current_pass < BUS_PASS_DEFAULT) {
+ bus_generic_new_pass(dev);
+ return;
+ }
+
+ sc->attach_done = 1;
+ node = ofw_bus_get_node(dev);
+
+ /* Allocate our IRQ resource. */
+ rid = 0;
+ sc->syncpt_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (sc->syncpt_irq_res == NULL) {
+ device_printf(dev, "Cannot allocate interrupt.\n");
+ rv = ENXIO;
+ goto fail;
+ }
+ rid = 1;
+ sc->gen_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (sc->gen_irq_res == NULL) {
+ device_printf(dev, "Cannot allocate interrupt.\n");
+ rv = ENXIO;
+ goto fail;
+ }
+
+ /* FDT resources */
+ rv = hwreset_get_by_ofw_name(sc->dev, 0, "host1x", &sc->reset);
+ if (rv != 0) {
+ device_printf(dev, "Cannot get fuse reset\n");
+ goto fail;
+ }
+ rv = clk_get_by_ofw_index(sc->dev, 0, 0, &sc->clk);
+ if (rv != 0) {
+ device_printf(dev, "Cannot get i2c clock: %d\n", rv);
+ goto fail;
+ }
+
+ rv = clk_enable(sc->clk);
+ if (rv != 0) {
+ device_printf(dev, "Cannot enable clock: %d\n", rv);
+ goto fail;
+ }
+ rv = hwreset_deassert(sc->reset);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot clear reset\n");
+ goto fail;
+ }
+
+ /* Setup interrupts */
+ rv = bus_setup_intr(dev, sc->gen_irq_res,
+ INTR_TYPE_MISC | INTR_MPSAFE, NULL, host1x_gen_intr,
+ sc, &sc->gen_irq_h);
+ if (rv) {
+ device_printf(dev, "Cannot setup gen interrupt.\n");
+ goto fail;
+ }
+
+ rv = bus_setup_intr(dev, sc->syncpt_irq_res,
+ INTR_TYPE_MISC | INTR_MPSAFE, NULL, host1x_syncpt_intr,
+ sc, &sc->syncpt_irq_h);
+ if (rv) {
+ device_printf(dev, "Cannot setup syncpt interrupt.\n");
+ goto fail;
+ }
+
+ simplebus_init(dev, 0);
+ for (node = OF_child(node); node > 0; node = OF_peer(node))
+ simplebus_add_device(dev, node, 0, NULL, -1, NULL);
+
+ sc->irq_hook.ich_func = host1x_irq_hook;
+ sc->irq_hook.ich_arg = sc;
+ config_intrhook_establish(&sc->irq_hook);
+ bus_generic_new_pass(dev);
+ return;
+
+fail:
+ device_detach(dev);
+ return;
+}
+
+static int
+host1x_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+host1x_attach(device_t dev)
+{
+ int rv, rid;
+ struct host1x_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->tegra_drm = malloc(sizeof(struct tegra_drm), DRM_MEM_DRIVER,
+ M_WAITOK | M_ZERO);
+
+ /* crosslink together all worlds */
+ sc->dev = dev;
+ sc->tegra_drm->drm_dev.dev_private = &sc->tegra_drm;
+ sc->tegra_drm->drm_dev.dev = dev;
+
+ TAILQ_INIT(&sc->clients);
+
+ LOCK_INIT(sc);
+
+ /* Get the memory resource for the register mapping. */
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "Cannot map registers.\n");
+ rv = ENXIO;
+ goto fail;
+ }
+
+ return (bus_generic_attach(dev));
+
+fail:
+ if (sc->tegra_drm != NULL)
+ free(sc->tegra_drm, DRM_MEM_DRIVER);
+ if (sc->mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+ LOCK_DESTROY(sc);
+ return (rv);
+}
+
+static int
+host1x_detach(device_t dev)
+{
+ struct host1x_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ host1x_drm_exit(sc);
+
+ if (sc->gen_irq_h != NULL)
+ bus_teardown_intr(dev, sc->gen_irq_res, sc->gen_irq_h);
+ if (sc->tegra_drm != NULL)
+ free(sc->tegra_drm, DRM_MEM_DRIVER);
+ if (sc->clk != NULL)
+ clk_release(sc->clk);
+ if (sc->reset != NULL)
+ hwreset_release(sc->reset);
+ if (sc->syncpt_irq_h != NULL)
+ bus_teardown_intr(dev, sc->syncpt_irq_res, sc->syncpt_irq_h);
+ if (sc->gen_irq_res != NULL)
+ bus_release_resource(dev, SYS_RES_IRQ, 1, sc->gen_irq_res);
+ if (sc->syncpt_irq_res != NULL)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->syncpt_irq_res);
+ if (sc->mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+ LOCK_DESTROY(sc);
+ return (bus_generic_detach(dev));
+}
+
+static device_method_t host1x_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, host1x_probe),
+ DEVMETHOD(device_attach, host1x_attach),
+ DEVMETHOD(device_detach, host1x_detach),
+
+ /* Bus interface */
+ DEVMETHOD(bus_new_pass, host1x_new_pass),
+
+ /* Framebuffer service methods */
+ DEVMETHOD(fb_getinfo, host1x_fb_helper_getinfo),
+
+ /* tegra drm interface */
+ DEVMETHOD(tegra_drm_register_client, host1x_register_client),
+ DEVMETHOD(tegra_drm_deregister_client, host1x_deregister_client),
+
+ DEVMETHOD_END
+};
+
+static devclass_t host1x_devclass;
+DEFINE_CLASS_1(host1x, host1x_driver, host1x_methods,
+ sizeof(struct host1x_softc), simplebus_driver);
+EARLY_DRIVER_MODULE(host1x, simplebus, host1x_driver,
+ host1x_devclass, 0, 0, BUS_PASS_BUS);
+
+/* Bindings for fbd device. */
+extern devclass_t fbd_devclass;
+extern driver_t fbd_driver;
+DRIVER_MODULE(fbd, host1x, fbd_driver, fbd_devclass, 0, 0);
diff --git a/sys/arm/nvidia/tegra124/files.tegra124 b/sys/arm/nvidia/tegra124/files.tegra124
new file mode 100644
index 000000000000..10f4a8899d32
--- /dev/null
+++ b/sys/arm/nvidia/tegra124/files.tegra124
@@ -0,0 +1,67 @@
+# $FreeBSD$
+
+#
+# Standard tegra124 devices and support.
+#
+arm/nvidia/tegra124/tegra124_machdep.c standard
+arm/nvidia/tegra124/tegra124_mp.c optional smp
+arm/nvidia/tegra124/tegra124_car.c standard
+arm/nvidia/tegra124/tegra124_clk_pll.c standard
+arm/nvidia/tegra124/tegra124_clk_per.c standard
+arm/nvidia/tegra124/tegra124_clk_super.c standard
+arm/nvidia/tegra124/tegra124_xusbpadctl.c standard
+arm/nvidia/tegra124/tegra124_pmc.c standard
+arm/nvidia/tegra124/tegra124_cpufreq.c standard
+arm/nvidia/tegra124/tegra124_coretemp.c standard
+arm/nvidia/tegra_usbphy.c standard
+arm/nvidia/tegra_pinmux.c standard
+arm/nvidia/tegra_uart.c optional uart
+arm/nvidia/tegra_sdhci.c optional sdhci
+arm/nvidia/tegra_gpio.c optional gpio
+arm/nvidia/tegra_ehci.c optional ehci
+arm/nvidia/tegra_xhci.c optional xhci
+arm/nvidia/tegra_ahci.c optional ahci
+arm/nvidia/tegra_pcie.c optional pci
+arm/nvidia/tegra_i2c.c optional iic
+arm/nvidia/tegra_rtc.c standard
+arm/nvidia/tegra_abpmisc.c standard
+arm/nvidia/tegra_efuse.c standard
+arm/nvidia/tegra_soctherm_if.m standard
+arm/nvidia/tegra_soctherm.c standard
+arm/nvidia/tegra_lic.c standard
+arm/nvidia/tegra_mc.c standard
+#arm/nvidia/tegra_hda.c optional snd_hda
+arm/nvidia/drm2/hdmi.c optional drm2
+arm/nvidia/drm2/tegra_drm_if.m optional drm2
+arm/nvidia/drm2/tegra_drm_subr.c optional drm2
+arm/nvidia/drm2/tegra_host1x.c optional drm2
+arm/nvidia/drm2/tegra_hdmi.c optional drm2
+arm/nvidia/drm2/tegra_dc_if.m optional drm2
+arm/nvidia/drm2/tegra_dc.c optional drm2
+arm/nvidia/drm2/tegra_fb.c optional drm2
+arm/nvidia/drm2/tegra_bo.c optional drm2
+#
+# Firmware
+#
+tegra124_xusb_fw.c optional tegra124_xusb_fw \
+ dependency "$S/arm/nvidia/tegra124/files.tegra124" \
+ compile-with "${AWK} -f $S/tools/fw_stub.awk tegra124_xusb.fw:tegra124_xusb_fw -mtegra124_xusb_fw -c${.TARGET}" \
+ no-implicit-rule before-depend local \
+ clean "tegra124_xusb_fw.c"
+tegra124_xusb.fwo optional tegra124_xusb_fw \
+ dependency "tegra124_xusb.fw" \
+ compile-with "${NORMAL_FWO}" \
+ no-implicit-rule \
+ clean "tegra124_xusb.fwo"
+tegra124_xusb.fw optional tegra124_xusb_fw \
+ dependency "$S/contrib/dev/nvidia/tegra124_xusb.bin.uu" \
+ compile-with "${NORMAL_FW}" \
+ no-obj no-implicit-rule \
+ clean "tegra124_xusb.fw"
+#
+# Temporary/to be moved stuff
+#
+arm/nvidia/as3722.c optional iic
+arm/nvidia/as3722_regulators.c optional iic
+arm/nvidia/as3722_rtc.c optional iic
+arm/nvidia/as3722_gpio.c optional iic
diff --git a/sys/arm/nvidia/tegra124/std.tegra124 b/sys/arm/nvidia/tegra124/std.tegra124
new file mode 100644
index 000000000000..48192c33961a
--- /dev/null
+++ b/sys/arm/nvidia/tegra124/std.tegra124
@@ -0,0 +1,6 @@
+# $FreeBSD$
+cpu CPU_CORTEXA
+machine arm armv7
+makeoptions CONF_CFLAGS="-march=armv7a"
+
+files "../nvidia/tegra124/files.tegra124"
diff --git a/sys/arm/nvidia/tegra124/tegra124_car.c b/sys/arm/nvidia/tegra124/tegra124_car.c
new file mode 100644
index 000000000000..89f3e392c0bc
--- /dev/null
+++ b/sys/arm/nvidia/tegra124/tegra124_car.c
@@ -0,0 +1,603 @@
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/kobj.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+
+#include <dev/extres/clk/clk_div.h>
+#include <dev/extres/clk/clk_fixed.h>
+#include <dev/extres/clk/clk_gate.h>
+#include <dev/extres/clk/clk_mux.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dt-bindings/clock/tegra124-car.h>
+
+#include "clkdev_if.h"
+#include "hwreset_if.h"
+#include "tegra124_car.h"
+
+static struct ofw_compat_data compat_data[] = {
+ {"nvidia,tegra124-car", 1},
+ {NULL, 0},
+};
+
+#define PLIST(x) static const char *x[]
+
+/* Pure multiplexer. */
+#define MUX(_id, cname, plists, o, s, w) \
+{ \
+ .clkdef.id = _id, \
+ .clkdef.name = cname, \
+ .clkdef.parent_names = plists, \
+ .clkdef.parent_cnt = nitems(plists), \
+ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \
+ .offset = o, \
+ .shift = s, \
+ .width = w, \
+}
+
+/* Fractional divider (7.1). */
+#define DIV7_1(_id, cname, plist, o, s) \
+{ \
+ .clkdef.id = _id, \
+ .clkdef.name = cname, \
+ .clkdef.parent_names = (const char *[]){plist}, \
+ .clkdef.parent_cnt = 1, \
+ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \
+ .offset = o, \
+ .i_shift = (s) + 1, \
+ .i_width = 7, \
+ .f_shift = s, \
+ .f_width = 1, \
+}
+
+/* Integer divider. */
+#define DIV(_id, cname, plist, o, s, w, f) \
+{ \
+ .clkdef.id = _id, \
+ .clkdef.name = cname, \
+ .clkdef.parent_names = (const char *[]){plist}, \
+ .clkdef.parent_cnt = 1, \
+ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \
+ .offset = o, \
+ .i_shift = s, \
+ .i_width = w, \
+ .div_flags = f, \
+}
+
+/* Gate in PLL block. */
+#define GATE_PLL(_id, cname, plist, o, s) \
+{ \
+ .clkdef.id = _id, \
+ .clkdef.name = cname, \
+ .clkdef.parent_names = (const char *[]){plist}, \
+ .clkdef.parent_cnt = 1, \
+ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \
+ .offset = o, \
+ .shift = s, \
+ .mask = 3, \
+ .on_value = 3, \
+ .off_value = 0, \
+}
+
+/* Standard gate. */
+#define GATE(_id, cname, plist, o, s) \
+{ \
+ .clkdef.id = _id, \
+ .clkdef.name = cname, \
+ .clkdef.parent_names = (const char *[]){plist}, \
+ .clkdef.parent_cnt = 1, \
+ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \
+ .offset = o, \
+ .shift = s, \
+ .mask = 1, \
+ .on_value = 1, \
+ .off_value = 0, \
+}
+
+/* Inverted gate. */
+#define GATE_INV(_id, cname, plist, o, s) \
+{ \
+ .clkdef.id = _id, \
+ .clkdef.name = cname, \
+ .clkdef.parent_names = (const char *[]){plist}, \
+ .clkdef.parent_cnt = 1, \
+ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \
+ .offset = o, \
+ .shift = s, \
+ .mask = 1, \
+ .on_value = 0, \
+ .off_value = 1, \
+}
+
+/* Fixed rate clock. */
+#define FRATE(_id, cname, _freq) \
+{ \
+ .clkdef.id = _id, \
+ .clkdef.name = cname, \
+ .clkdef.parent_names = NULL, \
+ .clkdef.parent_cnt = 0, \
+ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \
+ .freq = _freq, \
+}
+
+/* Fixed rate multipier/divider. */
+#define FACT(_id, cname, pname, _mult, _div) \
+{ \
+ .clkdef.id = _id, \
+ .clkdef.name = cname, \
+ .clkdef.parent_names = (const char *[]){pname}, \
+ .clkdef.parent_cnt = 1, \
+ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \
+ .mult = _mult, \
+ .div = _div, \
+}
+
+static uint32_t osc_freqs[16] = {
+ [0] = 13000000,
+ [1] = 16800000,
+ [4] = 19200000,
+ [5] = 38400000,
+ [8] = 12000000,
+ [9] = 48000000,
+ [12] = 260000000,
+};
+
+/* Parent lists. */
+PLIST(mux_pll_srcs) = {"osc_div_clk", NULL, "pllP_out0", NULL}; /* FIXME */
+PLIST(mux_plle_src1) = {"osc_div_clk", "pllP_out0"};
+PLIST(mux_plle_src) = {"pllE_src1", "pllREFE_out"};
+PLIST(mux_plld_out0_plld2_out0) = {"pllD_out0", "pllD2_out0"};
+PLIST(mux_xusb_hs) = {"xusb_ss_div2", "pllU_60"};
+PLIST(mux_xusb_ss) = {"pc_xusb_ss", "osc_div_clk"};
+
+/* Clocks ajusted online. */
+static struct clk_fixed_def fixed_clk_m =
+ FRATE(TEGRA124_CLK_CLK_M, "clk_m", 12000000);
+static struct clk_fixed_def fixed_osc_div_clk =
+ FACT(0, "osc_div_clk", "clk_m", 1, 1);
+
+static struct clk_fixed_def tegra124_fixed_clks[] = {
+ /* Core clocks. */
+ FRATE(0, "clk_s", 32768),
+ FACT(0, "clk_m_div2", "clk_m", 1, 2),
+ FACT(0, "clk_m_div4", "clk_m", 1, 3),
+ FACT(0, "pllU_60", "pllU_out", 1, 8),
+ FACT(0, "pllU_48", "pllU_out", 1, 10),
+ FACT(0, "pllU_12", "pllU_out", 1, 40),
+ FACT(TEGRA124_CLK_PLL_D_OUT0, "pllD_out0", "pllD_out", 1, 2),
+ FACT(TEGRA124_CLK_PLL_D2_OUT0, "pllD2_out0", "pllD2_out", 1, 1),
+ FACT(0, "pllX_out0", "pllX_out", 1, 2),
+ FACT(0, "pllC_UD", "pllC_out0", 1, 1),
+ FACT(0, "pllM_UD", "pllM_out0", 1, 1),
+
+ /* Audio clocks. */
+ FRATE(0, "audio0", 10000000),
+ FRATE(0, "audio1", 10000000),
+ FRATE(0, "audio2", 10000000),
+ FRATE(0, "audio3", 10000000),
+ FRATE(0, "audio4", 10000000),
+ FRATE(0, "ext_vimclk", 10000000),
+
+ /* XUSB */
+ FACT(TEGRA124_CLK_XUSB_SS_DIV2, "xusb_ss_div2", "xusb_ss", 1, 2),
+
+};
+
+static struct clk_mux_def tegra124_mux_clks[] = {
+ /* Core clocks. */
+ MUX(0, "pllD2_src", mux_pll_srcs, PLLD2_BASE, 25, 2),
+ MUX(0, "pllDP_src", mux_pll_srcs, PLLDP_BASE, 25, 2),
+ MUX(0, "pllC4_src", mux_pll_srcs, PLLC4_BASE, 25, 2),
+ MUX(0, "pllE_src1", mux_plle_src1, PLLE_AUX, 2, 1),
+ MUX(0, "pllE_src", mux_plle_src, PLLE_AUX, 28, 1),
+
+ /* Base peripheral clocks. */
+ MUX(0, "dsia_mux", mux_plld_out0_plld2_out0, PLLD_BASE, 25, 1),
+ MUX(0, "dsib_mux", mux_plld_out0_plld2_out0, PLLD2_BASE, 25, 1),
+
+ /* USB. */
+ MUX(TEGRA124_CLK_XUSB_HS_SRC, "xusb_hs", mux_xusb_hs, CLK_SOURCE_XUSB_SS, 25, 1),
+ MUX(0, "xusb_ss_mux", mux_xusb_ss, CLK_SOURCE_XUSB_SS, 24, 1),
+
+};
+
+static struct clk_gate_def tegra124_gate_clks[] = {
+ /* Core clocks. */
+ GATE_PLL(0, "pllC_out1", "pllC_out1_div", PLLC_OUT, 0),
+ GATE_PLL(0, "pllM_out1", "pllM_out1_div", PLLM_OUT, 0),
+ GATE_PLL(TEGRA124_CLK_PLL_U_480M, "pllU_480", "pllU_out", PLLU_BASE, 22),
+ GATE_PLL(0, "pllP_outX0", "pllP_outX0_div", PLLP_RESHIFT, 0),
+ GATE_PLL(0, "pllP_out1", "pllP_out1_div", PLLP_OUTA, 0),
+ GATE_PLL(0, "pllP_out2", "pllP_out2_div", PLLP_OUTA, 16),
+ GATE_PLL(0, "pllP_out3", "pllP_out3_div", PLLP_OUTB, 0),
+ GATE_PLL(0, "pllP_out4", "pllP_out4_div", PLLP_OUTB, 16),
+ GATE_PLL(0, "pllP_out5", "pllP_out5_div", PLLP_OUTC, 16),
+ GATE_PLL(0, "pllA_out0", "pllA_out1_div", PLLA_OUT, 0),
+
+ /* Base peripheral clocks. */
+ GATE(TEGRA124_CLK_CML0, "cml0", "pllE_out0", PLLE_AUX, 0),
+ GATE(TEGRA124_CLK_CML1, "cml1", "pllE_out0", PLLE_AUX, 1),
+ GATE_INV(TEGRA124_CLK_HCLK, "hclk", "hclk_div", CLK_SYSTEM_RATE, 7),
+ GATE_INV(TEGRA124_CLK_PCLK, "pclk", "pclk_div", CLK_SYSTEM_RATE, 3),
+};
+
+static struct clk_div_def tegra124_div_clks[] = {
+ /* Core clocks. */
+ DIV7_1(0, "pllC_out1_div", "pllC_out0", PLLC_OUT, 2),
+ DIV7_1(0, "pllM_out1_div", "pllM_out0", PLLM_OUT, 8),
+ DIV7_1(0, "pllP_outX0_div", "pllP_out0", PLLP_RESHIFT, 2),
+ DIV7_1(0, "pllP_out1_div", "pllP_out0", PLLP_OUTA, 8),
+ DIV7_1(0, "pllP_out2_div", "pllP_out0", PLLP_OUTA, 24),
+ DIV7_1(0, "pllP_out3_div", "pllP_out0", PLLP_OUTB, 8),
+ DIV7_1(0, "pllP_out4_div", "pllP_out0", PLLP_OUTB, 24),
+ DIV7_1(0, "pllP_out5_div", "pllP_out0", PLLP_OUTC, 24),
+ DIV7_1(0, "pllA_out1_div", "pllA_out", PLLA_OUT, 8),
+
+ /* Base peripheral clocks. */
+ DIV(0, "hclk_div", "sclk", CLK_SYSTEM_RATE, 4, 2, 0),
+ DIV(0, "pclk_div", "hclk", CLK_SYSTEM_RATE, 0, 2, 0),
+};
+
+/* Initial setup table. */
+static struct tegra124_init_item clk_init_table[] = {
+ /* clock, partent, frequency, enable */
+ {"uarta", "pllP_out0", 408000000, 0},
+ {"uartb", "pllP_out0", 408000000, 0},
+ {"uartc", "pllP_out0", 408000000, 0},
+ {"uartd", "pllP_out0", 408000000, 0},
+ {"pllA_out", NULL, 282240000, 1},
+ {"pllA_out0", NULL, 11289600, 1},
+ {"extperiph1", "pllA_out0", 0, 1},
+ {"i2s0", "pllA_out0", 11289600, 0},
+ {"i2s1", "pllA_out0", 11289600, 0},
+ {"i2s2", "pllA_out0", 11289600, 0},
+ {"i2s3", "pllA_out0", 11289600, 0},
+ {"i2s4", "pllA_out0", 11289600, 0},
+ {"vde", "pllP_out0", 0, 0},
+ {"host1x", "pllP_out0", 136000000, 1},
+ {"sclk", "pllP_out2", 102000000, 1},
+ {"dvfs_soc", "pllP_out0", 51000000, 1},
+ {"dvfs_ref", "pllP_out0", 51000000, 1},
+ {"pllC_out0", NULL, 600000000, 0},
+ {"pllC_out1", NULL, 100000000, 0},
+ {"spi4", "pllP_out0", 12000000, 1},
+ {"tsec", "pllC3_out0", 0, 0},
+ {"msenc", "pllC3_out0", 0, 0},
+ {"pllREFE_out", NULL, 672000000, 0},
+ {"pc_xusb_ss", "pllU_480", 120000000, 0},
+ {"xusb_ss", "pc_xusb_ss", 120000000, 0},
+ {"pc_xusb_fs", "pllU_48", 48000000, 0},
+ {"xusb_hs", "pllU_60", 60000000, 0},
+ {"pc_xusb_falcon", "pllREFE_out", 224000000, 0},
+ {"xusb_core_host", "pllREFE_out", 112000000, 0},
+ {"sata", "pllP_out0", 102000000, 0},
+ {"sata_oob", "pllP_out0", 204000000, 0},
+ {"sata_cold", NULL, 0, 1},
+ {"emc", NULL, 0, 1},
+ {"mselect", NULL, 0, 1},
+ {"csite", NULL, 0, 1},
+ {"tsensor", "clk_m", 400000, 0},
+
+ /* tegra124 only*/
+ {"soc_therm", "pllP_out0", 51000000, 0},
+ {"cclk_g", NULL, 0, 1},
+ {"hda", "pllP_out0", 102000000, 0},
+ {"hda2codec_2x", "pllP_out0", 48000000, 0},
+};
+
+static void
+init_divs(struct tegra124_car_softc *sc, struct clk_div_def *clks, int nclks)
+{
+ int i, rv;
+
+ for (i = 0; i < nclks; i++) {
+ rv = clknode_div_register(sc->clkdom, clks + i);
+ if (rv != 0)
+ panic("clk_div_register failed");
+ }
+}
+
+static void
+init_gates(struct tegra124_car_softc *sc, struct clk_gate_def *clks, int nclks)
+{
+ int i, rv;
+
+ for (i = 0; i < nclks; i++) {
+ rv = clknode_gate_register(sc->clkdom, clks + i);
+ if (rv != 0)
+ panic("clk_gate_register failed");
+ }
+}
+
+static void
+init_muxes(struct tegra124_car_softc *sc, struct clk_mux_def *clks, int nclks)
+{
+ int i, rv;
+
+ for (i = 0; i < nclks; i++) {
+ rv = clknode_mux_register(sc->clkdom, clks + i);
+ if (rv != 0)
+ panic("clk_mux_register failed");
+ }
+}
+
+static void
+init_fixeds(struct tegra124_car_softc *sc, struct clk_fixed_def *clks,
+ int nclks)
+{
+ int i, rv;
+ uint32_t val;
+ int osc_idx;
+
+ CLKDEV_READ_4(sc->dev, OSC_CTRL, &val);
+ osc_idx = val >> OSC_CTRL_OSC_FREQ_SHIFT;
+ fixed_clk_m.freq = osc_freqs[osc_idx];
+ if (fixed_clk_m.freq == 0)
+ panic("Undefined input frequency");
+ rv = clknode_fixed_register(sc->clkdom, &fixed_clk_m);
+ if (rv != 0) panic("clk_fixed_register failed");
+
+ val = (val >> OSC_CTRL_PLL_REF_DIV_SHIFT) & 3;
+ fixed_osc_div_clk.div = 1 << val;
+ rv = clknode_fixed_register(sc->clkdom, &fixed_osc_div_clk);
+ if (rv != 0) panic("clk_fixed_register failed");
+
+ for (i = 0; i < nclks; i++) {
+ rv = clknode_fixed_register(sc->clkdom, clks + i);
+ if (rv != 0)
+ panic("clk_fixed_register failed");
+ }
+}
+
+static void
+postinit_clock(struct tegra124_car_softc *sc)
+{
+ int i;
+ struct tegra124_init_item *tbl;
+ struct clknode *clknode;
+ int rv;
+
+ for (i = 0; i < nitems(clk_init_table); i++) {
+ tbl = &clk_init_table[i];
+
+ clknode = clknode_find_by_name(tbl->name);
+ if (clknode == NULL) {
+ device_printf(sc->dev, "Cannot find clock %s\n",
+ tbl->name);
+ continue;
+ }
+ if (tbl->parent != NULL) {
+ rv = clknode_set_parent_by_name(clknode, tbl->parent);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot set parent for %s (to %s): %d\n",
+ tbl->name, tbl->parent, rv);
+ continue;
+ }
+ }
+ if (tbl->frequency != 0) {
+ rv = clknode_set_freq(clknode, tbl->frequency, 0 , 9999);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot set frequency for %s: %d\n",
+ tbl->name, rv);
+ continue;
+ }
+ }
+ if (tbl->enable!= 0) {
+ rv = clknode_enable(clknode);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot enable %s: %d\n", tbl->name, rv);
+ continue;
+ }
+ }
+ }
+}
+
+static void
+register_clocks(device_t dev)
+{
+ struct tegra124_car_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->clkdom = clkdom_create(dev);
+ if (sc->clkdom == NULL)
+ panic("clkdom == NULL");
+
+ tegra124_init_plls(sc);
+ init_fixeds(sc, tegra124_fixed_clks, nitems(tegra124_fixed_clks));
+ init_muxes(sc, tegra124_mux_clks, nitems(tegra124_mux_clks));
+ init_divs(sc, tegra124_div_clks, nitems(tegra124_div_clks));
+ init_gates(sc, tegra124_gate_clks, nitems(tegra124_gate_clks));
+ tegra124_periph_clock(sc);
+ tegra124_super_mux_clock(sc);
+ clkdom_finit(sc->clkdom);
+ clkdom_xlock(sc->clkdom);
+ postinit_clock(sc);
+ clkdom_unlock(sc->clkdom);
+ if (bootverbose)
+ clkdom_dump(sc->clkdom);
+}
+
+static int
+tegra124_car_clkdev_read_4(device_t dev, bus_addr_t addr, uint32_t *val)
+{
+ struct tegra124_car_softc *sc;
+
+ sc = device_get_softc(dev);
+ *val = bus_read_4(sc->mem_res, addr);
+ return (0);
+}
+
+static int
+tegra124_car_clkdev_write_4(device_t dev, bus_addr_t addr, uint32_t val)
+{
+ struct tegra124_car_softc *sc;
+
+ sc = device_get_softc(dev);
+ bus_write_4(sc->mem_res, addr, val);
+ return (0);
+}
+
+static int
+tegra124_car_clkdev_modify_4(device_t dev, bus_addr_t addr, uint32_t clear_mask,
+ uint32_t set_mask)
+{
+ struct tegra124_car_softc *sc;
+ uint32_t reg;
+
+ sc = device_get_softc(dev);
+ reg = bus_read_4(sc->mem_res, addr);
+ reg &= ~clear_mask;
+ reg |= set_mask;
+ bus_write_4(sc->mem_res, addr, reg);
+ return (0);
+}
+
+static void
+tegra124_car_clkdev_device_lock(device_t dev)
+{
+ struct tegra124_car_softc *sc;
+
+ sc = device_get_softc(dev);
+ mtx_lock(&sc->mtx);
+}
+
+static void
+tegra124_car_clkdev_device_unlock(device_t dev)
+{
+ struct tegra124_car_softc *sc;
+
+ sc = device_get_softc(dev);
+ mtx_unlock(&sc->mtx);
+}
+
+static int
+tegra124_car_detach(device_t dev)
+{
+
+ device_printf(dev, "Error: Clock driver cannot be detached\n");
+ return (EBUSY);
+}
+
+static int
+tegra124_car_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) {
+ device_set_desc(dev, "Tegra Clock Driver");
+ return (BUS_PROBE_DEFAULT);
+ }
+
+ return (ENXIO);
+}
+
+static int
+tegra124_car_attach(device_t dev)
+{
+ struct tegra124_car_softc *sc = device_get_softc(dev);
+ int rid, rv;
+
+ sc->dev = dev;
+
+ mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+ sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+
+ /* Resource setup. */
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (!sc->mem_res) {
+ device_printf(dev, "cannot allocate memory resource\n");
+ rv = ENXIO;
+ goto fail;
+ }
+
+ register_clocks(dev);
+ hwreset_register_ofw_provider(dev);
+ return (0);
+
+fail:
+ if (sc->mem_res)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+
+ return (rv);
+}
+
+static int
+tegra124_car_hwreset_assert(device_t dev, intptr_t id, bool value)
+{
+ struct tegra124_car_softc *sc = device_get_softc(dev);
+
+ return (tegra124_hwreset_by_idx(sc, id, value));
+}
+
+static device_method_t tegra124_car_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, tegra124_car_probe),
+ DEVMETHOD(device_attach, tegra124_car_attach),
+ DEVMETHOD(device_detach, tegra124_car_detach),
+
+ /* Clkdev interface*/
+ DEVMETHOD(clkdev_read_4, tegra124_car_clkdev_read_4),
+ DEVMETHOD(clkdev_write_4, tegra124_car_clkdev_write_4),
+ DEVMETHOD(clkdev_modify_4, tegra124_car_clkdev_modify_4),
+ DEVMETHOD(clkdev_device_lock, tegra124_car_clkdev_device_lock),
+ DEVMETHOD(clkdev_device_unlock, tegra124_car_clkdev_device_unlock),
+
+ /* Reset interface */
+ DEVMETHOD(hwreset_assert, tegra124_car_hwreset_assert),
+
+ DEVMETHOD_END
+};
+
+static devclass_t tegra124_car_devclass;
+static DEFINE_CLASS_0(car, tegra124_car_driver, tegra124_car_methods,
+ sizeof(struct tegra124_car_softc));
+EARLY_DRIVER_MODULE(tegra124_car, simplebus, tegra124_car_driver,
+ tegra124_car_devclass, NULL, NULL, BUS_PASS_TIMER);
diff --git a/sys/arm/nvidia/tegra124/tegra124_car.h b/sys/arm/nvidia/tegra124/tegra124_car.h
new file mode 100644
index 000000000000..2dcf06184aed
--- /dev/null
+++ b/sys/arm/nvidia/tegra124/tegra124_car.h
@@ -0,0 +1,336 @@
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _TEGRA124_CAR_
+#define _TEGRA124_CAR_
+
+#include "clkdev_if.h"
+
+#define RD4(sc, reg, val) CLKDEV_READ_4((sc)->clkdev, reg, val)
+#define WR4(sc, reg, val) CLKDEV_WRITE_4((sc)->clkdev, reg, val)
+#define MD4(sc, reg, mask, set) CLKDEV_MODIFY_4((sc)->clkdev, reg, mask, set)
+#define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev)
+#define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev)
+
+#define RST_DEVICES_L 0x004
+#define RST_DEVICES_H 0x008
+#define RST_DEVICES_U 0x00C
+#define CLK_OUT_ENB_L 0x010
+#define CLK_OUT_ENB_H 0x014
+#define CLK_OUT_ENB_U 0x018
+#define CCLK_BURST_POLICY 0x020
+#define SUPER_CCLK_DIVIDER 0x024
+#define SCLK_BURST_POLICY 0x028
+#define SUPER_SCLK_DIVIDER 0x02c
+#define CLK_SYSTEM_RATE 0x030
+
+#define OSC_CTRL 0x050
+ #define OSC_CTRL_OSC_FREQ_SHIFT 28
+ #define OSC_CTRL_PLL_REF_DIV_SHIFT 26
+
+#define PLLE_SS_CNTL 0x068
+#define PLLE_SS_CNTL_SSCINCINTRV_MASK (0x3f << 24)
+#define PLLE_SS_CNTL_SSCINCINTRV_VAL (0x20 << 24)
+#define PLLE_SS_CNTL_SSCINC_MASK (0xff << 16)
+#define PLLE_SS_CNTL_SSCINC_VAL (0x1 << 16)
+#define PLLE_SS_CNTL_SSCINVERT (1 << 15)
+#define PLLE_SS_CNTL_SSCCENTER (1 << 14)
+#define PLLE_SS_CNTL_SSCBYP (1 << 12)
+#define PLLE_SS_CNTL_INTERP_RESET (1 << 11)
+#define PLLE_SS_CNTL_BYPASS_SS (1 << 10)
+#define PLLE_SS_CNTL_SSCMAX_MASK 0x1ff
+#define PLLE_SS_CNTL_SSCMAX_VAL 0x25
+#define PLLE_SS_CNTL_DISABLE (PLLE_SS_CNTL_BYPASS_SS | \
+ PLLE_SS_CNTL_INTERP_RESET | \
+ PLLE_SS_CNTL_SSCBYP)
+#define PLLE_SS_CNTL_COEFFICIENTS_MASK (PLLE_SS_CNTL_SSCMAX_MASK | \
+ PLLE_SS_CNTL_SSCINC_MASK | \
+ PLLE_SS_CNTL_SSCINCINTRV_MASK)
+#define PLLE_SS_CNTL_COEFFICIENTS_VAL (PLLE_SS_CNTL_SSCMAX_VAL | \
+ PLLE_SS_CNTL_SSCINC_VAL | \
+ PLLE_SS_CNTL_SSCINCINTRV_VAL)
+
+#define PLLC_BASE 0x080
+#define PLLC_OUT 0x084
+#define PLLC_MISC2 0x088
+#define PLLC_MISC 0x08c
+#define PLLM_BASE 0x090
+#define PLLM_OUT 0x094
+#define PLLM_MISC 0x09c
+#define PLLP_BASE 0x0a0
+#define PLLP_MISC 0x0ac
+#define PLLP_OUTA 0x0a4
+#define PLLP_OUTB 0x0a8
+#define PLLA_BASE 0x0b0
+#define PLLA_OUT 0x0b4
+#define PLLA_MISC 0x0bc
+#define PLLU_BASE 0x0c0
+#define PLLU_MISC 0x0cc
+#define PLLD_BASE 0x0d0
+#define PLLD_MISC 0x0dc
+#define PLLX_BASE 0x0e0
+#define PLLX_MISC 0x0e4
+#define PLLE_BASE 0x0e8
+#define PLLE_BASE_LOCK_OVERRIDE (1 << 29)
+#define PLLE_BASE_DIVCML_SHIFT 24
+#define PLLE_BASE_DIVCML_MASK 0xf
+
+#define PLLE_MISC 0x0ec
+#define PLLE_MISC_SETUP_BASE_SHIFT 16
+#define PLLE_MISC_SETUP_BASE_MASK (0xffff << PLLE_MISC_SETUP_BASE_SHIFT)
+#define PLLE_MISC_READY (1 << 15)
+#define PLLE_MISC_IDDQ_SWCTL (1 << 14)
+#define PLLE_MISC_IDDQ_OVERRIDE_VALUE (1 << 13)
+#define PLLE_MISC_LOCK (1 << 11)
+#define PLLE_MISC_REF_ENABLE (1 << 10)
+#define PLLE_MISC_LOCK_ENABLE (1 << 9)
+#define PLLE_MISC_PTS (1 << 8)
+#define PLLE_MISC_VREG_BG_CTRL_SHIFT 4
+#define PLLE_MISC_VREG_BG_CTRL_MASK (3 << PLLE_MISC_VREG_BG_CTRL_SHIFT)
+#define PLLE_MISC_VREG_CTRL_SHIFT 2
+#define PLLE_MISC_VREG_CTRL_MASK (2 << PLLE_MISC_VREG_CTRL_SHIFT)
+
+#define CLK_SOURCE_I2S1 0x100
+#define CLK_SOURCE_I2S2 0x104
+#define CLK_SOURCE_SPDIF_OUT 0x108
+#define CLK_SOURCE_SPDIF_IN 0x10c
+#define CLK_SOURCE_PWM 0x110
+#define CLK_SOURCE_SPI2 0x118
+#define CLK_SOURCE_SPI3 0x11c
+#define CLK_SOURCE_I2C1 0x124
+#define CLK_SOURCE_I2C5 0x128
+#define CLK_SOURCE_SPI1 0x134
+#define CLK_SOURCE_DISP1 0x138
+#define CLK_SOURCE_DISP2 0x13c
+#define CLK_SOURCE_ISP 0x144
+#define CLK_SOURCE_VI 0x148
+#define CLK_SOURCE_SDMMC1 0x150
+#define CLK_SOURCE_SDMMC2 0x154
+#define CLK_SOURCE_SDMMC4 0x164
+#define CLK_SOURCE_VFIR 0x168
+#define CLK_SOURCE_HSI 0x174
+#define CLK_SOURCE_UARTA 0x178
+#define CLK_SOURCE_UARTB 0x17c
+#define CLK_SOURCE_HOST1X 0x180
+#define CLK_SOURCE_HDMI 0x18c
+#define CLK_SOURCE_I2C2 0x198
+#define CLK_SOURCE_EMC 0x19c
+#define CLK_SOURCE_UARTC 0x1a0
+#define CLK_SOURCE_VI_SENSOR 0x1a8
+#define CLK_SOURCE_SPI4 0x1b4
+#define CLK_SOURCE_I2C3 0x1b8
+#define CLK_SOURCE_SDMMC3 0x1bc
+#define CLK_SOURCE_UARTD 0x1c0
+#define CLK_SOURCE_VDE 0x1c8
+#define CLK_SOURCE_OWR 0x1cc
+#define CLK_SOURCE_NOR 0x1d0
+#define CLK_SOURCE_CSITE 0x1d4
+#define CLK_SOURCE_I2S0 0x1d8
+#define CLK_SOURCE_DTV 0x1dc
+#define CLK_SOURCE_MSENC 0x1f0
+#define CLK_SOURCE_TSEC 0x1f4
+#define CLK_SOURCE_SPARE2 0x1f8
+
+#define CLK_OUT_ENB_X 0x280
+#define RST_DEVICES_X 0x28C
+
+#define RST_DEVICES_V 0x358
+#define RST_DEVICES_W 0x35C
+#define CLK_OUT_ENB_V 0x360
+#define CLK_OUT_ENB_W 0x364
+#define CCLKG_BURST_POLICY 0x368
+#define SUPER_CCLKG_DIVIDER 0x36C
+#define CCLKLP_BURST_POLICY 0x370
+#define SUPER_CCLKLP_DIVIDER 0x374
+
+#define CLK_SOURCE_MSELECT 0x3b4
+#define CLK_SOURCE_TSENSOR 0x3b8
+#define CLK_SOURCE_I2S3 0x3bc
+#define CLK_SOURCE_I2S4 0x3c0
+#define CLK_SOURCE_I2C4 0x3c4
+#define CLK_SOURCE_SPI5 0x3c8
+#define CLK_SOURCE_SPI6 0x3cc
+#define CLK_SOURCE_AUDIO 0x3d0
+#define CLK_SOURCE_DAM0 0x3d8
+#define CLK_SOURCE_DAM1 0x3dc
+#define CLK_SOURCE_DAM2 0x3e0
+#define CLK_SOURCE_HDA2CODEC_2X 0x3e4
+#define CLK_SOURCE_ACTMON 0x3e8
+#define CLK_SOURCE_EXTPERIPH1 0x3ec
+#define CLK_SOURCE_EXTPERIPH2 0x3f0
+#define CLK_SOURCE_EXTPERIPH3 0x3f4
+#define CLK_SOURCE_I2C_SLOW 0x3fc
+
+#define CLK_SOURCE_SYS 0x400
+#define CLK_SOURCE_SOR0 0x414
+#define CLK_SOURCE_SATA_OOB 0x420
+#define CLK_SOURCE_SATA 0x424
+#define CLK_SOURCE_HDA 0x428
+#define UTMIP_PLL_CFG0 0x480
+#define UTMIP_PLL_CFG1 0x484
+#define UTMIP_PLL_CFG1_FORCE_PLLU_POWERUP (1 << 17)
+#define UTMIP_PLL_CFG1_FORCE_PLLU_POWERDOWN (1 << 16)
+#define UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERUP (1 << 15)
+#define UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN (1 << 14)
+#define UTMIP_PLL_CFG1_FORCE_PLL_ACTIVE_POWERDOWN (1 << 12)
+#define UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(x) (((x) & 0x1f) << 6)
+#define UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0)
+
+#define UTMIP_PLL_CFG2 0x488
+#define UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(x) (((x) & 0x3f) << 18)
+#define UTMIP_PLL_CFG2_STABLE_COUNT(x) (((x) & 0xffff) << 6)
+#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_C_POWERDOWN (1 << 4)
+#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERDOWN (1 << 2)
+#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERDOWN (1 << 0)
+
+#define PLLE_AUX 0x48c
+#define PLLE_AUX_PLLRE_SEL (1 << 28)
+#define PLLE_AUX_SEQ_START_STATE (1 << 25)
+#define PLLE_AUX_SEQ_ENABLE (1 << 24)
+#define PLLE_AUX_SS_SWCTL (1 << 6)
+#define PLLE_AUX_ENABLE_SWCTL (1 << 4)
+#define PLLE_AUX_USE_LOCKDET (1 << 3)
+#define PLLE_AUX_PLLP_SEL (1 << 2)
+
+#define SATA_PLL_CFG0 0x490
+#define SATA_PLL_CFG0_SEQ_START_STATE (1 << 25)
+#define SATA_PLL_CFG0_SEQ_ENABLE (1 << 24)
+#define SATA_PLL_CFG0_SEQ_PADPLL_PD_INPUT_VALUE (1 << 7)
+#define SATA_PLL_CFG0_SEQ_LANE_PD_INPUT_VALUE (1 << 6)
+#define SATA_PLL_CFG0_SEQ_RESET_INPUT_VALUE (1 << 5)
+#define SATA_PLL_CFG0_SEQ_IN_SWCTL (1 << 4)
+#define SATA_PLL_CFG0_PADPLL_USE_LOCKDET (1 << 2)
+#define SATA_PLL_CFG0_PADPLL_RESET_OVERRIDE_VALUE (1 << 1)
+#define SATA_PLL_CFG0_PADPLL_RESET_SWCTL (1 << 0)
+
+#define SATA_PLL_CFG1 0x494
+#define PCIE_PLL_CFG0 0x498
+#define PCIE_PLL_CFG0_SEQ_START_STATE (1 << 25)
+#define PCIE_PLL_CFG0_SEQ_ENABLE (1 << 24)
+
+#define PLLD2_BASE 0x4b8
+#define PLLD2_MISC 0x4bc
+#define UTMIP_PLL_CFG3 0x4c0
+#define PLLRE_BASE 0x4c4
+#define PLLRE_MISC 0x4c8
+#define PLLC2_BASE 0x4e8
+#define PLLC2_MISC 0x4ec
+#define PLLC3_BASE 0x4fc
+
+#define PLLC3_MISC 0x500
+#define PLLX_MISC2 0x514
+#define PLLX_MISC2 0x514
+#define PLLX_MISC3 0x518
+#define PLLX_MISC3_DYNRAMP_STEPB_MASK 0xFF
+#define PLLX_MISC3_DYNRAMP_STEPB_SHIFT 24
+#define PLLX_MISC3_DYNRAMP_STEPA_MASK 0xFF
+#define PLLX_MISC3_DYNRAMP_STEPA_SHIFT 16
+#define PLLX_MISC3_NDIV_NEW_MASK 0xFF
+#define PLLX_MISC3_NDIV_NEW_SHIFT 8
+#define PLLX_MISC3_EN_FSTLCK (1 << 5)
+#define PLLX_MISC3_LOCK_OVERRIDE (1 << 4)
+#define PLLX_MISC3_PLL_FREQLOCK (1 << 3)
+#define PLLX_MISC3_DYNRAMP_DONE (1 << 2)
+#define PLLX_MISC3_CLAMP_NDIV (1 << 1)
+#define PLLX_MISC3_EN_DYNRAMP (1 << 0)
+#define XUSBIO_PLL_CFG0 0x51c
+#define XUSBIO_PLL_CFG0_SEQ_START_STATE (1 << 25)
+#define XUSBIO_PLL_CFG0_SEQ_ENABLE (1 << 24)
+#define XUSBIO_PLL_CFG0_PADPLL_USE_LOCKDET (1 << 6)
+#define XUSBIO_PLL_CFG0_CLK_ENABLE_SWCTL (1 << 2)
+#define XUSBIO_PLL_CFG0_PADPLL_RESET_SWCTL (1 << 0)
+
+#define PLLP_RESHIFT 0x528
+#define UTMIPLL_HW_PWRDN_CFG0 0x52c
+#define UTMIPLL_HW_PWRDN_CFG0_SEQ_START_STATE (1 << 25)
+#define UTMIPLL_HW_PWRDN_CFG0_SEQ_ENABLE (1 << 24)
+#define UTMIPLL_HW_PWRDN_CFG0_USE_LOCKDET (1 << 6)
+#define UTMIPLL_HW_PWRDN_CFG0_SEQ_RESET_INPUT_VALUE (1 << 5)
+#define UTMIPLL_HW_PWRDN_CFG0_SEQ_IN_SWCTL (1 << 4)
+#define UTMIPLL_HW_PWRDN_CFG0_CLK_ENABLE_SWCTL (1 << 2)
+#define UTMIPLL_HW_PWRDN_CFG0_IDDQ_OVERRIDE (1 << 1)
+#define UTMIPLL_HW_PWRDN_CFG0_IDDQ_SWCTL (1 << 0)
+
+#define PLLDP_BASE 0x590
+#define PLLDP_MISC 0x594
+#define PLLC4_BASE 0x5a4
+#define PLLC4_MISC 0x5a8
+
+#define CLK_SOURCE_XUSB_CORE_HOST 0x600
+#define CLK_SOURCE_XUSB_FALCON 0x604
+#define CLK_SOURCE_XUSB_FS 0x608
+#define CLK_SOURCE_XUSB_CORE_DEV 0x60c
+#define CLK_SOURCE_XUSB_SS 0x610
+#define CLK_SOURCE_CILAB 0x614
+#define CLK_SOURCE_CILCD 0x618
+#define CLK_SOURCE_CILE 0x61c
+#define CLK_SOURCE_DSIA_LP 0x620
+#define CLK_SOURCE_DSIB_LP 0x624
+#define CLK_SOURCE_ENTROPY 0x628
+#define CLK_SOURCE_DVFS_REF 0x62c
+#define CLK_SOURCE_DVFS_SOC 0x630
+#define CLK_SOURCE_TRACECLKIN 0x634
+#define CLK_SOURCE_ADX 0x638
+#define CLK_SOURCE_AMX 0x63c
+#define CLK_SOURCE_EMC_LATENCY 0x640
+#define CLK_SOURCE_SOC_THERM 0x644
+#define CLK_SOURCE_VI_SENSOR2 0x658
+#define CLK_SOURCE_I2C6 0x65c
+#define CLK_SOURCE_EMC_DLL 0x664
+#define CLK_SOURCE_HDMI_AUDIO 0x668
+#define CLK_SOURCE_CLK72MHZ 0x66c
+#define CLK_SOURCE_ADX1 0x670
+#define CLK_SOURCE_AMX1 0x674
+#define CLK_SOURCE_VIC 0x678
+#define PLLP_OUTC 0x67c
+#define PLLP_MISC1 0x680
+
+struct tegra124_car_softc {
+ device_t dev;
+ struct resource * mem_res;
+ struct mtx mtx;
+ struct clkdom *clkdom;
+ int type;
+};
+
+struct tegra124_init_item {
+ char *name;
+ char *parent;
+ uint64_t frequency;
+ int enable;
+};
+
+void tegra124_init_plls(struct tegra124_car_softc *sc);
+
+void tegra124_periph_clock(struct tegra124_car_softc *sc);
+void tegra124_super_mux_clock(struct tegra124_car_softc *sc);
+
+int tegra124_hwreset_by_idx(struct tegra124_car_softc *sc, intptr_t idx,
+ bool reset);
+
+#endif /*_TEGRA124_CAR_*/
diff --git a/sys/arm/nvidia/tegra124/tegra124_clk_per.c b/sys/arm/nvidia/tegra124/tegra124_clk_per.c
new file mode 100644
index 000000000000..4d68e8b22daf
--- /dev/null
+++ b/sys/arm/nvidia/tegra124/tegra124_clk_per.c
@@ -0,0 +1,825 @@
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+
+#include <dev/extres/clk/clk.h>
+
+#include <dt-bindings/clock/tegra124-car.h>
+#include "tegra124_car.h"
+
+/* The TEGRA124_CLK_XUSB_GATE is missing in current
+ * DT bindings, define it localy
+ */
+#ifdef TEGRA124_CLK_XUSB_GATE
+#error "TEGRA124_CLK_XUSB_GATE is now defined, revisit XUSB code!"
+#else
+#define TEGRA124_CLK_XUSB_GATE 143
+#endif
+
+/* Bits in base register. */
+#define PERLCK_AMUX_MASK 0x0F
+#define PERLCK_AMUX_SHIFT 16
+#define PERLCK_AMUX_DIS (1 << 20)
+#define PERLCK_UDIV_DIS (1 << 24)
+#define PERLCK_ENA_MASK (1 << 28)
+#define PERLCK_MUX_SHIFT 29
+#define PERLCK_MUX_MASK 0x07
+
+struct periph_def {
+ struct clknode_init_def clkdef;
+ uint32_t base_reg;
+ uint32_t div_width;
+ uint32_t div_mask;
+ uint32_t div_f_width;
+ uint32_t div_f_mask;
+ uint32_t flags;
+};
+
+struct pgate_def {
+ struct clknode_init_def clkdef;
+ uint32_t idx;
+ uint32_t flags;
+};
+#define PLIST(x) static const char *x[]
+
+#define GATE(_id, cname, plist, _idx) \
+{ \
+ .clkdef.id = TEGRA124_CLK_##_id, \
+ .clkdef.name = cname, \
+ .clkdef.parent_names = (const char *[]){plist}, \
+ .clkdef.parent_cnt = 1, \
+ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \
+ .idx = _idx, \
+ .flags = 0, \
+}
+
+/* Sources for multiplexors. */
+PLIST(mux_a_N_audio_N_p_N_clkm) =
+ {"pllA_out0", NULL, "audio", NULL,
+ "pllP_out0", NULL, "clk_m"};
+PLIST(mux_a_N_audio0_N_p_N_clkm) =
+ {"pllA_out0", NULL, "audio0", NULL,
+ "pllP_out0", NULL, "clk_m"};
+PLIST(mux_a_N_audio1_N_p_N_clkm) =
+ {"pllA_out0", NULL, "audio1", NULL,
+ "pllP_out0", NULL, "clk_m"};
+PLIST(mux_a_N_audio2_N_p_N_clkm) =
+ {"pllA_out0", NULL, "audio2", NULL,
+ "pllP_out0", NULL, "clk_m"};
+PLIST(mux_a_N_audio3_N_p_N_clkm) =
+ {"pllA_out0", NULL, "audio3", NULL,
+ "pllP_out0", NULL, "clk_m"};
+PLIST(mux_a_N_audio4_N_p_N_clkm) =
+ {"pllA_out0", NULL, "audio4", NULL,
+ "pllP_out0", NULL, "clk_m"};
+PLIST(mux_a_clks_p_clkm_e) =
+ {"pllA_out0", "clk_s", "pllP_out0",
+ "clk_m", "pllE_out0"};
+PLIST(mux_a_c2_c_c3_p_N_clkm) =
+ {"pllA_out0", "pllC2_out0", "pllC_out0", "pllC3_out0",
+ "pllP_out0", NULL, "clk_m"};
+
+PLIST(mux_m_c_p_a_c2_c3) =
+ {"pllM_out0", "pllC_out0", "pllP_out0", "pllA_out0",
+ "pllC2_out0", "pllC3_out0"};
+PLIST(mux_m_c_p_a_c2_c3_clkm) =
+ {"pllM_out0", "pllC_out0", "pllP_out0", "pllA_out0",
+ "pllC2_out0", "pllC3_out0", "clk_m"};
+PLIST(mux_m_c_p_a_c2_c3_clkm_c4) =
+ {"pllM_out0", "pllC_out0", "pllP_out0", "pllA_out0",
+ "pllC2_out0", "pllC3_out0", "clk_m", "pllC4_out0"};
+PLIST(mux_m_c_p_clkm_mud_c2_c3) =
+ {"pllM_out0", "pllC_out0", "pllP_out0", "clk_m",
+ "pllM_UD", "pllC2_out0", "pllC3_out0"};
+PLIST(mux_m_c_p_clkm_mud_c2_c3_cud) =
+ {"pllM_out0", "pllC_out0", "pllP_out0", "clk_m",
+ "pllM_UD", "pllC2_out0", "pllC3_out0", "pllC_UD"};
+
+PLIST(mux_m_c2_c_c3_p_N_a) =
+ {"pllM_out0", "pllC2_out0", "pllC_out0", "pllC3_out0",
+ "pllP_out0", NULL, "pllA_out0"};
+PLIST(mux_m_c2_c_c3_p_N_a_c4) =
+ {"pllM_out0", "pllC2_out0", "pllC_out0", "pllC3_out0",
+ NULL, "pllA_out0", "pllC4_out0"};
+
+PLIST(mux_p_N_c_N_N_N_clkm) =
+ {"pllP_out0", NULL, "pllC_out0", NULL,
+ NULL, NULL, "clk_m"};
+PLIST(mux_p_N_c_N_m_N_clkm) =
+ {"pllP_out0", NULL, "pllC_out0", NULL,
+ "pllM_out0", NULL, "clk_m"};
+PLIST(mux_p_c_c2_clkm) =
+ {"pllP_out0", "pllC_out0", "pllC2_out0", "clk_m"};
+PLIST(mux_p_c2_c_c3_m) =
+ {"pllP_out0", "pllC2_out0", "pllC_out0", "pllC3_out0",
+ "pllM_out0"};
+PLIST(mux_p_c2_c_c3_m_N_clkm) =
+ {"pllP_out0", "pllC2_out0", "pllC_out0", "pllC3_out0",
+ "pllM_out0", NULL, "clk_m"};
+PLIST(mux_p_c2_c_c3_m_e_clkm) =
+ {"pllP_out0", "pllC2_out0", "pllC_out0", "pllC3_out0",
+ "pllM_out0", "pllE_out0", "clk_m"};
+PLIST(mux_p_c2_c_c3_m_a_clkm) =
+ {"pllP_out0", "pllC2_out0", "pllC_out0", "pllC3_out0",
+ "pllM_out0", "pllA_out0", "clk_m"};
+PLIST(mux_p_c2_c_c3_m_clks_clkm) =
+ {"pllP_out0", "pllC2_out0", "pllC_out0", "pllC3_out0",
+ "pllM_out0", "clk_s", "clk_m"};
+PLIST(mux_p_c2_c_c3_clks_N_clkm) =
+ {"pllP_out0", "pllC2_out0", "pllC_out0", "pllC3_out0",
+ "clk_s", NULL, "clk_m"};
+PLIST(mux_p_c2_c_c3_clkm_N_clks) =
+ {"pllP_out0", "pllC2_out0", "pllC_out0", "pllC3_out0",
+ "clk_m", NULL, "clk_s"};
+PLIST(mux_p_clkm_clks_E) =
+ {"pllP_out0", "clk_m", "clk_s", "pllE_out0"};
+PLIST(mux_p_m_d_a_c_d2_clkm) =
+ {"pllP_out0", "pllM_out0", "pllD_out0", "pllA_out0",
+ "pllC_out0", "pllD2_out0", "clk_m"};
+
+PLIST(mux_clkm_N_u48_N_p_N_u480) =
+ {"clk_m", NULL, "pllU_48", NULL,
+ "pllP_out0", NULL, "pllU_480"};
+PLIST(mux_clkm_p_c2_c_c3_refre) =
+ {"clk_m", "pllP_out0", "pllC2_out0", "pllC_out0",
+ "pllC3_out0", "pllREFE_out"};
+PLIST(mux_clkm_refe_clks_u480_c_c2_c3_oscdiv) =
+ {"clk_m", "pllREFE_out", "clk_s", "pllU_480",
+ "pllC_out0", "pllC2_out0", "pllC3_out0", "osc_div_clk"};
+
+PLIST(mux_sep_audio) =
+ {"pllA_out0", "pllC2_out0", "pllC_out0", "pllC3_out0",
+ "pllP_out0", NULL, "clk_m", NULL,
+ "spdif_in", "i2s0", "i2s1", "i2s2",
+ "i2s4", "pllA_out0", "ext_vimclk"};
+
+static uint32_t clk_enable_reg[] = {
+ CLK_OUT_ENB_L,
+ CLK_OUT_ENB_H,
+ CLK_OUT_ENB_U,
+ CLK_OUT_ENB_V,
+ CLK_OUT_ENB_W,
+ CLK_OUT_ENB_X,
+};
+
+static uint32_t clk_reset_reg[] = {
+ RST_DEVICES_L,
+ RST_DEVICES_H,
+ RST_DEVICES_U,
+ RST_DEVICES_V,
+ RST_DEVICES_W,
+ RST_DEVICES_X,
+};
+
+#define L(n) ((0 * 32) + (n))
+#define H(n) ((1 * 32) + (n))
+#define U(n) ((2 * 32) + (n))
+#define V(n) ((3 * 32) + (n))
+#define W(n) ((4 * 32) + (n))
+#define X(n) ((5 * 32) + (n))
+
+static struct pgate_def pgate_def[] = {
+ /* bank L -> 0-31 */
+ /* GATE(CPU, "cpu", "clk_m", L(0)), */
+ GATE(ISPB, "ispb", "clk_m", L(3)),
+ GATE(RTC, "rtc", "clk_s", L(4)),
+ GATE(TIMER, "timer", "clk_m", L(5)),
+ GATE(UARTA, "uarta", "pc_uarta" , L(6)),
+ GATE(UARTB, "uartb", "pc_uartb", L(7)),
+ GATE(VFIR, "vfir", "pc_vfir", L(7)),
+ /* GATE(GPIO, "gpio", "clk_m", L(8)), */
+ GATE(SDMMC2, "sdmmc2", "pc_sdmmc2", L(9)),
+ GATE(SPDIF_OUT, "spdif_out", "pc_spdif_out", L(10)),
+ GATE(SPDIF_IN, "spdif_in", "pc_spdif_in", L(10)),
+ GATE(I2S1, "i2s1", "pc_i2s1", L(11)),
+ GATE(I2C1, "i2c1", "pc_i2c1", L(12)),
+ GATE(SDMMC1, "sdmmc1", "pc_sdmmc1", L(14)),
+ GATE(SDMMC4, "sdmmc4", "pc_sdmmc4", L(15)),
+ GATE(PWM, "pwm", "pc_pwm", L(17)),
+ GATE(I2S2, "i2s2", "pc_i2s2", L(18)),
+ GATE(VI, "vi", "pc_vi", L(20)),
+ GATE(USBD, "usbd", "clk_m", L(22)),
+ GATE(ISP, "isp", "pc_isp", L(23)),
+ GATE(DISP2, "disp2", "pc_disp2", L(26)),
+ GATE(DISP1, "disp1", "pc_disp1", L(27)),
+ GATE(HOST1X, "host1x", "pc_host1x", L(28)),
+ GATE(VCP, "vcp", "clk_m", L(29)),
+ GATE(I2S0, "i2s0", "pc_i2s0", L(30)),
+ /* GATE(CACHE2, "ccache2", "clk_m", L(31)), */
+
+ /* bank H -> 32-63 */
+ GATE(MC, "mem", "clk_m", H(0)),
+ /* GATE(AHBDMA, "ahbdma", "clk_m", H(1)), */
+ GATE(APBDMA, "apbdma", "clk_m", H(2)),
+ GATE(KBC, "kbc", "clk_s", H(4)),
+ /* GATE(STAT_MON, "stat_mon", "clk_s", H(5)), */
+ /* GATE(PMC, "pmc", "clk_s", H(6)), */
+ GATE(FUSE, "fuse", "clk_m", H(7)),
+ GATE(KFUSE, "kfuse", "clk_m", H(8)),
+ GATE(SBC1, "spi1", "pc_spi1", H(9)),
+ GATE(NOR, "snor", "pc_snor", H(10)),
+ /* GATE(JTAG2TBC, "jtag2tbc", "clk_m", H(11)), */
+ GATE(SBC2, "spi2", "pc_spi2", H(12)),
+ GATE(SBC3, "spi3", "pc_spi3", H(14)),
+ GATE(I2C5, "i2c5", "pc_i2c5", H(15)),
+ GATE(DSIA, "dsia", "dsia_mux", H(16)),
+ GATE(MIPI, "hsi", "pc_hsi", H(18)),
+ GATE(HDMI, "hdmi", "pc_hdmi", H(19)),
+ GATE(CSI, "csi", "pllP_out3", H(20)),
+ GATE(I2C2, "i2c2", "pc_i2c2", H(22)),
+ GATE(UARTC, "uartc", "pc_uartc", H(23)),
+ GATE(MIPI_CAL, "mipi_cal", "clk_m", H(24)),
+ GATE(EMC, "emc", "pc_emc_2x", H(25)),
+ GATE(USB2, "usb2", "clk_m", H(26)),
+ GATE(USB3, "usb3", "clk_m", H(27)),
+ GATE(VDE, "vde", "pc_vde", H(29)),
+ GATE(BSEA, "bsea", "clk_m", H(30)),
+ GATE(BSEV, "bsev", "clk_m", H(31)),
+
+ /* bank U -> 64-95 */
+ GATE(UARTD, "uartd", "pc_uartd", U(1)),
+ GATE(I2C3, "i2c3", "pc_i2c3", U(3)),
+ GATE(SBC4, "spi4", "pc_spi4", U(4)),
+ GATE(SDMMC3, "sdmmc3", "pc_sdmmc3", U(5)),
+ GATE(PCIE, "pcie", "clk_m", U(6)),
+ GATE(OWR, "owr", "pc_owr", U(7)),
+ GATE(AFI, "afi", "clk_m", U(8)),
+ GATE(CSITE, "csite", "pc_csite", U(9)),
+ /* GATE(AVPUCQ, "avpucq", clk_m, U(11)), */
+ GATE(TRACE, "traceclkin", "pc_traceclkin", U(13)),
+ GATE(SOC_THERM, "soc_therm", "pc_soc_therm", U(14)),
+ GATE(DTV, "dtv", "clk_m", U(15)),
+ GATE(I2CSLOW, "i2c_slow", "pc_i2c_slow", U(17)),
+ GATE(DSIB, "dsib", "dsib_mux", U(18)),
+ GATE(TSEC, "tsec", "pc_tsec", U(19)),
+ /* GATE(IRAMA, "irama", "clk_m", U(20)), */
+ /* GATE(IRAMB, "iramb", "clk_m", U(21)), */
+ /* GATE(IRAMC, "iramc", "clk_m", U(22)), */
+ /* GATE(IRAMD, "iramd", "clk_m", U(23)), */
+ /* GATE(CRAM2, "cram2", "clk_m", U(24)), */
+ GATE(XUSB_HOST, "xusb_core_host", "pc_xusb_core_host", U(25)),
+ /* GATE(M_DOUBLER, "m_doubler", "clk_m", U(26)), */
+ GATE(MSENC, "msenc", "pc_msenc", U(27)),
+ GATE(CSUS, "sus_out", "clk_m", U(28)),
+ /* GATE(DEVD2_OUT, "devd2_out", "clk_m", U(29)), */
+ /* GATE(DEVD1_OUT, "devd1_out", "clk_m", U(30)), */
+ GATE(XUSB_DEV, "xusb_core_dev", "pc_xusb_core_dev", U(31)),
+
+ /* bank V -> 96-127 */
+ /* GATE(CPUG, "cpug", "clk_m", V(0)), */
+ /* GATE(CPULP, "cpuLP", "clk_m", V(1)), */
+ GATE(MSELECT, "mselect", "pc_mselect", V(3)),
+ GATE(TSENSOR, "tsensor", "pc_tsensor", V(4)),
+ GATE(I2S3, "i2s3", "pc_i2s3", V(5)),
+ GATE(I2S4, "i2s4", "pc_i2s4", V(6)),
+ GATE(I2C4, "i2c4", "pc_i2c4", V(7)),
+ GATE(SBC5, "spi5", "pc_spi5", V(8)),
+ GATE(SBC6, "spi6", "pc_spi6", V(9)),
+ GATE(D_AUDIO, "audio", "pc_audio", V(10)),
+ GATE(APBIF, "apbif", "clk_m", V(11)),
+ GATE(DAM0, "dam0", "pc_dam0", V(12)),
+ GATE(DAM1, "dam1", "pc_dam1", V(13)),
+ GATE(DAM2, "dam2", "pc_dam2", V(14)),
+ GATE(HDA2CODEC_2X, "hda2codec_2x", "pc_hda2codec_2x", V(15)),
+ /* GATE(ATOMICS, "atomics", "clk_m", V(16)), */
+ /* GATE(SPDIF_DOUBLER, "spdif_doubler", "clk_m", V(22)), */
+ GATE(ACTMON, "actmon", "pc_actmon", V(23)),
+ GATE(EXTERN1, "extperiph1", "pc_extperiph1", V(24)),
+ GATE(EXTERN2, "extperiph2", "pc_extperiph2", V(25)),
+ GATE(EXTERN3, "extperiph3", "pc_extperiph3", V(26)),
+ GATE(SATA_OOB, "sata_oob", "pc_sata_oob", V(27)),
+ GATE(SATA, "sata", "pc_sata", V(28)),
+ GATE(HDA, "hda", "pc_hda", V(29)),
+
+ /* bank W -> 128-159*/
+ GATE(HDA2HDMI, "hda2hdmi", "clk_m", W(0)),
+ GATE(SATA_COLD, "sata_cold", "clk_m", W(1)), /* Reset only */
+ /* GATE(PCIERX0, "pcierx0", "clk_m", W(2)), */
+ /* GATE(PCIERX1, "pcierx1", "clk_m", W(3)), */
+ /* GATE(PCIERX2, "pcierx2", "clk_m", W(4)), */
+ /* GATE(PCIERX3, "pcierx3", "clk_m", W(5)), */
+ /* GATE(PCIERX4, "pcierx4", "clk_m", W(6)), */
+ /* GATE(PCIERX5, "pcierx5", "clk_m", W(7)), */
+ /* GATE(CEC, "cec", "clk_m", W(8)), */
+ /* GATE(PCIE2_IOBIST, "pcie2_iobist", "clk_m", W(9)), */
+ /* GATE(EMC_IOBIST, "emc_iobist", "clk_m", W(10)), */
+ /* GATE(HDMI_IOBIST, "hdmi_iobist", "clk_m", W(11)), */
+ /* GATE(SATA_IOBIST, "sata_iobist", "clk_m", W(12)), */
+ /* GATE(MIPI_IOBIST, "mipi_iobist", "clk_m", W(13)), */
+ GATE(XUSB_GATE, "xusb_gate", "clk_m", W(15)),
+ GATE(CILAB, "cilab", "pc_cilab", W(16)),
+ GATE(CILCD, "cilcd", "pc_cilcd", W(17)),
+ GATE(CILE, "cile", "pc_cile", W(18)),
+ GATE(DSIALP, "dsia_lp", "pc_dsia_lp", W(19)),
+ GATE(DSIBLP, "dsib_lp", "pc_dsib_lp", W(20)),
+ GATE(ENTROPY, "entropy", "pc_entropy", W(21)),
+ GATE(AMX, "amx", "pc_amx", W(25)),
+ GATE(ADX, "adx", "pc_adx", W(26)),
+ GATE(DFLL_REF, "dvfs_ref", "pc_dvfs_ref", W(27)),
+ GATE(DFLL_SOC, "dvfs_soc", "pc_dvfs_soc", W(27)),
+ GATE(XUSB_SS, "xusb_ss", "xusb_ss_mux", W(28)),
+ /* GATE(EMC_LATENCY, "emc_latency", "pc_emc_latency", W(29)), */
+
+ /* bank X -> 160-191*/
+ /* GATE(SPARE, "spare", "clk_m", X(0)), */
+ /* GATE(CAM_MCLK, "CAM_MCLK", "clk_m", X(4)), */
+ /* GATE(CAM_MCLK2, "CAM_MCLK2", "clk_m", X(5)), */
+ GATE(I2C6, "i2c6", "pc_i2c6", X(6)),
+ GATE(VIM2_CLK, "vim2_clk", "clk_m", X(11)),
+ /* GATE(EMC_DLL, "emc_dll", "pc_emc_dll", X(14)), */
+ GATE(HDMI_AUDIO, "hdmi_audio", "pc_hdmi_audio", X(16)),
+ GATE(CLK72MHZ, "clk72mhz", "pc_clk72mhz", X(17)),
+ GATE(VIC03, "vic", "pc_vic", X(18)),
+ GATE(ADX1, "adx1", "pc_adx1", X(20)),
+ GATE(DPAUX, "dpaux", "clk_m", X(21)),
+ GATE(SOR0_LVDS, "sor0", "pc_sor0", X(22)),
+ GATE(GPU, "gpu", "osc_div_clk", X(24)),
+ GATE(AMX1, "amx1", "pc_amx1", X(26)),
+};
+
+/* Peripheral clock clock */
+#define DCF_HAVE_MUX 0x0100 /* Block with multipexor */
+#define DCF_HAVE_ENA 0x0200 /* Block with enable bit */
+#define DCF_HAVE_DIV 0x0400 /* Block with divider */
+
+/* Mark block with additional bits / functionality. */
+#define DCF_IS_MASK 0x00FF
+#define DCF_IS_UART 0x0001
+#define DCF_IS_VI 0x0002
+#define DCF_IS_HOST1X 0x0003
+#define DCF_IS_XUSB_SS 0x0004
+#define DCF_IS_EMC_DLL 0x0005
+#define DCF_IS_SATA 0x0006
+#define DCF_IS_VIC 0x0007
+#define DCF_IS_AUDIO 0x0008
+#define DCF_IS_SOR0 0x0009
+#define DCF_IS_EMC 0x000A
+
+/* Basic pheripheral clock */
+#define PER_CLK(_id, cn, pl, r, diw, fiw, f) \
+{ \
+ .clkdef.id = _id, \
+ .clkdef.name = cn, \
+ .clkdef.parent_names = pl, \
+ .clkdef.parent_cnt = nitems(pl), \
+ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \
+ .base_reg = r, \
+ .div_width = diw, \
+ .div_f_width = fiw, \
+ .flags = f, \
+}
+
+/* Mux with fractional 8.1 divider. */
+#define CLK_8_1(id, cn, pl, r, f) \
+ PER_CLK(id, cn, pl, r, 8, 1, (f) | DCF_HAVE_MUX | DCF_HAVE_DIV)
+
+/* Mux with fractional 16.1 divider. */
+#define CLK16_1(id, cn, pl, r, f) \
+ PER_CLK(id, cn, pl, r, 16, 1, (f) | DCF_HAVE_MUX | DCF_HAVE_DIV)
+/* Mux with integer 16bits divider. */
+#define CLK16_0(id, cn, pl, r, f) \
+ PER_CLK(id, cn, pl, r, 16, 0, (f) | DCF_HAVE_MUX | DCF_HAVE_DIV)
+/* Mux wihout divider. */
+#define CLK_0_0(id, cn, pl, r, f) \
+ PER_CLK(id, cn, pl, r, 0, 0, (f) | DCF_HAVE_MUX)
+
+static struct periph_def periph_def[] = {
+ CLK_8_1(0, "pc_i2s1", mux_a_N_audio1_N_p_N_clkm, CLK_SOURCE_I2S1, DCF_HAVE_ENA),
+ CLK_8_1(0, "pc_i2s2", mux_a_N_audio2_N_p_N_clkm, CLK_SOURCE_I2S2, DCF_HAVE_ENA),
+ CLK_8_1(0, "pc_spdif_out", mux_a_N_audio_N_p_N_clkm, CLK_SOURCE_SPDIF_OUT, 0),
+ CLK_8_1(0, "pc_spdif_in", mux_p_c2_c_c3_m, CLK_SOURCE_SPDIF_IN, 0),
+ CLK_8_1(0, "pc_pwm", mux_p_c2_c_c3_clks_N_clkm, CLK_SOURCE_PWM, 0),
+ CLK_8_1(0, "pc_spi2", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_SPI2, 0),
+ CLK_8_1(0, "pc_spi3", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_SPI3, 0),
+ CLK16_0(0, "pc_i2c5", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_I2C5, 0),
+ CLK16_0(0, "pc_i2c1", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_I2C1, 0),
+ CLK_8_1(0, "pc_spi1", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_SPI1, 0),
+ CLK_0_0(0, "pc_disp1", mux_p_m_d_a_c_d2_clkm, CLK_SOURCE_DISP1, 0),
+ CLK_0_0(0, "pc_disp2", mux_p_m_d_a_c_d2_clkm, CLK_SOURCE_DISP2, 0),
+ CLK_8_1(0, "pc_isp", mux_m_c_p_a_c2_c3_clkm_c4, CLK_SOURCE_ISP, 0),
+ CLK_8_1(0, "pc_vi", mux_m_c2_c_c3_p_N_a_c4, CLK_SOURCE_VI, DCF_IS_VI),
+ CLK_8_1(0, "pc_sdmmc1", mux_p_c2_c_c3_m_e_clkm, CLK_SOURCE_SDMMC1, 0),
+ CLK_8_1(0, "pc_sdmmc2", mux_p_c2_c_c3_m_e_clkm, CLK_SOURCE_SDMMC2, 0),
+ CLK_8_1(0, "pc_sdmmc4", mux_p_c2_c_c3_m_e_clkm, CLK_SOURCE_SDMMC4, 0),
+ CLK_8_1(0, "pc_vfir", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_VFIR, 0),
+ CLK_8_1(0, "pc_hsi", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_HSI, 0),
+ CLK16_1(0, "pc_uarta", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_UARTA, DCF_IS_UART),
+ CLK16_1(0, "pc_uartb", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_UARTB, DCF_IS_UART),
+ CLK_8_1(0, "pc_host1x", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_HOST1X, DCF_IS_HOST1X),
+ CLK_8_1(0, "pc_hdmi", mux_p_m_d_a_c_d2_clkm, CLK_SOURCE_HDMI, 0),
+ CLK16_0(0, "pc_i2c2", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_I2C2, 0),
+ CLK_8_1(0, "pc_emc_2x", mux_m_c_p_clkm_mud_c2_c3_cud, CLK_SOURCE_EMC, DCF_IS_EMC),
+ CLK16_1(0, "pc_uartc", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_UARTC, DCF_IS_UART),
+ CLK_8_1(0, "pc_vi_sensor", mux_m_c2_c_c3_p_N_a, CLK_SOURCE_VI_SENSOR, 0),
+ CLK_8_1(0, "pc_spi4", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_SPI4, 0),
+ CLK16_0(0, "pc_i2c3", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_I2C3, 0),
+ CLK_8_1(0, "pc_sdmmc3", mux_p_c2_c_c3_m_e_clkm, CLK_SOURCE_SDMMC3, 0),
+ CLK16_1(0, "pc_uartd", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_UARTD, DCF_IS_UART),
+ CLK_8_1(0, "pc_vde", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_VDE, 0),
+ CLK_8_1(0, "pc_owr", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_OWR, 0),
+ CLK_8_1(0, "pc_snor", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_NOR, 0),
+ CLK_8_1(0, "pc_csite", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_CSITE, 0),
+ CLK_8_1(0, "pc_i2s0", mux_a_N_audio0_N_p_N_clkm, CLK_SOURCE_I2S0, 0),
+/* DTV xxx */
+ CLK_8_1(0, "pc_msenc", mux_m_c2_c_c3_p_N_a, CLK_SOURCE_MSENC, 0),
+ CLK_8_1(0, "pc_tsec", mux_p_c2_c_c3_m_a_clkm, CLK_SOURCE_TSEC, 0),
+/* SPARE2 */
+
+ CLK_8_1(0, "pc_mselect", mux_p_c2_c_c3_m_clks_clkm, CLK_SOURCE_MSELECT, 0),
+ CLK_8_1(0, "pc_tsensor", mux_p_c2_c_c3_clkm_N_clks, CLK_SOURCE_TSENSOR, 0),
+ CLK_8_1(0, "pc_i2s3", mux_a_N_audio3_N_p_N_clkm, CLK_SOURCE_I2S3, DCF_HAVE_ENA),
+ CLK_8_1(0, "pc_i2s4", mux_a_N_audio4_N_p_N_clkm, CLK_SOURCE_I2S4, DCF_HAVE_ENA),
+ CLK16_0(0, "pc_i2c4", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_I2C4, 0),
+ CLK_8_1(0, "pc_spi5", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_SPI5, 0),
+ CLK_8_1(0, "pc_spi6", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_SPI6, 0),
+ CLK_8_1(0, "pc_audio", mux_sep_audio, CLK_SOURCE_AUDIO, DCF_IS_AUDIO),
+ CLK_8_1(0, "pc_dam0", mux_sep_audio, CLK_SOURCE_DAM0, DCF_IS_AUDIO),
+ CLK_8_1(0, "pc_dam1", mux_sep_audio, CLK_SOURCE_DAM1, DCF_IS_AUDIO),
+ CLK_8_1(0, "pc_dam2", mux_sep_audio, CLK_SOURCE_DAM2, DCF_IS_AUDIO),
+ CLK_8_1(0, "pc_hda2codec_2x", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_HDA2CODEC_2X, 0),
+ CLK_8_1(0, "pc_actmon", mux_p_c2_c_c3_clks_N_clkm, CLK_SOURCE_ACTMON, 0),
+ CLK_8_1(0, "pc_extperiph1", mux_a_clks_p_clkm_e, CLK_SOURCE_EXTPERIPH1, 0),
+ CLK_8_1(0, "pc_extperiph2", mux_a_clks_p_clkm_e, CLK_SOURCE_EXTPERIPH2, 0),
+ CLK_8_1(0, "pc_extperiph3", mux_a_clks_p_clkm_e, CLK_SOURCE_EXTPERIPH3, 0),
+ CLK_8_1(0, "pc_i2c_slow", mux_p_c2_c_c3_clks_N_clkm, CLK_SOURCE_I2C_SLOW, 0),
+/* SYS */
+ CLK_8_1(0, "pc_sor0", mux_p_m_d_a_c_d2_clkm, CLK_SOURCE_SOR0, DCF_IS_SOR0),
+ CLK_8_1(0, "pc_sata_oob", mux_p_N_c_N_m_N_clkm, CLK_SOURCE_SATA_OOB, 0),
+ CLK_8_1(0, "pc_sata", mux_p_N_c_N_m_N_clkm, CLK_SOURCE_SATA, DCF_IS_SATA),
+ CLK_8_1(0, "pc_hda", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_HDA, 0),
+ CLK_8_1(TEGRA124_CLK_XUSB_HOST_SRC,
+ "pc_xusb_core_host", mux_clkm_p_c2_c_c3_refre, CLK_SOURCE_XUSB_CORE_HOST, 0),
+ CLK_8_1(TEGRA124_CLK_XUSB_FALCON_SRC,
+ "pc_xusb_falcon", mux_clkm_p_c2_c_c3_refre, CLK_SOURCE_XUSB_FALCON, 0),
+ CLK_8_1(TEGRA124_CLK_XUSB_FS_SRC,
+ "pc_xusb_fs", mux_clkm_N_u48_N_p_N_u480, CLK_SOURCE_XUSB_FS, 0),
+ CLK_8_1(TEGRA124_CLK_XUSB_DEV_SRC,
+ "pc_xusb_core_dev", mux_clkm_p_c2_c_c3_refre, CLK_SOURCE_XUSB_CORE_DEV, 0),
+ CLK_8_1(TEGRA124_CLK_XUSB_SS_SRC,
+ "pc_xusb_ss", mux_clkm_refe_clks_u480_c_c2_c3_oscdiv, CLK_SOURCE_XUSB_SS, DCF_IS_XUSB_SS),
+ CLK_8_1(0, "pc_cilab", mux_p_N_c_N_N_N_clkm, CLK_SOURCE_CILAB, 0),
+ CLK_8_1(0, "pc_cilcd", mux_p_N_c_N_N_N_clkm, CLK_SOURCE_CILCD, 0),
+ CLK_8_1(0, "pc_cile", mux_p_N_c_N_N_N_clkm, CLK_SOURCE_CILE, 0),
+ CLK_8_1(0, "pc_dsia_lp", mux_p_N_c_N_N_N_clkm, CLK_SOURCE_DSIA_LP, 0),
+ CLK_8_1(0, "pc_dsib_lp", mux_p_N_c_N_N_N_clkm, CLK_SOURCE_DSIB_LP, 0),
+ CLK_8_1(0, "pc_entropy", mux_p_clkm_clks_E, CLK_SOURCE_ENTROPY, 0),
+ CLK_8_1(0, "pc_dvfs_ref", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_DVFS_REF, DCF_HAVE_ENA),
+ CLK_8_1(0, "pc_dvfs_soc", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_DVFS_SOC, DCF_HAVE_ENA),
+ CLK_8_1(0, "pc_traceclkin", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_TRACECLKIN, 0),
+ CLK_8_1(0, "pc_adx", mux_a_c2_c_c3_p_N_clkm, CLK_SOURCE_ADX, DCF_HAVE_ENA),
+ CLK_8_1(0, "pc_amx", mux_a_c2_c_c3_p_N_clkm, CLK_SOURCE_AMX, DCF_HAVE_ENA),
+ CLK_8_1(0, "pc_emc_latency", mux_m_c_p_clkm_mud_c2_c3, CLK_SOURCE_EMC_LATENCY, 0),
+ CLK_8_1(0, "pc_soc_therm", mux_m_c_p_a_c2_c3, CLK_SOURCE_SOC_THERM, 0),
+ CLK_8_1(0, "pc_vi_sensor2", mux_m_c2_c_c3_p_N_a, CLK_SOURCE_VI_SENSOR2, 0),
+ CLK16_0(0, "pc_i2c6", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_I2C6, 0),
+ CLK_8_1(0, "pc_emc_dll", mux_m_c_p_clkm_mud_c2_c3, CLK_SOURCE_EMC_DLL, DCF_IS_EMC_DLL),
+ CLK_8_1(0, "pc_hdmi_audio", mux_p_c_c2_clkm, CLK_SOURCE_HDMI_AUDIO, 0),
+ CLK_8_1(0, "pc_clk72mhz", mux_p_c_c2_clkm, CLK_SOURCE_CLK72MHZ, 0),
+ CLK_8_1(0, "pc_adx1", mux_a_c2_c_c3_p_N_clkm, CLK_SOURCE_ADX1, DCF_HAVE_ENA),
+ CLK_8_1(0, "pc_amx1", mux_a_c2_c_c3_p_N_clkm, CLK_SOURCE_AMX1, DCF_HAVE_ENA),
+ CLK_8_1(0, "pc_vic", mux_m_c_p_a_c2_c3_clkm, CLK_SOURCE_VIC, DCF_IS_VIC),
+};
+
+static int periph_init(struct clknode *clk, device_t dev);
+static int periph_recalc(struct clknode *clk, uint64_t *freq);
+static int periph_set_freq(struct clknode *clk, uint64_t fin,
+ uint64_t *fout, int flags, int *stop);
+static int periph_set_mux(struct clknode *clk, int idx);
+
+struct periph_sc {
+ device_t clkdev;
+ uint32_t base_reg;
+ uint32_t div_shift;
+ uint32_t div_width;
+ uint32_t div_mask;
+ uint32_t div_f_width;
+ uint32_t div_f_mask;
+ uint32_t flags;
+
+ uint32_t divider;
+ int mux;
+};
+
+static clknode_method_t periph_methods[] = {
+ /* Device interface */
+ CLKNODEMETHOD(clknode_init, periph_init),
+ CLKNODEMETHOD(clknode_recalc_freq, periph_recalc),
+ CLKNODEMETHOD(clknode_set_freq, periph_set_freq),
+ CLKNODEMETHOD(clknode_set_mux, periph_set_mux),
+ CLKNODEMETHOD_END
+};
+DEFINE_CLASS_1(tegra124_periph, tegra124_periph_class, periph_methods,
+ sizeof(struct periph_sc), clknode_class);
+
+static int
+periph_init(struct clknode *clk, device_t dev)
+{
+ struct periph_sc *sc;
+ uint32_t reg;
+ sc = clknode_get_softc(clk);
+
+ DEVICE_LOCK(sc);
+ if (sc->flags & DCF_HAVE_ENA)
+ MD4(sc, sc->base_reg, PERLCK_ENA_MASK, PERLCK_ENA_MASK);
+
+ RD4(sc, sc->base_reg, &reg);
+ DEVICE_UNLOCK(sc);
+
+ /* Stnadard mux. */
+ if (sc->flags & DCF_HAVE_MUX)
+ sc->mux = (reg >> PERLCK_MUX_SHIFT) & PERLCK_MUX_MASK;
+ else
+ sc->mux = 0;
+ if (sc->flags & DCF_HAVE_DIV)
+ sc->divider = (reg & sc->div_mask) + 2;
+ else
+ sc->divider = 1;
+ if ((sc->flags & DCF_IS_MASK) == DCF_IS_UART) {
+ if (!(reg & PERLCK_UDIV_DIS))
+ sc->divider = 2;
+ }
+
+ /* AUDIO MUX */
+ if ((sc->flags & DCF_IS_MASK) == DCF_IS_AUDIO) {
+ if (!(reg & PERLCK_AMUX_DIS) && (sc->mux == 7)) {
+ sc->mux = 8 +
+ ((reg >> PERLCK_AMUX_SHIFT) & PERLCK_MUX_MASK);
+ }
+ }
+ clknode_init_parent_idx(clk, sc->mux);
+ return(0);
+}
+
+static int
+periph_set_mux(struct clknode *clk, int idx)
+{
+ struct periph_sc *sc;
+ uint32_t reg;
+
+ sc = clknode_get_softc(clk);
+ if (!(sc->flags & DCF_HAVE_MUX))
+ return (ENXIO);
+
+ sc->mux = idx;
+ DEVICE_LOCK(sc);
+ RD4(sc, sc->base_reg, &reg);
+ reg &= ~(PERLCK_MUX_MASK << PERLCK_MUX_SHIFT);
+ if ((sc->flags & DCF_IS_MASK) == DCF_IS_AUDIO) {
+ reg &= ~PERLCK_AMUX_DIS;
+ reg &= ~(PERLCK_MUX_MASK << PERLCK_AMUX_SHIFT);
+
+ if (idx <= 7) {
+ reg |= idx << PERLCK_MUX_SHIFT;
+ } else {
+ reg |= 7 << PERLCK_MUX_SHIFT;
+ reg |= (idx - 8) << PERLCK_AMUX_SHIFT;
+ }
+ } else {
+ reg |= idx << PERLCK_MUX_SHIFT;
+ }
+ WR4(sc, sc->base_reg, reg);
+ DEVICE_UNLOCK(sc);
+
+ return(0);
+}
+
+static int
+periph_recalc(struct clknode *clk, uint64_t *freq)
+{
+ struct periph_sc *sc;
+ uint32_t reg;
+
+ sc = clknode_get_softc(clk);
+
+ if (sc->flags & DCF_HAVE_DIV) {
+ DEVICE_LOCK(sc);
+ RD4(sc, sc->base_reg, &reg);
+ DEVICE_UNLOCK(sc);
+ *freq = (*freq << sc->div_f_width) / sc->divider;
+ }
+ return (0);
+}
+
+static int
+periph_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
+ int flags, int *stop)
+{
+ struct periph_sc *sc;
+ uint64_t tmp, divider;
+
+ sc = clknode_get_softc(clk);
+ if (!(sc->flags & DCF_HAVE_DIV)) {
+ *stop = 0;
+ return (0);
+ }
+
+ tmp = fin << sc->div_f_width;
+ divider = tmp / *fout;
+ if ((tmp % *fout) != 0)
+ divider++;
+
+ if (divider < (1 << sc->div_f_width))
+ divider = 1 << (sc->div_f_width - 1);
+
+ if (flags & CLK_SET_DRYRUN) {
+ if (((flags & (CLK_SET_ROUND_UP | CLK_SET_ROUND_DOWN)) == 0) &&
+ (*fout != (tmp / divider)))
+ return (ERANGE);
+ } else {
+ DEVICE_LOCK(sc);
+ MD4(sc, sc->base_reg, sc->div_mask,
+ (divider - (1 << sc->div_f_width)));
+ DEVICE_UNLOCK(sc);
+ sc->divider = divider;
+ }
+ *fout = tmp / divider;
+ *stop = 1;
+ return (0);
+}
+
+static int
+periph_register(struct clkdom *clkdom, struct periph_def *clkdef)
+{
+ struct clknode *clk;
+ struct periph_sc *sc;
+
+ clk = clknode_create(clkdom, &tegra124_periph_class, &clkdef->clkdef);
+ if (clk == NULL)
+ return (1);
+
+ sc = clknode_get_softc(clk);
+ sc->clkdev = clknode_get_device(clk);
+ sc->base_reg = clkdef->base_reg;
+ sc->div_width = clkdef->div_width;
+ sc->div_mask = (1 <<clkdef->div_width) - 1;
+ sc->div_f_width = clkdef->div_f_width;
+ sc->div_f_mask = (1 <<clkdef->div_f_width) - 1;
+ sc->flags = clkdef->flags;
+
+ clknode_register(clkdom, clk);
+ return (0);
+}
+
+/* -------------------------------------------------------------------------- */
+static int pgate_init(struct clknode *clk, device_t dev);
+static int pgate_set_gate(struct clknode *clk, bool enable);
+
+struct pgate_sc {
+ device_t clkdev;
+ uint32_t idx;
+ uint32_t flags;
+ uint32_t enabled;
+
+};
+
+static clknode_method_t pgate_methods[] = {
+ /* Device interface */
+ CLKNODEMETHOD(clknode_init, pgate_init),
+ CLKNODEMETHOD(clknode_set_gate, pgate_set_gate),
+ CLKNODEMETHOD_END
+};
+DEFINE_CLASS_1(tegra124_pgate, tegra124_pgate_class, pgate_methods,
+ sizeof(struct pgate_sc), clknode_class);
+
+static uint32_t
+get_enable_reg(int idx)
+{
+ KASSERT(idx / 32 < nitems(clk_enable_reg),
+ ("Invalid clock index for enable: %d", idx));
+ return (clk_enable_reg[idx / 32]);
+}
+
+static uint32_t
+get_reset_reg(int idx)
+{
+ KASSERT(idx / 32 < nitems(clk_reset_reg),
+ ("Invalid clock index for reset: %d", idx));
+ return (clk_reset_reg[idx / 32]);
+}
+
+static int
+pgate_init(struct clknode *clk, device_t dev)
+{
+ struct pgate_sc *sc;
+ uint32_t ena_reg, rst_reg, mask;
+
+ sc = clknode_get_softc(clk);
+ mask = 1 << (sc->idx % 32);
+
+ DEVICE_LOCK(sc);
+ RD4(sc, get_enable_reg(sc->idx), &ena_reg);
+ RD4(sc, get_reset_reg(sc->idx), &rst_reg);
+ DEVICE_UNLOCK(sc);
+
+ sc->enabled = ena_reg & mask ? 1 : 0;
+ clknode_init_parent_idx(clk, 0);
+
+ return(0);
+}
+
+static int
+pgate_set_gate(struct clknode *clk, bool enable)
+{
+ struct pgate_sc *sc;
+ uint32_t reg, mask, base_reg;
+
+ sc = clknode_get_softc(clk);
+ mask = 1 << (sc->idx % 32);
+ sc->enabled = enable;
+ base_reg = get_enable_reg(sc->idx);
+
+ DEVICE_LOCK(sc);
+ MD4(sc, base_reg, mask, enable ? mask : 0);
+ RD4(sc, base_reg, &reg);
+ DEVICE_UNLOCK(sc);
+
+ DELAY(2);
+ return(0);
+}
+
+int
+tegra124_hwreset_by_idx(struct tegra124_car_softc *sc, intptr_t idx, bool reset)
+{
+ uint32_t reg, mask, reset_reg;
+
+ mask = 1 << (idx % 32);
+ reset_reg = get_reset_reg(idx);
+
+ CLKDEV_DEVICE_LOCK(sc->dev);
+ CLKDEV_MODIFY_4(sc->dev, reset_reg, mask, reset ? mask : 0);
+ CLKDEV_READ_4(sc->dev, reset_reg, &reg);
+ CLKDEV_DEVICE_UNLOCK(sc->dev);
+
+ return(0);
+}
+
+static int
+pgate_register(struct clkdom *clkdom, struct pgate_def *clkdef)
+{
+ struct clknode *clk;
+ struct pgate_sc *sc;
+
+ clk = clknode_create(clkdom, &tegra124_pgate_class, &clkdef->clkdef);
+ if (clk == NULL)
+ return (1);
+
+ sc = clknode_get_softc(clk);
+ sc->clkdev = clknode_get_device(clk);
+ sc->idx = clkdef->idx;
+ sc->flags = clkdef->flags;
+
+ clknode_register(clkdom, clk);
+ return (0);
+}
+
+void
+tegra124_periph_clock(struct tegra124_car_softc *sc)
+{
+ int i, rv;
+
+ for (i = 0; i < nitems(periph_def); i++) {
+ rv = periph_register(sc->clkdom, &periph_def[i]);
+ if (rv != 0)
+ panic("tegra124_periph_register failed");
+ }
+ for (i = 0; i < nitems(pgate_def); i++) {
+ rv = pgate_register(sc->clkdom, &pgate_def[i]);
+ if (rv != 0)
+ panic("tegra124_pgate_register failed");
+ }
+
+}
diff --git a/sys/arm/nvidia/tegra124/tegra124_clk_pll.c b/sys/arm/nvidia/tegra124/tegra124_clk_pll.c
new file mode 100644
index 000000000000..82b34fd71203
--- /dev/null
+++ b/sys/arm/nvidia/tegra124/tegra124_clk_pll.c
@@ -0,0 +1,1143 @@
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+
+#include <dev/extres/clk/clk.h>
+
+#include <dt-bindings/clock/tegra124-car.h>
+#include "tegra124_car.h"
+
+/* #define TEGRA_PLL_DEBUG */
+#ifdef TEGRA_PLL_DEBUG
+#define dprintf(...) printf(__VA_ARGS__)
+#else
+#define dprintf(...)
+#endif
+
+/* All PLLs. */
+enum pll_type {
+ PLL_M,
+ PLL_X,
+ PLL_C,
+ PLL_C2,
+ PLL_C3,
+ PLL_C4,
+ PLL_P,
+ PLL_A,
+ PLL_U,
+ PLL_D,
+ PLL_D2,
+ PLL_DP,
+ PLL_E,
+ PLL_REFE};
+
+/* Common base register bits. */
+#define PLL_BASE_BYPASS (1U << 31)
+#define PLL_BASE_ENABLE (1 << 30)
+#define PLL_BASE_REFDISABLE (1 << 29)
+#define PLL_BASE_LOCK (1 << 27)
+#define PLL_BASE_DIVM_SHIFT 0
+#define PLL_BASE_DIVN_SHIFT 8
+
+#define PLLRE_MISC_LOCK (1 << 24)
+
+#define PLL_MISC_LOCK_ENABLE (1 << 18)
+#define PLLC_MISC_LOCK_ENABLE (1 << 24)
+#define PLLDU_MISC_LOCK_ENABLE (1 << 22)
+#define PLLRE_MISC_LOCK_ENABLE (1 << 30)
+#define PLLSS_MISC_LOCK_ENABLE (1 << 30)
+
+#define PLLC_IDDQ_BIT 26
+#define PLLX_IDDQ_BIT 3
+#define PLLRE_IDDQ_BIT 16
+#define PLLSS_IDDQ_BIT 19
+
+#define PLL_LOCK_TIMEOUT 5000
+
+/* Post divider <-> register value mapping. */
+struct pdiv_table {
+ uint32_t divider; /* real divider */
+ uint32_t value; /* register value */
+};
+
+/* Bits definition of M, N and P fields. */
+struct mnp_bits {
+ uint32_t m_width;
+ uint32_t n_width;
+ uint32_t p_width;
+ uint32_t p_shift;
+};
+
+struct clk_pll_def {
+ struct clknode_init_def clkdef;
+ enum pll_type type;
+ uint32_t base_reg;
+ uint32_t misc_reg;
+ uint32_t lock_mask;
+ uint32_t lock_enable;
+ uint32_t iddq_reg;
+ uint32_t iddq_mask;
+ uint32_t flags;
+ struct pdiv_table *pdiv_table;
+ struct mnp_bits mnp_bits;
+};
+
+#define PLL(_id, cname, pname) \
+ .clkdef.id = _id, \
+ .clkdef.name = cname, \
+ .clkdef.parent_names = (const char *[]){pname}, \
+ .clkdef.parent_cnt = 1, \
+ .clkdef.flags = CLK_NODE_STATIC_STRINGS
+
+/* Tegra K1 PLLs
+ PLLM: Clock source for EMC 2x clock
+ PLLX: Clock source for the fast CPU cluster and the shadow CPU
+ PLLC: Clock source for general use
+ PLLC2: Clock source for engine scaling
+ PLLC3: Clock source for engine scaling
+ PLLC4: Clock source for ISP/VI units
+ PLLP: Clock source for most peripherals
+ PLLA: Audio clock sources: (11.2896 MHz, 12.288 MHz, 24.576 MHz)
+ PLLU: Clock source for USB PHY, provides 12/60/480 MHz
+ PLLD: Clock sources for the DSI and display subsystem
+ PLLD2: Clock sources for the DSI and display subsystem
+ refPLLe:
+ PLLE: generate the 100 MHz reference clock for USB 3.0 (spread spectrum)
+ PLLDP: Clock source for eDP/LVDS (spread spectrum)
+
+ DFLLCPU: DFLL clock source for the fast CPU cluster
+ GPCPLL: Clock source for the GPU
+*/
+
+static struct pdiv_table pllm_map[] = {
+ {1, 0},
+ {2, 1},
+ {0, 0}
+};
+
+static struct pdiv_table pllxc_map[] = {
+ { 1, 0},
+ { 2, 1},
+ { 3, 2},
+ { 4, 3},
+ { 5, 4},
+ { 6, 5},
+ { 8, 6},
+ {10, 7},
+ {12, 8},
+ {16, 9},
+ {12, 10},
+ {16, 11},
+ {20, 12},
+ {24, 13},
+ {32, 14},
+ { 0, 0}
+};
+
+static struct pdiv_table pllc_map[] = {
+ { 1, 0},
+ { 2, 1},
+ { 3, 2},
+ { 4, 3},
+ { 6, 4},
+ { 8, 5},
+ {12, 6},
+ {16, 7},
+ { 0, 0}
+};
+
+static struct pdiv_table pll12g_ssd_esd_map[] = {
+ { 1, 0},
+ { 2, 1},
+ { 3, 2},
+ { 4, 3},
+ { 5, 4},
+ { 6, 5},
+ { 8, 6},
+ {10, 7},
+ {12, 8},
+ {16, 9},
+ {12, 10},
+ {16, 11},
+ {20, 12},
+ {24, 13},
+ {32, 14},
+ { 0, 0}
+};
+
+static struct pdiv_table pllu_map[] = {
+ {1, 1},
+ {2, 0},
+ {0, 0}
+};
+
+static struct pdiv_table pllrefe_map[] = {
+ {1, 0},
+ {2, 1},
+ {3, 2},
+ {4, 3},
+ {5, 4},
+ {6, 5},
+ {0, 0},
+};
+
+static struct clk_pll_def pll_clks[] = {
+/* PLLM: 880 MHz Clock source for EMC 2x clock */
+ {
+ PLL(TEGRA124_CLK_PLL_M, "pllM_out0", "osc_div_clk"),
+ .type = PLL_M,
+ .base_reg = PLLM_BASE,
+ .misc_reg = PLLM_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable = PLL_MISC_LOCK_ENABLE,
+ .pdiv_table = pllm_map,
+ .mnp_bits = {8, 8, 1, 20},
+ },
+/* PLLX: 1GHz Clock source for the fast CPU cluster and the shadow CPU */
+ {
+ PLL(TEGRA124_CLK_PLL_X, "pllX_out", "osc_div_clk"),
+ .type = PLL_X,
+ .base_reg = PLLX_BASE,
+ .misc_reg = PLLX_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable = PLL_MISC_LOCK_ENABLE,
+ .iddq_reg = PLLX_MISC3,
+ .iddq_mask = 1 << PLLX_IDDQ_BIT,
+ .pdiv_table = pllxc_map,
+ .mnp_bits = {8, 8, 4, 20},
+ },
+/* PLLC: 600 MHz Clock source for general use */
+ {
+ PLL(TEGRA124_CLK_PLL_C, "pllC_out0", "osc_div_clk"),
+ .type = PLL_C,
+ .base_reg = PLLC_BASE,
+ .misc_reg = PLLC_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable = PLLC_MISC_LOCK_ENABLE,
+ .iddq_reg = PLLC_MISC,
+ .iddq_mask = 1 << PLLC_IDDQ_BIT,
+ .pdiv_table = pllc_map,
+ .mnp_bits = {8, 8, 4, 20},
+ },
+/* PLLC2: 600 MHz Clock source for engine scaling */
+ {
+ PLL(TEGRA124_CLK_PLL_C2, "pllC2_out0", "osc_div_clk"),
+ .type = PLL_C2,
+ .base_reg = PLLC2_BASE,
+ .misc_reg = PLLC2_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable = PLL_MISC_LOCK_ENABLE,
+ .pdiv_table = pllc_map,
+ .mnp_bits = {2, 8, 3, 20},
+ },
+/* PLLC3: 600 MHz Clock source for engine scaling */
+ {
+ PLL(TEGRA124_CLK_PLL_C3, "pllC3_out0", "osc_div_clk"),
+ .type = PLL_C3,
+ .base_reg = PLLC3_BASE,
+ .misc_reg = PLLC3_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable = PLL_MISC_LOCK_ENABLE,
+ .pdiv_table = pllc_map,
+ .mnp_bits = {2, 8, 3, 20},
+ },
+/* PLLC4: 600 MHz Clock source for ISP/VI units */
+ {
+ PLL(TEGRA124_CLK_PLL_C4, "pllC4_out0", "pllC4_src"),
+ .type = PLL_C4,
+ .base_reg = PLLC4_BASE,
+ .misc_reg = PLLC4_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable = PLLSS_MISC_LOCK_ENABLE,
+ .iddq_reg = PLLC4_BASE,
+ .iddq_mask = 1 << PLLSS_IDDQ_BIT,
+ .pdiv_table = pll12g_ssd_esd_map,
+ .mnp_bits = {8, 8, 4, 20},
+ },
+/* PLLP: 408 MHz Clock source for most peripherals */
+ {
+ PLL(TEGRA124_CLK_PLL_P, "pllP_out0", "osc_div_clk"),
+ .type = PLL_P,
+ .base_reg = PLLP_BASE,
+ .misc_reg = PLLP_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable = PLL_MISC_LOCK_ENABLE,
+ .mnp_bits = {5, 10, 3, 20},
+ },
+/* PLLA: Audio clock sources: (11.2896 MHz, 12.288 MHz, 24.576 MHz) */
+ {
+ PLL(TEGRA124_CLK_PLL_A, "pllA_out", "pllP_out1"),
+ .type = PLL_A,
+ .base_reg = PLLA_BASE,
+ .misc_reg = PLLA_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable = PLL_MISC_LOCK_ENABLE,
+ .mnp_bits = {5, 10, 3, 20},
+ },
+/* PLLU: 480 MHz Clock source for USB PHY, provides 12/60/480 MHz */
+ {
+ PLL(TEGRA124_CLK_PLL_U, "pllU_out", "osc_div_clk"),
+ .type = PLL_U,
+ .base_reg = PLLU_BASE,
+ .misc_reg = PLLU_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable = PLLDU_MISC_LOCK_ENABLE,
+ .pdiv_table = pllu_map,
+ .mnp_bits = {5, 10, 1, 20},
+ },
+/* PLLD: 600 MHz Clock sources for the DSI and display subsystem */
+ {
+ PLL(TEGRA124_CLK_PLL_D, "pllD_out", "osc_div_clk"),
+ .type = PLL_D,
+ .base_reg = PLLD_BASE,
+ .misc_reg = PLLD_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable = PLL_MISC_LOCK_ENABLE,
+ .mnp_bits = {5, 11, 3, 20},
+ },
+/* PLLD2: 600 MHz Clock sources for the DSI and display subsystem */
+ {
+ PLL(TEGRA124_CLK_PLL_D2, "pllD2_out", "pllD2_src"),
+ .type = PLL_D2,
+ .base_reg = PLLD2_BASE,
+ .misc_reg = PLLD2_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable = PLLSS_MISC_LOCK_ENABLE,
+ .iddq_reg = PLLD2_BASE,
+ .iddq_mask = 1 << PLLSS_IDDQ_BIT,
+ .pdiv_table = pll12g_ssd_esd_map,
+ .mnp_bits = {8, 8, 4, 20},
+ },
+/* refPLLe: */
+ {
+ PLL(0, "pllREFE_out", "osc_div_clk"),
+ .type = PLL_REFE,
+ .base_reg = PLLRE_BASE,
+ .misc_reg = PLLRE_MISC,
+ .lock_mask = PLLRE_MISC_LOCK,
+ .lock_enable = PLLRE_MISC_LOCK_ENABLE,
+ .iddq_reg = PLLRE_MISC,
+ .iddq_mask = 1 << PLLRE_IDDQ_BIT,
+ .pdiv_table = pllrefe_map,
+ .mnp_bits = {8, 8, 4, 16},
+ },
+/* PLLE: generate the 100 MHz reference clock for USB 3.0 (spread spectrum) */
+ {
+ PLL(TEGRA124_CLK_PLL_E, "pllE_out0", "pllE_src"),
+ .type = PLL_E,
+ .base_reg = PLLE_BASE,
+ .misc_reg = PLLE_MISC,
+ .lock_mask = PLLE_MISC_LOCK,
+ .lock_enable = PLLE_MISC_LOCK_ENABLE,
+ .mnp_bits = {8, 8, 4, 24},
+ },
+/* PLLDP: 600 MHz Clock source for eDP/LVDS (spread spectrum) */
+ {
+ PLL(0, "pllDP_out0", "pllDP_src"),
+ .type = PLL_DP,
+ .base_reg = PLLDP_BASE,
+ .misc_reg = PLLDP_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable = PLLSS_MISC_LOCK_ENABLE,
+ .iddq_reg = PLLDP_BASE,
+ .iddq_mask = 1 << PLLSS_IDDQ_BIT,
+ .pdiv_table = pll12g_ssd_esd_map,
+ .mnp_bits = {8, 8, 4, 20},
+ },
+};
+
+static int tegra124_pll_init(struct clknode *clk, device_t dev);
+static int tegra124_pll_set_gate(struct clknode *clk, bool enable);
+static int tegra124_pll_recalc(struct clknode *clk, uint64_t *freq);
+static int tegra124_pll_set_freq(struct clknode *clknode, uint64_t fin,
+ uint64_t *fout, int flags, int *stop);
+struct pll_sc {
+ device_t clkdev;
+ enum pll_type type;
+ uint32_t base_reg;
+ uint32_t misc_reg;
+ uint32_t lock_mask;
+ uint32_t lock_enable;
+ uint32_t iddq_reg;
+ uint32_t iddq_mask;
+ uint32_t flags;
+ struct pdiv_table *pdiv_table;
+ struct mnp_bits mnp_bits;
+};
+
+static clknode_method_t tegra124_pll_methods[] = {
+ /* Device interface */
+ CLKNODEMETHOD(clknode_init, tegra124_pll_init),
+ CLKNODEMETHOD(clknode_set_gate, tegra124_pll_set_gate),
+ CLKNODEMETHOD(clknode_recalc_freq, tegra124_pll_recalc),
+ CLKNODEMETHOD(clknode_set_freq, tegra124_pll_set_freq),
+ CLKNODEMETHOD_END
+};
+DEFINE_CLASS_1(tegra124_pll, tegra124_pll_class, tegra124_pll_methods,
+ sizeof(struct pll_sc), clknode_class);
+
+static int
+pll_enable(struct pll_sc *sc)
+{
+ uint32_t reg;
+
+ RD4(sc, sc->base_reg, &reg);
+ if (sc->type != PLL_E)
+ reg &= ~PLL_BASE_BYPASS;
+ reg |= PLL_BASE_ENABLE;
+ WR4(sc, sc->base_reg, reg);
+ return (0);
+}
+
+static int
+pll_disable(struct pll_sc *sc)
+{
+ uint32_t reg;
+
+ RD4(sc, sc->base_reg, &reg);
+ if (sc->type != PLL_E)
+ reg |= PLL_BASE_BYPASS;
+ reg &= ~PLL_BASE_ENABLE;
+ WR4(sc, sc->base_reg, reg);
+ return (0);
+}
+
+static uint32_t
+pdiv_to_reg(struct pll_sc *sc, uint32_t p_div)
+{
+ struct pdiv_table *tbl;
+
+ tbl = sc->pdiv_table;
+ if (tbl == NULL)
+ return (ffs(p_div) - 1);
+
+ while (tbl->divider != 0) {
+ if (p_div <= tbl->divider)
+ return (tbl->value);
+ tbl++;
+ }
+ return (0xFFFFFFFF);
+}
+
+static uint32_t
+reg_to_pdiv(struct pll_sc *sc, uint32_t reg)
+{
+ struct pdiv_table *tbl;
+
+ tbl = sc->pdiv_table;
+ if (tbl == NULL)
+ return (1 << reg);
+
+ while (tbl->divider) {
+ if (reg == tbl->value)
+ return (tbl->divider);
+ tbl++;
+ }
+ return (0);
+}
+
+static uint32_t
+get_masked(uint32_t val, uint32_t shift, uint32_t width)
+{
+
+ return ((val >> shift) & ((1 << width) - 1));
+}
+
+static uint32_t
+set_masked(uint32_t val, uint32_t v, uint32_t shift, uint32_t width)
+{
+
+ val &= ~(((1 << width) - 1) << shift);
+ val |= (v & ((1 << width) - 1)) << shift;
+ return (val);
+}
+
+static void
+get_divisors(struct pll_sc *sc, uint32_t *m, uint32_t *n, uint32_t *p)
+{
+ uint32_t val;
+ struct mnp_bits *mnp_bits;
+
+ mnp_bits = &sc->mnp_bits;
+ RD4(sc, sc->base_reg, &val);
+ *m = get_masked(val, PLL_BASE_DIVM_SHIFT, mnp_bits->m_width);
+ *n = get_masked(val, PLL_BASE_DIVN_SHIFT, mnp_bits->n_width);
+ *p = get_masked(val, mnp_bits->p_shift, mnp_bits->p_width);
+}
+
+static uint32_t
+set_divisors(struct pll_sc *sc, uint32_t val, uint32_t m, uint32_t n,
+ uint32_t p)
+{
+ struct mnp_bits *mnp_bits;
+
+ mnp_bits = &sc->mnp_bits;
+ val = set_masked(val, m, PLL_BASE_DIVM_SHIFT, mnp_bits->m_width);
+ val = set_masked(val, n, PLL_BASE_DIVN_SHIFT, mnp_bits->n_width);
+ val = set_masked(val, p, mnp_bits->p_shift, mnp_bits->p_width);
+ return (val);
+}
+
+static bool
+is_locked(struct pll_sc *sc)
+{
+ uint32_t reg;
+
+ switch (sc->type) {
+ case PLL_REFE:
+ RD4(sc, sc->misc_reg, &reg);
+ reg &= PLLRE_MISC_LOCK;
+ break;
+
+ case PLL_E:
+ RD4(sc, sc->misc_reg, &reg);
+ reg &= PLLE_MISC_LOCK;
+ break;
+
+ default:
+ RD4(sc, sc->base_reg, &reg);
+ reg &= PLL_BASE_LOCK;
+ break;
+ }
+ return (reg != 0);
+}
+
+static int
+wait_for_lock(struct pll_sc *sc)
+{
+ int i;
+
+ for (i = PLL_LOCK_TIMEOUT / 10; i > 0; i--) {
+ if (is_locked(sc))
+ break;
+ DELAY(10);
+ }
+ if (i <= 0) {
+ printf("PLL lock timeout\n");
+ return (ETIMEDOUT);
+ }
+ return (0);
+}
+
+static int
+plle_enable(struct pll_sc *sc)
+{
+ uint32_t reg;
+ int rv;
+ struct mnp_bits *mnp_bits;
+ uint32_t pll_m = 1;
+ uint32_t pll_n = 200;
+ uint32_t pll_p = 13;
+ uint32_t pll_cml = 13;
+
+ mnp_bits = &sc->mnp_bits;
+
+ /* Disable lock override. */
+ RD4(sc, sc->base_reg, &reg);
+ reg &= ~PLLE_BASE_LOCK_OVERRIDE;
+ WR4(sc, sc->base_reg, reg);
+
+ RD4(sc, PLLE_AUX, &reg);
+ reg |= PLLE_AUX_ENABLE_SWCTL;
+ reg &= ~PLLE_AUX_SEQ_ENABLE;
+ WR4(sc, PLLE_AUX, reg);
+ DELAY(10);
+
+ RD4(sc, sc->misc_reg, &reg);
+ reg |= PLLE_MISC_LOCK_ENABLE;
+ reg |= PLLE_MISC_IDDQ_SWCTL;
+ reg &= ~PLLE_MISC_IDDQ_OVERRIDE_VALUE;
+ reg |= PLLE_MISC_PTS;
+ reg |= PLLE_MISC_VREG_BG_CTRL_MASK;
+ reg |= PLLE_MISC_VREG_CTRL_MASK;
+ WR4(sc, sc->misc_reg, reg);
+ DELAY(10);
+
+ RD4(sc, PLLE_SS_CNTL, &reg);
+ reg |= PLLE_SS_CNTL_DISABLE;
+ WR4(sc, PLLE_SS_CNTL, reg);
+
+ RD4(sc, sc->base_reg, &reg);
+ reg = set_divisors(sc, reg, pll_m, pll_n, pll_p);
+ reg &= ~(PLLE_BASE_DIVCML_MASK << PLLE_BASE_DIVCML_SHIFT);
+ reg |= pll_cml << PLLE_BASE_DIVCML_SHIFT;
+ WR4(sc, sc->base_reg, reg);
+ DELAY(10);
+
+ pll_enable(sc);
+ rv = wait_for_lock(sc);
+ if (rv != 0)
+ return (rv);
+
+ RD4(sc, PLLE_SS_CNTL, &reg);
+ reg &= ~PLLE_SS_CNTL_SSCCENTER;
+ reg &= ~PLLE_SS_CNTL_SSCINVERT;
+ reg &= ~PLLE_SS_CNTL_COEFFICIENTS_MASK;
+ reg |= PLLE_SS_CNTL_COEFFICIENTS_VAL;
+ WR4(sc, PLLE_SS_CNTL, reg);
+ reg &= ~PLLE_SS_CNTL_SSCBYP;
+ reg &= ~PLLE_SS_CNTL_BYPASS_SS;
+ WR4(sc, PLLE_SS_CNTL, reg);
+ DELAY(10);
+
+ reg &= ~PLLE_SS_CNTL_INTERP_RESET;
+ WR4(sc, PLLE_SS_CNTL, reg);
+ DELAY(10);
+
+ /* HW control of brick pll. */
+ RD4(sc, sc->misc_reg, &reg);
+ reg &= ~PLLE_MISC_IDDQ_SWCTL;
+ WR4(sc, sc->misc_reg, reg);
+
+ RD4(sc, PLLE_AUX, &reg);
+ reg |= PLLE_AUX_USE_LOCKDET;
+ reg |= PLLE_AUX_SEQ_START_STATE;
+ reg &= ~PLLE_AUX_ENABLE_SWCTL;
+ reg &= ~PLLE_AUX_SS_SWCTL;
+ WR4(sc, PLLE_AUX, reg);
+ reg |= PLLE_AUX_SEQ_START_STATE;
+ DELAY(10);
+ reg |= PLLE_AUX_SEQ_ENABLE;
+ WR4(sc, PLLE_AUX, reg);
+
+ RD4(sc, XUSBIO_PLL_CFG0, &reg);
+ reg |= XUSBIO_PLL_CFG0_PADPLL_USE_LOCKDET;
+ reg |= XUSBIO_PLL_CFG0_SEQ_START_STATE;
+ reg &= ~XUSBIO_PLL_CFG0_CLK_ENABLE_SWCTL;
+ reg &= ~XUSBIO_PLL_CFG0_PADPLL_RESET_SWCTL;
+ WR4(sc, XUSBIO_PLL_CFG0, reg);
+ DELAY(10);
+
+ reg |= XUSBIO_PLL_CFG0_SEQ_ENABLE;
+ WR4(sc, XUSBIO_PLL_CFG0, reg);
+
+ /* Enable HW control and unreset SATA PLL. */
+ RD4(sc, SATA_PLL_CFG0, &reg);
+ reg &= ~SATA_PLL_CFG0_PADPLL_RESET_SWCTL;
+ reg &= ~SATA_PLL_CFG0_PADPLL_RESET_OVERRIDE_VALUE;
+ reg |= SATA_PLL_CFG0_PADPLL_USE_LOCKDET;
+ reg &= ~SATA_PLL_CFG0_SEQ_IN_SWCTL;
+ reg &= ~SATA_PLL_CFG0_SEQ_RESET_INPUT_VALUE;
+ reg &= ~SATA_PLL_CFG0_SEQ_LANE_PD_INPUT_VALUE;
+ reg &= ~SATA_PLL_CFG0_SEQ_PADPLL_PD_INPUT_VALUE;
+ reg &= ~SATA_PLL_CFG0_SEQ_ENABLE;
+ reg |= SATA_PLL_CFG0_SEQ_START_STATE;
+ WR4(sc, SATA_PLL_CFG0, reg);
+ DELAY(10);
+ reg |= SATA_PLL_CFG0_SEQ_ENABLE;
+ WR4(sc, SATA_PLL_CFG0, reg);
+
+ /* Enable HW control of PCIe PLL. */
+ RD4(sc, PCIE_PLL_CFG0, &reg);
+ reg |= PCIE_PLL_CFG0_SEQ_ENABLE;
+ WR4(sc, PCIE_PLL_CFG0, reg);
+
+ return (0);
+}
+
+static int
+tegra124_pll_set_gate(struct clknode *clknode, bool enable)
+{
+ int rv;
+ struct pll_sc *sc;
+
+ sc = clknode_get_softc(clknode);
+ if (enable == 0) {
+ rv = pll_disable(sc);
+ return(rv);
+ }
+
+ if (sc->type == PLL_E)
+ rv = plle_enable(sc);
+ else
+ rv = pll_enable(sc);
+ return (rv);
+}
+
+static int
+pll_set_std(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags,
+ uint32_t m, uint32_t n, uint32_t p)
+{
+ uint32_t reg;
+ struct mnp_bits *mnp_bits;
+ int rv;
+
+ mnp_bits = &sc->mnp_bits;
+ if (m >= (1 << mnp_bits->m_width))
+ return (ERANGE);
+ if (n >= (1 << mnp_bits->n_width))
+ return (ERANGE);
+ if (pdiv_to_reg(sc, p) >= (1 << mnp_bits->p_width))
+ return (ERANGE);
+
+ if (flags & CLK_SET_DRYRUN) {
+ if (((flags & (CLK_SET_ROUND_UP | CLK_SET_ROUND_DOWN)) == 0) &&
+ (*fout != (((fin / m) * n) /p)))
+ return (ERANGE);
+
+ *fout = ((fin / m) * n) /p;
+
+ return (0);
+ }
+
+ pll_disable(sc);
+
+ /* take pll out of IDDQ */
+ if (sc->iddq_reg != 0)
+ MD4(sc, sc->iddq_reg, sc->iddq_mask, 0);
+
+ RD4(sc, sc->base_reg, &reg);
+ reg = set_masked(reg, m, PLL_BASE_DIVM_SHIFT, mnp_bits->m_width);
+ reg = set_masked(reg, n, PLL_BASE_DIVN_SHIFT, mnp_bits->n_width);
+ reg = set_masked(reg, pdiv_to_reg(sc, p), mnp_bits->p_shift,
+ mnp_bits->p_width);
+ WR4(sc, sc->base_reg, reg);
+
+ /* Enable PLL. */
+ RD4(sc, sc->base_reg, &reg);
+ reg |= PLL_BASE_ENABLE;
+ WR4(sc, sc->base_reg, reg);
+
+ /* Enable lock detection. */
+ RD4(sc, sc->misc_reg, &reg);
+ reg |= sc->lock_enable;
+ WR4(sc, sc->misc_reg, reg);
+
+ rv = wait_for_lock(sc);
+ if (rv != 0) {
+ /* Disable PLL */
+ RD4(sc, sc->base_reg, &reg);
+ reg &= ~PLL_BASE_ENABLE;
+ WR4(sc, sc->base_reg, reg);
+ return (rv);
+ }
+ RD4(sc, sc->misc_reg, &reg);
+
+ pll_enable(sc);
+ *fout = ((fin / m) * n) / p;
+ return 0;
+}
+
+static int
+plla_set_freq(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags)
+{
+ uint32_t m, n, p;
+
+ p = 1;
+ m = 5;
+ n = (*fout * p * m + fin / 2)/ fin;
+ dprintf("%s: m: %d, n: %d, p: %d\n", __func__, m, n, p);
+ return (pll_set_std(sc, fin, fout, flags, m, n, p));
+}
+
+static int
+pllc_set_freq(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags)
+{
+ uint32_t m, n, p;
+
+ p = 2;
+ m = 1;
+ n = (*fout * p * m + fin / 2)/ fin;
+ dprintf("%s: m: %d, n: %d, p: %d\n", __func__, m, n, p);
+ return (pll_set_std( sc, fin, fout, flags, m, n, p));
+}
+
+/*
+ * PLLD2 is used as source for pixel clock for HDMI.
+ * We must be able to set it frequency very flexibly and
+ * precisely (within 5% tolerance limit allowed by HDMI specs).
+ *
+ * For this reason, it is necessary to search the full state space.
+ * Fortunately, thanks to early cycle terminations, performance
+ * is within acceptable limits.
+ */
+#define PLLD2_PFD_MIN 12000000 /* 12 MHz */
+#define PLLD2_PFD_MAX 38000000 /* 38 MHz */
+#define PLLD2_VCO_MIN 600000000 /* 600 MHz */
+#define PLLD2_VCO_MAX 1200000000 /* 1.2 GHz */
+
+static int
+plld2_set_freq(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags)
+{
+ uint32_t m, n, p;
+ uint32_t best_m, best_n, best_p;
+ uint64_t vco, pfd;
+ int64_t err, best_err;
+ struct mnp_bits *mnp_bits;
+ struct pdiv_table *tbl;
+ int p_idx, rv;
+
+ mnp_bits = &sc->mnp_bits;
+ tbl = sc->pdiv_table;
+ best_err = INT64_MAX;
+
+ for (p_idx = 0; tbl[p_idx].divider != 0; p_idx++) {
+ p = tbl[p_idx].divider;
+
+ /* Check constraints */
+ vco = *fout * p;
+ if (vco < PLLD2_VCO_MIN)
+ continue;
+ if (vco > PLLD2_VCO_MAX)
+ break;
+
+ for (m = 1; m < (1 << mnp_bits->m_width); m++) {
+ n = (*fout * p * m + fin / 2) / fin;
+
+ /* Check constraints */
+ if (n == 0)
+ continue;
+ if (n >= (1 << mnp_bits->n_width))
+ break;
+ vco = (fin * n) / m;
+ if (vco > PLLD2_VCO_MAX || vco < PLLD2_VCO_MIN)
+ continue;
+ pfd = fin / m;
+ if (pfd > PLLD2_PFD_MAX || vco < PLLD2_PFD_MIN)
+ continue;
+
+ /* Constraints passed, save best result */
+ err = *fout - vco / p;
+ if (err < 0)
+ err = -err;
+ if (err < best_err) {
+ best_err = err;
+ best_p = p;
+ best_m = m;
+ best_n = n;
+ }
+ if (err == 0)
+ goto done;
+ }
+ }
+done:
+ /*
+ * HDMI specification allows 5% pixel clock tolerance,
+ * we will by a slightly stricter
+ */
+ if (best_err > ((*fout * 100) / 4))
+ return (ERANGE);
+
+ if (flags & CLK_SET_DRYRUN)
+ return (0);
+ rv = pll_set_std(sc, fin, fout, flags, best_m, best_n, best_p);
+ /* XXXX Panic for rv == ERANGE ? */
+ return (rv);
+}
+
+static int
+pllrefe_set_freq(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags)
+{
+ uint32_t m, n, p;
+
+ m = 1;
+ p = 1;
+ n = *fout * p * m / fin;
+ dprintf("%s: m: %d, n: %d, p: %d\n", __func__, m, n, p);
+ return (pll_set_std(sc, fin, fout, flags, m, n, p));
+}
+
+static int
+pllx_set_freq(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags)
+{
+ uint32_t reg;
+ uint32_t m, n, p;
+ struct mnp_bits *mnp_bits;
+ int rv;
+
+ mnp_bits = &sc->mnp_bits;
+
+ p = 1;
+ m = 1;
+ n = (*fout * p * m + fin / 2)/ fin;
+ dprintf("%s: m: %d, n: %d, p: %d\n", __func__, m, n, p);
+
+ if (m >= (1 << mnp_bits->m_width))
+ return (ERANGE);
+ if (n >= (1 << mnp_bits->n_width))
+ return (ERANGE);
+ if (pdiv_to_reg(sc, p) >= (1 << mnp_bits->p_width))
+ return (ERANGE);
+
+ if (flags & CLK_SET_DRYRUN) {
+ if (((flags & (CLK_SET_ROUND_UP | CLK_SET_ROUND_DOWN)) == 0) &&
+ (*fout != (((fin / m) * n) /p)))
+ return (ERANGE);
+ *fout = ((fin / m) * n) /p;
+ return (0);
+ }
+
+ /* PLLX doesn't have bypass, disable it first. */
+ RD4(sc, sc->base_reg, &reg);
+ reg &= ~PLL_BASE_ENABLE;
+ WR4(sc, sc->base_reg, reg);
+
+ /* Set PLL. */
+ RD4(sc, sc->base_reg, &reg);
+ reg = set_masked(reg, m, PLL_BASE_DIVM_SHIFT, mnp_bits->m_width);
+ reg = set_masked(reg, n, PLL_BASE_DIVN_SHIFT, mnp_bits->n_width);
+ reg = set_masked(reg, pdiv_to_reg(sc, p), mnp_bits->p_shift,
+ mnp_bits->p_width);
+ WR4(sc, sc->base_reg, reg);
+ RD4(sc, sc->base_reg, &reg);
+ DELAY(100);
+
+ /* Enable lock detection. */
+ RD4(sc, sc->misc_reg, &reg);
+ reg |= sc->lock_enable;
+ WR4(sc, sc->misc_reg, reg);
+
+ /* Enable PLL. */
+ RD4(sc, sc->base_reg, &reg);
+ reg |= PLL_BASE_ENABLE;
+ WR4(sc, sc->base_reg, reg);
+
+ rv = wait_for_lock(sc);
+ if (rv != 0) {
+ /* Disable PLL */
+ RD4(sc, sc->base_reg, &reg);
+ reg &= ~PLL_BASE_ENABLE;
+ WR4(sc, sc->base_reg, reg);
+ return (rv);
+ }
+ RD4(sc, sc->misc_reg, &reg);
+
+ *fout = ((fin / m) * n) / p;
+ return (0);
+}
+
+static int
+tegra124_pll_set_freq(struct clknode *clknode, uint64_t fin, uint64_t *fout,
+ int flags, int *stop)
+{
+ *stop = 1;
+ int rv;
+ struct pll_sc *sc;
+
+ sc = clknode_get_softc(clknode);
+ dprintf("%s: %s requested freq: %llu, input freq: %llu\n", __func__,
+ clknode_get_name(clknode), *fout, fin);
+ switch (sc->type) {
+ case PLL_A:
+ rv = plla_set_freq(sc, fin, fout, flags);
+ break;
+ case PLL_C:
+ rv = pllc_set_freq(sc, fin, fout, flags);
+ break;
+ case PLL_D2:
+ rv = plld2_set_freq(sc, fin, fout, flags);
+ break;
+
+ case PLL_REFE:
+ rv = pllrefe_set_freq(sc, fin, fout, flags);
+ break;
+
+ case PLL_X:
+ rv = pllx_set_freq(sc, fin, fout, flags);
+ break;
+
+ case PLL_U:
+ if (*fout == 480000000) /* PLLU is fixed to 480 MHz */
+ rv = 0;
+ else
+ rv = ERANGE;
+ break;
+ default:
+ rv = ENXIO;
+ break;
+ }
+
+ return (rv);
+}
+
+static int
+tegra124_pll_init(struct clknode *clk, device_t dev)
+{
+ struct pll_sc *sc;
+ uint32_t reg;
+
+ sc = clknode_get_softc(clk);
+
+ /* If PLL is enabled, enable lock detect too. */
+ RD4(sc, sc->base_reg, &reg);
+ if (reg & PLL_BASE_ENABLE) {
+ RD4(sc, sc->misc_reg, &reg);
+ reg |= sc->lock_enable;
+ WR4(sc, sc->misc_reg, reg);
+ }
+ if (sc->type == PLL_REFE) {
+ RD4(sc, sc->misc_reg, &reg);
+ reg &= ~(1 << 29); /* Diasble lock override */
+ WR4(sc, sc->misc_reg, reg);
+ }
+
+ clknode_init_parent_idx(clk, 0);
+ return(0);
+}
+
+static int
+tegra124_pll_recalc(struct clknode *clk, uint64_t *freq)
+{
+ struct pll_sc *sc;
+ uint32_t m, n, p, pr;
+ uint32_t reg, misc_reg;
+ int locked;
+
+ sc = clknode_get_softc(clk);
+
+ RD4(sc, sc->base_reg, &reg);
+ RD4(sc, sc->misc_reg, &misc_reg);
+
+ get_divisors(sc, &m, &n, &pr);
+ if (sc->type != PLL_E)
+ p = reg_to_pdiv(sc, pr);
+ else
+ p = 2 * (pr - 1);
+ locked = is_locked(sc);
+
+ dprintf("%s: %s (0x%08x, 0x%08x) - m: %d, n: %d, p: %d (%d): "
+ "e: %d, r: %d, o: %d - %s\n", __func__,
+ clknode_get_name(clk), reg, misc_reg, m, n, p, pr,
+ (reg >> 30) & 1, (reg >> 29) & 1, (reg >> 28) & 1,
+ locked ? "locked" : "unlocked");
+
+ if ((m == 0) || (n == 0) || (p == 0)) {
+ *freq = 0;
+ return (EINVAL);
+ }
+ *freq = ((*freq / m) * n) / p;
+ return (0);
+}
+
+static int
+pll_register(struct clkdom *clkdom, struct clk_pll_def *clkdef)
+{
+ struct clknode *clk;
+ struct pll_sc *sc;
+
+ clk = clknode_create(clkdom, &tegra124_pll_class, &clkdef->clkdef);
+ if (clk == NULL)
+ return (ENXIO);
+
+ sc = clknode_get_softc(clk);
+ sc->clkdev = clknode_get_device(clk);
+ sc->type = clkdef->type;
+ sc->base_reg = clkdef->base_reg;
+ sc->misc_reg = clkdef->misc_reg;
+ sc->lock_mask = clkdef->lock_mask;
+ sc->lock_enable = clkdef->lock_enable;
+ sc->iddq_reg = clkdef->iddq_reg;
+ sc->iddq_mask = clkdef->iddq_mask;
+ sc->flags = clkdef->flags;
+ sc->pdiv_table = clkdef->pdiv_table;
+ sc->mnp_bits = clkdef->mnp_bits;
+ clknode_register(clkdom, clk);
+ return (0);
+}
+
+static void config_utmi_pll(struct tegra124_car_softc *sc)
+{
+ uint32_t reg;
+ /*
+ * XXX Simplified UTMIP settings for 12MHz base clock.
+ */
+#define ENABLE_DELAY_COUNT 0x02
+#define STABLE_COUNT 0x2F
+#define ACTIVE_DELAY_COUNT 0x04
+#define XTAL_FREQ_COUNT 0x76
+
+ CLKDEV_READ_4(sc->dev, UTMIP_PLL_CFG2, &reg);
+ reg &= ~UTMIP_PLL_CFG2_STABLE_COUNT(~0);
+ reg |= UTMIP_PLL_CFG2_STABLE_COUNT(STABLE_COUNT);
+ reg &= ~UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(~0);
+ reg |= UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(ACTIVE_DELAY_COUNT);
+ reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERDOWN;
+ reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERDOWN;
+ reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_C_POWERDOWN;
+ CLKDEV_WRITE_4(sc->dev, UTMIP_PLL_CFG2, reg);
+
+ CLKDEV_READ_4(sc->dev, UTMIP_PLL_CFG1, &reg);
+ reg &= ~UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(~0);
+ reg |= UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(ENABLE_DELAY_COUNT);
+ reg &= ~UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(~0);
+ reg |= UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(XTAL_FREQ_COUNT);
+ reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN;
+ reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ACTIVE_POWERDOWN;
+ reg &= ~UTMIP_PLL_CFG1_FORCE_PLLU_POWERUP;
+ reg &= ~UTMIP_PLL_CFG1_FORCE_PLLU_POWERDOWN;
+ CLKDEV_WRITE_4(sc->dev, UTMIP_PLL_CFG1, reg);
+
+ /* Prepare UTMIP requencer. */
+ CLKDEV_READ_4(sc->dev, UTMIPLL_HW_PWRDN_CFG0, &reg);
+ reg |= UTMIPLL_HW_PWRDN_CFG0_USE_LOCKDET;
+ reg &= ~UTMIPLL_HW_PWRDN_CFG0_CLK_ENABLE_SWCTL;
+ reg |= UTMIPLL_HW_PWRDN_CFG0_SEQ_START_STATE;
+ CLKDEV_WRITE_4(sc->dev, UTMIPLL_HW_PWRDN_CFG0, reg);
+
+ /* Powerup UTMIP. */
+ CLKDEV_READ_4(sc->dev, UTMIP_PLL_CFG1, &reg);
+ reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERUP;
+ reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN;
+ CLKDEV_WRITE_4(sc->dev, UTMIP_PLL_CFG1, reg);
+ DELAY(10);
+
+ /* SW override for UTMIPLL */
+ CLKDEV_READ_4(sc->dev, UTMIPLL_HW_PWRDN_CFG0, &reg);
+ reg |= UTMIPLL_HW_PWRDN_CFG0_IDDQ_SWCTL;
+ reg &= ~UTMIPLL_HW_PWRDN_CFG0_IDDQ_OVERRIDE;
+ CLKDEV_WRITE_4(sc->dev, UTMIPLL_HW_PWRDN_CFG0, reg);
+ DELAY(10);
+
+ /* HW control of UTMIPLL. */
+ CLKDEV_READ_4(sc->dev, UTMIPLL_HW_PWRDN_CFG0, &reg);
+ reg |= UTMIPLL_HW_PWRDN_CFG0_SEQ_ENABLE;
+ CLKDEV_WRITE_4(sc->dev, UTMIPLL_HW_PWRDN_CFG0, reg);
+}
+
+void
+tegra124_init_plls(struct tegra124_car_softc *sc)
+{
+ int i, rv;
+
+ for (i = 0; i < nitems(pll_clks); i++) {
+ rv = pll_register(sc->clkdom, pll_clks + i);
+ if (rv != 0)
+ panic("pll_register failed");
+ }
+ config_utmi_pll(sc);
+
+}
diff --git a/sys/arm/nvidia/tegra124/tegra124_clk_super.c b/sys/arm/nvidia/tegra124/tegra124_clk_super.c
new file mode 100644
index 000000000000..43582508edea
--- /dev/null
+++ b/sys/arm/nvidia/tegra124/tegra124_clk_super.c
@@ -0,0 +1,264 @@
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+
+#include <dev/extres/clk/clk.h>
+
+#include <dt-bindings/clock/tegra124-car.h>
+#include "tegra124_car.h"
+
+/* Flags */
+#define SMF_HAVE_DIVIDER_2 1
+
+struct super_mux_def {
+ struct clknode_init_def clkdef;
+ uint32_t base_reg;
+ uint32_t flags;
+ int src_pllx;
+ int src_div2;
+};
+
+#define PLIST(x) static const char *x[]
+#define SM(_id, cn, pl, r, x, d, f) \
+{ \
+ .clkdef.id = _id, \
+ .clkdef.name = cn, \
+ .clkdef.parent_names = pl, \
+ .clkdef.parent_cnt = nitems(pl), \
+ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \
+ .base_reg = r, \
+ .src_pllx = x, \
+ .src_div2 = d, \
+ .flags = f, \
+}
+
+PLIST(cclk_g_parents) = {
+ "clk_m", "pllC_out0", "clk_s", "pllM_out0",
+ "pllP_out0", "pllP_out4", "pllC2_out0", "pllC3_out0",
+ "pllX_out", NULL, NULL, NULL,
+ NULL, NULL, NULL,NULL, // "dfllCPU_out0"
+};
+
+PLIST(cclk_lp_parents) = {
+ "clk_m", "pllC_out0", "clk_s", "pllM_out0",
+ "pllP_out0", "pllP_out4", "pllC2_out0", "pllC3_out0",
+ "pllX_out", NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ "pllX_out0"
+};
+
+PLIST(sclk_parents) = {
+ "clk_m", "pllC_out1", "pllP_out4", "pllP_out0",
+ "pllP_out2", "pllC_out0", "clk_s", "pllM_out1",
+};
+
+static struct super_mux_def super_mux_def[] = {
+ SM(TEGRA124_CLK_CCLK_G, "cclk_g", cclk_g_parents, CCLKG_BURST_POLICY, 0, 0, 0),
+ SM(TEGRA124_CLK_CCLK_LP, "cclk_lp", cclk_lp_parents, CCLKLP_BURST_POLICY, 8, 16, SMF_HAVE_DIVIDER_2),
+ SM(TEGRA124_CLK_SCLK, "sclk", sclk_parents, SCLK_BURST_POLICY, 0, 0, 0),
+};
+
+static int super_mux_init(struct clknode *clk, device_t dev);
+static int super_mux_set_mux(struct clknode *clk, int idx);
+
+struct super_mux_sc {
+ device_t clkdev;
+ uint32_t base_reg;
+ int src_pllx;
+ int src_div2;
+ uint32_t flags;
+
+ int mux;
+};
+
+static clknode_method_t super_mux_methods[] = {
+ /* Device interface */
+ CLKNODEMETHOD(clknode_init, super_mux_init),
+ CLKNODEMETHOD(clknode_set_mux, super_mux_set_mux),
+ CLKNODEMETHOD_END
+};
+DEFINE_CLASS_1(tegra124_super_mux, tegra124_super_mux_class, super_mux_methods,
+ sizeof(struct super_mux_sc), clknode_class);
+
+/* Mux status. */
+#define SUPER_MUX_STATE_STDBY 0
+#define SUPER_MUX_STATE_IDLE 1
+#define SUPER_MUX_STATE_RUN 2
+#define SUPER_MUX_STATE_IRQ 3
+#define SUPER_MUX_STATE_FIQ 4
+
+/* Mux register bits. */
+#define SUPER_MUX_STATE_BIT_SHIFT 28
+#define SUPER_MUX_STATE_BIT_MASK 0xF
+/* State is Priority encoded */
+#define SUPER_MUX_STATE_BIT_STDBY 0x00
+#define SUPER_MUX_STATE_BIT_IDLE 0x01
+#define SUPER_MUX_STATE_BIT_RUN 0x02
+#define SUPER_MUX_STATE_BIT_IRQ 0x04
+#define SUPER_MUX_STATE_BIT_FIQ 0x08
+
+#define SUPER_MUX_MUX_WIDTH 4
+#define SUPER_MUX_LP_DIV2_BYPASS (1 << 16)
+
+static uint32_t
+super_mux_get_state(uint32_t reg)
+{
+ reg = (reg >> SUPER_MUX_STATE_BIT_SHIFT) & SUPER_MUX_STATE_BIT_MASK;
+ if (reg & SUPER_MUX_STATE_BIT_FIQ)
+ return (SUPER_MUX_STATE_FIQ);
+ if (reg & SUPER_MUX_STATE_BIT_IRQ)
+ return (SUPER_MUX_STATE_IRQ);
+ if (reg & SUPER_MUX_STATE_BIT_RUN)
+ return (SUPER_MUX_STATE_RUN);
+ if (reg & SUPER_MUX_STATE_BIT_IDLE)
+ return (SUPER_MUX_STATE_IDLE);
+ return (SUPER_MUX_STATE_STDBY);
+}
+
+static int
+super_mux_init(struct clknode *clk, device_t dev)
+{
+ struct super_mux_sc *sc;
+ uint32_t reg;
+ int shift, state;
+
+ sc = clknode_get_softc(clk);
+
+ DEVICE_LOCK(sc);
+ RD4(sc, sc->base_reg, &reg);
+ DEVICE_UNLOCK(sc);
+ state = super_mux_get_state(reg);
+
+ if ((state != SUPER_MUX_STATE_RUN) &&
+ (state != SUPER_MUX_STATE_IDLE)) {
+ panic("Unexpected super mux state: %u", state);
+ }
+
+ shift = state * SUPER_MUX_MUX_WIDTH;
+
+ sc->mux = (reg >> shift) & ((1 << SUPER_MUX_MUX_WIDTH) - 1);
+
+ /*
+ * CCLKLP uses PLLX/2 as source if LP_DIV2_BYPASS isn't set
+ * and source mux is set to PLLX.
+ */
+ if (sc->flags & SMF_HAVE_DIVIDER_2) {
+ if (((reg & SUPER_MUX_LP_DIV2_BYPASS) == 0) &&
+ (sc->mux == sc->src_pllx))
+ sc->mux = sc->src_div2;
+ }
+ clknode_init_parent_idx(clk, sc->mux);
+
+ return(0);
+}
+
+static int
+super_mux_set_mux(struct clknode *clk, int idx)
+{
+
+ struct super_mux_sc *sc;
+ int shift, state;
+ uint32_t reg, dummy;
+
+ sc = clknode_get_softc(clk);
+
+ DEVICE_LOCK(sc);
+ RD4(sc, sc->base_reg, &reg);
+ state = super_mux_get_state(reg);
+
+ if ((state != SUPER_MUX_STATE_RUN) &&
+ (state != SUPER_MUX_STATE_IDLE)) {
+ panic("Unexpected super mux state: %u", state);
+ }
+ shift = (state - 1) * SUPER_MUX_MUX_WIDTH;
+ sc->mux = idx;
+ if (sc->flags & SMF_HAVE_DIVIDER_2) {
+ if (idx == sc->src_div2) {
+ idx = sc->src_pllx;
+ reg &= ~SUPER_MUX_LP_DIV2_BYPASS;
+ WR4(sc, sc->base_reg, reg);
+ RD4(sc, sc->base_reg, &dummy);
+ } else if (idx == sc->src_pllx) {
+ reg = SUPER_MUX_LP_DIV2_BYPASS;
+ WR4(sc, sc->base_reg, reg);
+ RD4(sc, sc->base_reg, &dummy);
+ }
+ }
+ reg &= ~(((1 << SUPER_MUX_MUX_WIDTH) - 1) << shift);
+ reg |= idx << shift;
+
+ WR4(sc, sc->base_reg, reg);
+ RD4(sc, sc->base_reg, &dummy);
+ DEVICE_UNLOCK(sc);
+
+ return(0);
+}
+
+static int
+super_mux_register(struct clkdom *clkdom, struct super_mux_def *clkdef)
+{
+ struct clknode *clk;
+ struct super_mux_sc *sc;
+
+ clk = clknode_create(clkdom, &tegra124_super_mux_class,
+ &clkdef->clkdef);
+ if (clk == NULL)
+ return (1);
+
+ sc = clknode_get_softc(clk);
+ sc->clkdev = clknode_get_device(clk);
+ sc->base_reg = clkdef->base_reg;
+ sc->src_pllx = clkdef->src_pllx;
+ sc->src_div2 = clkdef->src_div2;
+ sc->flags = clkdef->flags;
+
+ clknode_register(clkdom, clk);
+ return (0);
+}
+
+void
+tegra124_super_mux_clock(struct tegra124_car_softc *sc)
+{
+ int i, rv;
+
+ for (i = 0; i < nitems(super_mux_def); i++) {
+ rv = super_mux_register(sc->clkdom, &super_mux_def[i]);
+ if (rv != 0)
+ panic("super_mux_register failed");
+ }
+
+}
diff --git a/sys/arm/nvidia/tegra124/tegra124_coretemp.c b/sys/arm/nvidia/tegra124/tegra124_coretemp.c
new file mode 100644
index 000000000000..1ed592cc04ba
--- /dev/null
+++ b/sys/arm/nvidia/tegra124/tegra124_coretemp.c
@@ -0,0 +1,268 @@
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/cpu.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "tegra_soctherm_if.h"
+
+enum therm_info {
+ CORETEMP_TEMP,
+ CORETEMP_DELTA,
+ CORETEMP_RESOLUTION,
+ CORETEMP_TJMAX,
+};
+
+struct tegra124_coretemp_softc {
+ device_t dev;
+ int overheat_log;
+ int core_max_temp;
+ int cpu_id;
+ device_t tsens_dev;
+ intptr_t tsens_id;
+};
+
+static int
+coretemp_get_val_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ device_t dev;
+ int val, temp, rv;
+ struct tegra124_coretemp_softc *sc;
+ enum therm_info type;
+ char stemp[16];
+
+ dev = (device_t) arg1;
+ sc = device_get_softc(dev);
+ type = arg2;
+
+ rv = TEGRA_SOCTHERM_GET_TEMPERATURE(sc->tsens_dev, sc->dev,
+ sc->tsens_id, &temp);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot read temperature sensor %d: %d\n",
+ sc->tsens_id, rv);
+ return (rv);
+ }
+
+ switch (type) {
+ case CORETEMP_TEMP:
+ val = temp / 100;
+ val += 2731;
+ break;
+ case CORETEMP_DELTA:
+ val = (sc->core_max_temp - temp) / 1000;
+ break;
+ case CORETEMP_RESOLUTION:
+ val = 1;
+ break;
+ case CORETEMP_TJMAX:
+ val = sc->core_max_temp / 100;
+ val += 2731;
+ break;
+ }
+
+ if ((temp > sc->core_max_temp) && !sc->overheat_log) {
+ sc->overheat_log = 1;
+
+ /*
+ * Check for Critical Temperature Status and Critical
+ * Temperature Log. It doesn't really matter if the
+ * current temperature is invalid because the "Critical
+ * Temperature Log" bit will tell us if the Critical
+ * Temperature has * been reached in past. It's not
+ * directly related to the current temperature.
+ *
+ * If we reach a critical level, allow devctl(4)
+ * to catch this and shutdown the system.
+ */
+ device_printf(dev, "critical temperature detected, "
+ "suggest system shutdown\n");
+ snprintf(stemp, sizeof(stemp), "%d", val);
+ devctl_notify("coretemp", "Thermal", stemp,
+ "notify=0xcc");
+ } else {
+ sc->overheat_log = 0;
+ }
+
+ return (sysctl_handle_int(oidp, 0, val, req));
+}
+
+static int
+tegra124_coretemp_ofw_parse(struct tegra124_coretemp_softc *sc)
+{
+ int rv, ncells;
+ phandle_t node, xnode;
+ pcell_t *cells;
+
+ node = OF_peer(0);
+ node = ofw_bus_find_child(node, "thermal-zones");
+ if (node <= 0) {
+ device_printf(sc->dev, "Cannot find 'thermal-zones'.\n");
+ return (ENXIO);
+ }
+
+ node = ofw_bus_find_child(node, "cpu");
+ if (node <= 0) {
+ device_printf(sc->dev, "Cannot find 'cpu'\n");
+ return (ENXIO);
+ }
+ rv = ofw_bus_parse_xref_list_alloc(node, "thermal-sensors",
+ "#thermal-sensor-cells", 0, &xnode, &ncells, &cells);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot parse 'thermal-sensors' property.\n");
+ return (ENXIO);
+ }
+ if (ncells != 1) {
+ device_printf(sc->dev,
+ "Invalid format of 'thermal-sensors' property(%d).\n",
+ ncells);
+ return (ENXIO);
+ }
+
+ sc->tsens_id = 0x100 + sc->cpu_id; //cells[0];
+ OF_prop_free(cells);
+
+ sc->tsens_dev = OF_device_from_xref(xnode);
+ if (sc->tsens_dev == NULL) {
+ device_printf(sc->dev,
+ "Cannot find thermal sensors device.");
+ return (ENXIO);
+ }
+ return (0);
+}
+
+static void
+tegra124_coretemp_identify(driver_t *driver, device_t parent)
+{
+ phandle_t root;
+
+ root = OF_finddevice("/");
+ if (!ofw_bus_node_is_compatible(root, "nvidia,tegra124"))
+ return;
+ if (device_find_child(parent, "tegra124_coretemp", -1) != NULL)
+ return;
+ if (BUS_ADD_CHILD(parent, 0, "tegra124_coretemp", -1) == NULL)
+ device_printf(parent, "add child failed\n");
+}
+
+static int
+tegra124_coretemp_probe(device_t dev)
+{
+
+ device_set_desc(dev, "CPU Thermal Sensor");
+ return (0);
+}
+
+static int
+tegra124_coretemp_attach(device_t dev)
+{
+ struct tegra124_coretemp_softc *sc;
+ device_t pdev;
+ struct sysctl_oid *oid;
+ struct sysctl_ctx_list *ctx;
+ int rv;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ sc->cpu_id = device_get_unit(dev);
+ sc->core_max_temp = 102000;
+ pdev = device_get_parent(dev);
+
+ rv = tegra124_coretemp_ofw_parse(sc);
+ if (rv != 0)
+ return (rv);
+
+ ctx = device_get_sysctl_ctx(dev);
+
+ oid = SYSCTL_ADD_NODE(ctx,
+ SYSCTL_CHILDREN(device_get_sysctl_tree(pdev)), OID_AUTO,
+ "coretemp", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
+ "Per-CPU thermal information");
+
+ /*
+ * Add the MIBs to dev.cpu.N and dev.cpu.N.coretemp.
+ */
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(device_get_sysctl_tree(pdev)),
+ OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
+ dev, CORETEMP_TEMP, coretemp_get_val_sysctl, "IK",
+ "Current temperature");
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "delta",
+ CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, CORETEMP_DELTA,
+ coretemp_get_val_sysctl, "I",
+ "Delta between TCC activation and current temperature");
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "resolution",
+ CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, CORETEMP_RESOLUTION,
+ coretemp_get_val_sysctl, "I",
+ "Resolution of CPU thermal sensor");
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "tjmax",
+ CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, CORETEMP_TJMAX,
+ coretemp_get_val_sysctl, "IK",
+ "TCC activation temperature");
+
+ return (0);
+}
+
+static int
+tegra124_coretemp_detach(device_t dev)
+{
+ struct tegra124_coretemp_softc *sc;
+
+ sc = device_get_softc(dev);
+ return (0);
+}
+
+static device_method_t tegra124_coretemp_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_identify, tegra124_coretemp_identify),
+ DEVMETHOD(device_probe, tegra124_coretemp_probe),
+ DEVMETHOD(device_attach, tegra124_coretemp_attach),
+ DEVMETHOD(device_detach, tegra124_coretemp_detach),
+
+ DEVMETHOD_END
+};
+
+static devclass_t tegra124_coretemp_devclass;
+static DEFINE_CLASS_0(tegra124_coretemp, tegra124_coretemp_driver,
+ tegra124_coretemp_methods, sizeof(struct tegra124_coretemp_softc));
+DRIVER_MODULE(tegra124_coretemp, cpu, tegra124_coretemp_driver,
+ tegra124_coretemp_devclass, NULL, NULL);
diff --git a/sys/arm/nvidia/tegra124/tegra124_cpufreq.c b/sys/arm/nvidia/tegra124/tegra124_cpufreq.c
new file mode 100644
index 000000000000..629d8a7b8cfb
--- /dev/null
+++ b/sys/arm/nvidia/tegra124/tegra124_cpufreq.c
@@ -0,0 +1,594 @@
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/cpu.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/regulator/regulator.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/nvidia/tegra_efuse.h>
+
+#include "cpufreq_if.h"
+
+#define XXX
+
+/* CPU voltage table entry */
+struct speedo_entry {
+ uint64_t freq; /* Frequency point */
+ int c0; /* Coeeficient values for */
+ int c1; /* quadratic equation: */
+ int c2; /* c2 * speedo^2 + c1 * speedo + c0 */
+};
+
+struct cpu_volt_def {
+ int min_uvolt; /* Min allowed CPU voltage */
+ int max_uvolt; /* Max allowed CPU voltage */
+ int step_uvolt; /* Step of CPU voltage */
+ int speedo_scale; /* Scaling factor for cvt */
+ int speedo_nitems; /* Size of speedo table */
+ struct speedo_entry *speedo_tbl; /* CPU voltage table */
+};
+
+struct cpu_speed_point {
+ uint64_t freq; /* Frequecy */
+ int uvolt; /* Requested voltage */
+};
+
+static struct speedo_entry tegra124_speedo_dpll_tbl[] =
+{
+ { 204000000ULL, 1112619, -29295, 402},
+ { 306000000ULL, 1150460, -30585, 402},
+ { 408000000ULL, 1190122, -31865, 402},
+ { 510000000ULL, 1231606, -33155, 402},
+ { 612000000ULL, 1274912, -34435, 402},
+ { 714000000ULL, 1320040, -35725, 402},
+ { 816000000ULL, 1366990, -37005, 402},
+ { 918000000ULL, 1415762, -38295, 402},
+ {1020000000ULL, 1466355, -39575, 402},
+ {1122000000ULL, 1518771, -40865, 402},
+ {1224000000ULL, 1573009, -42145, 402},
+ {1326000000ULL, 1629068, -43435, 402},
+ {1428000000ULL, 1686950, -44715, 402},
+ {1530000000ULL, 1746653, -46005, 402},
+ {1632000000ULL, 1808179, -47285, 402},
+ {1734000000ULL, 1871526, -48575, 402},
+ {1836000000ULL, 1936696, -49855, 402},
+ {1938000000ULL, 2003687, -51145, 402},
+ {2014500000ULL, 2054787, -52095, 402},
+ {2116500000ULL, 2124957, -53385, 402},
+ {2218500000ULL, 2196950, -54665, 402},
+ {2320500000ULL, 2270765, -55955, 402},
+ {2320500000ULL, 2270765, -55955, 402},
+ {2422500000ULL, 2346401, -57235, 402},
+ {2524500000ULL, 2437299, -58535, 402},
+};
+
+static struct cpu_volt_def tegra124_cpu_volt_dpll_def =
+{
+ .min_uvolt = 900000, /* 0.9 V */
+ .max_uvolt = 1260000, /* 1.26 */
+ .step_uvolt = 10000, /* 10 mV */
+ .speedo_scale = 100,
+ .speedo_nitems = nitems(tegra124_speedo_dpll_tbl),
+ .speedo_tbl = tegra124_speedo_dpll_tbl,
+};
+
+static struct speedo_entry tegra124_speedo_pllx_tbl[] =
+{
+ { 204000000ULL, 800000, 0, 0},
+ { 306000000ULL, 800000, 0, 0},
+ { 408000000ULL, 800000, 0, 0},
+ { 510000000ULL, 800000, 0, 0},
+ { 612000000ULL, 800000, 0, 0},
+ { 714000000ULL, 800000, 0, 0},
+ { 816000000ULL, 820000, 0, 0},
+ { 918000000ULL, 840000, 0, 0},
+ {1020000000ULL, 880000, 0, 0},
+ {1122000000ULL, 900000, 0, 0},
+ {1224000000ULL, 930000, 0, 0},
+ {1326000000ULL, 960000, 0, 0},
+ {1428000000ULL, 990000, 0, 0},
+ {1530000000ULL, 1020000, 0, 0},
+ {1632000000ULL, 1070000, 0, 0},
+ {1734000000ULL, 1100000, 0, 0},
+ {1836000000ULL, 1140000, 0, 0},
+ {1938000000ULL, 1180000, 0, 0},
+ {2014500000ULL, 1220000, 0, 0},
+ {2116500000ULL, 1260000, 0, 0},
+ {2218500000ULL, 1310000, 0, 0},
+ {2320500000ULL, 1360000, 0, 0},
+ {2397000000ULL, 1400000, 0, 0},
+ {2499000000ULL, 1400000, 0, 0},
+};
+
+static struct cpu_volt_def tegra124_cpu_volt_pllx_def =
+{
+ .min_uvolt = 1000000, /* XXX 0.9 V doesn't work on all boards */
+ .max_uvolt = 1260000, /* 1.26 */
+ .step_uvolt = 10000, /* 10 mV */
+ .speedo_scale = 100,
+ .speedo_nitems = nitems(tegra124_speedo_pllx_tbl),
+ .speedo_tbl = tegra124_speedo_pllx_tbl,
+};
+
+static uint64_t cpu_freq_tbl[] = {
+ 204000000ULL,
+ 306000000ULL,
+ 408000000ULL,
+ 510000000ULL,
+ 612000000ULL,
+ 714000000ULL,
+ 816000000ULL,
+ 918000000ULL,
+ 1020000000ULL,
+ 1122000000ULL,
+ 1224000000ULL,
+ 1326000000ULL,
+ 1428000000ULL,
+ 1530000000ULL,
+ 1632000000ULL,
+ 1734000000ULL,
+ 1836000000ULL,
+ 1938000000ULL,
+ 2014000000ULL,
+ 2116000000ULL,
+ 2218000000ULL,
+ 2320000000ULL,
+ 2422000000ULL,
+ 2524000000ULL,
+};
+
+static uint64_t cpu_max_freq[] = {
+ 2014500000ULL,
+ 2320500000ULL,
+ 2116500000ULL,
+ 2524500000ULL,
+};
+
+struct tegra124_cpufreq_softc {
+ device_t dev;
+ phandle_t node;
+
+ regulator_t supply_vdd_cpu;
+ clk_t clk_cpu_g;
+ clk_t clk_cpu_lp;
+ clk_t clk_pll_x;
+ clk_t clk_pll_p;
+ clk_t clk_dfll;
+
+ int process_id;
+ int speedo_id;
+ int speedo_value;
+
+ uint64_t cpu_max_freq;
+ struct cpu_volt_def *cpu_def;
+ struct cpu_speed_point *speed_points;
+ int nspeed_points;
+
+ struct cpu_speed_point *act_speed_point;
+
+ int latency;
+};
+
+static int cpufreq_lowest_freq = 1;
+TUNABLE_INT("hw.tegra124.cpufreq.lowest_freq", &cpufreq_lowest_freq);
+
+#define DIV_ROUND_CLOSEST(val, div) (((val) + ((div) / 2)) / (div))
+
+#define ROUND_UP(val, div) roundup(val, div)
+#define ROUND_DOWN(val, div) rounddown(val, div)
+
+/*
+ * Compute requesetd voltage for given frequency and SoC process variations,
+ * - compute base voltage from speedo value using speedo table
+ * - round up voltage to next regulator step
+ * - clamp it to regulator limits
+ */
+static int
+freq_to_voltage(struct tegra124_cpufreq_softc *sc, uint64_t freq)
+{
+ int uv, scale, min_uvolt, max_uvolt, step_uvolt;
+ struct speedo_entry *ent;
+ int i;
+
+ /* Get speedo entry with higher frequency */
+ ent = NULL;
+ for (i = 0; i < sc->cpu_def->speedo_nitems; i++) {
+ if (sc->cpu_def->speedo_tbl[i].freq >= freq) {
+ ent = &sc->cpu_def->speedo_tbl[i];
+ break;
+ }
+ }
+ if (ent == NULL)
+ ent = &sc->cpu_def->speedo_tbl[sc->cpu_def->speedo_nitems - 1];
+ scale = sc->cpu_def->speedo_scale;
+
+ /* uV = (c2 * speedo / scale + c1) * speedo / scale + c0) */
+ uv = DIV_ROUND_CLOSEST(ent->c2 * sc->speedo_value, scale);
+ uv = DIV_ROUND_CLOSEST((uv + ent->c1) * sc->speedo_value, scale) +
+ ent->c0;
+ step_uvolt = sc->cpu_def->step_uvolt;
+ /* Round up it to next regulator step */
+ uv = ROUND_UP(uv, step_uvolt);
+
+ /* Clamp result */
+ min_uvolt = ROUND_UP(sc->cpu_def->min_uvolt, step_uvolt);
+ max_uvolt = ROUND_DOWN(sc->cpu_def->max_uvolt, step_uvolt);
+ if (uv < min_uvolt)
+ uv = min_uvolt;
+ if (uv > max_uvolt)
+ uv = max_uvolt;
+ return (uv);
+
+}
+
+static void
+build_speed_points(struct tegra124_cpufreq_softc *sc) {
+ int i;
+
+ sc->nspeed_points = nitems(cpu_freq_tbl);
+ sc->speed_points = malloc(sizeof(struct cpu_speed_point) *
+ sc->nspeed_points, M_DEVBUF, M_NOWAIT);
+ for (i = 0; i < sc->nspeed_points; i++) {
+ sc->speed_points[i].freq = cpu_freq_tbl[i];
+ sc->speed_points[i].uvolt = freq_to_voltage(sc,
+ cpu_freq_tbl[i]);
+ }
+}
+
+static struct cpu_speed_point *
+get_speed_point(struct tegra124_cpufreq_softc *sc, uint64_t freq)
+{
+ int i;
+
+ if (sc->speed_points[0].freq >= freq)
+ return (sc->speed_points + 0);
+
+ for (i = 0; i < sc->nspeed_points - 1; i++) {
+ if (sc->speed_points[i + 1].freq > freq)
+ return (sc->speed_points + i);
+ }
+
+ return (sc->speed_points + sc->nspeed_points - 1);
+}
+
+static int
+tegra124_cpufreq_settings(device_t dev, struct cf_setting *sets, int *count)
+{
+ struct tegra124_cpufreq_softc *sc;
+ int i, j, max_cnt;
+
+ if (sets == NULL || count == NULL)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+ memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * (*count));
+
+ max_cnt = min(sc->nspeed_points, *count);
+ for (i = 0, j = sc->nspeed_points - 1; j >= 0; j--) {
+ if (sc->cpu_max_freq < sc->speed_points[j].freq)
+ continue;
+ sets[i].freq = sc->speed_points[j].freq / 1000000;
+ sets[i].volts = sc->speed_points[j].uvolt / 1000;
+ sets[i].lat = sc->latency;
+ sets[i].dev = dev;
+ i++;
+ }
+ *count = i;
+
+ return (0);
+}
+
+static int
+set_cpu_freq(struct tegra124_cpufreq_softc *sc, uint64_t freq)
+{
+ struct cpu_speed_point *point;
+ int rv;
+
+ point = get_speed_point(sc, freq);
+
+ if (sc->act_speed_point->uvolt < point->uvolt) {
+ /* set cpu voltage */
+ rv = regulator_set_voltage(sc->supply_vdd_cpu,
+ point->uvolt, point->uvolt);
+ DELAY(10000);
+ if (rv != 0)
+ return (rv);
+ }
+
+ /* Switch supermux to PLLP first */
+ rv = clk_set_parent_by_clk(sc->clk_cpu_g, sc->clk_pll_p);
+ if (rv != 0) {
+ device_printf(sc->dev, "Can't set parent to PLLP\n");
+ return (rv);
+ }
+
+ /* Set PLLX frequency */
+ rv = clk_set_freq(sc->clk_pll_x, point->freq, CLK_SET_ROUND_DOWN);
+ if (rv != 0) {
+ device_printf(sc->dev, "Can't set CPU clock frequency\n");
+ return (rv);
+ }
+
+ rv = clk_set_parent_by_clk(sc->clk_cpu_g, sc->clk_pll_x);
+ if (rv != 0) {
+ device_printf(sc->dev, "Can't set parent to PLLX\n");
+ return (rv);
+ }
+
+ if (sc->act_speed_point->uvolt > point->uvolt) {
+ /* set cpu voltage */
+ rv = regulator_set_voltage(sc->supply_vdd_cpu,
+ point->uvolt, point->uvolt);
+ if (rv != 0)
+ return (rv);
+ }
+
+ sc->act_speed_point = point;
+
+ return (0);
+}
+
+static int
+tegra124_cpufreq_set(device_t dev, const struct cf_setting *cf)
+{
+ struct tegra124_cpufreq_softc *sc;
+ uint64_t freq;
+ int rv;
+
+ if (cf == NULL || cf->freq < 0)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+
+ freq = cf->freq;
+ if (freq < cpufreq_lowest_freq)
+ freq = cpufreq_lowest_freq;
+ freq *= 1000000;
+ if (freq >= sc->cpu_max_freq)
+ freq = sc->cpu_max_freq;
+ rv = set_cpu_freq(sc, freq);
+
+ return (rv);
+}
+
+static int
+tegra124_cpufreq_get(device_t dev, struct cf_setting *cf)
+{
+ struct tegra124_cpufreq_softc *sc;
+
+ if (cf == NULL)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+ memset(cf, CPUFREQ_VAL_UNKNOWN, sizeof(*cf));
+ cf->dev = NULL;
+ cf->freq = sc->act_speed_point->freq / 1000000;
+ cf->volts = sc->act_speed_point->uvolt / 1000;
+ /* Transition latency in us. */
+ cf->lat = sc->latency;
+ /* Driver providing this setting. */
+ cf->dev = dev;
+
+ return (0);
+}
+
+static int
+tegra124_cpufreq_type(device_t dev, int *type)
+{
+
+ if (type == NULL)
+ return (EINVAL);
+ *type = CPUFREQ_TYPE_ABSOLUTE;
+
+ return (0);
+}
+
+static int
+get_fdt_resources(struct tegra124_cpufreq_softc *sc, phandle_t node)
+{
+ int rv;
+ device_t parent_dev;
+
+ parent_dev = device_get_parent(sc->dev);
+ rv = regulator_get_by_ofw_property(parent_dev, 0, "vdd-cpu-supply",
+ &sc->supply_vdd_cpu);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'vdd-cpu' regulator\n");
+ return (rv);
+ }
+
+ rv = clk_get_by_ofw_name(parent_dev, 0, "cpu_g", &sc->clk_cpu_g);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'cpu_g' clock: %d\n", rv);
+ return (ENXIO);
+ }
+
+ rv = clk_get_by_ofw_name(parent_dev, 0, "cpu_lp", &sc->clk_cpu_lp);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'cpu_lp' clock\n");
+ return (ENXIO);
+ }
+
+ rv = clk_get_by_ofw_name(parent_dev, 0, "pll_x", &sc->clk_pll_x);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'pll_x' clock\n");
+ return (ENXIO);
+ }
+ rv = clk_get_by_ofw_name(parent_dev, 0, "pll_p", &sc->clk_pll_p);
+ if (rv != 0) {
+ device_printf(parent_dev, "Cannot get 'pll_p' clock\n");
+ return (ENXIO);
+ }
+ rv = clk_get_by_ofw_name(parent_dev, 0, "dfll", &sc->clk_dfll);
+ if (rv != 0) {
+ /* XXX DPLL is not implemented yet */
+/*
+ device_printf(sc->dev, "Cannot get 'dfll' clock\n");
+ return (ENXIO);
+*/
+ }
+ return (0);
+}
+
+static void
+tegra124_cpufreq_identify(driver_t *driver, device_t parent)
+{
+ phandle_t root;
+
+ root = OF_finddevice("/");
+ if (!ofw_bus_node_is_compatible(root, "nvidia,tegra124"))
+ return;
+
+ if (device_get_unit(parent) != 0)
+ return;
+ if (device_find_child(parent, "tegra124_cpufreq", -1) != NULL)
+ return;
+ if (BUS_ADD_CHILD(parent, 0, "tegra124_cpufreq", -1) == NULL)
+ device_printf(parent, "add child failed\n");
+}
+
+static int
+tegra124_cpufreq_probe(device_t dev)
+{
+
+ device_set_desc(dev, "CPU Frequency Control");
+
+ return (0);
+}
+
+static int
+tegra124_cpufreq_attach(device_t dev)
+{
+ struct tegra124_cpufreq_softc *sc;
+ uint64_t freq;
+ int rv;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ sc->node = ofw_bus_get_node(device_get_parent(dev));
+
+ sc->process_id = tegra_sku_info.cpu_process_id;
+ sc->speedo_id = tegra_sku_info.cpu_speedo_id;
+ sc->speedo_value = tegra_sku_info.cpu_speedo_value;
+
+ /* Tegra 124 */
+ /* XXX DPLL is not implemented yet */
+ if (1)
+ sc->cpu_def = &tegra124_cpu_volt_pllx_def;
+ else
+ sc->cpu_def = &tegra124_cpu_volt_dpll_def;
+
+ rv = get_fdt_resources(sc, sc->node);
+ if (rv != 0) {
+ return (rv);
+ }
+
+ build_speed_points(sc);
+
+ rv = clk_get_freq(sc->clk_cpu_g, &freq);
+ if (rv != 0) {
+ device_printf(dev, "Can't get CPU clock frequency\n");
+ return (rv);
+ }
+ if (sc->speedo_id < nitems(cpu_max_freq))
+ sc->cpu_max_freq = cpu_max_freq[sc->speedo_id];
+ else
+ sc->cpu_max_freq = cpu_max_freq[0];
+ sc->act_speed_point = get_speed_point(sc, freq);
+
+ /* Set safe startup CPU frequency. */
+ rv = set_cpu_freq(sc, 1632000000);
+ if (rv != 0) {
+ device_printf(dev, "Can't set initial CPU clock frequency\n");
+ return (rv);
+ }
+
+ /* This device is controlled by cpufreq(4). */
+ cpufreq_register(dev);
+
+ return (0);
+}
+
+static int
+tegra124_cpufreq_detach(device_t dev)
+{
+ struct tegra124_cpufreq_softc *sc;
+
+ sc = device_get_softc(dev);
+ cpufreq_unregister(dev);
+
+ if (sc->supply_vdd_cpu != NULL)
+ regulator_release(sc->supply_vdd_cpu);
+
+ if (sc->clk_cpu_g != NULL)
+ clk_release(sc->clk_cpu_g);
+ if (sc->clk_cpu_lp != NULL)
+ clk_release(sc->clk_cpu_lp);
+ if (sc->clk_pll_x != NULL)
+ clk_release(sc->clk_pll_x);
+ if (sc->clk_pll_p != NULL)
+ clk_release(sc->clk_pll_p);
+ if (sc->clk_dfll != NULL)
+ clk_release(sc->clk_dfll);
+ return (0);
+}
+
+static device_method_t tegra124_cpufreq_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_identify, tegra124_cpufreq_identify),
+ DEVMETHOD(device_probe, tegra124_cpufreq_probe),
+ DEVMETHOD(device_attach, tegra124_cpufreq_attach),
+ DEVMETHOD(device_detach, tegra124_cpufreq_detach),
+
+ /* cpufreq interface */
+ DEVMETHOD(cpufreq_drv_set, tegra124_cpufreq_set),
+ DEVMETHOD(cpufreq_drv_get, tegra124_cpufreq_get),
+ DEVMETHOD(cpufreq_drv_settings, tegra124_cpufreq_settings),
+ DEVMETHOD(cpufreq_drv_type, tegra124_cpufreq_type),
+
+ DEVMETHOD_END
+};
+
+static devclass_t tegra124_cpufreq_devclass;
+static DEFINE_CLASS_0(tegra124_cpufreq, tegra124_cpufreq_driver,
+ tegra124_cpufreq_methods, sizeof(struct tegra124_cpufreq_softc));
+DRIVER_MODULE(tegra124_cpufreq, cpu, tegra124_cpufreq_driver,
+ tegra124_cpufreq_devclass, NULL, NULL);
diff --git a/sys/arm/nvidia/tegra124/tegra124_machdep.c b/sys/arm/nvidia/tegra124/tegra124_machdep.c
new file mode 100644
index 000000000000..0bb55666c175
--- /dev/null
+++ b/sys/arm/nvidia/tegra124/tegra124_machdep.c
@@ -0,0 +1,155 @@
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "opt_platform.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/devmap.h>
+#include <sys/lock.h>
+#include <sys/reboot.h>
+#include <sys/systm.h>
+
+#include <vm/vm.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+#include <machine/intr.h>
+#include <machine/machdep.h>
+#include <machine/platformvar.h>
+
+#include <dev/ofw/openfirm.h>
+
+#include <arm/nvidia/tegra124/tegra124_mp.h>
+
+#include "platform_if.h"
+
+#define PMC_PHYSBASE 0x7000e400
+#define PMC_SIZE 0x400
+#define PMC_CONTROL_REG 0x0
+#define PMC_SCRATCH0 0x50
+#define PMC_SCRATCH0_MODE_RECOVERY (1 << 31)
+#define PMC_SCRATCH0_MODE_BOOTLOADER (1 << 30)
+#define PMC_SCRATCH0_MODE_RCM (1 << 1)
+#define PMC_SCRATCH0_MODE_MASK (PMC_SCRATCH0_MODE_RECOVERY | \
+ PMC_SCRATCH0_MODE_BOOTLOADER | \
+ PMC_SCRATCH0_MODE_RCM)
+
+static platform_attach_t tegra124_attach;
+static platform_devmap_init_t tegra124_devmap_init;
+static platform_late_init_t tegra124_late_init;
+static platform_cpu_reset_t tegra124_cpu_reset;
+
+static int
+tegra124_attach(platform_t plat)
+{
+
+ return (0);
+}
+
+static void
+tegra124_late_init(platform_t plat)
+{
+
+}
+
+/*
+ * Set up static device mappings.
+ *
+ */
+static int
+tegra124_devmap_init(platform_t plat)
+{
+
+ devmap_add_entry(0x70000000, 0x01000000);
+ return (0);
+}
+
+static void
+tegra124_cpu_reset(platform_t plat)
+{
+ bus_space_handle_t pmc;
+ uint32_t reg;
+
+ printf("Resetting...\n");
+ bus_space_map(fdtbus_bs_tag, PMC_PHYSBASE, PMC_SIZE, 0, &pmc);
+
+ reg = bus_space_read_4(fdtbus_bs_tag, pmc, PMC_SCRATCH0);
+ reg &= PMC_SCRATCH0_MODE_MASK;
+ bus_space_write_4(fdtbus_bs_tag, pmc, PMC_SCRATCH0,
+ reg | PMC_SCRATCH0_MODE_BOOTLOADER); /* boot to bootloader */
+ bus_space_read_4(fdtbus_bs_tag, pmc, PMC_SCRATCH0);
+
+ reg = bus_space_read_4(fdtbus_bs_tag, pmc, PMC_CONTROL_REG);
+ spinlock_enter();
+ dsb();
+ bus_space_write_4(fdtbus_bs_tag, pmc, PMC_CONTROL_REG, reg | 0x10);
+ bus_space_read_4(fdtbus_bs_tag, pmc, PMC_CONTROL_REG);
+ while(1)
+ ;
+
+}
+
+/*
+ * Early putc routine for EARLY_PRINTF support. To use, add to kernel config:
+ * option SOCDEV_PA=0x70000000
+ * option SOCDEV_VA=0x70000000
+ * option EARLY_PRINTF
+ */
+#if 0
+#ifdef EARLY_PRINTF
+static void
+tegra124_early_putc(int c)
+{
+
+ volatile uint32_t * UART_STAT_REG = (uint32_t *)(0x70006314);
+ volatile uint32_t * UART_TX_REG = (uint32_t *)(0x70006300);
+ const uint32_t UART_TXRDY = (1 << 6);
+ while ((*UART_STAT_REG & UART_TXRDY) == 0)
+ continue;
+ *UART_TX_REG = c;
+}
+early_putc_t *early_putc = tegra124_early_putc;
+#endif
+#endif
+
+static platform_method_t tegra124_methods[] = {
+ PLATFORMMETHOD(platform_attach, tegra124_attach),
+ PLATFORMMETHOD(platform_devmap_init, tegra124_devmap_init),
+ PLATFORMMETHOD(platform_late_init, tegra124_late_init),
+ PLATFORMMETHOD(platform_cpu_reset, tegra124_cpu_reset),
+
+#ifdef SMP
+ PLATFORMMETHOD(platform_mp_start_ap, tegra124_mp_start_ap),
+ PLATFORMMETHOD(platform_mp_setmaxid, tegra124_mp_setmaxid),
+#endif
+ PLATFORMMETHOD_END,
+};
+
+FDT_PLATFORM_DEF(tegra124, "Nvidia Jetson-TK1", 0, "nvidia,jetson-tk1", 120);
diff --git a/sys/arm/nvidia/tegra124/tegra124_mp.c b/sys/arm/nvidia/tegra124/tegra124_mp.c
new file mode 100644
index 000000000000..19a1a2cfc99e
--- /dev/null
+++ b/sys/arm/nvidia/tegra124/tegra124_mp.c
@@ -0,0 +1,126 @@
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/smp.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/cpu.h>
+#include <machine/intr.h>
+#include <machine/fdt.h>
+#include <machine/smp.h>
+#include <machine/platformvar.h>
+#include <machine/pmap.h>
+
+#include <arm/nvidia/tegra124/tegra124_mp.h>
+
+#define PMC_PHYSBASE 0x7000e400
+#define PMC_SIZE 0x400
+#define PMC_CONTROL_REG 0x0
+#define PMC_PWRGATE_TOGGLE 0x30
+#define PCM_PWRGATE_TOGGLE_START (1 << 8)
+#define PMC_PWRGATE_STATUS 0x38
+
+#define TEGRA_EXCEPTION_VECTORS_BASE 0x6000F000 /* exception vectors */
+#define TEGRA_EXCEPTION_VECTORS_SIZE 1024
+#define TEGRA_EXCEPTION_VECTOR_ENTRY 0x100
+
+void
+tegra124_mp_setmaxid(platform_t plat)
+{
+ int ncpu;
+
+ /* If we've already set the global vars don't bother to do it again. */
+ if (mp_ncpus != 0)
+ return;
+
+ /* Read current CP15 Cache Size ID Register */
+ ncpu = cp15_l2ctlr_get();
+ ncpu = CPUV7_L2CTLR_NPROC(ncpu);
+
+ mp_ncpus = ncpu;
+ mp_maxid = ncpu - 1;
+}
+
+void
+tegra124_mp_start_ap(platform_t plat)
+{
+ bus_space_handle_t pmc;
+ bus_space_handle_t exvec;
+ int i;
+ uint32_t val;
+ uint32_t mask;
+
+ if (bus_space_map(fdtbus_bs_tag, PMC_PHYSBASE, PMC_SIZE, 0, &pmc) != 0)
+ panic("Couldn't map the PMC\n");
+ if (bus_space_map(fdtbus_bs_tag, TEGRA_EXCEPTION_VECTORS_BASE,
+ TEGRA_EXCEPTION_VECTORS_SIZE, 0, &exvec) != 0)
+ panic("Couldn't map the exception vectors\n");
+
+ bus_space_write_4(fdtbus_bs_tag, exvec , TEGRA_EXCEPTION_VECTOR_ENTRY,
+ pmap_kextract((vm_offset_t)mpentry));
+ bus_space_read_4(fdtbus_bs_tag, exvec , TEGRA_EXCEPTION_VECTOR_ENTRY);
+
+ /* Wait until POWERGATE is ready (max 20 APB cycles). */
+ do {
+ val = bus_space_read_4(fdtbus_bs_tag, pmc,
+ PMC_PWRGATE_TOGGLE);
+ } while ((val & PCM_PWRGATE_TOGGLE_START) != 0);
+
+ for (i = 1; i < mp_ncpus; i++) {
+ val = bus_space_read_4(fdtbus_bs_tag, pmc, PMC_PWRGATE_STATUS);
+ mask = 1 << (i + 8); /* cpu mask */
+ if ((val & mask) == 0) {
+ /* Wait until POWERGATE is ready (max 20 APB cycles). */
+ do {
+ val = bus_space_read_4(fdtbus_bs_tag, pmc,
+ PMC_PWRGATE_TOGGLE);
+ } while ((val & PCM_PWRGATE_TOGGLE_START) != 0);
+ bus_space_write_4(fdtbus_bs_tag, pmc,
+ PMC_PWRGATE_TOGGLE,
+ PCM_PWRGATE_TOGGLE_START | (8 + i));
+
+ /* Wait until CPU is powered */
+ do {
+ val = bus_space_read_4(fdtbus_bs_tag, pmc,
+ PMC_PWRGATE_STATUS);
+ } while ((val & mask) == 0);
+ }
+ }
+ dsb();
+ sev();
+ bus_space_unmap(fdtbus_bs_tag, pmc, PMC_SIZE);
+ bus_space_unmap(fdtbus_bs_tag, exvec, TEGRA_EXCEPTION_VECTORS_SIZE);
+}
diff --git a/sys/arm/nvidia/tegra124/tegra124_mp.h b/sys/arm/nvidia/tegra124/tegra124_mp.h
new file mode 100644
index 000000000000..fadf9d1aacea
--- /dev/null
+++ b/sys/arm/nvidia/tegra124/tegra124_mp.h
@@ -0,0 +1,35 @@
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _TEGRA124_MP_H_
+#define _TEGRA124_MP_H_
+
+void tegra124_mp_setmaxid(platform_t plat);
+void tegra124_mp_start_ap(platform_t plat);
+
+#endif /*_TEGRA124_MP_H_*/
diff --git a/sys/arm/nvidia/tegra124/tegra124_pmc.c b/sys/arm/nvidia/tegra124/tegra124_pmc.c
new file mode 100644
index 000000000000..2af3826587cd
--- /dev/null
+++ b/sys/arm/nvidia/tegra124/tegra124_pmc.c
@@ -0,0 +1,562 @@
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/systm.h>
+
+#include <machine/bus.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/nvidia/tegra_pmc.h>
+
+#define PMC_CNTRL 0x000
+#define PMC_CNTRL_CPUPWRGOOD_SEL_MASK (0x3 << 20)
+#define PMC_CNTRL_CPUPWRGOOD_SEL_SHIFT 20
+#define PMC_CNTRL_CPUPWRGOOD_EN (1 << 19)
+#define PMC_CNTRL_FUSE_OVERRIDE (1 << 18)
+#define PMC_CNTRL_INTR_POLARITY (1 << 17)
+#define PMC_CNTRL_CPU_PWRREQ_OE (1 << 16)
+#define PMC_CNTRL_CPU_PWRREQ_POLARITY (1 << 15)
+#define PMC_CNTRL_SIDE_EFFECT_LP0 (1 << 14)
+#define PMC_CNTRL_AOINIT (1 << 13)
+#define PMC_CNTRL_PWRGATE_DIS (1 << 12)
+#define PMC_CNTRL_SYSCLK_OE (1 << 11)
+#define PMC_CNTRL_SYSCLK_POLARITY (1 << 10)
+#define PMC_CNTRL_PWRREQ_OE (1 << 9)
+#define PMC_CNTRL_PWRREQ_POLARITY (1 << 8)
+#define PMC_CNTRL_BLINK_EN (1 << 7)
+#define PMC_CNTRL_GLITCHDET_DIS (1 << 6)
+#define PMC_CNTRL_LATCHWAKE_EN (1 << 5)
+#define PMC_CNTRL_MAIN_RST (1 << 4)
+#define PMC_CNTRL_KBC_RST (1 << 3)
+#define PMC_CNTRL_RTC_RST (1 << 2)
+#define PMC_CNTRL_RTC_CLK_DIS (1 << 1)
+#define PMC_CNTRL_KBC_CLK_DIS (1 << 0)
+
+#define PMC_DPD_SAMPLE 0x020
+
+#define PMC_CLAMP_STATUS 0x02C
+#define PMC_CLAMP_STATUS_PARTID(x) (1 << ((x) & 0x1F))
+
+#define PMC_PWRGATE_TOGGLE 0x030
+#define PMC_PWRGATE_TOGGLE_START (1 << 8)
+#define PMC_PWRGATE_TOGGLE_PARTID(x) (((x) & 0x1F) << 0)
+
+#define PMC_REMOVE_CLAMPING_CMD 0x034
+#define PMC_REMOVE_CLAMPING_CMD_PARTID(x) (1 << ((x) & 0x1F))
+
+#define PMC_PWRGATE_STATUS 0x038
+#define PMC_PWRGATE_STATUS_PARTID(x) (1 << ((x) & 0x1F))
+
+#define PMC_SCRATCH0 0x050
+#define PMC_SCRATCH0_MODE_RECOVERY (1 << 31)
+#define PMC_SCRATCH0_MODE_BOOTLOADER (1 << 30)
+#define PMC_SCRATCH0_MODE_RCM (1 << 1)
+#define PMC_SCRATCH0_MODE_MASK (PMC_SCRATCH0_MODE_RECOVERY | \
+ PMC_SCRATCH0_MODE_BOOTLOADER | \
+ PMC_SCRATCH0_MODE_RCM)
+
+#define PMC_CPUPWRGOOD_TIMER 0x0c8
+#define PMC_CPUPWROFF_TIMER 0x0cc
+
+#define PMC_SCRATCH41 0x140
+
+#define PMC_SENSOR_CTRL 0x1b0
+#define PMC_SENSOR_CTRL_BLOCK_SCRATCH_WRITE (1 << 2)
+#define PMC_SENSOR_CTRL_ENABLE_RST (1 << 1)
+#define PMC_SENSOR_CTRL_ENABLE_PG (1 << 0)
+
+#define PMC_IO_DPD_REQ 0x1b8
+#define PMC_IO_DPD_REQ_CODE_IDLE (0 << 30)
+#define PMC_IO_DPD_REQ_CODE_OFF (1 << 30)
+#define PMC_IO_DPD_REQ_CODE_ON (2 << 30)
+#define PMC_IO_DPD_REQ_CODE_MASK (3 << 30)
+
+#define PMC_IO_DPD_STATUS 0x1bc
+#define PMC_IO_DPD_STATUS_HDMI (1 << 28)
+#define PMC_IO_DPD2_REQ 0x1c0
+#define PMC_IO_DPD2_STATUS 0x1c4
+#define PMC_IO_DPD2_STATUS_HV (1 << 6)
+#define PMC_SEL_DPD_TIM 0x1c8
+
+#define PMC_SCRATCH54 0x258
+#define PMC_SCRATCH54_DATA_SHIFT 8
+#define PMC_SCRATCH54_ADDR_SHIFT 0
+
+#define PMC_SCRATCH55 0x25c
+#define PMC_SCRATCH55_RST_ENABLE (1 << 31)
+#define PMC_SCRATCH55_CNTRL_TYPE (1 << 30)
+#define PMC_SCRATCH55_CNTRL_ID_SHIFT 27
+#define PMC_SCRATCH55_CNTRL_ID_MASK 0x07
+#define PMC_SCRATCH55_PINMUX_SHIFT 24
+#define PMC_SCRATCH55_PINMUX_MASK 0x07
+#define PMC_SCRATCH55_CHECKSUM_SHIFT 16
+#define PMC_SCRATCH55_CHECKSUM_MASK 0xFF
+#define PMC_SCRATCH55_16BITOP (1 << 15)
+#define PMC_SCRATCH55_I2CSLV1_SHIFT 0
+#define PMC_SCRATCH55_I2CSLV1_MASK 0x7F
+
+#define PMC_GPU_RG_CNTRL 0x2d4
+
+#define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v))
+#define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r))
+
+#define PMC_LOCK(_sc) mtx_lock(&(_sc)->mtx)
+#define PMC_UNLOCK(_sc) mtx_unlock(&(_sc)->mtx)
+#define PMC_LOCK_INIT(_sc) mtx_init(&(_sc)->mtx, \
+ device_get_nameunit(_sc->dev), "tegra124_pmc", MTX_DEF)
+#define PMC_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->mtx);
+#define PMC_ASSERT_LOCKED(_sc) mtx_assert(&(_sc)->mtx, MA_OWNED);
+#define PMC_ASSERT_UNLOCKED(_sc) mtx_assert(&(_sc)->mtx, MA_NOTOWNED);
+
+struct tegra124_pmc_softc {
+ device_t dev;
+ struct resource *mem_res;
+ clk_t clk;
+ struct mtx mtx;
+
+ uint32_t rate;
+ enum tegra_suspend_mode suspend_mode;
+ uint32_t cpu_good_time;
+ uint32_t cpu_off_time;
+ uint32_t core_osc_time;
+ uint32_t core_pmu_time;
+ uint32_t core_off_time;
+ int corereq_high;
+ int sysclkreq_high;
+ int combined_req;
+ int cpu_pwr_good_en;
+ uint32_t lp0_vec_phys;
+ uint32_t lp0_vec_size;
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"nvidia,tegra124-pmc", 1},
+ {NULL, 0},
+};
+
+static struct tegra124_pmc_softc *pmc_sc;
+
+static inline struct tegra124_pmc_softc *
+tegra124_pmc_get_sc(void)
+{
+ if (pmc_sc == NULL)
+ panic("To early call to Tegra PMC driver.\n");
+ return (pmc_sc);
+}
+
+static int
+tegra124_pmc_set_powergate(struct tegra124_pmc_softc *sc,
+ enum tegra_powergate_id id, int ena)
+{
+ uint32_t reg;
+ int i;
+
+ PMC_LOCK(sc);
+
+ reg = RD4(sc, PMC_PWRGATE_STATUS) & PMC_PWRGATE_STATUS_PARTID(id);
+ if (((reg != 0) && ena) || ((reg == 0) && !ena)) {
+ PMC_UNLOCK(sc);
+ return (0);
+ }
+
+ for (i = 100; i > 0; i--) {
+ reg = RD4(sc, PMC_PWRGATE_TOGGLE);
+ if ((reg & PMC_PWRGATE_TOGGLE_START) == 0)
+ break;
+ DELAY(1);
+ }
+ if (i <= 0)
+ device_printf(sc->dev,
+ "Timeout when waiting for TOGGLE_START\n");
+
+ WR4(sc, PMC_PWRGATE_TOGGLE,
+ PMC_PWRGATE_TOGGLE_START | PMC_PWRGATE_TOGGLE_PARTID(id));
+
+ for (i = 100; i > 0; i--) {
+ reg = RD4(sc, PMC_PWRGATE_TOGGLE);
+ if ((reg & PMC_PWRGATE_TOGGLE_START) == 0)
+ break;
+ DELAY(1);
+ }
+ if (i <= 0)
+ device_printf(sc->dev,
+ "Timeout when waiting for TOGGLE_START\n");
+ PMC_UNLOCK(sc);
+ return (0);
+}
+
+int
+tegra_powergate_remove_clamping(enum tegra_powergate_id id)
+{
+ struct tegra124_pmc_softc *sc;
+ uint32_t reg;
+ enum tegra_powergate_id swid;
+ int i;
+
+ sc = tegra124_pmc_get_sc();
+
+ if (id == TEGRA_POWERGATE_3D) {
+ WR4(sc, PMC_GPU_RG_CNTRL, 0);
+ return (0);
+ }
+
+ reg = RD4(sc, PMC_PWRGATE_STATUS);
+ if ((reg & PMC_PWRGATE_STATUS_PARTID(id)) == 0)
+ panic("Attempt to remove clamping for unpowered partition.\n");
+
+ if (id == TEGRA_POWERGATE_PCX)
+ swid = TEGRA_POWERGATE_VDE;
+ else if (id == TEGRA_POWERGATE_VDE)
+ swid = TEGRA_POWERGATE_PCX;
+ else
+ swid = id;
+ WR4(sc, PMC_REMOVE_CLAMPING_CMD, PMC_REMOVE_CLAMPING_CMD_PARTID(swid));
+
+ for (i = 100; i > 0; i--) {
+ reg = RD4(sc, PMC_REMOVE_CLAMPING_CMD);
+ if ((reg & PMC_REMOVE_CLAMPING_CMD_PARTID(swid)) == 0)
+ break;
+ DELAY(1);
+ }
+ if (i <= 0)
+ device_printf(sc->dev, "Timeout when remove clamping\n");
+
+ reg = RD4(sc, PMC_CLAMP_STATUS);
+ if ((reg & PMC_CLAMP_STATUS_PARTID(id)) != 0)
+ panic("Cannot remove clamping\n");
+
+ return (0);
+}
+
+int
+tegra_powergate_is_powered(enum tegra_powergate_id id)
+{
+ struct tegra124_pmc_softc *sc;
+ uint32_t reg;
+
+ sc = tegra124_pmc_get_sc();
+
+ reg = RD4(sc, PMC_PWRGATE_STATUS);
+ return ((reg & PMC_PWRGATE_STATUS_PARTID(id)) ? 1 : 0);
+}
+
+int
+tegra_powergate_power_on(enum tegra_powergate_id id)
+{
+ struct tegra124_pmc_softc *sc;
+ int rv, i;
+
+ sc = tegra124_pmc_get_sc();
+
+ rv = tegra124_pmc_set_powergate(sc, id, 1);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot set powergate: %d\n", id);
+ return (rv);
+ }
+
+ for (i = 100; i > 0; i--) {
+ if (tegra_powergate_is_powered(id))
+ break;
+ DELAY(1);
+ }
+ if (i <= 0)
+ device_printf(sc->dev, "Timeout when waiting on power up\n");
+
+ return (rv);
+}
+
+int
+tegra_powergate_power_off(enum tegra_powergate_id id)
+{
+ struct tegra124_pmc_softc *sc;
+ int rv, i;
+
+ sc = tegra124_pmc_get_sc();
+
+ rv = tegra124_pmc_set_powergate(sc, id, 0);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot set powergate: %d\n", id);
+ return (rv);
+ }
+ for (i = 100; i > 0; i--) {
+ if (!tegra_powergate_is_powered(id))
+ break;
+ DELAY(1);
+ }
+ if (i <= 0)
+ device_printf(sc->dev, "Timeout when waiting on power off\n");
+
+ return (rv);
+}
+
+int
+tegra_powergate_sequence_power_up(enum tegra_powergate_id id, clk_t clk,
+ hwreset_t rst)
+{
+ struct tegra124_pmc_softc *sc;
+ int rv;
+
+ sc = tegra124_pmc_get_sc();
+
+ rv = hwreset_assert(rst);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot assert reset\n");
+ return (rv);
+ }
+
+ rv = clk_stop(clk);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot stop clock\n");
+ goto clk_fail;
+ }
+
+ rv = tegra_powergate_power_on(id);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot power on powergate\n");
+ goto clk_fail;
+ }
+
+ rv = clk_enable(clk);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot enable clock\n");
+ goto clk_fail;
+ }
+ DELAY(20);
+
+ rv = tegra_powergate_remove_clamping(id);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot remove clamping\n");
+ goto fail;
+ }
+ rv = hwreset_deassert(rst);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot unreset reset\n");
+ goto fail;
+ }
+ return 0;
+
+fail:
+ clk_disable(clk);
+clk_fail:
+ hwreset_assert(rst);
+ tegra_powergate_power_off(id);
+ return (rv);
+}
+
+static int
+tegra124_pmc_parse_fdt(struct tegra124_pmc_softc *sc, phandle_t node)
+{
+ int rv;
+ uint32_t tmp;
+ uint32_t tmparr[2];
+
+ rv = OF_getencprop(node, "nvidia,suspend-mode", &tmp, sizeof(tmp));
+ if (rv > 0) {
+ switch (tmp) {
+ case 0:
+ sc->suspend_mode = TEGRA_SUSPEND_LP0;
+ break;
+
+ case 1:
+ sc->suspend_mode = TEGRA_SUSPEND_LP1;
+ break;
+
+ case 2:
+ sc->suspend_mode = TEGRA_SUSPEND_LP2;
+ break;
+
+ default:
+ sc->suspend_mode = TEGRA_SUSPEND_NONE;
+ break;
+ }
+ }
+
+ rv = OF_getencprop(node, "nvidia,cpu-pwr-good-time", &tmp, sizeof(tmp));
+ if (rv > 0) {
+ sc->cpu_good_time = tmp;
+ sc->suspend_mode = TEGRA_SUSPEND_NONE;
+ }
+
+ rv = OF_getencprop(node, "nvidia,cpu-pwr-off-time", &tmp, sizeof(tmp));
+ if (rv > 0) {
+ sc->cpu_off_time = tmp;
+ sc->suspend_mode = TEGRA_SUSPEND_NONE;
+ }
+
+ rv = OF_getencprop(node, "nvidia,core-pwr-good-time", tmparr,
+ sizeof(tmparr));
+ if (rv == sizeof(tmparr)) {
+ sc->core_osc_time = tmparr[0];
+ sc->core_pmu_time = tmparr[1];
+ sc->suspend_mode = TEGRA_SUSPEND_NONE;
+ }
+
+ rv = OF_getencprop(node, "nvidia,core-pwr-off-time", &tmp, sizeof(tmp));
+ if (rv > 0) {
+ sc->core_off_time = tmp;
+ sc->suspend_mode = TEGRA_SUSPEND_NONE;
+ }
+
+ sc->corereq_high =
+ OF_hasprop(node, "nvidia,core-power-req-active-high");
+ sc->sysclkreq_high =
+ OF_hasprop(node, "nvidia,sys-clock-req-active-high");
+ sc->combined_req =
+ OF_hasprop(node, "nvidia,combined-power-req");
+ sc->cpu_pwr_good_en =
+ OF_hasprop(node, "nvidia,cpu-pwr-good-en");
+
+ rv = OF_getencprop(node, "nvidia,lp0-vec", tmparr, sizeof(tmparr));
+ if (rv == sizeof(tmparr)) {
+ sc->lp0_vec_phys = tmparr[0];
+ sc->core_pmu_time = tmparr[1];
+ sc->lp0_vec_size = TEGRA_SUSPEND_NONE;
+ if (sc->suspend_mode == TEGRA_SUSPEND_LP0)
+ sc->suspend_mode = TEGRA_SUSPEND_LP1;
+ }
+ return 0;
+}
+
+static int
+tegra124_pmc_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+ return (ENXIO);
+
+ device_set_desc(dev, "Tegra PMC");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+tegra124_pmc_detach(device_t dev)
+{
+
+ /* This device is always present. */
+ return (EBUSY);
+}
+
+static int
+tegra124_pmc_attach(device_t dev)
+{
+ struct tegra124_pmc_softc *sc;
+ int rid, rv;
+ uint32_t reg;
+ phandle_t node;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ node = ofw_bus_get_node(dev);
+
+ rv = tegra124_pmc_parse_fdt(sc, node);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot parse FDT data\n");
+ return (rv);
+ }
+
+ rv = clk_get_by_ofw_name(sc->dev, 0, "pclk", &sc->clk);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get \"pclk\" clock\n");
+ return (ENXIO);
+ }
+
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "Cannot allocate memory resources\n");
+ return (ENXIO);
+ }
+
+ PMC_LOCK_INIT(sc);
+
+ /* Enable CPU power request. */
+ reg = RD4(sc, PMC_CNTRL);
+ reg |= PMC_CNTRL_CPU_PWRREQ_OE;
+ WR4(sc, PMC_CNTRL, reg);
+
+ /* Set sysclk output polarity */
+ reg = RD4(sc, PMC_CNTRL);
+ if (sc->sysclkreq_high)
+ reg &= ~PMC_CNTRL_SYSCLK_POLARITY;
+ else
+ reg |= PMC_CNTRL_SYSCLK_POLARITY;
+ WR4(sc, PMC_CNTRL, reg);
+
+ /* Enable sysclk request. */
+ reg = RD4(sc, PMC_CNTRL);
+ reg |= PMC_CNTRL_SYSCLK_OE;
+ WR4(sc, PMC_CNTRL, reg);
+
+ /*
+ * Remove HDMI from deep power down mode.
+ * XXX mote this to HDMI driver
+ */
+ reg = RD4(sc, PMC_IO_DPD_STATUS);
+ reg &= ~ PMC_IO_DPD_STATUS_HDMI;
+ WR4(sc, PMC_IO_DPD_STATUS, reg);
+
+ reg = RD4(sc, PMC_IO_DPD2_STATUS);
+ reg &= ~ PMC_IO_DPD2_STATUS_HV;
+ WR4(sc, PMC_IO_DPD2_STATUS, reg);
+
+ if (pmc_sc != NULL)
+ panic("tegra124_pmc: double driver attach");
+ pmc_sc = sc;
+ return (0);
+}
+
+static device_method_t tegra124_pmc_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, tegra124_pmc_probe),
+ DEVMETHOD(device_attach, tegra124_pmc_attach),
+ DEVMETHOD(device_detach, tegra124_pmc_detach),
+
+ DEVMETHOD_END
+};
+
+static devclass_t tegra124_pmc_devclass;
+static DEFINE_CLASS_0(pmc, tegra124_pmc_driver, tegra124_pmc_methods,
+ sizeof(struct tegra124_pmc_softc));
+EARLY_DRIVER_MODULE(tegra124_pmc, simplebus, tegra124_pmc_driver,
+ tegra124_pmc_devclass, NULL, NULL, 70);
diff --git a/sys/arm/nvidia/tegra124/tegra124_xusbpadctl.c b/sys/arm/nvidia/tegra124/tegra124_xusbpadctl.c
new file mode 100644
index 000000000000..f41234197335
--- /dev/null
+++ b/sys/arm/nvidia/tegra124/tegra124_xusbpadctl.c
@@ -0,0 +1,1206 @@
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/extres/phy/phy.h>
+#include <dev/extres/regulator/regulator.h>
+#include <dev/fdt/fdt_common.h>
+#include <dev/fdt/fdt_pinctrl.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/nvidia/tegra_efuse.h>
+
+#include <dt-bindings/pinctrl/pinctrl-tegra-xusb.h>
+
+#include "phydev_if.h"
+
+/* FUSE calibration data. */
+#define FUSE_XUSB_CALIB 0x0F0
+#define FUSE_XUSB_CALIB_HS_CURR_LEVEL_123(x) (((x) >> 15) & 0x3F);
+#define FUSE_XUSB_CALIB_HS_IREF_CAP(x) (((x) >> 13) & 0x03);
+#define FUSE_XUSB_CALIB_HS_SQUELCH_LEVEL(x) (((x) >> 11) & 0x03);
+#define FUSE_XUSB_CALIB_HS_TERM_RANGE_ADJ(x) (((x) >> 7) & 0x0F);
+#define FUSE_XUSB_CALIB_HS_CURR_LEVEL_0(x) (((x) >> 0) & 0x3F);
+
+/* Registers. */
+#define XUSB_PADCTL_USB2_PAD_MUX 0x004
+
+#define XUSB_PADCTL_USB2_PORT_CAP 0x008
+#define USB2_PORT_CAP_ULPI_PORT_INTERNAL (1 << 25)
+#define USB2_PORT_CAP_ULPI_PORT_CAP (1 << 24)
+#define USB2_PORT_CAP_PORT_REVERSE_ID(p) (1 << (3 + (p) * 4))
+#define USB2_PORT_CAP_PORT_INTERNAL(p) (1 << (2 + (p) * 4))
+#define USB2_PORT_CAP_PORT_CAP(p, x) (((x) & 3) << ((p) * 4))
+#define USB2_PORT_CAP_PORT_CAP_OTG 0x3
+#define USB2_PORT_CAP_PORT_CAP_DEVICE 0x2
+#define USB2_PORT_CAP_PORT_CAP_HOST 0x1
+#define USB2_PORT_CAP_PORT_CAP_DISABLED 0x0
+
+#define XUSB_PADCTL_SS_PORT_MAP 0x014
+#define SS_PORT_MAP_PORT_INTERNAL(p) (1 << (3 + (p) * 4))
+#define SS_PORT_MAP_PORT_MAP(p, x) (((x) & 7) << ((p) * 4))
+
+#define XUSB_PADCTL_ELPG_PROGRAM 0x01C
+#define ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN (1 << 26)
+#define ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY (1 << 25)
+#define ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN (1 << 24)
+#define ELPG_PROGRAM_SSP_ELPG_VCORE_DOWN(x) (1 << (18 + (x) * 4))
+#define ELPG_PROGRAM_SSP_ELPG_CLAMP_EN_EARLY(x) (1 << (17 + (x) * 4))
+#define ELPG_PROGRAM_SSP_ELPG_CLAMP_EN(x) (1 << (16 + (x) * 4))
+
+#define XUSB_PADCTL_IOPHY_PLL_P0_CTL1 0x040
+#define IOPHY_PLL_P0_CTL1_PLL0_LOCKDET (1 << 19)
+#define IOPHY_PLL_P0_CTL1_REFCLK_SEL(x) (((x) & 0xF) << 12)
+#define IOPHY_PLL_P0_CTL1_PLL_RST (1 << 1)
+
+#define XUSB_PADCTL_IOPHY_PLL_P0_CTL2 0x044
+#define IOPHY_PLL_P0_CTL2_REFCLKBUF_EN (1 << 6)
+#define IOPHY_PLL_P0_CTL2_TXCLKREF_EN (1 << 5)
+#define IOPHY_PLL_P0_CTL2_TXCLKREF_SEL (1 << 4)
+
+#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2(x) (0x058 + (x) * 4)
+#define IOPHY_USB3_PAD_CTL2_CDR_CNTL(x) (((x) & 0x00FF) << 4)
+#define IOPHY_USB3_PAD_CTL2_RX_EQ(x) (((x) & 0xFFFF) << 8)
+#define IOPHY_USB3_PAD_CTL2_RX_WANDER(x) (((x) & 0x000F) << 4)
+#define IOPHY_USB3_PAD_CTL2_RX_TERM_CNTL(x) (((x) & 0x0003) << 2)
+#define IOPHY_USB3_PAD_CTL2_TX_TERM_CNTL(x) (((x) & 0x0003) << 0)
+
+#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL4(x) (0x068 + (x) * 4)
+
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL0(x) (0x0A0 + (x) * 4)
+#define USB2_OTG_PAD_CTL0_LSBIAS_SEL (1 << 23)
+#define USB2_OTG_PAD_CTL0_DISCON_DETECT_METHOD (1 << 22)
+#define USB2_OTG_PAD_CTL0_PD_ZI (1 << 21)
+#define USB2_OTG_PAD_CTL0_PD2 (1 << 20)
+#define USB2_OTG_PAD_CTL0_PD (1 << 19)
+#define USB2_OTG_PAD_CTL0_TERM_EN (1 << 18)
+#define USB2_OTG_PAD_CTL0_LS_LS_FSLEW(x) (((x) & 0x03) << 16)
+#define USB2_OTG_PAD_CTL0_LS_RSLEW(x) (((x) & 0x03) << 14)
+#define USB2_OTG_PAD_CTL0_FS_SLEW(x) (((x) & 0x03) << 12)
+#define USB2_OTG_PAD_CTL0_HS_SLEW(x) (((x) & 0x3F) << 6)
+#define USB2_OTG_PAD_CTL0_HS_CURR_LEVEL(x) (((x) & 0x3F) << 0)
+
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL1(x) (0x0AC + (x) * 4)
+#define USB2_OTG_PAD_CTL1_RPU_RANGE_ADJ(x) (((x) & 0x3) << 11)
+#define USB2_OTG_PAD_CTL1_HS_IREF_CAP(x) (((x) & 0x3) << 9)
+#define USB2_OTG_PAD_CTL1_SPARE(x) (((x) & 0x3) << 7)
+#define USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ(x) (((x) & 0xF) << 3)
+#define USB2_OTG_PAD_CTL1_PD_DR (1 << 2)
+#define USB2_OTG_PAD_CTL1_PD_DISC_FORCE_POWERUP (1 << 1)
+#define USB2_OTG_PAD_CTL1_PD_CHRP_FORCE_POWERUP (1 << 0)
+
+#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0 0x0B8
+#define USB2_BIAS_PAD_CTL0_ADJRPU(x) (((x) & 0x7) << 14)
+#define USB2_BIAS_PAD_CTL0_PD_TRK (1 << 13)
+#define USB2_BIAS_PAD_CTL0_PD (1 << 12)
+#define USB2_BIAS_PAD_CTL0_TERM_OFFSETL(x) (((x) & 0x3) << 9)
+#define USB2_BIAS_PAD_CTL0_VBUS_LEVEL(x) (((x) & 0x3) << 7)
+#define USB2_BIAS_PAD_CTL0_HS_CHIRP_LEVEL(x) (((x) & 0x3) << 5)
+#define USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL(x) (((x) & 0x7) << 2)
+#define USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL(x) (((x) & 0x3) << 0)
+
+#define XUSB_PADCTL_HSIC_PAD0_CTL0 0x0C8
+#define HSIC_PAD0_CTL0_HSIC_OPT(x) (((x) & 0xF) << 16)
+#define HSIC_PAD0_CTL0_TX_SLEWN(x) (((x) & 0xF) << 12)
+#define HSIC_PAD0_CTL0_TX_SLEWP(x) (((x) & 0xF) << 8)
+#define HSIC_PAD0_CTL0_TX_RTUNEN(x) (((x) & 0xF) << 4)
+#define HSIC_PAD0_CTL0_TX_RTUNEP(x) (((x) & 0xF) << 0)
+
+#define XUSB_PADCTL_USB3_PAD_MUX 0x134
+#define USB3_PAD_MUX_PCIE_IDDQ_DISABLE(x) (1 << (1 + (x)))
+#define USB3_PAD_MUX_SATA_IDDQ_DISABLE (1 << 6)
+
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1 0x138
+#define IOPHY_PLL_S0_CTL1_PLL1_LOCKDET (1 << 27)
+#define IOPHY_PLL_S0_CTL1_PLL1_MODE (1 << 24)
+#define IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD (1 << 3)
+#define IOPHY_PLL_S0_CTL1_PLL_RST_L (1 << 1)
+#define IOPHY_PLL_S0_CTL1_PLL_IDDQ (1 << 0)
+
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2 0x13C
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL3 0x140
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL4 0x144
+
+#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1 0x148
+#define IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD (1 << 1)
+#define IOPHY_MISC_PAD_S0_CTL1_IDDQ (1 << 0)
+
+#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL2 0x14C
+#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL3 0x150
+#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL4 0x154
+#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL5 0x158
+#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL6 0x15C
+
+#define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v))
+#define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r))
+
+struct padctl_softc {
+ device_t dev;
+ struct resource *mem_res;
+ hwreset_t rst;
+ int phy_ena_cnt;
+
+ /* Fuses calibration data */
+ uint32_t hs_curr_level_0;
+ uint32_t hs_curr_level_123;
+ uint32_t hs_iref_cap;
+ uint32_t hs_term_range_adj;
+ uint32_t hs_squelch_level;
+
+ uint32_t hs_curr_level_offset;
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"nvidia,tegra124-xusb-padctl", 1},
+ {NULL, 0},
+};
+
+/* Ports. */
+enum padctl_port_type {
+ PADCTL_PORT_USB2,
+ PADCTL_PORT_ULPI,
+ PADCTL_PORT_HSIC,
+ PADCTL_PORT_USB3,
+};
+
+struct padctl_lane;
+struct padctl_port {
+ enum padctl_port_type type;
+ const char *name;
+ const char *base_name;
+ int idx;
+ int (*init)(struct padctl_softc *sc,
+ struct padctl_port *port);
+
+ /* Runtime data. */
+ bool enabled;
+ regulator_t supply_vbus; /* USB2, USB3 */
+ bool internal; /* ULPI, USB2, USB3 */
+ uint32_t companion; /* USB3 */
+ struct padctl_lane *lane;
+};
+
+static int usb3_port_init(struct padctl_softc *sc, struct padctl_port *port);
+
+#define PORT(t, n, p, i) { \
+ .type = t, \
+ .name = n "-" #p, \
+ .base_name = n, \
+ .idx = p, \
+ .init = i, \
+}
+static struct padctl_port ports_tbl[] = {
+ PORT(PADCTL_PORT_USB2, "usb2", 0, NULL),
+ PORT(PADCTL_PORT_USB2, "usb2", 1, NULL),
+ PORT(PADCTL_PORT_USB2, "usb2", 2, NULL),
+ PORT(PADCTL_PORT_ULPI, "ulpi", 0, NULL),
+ PORT(PADCTL_PORT_HSIC, "hsic", 0, NULL),
+ PORT(PADCTL_PORT_HSIC, "hsic", 1, NULL),
+ PORT(PADCTL_PORT_USB3, "usb3", 0, usb3_port_init),
+ PORT(PADCTL_PORT_USB3, "usb3", 1, usb3_port_init),
+};
+
+/* Pads - a group of lannes. */
+enum padctl_pad_type {
+ PADCTL_PAD_USB2,
+ PADCTL_PAD_ULPI,
+ PADCTL_PAD_HSIC,
+ PADCTL_PAD_PCIE,
+ PADCTL_PAD_SATA,
+};
+
+struct padctl_lane;
+struct padctl_pad {
+ const char *name;
+ enum padctl_pad_type type;
+ int (*powerup)(struct padctl_softc *sc,
+ struct padctl_lane *lane);
+ int (*powerdown)(struct padctl_softc *sc,
+ struct padctl_lane *lane);
+ /* Runtime data. */
+ bool enabled;
+ struct padctl_lane *lanes[8]; /* Safe maximum value. */
+ int nlanes;
+};
+
+static int usb2_powerup(struct padctl_softc *sc, struct padctl_lane *lane);
+static int usb2_powerdown(struct padctl_softc *sc, struct padctl_lane *lane);
+static int pcie_powerup(struct padctl_softc *sc, struct padctl_lane *lane);
+static int pcie_powerdown(struct padctl_softc *sc, struct padctl_lane *lane);
+static int sata_powerup(struct padctl_softc *sc, struct padctl_lane *lane);
+static int sata_powerdown(struct padctl_softc *sc, struct padctl_lane *lane);
+
+#define PAD(n, t, u, d) { \
+ .name = n, \
+ .type = t, \
+ .powerup = u, \
+ .powerdown = d, \
+}
+static struct padctl_pad pads_tbl[] = {
+ PAD("usb2", PADCTL_PAD_USB2, usb2_powerup, usb2_powerdown),
+ PAD("ulpi", PADCTL_PAD_ULPI, NULL, NULL),
+ PAD("hsic", PADCTL_PAD_HSIC, NULL, NULL),
+ PAD("pcie", PADCTL_PAD_PCIE, pcie_powerup, pcie_powerdown),
+ PAD("sata", PADCTL_PAD_SATA, sata_powerup, sata_powerdown),
+};
+
+/* Lanes. */
+static char *otg_mux[] = {"snps", "xusb", "uart", "rsvd"};
+static char *usb_mux[] = {"snps", "xusb"};
+static char *pci_mux[] = {"pcie", "usb3-ss", "sata", "rsvd"};
+
+struct padctl_lane {
+ const char *name;
+ int idx;
+ bus_size_t reg;
+ uint32_t shift;
+ uint32_t mask;
+ char **mux;
+ int nmux;
+ /* Runtime data. */
+ bool enabled;
+ struct padctl_pad *pad;
+ struct padctl_port *port;
+ int mux_idx;
+
+};
+
+#define LANE(n, p, r, s, m, mx) { \
+ .name = n "-" #p, \
+ .idx = p, \
+ .reg = r, \
+ .shift = s, \
+ .mask = m, \
+ .mux = mx, \
+ .nmux = nitems(mx), \
+}
+static struct padctl_lane lanes_tbl[] = {
+ LANE("usb2", 0, XUSB_PADCTL_USB2_PAD_MUX, 0, 0x3, otg_mux),
+ LANE("usb2", 1, XUSB_PADCTL_USB2_PAD_MUX, 2, 0x3, otg_mux),
+ LANE("usb2", 2, XUSB_PADCTL_USB2_PAD_MUX, 4, 0x3, otg_mux),
+ LANE("ulpi", 0, XUSB_PADCTL_USB2_PAD_MUX, 12, 0x1, usb_mux),
+ LANE("hsic", 0, XUSB_PADCTL_USB2_PAD_MUX, 14, 0x1, usb_mux),
+ LANE("hsic", 1, XUSB_PADCTL_USB2_PAD_MUX, 15, 0x1, usb_mux),
+ LANE("pcie", 0, XUSB_PADCTL_USB3_PAD_MUX, 16, 0x3, pci_mux),
+ LANE("pcie", 1, XUSB_PADCTL_USB3_PAD_MUX, 18, 0x3, pci_mux),
+ LANE("pcie", 2, XUSB_PADCTL_USB3_PAD_MUX, 20, 0x3, pci_mux),
+ LANE("pcie", 3, XUSB_PADCTL_USB3_PAD_MUX, 22, 0x3, pci_mux),
+ LANE("pcie", 4, XUSB_PADCTL_USB3_PAD_MUX, 24, 0x3, pci_mux),
+ LANE("sata", 0, XUSB_PADCTL_USB3_PAD_MUX, 26, 0x3, pci_mux),
+};
+
+/* Define all possible mappings for USB3 port lanes */
+struct padctl_lane_map {
+ int port_idx;
+ enum padctl_pad_type pad_type;
+ int lane_idx;
+};
+
+#define LANE_MAP(pi, pt, li) { \
+ .port_idx = pi, \
+ .pad_type = pt, \
+ .lane_idx = li, \
+}
+static struct padctl_lane_map lane_map_tbl[] = {
+ LANE_MAP(0, PADCTL_PAD_PCIE, 0), /* port USB3-0 -> lane PCIE-0 */
+ LANE_MAP(1, PADCTL_PAD_PCIE, 1), /* port USB3-1 -> lane PCIE-1 */
+ /* -- or -- */
+ LANE_MAP(1, PADCTL_PAD_SATA, 0), /* port USB3-1 -> lane SATA-0 */
+};
+
+ /* Phy class and methods. */
+static int xusbpadctl_phy_enable(struct phynode *phy, bool enable);
+static phynode_method_t xusbpadctl_phynode_methods[] = {
+ PHYNODEMETHOD(phynode_enable, xusbpadctl_phy_enable),
+ PHYNODEMETHOD_END
+
+};
+DEFINE_CLASS_1(xusbpadctl_phynode, xusbpadctl_phynode_class,
+ xusbpadctl_phynode_methods, 0, phynode_class);
+
+static struct padctl_port *search_lane_port(struct padctl_softc *sc,
+ struct padctl_lane *lane);
+/* -------------------------------------------------------------------------
+ *
+ * PHY functions
+ */
+static int
+usb3_port_init(struct padctl_softc *sc, struct padctl_port *port)
+{
+ uint32_t reg;
+
+ reg = RD4(sc, XUSB_PADCTL_SS_PORT_MAP);
+ if (port->internal)
+ reg &= ~SS_PORT_MAP_PORT_INTERNAL(port->idx);
+ else
+ reg |= SS_PORT_MAP_PORT_INTERNAL(port->idx);
+ reg &= ~SS_PORT_MAP_PORT_MAP(port->idx, ~0);
+ reg |= SS_PORT_MAP_PORT_MAP(port->idx, port->companion);
+ WR4(sc, XUSB_PADCTL_SS_PORT_MAP, reg);
+
+ reg = RD4(sc, XUSB_PADCTL_IOPHY_USB3_PAD_CTL2(port->idx));
+ reg &= ~IOPHY_USB3_PAD_CTL2_CDR_CNTL(~0);
+ reg &= ~IOPHY_USB3_PAD_CTL2_RX_EQ(~0);
+ reg &= ~IOPHY_USB3_PAD_CTL2_RX_WANDER(~0);
+ reg |= IOPHY_USB3_PAD_CTL2_CDR_CNTL(0x24);
+ reg |= IOPHY_USB3_PAD_CTL2_RX_EQ(0xF070);
+ reg |= IOPHY_USB3_PAD_CTL2_RX_WANDER(0xF);
+ WR4(sc, XUSB_PADCTL_IOPHY_USB3_PAD_CTL2(port->idx), reg);
+
+ WR4(sc, XUSB_PADCTL_IOPHY_USB3_PAD_CTL4(port->idx),
+ 0x002008EE);
+
+ reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM);
+ reg &= ~ELPG_PROGRAM_SSP_ELPG_VCORE_DOWN(port->idx);
+ WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg);
+ DELAY(100);
+
+ reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM);
+ reg &= ~ELPG_PROGRAM_SSP_ELPG_CLAMP_EN_EARLY(port->idx);
+ WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg);
+ DELAY(100);
+
+ reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM);
+ reg &= ~ELPG_PROGRAM_SSP_ELPG_CLAMP_EN(port->idx);
+ WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg);
+ DELAY(100);
+
+ return (0);
+}
+
+static int
+pcie_powerup(struct padctl_softc *sc, struct padctl_lane *lane)
+{
+ uint32_t reg;
+ int i;
+
+ reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
+ reg &= ~IOPHY_PLL_P0_CTL1_REFCLK_SEL(~0);
+ WR4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1, reg);
+ DELAY(100);
+
+ reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL2);
+ reg |= IOPHY_PLL_P0_CTL2_REFCLKBUF_EN;
+ reg |= IOPHY_PLL_P0_CTL2_TXCLKREF_EN;
+ reg |= IOPHY_PLL_P0_CTL2_TXCLKREF_SEL;
+ WR4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL2, reg);
+ DELAY(100);
+
+ reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
+ reg |= IOPHY_PLL_P0_CTL1_PLL_RST;
+ WR4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1, reg);
+ DELAY(100);
+
+ for (i = 100; i > 0; i--) {
+ reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
+ if (reg & IOPHY_PLL_P0_CTL1_PLL0_LOCKDET)
+ break;
+ DELAY(10);
+ }
+ if (i <= 0) {
+ device_printf(sc->dev, "Failed to power up PCIe phy\n");
+ return (ETIMEDOUT);
+ }
+ reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX);
+ reg |= USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->idx);
+ WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg);
+
+ return (0);
+}
+
+static int
+pcie_powerdown(struct padctl_softc *sc, struct padctl_lane *lane)
+{
+ uint32_t reg;
+
+ reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX);
+ reg &= ~USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->idx);
+ WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg);
+
+ reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
+ reg &= ~IOPHY_PLL_P0_CTL1_PLL_RST;
+ WR4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1, reg);
+ DELAY(100);
+
+ return (0);
+
+}
+
+static int
+sata_powerup(struct padctl_softc *sc, struct padctl_lane *lane)
+{
+ uint32_t reg;
+ int i;
+
+ reg = RD4(sc, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1);
+ reg &= ~IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD;
+ reg &= ~IOPHY_MISC_PAD_S0_CTL1_IDDQ;
+ WR4(sc, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1, reg);
+
+ reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+ reg &= ~IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD;
+ reg &= ~IOPHY_PLL_S0_CTL1_PLL_IDDQ;
+ WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg);
+
+ reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+ reg |= IOPHY_PLL_S0_CTL1_PLL1_MODE;
+ WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg);
+
+ reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+ reg |= IOPHY_PLL_S0_CTL1_PLL_RST_L;
+ WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg);
+
+ for (i = 100; i >= 0; i--) {
+ reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+ if (reg & IOPHY_PLL_S0_CTL1_PLL1_LOCKDET)
+ break;
+ DELAY(100);
+ }
+ if (i <= 0) {
+ device_printf(sc->dev, "Failed to power up SATA phy\n");
+ return (ETIMEDOUT);
+ }
+ reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX);
+ reg |= IOPHY_PLL_S0_CTL1_PLL_RST_L;
+ WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg);
+
+ reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX);
+ reg |= USB3_PAD_MUX_SATA_IDDQ_DISABLE;
+ WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg);
+
+ return (0);
+}
+
+static int
+sata_powerdown(struct padctl_softc *sc, struct padctl_lane *lane)
+{
+ uint32_t reg;
+
+ reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX);
+ reg &= ~USB3_PAD_MUX_SATA_IDDQ_DISABLE;
+ WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg);
+
+ reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+ reg &= ~IOPHY_PLL_S0_CTL1_PLL_RST_L;
+ WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg);
+ DELAY(100);
+
+ reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+ reg &= ~IOPHY_PLL_S0_CTL1_PLL1_MODE;
+ WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg);
+ DELAY(100);
+
+ reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+ reg |= IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD;
+ reg |= IOPHY_PLL_S0_CTL1_PLL_IDDQ;
+ WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg);
+ DELAY(100);
+
+ reg = RD4(sc, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1);
+ reg |= IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD;
+ reg |= IOPHY_MISC_PAD_S0_CTL1_IDDQ;
+ WR4(sc, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1, reg);
+ DELAY(100);
+
+ return (0);
+}
+
+static int
+usb2_powerup(struct padctl_softc *sc, struct padctl_lane *lane)
+{
+ uint32_t reg;
+ struct padctl_port *port;
+ int rv;
+
+ port = search_lane_port(sc, lane);
+ if (port == NULL) {
+ device_printf(sc->dev, "Cannot find port for lane: %s\n",
+ lane->name);
+ }
+ reg = RD4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
+ reg &= ~USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL(~0);
+ reg &= ~USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL(~0);
+ reg |= USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL(sc->hs_squelch_level);
+ reg |= USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL(5);
+ WR4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0, reg);
+
+ reg = RD4(sc, XUSB_PADCTL_USB2_PORT_CAP);
+ reg &= ~USB2_PORT_CAP_PORT_CAP(lane->idx, ~0);
+ reg |= USB2_PORT_CAP_PORT_CAP(lane->idx, USB2_PORT_CAP_PORT_CAP_HOST);
+ WR4(sc, XUSB_PADCTL_USB2_PORT_CAP, reg);
+
+ reg = RD4(sc, XUSB_PADCTL_USB2_OTG_PAD_CTL0(lane->idx));
+ reg &= ~USB2_OTG_PAD_CTL0_HS_CURR_LEVEL(~0);
+ reg &= ~USB2_OTG_PAD_CTL0_HS_SLEW(~0);
+ reg &= ~USB2_OTG_PAD_CTL0_LS_RSLEW(~0);
+ reg &= ~USB2_OTG_PAD_CTL0_PD;
+ reg &= ~USB2_OTG_PAD_CTL0_PD2;
+ reg &= ~USB2_OTG_PAD_CTL0_PD_ZI;
+
+ reg |= USB2_OTG_PAD_CTL0_HS_SLEW(14);
+ if (lane->idx == 0) {
+ reg |= USB2_OTG_PAD_CTL0_HS_CURR_LEVEL(sc->hs_curr_level_0);
+ reg |= USB2_OTG_PAD_CTL0_LS_RSLEW(3);
+ } else {
+ reg |= USB2_OTG_PAD_CTL0_HS_CURR_LEVEL(sc->hs_curr_level_123);
+ reg |= USB2_OTG_PAD_CTL0_LS_RSLEW(0);
+ }
+ WR4(sc, XUSB_PADCTL_USB2_OTG_PAD_CTL0(lane->idx), reg);
+
+ reg = RD4(sc, XUSB_PADCTL_USB2_OTG_PAD_CTL1(lane->idx));
+ reg &= ~USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ(~0);
+ reg &= ~USB2_OTG_PAD_CTL1_HS_IREF_CAP(~0);
+ reg &= ~USB2_OTG_PAD_CTL1_PD_DR;
+ reg &= ~USB2_OTG_PAD_CTL1_PD_DISC_FORCE_POWERUP;
+ reg &= ~USB2_OTG_PAD_CTL1_PD_CHRP_FORCE_POWERUP;
+
+ reg |= USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ(sc->hs_term_range_adj);
+ reg |= USB2_OTG_PAD_CTL1_HS_IREF_CAP(sc->hs_iref_cap);
+ WR4(sc, XUSB_PADCTL_USB2_OTG_PAD_CTL1(lane->idx), reg);
+
+ if (port != NULL && port->supply_vbus != NULL) {
+ rv = regulator_enable(port->supply_vbus);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot enable vbus regulator\n");
+ return (rv);
+ }
+ }
+ reg = RD4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
+ reg &= ~USB2_BIAS_PAD_CTL0_PD;
+ WR4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0, reg);
+
+ return (0);
+}
+
+static int
+usb2_powerdown(struct padctl_softc *sc, struct padctl_lane *lane)
+{
+ uint32_t reg;
+ struct padctl_port *port;
+ int rv;
+
+ port = search_lane_port(sc, lane);
+ if (port == NULL) {
+ device_printf(sc->dev, "Cannot find port for lane: %s\n",
+ lane->name);
+ }
+ reg = RD4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
+ reg |= USB2_BIAS_PAD_CTL0_PD;
+ WR4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0, reg);
+
+ if (port != NULL && port->supply_vbus != NULL) {
+ rv = regulator_enable(port->supply_vbus);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot disable vbus regulator\n");
+ return (rv);
+ }
+ }
+ return (0);
+}
+
+static int
+phy_powerup(struct padctl_softc *sc)
+{
+ uint32_t reg;
+
+ reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM);
+ reg &= ~ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN;
+ WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg);
+ DELAY(100);
+
+ reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM);
+ reg &= ~ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN;
+ WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg);
+ DELAY(100);
+
+ reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM);
+ reg &= ~ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY;
+ WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg);
+ DELAY(100);
+
+ return (0);
+}
+
+static int
+phy_powerdown(struct padctl_softc *sc)
+{
+ uint32_t reg;
+
+ reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM);
+ reg |= ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY;
+ WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg);
+ DELAY(100);
+
+ reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM);
+ reg |= ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN;
+ WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg);
+ DELAY(100);
+
+ reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM);
+ reg |= ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN;
+ WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg);
+ DELAY(100);
+
+ return (0);
+}
+
+static int
+xusbpadctl_phy_enable(struct phynode *phy, bool enable)
+{
+ device_t dev;
+ intptr_t id;
+ struct padctl_softc *sc;
+ struct padctl_lane *lane;
+ struct padctl_pad *pad;
+ int rv;
+
+ dev = phynode_get_device(phy);
+ id = phynode_get_id(phy);
+ sc = device_get_softc(dev);
+
+ if (id < 0 || id >= nitems(lanes_tbl)) {
+ device_printf(dev, "Unknown phy: %d\n", id);
+ return (ENXIO);
+ }
+ lane = lanes_tbl + id;
+ if (!lane->enabled) {
+ device_printf(dev, "Lane is not enabled/configured: %s\n",
+ lane->name);
+ return (ENXIO);
+ }
+ pad = lane->pad;
+ if (enable) {
+ if (sc->phy_ena_cnt == 0) {
+ rv = phy_powerup(sc);
+ if (rv != 0)
+ return (rv);
+ }
+ sc->phy_ena_cnt++;
+ }
+
+ if (enable)
+ rv = pad->powerup(sc, lane);
+ else
+ rv = pad->powerdown(sc, lane);
+ if (rv != 0)
+ return (rv);
+
+ if (!enable) {
+ if (sc->phy_ena_cnt == 1) {
+ rv = phy_powerdown(sc);
+ if (rv != 0)
+ return (rv);
+ }
+ sc->phy_ena_cnt--;
+ }
+
+ return (0);
+}
+
+/* -------------------------------------------------------------------------
+ *
+ * FDT processing
+ */
+static struct padctl_port *
+search_port(struct padctl_softc *sc, char *port_name)
+{
+ int i;
+
+ for (i = 0; i < nitems(ports_tbl); i++) {
+ if (strcmp(port_name, ports_tbl[i].name) == 0)
+ return (&ports_tbl[i]);
+ }
+ return (NULL);
+}
+
+static struct padctl_port *
+search_lane_port(struct padctl_softc *sc, struct padctl_lane *lane)
+{
+ int i;
+
+ for (i = 0; i < nitems(ports_tbl); i++) {
+ if (!ports_tbl[i].enabled)
+ continue;
+ if (ports_tbl[i].lane == lane)
+ return (ports_tbl + i);
+ }
+ return (NULL);
+}
+
+static struct padctl_lane *
+search_lane(struct padctl_softc *sc, char *lane_name)
+{
+ int i;
+
+ for (i = 0; i < nitems(lanes_tbl); i++) {
+ if (strcmp(lane_name, lanes_tbl[i].name) == 0)
+ return (lanes_tbl + i);
+ }
+ return (NULL);
+}
+
+static struct padctl_lane *
+search_pad_lane(struct padctl_softc *sc, enum padctl_pad_type type, int idx)
+{
+ int i;
+
+ for (i = 0; i < nitems(lanes_tbl); i++) {
+ if (!lanes_tbl[i].enabled)
+ continue;
+ if (type == lanes_tbl[i].pad->type && idx == lanes_tbl[i].idx)
+ return (lanes_tbl + i);
+ }
+ return (NULL);
+}
+
+static struct padctl_lane *
+search_usb3_pad_lane(struct padctl_softc *sc, int idx)
+{
+ int i;
+ struct padctl_lane *lane, *tmp;
+
+ lane = NULL;
+ for (i = 0; i < nitems(lane_map_tbl); i++) {
+ if (idx != lane_map_tbl[i].port_idx)
+ continue;
+ tmp = search_pad_lane(sc, lane_map_tbl[i].pad_type,
+ lane_map_tbl[i].lane_idx);
+ if (tmp == NULL)
+ continue;
+ if (strcmp(tmp->mux[tmp->mux_idx], "usb3-ss") != 0)
+ continue;
+ if (lane != NULL) {
+ device_printf(sc->dev, "Duplicated mappings found for"
+ " lanes: %s and %s\n", lane->name, tmp->name);
+ return (NULL);
+ }
+ lane = tmp;
+ }
+ return (lane);
+}
+
+static struct padctl_pad *
+search_pad(struct padctl_softc *sc, char *pad_name)
+{
+ int i;
+
+ for (i = 0; i < nitems(pads_tbl); i++) {
+ if (strcmp(pad_name, pads_tbl[i].name) == 0)
+ return (pads_tbl + i);
+ }
+ return (NULL);
+}
+
+static int
+search_mux(struct padctl_softc *sc, struct padctl_lane *lane, char *fnc_name)
+{
+ int i;
+
+ for (i = 0; i < lane->nmux; i++) {
+ if (strcmp(fnc_name, lane->mux[i]) == 0)
+ return (i);
+ }
+ return (-1);
+}
+
+static int
+config_lane(struct padctl_softc *sc, struct padctl_lane *lane)
+{
+ uint32_t reg;
+
+ reg = RD4(sc, lane->reg);
+ reg &= ~(lane->mask << lane->shift);
+ reg |= (lane->mux_idx & lane->mask) << lane->shift;
+ WR4(sc, lane->reg, reg);
+ return (0);
+}
+
+static int
+process_lane(struct padctl_softc *sc, phandle_t node, struct padctl_pad *pad)
+{
+ struct padctl_lane *lane;
+ struct phynode *phynode;
+ struct phynode_init_def phy_init;
+ char *name;
+ char *function;
+ int rv;
+
+ name = NULL;
+ function = NULL;
+ rv = OF_getprop_alloc(node, "name", (void **)&name);
+ if (rv <= 0) {
+ device_printf(sc->dev, "Cannot read lane name.\n");
+ return (ENXIO);
+ }
+
+ lane = search_lane(sc, name);
+ if (lane == NULL) {
+ device_printf(sc->dev, "Unknown lane: %s\n", name);
+ rv = ENXIO;
+ goto end;
+ }
+
+ /* Read function (mux) settings. */
+ rv = OF_getprop_alloc(node, "nvidia,function", (void **)&function);
+ if (rv <= 0) {
+ device_printf(sc->dev, "Cannot read lane function.\n");
+ rv = ENXIO;
+ goto end;
+ }
+
+ lane->mux_idx = search_mux(sc, lane, function);
+ if (lane->mux_idx == ~0) {
+ device_printf(sc->dev, "Unknown function %s for lane %s\n",
+ function, name);
+ rv = ENXIO;
+ goto end;
+ }
+
+ rv = config_lane(sc, lane);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot configure lane: %s: %d\n",
+ name, rv);
+ rv = ENXIO;
+ goto end;
+ }
+ lane->pad = pad;
+ lane->enabled = true;
+ pad->lanes[pad->nlanes++] = lane;
+
+ /* Create and register phy. */
+ bzero(&phy_init, sizeof(phy_init));
+ phy_init.id = lane - lanes_tbl;
+ phy_init.ofw_node = node;
+ phynode = phynode_create(sc->dev, &xusbpadctl_phynode_class, &phy_init);
+ if (phynode == NULL) {
+ device_printf(sc->dev, "Cannot create phy\n");
+ rv = ENXIO;
+ goto end;
+ }
+ if (phynode_register(phynode) == NULL) {
+ device_printf(sc->dev, "Cannot create phy\n");
+ return (ENXIO);
+ }
+
+ rv = 0;
+
+end:
+ if (name != NULL)
+ OF_prop_free(name);
+ if (function != NULL)
+ OF_prop_free(function);
+ return (rv);
+}
+
+static int
+process_pad(struct padctl_softc *sc, phandle_t node)
+{
+ struct padctl_pad *pad;
+ char *name;
+ int rv;
+
+ name = NULL;
+ rv = OF_getprop_alloc(node, "name", (void **)&name);
+ if (rv <= 0) {
+ device_printf(sc->dev, "Cannot read pad name.\n");
+ return (ENXIO);
+ }
+ pad = search_pad(sc, name);
+ if (pad == NULL) {
+ device_printf(sc->dev, "Unknown pad: %s\n", name);
+ rv = ENXIO;
+ goto end;
+ }
+
+ /* Read and process associated lanes. */
+ node = ofw_bus_find_child(node, "lanes");
+ if (node <= 0) {
+ device_printf(sc->dev, "Cannot find regulators subnode\n");
+ rv = ENXIO;
+ goto end;
+ }
+
+ for (node = OF_child(node); node != 0; node = OF_peer(node)) {
+ if (!ofw_bus_node_status_okay(node))
+ continue;
+
+ rv = process_lane(sc, node, pad);
+ if (rv != 0)
+ goto end;
+ }
+ pad->enabled = true;
+ rv = 0;
+end:
+ if (name != NULL)
+ OF_prop_free(name);
+ return (rv);
+}
+
+static int
+process_port(struct padctl_softc *sc, phandle_t node)
+{
+
+ struct padctl_port *port;
+ char *name;
+ int rv;
+
+ name = NULL;
+ rv = OF_getprop_alloc(node, "name", (void **)&name);
+ if (rv <= 0) {
+ device_printf(sc->dev, "Cannot read port name.\n");
+ return (ENXIO);
+ }
+
+ port = search_port(sc, name);
+ if (port == NULL) {
+ device_printf(sc->dev, "Unknown port: %s\n", name);
+ rv = ENXIO;
+ goto end;
+ }
+
+ if (port->type == PADCTL_PORT_USB3) {
+ rv = OF_getencprop(node, "nvidia,usb2-companion",
+ &(port->companion), sizeof(port->companion));
+ if (rv <= 0) {
+ device_printf(sc->dev,
+ "Missing 'nvidia,usb2-companion' property "
+ "for port: %s\n", name);
+ rv = ENXIO;
+ goto end;
+ }
+ }
+
+ if (OF_hasprop(node, "vbus-supply")) {
+ rv = regulator_get_by_ofw_property(sc->dev, 0,
+ "vbus-supply", &port->supply_vbus);
+ if (rv <= 0) {
+ device_printf(sc->dev,
+ "Cannot get 'vbus-supply' regulator "
+ "for port: %s\n", name);
+ rv = ENXIO;
+ goto end;
+ }
+ }
+
+ if (OF_hasprop(node, "nvidia,internal"))
+ port->internal = true;
+ /* Find assigned lane */
+ if (port->lane == NULL) {
+ switch(port->type) {
+ /* Routing is fixed for USB2, ULPI AND HSIC. */
+ case PADCTL_PORT_USB2:
+ port->lane = search_pad_lane(sc, PADCTL_PAD_USB2,
+ port->idx);
+ break;
+ case PADCTL_PORT_ULPI:
+ port->lane = search_pad_lane(sc, PADCTL_PAD_ULPI,
+ port->idx);
+ break;
+ case PADCTL_PORT_HSIC:
+ port->lane = search_pad_lane(sc, PADCTL_PAD_HSIC,
+ port->idx);
+ break;
+ case PADCTL_PORT_USB3:
+ port->lane = search_usb3_pad_lane(sc, port->idx);
+ break;
+ }
+ }
+ if (port->lane == NULL) {
+ device_printf(sc->dev, "Cannot find lane for port: %s\n", name);
+ rv = ENXIO;
+ goto end;
+ }
+ port->enabled = true;
+ rv = 0;
+end:
+ if (name != NULL)
+ OF_prop_free(name);
+ return (rv);
+}
+
+static int
+parse_fdt(struct padctl_softc *sc, phandle_t base_node)
+{
+ phandle_t node;
+ int rv;
+
+ rv = 0;
+ node = ofw_bus_find_child(base_node, "pads");
+
+ if (node <= 0) {
+ device_printf(sc->dev, "Cannot find pads subnode.\n");
+ return (ENXIO);
+ }
+ for (node = OF_child(node); node != 0; node = OF_peer(node)) {
+ if (!ofw_bus_node_status_okay(node))
+ continue;
+ rv = process_pad(sc, node);
+ if (rv != 0)
+ return (rv);
+ }
+
+ node = ofw_bus_find_child(base_node, "ports");
+ if (node <= 0) {
+ device_printf(sc->dev, "Cannot find ports subnode.\n");
+ return (ENXIO);
+ }
+ for (node = OF_child(node); node != 0; node = OF_peer(node)) {
+ if (!ofw_bus_node_status_okay(node))
+ continue;
+ rv = process_port(sc, node);
+ if (rv != 0)
+ return (rv);
+ }
+
+ return (0);
+}
+
+static void
+load_calibration(struct padctl_softc *sc)
+{
+ uint32_t reg;
+
+ /* All XUSB pad calibrations are packed into single dword.*/
+ reg = tegra_fuse_read_4(FUSE_XUSB_CALIB);
+ sc->hs_curr_level_0 = FUSE_XUSB_CALIB_HS_CURR_LEVEL_0(reg);
+ sc->hs_curr_level_123 = FUSE_XUSB_CALIB_HS_CURR_LEVEL_123(reg);
+ sc->hs_iref_cap = FUSE_XUSB_CALIB_HS_IREF_CAP(reg);
+ sc->hs_squelch_level = FUSE_XUSB_CALIB_HS_SQUELCH_LEVEL(reg);
+ sc->hs_term_range_adj = FUSE_XUSB_CALIB_HS_TERM_RANGE_ADJ(reg);
+}
+
+/* -------------------------------------------------------------------------
+ *
+ * BUS functions
+ */
+static int
+xusbpadctl_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+ return (ENXIO);
+
+ device_set_desc(dev, "Tegra XUSB phy");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+xusbpadctl_detach(device_t dev)
+{
+
+ /* This device is always present. */
+ return (EBUSY);
+}
+
+static int
+xusbpadctl_attach(device_t dev)
+{
+ struct padctl_softc * sc;
+ int i, rid, rv;
+ struct padctl_port *port;
+ phandle_t node;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ node = ofw_bus_get_node(dev);
+
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "Cannot allocate memory resources\n");
+ return (ENXIO);
+ }
+
+ rv = hwreset_get_by_ofw_name(dev, 0, "padctl", &sc->rst);
+ if (rv != 0) {
+ device_printf(dev, "Cannot get 'padctl' reset: %d\n", rv);
+ return (rv);
+ }
+ rv = hwreset_deassert(sc->rst);
+ if (rv != 0) {
+ device_printf(dev, "Cannot unreset 'padctl' reset: %d\n", rv);
+ return (rv);
+ }
+
+ load_calibration(sc);
+
+ rv = parse_fdt(sc, node);
+ if (rv != 0) {
+ device_printf(dev, "Cannot parse fdt configuration: %d\n", rv);
+ return (rv);
+ }
+ for (i = 0; i < nitems(ports_tbl); i++) {
+ port = ports_tbl + i;
+ if (!port->enabled)
+ continue;
+ if (port->init == NULL)
+ continue;
+ rv = port->init(sc, port);
+ if (rv != 0) {
+ device_printf(dev, "Cannot init port '%s'\n",
+ port->name);
+ return (rv);
+ }
+ }
+ return (0);
+}
+
+static device_method_t tegra_xusbpadctl_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, xusbpadctl_probe),
+ DEVMETHOD(device_attach, xusbpadctl_attach),
+ DEVMETHOD(device_detach, xusbpadctl_detach),
+
+ DEVMETHOD_END
+};
+
+static devclass_t tegra_xusbpadctl_devclass;
+static DEFINE_CLASS_0(xusbpadctl, tegra_xusbpadctl_driver,
+ tegra_xusbpadctl_methods, sizeof(struct padctl_softc));
+EARLY_DRIVER_MODULE(tegra_xusbpadctl, simplebus, tegra_xusbpadctl_driver,
+ tegra_xusbpadctl_devclass, NULL, NULL, 73);
diff --git a/sys/arm/nvidia/tegra_abpmisc.c b/sys/arm/nvidia/tegra_abpmisc.c
new file mode 100644
index 000000000000..8a8e39862ca3
--- /dev/null
+++ b/sys/arm/nvidia/tegra_abpmisc.c
@@ -0,0 +1,193 @@
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * SoC misc configuration and indentification driver.
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/clock.h>
+#include <sys/kernel.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/module.h>
+#include <sys/resource.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/nvidia/tegra_efuse.h>
+
+#define PMC_STRAPPING_OPT_A 0 /* 0x464 */
+
+#define PMC_STRAPPING_OPT_A_RAM_CODE_SHIFT 4
+#define PMC_STRAPPING_OPT_A_RAM_CODE_MASK_LONG \
+ (0xf << PMC_STRAPPING_OPT_A_RAM_CODE_SHIFT)
+#define PMC_STRAPPING_OPT_A_RAM_CODE_MASK_SHORT \
+ (0x3 << PMC_STRAPPING_OPT_A_RAM_CODE_SHIFT)
+
+#define ABP_RD4(_sc, _r) bus_read_4((_sc)->abp_misc_res, (_r))
+#define STR_RD4(_sc, _r) bus_read_4((_sc)->strap_opt_res, (_r))
+
+static struct ofw_compat_data compat_data[] = {
+ {"nvidia,tegra124-apbmisc", 1},
+ {"nvidia,tegra210-apbmisc", 1},
+ {NULL, 0}
+};
+
+struct tegra_abpmisc_softc {
+ device_t dev;
+
+ struct resource *abp_misc_res;
+ struct resource *strap_opt_res;
+};
+
+static struct tegra_abpmisc_softc *dev_sc;
+
+static void
+tegra_abpmisc_read_revision(struct tegra_abpmisc_softc *sc)
+{
+ uint32_t id, chip_id, minor_rev;
+ int rev;
+
+ id = ABP_RD4(sc, 4);
+ chip_id = (id >> 8) & 0xff;
+ minor_rev = (id >> 16) & 0xf;
+
+ switch (minor_rev) {
+ case 1:
+ rev = TEGRA_REVISION_A01;
+ break;
+ case 2:
+ rev = TEGRA_REVISION_A02;
+ break;
+ case 3:
+ rev = TEGRA_REVISION_A03;
+ break;
+ case 4:
+ rev = TEGRA_REVISION_A04;
+ break;
+ default:
+ rev = TEGRA_REVISION_UNKNOWN;
+ }
+
+ tegra_sku_info.chip_id = chip_id;
+ tegra_sku_info.revision = rev;
+}
+
+static int
+tegra_abpmisc_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+tegra_abpmisc_attach(device_t dev)
+{
+ int rid;
+ struct tegra_abpmisc_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ rid = 0;
+ sc->abp_misc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE | RF_SHAREABLE);
+ if (sc->abp_misc_res == NULL) {
+ device_printf(dev, "Cannot map ABP misc registers.\n");
+ goto fail;
+ }
+
+ rid = 1;
+ sc->strap_opt_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->strap_opt_res == NULL) {
+ device_printf(dev, "Cannot map strapping options registers.\n");
+ goto fail;
+ }
+
+ tegra_abpmisc_read_revision(sc);
+
+ /* XXX - Hack - address collision with pinmux. */
+ if (sc->abp_misc_res != NULL) {
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->abp_misc_res);
+ sc->abp_misc_res = NULL;
+ }
+
+ dev_sc = sc;
+ return (bus_generic_attach(dev));
+
+fail:
+ if (sc->abp_misc_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->abp_misc_res);
+ if (sc->strap_opt_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 1, sc->strap_opt_res);
+
+ return (ENXIO);
+}
+
+static int
+tegra_abpmisc_detach(device_t dev)
+{
+ struct tegra_abpmisc_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (sc->abp_misc_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->abp_misc_res);
+ if (sc->strap_opt_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 1, sc->strap_opt_res);
+ return (bus_generic_detach(dev));
+}
+
+static device_method_t tegra_abpmisc_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, tegra_abpmisc_probe),
+ DEVMETHOD(device_attach, tegra_abpmisc_attach),
+ DEVMETHOD(device_detach, tegra_abpmisc_detach),
+
+ DEVMETHOD_END
+};
+
+static devclass_t tegra_abpmisc_devclass;
+static DEFINE_CLASS_0(abpmisc, tegra_abpmisc_driver, tegra_abpmisc_methods,
+ sizeof(struct tegra_abpmisc_softc));
+EARLY_DRIVER_MODULE(tegra_abpmisc, simplebus, tegra_abpmisc_driver,
+ tegra_abpmisc_devclass, NULL, NULL, BUS_PASS_TIMER);
diff --git a/sys/arm/nvidia/tegra_ahci.c b/sys/arm/nvidia/tegra_ahci.c
new file mode 100644
index 000000000000..725fc999a3a3
--- /dev/null
+++ b/sys/arm/nvidia/tegra_ahci.c
@@ -0,0 +1,785 @@
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * AHCI driver for Tegra SoCs.
+ */
+#include <sys/param.h>
+#include <sys/module.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/ahci/ahci.h>
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/extres/phy/phy.h>
+#include <dev/extres/regulator/regulator.h>
+#include <dev/fdt/fdt_pinctrl.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/nvidia/tegra_efuse.h>
+#include <arm/nvidia/tegra_pmc.h>
+
+
+#define SATA_CONFIGURATION 0x180
+#define SATA_CONFIGURATION_CLK_OVERRIDE (1U << 31)
+#define SATA_CONFIGURATION_EN_FPCI (1 << 0)
+
+#define SATA_FPCI_BAR5 0x94
+#define SATA_FPCI_BAR_START(x) (((x) & 0xFFFFFFF) << 4)
+#define SATA_FPCI_BAR_ACCESS_TYPE (1 << 0)
+
+#define SATA_INTR_MASK 0x188
+#define SATA_INTR_MASK_IP_INT_MASK (1 << 16)
+
+#define SCFG_OFFSET 0x1000
+
+#define T_SATA0_CFG_1 0x04
+#define T_SATA0_CFG_1_IO_SPACE (1 << 0)
+#define T_SATA0_CFG_1_MEMORY_SPACE (1 << 1)
+#define T_SATA0_CFG_1_BUS_MASTER (1 << 2)
+#define T_SATA0_CFG_1_SERR (1 << 8)
+
+#define T_SATA0_CFG_9 0x24
+#define T_SATA0_CFG_9_BASE_ADDRESS_SHIFT 13
+
+#define T_SATA0_CFG_35 0x94
+#define T_SATA0_CFG_35_IDP_INDEX_MASK (0x7ff << 2)
+#define T_SATA0_CFG_35_IDP_INDEX (0x2a << 2)
+
+#define T_SATA0_AHCI_IDP1 0x98
+#define T_SATA0_AHCI_IDP1_DATA 0x400040
+
+#define T_SATA0_CFG_PHY_1 0x12c
+#define T_SATA0_CFG_PHY_1_PADS_IDDQ_EN (1 << 23)
+#define T_SATA0_CFG_PHY_1_PAD_PLL_IDDQ_EN (1 << 22)
+
+#define T_SATA0_NVOOB 0x114
+#define T_SATA0_NVOOB_SQUELCH_FILTER_LENGTH_MASK (0x3 << 26)
+#define T_SATA0_NVOOB_SQUELCH_FILTER_LENGTH (0x3 << 26)
+#define T_SATA0_NVOOB_SQUELCH_FILTER_MODE_MASK (0x3 << 24)
+#define T_SATA0_NVOOB_SQUELCH_FILTER_MODE (0x1 << 24)
+#define T_SATA0_NVOOB_COMMA_CNT_MASK (0xff << 16)
+#define T_SATA0_NVOOB_COMMA_CNT (0x07 << 16)
+
+#define T_SATA0_CFG_PHY 0x120
+#define T_SATA0_CFG_PHY_MASK_SQUELCH (1 << 24)
+#define T_SATA0_CFG_PHY_USE_7BIT_ALIGN_DET_FOR_SPD (1 << 11)
+
+#define T_SATA0_CFG2NVOOB_2 0x134
+#define T_SATA0_CFG2NVOOB_2_COMWAKE_IDLE_CNT_LOW_MASK (0x1ff << 18)
+#define T_SATA0_CFG2NVOOB_2_COMWAKE_IDLE_CNT_LOW (0xc << 18)
+
+#define T_SATA0_AHCI_HBA_CAP_BKDR 0x300
+#define T_SATA0_AHCI_HBA_CAP_BKDR_SNCQ (1 << 30)
+#define T_SATA0_AHCI_HBA_CAP_BKDR_SUPP_PM (1 << 17)
+#define T_SATA0_AHCI_HBA_CAP_BKDR_SALP (1 << 26)
+#define T_SATA0_AHCI_HBA_CAP_BKDR_SLUMBER_ST_CAP (1 << 14)
+#define T_SATA0_AHCI_HBA_CAP_BKDR_PARTIAL_ST_CAP (1 << 13)
+
+#define T_SATA0_BKDOOR_CC 0x4a4
+#define T_SATA0_BKDOOR_CC_CLASS_CODE_MASK (0xffff << 16)
+#define T_SATA0_BKDOOR_CC_CLASS_CODE (0x0106 << 16)
+#define T_SATA0_BKDOOR_CC_PROG_IF_MASK (0xff << 8)
+#define T_SATA0_BKDOOR_CC_PROG_IF (0x01 << 8)
+
+#define T_SATA0_CFG_SATA 0x54c
+#define T_SATA0_CFG_SATA_BACKDOOR_PROG_IF_EN (1 << 12)
+
+#define T_SATA0_CFG_MISC 0x550
+#define T_SATA0_INDEX 0x680
+
+#define T_SATA0_CHX_PHY_CTRL1_GEN1 0x690
+#define T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_MASK 0xff
+#define T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_SHIFT 8
+#define T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_MASK 0xff
+#define T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_SHIFT 0
+
+#define T_SATA0_CHX_PHY_CTRL1_GEN2 0x694
+#define T_SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_MASK 0xff
+#define T_SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_SHIFT 12
+#define T_SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_MASK 0xff
+#define T_SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_SHIFT 0
+
+#define T_SATA0_CHX_PHY_CTRL2 0x69c
+#define T_SATA0_CHX_PHY_CTRL2_CDR_CNTL_GEN1 0x23
+
+#define T_SATA0_CHX_PHY_CTRL11 0x6d0
+#define T_SATA0_CHX_PHY_CTRL11_GEN2_RX_EQ (0x2800 << 16)
+
+#define T_SATA0_CHX_PHY_CTRL17 0x6e8
+#define T_SATA0_CHX_PHY_CTRL18 0x6ec
+#define T_SATA0_CHX_PHY_CTRL20 0x6f4
+#define T_SATA0_CHX_PHY_CTRL21 0x6f8
+
+#define FUSE_SATA_CALIB 0x124
+#define FUSE_SATA_CALIB_MASK 0x3
+
+#define SATA_AUX_MISC_CNTL 0x1108
+#define SATA_AUX_PAD_PLL_CTRL_0 0x1120
+#define SATA_AUX_PAD_PLL_CTRL_1 0x1124
+#define SATA_AUX_PAD_PLL_CTRL_2 0x1128
+#define SATA_AUX_PAD_PLL_CTRL_3 0x112c
+
+#define T_AHCI_HBA_CCC_PORTS 0x0018
+#define T_AHCI_HBA_CAP_BKDR 0x00A0
+#define T_AHCI_HBA_CAP_BKDR_S64A (1 << 31)
+#define T_AHCI_HBA_CAP_BKDR_SNCQ (1 << 30)
+#define T_AHCI_HBA_CAP_BKDR_SSNTF (1 << 29)
+#define T_AHCI_HBA_CAP_BKDR_SMPS (1 << 28)
+#define T_AHCI_HBA_CAP_BKDR_SUPP_STG_SPUP (1 << 27)
+#define T_AHCI_HBA_CAP_BKDR_SALP (1 << 26)
+#define T_AHCI_HBA_CAP_BKDR_SAL (1 << 25)
+#define T_AHCI_HBA_CAP_BKDR_SUPP_CLO (1 << 24)
+#define T_AHCI_HBA_CAP_BKDR_INTF_SPD_SUPP(x) (((x) & 0xF) << 20)
+#define T_AHCI_HBA_CAP_BKDR_SUPP_NONZERO_OFFSET (1 << 19)
+#define T_AHCI_HBA_CAP_BKDR_SUPP_AHCI_ONLY (1 << 18)
+#define T_AHCI_HBA_CAP_BKDR_SUPP_PM (1 << 17)
+#define T_AHCI_HBA_CAP_BKDR_FIS_SWITCHING (1 << 16)
+#define T_AHCI_HBA_CAP_BKDR_PIO_MULT_DRQ_BLK (1 << 15)
+#define T_AHCI_HBA_CAP_BKDR_SLUMBER_ST_CAP (1 << 14)
+#define T_AHCI_HBA_CAP_BKDR_PARTIAL_ST_CAP (1 << 13)
+#define T_AHCI_HBA_CAP_BKDR_NUM_CMD_SLOTS(x) (((x) & 0x1F) << 8)
+#define T_AHCI_HBA_CAP_BKDR_CMD_CMPL_COALESING (1 << 7)
+#define T_AHCI_HBA_CAP_BKDR_ENCL_MGMT_SUPP (1 << 6)
+#define T_AHCI_HBA_CAP_BKDR_EXT_SATA (1 << 5)
+#define T_AHCI_HBA_CAP_BKDR_NUM_PORTS(x) (((x) & 0xF) << 0)
+
+#define T_AHCI_PORT_BKDR 0x0170
+
+#define T_AHCI_PORT_BKDR_PXDEVSLP_DETO_OVERRIDE_VAL(x) (((x) & 0xFF) << 24)
+#define T_AHCI_PORT_BKDR_PXDEVSLP_MDAT_OVERRIDE_VAL(x) (((x) & 0x1F) << 16)
+#define T_AHCI_PORT_BKDR_PXDEVSLP_DETO_OVERRIDE (1 << 15)
+#define T_AHCI_PORT_BKDR_PXDEVSLP_MDAT_OVERRIDE (1 << 14)
+#define T_AHCI_PORT_BKDR_PXDEVSLP_DM(x) (((x) & 0xF) << 10)
+#define T_AHCI_PORT_BKDR_PORT_UNCONNECTED (1 << 9)
+#define T_AHCI_PORT_BKDR_CLK_CLAMP_CTRL_CLAMP_THIS_CH (1 << 8)
+#define T_AHCI_PORT_BKDR_CLK_CLAMP_CTRL_TXRXCLK_UNCLAMP (1 << 7)
+#define T_AHCI_PORT_BKDR_CLK_CLAMP_CTRL_TXRXCLK_CLAMP (1 << 6)
+#define T_AHCI_PORT_BKDR_CLK_CLAMP_CTRL_DEVCLK_UNCLAMP (1 << 5)
+#define T_AHCI_PORT_BKDR_CLK_CLAMP_CTRL_DEVCLK_CLAMP (1 << 4)
+#define T_AHCI_PORT_BKDR_HOTPLUG_CAP (1 << 3)
+#define T_AHCI_PORT_BKDR_MECH_SWITCH (1 << 2)
+#define T_AHCI_PORT_BKDR_COLD_PRSN_DET (1 << 1)
+#define T_AHCI_PORT_BKDR_EXT_SATA_SUPP (1 << 0)
+
+/* AUX registers */
+#define SATA_AUX_MISC_CNTL_1 0x008
+#define SATA_AUX_MISC_CNTL_1_DEVSLP_OVERRIDE (1 << 17)
+#define SATA_AUX_MISC_CNTL_1_SDS_SUPPORT (1 << 13)
+#define SATA_AUX_MISC_CNTL_1_DESO_SUPPORT (1 << 15)
+
+#define AHCI_WR4(_sc, _r, _v) bus_write_4((_sc)->ctlr.r_mem, (_r), (_v))
+#define AHCI_RD4(_sc, _r) bus_read_4((_sc)->ctlr.r_mem, (_r))
+#define SATA_WR4(_sc, _r, _v) bus_write_4((_sc)->sata_mem, (_r), (_v))
+#define SATA_RD4(_sc, _r) bus_read_4((_sc)->sata_mem, (_r))
+
+struct sata_pad_calibration {
+ uint32_t gen1_tx_amp;
+ uint32_t gen1_tx_peak;
+ uint32_t gen2_tx_amp;
+ uint32_t gen2_tx_peak;
+};
+
+static const struct sata_pad_calibration tegra124_pad_calibration[] = {
+ {0x18, 0x04, 0x18, 0x0a},
+ {0x0e, 0x04, 0x14, 0x0a},
+ {0x0e, 0x07, 0x1a, 0x0e},
+ {0x14, 0x0e, 0x1a, 0x0e},
+};
+
+struct ahci_soc;
+struct tegra_ahci_sc {
+ struct ahci_controller ctlr; /* Must be first */
+ device_t dev;
+ struct ahci_soc *soc;
+ struct resource *sata_mem;
+ struct resource *aux_mem;
+ clk_t clk_sata;
+ clk_t clk_sata_oob;
+ clk_t clk_pll_e;
+ clk_t clk_cml;
+ hwreset_t hwreset_sata;
+ hwreset_t hwreset_sata_oob;
+ hwreset_t hwreset_sata_cold;
+ regulator_t regulators[16]; /* Safe maximum */
+ phy_t phy;
+};
+
+struct ahci_soc {
+ char **regulator_names;
+ int (*init)(struct tegra_ahci_sc *sc);
+};
+
+/* Tegra 124 config. */
+static char *tegra124_reg_names[] = {
+ "hvdd-supply",
+ "vddio-supply",
+ "avdd-supply",
+ "target-5v-supply",
+ "target-12v-supply",
+ NULL
+};
+
+static int tegra124_ahci_init(struct tegra_ahci_sc *sc);
+static struct ahci_soc tegra124_soc = {
+ .regulator_names = tegra124_reg_names,
+ .init = tegra124_ahci_init,
+};
+
+/* Tegra 210 config. */
+static char *tegra210_reg_names[] = {
+ NULL
+};
+
+static struct ahci_soc tegra210_soc = {
+ .regulator_names = tegra210_reg_names,
+};
+
+
+static struct ofw_compat_data compat_data[] = {
+ {"nvidia,tegra124-ahci", (uintptr_t)&tegra124_soc},
+ {"nvidia,tegra210-ahci", (uintptr_t)&tegra210_soc},
+ {NULL, 0}
+};
+
+static int
+get_fdt_resources(struct tegra_ahci_sc *sc, phandle_t node)
+{
+ int i, rv;
+
+ /* Regulators. */
+ for (i = 0; sc->soc->regulator_names[i] != NULL; i++) {
+ if (i >= nitems(sc->regulators)) {
+ device_printf(sc->dev,
+ "Too many regulators present in DT.\n");
+ return (EOVERFLOW);
+ }
+ rv = regulator_get_by_ofw_property(sc->dev, 0,
+ sc->soc->regulator_names[i], sc->regulators + i);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot get '%s' regulator\n",
+ sc->soc->regulator_names[i]);
+ return (ENXIO);
+ }
+ }
+
+ /* Resets. */
+ rv = hwreset_get_by_ofw_name(sc->dev, 0, "sata", &sc->hwreset_sata );
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'sata' reset\n");
+ return (ENXIO);
+ }
+ rv = hwreset_get_by_ofw_name(sc->dev, 0, "sata-oob",
+ &sc->hwreset_sata_oob);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'sata oob' reset\n");
+ return (ENXIO);
+ }
+ rv = hwreset_get_by_ofw_name(sc->dev, 0, "sata-cold",
+ &sc->hwreset_sata_cold);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'sata cold' reset\n");
+ return (ENXIO);
+ }
+
+ /* Phy */
+ rv = phy_get_by_ofw_name(sc->dev, 0, "sata-0", &sc->phy);
+ if (rv != 0) {
+ rv = phy_get_by_ofw_idx(sc->dev, 0, 0, &sc->phy);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'sata' phy\n");
+ return (ENXIO);
+ }
+ }
+
+ /* Clocks. */
+ rv = clk_get_by_ofw_name(sc->dev, 0, "sata", &sc->clk_sata);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'sata' clock\n");
+ return (ENXIO);
+ }
+ rv = clk_get_by_ofw_name(sc->dev, 0, "sata-oob", &sc->clk_sata_oob);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'sata oob' clock\n");
+ return (ENXIO);
+ }
+ /* These are optional */
+ rv = clk_get_by_ofw_name(sc->dev, 0, "cml1", &sc->clk_cml);
+ if (rv != 0)
+ sc->clk_cml = NULL;
+
+ rv = clk_get_by_ofw_name(sc->dev, 0, "pll_e", &sc->clk_pll_e);
+ if (rv != 0)
+ sc->clk_pll_e = NULL;
+ return (0);
+}
+
+static int
+enable_fdt_resources(struct tegra_ahci_sc *sc)
+{
+ int i, rv;
+
+ /* Enable regulators. */
+ for (i = 0; i < nitems(sc->regulators); i++) {
+ if (sc->regulators[i] == NULL)
+ continue;
+ rv = regulator_enable(sc->regulators[i]);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot enable '%s' regulator\n",
+ sc->soc->regulator_names[i]);
+ return (rv);
+ }
+ }
+
+ /* Stop clocks */
+ clk_stop(sc->clk_sata);
+ clk_stop(sc->clk_sata_oob);
+ tegra_powergate_power_off(TEGRA_POWERGATE_SAX);
+
+ rv = hwreset_assert(sc->hwreset_sata);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot assert 'sata' reset\n");
+ return (rv);
+ }
+ rv = hwreset_assert(sc->hwreset_sata_oob);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot assert 'sata oob' reset\n");
+ return (rv);
+ }
+
+ rv = hwreset_assert(sc->hwreset_sata_cold);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot assert 'sata cold' reset\n");
+ return (rv);
+ }
+ rv = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_SAX,
+ sc->clk_sata, sc->hwreset_sata);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot enable 'SAX' powergate\n");
+ return (rv);
+ }
+
+ rv = clk_enable(sc->clk_sata_oob);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot enable 'sata oob' clock\n");
+ return (rv);
+ }
+ if (sc->clk_cml != NULL) {
+ rv = clk_enable(sc->clk_cml);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot enable 'cml' clock\n");
+ return (rv);
+ }
+ }
+ if (sc->clk_pll_e != NULL) {
+ rv = clk_enable(sc->clk_pll_e);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot enable 'pll e' clock\n");
+ return (rv);
+ }
+ }
+
+ rv = hwreset_deassert(sc->hwreset_sata_cold);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot unreset 'sata cold' reset\n");
+ return (rv);
+ }
+ rv = hwreset_deassert(sc->hwreset_sata_oob);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot unreset 'sata oob' reset\n");
+ return (rv);
+ }
+
+ rv = phy_enable(sc->phy);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot enable SATA phy\n");
+ return (rv);
+ }
+
+ return (0);
+}
+
+static int
+tegra124_ahci_init(struct tegra_ahci_sc *sc)
+{
+ uint32_t val;
+ const struct sata_pad_calibration *calib;
+
+ /* Pad calibration. */
+ val = tegra_fuse_read_4(FUSE_SATA_CALIB);
+ calib = tegra124_pad_calibration + (val & FUSE_SATA_CALIB_MASK);
+ SATA_WR4(sc, SCFG_OFFSET + T_SATA0_INDEX, 1);
+
+ val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL1_GEN1);
+ val &= ~(T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_MASK <<
+ T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_SHIFT);
+ val &= ~(T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_MASK <<
+ T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_SHIFT);
+ val |= calib->gen1_tx_amp << T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_SHIFT;
+ val |= calib->gen1_tx_peak << T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_SHIFT;
+ SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL1_GEN1, val);
+
+ val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL1_GEN2);
+ val &= ~(T_SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_MASK <<
+ T_SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_SHIFT);
+ val &= ~(T_SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_MASK <<
+ T_SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_SHIFT);
+ val |= calib->gen2_tx_amp << T_SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_SHIFT;
+ val |= calib->gen2_tx_peak << T_SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_SHIFT;
+ SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL1_GEN2, val);
+
+ SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL11,
+ T_SATA0_CHX_PHY_CTRL11_GEN2_RX_EQ);
+
+ SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL2,
+ T_SATA0_CHX_PHY_CTRL2_CDR_CNTL_GEN1);
+
+ SATA_WR4(sc, SCFG_OFFSET + T_SATA0_INDEX, 0);
+
+ return (0);
+}
+
+static int
+tegra_ahci_ctrl_init(struct tegra_ahci_sc *sc)
+{
+ uint32_t val;
+ int rv;
+
+ /* Enable SATA MMIO. */
+ val = SATA_RD4(sc, SATA_FPCI_BAR5);
+ val &= ~SATA_FPCI_BAR_START(~0);
+ val |= SATA_FPCI_BAR_START(0x10000);
+ val |= SATA_FPCI_BAR_ACCESS_TYPE;
+ SATA_WR4(sc, SATA_FPCI_BAR5, val);
+
+ /* Enable FPCI access */
+ val = SATA_RD4(sc, SATA_CONFIGURATION);
+ val |= SATA_CONFIGURATION_EN_FPCI;
+ SATA_WR4(sc, SATA_CONFIGURATION, val);
+
+ /* Recommended electrical settings for phy */
+ SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL17, 0x55010000);
+ SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL18, 0x55010000);
+ SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL20, 0x1);
+ SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL21, 0x1);
+
+ /* SQUELCH and Gen3 */
+ val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_CFG_PHY);
+ val |= T_SATA0_CFG_PHY_MASK_SQUELCH;
+ val &= ~T_SATA0_CFG_PHY_USE_7BIT_ALIGN_DET_FOR_SPD;
+ SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CFG_PHY, val);
+
+ val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_NVOOB);
+ val &= ~T_SATA0_NVOOB_COMMA_CNT_MASK;
+ val &= ~T_SATA0_NVOOB_SQUELCH_FILTER_LENGTH_MASK;
+ val &= ~T_SATA0_NVOOB_SQUELCH_FILTER_MODE_MASK;
+ val |= T_SATA0_NVOOB_COMMA_CNT;
+ val |= T_SATA0_NVOOB_SQUELCH_FILTER_LENGTH;
+ val |= T_SATA0_NVOOB_SQUELCH_FILTER_MODE;
+ SATA_WR4(sc, SCFG_OFFSET + T_SATA0_NVOOB, val);
+
+ /* Setup COMWAKE_IDLE_CNT */
+ val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_CFG2NVOOB_2);
+ val &= ~T_SATA0_CFG2NVOOB_2_COMWAKE_IDLE_CNT_LOW_MASK;
+ val |= T_SATA0_CFG2NVOOB_2_COMWAKE_IDLE_CNT_LOW;
+ SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CFG2NVOOB_2, val);
+
+ if (sc->soc->init != NULL) {
+ rv = sc->soc->init(sc);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "SOC specific intialization failed: %d\n", rv);
+ return (rv);
+ }
+ }
+
+ /* Enable backdoor programming. */
+ val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_CFG_SATA);
+ val |= T_SATA0_CFG_SATA_BACKDOOR_PROG_IF_EN;
+ SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CFG_SATA, val);
+
+ /* Set device class and interface */
+ val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_BKDOOR_CC);
+ val &= ~T_SATA0_BKDOOR_CC_CLASS_CODE_MASK;
+ val &= ~T_SATA0_BKDOOR_CC_PROG_IF_MASK;
+ val |= T_SATA0_BKDOOR_CC_CLASS_CODE;
+ val |= T_SATA0_BKDOOR_CC_PROG_IF;
+ SATA_WR4(sc, SCFG_OFFSET + T_SATA0_BKDOOR_CC, val);
+
+ /* Enable LPM capabilities */
+ val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_AHCI_HBA_CAP_BKDR);
+ val |= T_SATA0_AHCI_HBA_CAP_BKDR_PARTIAL_ST_CAP;
+ val |= T_SATA0_AHCI_HBA_CAP_BKDR_SLUMBER_ST_CAP;
+ val |= T_SATA0_AHCI_HBA_CAP_BKDR_SALP;
+ val |= T_SATA0_AHCI_HBA_CAP_BKDR_SUPP_PM;
+ SATA_WR4(sc, SCFG_OFFSET + T_SATA0_AHCI_HBA_CAP_BKDR, val);
+
+ /* Disable backdoor programming. */
+ val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_CFG_SATA);
+ val &= ~T_SATA0_CFG_SATA_BACKDOOR_PROG_IF_EN;
+ SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CFG_SATA, val);
+
+ /* SATA Second Level Clock Gating */
+ val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_CFG_35);
+ val &= ~T_SATA0_CFG_35_IDP_INDEX_MASK;
+ val |= T_SATA0_CFG_35_IDP_INDEX;
+ SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CFG_35, val);
+
+ SATA_WR4(sc, SCFG_OFFSET + T_SATA0_AHCI_IDP1, 0x400040);
+
+ val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_CFG_PHY_1);
+ val |= T_SATA0_CFG_PHY_1_PADS_IDDQ_EN;
+ val |= T_SATA0_CFG_PHY_1_PAD_PLL_IDDQ_EN;
+ SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CFG_PHY_1, val);
+
+ /*
+ * Indicate Sata only has the capability to enter DevSleep
+ * from slumber link.
+ */
+ if (sc->aux_mem != NULL) {
+ val = bus_read_4(sc->aux_mem, SATA_AUX_MISC_CNTL_1);
+ val |= SATA_AUX_MISC_CNTL_1_DESO_SUPPORT;
+ bus_write_4(sc->aux_mem, SATA_AUX_MISC_CNTL_1, val);
+ }
+
+ /* Enable IPFS Clock Gating */
+ val = SATA_RD4(sc, SCFG_OFFSET + SATA_CONFIGURATION);
+ val &= ~SATA_CONFIGURATION_CLK_OVERRIDE;
+ SATA_WR4(sc, SCFG_OFFSET + SATA_CONFIGURATION, val);
+
+
+ /* Enable IO & memory access, bus master mode */
+ val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_CFG_1);
+ val |= T_SATA0_CFG_1_IO_SPACE;
+ val |= T_SATA0_CFG_1_MEMORY_SPACE;
+ val |= T_SATA0_CFG_1_BUS_MASTER;
+ val |= T_SATA0_CFG_1_SERR;
+ SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CFG_1, val);
+
+ /* AHCI bar */
+ SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CFG_9,
+ 0x08000 << T_SATA0_CFG_9_BASE_ADDRESS_SHIFT);
+
+ /* Unmask interrupts. */
+ val = SATA_RD4(sc, SATA_INTR_MASK);
+ val |= SATA_INTR_MASK_IP_INT_MASK;
+ SATA_WR4(sc, SATA_INTR_MASK, val);
+
+ return (0);
+}
+
+static int
+tegra_ahci_ctlr_reset(device_t dev)
+{
+ struct tegra_ahci_sc *sc;
+ int rv;
+ uint32_t reg;
+
+ sc = device_get_softc(dev);
+ rv = ahci_ctlr_reset(dev);
+ if (rv != 0)
+ return (0);
+ AHCI_WR4(sc, T_AHCI_HBA_CCC_PORTS, 1);
+
+ /* Overwrite AHCI capabilites. */
+ reg = AHCI_RD4(sc, T_AHCI_HBA_CAP_BKDR);
+ reg &= ~T_AHCI_HBA_CAP_BKDR_NUM_PORTS(~0);
+ reg |= T_AHCI_HBA_CAP_BKDR_NUM_PORTS(0);
+ reg |= T_AHCI_HBA_CAP_BKDR_EXT_SATA;
+ reg |= T_AHCI_HBA_CAP_BKDR_ENCL_MGMT_SUPP;
+ reg |= T_AHCI_HBA_CAP_BKDR_CMD_CMPL_COALESING;
+ reg |= T_AHCI_HBA_CAP_BKDR_FIS_SWITCHING;
+ reg |= T_AHCI_HBA_CAP_BKDR_SUPP_PM;
+ reg |= T_AHCI_HBA_CAP_BKDR_SUPP_CLO;
+ reg |= T_AHCI_HBA_CAP_BKDR_SUPP_STG_SPUP;
+ AHCI_WR4(sc, T_AHCI_HBA_CAP_BKDR, reg);
+
+ /* Overwrite AHCI portcapabilites. */
+ reg = AHCI_RD4(sc, T_AHCI_PORT_BKDR);
+ reg |= T_AHCI_PORT_BKDR_COLD_PRSN_DET;
+ reg |= T_AHCI_PORT_BKDR_HOTPLUG_CAP;
+ reg |= T_AHCI_PORT_BKDR_EXT_SATA_SUPP;
+ AHCI_WR4(sc, T_AHCI_PORT_BKDR, reg);
+
+ return (0);
+}
+
+static int
+tegra_ahci_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+ return (ENXIO);
+
+ device_set_desc_copy(dev, "AHCI SATA controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+tegra_ahci_attach(device_t dev)
+{
+ struct tegra_ahci_sc *sc;
+ struct ahci_controller *ctlr;
+ phandle_t node;
+ int rv, rid;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ ctlr = &sc->ctlr;
+ node = ofw_bus_get_node(dev);
+ sc->soc = (struct ahci_soc *)ofw_bus_search_compatible(dev,
+ compat_data)->ocd_data;
+
+ ctlr->r_rid = 0;
+ ctlr->r_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &ctlr->r_rid, RF_ACTIVE);
+ if (ctlr->r_mem == NULL)
+ return (ENXIO);
+
+ rid = 1;
+ sc->sata_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &rid, RF_ACTIVE);
+ if (sc->sata_mem == NULL) {
+ rv = ENXIO;
+ goto fail;
+ }
+
+ /* Aux is optionall */
+ rid = 2;
+ sc->aux_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &rid, RF_ACTIVE);
+
+ rv = get_fdt_resources(sc, node);
+ if (rv != 0) {
+ device_printf(sc->dev, "Failed to allocate FDT resource(s)\n");
+ goto fail;
+ }
+
+ rv = enable_fdt_resources(sc);
+ if (rv != 0) {
+ device_printf(sc->dev, "Failed to enable FDT resource(s)\n");
+ goto fail;
+ }
+ rv = tegra_ahci_ctrl_init(sc);
+ if (rv != 0) {
+ device_printf(sc->dev, "Failed to initialize controller)\n");
+ goto fail;
+ }
+
+ /* Setup controller defaults. */
+ ctlr->msi = 0;
+ ctlr->numirqs = 1;
+ ctlr->ccc = 0;
+
+ /* Reset controller. */
+ rv = tegra_ahci_ctlr_reset(dev);
+ if (rv != 0)
+ goto fail;
+ rv = ahci_attach(dev);
+ return (rv);
+
+fail:
+ /* XXX FDT stuff */
+ if (sc->sata_mem != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 1, sc->sata_mem);
+ if (ctlr->r_mem != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid,
+ ctlr->r_mem);
+ return (rv);
+}
+
+static int
+tegra_ahci_detach(device_t dev)
+{
+
+ ahci_detach(dev);
+ return (0);
+}
+
+static int
+tegra_ahci_suspend(device_t dev)
+{
+ struct tegra_ahci_sc *sc = device_get_softc(dev);
+
+ bus_generic_suspend(dev);
+ /* Disable interupts, so the state change(s) doesn't trigger. */
+ ATA_OUTL(sc->ctlr.r_mem, AHCI_GHC,
+ ATA_INL(sc->ctlr.r_mem, AHCI_GHC) & (~AHCI_GHC_IE));
+ return (0);
+}
+
+static int
+tegra_ahci_resume(device_t dev)
+{
+ int res;
+
+ if ((res = tegra_ahci_ctlr_reset(dev)) != 0)
+ return (res);
+ ahci_ctlr_setup(dev);
+ return (bus_generic_resume(dev));
+}
+
+static device_method_t tegra_ahci_methods[] = {
+ DEVMETHOD(device_probe, tegra_ahci_probe),
+ DEVMETHOD(device_attach, tegra_ahci_attach),
+ DEVMETHOD(device_detach, tegra_ahci_detach),
+ DEVMETHOD(device_suspend, tegra_ahci_suspend),
+ DEVMETHOD(device_resume, tegra_ahci_resume),
+ DEVMETHOD(bus_print_child, ahci_print_child),
+ DEVMETHOD(bus_alloc_resource, ahci_alloc_resource),
+ DEVMETHOD(bus_release_resource, ahci_release_resource),
+ DEVMETHOD(bus_setup_intr, ahci_setup_intr),
+ DEVMETHOD(bus_teardown_intr, ahci_teardown_intr),
+ DEVMETHOD(bus_child_location_str, ahci_child_location_str),
+ DEVMETHOD(bus_get_dma_tag, ahci_get_dma_tag),
+
+ DEVMETHOD_END
+};
+
+static DEFINE_CLASS_0(ahci, tegra_ahci_driver, tegra_ahci_methods,
+ sizeof(struct tegra_ahci_sc));
+DRIVER_MODULE(tegra_ahci, simplebus, tegra_ahci_driver, ahci_devclass,
+ NULL, NULL);
diff --git a/sys/arm/nvidia/tegra_efuse.c b/sys/arm/nvidia/tegra_efuse.c
new file mode 100644
index 000000000000..e18fd9c3a274
--- /dev/null
+++ b/sys/arm/nvidia/tegra_efuse.c
@@ -0,0 +1,538 @@
+/*-
+ * Copyright (c) 2015 Michal Meloun
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/clock.h>
+#include <sys/kernel.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/module.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/nvidia/tegra_efuse.h>
+
+#define FUSES_START 0x100
+#define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (FUSES_START + (_r)))
+
+struct efuse_soc;
+struct tegra_efuse_softc {
+ device_t dev;
+ struct resource *mem_res;
+
+ struct efuse_soc *soc;
+ clk_t clk;
+ hwreset_t reset;
+};
+
+struct tegra_efuse_softc *dev_sc;
+struct tegra_sku_info tegra_sku_info;
+static char *tegra_rev_name[] = {
+ [TEGRA_REVISION_UNKNOWN] = "unknown",
+ [TEGRA_REVISION_A01] = "A01",
+ [TEGRA_REVISION_A02] = "A02",
+ [TEGRA_REVISION_A03] = "A03",
+ [TEGRA_REVISION_A03p] = "A03 prime",
+ [TEGRA_REVISION_A04] = "A04",
+};
+
+struct efuse_soc {
+ void (*init)(struct tegra_efuse_softc *sc,
+ struct tegra_sku_info *sku);
+};
+
+static void tegra124_init(struct tegra_efuse_softc *sc,
+ struct tegra_sku_info *sku);
+struct efuse_soc tegra124_efuse_soc = {
+ .init = tegra124_init,
+};
+
+static void tegra210_init(struct tegra_efuse_softc *sc,
+ struct tegra_sku_info *sku);
+struct efuse_soc tegra210_efuse_soc = {
+ .init = tegra210_init,
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"nvidia,tegra124-efuse", (intptr_t)&tegra124_efuse_soc},
+ {"nvidia,tegra210-efuse", (intptr_t)&tegra210_efuse_soc},
+ {NULL, 0}
+};
+
+/* ---------------------- Tegra 124 specific code & data --------------- */
+#define TEGRA124_CPU_PROCESS_CORNERS 2
+#define TEGRA124_GPU_PROCESS_CORNERS 2
+#define TEGRA124_SOC_PROCESS_CORNERS 2
+
+#define TEGRA124_FUSE_SKU_INFO 0x10
+#define TEGRA124_FUSE_CPU_SPEEDO_0 0x14
+#define TEGRA124_FUSE_CPU_IDDQ 0x18
+#define TEGRA124_FUSE_FT_REV 0x28
+#define TEGRA124_FUSE_CPU_SPEEDO_1 0x2c
+#define TEGRA124_FUSE_CPU_SPEEDO_2 0x30
+#define TEGRA124_FUSE_SOC_SPEEDO_0 0x34
+#define TEGRA124_FUSE_SOC_SPEEDO_1 0x38
+#define TEGRA124_FUSE_SOC_SPEEDO_2 0x3c
+#define TEGRA124_FUSE_SOC_IDDQ 0x40
+#define TEGRA124_FUSE_GPU_IDDQ 0x128
+
+enum {
+ TEGRA124_THRESHOLD_INDEX_0,
+ TEGRA124_THRESHOLD_INDEX_1,
+ TEGRA124_THRESHOLD_INDEX_COUNT,
+};
+
+static uint32_t tegra124_cpu_process_speedos[][TEGRA124_CPU_PROCESS_CORNERS] =
+{
+ {2190, UINT_MAX},
+ {0, UINT_MAX},
+};
+
+static uint32_t tegra124_gpu_process_speedos[][TEGRA124_GPU_PROCESS_CORNERS] =
+{
+ {1965, UINT_MAX},
+ {0, UINT_MAX},
+};
+
+static uint32_t tegra124_soc_process_speedos[][TEGRA124_SOC_PROCESS_CORNERS] =
+{
+ {2101, UINT_MAX},
+ {0, UINT_MAX},
+};
+
+
+static void
+tegra124_rev_sku_to_speedo_ids(struct tegra_efuse_softc *sc,
+ struct tegra_sku_info *sku, int *threshold)
+{
+
+ /* Set default */
+ sku->cpu_speedo_id = 0;
+ sku->soc_speedo_id = 0;
+ sku->gpu_speedo_id = 0;
+ *threshold = TEGRA124_THRESHOLD_INDEX_0;
+
+ switch (sku->sku_id) {
+ case 0x00: /* Eng sku */
+ case 0x0F:
+ case 0x23:
+ /* Using the default */
+ break;
+ case 0x83:
+ sku->cpu_speedo_id = 2;
+ break;
+
+ case 0x1F:
+ case 0x87:
+ case 0x27:
+ sku->cpu_speedo_id = 2;
+ sku->soc_speedo_id = 0;
+ sku->gpu_speedo_id = 1;
+ *threshold = TEGRA124_THRESHOLD_INDEX_0;
+ break;
+ case 0x81:
+ case 0x21:
+ case 0x07:
+ sku->cpu_speedo_id = 1;
+ sku->soc_speedo_id = 1;
+ sku->gpu_speedo_id = 1;
+ *threshold = TEGRA124_THRESHOLD_INDEX_1;
+ break;
+ case 0x49:
+ case 0x4A:
+ case 0x48:
+ sku->cpu_speedo_id = 4;
+ sku->soc_speedo_id = 2;
+ sku->gpu_speedo_id = 3;
+ *threshold = TEGRA124_THRESHOLD_INDEX_1;
+ break;
+ default:
+ device_printf(sc->dev, " Unknown SKU ID %d\n", sku->sku_id);
+ break;
+ }
+}
+
+static void
+tegra124_init(struct tegra_efuse_softc *sc, struct tegra_sku_info *sku)
+{
+ int i, threshold;
+
+ sku->sku_id = RD4(sc, TEGRA124_FUSE_SKU_INFO);
+ sku->soc_iddq_value = RD4(sc, TEGRA124_FUSE_SOC_IDDQ);
+ sku->cpu_iddq_value = RD4(sc, TEGRA124_FUSE_CPU_IDDQ);
+ sku->gpu_iddq_value = RD4(sc, TEGRA124_FUSE_GPU_IDDQ);
+ sku->soc_speedo_value = RD4(sc, TEGRA124_FUSE_SOC_SPEEDO_0);
+ sku->cpu_speedo_value = RD4(sc, TEGRA124_FUSE_CPU_SPEEDO_0);
+ sku->gpu_speedo_value = RD4(sc, TEGRA124_FUSE_CPU_SPEEDO_2);
+
+ if (sku->cpu_speedo_value == 0) {
+ device_printf(sc->dev, "CPU Speedo value is not fused.\n");
+ return;
+ }
+
+ tegra124_rev_sku_to_speedo_ids(sc, sku, &threshold);
+
+ for (i = 0; i < TEGRA124_SOC_PROCESS_CORNERS; i++) {
+ if (sku->soc_speedo_value <
+ tegra124_soc_process_speedos[threshold][i])
+ break;
+ }
+ sku->soc_process_id = i;
+
+ for (i = 0; i < TEGRA124_CPU_PROCESS_CORNERS; i++) {
+ if (sku->cpu_speedo_value <
+ tegra124_cpu_process_speedos[threshold][i])
+ break;
+ }
+ sku->cpu_process_id = i;
+
+ for (i = 0; i < TEGRA124_GPU_PROCESS_CORNERS; i++) {
+ if (sku->gpu_speedo_value <
+ tegra124_gpu_process_speedos[threshold][i])
+ break;
+ }
+ sku->gpu_process_id = i;
+
+}
+/* ----------------- End of Tegra 124 specific code & data --------------- */
+
+/* -------------------- Tegra 201 specific code & data ------------------- */
+#define TEGRA210_CPU_PROCESS_CORNERS 2
+#define TEGRA210_GPU_PROCESS_CORNERS 2
+#define TEGRA210_SOC_PROCESS_CORNERS 3
+
+#define TEGRA210_FUSE_SKU_INFO 0x010
+#define TEGRA210_FUSE_CPU_SPEEDO_0 0x014
+#define TEGRA210_FUSE_CPU_IDDQ 0x018
+#define TEGRA210_FUSE_FT_REV 0x028
+#define TEGRA210_FUSE_CPU_SPEEDO_1 0x02c
+#define TEGRA210_FUSE_CPU_SPEEDO_2 0x030
+#define TEGRA210_FUSE_SOC_SPEEDO_0 0x034
+#define TEGRA210_FUSE_SOC_SPEEDO_1 0x038
+#define TEGRA210_FUSE_SOC_SPEEDO_2 0x03c
+#define TEGRA210_FUSE_SOC_IDDQ 0x040
+#define TEGRA210_FUSE_GPU_IDDQ 0x128
+#define TEGRA210_FUSE_SPARE 0x270
+
+enum {
+ TEGRA210_THRESHOLD_INDEX_0,
+ TEGRA210_THRESHOLD_INDEX_1,
+ TEGRA210_THRESHOLD_INDEX_COUNT,
+};
+
+static uint32_t tegra210_cpu_process_speedos[][TEGRA210_CPU_PROCESS_CORNERS] =
+{
+ {2119, UINT_MAX},
+ {2119, UINT_MAX},
+};
+
+static uint32_t tegra210_gpu_process_speedos[][TEGRA210_GPU_PROCESS_CORNERS] =
+{
+ {UINT_MAX, UINT_MAX},
+ {UINT_MAX, UINT_MAX},
+};
+
+static uint32_t tegra210_soc_process_speedos[][TEGRA210_SOC_PROCESS_CORNERS] =
+{
+ {1950, 2100, UINT_MAX},
+ {1950, 2100, UINT_MAX},
+};
+
+static uint32_t
+tegra210_get_speedo_revision(struct tegra_efuse_softc *sc)
+{
+ uint32_t reg;
+ uint32_t val;
+
+ val = 0;
+
+ /* Revision i encoded in spare fields */
+ reg = RD4(sc, TEGRA210_FUSE_SPARE + 2 * 4);
+ val |= (reg & 1) << 0;
+ reg = RD4(sc, TEGRA210_FUSE_SPARE + 3 * 4);
+ val |= (reg & 1) << 1;
+ reg = RD4(sc, TEGRA210_FUSE_SPARE + 4 * 4);
+ val |= (reg & 1) << 2;
+
+ return (val);
+}
+
+
+static void
+tegra210_rev_sku_to_speedo_ids(struct tegra_efuse_softc *sc,
+ struct tegra_sku_info *sku, int speedo_rev, int *threshold)
+{
+
+ /* Set defaults */
+ sku->cpu_speedo_id = 0;
+ sku->soc_speedo_id = 0;
+ sku->gpu_speedo_id = 0;
+ *threshold = TEGRA210_THRESHOLD_INDEX_0;
+
+ switch (sku->sku_id) {
+ case 0x00: /* Eng sku */
+ case 0x01: /* Eng sku */
+ case 0x07:
+ case 0x17:
+ case 0x27:
+ /* Use defaults */
+ if (speedo_rev >= 2)
+ sku->gpu_speedo_id = 1;
+ break;
+ case 0x13:
+ if (speedo_rev >= 2)
+ sku->gpu_speedo_id = 1;
+ sku->cpu_speedo_id = 1;
+ break;
+
+ default:
+ device_printf(sc->dev, " Unknown SKU ID %d\n", sku->sku_id);
+ break;
+ }
+}
+
+
+static void
+tegra210_init(struct tegra_efuse_softc *sc, struct tegra_sku_info *sku)
+{
+ int i, threshold, speedo_rev;
+ uint32_t cpu_speedo[3], soc_speedo[3];
+ uint32_t cpu_iddq, soc_iddq, gpu_iddq;
+
+ cpu_speedo[0] = RD4(sc, TEGRA210_FUSE_CPU_SPEEDO_0);
+ cpu_speedo[1] = RD4(sc, TEGRA210_FUSE_CPU_SPEEDO_1);
+ cpu_speedo[2] = RD4(sc, TEGRA210_FUSE_CPU_SPEEDO_2);
+ soc_speedo[0] = RD4(sc, TEGRA210_FUSE_SOC_SPEEDO_0);
+ soc_speedo[1] = RD4(sc, TEGRA210_FUSE_SOC_SPEEDO_1);
+ soc_speedo[2] = RD4(sc, TEGRA210_FUSE_SOC_SPEEDO_2);
+
+
+ sku->cpu_iddq_value = RD4(sc, TEGRA210_FUSE_CPU_IDDQ);
+ sku->soc_iddq_value = RD4(sc, TEGRA210_FUSE_SOC_IDDQ);
+ sku->gpu_iddq_value = RD4(sc, TEGRA210_FUSE_GPU_IDDQ);
+
+ cpu_iddq = RD4(sc, TEGRA210_FUSE_CPU_IDDQ) * 4;
+ soc_iddq = RD4(sc, TEGRA210_FUSE_SOC_IDDQ) * 4;
+ gpu_iddq = RD4(sc, TEGRA210_FUSE_GPU_IDDQ) * 5;
+
+ speedo_rev = tegra210_get_speedo_revision(sc);
+device_printf(sc->dev, " Speedo revision: %u\n", speedo_rev);
+
+ if (speedo_rev >= 3) {
+ sku->cpu_speedo_value = cpu_speedo[0];
+ sku->gpu_speedo_value = cpu_speedo[2];
+ sku->soc_speedo_value = soc_speedo[0];
+ } else if (speedo_rev == 2) {
+ sku->cpu_speedo_value =
+ (-1938 + (1095 * cpu_speedo[0] / 100)) / 10;
+ sku->gpu_speedo_value =
+ (-1662 + (1082 * cpu_speedo[2] / 100)) / 10;
+ sku->soc_speedo_value =
+ ( -705 + (1037 * soc_speedo[0] / 100)) / 10;
+ } else {
+ sku->cpu_speedo_value = 2100;
+ sku->gpu_speedo_value = cpu_speedo[2] - 75;
+ sku->soc_speedo_value = 1900;
+ }
+
+ tegra210_rev_sku_to_speedo_ids(sc, sku, speedo_rev, &threshold);
+
+ for (i = 0; i < TEGRA210_SOC_PROCESS_CORNERS; i++) {
+ if (sku->soc_speedo_value <
+ tegra210_soc_process_speedos[threshold][i])
+ break;
+ }
+ sku->soc_process_id = i;
+
+ for (i = 0; i < TEGRA210_CPU_PROCESS_CORNERS; i++) {
+ if (sku->cpu_speedo_value <
+ tegra210_cpu_process_speedos[threshold][i])
+ break;
+ }
+ sku->cpu_process_id = i;
+
+ for (i = 0; i < TEGRA210_GPU_PROCESS_CORNERS; i++) {
+ if (sku->gpu_speedo_value <
+ tegra210_gpu_process_speedos[threshold][i])
+ break;
+ }
+ sku->gpu_process_id = i;
+
+}
+
+/* ----------------- End of Tegra 210 specific code & data --------------- */
+
+
+uint32_t
+tegra_fuse_read_4(int addr) {
+ if (dev_sc == NULL)
+ panic("tegra_fuse_read_4 called too early");
+ return (RD4(dev_sc, addr));
+}
+
+static void
+tegra_efuse_dump_sku(void)
+{
+ printf(" TEGRA SKU Info:\n");
+ printf(" chip_id: %u\n", tegra_sku_info.chip_id);
+ printf(" sku_id: %u\n", tegra_sku_info.sku_id);
+ printf(" cpu_process_id: %u\n", tegra_sku_info.cpu_process_id);
+ printf(" cpu_speedo_id: %u\n", tegra_sku_info.cpu_speedo_id);
+ printf(" cpu_speedo_value: %u\n", tegra_sku_info.cpu_speedo_value);
+ printf(" cpu_iddq_value: %u\n", tegra_sku_info.cpu_iddq_value);
+ printf(" soc_process_id: %u\n", tegra_sku_info.soc_process_id);
+ printf(" soc_speedo_id: %u\n", tegra_sku_info.soc_speedo_id);
+ printf(" soc_speedo_value: %u\n", tegra_sku_info.soc_speedo_value);
+ printf(" soc_iddq_value: %u\n", tegra_sku_info.soc_iddq_value);
+ printf(" gpu_process_id: %u\n", tegra_sku_info.gpu_process_id);
+ printf(" gpu_speedo_id: %u\n", tegra_sku_info.gpu_speedo_id);
+ printf(" gpu_speedo_value: %u\n", tegra_sku_info.gpu_speedo_value);
+ printf(" gpu_iddq_value: %u\n", tegra_sku_info.gpu_iddq_value);
+ printf(" revision: %s\n", tegra_rev_name[tegra_sku_info.revision]);
+}
+
+static int
+tegra_efuse_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+tegra_efuse_attach(device_t dev)
+{
+ int rv, rid;
+ phandle_t node;
+ struct tegra_efuse_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ node = ofw_bus_get_node(dev);
+ sc->soc = (struct efuse_soc *)ofw_bus_search_compatible(dev,
+ compat_data)->ocd_data;
+
+ /* Get the memory resource for the register mapping. */
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "Cannot map registers.\n");
+ rv = ENXIO;
+ goto fail;
+ }
+
+ /* OFW resources. */
+ rv = clk_get_by_ofw_name(dev, 0, "fuse", &sc->clk);
+ if (rv != 0) {
+ device_printf(dev, "Cannot get fuse clock: %d\n", rv);
+ goto fail;
+ }
+ rv = clk_enable(sc->clk);
+ if (rv != 0) {
+ device_printf(dev, "Cannot enable clock: %d\n", rv);
+ goto fail;
+ }
+ rv = hwreset_get_by_ofw_name(sc->dev, 0, "fuse", &sc->reset);
+ if (rv != 0) {
+ device_printf(dev, "Cannot get fuse reset\n");
+ goto fail;
+ }
+ rv = hwreset_deassert(sc->reset);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot clear reset\n");
+ goto fail;
+ }
+
+ sc->soc->init(sc, &tegra_sku_info);
+
+ dev_sc = sc;
+
+ if (bootverbose)
+ tegra_efuse_dump_sku();
+ return (bus_generic_attach(dev));
+
+fail:
+ dev_sc = NULL;
+ if (sc->clk != NULL)
+ clk_release(sc->clk);
+ if (sc->reset != NULL)
+ hwreset_release(sc->reset);
+ if (sc->mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+
+ return (rv);
+}
+
+static int
+tegra_efuse_detach(device_t dev)
+{
+ struct tegra_efuse_softc *sc;
+
+ sc = device_get_softc(dev);
+ dev_sc = NULL;
+ if (sc->clk != NULL)
+ clk_release(sc->clk);
+ if (sc->reset != NULL)
+ hwreset_release(sc->reset);
+ if (sc->mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+
+ return (bus_generic_detach(dev));
+}
+
+static device_method_t tegra_efuse_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, tegra_efuse_probe),
+ DEVMETHOD(device_attach, tegra_efuse_attach),
+ DEVMETHOD(device_detach, tegra_efuse_detach),
+
+ DEVMETHOD_END
+};
+
+static devclass_t tegra_efuse_devclass;
+static DEFINE_CLASS_0(efuse, tegra_efuse_driver, tegra_efuse_methods,
+ sizeof(struct tegra_efuse_softc));
+EARLY_DRIVER_MODULE(tegra_efuse, simplebus, tegra_efuse_driver,
+ tegra_efuse_devclass, NULL, NULL, BUS_PASS_TIMER);
diff --git a/sys/arm/nvidia/tegra_efuse.h b/sys/arm/nvidia/tegra_efuse.h
new file mode 100644
index 000000000000..36804d067cd4
--- /dev/null
+++ b/sys/arm/nvidia/tegra_efuse.h
@@ -0,0 +1,61 @@
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _TEGRA_EFUSE_H_
+
+enum tegra_revision {
+ TEGRA_REVISION_UNKNOWN = 0,
+ TEGRA_REVISION_A01,
+ TEGRA_REVISION_A02,
+ TEGRA_REVISION_A03,
+ TEGRA_REVISION_A03p,
+ TEGRA_REVISION_A04,
+};
+
+struct tegra_sku_info {
+ u_int chip_id;
+ u_int sku_id;
+ u_int cpu_process_id;
+ u_int cpu_speedo_id;
+ u_int cpu_speedo_value;
+ u_int cpu_iddq_value;
+ u_int soc_process_id;
+ u_int soc_speedo_id;
+ u_int soc_speedo_value;
+ u_int soc_iddq_value;
+ u_int gpu_process_id;
+ u_int gpu_speedo_id;
+ u_int gpu_speedo_value;
+ u_int gpu_iddq_value;
+ enum tegra_revision revision;
+};
+
+extern struct tegra_sku_info tegra_sku_info;
+uint32_t tegra_fuse_read_4(int addr);
+
+#endif /* _TEGRA_EFUSE_H_ */
diff --git a/sys/arm/nvidia/tegra_ehci.c b/sys/arm/nvidia/tegra_ehci.c
new file mode 100644
index 000000000000..058cbe7c5fb9
--- /dev/null
+++ b/sys/arm/nvidia/tegra_ehci.c
@@ -0,0 +1,320 @@
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * EHCI driver for Tegra SoCs.
+ */
+#include "opt_bus.h"
+#include "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/condvar.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/extres/phy/phy.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/controller/ehci.h>
+#include <dev/usb/controller/ehcireg.h>
+
+#include "usbdevs.h"
+
+#define TEGRA_EHCI_REG_OFF 0x100
+#define TEGRA_EHCI_REG_SIZE 0x100
+
+/* Compatible devices. */
+#define TEGRA124_EHCI 1
+#define TEGRA210_EHCI 2
+static struct ofw_compat_data compat_data[] = {
+ {"nvidia,tegra124-ehci", (uintptr_t)TEGRA124_EHCI},
+ {"nvidia,tegra210-ehci", (uintptr_t)TEGRA210_EHCI},
+ {NULL, 0},
+};
+
+struct tegra_ehci_softc {
+ ehci_softc_t ehci_softc;
+ device_t dev;
+ struct resource *ehci_mem_res; /* EHCI core regs. */
+ struct resource *ehci_irq_res; /* EHCI core IRQ. */
+ int usb_alloc_called;
+ clk_t clk;
+ phy_t phy;
+ hwreset_t reset;
+};
+
+static void
+tegra_ehci_post_reset(struct ehci_softc *ehci_softc)
+{
+ uint32_t usbmode;
+
+ /* Force HOST mode. */
+ usbmode = EOREAD4(ehci_softc, EHCI_USBMODE_LPM);
+ usbmode &= ~EHCI_UM_CM;
+ usbmode |= EHCI_UM_CM_HOST;
+ device_printf(ehci_softc->sc_bus.bdev, "set host controller mode\n");
+ EOWRITE4(ehci_softc, EHCI_USBMODE_LPM, usbmode);
+}
+
+static int
+tegra_ehci_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) {
+ device_set_desc(dev, "Nvidia Tegra EHCI controller");
+ return (BUS_PROBE_DEFAULT);
+ }
+ return (ENXIO);
+}
+
+static int
+tegra_ehci_detach(device_t dev)
+{
+ struct tegra_ehci_softc *sc;
+ ehci_softc_t *esc;
+
+ sc = device_get_softc(dev);
+
+ esc = &sc->ehci_softc;
+ if (sc->clk != NULL)
+ clk_release(sc->clk);
+ if (esc->sc_bus.bdev != NULL)
+ device_delete_child(dev, esc->sc_bus.bdev);
+ if (esc->sc_flags & EHCI_SCFLG_DONEINIT)
+ ehci_detach(esc);
+ if (esc->sc_intr_hdl != NULL)
+ bus_teardown_intr(dev, esc->sc_irq_res,
+ esc->sc_intr_hdl);
+ if (sc->ehci_irq_res != NULL)
+ bus_release_resource(dev, SYS_RES_IRQ, 0,
+ sc->ehci_irq_res);
+ if (sc->ehci_mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0,
+ sc->ehci_mem_res);
+ if (sc->usb_alloc_called)
+ usb_bus_mem_free_all(&esc->sc_bus, &ehci_iterate_hw_softc);
+
+ /* During module unload there are lots of children leftover. */
+ device_delete_children(dev);
+
+ return (0);
+}
+
+static int
+tegra_ehci_attach(device_t dev)
+{
+ struct tegra_ehci_softc *sc;
+ ehci_softc_t *esc;
+ int rv, rid;
+ uint64_t freq;
+ phandle_t node;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ node = ofw_bus_get_node(dev);
+ esc = &sc->ehci_softc;
+
+ /* Allocate resources. */
+ rid = 0;
+ sc->ehci_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE | RF_SHAREABLE);
+ if (sc->ehci_mem_res == NULL) {
+ device_printf(dev, "Cannot allocate memory resources\n");
+ rv = ENXIO;
+ goto out;
+ }
+
+ rid = 0;
+ sc->ehci_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (sc->ehci_irq_res == NULL) {
+ device_printf(dev, "Cannot allocate IRQ resources\n");
+ rv = ENXIO;
+ goto out;
+ }
+
+ rv = hwreset_get_by_ofw_name(dev, 0, "usb", &sc->reset);
+ if (rv != 0) {
+ device_printf(dev, "Cannot get reset\n");
+ rv = ENXIO;
+ goto out;
+ }
+
+ rv = phy_get_by_ofw_property(sc->dev, 0, "nvidia,phy", &sc->phy);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'nvidia,phy' phy\n");
+ rv = ENXIO;
+ goto out;
+ }
+
+ rv = clk_get_by_ofw_index(sc->dev, 0, 0, &sc->clk);
+ if (rv != 0) {
+ device_printf(dev, "Cannot get clock\n");
+ goto out;
+ }
+
+ rv = clk_enable(sc->clk);
+ if (rv != 0) {
+ device_printf(dev, "Cannot enable clock\n");
+ goto out;
+ }
+
+ freq = 0;
+ rv = clk_get_freq(sc->clk, &freq);
+ if (rv != 0) {
+ device_printf(dev, "Cannot get clock frequency\n");
+ goto out;
+ }
+
+ rv = hwreset_deassert(sc->reset);
+ if (rv != 0) {
+ device_printf(dev, "Cannot clear reset: %d\n", rv);
+ rv = ENXIO;
+ goto out;
+ }
+
+ rv = phy_enable(sc->phy);
+ if (rv != 0) {
+ device_printf(dev, "Cannot enable phy: %d\n", rv);
+ goto out;
+ }
+
+ /* Fill data for EHCI driver. */
+ esc->sc_vendor_get_port_speed = ehci_get_port_speed_hostc;
+ esc->sc_vendor_post_reset = tegra_ehci_post_reset;
+ esc->sc_io_tag = rman_get_bustag(sc->ehci_mem_res);
+ esc->sc_bus.parent = dev;
+ esc->sc_bus.devices = esc->sc_devices;
+ esc->sc_bus.devices_max = EHCI_MAX_DEVICES;
+ esc->sc_bus.dma_bits = 32;
+
+ /* Allocate all DMA memory. */
+ rv = usb_bus_mem_alloc_all(&esc->sc_bus, USB_GET_DMA_TAG(dev),
+ &ehci_iterate_hw_softc);
+ sc->usb_alloc_called = 1;
+ if (rv != 0) {
+ device_printf(dev, "usb_bus_mem_alloc_all() failed\n");
+ rv = ENOMEM;
+ goto out;
+ }
+
+ /*
+ * Set handle to USB related registers subregion used by
+ * generic EHCI driver.
+ */
+ rv = bus_space_subregion(esc->sc_io_tag,
+ rman_get_bushandle(sc->ehci_mem_res),
+ TEGRA_EHCI_REG_OFF, TEGRA_EHCI_REG_SIZE, &esc->sc_io_hdl);
+ if (rv != 0) {
+ device_printf(dev, "Could not create USB memory subregion\n");
+ rv = ENXIO;
+ goto out;
+ }
+
+ /* Setup interrupt handler. */
+ rv = bus_setup_intr(dev, sc->ehci_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ NULL, (driver_intr_t *)ehci_interrupt, esc, &esc->sc_intr_hdl);
+ if (rv != 0) {
+ device_printf(dev, "Could not setup IRQ\n");
+ goto out;
+ }
+
+ /* Add USB bus device. */
+ esc->sc_bus.bdev = device_add_child(dev, "usbus", -1);
+ if (esc->sc_bus.bdev == NULL) {
+ device_printf(dev, "Could not add USB device\n");
+ goto out;
+ }
+ device_set_ivars(esc->sc_bus.bdev, &esc->sc_bus);
+
+ esc->sc_id_vendor = USB_VENDOR_FREESCALE;
+ strlcpy(esc->sc_vendor, "Nvidia", sizeof(esc->sc_vendor));
+
+ /* Set flags that affect ehci_init() behavior. */
+ esc->sc_flags |= EHCI_SCFLG_TT;
+ esc->sc_flags |= EHCI_SCFLG_NORESTERM;
+ rv = ehci_init(esc);
+ if (rv != 0) {
+ device_printf(dev, "USB init failed: %d\n",
+ rv);
+ goto out;
+ }
+ esc->sc_flags |= EHCI_SCFLG_DONEINIT;
+
+ /* Probe the bus. */
+ rv = device_probe_and_attach(esc->sc_bus.bdev);
+ if (rv != 0) {
+ device_printf(dev,
+ "device_probe_and_attach() failed\n");
+ goto out;
+ }
+ return (0);
+
+out:
+ tegra_ehci_detach(dev);
+ return (rv);
+}
+
+static device_method_t ehci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, tegra_ehci_probe),
+ DEVMETHOD(device_attach, tegra_ehci_attach),
+ DEVMETHOD(device_detach, tegra_ehci_detach),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+
+ DEVMETHOD_END
+};
+
+static devclass_t ehci_devclass;
+static DEFINE_CLASS_0(ehci, ehci_driver, ehci_methods,
+ sizeof(struct tegra_ehci_softc));
+DRIVER_MODULE(tegra_ehci, simplebus, ehci_driver, ehci_devclass, NULL, NULL);
+MODULE_DEPEND(tegra_ehci, usb, 1, 1, 1);
diff --git a/sys/arm/nvidia/tegra_gpio.c b/sys/arm/nvidia/tegra_gpio.c
new file mode 100644
index 000000000000..17a7ac2a9fb7
--- /dev/null
+++ b/sys/arm/nvidia/tegra_gpio.c
@@ -0,0 +1,889 @@
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Tegra GPIO driver.
+ */
+#include "opt_platform.h"
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/gpio.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+#include <machine/resource.h>
+
+#include <dev/gpio/gpiobusvar.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "pic_if.h"
+
+#define GPIO_LOCK(_sc) mtx_lock(&(_sc)->mtx)
+#define GPIO_UNLOCK(_sc) mtx_unlock(&(_sc)->mtx)
+#define GPIO_LOCK_INIT(_sc) mtx_init(&_sc->mtx, \
+ device_get_nameunit(_sc->dev), "tegra_gpio", MTX_DEF)
+#define GPIO_LOCK_DESTROY(_sc) mtx_destroy(&_sc->mtx);
+#define GPIO_ASSERT_LOCKED(_sc) mtx_assert(&_sc->mtx, MA_OWNED);
+#define GPIO_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED);
+
+#define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v))
+#define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r))
+
+#define GPIO_BANK_OFFS 0x100 /* Bank offset */
+#define GPIO_NUM_BANKS 8 /* Total number per bank */
+#define GPIO_REGS_IN_BANK 4 /* Total registers in bank */
+#define GPIO_PINS_IN_REG 8 /* Total pin in register */
+
+#define GPIO_BANKNUM(n) ((n) / (GPIO_REGS_IN_BANK * GPIO_PINS_IN_REG))
+#define GPIO_PORTNUM(n) (((n) / GPIO_PINS_IN_REG) % GPIO_REGS_IN_BANK)
+#define GPIO_BIT(n) ((n) % GPIO_PINS_IN_REG)
+
+#define GPIO_REGNUM(n) (GPIO_BANKNUM(n) * GPIO_BANK_OFFS + \
+ GPIO_PORTNUM(n) * 4)
+
+#define NGPIO ((GPIO_NUM_BANKS * GPIO_REGS_IN_BANK * GPIO_PINS_IN_REG) - 8)
+
+/* Register offsets */
+#define GPIO_CNF 0x00
+#define GPIO_OE 0x10
+#define GPIO_OUT 0x20
+#define GPIO_IN 0x30
+#define GPIO_INT_STA 0x40
+#define GPIO_INT_ENB 0x50
+#define GPIO_INT_LVL 0x60
+#define GPIO_INT_LVL_DELTA (1 << 16)
+#define GPIO_INT_LVL_EDGE (1 << 8)
+#define GPIO_INT_LVL_HIGH (1 << 0)
+#define GPIO_INT_LVL_MASK (GPIO_INT_LVL_DELTA | \
+ GPIO_INT_LVL_EDGE | GPIO_INT_LVL_HIGH)
+#define GPIO_INT_CLR 0x70
+#define GPIO_MSK_CNF 0x80
+#define GPIO_MSK_OE 0x90
+#define GPIO_MSK_OUT 0xA0
+#define GPIO_MSK_INT_STA 0xC0
+#define GPIO_MSK_INT_ENB 0xD0
+#define GPIO_MSK_INT_LVL 0xE0
+
+char *tegra_gpio_port_names[] = {
+ "A", "B", "C", "D", /* Bank 0 */
+ "E", "F", "G", "H", /* Bank 1 */
+ "I", "J", "K", "L", /* Bank 2 */
+ "M", "N", "O", "P", /* Bank 3 */
+ "Q", "R", "S", "T", /* Bank 4 */
+ "U", "V", "W", "X", /* Bank 5 */
+ "Y", "Z", "AA", "BB", /* Bank 6 */
+ "CC", "DD", "EE" /* Bank 7 */
+};
+
+struct tegra_gpio_irqsrc {
+ struct intr_irqsrc isrc;
+ u_int irq;
+ uint32_t cfgreg;
+};
+
+struct tegra_gpio_softc;
+struct tegra_gpio_irq_cookie {
+ struct tegra_gpio_softc *sc;
+ int bank_num;
+};
+
+struct tegra_gpio_softc {
+ device_t dev;
+ device_t busdev;
+ struct mtx mtx;
+ struct resource *mem_res;
+ struct resource *irq_res[GPIO_NUM_BANKS];
+ void *irq_ih[GPIO_NUM_BANKS];
+ struct tegra_gpio_irq_cookie irq_cookies[GPIO_NUM_BANKS];
+ int gpio_npins;
+ struct gpio_pin gpio_pins[NGPIO];
+ struct tegra_gpio_irqsrc *isrcs;
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"nvidia,tegra124-gpio", 1},
+ {"nvidia,tegra210-gpio", 1},
+ {NULL, 0}
+};
+
+/* --------------------------------------------------------------------------
+ *
+ * GPIO
+ *
+ */
+static inline void
+gpio_write_masked(struct tegra_gpio_softc *sc, bus_size_t reg,
+ struct gpio_pin *pin, uint32_t val)
+{
+ uint32_t tmp;
+ int bit;
+
+ bit = GPIO_BIT(pin->gp_pin);
+ tmp = 0x100 << bit; /* mask */
+ tmp |= (val & 1) << bit; /* value */
+ bus_write_4(sc->mem_res, reg + GPIO_REGNUM(pin->gp_pin), tmp);
+}
+
+static inline uint32_t
+gpio_read(struct tegra_gpio_softc *sc, bus_size_t reg, struct gpio_pin *pin)
+{
+ int bit;
+ uint32_t val;
+
+ bit = GPIO_BIT(pin->gp_pin);
+ val = bus_read_4(sc->mem_res, reg + GPIO_REGNUM(pin->gp_pin));
+ return (val >> bit) & 1;
+}
+
+static void
+tegra_gpio_pin_configure(struct tegra_gpio_softc *sc, struct gpio_pin *pin,
+ unsigned int flags)
+{
+
+ if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) == 0)
+ return;
+
+ /* Manage input/output */
+ pin->gp_flags &= ~(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT);
+ if (flags & GPIO_PIN_OUTPUT) {
+ pin->gp_flags |= GPIO_PIN_OUTPUT;
+ gpio_write_masked(sc, GPIO_MSK_OE, pin, 1);
+ } else {
+ pin->gp_flags |= GPIO_PIN_INPUT;
+ gpio_write_masked(sc, GPIO_MSK_OE, pin, 0);
+ }
+}
+
+static device_t
+tegra_gpio_get_bus(device_t dev)
+{
+ struct tegra_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+ return (sc->busdev);
+}
+
+static int
+tegra_gpio_pin_max(device_t dev, int *maxpin)
+{
+
+ *maxpin = NGPIO - 1;
+ return (0);
+}
+
+static int
+tegra_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
+{
+ struct tegra_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->gpio_npins)
+ return (EINVAL);
+
+ GPIO_LOCK(sc);
+ *caps = sc->gpio_pins[pin].gp_caps;
+ GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+tegra_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
+{
+ struct tegra_gpio_softc *sc;
+ int cnf;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->gpio_npins)
+ return (EINVAL);
+
+ GPIO_LOCK(sc);
+ cnf = gpio_read(sc, GPIO_CNF, &sc->gpio_pins[pin]);
+ if (cnf == 0) {
+ GPIO_UNLOCK(sc);
+ return (ENXIO);
+ }
+ *flags = sc->gpio_pins[pin].gp_flags;
+ GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+tegra_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
+{
+ struct tegra_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->gpio_npins)
+ return (EINVAL);
+
+ GPIO_LOCK(sc);
+ memcpy(name, sc->gpio_pins[pin].gp_name, GPIOMAXNAME);
+ GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+tegra_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
+{
+ struct tegra_gpio_softc *sc;
+ int cnf;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->gpio_npins)
+ return (EINVAL);
+
+ GPIO_LOCK(sc);
+ cnf = gpio_read(sc, GPIO_CNF, &sc->gpio_pins[pin]);
+ if (cnf == 0) {
+ /* XXX - allow this for while ....
+ GPIO_UNLOCK(sc);
+ return (ENXIO);
+ */
+ gpio_write_masked(sc, GPIO_MSK_CNF, &sc->gpio_pins[pin], 1);
+ }
+ tegra_gpio_pin_configure(sc, &sc->gpio_pins[pin], flags);
+ GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+tegra_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
+{
+ struct tegra_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->gpio_npins)
+ return (EINVAL);
+ GPIO_LOCK(sc);
+ gpio_write_masked(sc, GPIO_MSK_OUT, &sc->gpio_pins[pin], value);
+ GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+tegra_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
+{
+ struct tegra_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->gpio_npins)
+ return (EINVAL);
+
+ GPIO_LOCK(sc);
+ *val = gpio_read(sc, GPIO_IN, &sc->gpio_pins[pin]);
+ GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+tegra_gpio_pin_toggle(device_t dev, uint32_t pin)
+{
+ struct tegra_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->gpio_npins)
+ return (EINVAL);
+
+ GPIO_LOCK(sc);
+ gpio_write_masked(sc, GPIO_MSK_OE, &sc->gpio_pins[pin],
+ gpio_read(sc, GPIO_IN, &sc->gpio_pins[pin]) ^ 1);
+ GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+/* --------------------------------------------------------------------------
+ *
+ * Interrupts
+ *
+ */
+static inline void
+intr_write_masked(struct tegra_gpio_softc *sc, bus_addr_t reg,
+ struct tegra_gpio_irqsrc *tgi, uint32_t val)
+{
+ uint32_t tmp;
+ int bit;
+
+ bit = GPIO_BIT(tgi->irq);
+ tmp = 0x100 << bit; /* mask */
+ tmp |= (val & 1) << bit; /* value */
+ bus_write_4(sc->mem_res, reg + GPIO_REGNUM(tgi->irq), tmp);
+}
+
+static inline void
+intr_write_modify(struct tegra_gpio_softc *sc, bus_addr_t reg,
+ struct tegra_gpio_irqsrc *tgi, uint32_t val, uint32_t mask)
+{
+ uint32_t tmp;
+ int bit;
+
+ bit = GPIO_BIT(tgi->irq);
+ GPIO_LOCK(sc);
+ tmp = bus_read_4(sc->mem_res, reg + GPIO_REGNUM(tgi->irq));
+ tmp &= ~(mask << bit);
+ tmp |= val << bit;
+ bus_write_4(sc->mem_res, reg + GPIO_REGNUM(tgi->irq), tmp);
+ GPIO_UNLOCK(sc);
+}
+
+static inline void
+tegra_gpio_isrc_mask(struct tegra_gpio_softc *sc,
+ struct tegra_gpio_irqsrc *tgi, uint32_t val)
+{
+
+ intr_write_masked(sc, GPIO_MSK_INT_ENB, tgi, val);
+}
+
+static inline void
+tegra_gpio_isrc_eoi(struct tegra_gpio_softc *sc,
+ struct tegra_gpio_irqsrc *tgi)
+{
+
+ intr_write_masked(sc, GPIO_INT_CLR, tgi, 1);
+}
+
+static inline bool
+tegra_gpio_isrc_is_level(struct tegra_gpio_irqsrc *tgi)
+{
+
+ return (tgi->cfgreg & GPIO_INT_LVL_EDGE);
+}
+
+static int
+tegra_gpio_intr(void *arg)
+{
+ u_int irq, i, j, val, basepin;
+ struct tegra_gpio_softc *sc;
+ struct trapframe *tf;
+ struct tegra_gpio_irqsrc *tgi;
+ struct tegra_gpio_irq_cookie *cookie;
+
+ cookie = (struct tegra_gpio_irq_cookie *)arg;
+ sc = cookie->sc;
+ tf = curthread->td_intr_frame;
+
+ for (i = 0; i < GPIO_REGS_IN_BANK; i++) {
+ basepin = cookie->bank_num * GPIO_REGS_IN_BANK *
+ GPIO_PINS_IN_REG + i * GPIO_PINS_IN_REG;
+
+ val = bus_read_4(sc->mem_res, GPIO_INT_STA +
+ GPIO_REGNUM(basepin));
+ val &= bus_read_4(sc->mem_res, GPIO_INT_ENB +
+ GPIO_REGNUM(basepin));
+ /* Interrupt handling */
+ for (j = 0; j < GPIO_PINS_IN_REG; j++) {
+ if ((val & (1 << j)) == 0)
+ continue;
+ irq = basepin + j;
+ tgi = &sc->isrcs[irq];
+ if (!tegra_gpio_isrc_is_level(tgi))
+ tegra_gpio_isrc_eoi(sc, tgi);
+ if (intr_isrc_dispatch(&tgi->isrc, tf) != 0) {
+ tegra_gpio_isrc_mask(sc, tgi, 0);
+ if (tegra_gpio_isrc_is_level(tgi))
+ tegra_gpio_isrc_eoi(sc, tgi);
+ device_printf(sc->dev,
+ "Stray irq %u disabled\n", irq);
+ }
+ }
+ }
+
+ return (FILTER_HANDLED);
+}
+
+static int
+tegra_gpio_pic_attach(struct tegra_gpio_softc *sc)
+{
+ int error;
+ uint32_t irq;
+ const char *name;
+
+ sc->isrcs = malloc(sizeof(*sc->isrcs) * sc->gpio_npins, M_DEVBUF,
+ M_WAITOK | M_ZERO);
+
+ name = device_get_nameunit(sc->dev);
+ for (irq = 0; irq < sc->gpio_npins; irq++) {
+ sc->isrcs[irq].irq = irq;
+ sc->isrcs[irq].cfgreg = 0;
+ error = intr_isrc_register(&sc->isrcs[irq].isrc,
+ sc->dev, 0, "%s,%u", name, irq);
+ if (error != 0)
+ return (error); /* XXX deregister ISRCs */
+ }
+ if (intr_pic_register(sc->dev,
+ OF_xref_from_node(ofw_bus_get_node(sc->dev))) == NULL)
+ return (ENXIO);
+
+ return (0);
+}
+
+static int
+tegra_gpio_pic_detach(struct tegra_gpio_softc *sc)
+{
+
+ /*
+ * There has not been established any procedure yet
+ * how to detach PIC from living system correctly.
+ */
+ device_printf(sc->dev, "%s: not implemented yet\n", __func__);
+ return (EBUSY);
+}
+
+static void
+tegra_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct tegra_gpio_softc *sc;
+ struct tegra_gpio_irqsrc *tgi;
+
+ sc = device_get_softc(dev);
+ tgi = (struct tegra_gpio_irqsrc *)isrc;
+ tegra_gpio_isrc_mask(sc, tgi, 0);
+}
+
+static void
+tegra_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct tegra_gpio_softc *sc;
+ struct tegra_gpio_irqsrc *tgi;
+
+ sc = device_get_softc(dev);
+ tgi = (struct tegra_gpio_irqsrc *)isrc;
+ tegra_gpio_isrc_mask(sc, tgi, 1);
+}
+
+static int
+tegra_gpio_pic_map_fdt(struct tegra_gpio_softc *sc, u_int ncells,
+ pcell_t *cells, u_int *irqp, uint32_t *regp)
+{
+ uint32_t reg;
+
+ /*
+ * The first cell is the interrupt number.
+ * The second cell is used to specify flags:
+ * bits[3:0] trigger type and level flags:
+ * 1 = low-to-high edge triggered.
+ * 2 = high-to-low edge triggered.
+ * 4 = active high level-sensitive.
+ * 8 = active low level-sensitive.
+ */
+ if (ncells != 2 || cells[0] >= sc->gpio_npins)
+ return (EINVAL);
+
+ /*
+ * All interrupt types could be set for an interrupt at one moment.
+ * At least, the combination of 'low-to-high' and 'high-to-low' edge
+ * triggered interrupt types can make a sense.
+ */
+ if (cells[1] == 1)
+ reg = GPIO_INT_LVL_EDGE | GPIO_INT_LVL_HIGH;
+ else if (cells[1] == 2)
+ reg = GPIO_INT_LVL_EDGE;
+ else if (cells[1] == 3)
+ reg = GPIO_INT_LVL_EDGE | GPIO_INT_LVL_DELTA;
+ else if (cells[1] == 4)
+ reg = GPIO_INT_LVL_HIGH;
+ else if (cells[1] == 8)
+ reg = 0;
+ else
+ return (EINVAL);
+
+ *irqp = cells[0];
+ if (regp != NULL)
+ *regp = reg;
+ return (0);
+}
+
+static int
+tegra_gpio_pic_map_gpio(struct tegra_gpio_softc *sc, u_int gpio_pin_num,
+ u_int gpio_pin_flags, u_int intr_mode, u_int *irqp, uint32_t *regp)
+{
+
+ uint32_t reg;
+
+ if (gpio_pin_num >= sc->gpio_npins)
+ return (EINVAL);
+ switch (intr_mode) {
+ case GPIO_INTR_CONFORM:
+ case GPIO_INTR_LEVEL_LOW:
+ reg = 0;
+ break;
+ case GPIO_INTR_LEVEL_HIGH:
+ reg = GPIO_INT_LVL_HIGH;
+ break;
+ case GPIO_INTR_EDGE_RISING:
+ reg = GPIO_INT_LVL_EDGE | GPIO_INT_LVL_HIGH;
+ break;
+ case GPIO_INTR_EDGE_FALLING:
+ reg = GPIO_INT_LVL_EDGE;
+ break;
+ case GPIO_INTR_EDGE_BOTH:
+ reg = GPIO_INT_LVL_EDGE | GPIO_INT_LVL_DELTA;
+ break;
+ default:
+ return (EINVAL);
+ }
+ *irqp = gpio_pin_num;
+ if (regp != NULL)
+ *regp = reg;
+ return (0);
+}
+
+static int
+tegra_gpio_pic_map_intr(device_t dev, struct intr_map_data *data,
+ struct intr_irqsrc **isrcp)
+{
+ int rv;
+ u_int irq;
+ struct tegra_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (data->type == INTR_MAP_DATA_FDT) {
+ struct intr_map_data_fdt *daf;
+
+ daf = (struct intr_map_data_fdt *)data;
+ rv = tegra_gpio_pic_map_fdt(sc, daf->ncells, daf->cells, &irq,
+ NULL);
+ } else if (data->type == INTR_MAP_DATA_GPIO) {
+ struct intr_map_data_gpio *dag;
+
+ dag = (struct intr_map_data_gpio *)data;
+ rv = tegra_gpio_pic_map_gpio(sc, dag->gpio_pin_num,
+ dag->gpio_pin_flags, dag->gpio_intr_mode, &irq, NULL);
+ } else
+ return (ENOTSUP);
+
+ if (rv == 0)
+ *isrcp = &sc->isrcs[irq].isrc;
+ return (rv);
+}
+
+static void
+tegra_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct tegra_gpio_softc *sc;
+ struct tegra_gpio_irqsrc *tgi;
+
+ sc = device_get_softc(dev);
+ tgi = (struct tegra_gpio_irqsrc *)isrc;
+ if (tegra_gpio_isrc_is_level(tgi))
+ tegra_gpio_isrc_eoi(sc, tgi);
+}
+
+static void
+tegra_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct tegra_gpio_softc *sc;
+ struct tegra_gpio_irqsrc *tgi;
+
+ sc = device_get_softc(dev);
+ tgi = (struct tegra_gpio_irqsrc *)isrc;
+ tegra_gpio_isrc_mask(sc, tgi, 1);
+}
+
+static void
+tegra_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct tegra_gpio_softc *sc;
+ struct tegra_gpio_irqsrc *tgi;
+
+ sc = device_get_softc(dev);
+ tgi = (struct tegra_gpio_irqsrc *)isrc;
+
+ tegra_gpio_isrc_mask(sc, tgi, 0);
+ if (tegra_gpio_isrc_is_level(tgi))
+ tegra_gpio_isrc_eoi(sc, tgi);
+}
+
+static int
+tegra_gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ u_int irq;
+ uint32_t cfgreg;
+ int rv;
+ struct tegra_gpio_softc *sc;
+ struct tegra_gpio_irqsrc *tgi;
+
+ sc = device_get_softc(dev);
+ tgi = (struct tegra_gpio_irqsrc *)isrc;
+
+ if (data == NULL)
+ return (ENOTSUP);
+
+ /* Get and check config for an interrupt. */
+ if (data->type == INTR_MAP_DATA_FDT) {
+ struct intr_map_data_fdt *daf;
+
+ daf = (struct intr_map_data_fdt *)data;
+ rv = tegra_gpio_pic_map_fdt(sc, daf->ncells, daf->cells, &irq,
+ &cfgreg);
+ } else if (data->type == INTR_MAP_DATA_GPIO) {
+ struct intr_map_data_gpio *dag;
+
+ dag = (struct intr_map_data_gpio *)data;
+ rv = tegra_gpio_pic_map_gpio(sc, dag->gpio_pin_num,
+ dag->gpio_pin_flags, dag->gpio_intr_mode, &irq, &cfgreg);
+ } else
+ return (ENOTSUP);
+ if (rv != 0)
+ return (EINVAL);
+
+ /*
+ * If this is a setup for another handler,
+ * only check that its configuration match.
+ */
+ if (isrc->isrc_handlers != 0)
+ return (tgi->cfgreg == cfgreg ? 0 : EINVAL);
+
+ tgi->cfgreg = cfgreg;
+ intr_write_modify(sc, GPIO_INT_LVL, tgi, cfgreg, GPIO_INT_LVL_MASK);
+ tegra_gpio_pic_enable_intr(dev, isrc);
+
+ return (0);
+}
+
+static int
+tegra_gpio_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ struct tegra_gpio_softc *sc;
+ struct tegra_gpio_irqsrc *tgi;
+
+ sc = device_get_softc(dev);
+ tgi = (struct tegra_gpio_irqsrc *)isrc;
+
+ if (isrc->isrc_handlers == 0)
+ tegra_gpio_isrc_mask(sc, tgi, 0);
+ return (0);
+}
+
+static int
+tegra_gpio_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) {
+ device_set_desc(dev, "Tegra GPIO Controller");
+ return (BUS_PROBE_DEFAULT);
+ }
+
+ return (ENXIO);
+}
+
+/* --------------------------------------------------------------------------
+ *
+ * Bus
+ *
+ */
+static int
+tegra_gpio_detach(device_t dev)
+{
+ struct tegra_gpio_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+
+ KASSERT(mtx_initialized(&sc->mtx), ("gpio mutex not initialized"));
+
+ for (i = 0; i < GPIO_NUM_BANKS; i++) {
+ if (sc->irq_ih[i] != NULL)
+ bus_teardown_intr(dev, sc->irq_res[i], sc->irq_ih[i]);
+ }
+
+ if (sc->isrcs != NULL)
+ tegra_gpio_pic_detach(sc);
+
+ gpiobus_detach_bus(dev);
+
+ for (i = 0; i < GPIO_NUM_BANKS; i++) {
+ if (sc->irq_res[i] != NULL)
+ bus_release_resource(dev, SYS_RES_IRQ, 0,
+ sc->irq_res[i]);
+ }
+ if (sc->mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+ GPIO_LOCK_DESTROY(sc);
+
+ return(0);
+}
+
+static int
+tegra_gpio_attach(device_t dev)
+{
+ struct tegra_gpio_softc *sc;
+ int i, rid;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ GPIO_LOCK_INIT(sc);
+
+ /* Allocate bus_space resources. */
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "Cannot allocate memory resources\n");
+ tegra_gpio_detach(dev);
+ return (ENXIO);
+ }
+
+ sc->gpio_npins = NGPIO;
+ for (i = 0; i < sc->gpio_npins; i++) {
+ sc->gpio_pins[i].gp_pin = i;
+ sc->gpio_pins[i].gp_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT |
+ GPIO_INTR_LEVEL_LOW | GPIO_INTR_LEVEL_HIGH |
+ GPIO_INTR_EDGE_RISING | GPIO_INTR_EDGE_FALLING |
+ GPIO_INTR_EDGE_BOTH;
+ snprintf(sc->gpio_pins[i].gp_name, GPIOMAXNAME, "gpio_%s.%d",
+ tegra_gpio_port_names[ i / GPIO_PINS_IN_REG],
+ i % GPIO_PINS_IN_REG);
+ sc->gpio_pins[i].gp_flags =
+ gpio_read(sc, GPIO_OE, &sc->gpio_pins[i]) != 0 ?
+ GPIO_PIN_OUTPUT : GPIO_PIN_INPUT;
+ }
+
+ /* Init interrupt related registes. */
+ for (i = 0; i < sc->gpio_npins; i += GPIO_PINS_IN_REG) {
+ bus_write_4(sc->mem_res, GPIO_INT_ENB + GPIO_REGNUM(i), 0);
+ bus_write_4(sc->mem_res, GPIO_INT_STA + GPIO_REGNUM(i), 0xFF);
+ bus_write_4(sc->mem_res, GPIO_INT_CLR + GPIO_REGNUM(i), 0xFF);
+ }
+
+ /* Allocate interrupts. */
+ for (i = 0; i < GPIO_NUM_BANKS; i++) {
+ sc->irq_cookies[i].sc = sc;
+ sc->irq_cookies[i].bank_num = i;
+ rid = i;
+ sc->irq_res[i] = bus_alloc_resource_any(dev, SYS_RES_IRQ,
+ &rid, RF_ACTIVE);
+ if (sc->irq_res[i] == NULL) {
+ device_printf(dev, "Cannot allocate IRQ resources\n");
+ tegra_gpio_detach(dev);
+ return (ENXIO);
+ }
+ if ((bus_setup_intr(dev, sc->irq_res[i],
+ INTR_TYPE_MISC | INTR_MPSAFE, tegra_gpio_intr, NULL,
+ &sc->irq_cookies[i], &sc->irq_ih[i]))) {
+ device_printf(dev,
+ "WARNING: unable to register interrupt handler\n");
+ tegra_gpio_detach(dev);
+ return (ENXIO);
+ }
+ }
+
+ if (tegra_gpio_pic_attach(sc) != 0) {
+ device_printf(dev, "WARNING: unable to attach PIC\n");
+ tegra_gpio_detach(dev);
+ return (ENXIO);
+ }
+
+ sc->busdev = gpiobus_attach_bus(dev);
+ if (sc->busdev == NULL) {
+ tegra_gpio_detach(dev);
+ return (ENXIO);
+ }
+
+ return (bus_generic_attach(dev));
+}
+
+static int
+tegra_map_gpios(device_t dev, phandle_t pdev, phandle_t gparent,
+ int gcells, pcell_t *gpios, uint32_t *pin, uint32_t *flags)
+{
+
+ if (gcells != 2)
+ return (ERANGE);
+ *pin = gpios[0];
+ *flags= gpios[1];
+ return (0);
+}
+
+static phandle_t
+tegra_gpio_get_node(device_t bus, device_t dev)
+{
+
+ /* We only have one child, the GPIO bus, which needs our own node. */
+ return (ofw_bus_get_node(bus));
+}
+
+static device_method_t tegra_gpio_methods[] = {
+ DEVMETHOD(device_probe, tegra_gpio_probe),
+ DEVMETHOD(device_attach, tegra_gpio_attach),
+ DEVMETHOD(device_detach, tegra_gpio_detach),
+
+ /* Interrupt controller interface */
+ DEVMETHOD(pic_disable_intr, tegra_gpio_pic_disable_intr),
+ DEVMETHOD(pic_enable_intr, tegra_gpio_pic_enable_intr),
+ DEVMETHOD(pic_map_intr, tegra_gpio_pic_map_intr),
+ DEVMETHOD(pic_setup_intr, tegra_gpio_pic_setup_intr),
+ DEVMETHOD(pic_teardown_intr, tegra_gpio_pic_teardown_intr),
+ DEVMETHOD(pic_post_filter, tegra_gpio_pic_post_filter),
+ DEVMETHOD(pic_post_ithread, tegra_gpio_pic_post_ithread),
+ DEVMETHOD(pic_pre_ithread, tegra_gpio_pic_pre_ithread),
+
+ /* GPIO protocol */
+ DEVMETHOD(gpio_get_bus, tegra_gpio_get_bus),
+ DEVMETHOD(gpio_pin_max, tegra_gpio_pin_max),
+ DEVMETHOD(gpio_pin_getname, tegra_gpio_pin_getname),
+ DEVMETHOD(gpio_pin_getflags, tegra_gpio_pin_getflags),
+ DEVMETHOD(gpio_pin_getcaps, tegra_gpio_pin_getcaps),
+ DEVMETHOD(gpio_pin_setflags, tegra_gpio_pin_setflags),
+ DEVMETHOD(gpio_pin_get, tegra_gpio_pin_get),
+ DEVMETHOD(gpio_pin_set, tegra_gpio_pin_set),
+ DEVMETHOD(gpio_pin_toggle, tegra_gpio_pin_toggle),
+ DEVMETHOD(gpio_map_gpios, tegra_map_gpios),
+
+ /* ofw_bus interface */
+ DEVMETHOD(ofw_bus_get_node, tegra_gpio_get_node),
+
+ DEVMETHOD_END
+};
+
+static devclass_t tegra_gpio_devclass;
+static DEFINE_CLASS_0(gpio, tegra_gpio_driver, tegra_gpio_methods,
+ sizeof(struct tegra_gpio_softc));
+EARLY_DRIVER_MODULE(tegra_gpio, simplebus, tegra_gpio_driver,
+ tegra_gpio_devclass, NULL, NULL, 70);
diff --git a/sys/arm/nvidia/tegra_i2c.c b/sys/arm/nvidia/tegra_i2c.c
new file mode 100644
index 000000000000..02db5218b883
--- /dev/null
+++ b/sys/arm/nvidia/tegra_i2c.c
@@ -0,0 +1,802 @@
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * I2C driver for Tegra SoCs.
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/limits.h>
+#include <sys/module.h>
+#include <sys/resource.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/iicbus/iiconf.h>
+#include <dev/iicbus/iicbus.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "iicbus_if.h"
+
+#define I2C_CNFG 0x000
+#define I2C_CNFG_MSTR_CLR_BUS_ON_TIMEOUT (1 << 15)
+#define I2C_CNFG_DEBOUNCE_CNT(x) (((x) & 0x07) << 12)
+#define I2C_CNFG_NEW_MASTER_FSM (1 << 11)
+#define I2C_CNFG_PACKET_MODE_EN (1 << 10)
+#define I2C_CNFG_SEND (1 << 9)
+#define I2C_CNFG_NOACK (1 << 8)
+#define I2C_CNFG_CMD2 (1 << 7)
+#define I2C_CNFG_CMD1 (1 << 6)
+#define I2C_CNFG_START (1 << 5)
+#define I2C_CNFG_SLV2 (1 << 4)
+#define I2C_CNFG_LENGTH_SHIFT 1
+#define I2C_CNFG_LENGTH_MASK 0x7
+#define I2C_CNFG_A_MOD (1 << 0)
+
+#define I2C_CMD_ADDR0 0x004
+#define I2C_CMD_ADDR1 0x008
+#define I2C_CMD_DATA1 0x00c
+#define I2C_CMD_DATA2 0x010
+#define I2C_STATUS 0x01c
+#define I2C_SL_CNFG 0x020
+#define I2C_SL_RCVD 0x024
+#define I2C_SL_STATUS 0x028
+#define I2C_SL_ADDR1 0x02c
+#define I2C_SL_ADDR2 0x030
+#define I2C_TLOW_SEXT 0x034
+#define I2C_SL_DELAY_COUNT 0x03c
+#define I2C_SL_INT_MASK 0x040
+#define I2C_SL_INT_SOURCE 0x044
+#define I2C_SL_INT_SET 0x048
+#define I2C_TX_PACKET_FIFO 0x050
+#define I2C_RX_FIFO 0x054
+#define I2C_PACKET_TRANSFER_STATUS 0x058
+#define I2C_FIFO_CONTROL 0x05c
+#define I2C_FIFO_CONTROL_SLV_TX_FIFO_TRIG(x) (((x) & 0x07) << 13)
+#define I2C_FIFO_CONTROL_SLV_RX_FIFO_TRIG(x) (((x) & 0x07) << 10)
+#define I2C_FIFO_CONTROL_SLV_TX_FIFO_FLUSH (1 << 9)
+#define I2C_FIFO_CONTROL_SLV_RX_FIFO_FLUSH (1 << 8)
+#define I2C_FIFO_CONTROL_TX_FIFO_TRIG(x) (((x) & 0x07) << 5)
+#define I2C_FIFO_CONTROL_RX_FIFO_TRIG(x) (((x) & 0x07) << 2)
+#define I2C_FIFO_CONTROL_TX_FIFO_FLUSH (1 << 1)
+#define I2C_FIFO_CONTROL_RX_FIFO_FLUSH (1 << 0)
+
+#define I2C_FIFO_STATUS 0x060
+#define I2C_FIFO_STATUS_SLV_XFER_ERR_REASON (1 << 25)
+#define I2C_FIFO_STATUS_TX_FIFO_SLV_EMPTY_CNT(x) (((x) >> 20) & 0xF)
+#define I2C_FIFO_STATUS_RX_FIFO_SLV_FULL_CNT(x) (((x) >> 16) & 0xF)
+#define I2C_FIFO_STATUS_TX_FIFO_EMPTY_CNT(x) (((x) >> 4) & 0xF)
+#define I2C_FIFO_STATUS_RX_FIFO_FULL_CNT(x) (((x) >> 0) & 0xF)
+
+#define I2C_INTERRUPT_MASK_REGISTER 0x064
+#define I2C_INTERRUPT_STATUS_REGISTER 0x068
+#define I2C_INT_SLV_ACK_WITHHELD (1 << 28)
+#define I2C_INT_SLV_RD2WR (1 << 27)
+#define I2C_INT_SLV_WR2RD (1 << 26)
+#define I2C_INT_SLV_PKT_XFER_ERR (1 << 25)
+#define I2C_INT_SLV_TX_BUFFER_REQ (1 << 24)
+#define I2C_INT_SLV_RX_BUFFER_FILLED (1 << 23)
+#define I2C_INT_SLV_PACKET_XFER_COMPLETE (1 << 22)
+#define I2C_INT_SLV_TFIFO_OVF (1 << 21)
+#define I2C_INT_SLV_RFIFO_UNF (1 << 20)
+#define I2C_INT_SLV_TFIFO_DATA_REQ (1 << 17)
+#define I2C_INT_SLV_RFIFO_DATA_REQ (1 << 16)
+#define I2C_INT_BUS_CLEAR_DONE (1 << 11)
+#define I2C_INT_TLOW_MEXT_TIMEOUT (1 << 10)
+#define I2C_INT_TLOW_SEXT_TIMEOUT (1 << 9)
+#define I2C_INT_TIMEOUT (1 << 8)
+#define I2C_INT_PACKET_XFER_COMPLETE (1 << 7)
+#define I2C_INT_ALL_PACKETS_XFER_COMPLETE (1 << 6)
+#define I2C_INT_TFIFO_OVR (1 << 5)
+#define I2C_INT_RFIFO_UNF (1 << 4)
+#define I2C_INT_NOACK (1 << 3)
+#define I2C_INT_ARB_LOST (1 << 2)
+#define I2C_INT_TFIFO_DATA_REQ (1 << 1)
+#define I2C_INT_RFIFO_DATA_REQ (1 << 0)
+#define I2C_ERROR_MASK (I2C_INT_ARB_LOST | I2C_INT_NOACK | \
+ I2C_INT_RFIFO_UNF | I2C_INT_TFIFO_OVR)
+
+#define I2C_CLK_DIVISOR 0x06c
+#define I2C_CLK_DIVISOR_STD_FAST_MODE_SHIFT 16
+#define I2C_CLK_DIVISOR_STD_FAST_MODE_MASK 0xffff
+#define I2C_CLK_DIVISOR_HSMODE_SHIFT 0
+#define I2C_CLK_DIVISOR_HSMODE_MASK 0xffff
+#define I2C_INTERRUPT_SOURCE_REGISTER 0x070
+#define I2C_INTERRUPT_SET_REGISTER 0x074
+#define I2C_SLV_TX_PACKET_FIFO 0x07c
+#define I2C_SLV_PACKET_STATUS 0x080
+#define I2C_BUS_CLEAR_CONFIG 0x084
+#define I2C_BUS_CLEAR_CONFIG_BC_SCLK_THRESHOLD(x) (((x) & 0xFF) << 16)
+#define I2C_BUS_CLEAR_CONFIG_BC_STOP_COND (1 << 2)
+#define I2C_BUS_CLEAR_CONFIG_BC_TERMINATE (1 << 1)
+#define I2C_BUS_CLEAR_CONFIG_BC_ENABLE (1 << 0)
+
+#define I2C_BUS_CLEAR_STATUS 0x088
+#define I2C_BUS_CLEAR_STATUS_BC_STATUS (1 << 0)
+
+#define I2C_CONFIG_LOAD 0x08c
+#define I2C_CONFIG_LOAD_TIMEOUT_CONFIG_LOAD (1 << 2)
+#define I2C_CONFIG_LOAD_SLV_CONFIG_LOAD (1 << 1)
+#define I2C_CONFIG_LOAD_MSTR_CONFIG_LOAD (1 << 0)
+
+#define I2C_INTERFACE_TIMING_0 0x094
+#define I2C_INTERFACE_TIMING_1 0x098
+#define I2C_HS_INTERFACE_TIMING_0 0x09c
+#define I2C_HS_INTERFACE_TIMING_1 0x0a0
+
+/* Protocol header 0 */
+#define PACKET_HEADER0_HEADER_SIZE_SHIFT 28
+#define PACKET_HEADER0_HEADER_SIZE_MASK 0x3
+#define PACKET_HEADER0_PACKET_ID_SHIFT 16
+#define PACKET_HEADER0_PACKET_ID_MASK 0xff
+#define PACKET_HEADER0_CONT_ID_SHIFT 12
+#define PACKET_HEADER0_CONT_ID_MASK 0xf
+#define PACKET_HEADER0_PROTOCOL_I2C (1 << 4)
+#define PACKET_HEADER0_TYPE_SHIFT 0
+#define PACKET_HEADER0_TYPE_MASK 0x7
+
+/* I2C header */
+#define I2C_HEADER_HIGHSPEED_MODE (1 << 22)
+#define I2C_HEADER_CONT_ON_NAK (1 << 21)
+#define I2C_HEADER_SEND_START_BYTE (1 << 20)
+#define I2C_HEADER_READ (1 << 19)
+#define I2C_HEADER_10BIT_ADDR (1 << 18)
+#define I2C_HEADER_IE_ENABLE (1 << 17)
+#define I2C_HEADER_REPEAT_START (1 << 16)
+#define I2C_HEADER_CONTINUE_XFER (1 << 15)
+#define I2C_HEADER_MASTER_ADDR_SHIFT 12
+#define I2C_HEADER_MASTER_ADDR_MASK 0x7
+#define I2C_HEADER_SLAVE_ADDR_SHIFT 0
+#define I2C_HEADER_SLAVE_ADDR_MASK 0x3ff
+
+#define I2C_CLK_DIVISOR_STD_FAST_MODE 0x19
+#define I2C_CLK_MULTIPLIER_STD_FAST_MODE 8
+
+#define I2C_REQUEST_TIMEOUT (5 * hz)
+
+#define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v))
+#define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r))
+
+#define LOCK(_sc) mtx_lock(&(_sc)->mtx)
+#define UNLOCK(_sc) mtx_unlock(&(_sc)->mtx)
+#define SLEEP(_sc, timeout) \
+ mtx_sleep(sc, &sc->mtx, 0, "i2cbuswait", timeout);
+#define LOCK_INIT(_sc) \
+ mtx_init(&_sc->mtx, device_get_nameunit(_sc->dev), "tegra_i2c", MTX_DEF)
+#define LOCK_DESTROY(_sc) mtx_destroy(&_sc->mtx)
+#define ASSERT_LOCKED(_sc) mtx_assert(&_sc->mtx, MA_OWNED)
+#define ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED)
+
+static struct ofw_compat_data compat_data[] = {
+ {"nvidia,tegra124-i2c", 1},
+ {"nvidia,tegra210-i2c", 1},
+ {NULL, 0}
+};
+enum tegra_i2c_xfer_type {
+ XFER_STOP, /* Send stop condition after xfer */
+ XFER_REPEAT_START, /* Send repeated start after xfer */
+ XFER_CONTINUE /* Don't send nothing */
+} ;
+
+struct tegra_i2c_softc {
+ device_t dev;
+ struct mtx mtx;
+
+ struct resource *mem_res;
+ struct resource *irq_res;
+ void *irq_h;
+
+ device_t iicbus;
+ clk_t clk;
+ hwreset_t reset;
+ uint32_t core_freq;
+ uint32_t bus_freq;
+ int bus_inuse;
+
+ struct iic_msg *msg;
+ int msg_idx;
+ uint32_t bus_err;
+ int done;
+};
+
+static int
+tegra_i2c_flush_fifo(struct tegra_i2c_softc *sc)
+{
+ int timeout;
+ uint32_t reg;
+
+ reg = RD4(sc, I2C_FIFO_CONTROL);
+ reg |= I2C_FIFO_CONTROL_TX_FIFO_FLUSH | I2C_FIFO_CONTROL_RX_FIFO_FLUSH;
+ WR4(sc, I2C_FIFO_CONTROL, reg);
+
+ timeout = 10;
+ while (timeout > 0) {
+ reg = RD4(sc, I2C_FIFO_CONTROL);
+ reg &= I2C_FIFO_CONTROL_TX_FIFO_FLUSH |
+ I2C_FIFO_CONTROL_RX_FIFO_FLUSH;
+ if (reg == 0)
+ break;
+ DELAY(10);
+ }
+ if (timeout <= 0) {
+ device_printf(sc->dev, "FIFO flush timedout\n");
+ return (ETIMEDOUT);
+ }
+ return (0);
+}
+
+static void
+tegra_i2c_setup_clk(struct tegra_i2c_softc *sc, int clk_freq)
+{
+ int div;
+
+ div = ((sc->core_freq / clk_freq) / 10) - 1;
+ if ((sc->core_freq / (10 * (div + 1))) > clk_freq)
+ div++;
+ if (div > 65535)
+ div = 65535;
+ WR4(sc, I2C_CLK_DIVISOR,
+ (1 << I2C_CLK_DIVISOR_HSMODE_SHIFT) |
+ (div << I2C_CLK_DIVISOR_STD_FAST_MODE_SHIFT));
+}
+
+static void
+tegra_i2c_bus_clear(struct tegra_i2c_softc *sc)
+{
+ int timeout;
+ uint32_t reg, status;
+
+ WR4(sc, I2C_BUS_CLEAR_CONFIG,
+ I2C_BUS_CLEAR_CONFIG_BC_SCLK_THRESHOLD(18) |
+ I2C_BUS_CLEAR_CONFIG_BC_STOP_COND |
+ I2C_BUS_CLEAR_CONFIG_BC_TERMINATE);
+
+ WR4(sc, I2C_CONFIG_LOAD, I2C_CONFIG_LOAD_MSTR_CONFIG_LOAD);
+ for (timeout = 1000; timeout > 0; timeout--) {
+ if (RD4(sc, I2C_CONFIG_LOAD) == 0)
+ break;
+ DELAY(10);
+ }
+ if (timeout <= 0)
+ device_printf(sc->dev, "config load timeouted\n");
+ reg = RD4(sc, I2C_BUS_CLEAR_CONFIG);
+ reg |= I2C_BUS_CLEAR_CONFIG_BC_ENABLE;
+ WR4(sc, I2C_BUS_CLEAR_CONFIG,reg);
+
+ for (timeout = 1000; timeout > 0; timeout--) {
+ if ((RD4(sc, I2C_BUS_CLEAR_CONFIG) &
+ I2C_BUS_CLEAR_CONFIG_BC_ENABLE) == 0)
+ break;
+ DELAY(10);
+ }
+ if (timeout <= 0)
+ device_printf(sc->dev, "bus clear timeouted\n");
+
+ status = RD4(sc, I2C_BUS_CLEAR_STATUS);
+ if ((status & I2C_BUS_CLEAR_STATUS_BC_STATUS) == 0)
+ device_printf(sc->dev, "bus clear failed\n");
+}
+
+static int
+tegra_i2c_hw_init(struct tegra_i2c_softc *sc)
+{
+ int rv, timeout;
+
+ /* Reset the core. */
+ rv = hwreset_assert(sc->reset);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot assert reset\n");
+ return (rv);
+ }
+ DELAY(10);
+ rv = hwreset_deassert(sc->reset);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot clear reset\n");
+ return (rv);
+ }
+
+ WR4(sc, I2C_INTERRUPT_MASK_REGISTER, 0);
+ WR4(sc, I2C_INTERRUPT_STATUS_REGISTER, 0xFFFFFFFF);
+ WR4(sc, I2C_CNFG, I2C_CNFG_NEW_MASTER_FSM | I2C_CNFG_PACKET_MODE_EN |
+ I2C_CNFG_DEBOUNCE_CNT(2));
+
+ tegra_i2c_setup_clk(sc, sc->bus_freq);
+
+ WR4(sc, I2C_FIFO_CONTROL, I2C_FIFO_CONTROL_TX_FIFO_TRIG(7) |
+ I2C_FIFO_CONTROL_RX_FIFO_TRIG(0));
+
+ WR4(sc, I2C_CONFIG_LOAD, I2C_CONFIG_LOAD_MSTR_CONFIG_LOAD);
+ for (timeout = 1000; timeout > 0; timeout--) {
+ if (RD4(sc, I2C_CONFIG_LOAD) == 0)
+ break;
+ DELAY(10);
+ }
+ if (timeout <= 0)
+ device_printf(sc->dev, "config load timeouted\n");
+
+ tegra_i2c_bus_clear(sc);
+ return (0);
+}
+
+static int
+tegra_i2c_tx(struct tegra_i2c_softc *sc)
+{
+ uint32_t reg;
+ int cnt, i;
+
+ if (sc->msg_idx >= sc->msg->len)
+ panic("Invalid call to tegra_i2c_tx\n");
+
+ while(sc->msg_idx < sc->msg->len) {
+ reg = RD4(sc, I2C_FIFO_STATUS);
+ if (I2C_FIFO_STATUS_TX_FIFO_EMPTY_CNT(reg) == 0)
+ break;
+ cnt = min(4, sc->msg->len - sc->msg_idx);
+ reg = 0;
+ for (i = 0; i < cnt; i++) {
+ reg |= sc->msg->buf[sc->msg_idx] << (i * 8);
+ sc->msg_idx++;
+ }
+ WR4(sc, I2C_TX_PACKET_FIFO, reg);
+ }
+ if (sc->msg_idx >= sc->msg->len)
+ return (0);
+ return (sc->msg->len - sc->msg_idx - 1);
+}
+
+static int
+tegra_i2c_rx(struct tegra_i2c_softc *sc)
+{
+ uint32_t reg;
+ int cnt, i;
+
+ if (sc->msg_idx >= sc->msg->len)
+ panic("Invalid call to tegra_i2c_rx\n");
+
+ while(sc->msg_idx < sc->msg->len) {
+ reg = RD4(sc, I2C_FIFO_STATUS);
+ if (I2C_FIFO_STATUS_RX_FIFO_FULL_CNT(reg) == 0)
+ break;
+ cnt = min(4, sc->msg->len - sc->msg_idx);
+ reg = RD4(sc, I2C_RX_FIFO);
+ for (i = 0; i < cnt; i++) {
+ sc->msg->buf[sc->msg_idx] = (reg >> (i * 8)) & 0xFF;
+ sc->msg_idx++;
+ }
+ }
+
+ if (sc->msg_idx >= sc->msg->len)
+ return (0);
+ return (sc->msg->len - sc->msg_idx - 1);
+}
+
+static void
+tegra_i2c_intr(void *arg)
+{
+ struct tegra_i2c_softc *sc;
+ uint32_t status, reg;
+ int rv;
+
+ sc = (struct tegra_i2c_softc *)arg;
+
+ LOCK(sc);
+ status = RD4(sc, I2C_INTERRUPT_SOURCE_REGISTER);
+ if (sc->msg == NULL) {
+ /* Unexpected interrupt - disable FIFOs, clear reset. */
+ reg = RD4(sc, I2C_INTERRUPT_MASK_REGISTER);
+ reg &= ~I2C_INT_TFIFO_DATA_REQ;
+ reg &= ~I2C_INT_RFIFO_DATA_REQ;
+ WR4(sc, I2C_INTERRUPT_MASK_REGISTER, 0);
+ WR4(sc, I2C_INTERRUPT_STATUS_REGISTER, status);
+ UNLOCK(sc);
+ return;
+ }
+
+ if ((status & I2C_ERROR_MASK) != 0) {
+ if (status & I2C_INT_NOACK)
+ sc->bus_err = IIC_ENOACK;
+ if (status & I2C_INT_ARB_LOST)
+ sc->bus_err = IIC_EBUSERR;
+ if ((status & I2C_INT_TFIFO_OVR) ||
+ (status & I2C_INT_RFIFO_UNF))
+ sc->bus_err = IIC_EBUSERR;
+ sc->done = 1;
+ } else if ((status & I2C_INT_RFIFO_DATA_REQ) &&
+ (sc->msg != NULL) && (sc->msg->flags & IIC_M_RD)) {
+ rv = tegra_i2c_rx(sc);
+ if (rv == 0) {
+ reg = RD4(sc, I2C_INTERRUPT_MASK_REGISTER);
+ reg &= ~I2C_INT_RFIFO_DATA_REQ;
+ WR4(sc, I2C_INTERRUPT_MASK_REGISTER, reg);
+ }
+ } else if ((status & I2C_INT_TFIFO_DATA_REQ) &&
+ (sc->msg != NULL) && !(sc->msg->flags & IIC_M_RD)) {
+ rv = tegra_i2c_tx(sc);
+ if (rv == 0) {
+ reg = RD4(sc, I2C_INTERRUPT_MASK_REGISTER);
+ reg &= ~I2C_INT_TFIFO_DATA_REQ;
+ WR4(sc, I2C_INTERRUPT_MASK_REGISTER, reg);
+ }
+ } else if ((status & I2C_INT_RFIFO_DATA_REQ) ||
+ (status & I2C_INT_TFIFO_DATA_REQ)) {
+ device_printf(sc->dev, "Unexpected data interrupt: 0x%08X\n",
+ status);
+ reg = RD4(sc, I2C_INTERRUPT_MASK_REGISTER);
+ reg &= ~I2C_INT_TFIFO_DATA_REQ;
+ reg &= ~I2C_INT_RFIFO_DATA_REQ;
+ WR4(sc, I2C_INTERRUPT_MASK_REGISTER, reg);
+ }
+ if (status & I2C_INT_PACKET_XFER_COMPLETE)
+ sc->done = 1;
+ WR4(sc, I2C_INTERRUPT_STATUS_REGISTER, status);
+ if (sc->done) {
+ WR4(sc, I2C_INTERRUPT_MASK_REGISTER, 0);
+ wakeup(&(sc->done));
+ }
+ UNLOCK(sc);
+}
+
+static void
+tegra_i2c_start_msg(struct tegra_i2c_softc *sc, struct iic_msg *msg,
+ enum tegra_i2c_xfer_type xtype)
+{
+ uint32_t tmp, mask;
+
+ /* Packet header. */
+ tmp = (0 << PACKET_HEADER0_HEADER_SIZE_SHIFT) |
+ PACKET_HEADER0_PROTOCOL_I2C |
+ (1 << PACKET_HEADER0_CONT_ID_SHIFT) |
+ (1 << PACKET_HEADER0_PACKET_ID_SHIFT);
+ WR4(sc, I2C_TX_PACKET_FIFO, tmp);
+
+ /* Packet size. */
+ WR4(sc, I2C_TX_PACKET_FIFO, msg->len - 1);
+
+ /* I2C header. */
+ tmp = I2C_HEADER_IE_ENABLE;
+ if (xtype == XFER_CONTINUE)
+ tmp |= I2C_HEADER_CONTINUE_XFER;
+ else if (xtype == XFER_REPEAT_START)
+ tmp |= I2C_HEADER_REPEAT_START;
+ tmp |= msg->slave << I2C_HEADER_SLAVE_ADDR_SHIFT;
+ if (msg->flags & IIC_M_RD) {
+ tmp |= I2C_HEADER_READ;
+ tmp |= 1 << I2C_HEADER_SLAVE_ADDR_SHIFT;
+ } else
+ tmp &= ~(1 << I2C_HEADER_SLAVE_ADDR_SHIFT);
+
+ WR4(sc, I2C_TX_PACKET_FIFO, tmp);
+
+ /* Interrupt mask. */
+ mask = I2C_INT_NOACK | I2C_INT_ARB_LOST | I2C_INT_PACKET_XFER_COMPLETE;
+ if (msg->flags & IIC_M_RD)
+ mask |= I2C_INT_RFIFO_DATA_REQ;
+ else
+ mask |= I2C_INT_TFIFO_DATA_REQ;
+ WR4(sc, I2C_INTERRUPT_MASK_REGISTER, mask);
+}
+
+static int
+tegra_i2c_poll(struct tegra_i2c_softc *sc)
+{
+ int timeout;
+
+ for(timeout = 10000; timeout > 0; timeout--) {
+ UNLOCK(sc);
+ tegra_i2c_intr(sc);
+ LOCK(sc);
+ if (sc->done != 0)
+ break;
+ DELAY(1);
+ }
+ if (timeout <= 0)
+ return (ETIMEDOUT);
+ return (0);
+}
+
+static int
+tegra_i2c_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
+{
+ int rv, i;
+ struct tegra_i2c_softc *sc;
+ enum tegra_i2c_xfer_type xtype;
+
+ sc = device_get_softc(dev);
+ LOCK(sc);
+
+ /* Get the bus. */
+ while (sc->bus_inuse == 1)
+ SLEEP(sc, 0);
+ sc->bus_inuse = 1;
+
+ rv = 0;
+ for (i = 0; i < nmsgs; i++) {
+ sc->msg = &msgs[i];
+ sc->msg_idx = 0;
+ sc->bus_err = 0;
+ sc->done = 0;
+ /* Check for valid parameters. */
+ if (sc->msg == NULL || sc->msg->buf == NULL ||
+ sc->msg->len == 0) {
+ rv = EINVAL;
+ break;
+ }
+
+ /* Get flags for next transfer. */
+ if (i == (nmsgs - 1)) {
+ if (msgs[i].flags & IIC_M_NOSTOP)
+ xtype = XFER_CONTINUE;
+ else
+ xtype = XFER_STOP;
+ } else {
+ if (msgs[i + 1].flags & IIC_M_NOSTART)
+ xtype = XFER_CONTINUE;
+ else
+ xtype = XFER_REPEAT_START;
+ }
+ tegra_i2c_start_msg(sc, sc->msg, xtype);
+ if (cold)
+ rv = tegra_i2c_poll(sc);
+ else
+ rv = msleep(&sc->done, &sc->mtx, PZERO, "iic",
+ I2C_REQUEST_TIMEOUT);
+
+ WR4(sc, I2C_INTERRUPT_MASK_REGISTER, 0);
+ WR4(sc, I2C_INTERRUPT_STATUS_REGISTER, 0xFFFFFFFF);
+ if (rv == 0)
+ rv = sc->bus_err;
+ if (rv != 0)
+ break;
+ }
+
+ if (rv != 0) {
+ tegra_i2c_hw_init(sc);
+ tegra_i2c_flush_fifo(sc);
+ }
+
+ sc->msg = NULL;
+ sc->msg_idx = 0;
+ sc->bus_err = 0;
+ sc->done = 0;
+
+ /* Wake up the processes that are waiting for the bus. */
+ sc->bus_inuse = 0;
+ wakeup(sc);
+ UNLOCK(sc);
+
+ return (rv);
+}
+
+static int
+tegra_i2c_iicbus_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
+{
+ struct tegra_i2c_softc *sc;
+ int busfreq;
+
+ sc = device_get_softc(dev);
+ busfreq = IICBUS_GET_FREQUENCY(sc->iicbus, speed);
+ sc = device_get_softc(dev);
+ LOCK(sc);
+ tegra_i2c_setup_clk(sc, busfreq);
+ UNLOCK(sc);
+ return (0);
+}
+
+static int
+tegra_i2c_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+tegra_i2c_attach(device_t dev)
+{
+ int rv, rid;
+ phandle_t node;
+ struct tegra_i2c_softc *sc;
+ uint64_t freq;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ node = ofw_bus_get_node(dev);
+
+ LOCK_INIT(sc);
+
+ /* Get the memory resource for the register mapping. */
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "Cannot map registers.\n");
+ rv = ENXIO;
+ goto fail;
+ }
+
+ /* Allocate our IRQ resource. */
+ rid = 0;
+ sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (sc->irq_res == NULL) {
+ device_printf(dev, "Cannot allocate interrupt.\n");
+ rv = ENXIO;
+ goto fail;
+ }
+
+ /* FDT resources. */
+ rv = clk_get_by_ofw_name(dev, 0, "div-clk", &sc->clk);
+ if (rv != 0) {
+ device_printf(dev, "Cannot get i2c clock: %d\n", rv);
+ goto fail;
+ }
+ rv = hwreset_get_by_ofw_name(sc->dev, 0, "i2c", &sc->reset);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get i2c reset\n");
+ return (ENXIO);
+ }
+ rv = OF_getencprop(node, "clock-frequency", &sc->bus_freq,
+ sizeof(sc->bus_freq));
+ if (rv != sizeof(sc->bus_freq)) {
+ sc->bus_freq = 100000;
+ }
+
+ /* Request maximum frequency for I2C block 136MHz (408MHz / 3). */
+ rv = clk_set_freq(sc->clk, 136000000, CLK_SET_ROUND_DOWN);
+ if (rv != 0) {
+ device_printf(dev, "Cannot set clock frequency\n");
+ goto fail;
+ }
+ rv = clk_get_freq(sc->clk, &freq);
+ if (rv != 0) {
+ device_printf(dev, "Cannot get clock frequency\n");
+ goto fail;
+ }
+ sc->core_freq = (uint32_t)freq;
+
+ rv = clk_enable(sc->clk);
+ if (rv != 0) {
+ device_printf(dev, "Cannot enable clock: %d\n", rv);
+ goto fail;
+ }
+
+ /* Init hardware. */
+ rv = tegra_i2c_hw_init(sc);
+ if (rv) {
+ device_printf(dev, "tegra_i2c_activate failed\n");
+ goto fail;
+ }
+
+ /* Setup interrupt. */
+ rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+ NULL, tegra_i2c_intr, sc, &sc->irq_h);
+ if (rv) {
+ device_printf(dev, "Cannot setup interrupt.\n");
+ goto fail;
+ }
+
+ /* Attach the iicbus. */
+ sc->iicbus = device_add_child(dev, "iicbus", -1);
+ if (sc->iicbus == NULL) {
+ device_printf(dev, "Could not allocate iicbus instance.\n");
+ rv = ENXIO;
+ goto fail;
+ }
+
+ /* Probe and attach the iicbus. */
+ return (bus_generic_attach(dev));
+
+fail:
+ if (sc->irq_h != NULL)
+ bus_teardown_intr(dev, sc->irq_res, sc->irq_h);
+ if (sc->irq_res != NULL)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+ if (sc->mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+ LOCK_DESTROY(sc);
+
+ return (rv);
+}
+
+static int
+tegra_i2c_detach(device_t dev)
+{
+ struct tegra_i2c_softc *sc;
+ int rv;
+
+ sc = device_get_softc(dev);
+ tegra_i2c_hw_init(sc);
+ if (sc->irq_h != NULL)
+ bus_teardown_intr(dev, sc->irq_res, sc->irq_h);
+ if (sc->irq_res != NULL)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+ if (sc->mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+
+ LOCK_DESTROY(sc);
+ if (sc->iicbus)
+ rv = device_delete_child(dev, sc->iicbus);
+ return (bus_generic_detach(dev));
+}
+
+static phandle_t
+tegra_i2c_get_node(device_t bus, device_t dev)
+{
+
+ /* Share controller node with iibus device. */
+ return (ofw_bus_get_node(bus));
+}
+
+static device_method_t tegra_i2c_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, tegra_i2c_probe),
+ DEVMETHOD(device_attach, tegra_i2c_attach),
+ DEVMETHOD(device_detach, tegra_i2c_detach),
+
+ /* Bus interface */
+ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
+ DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
+ DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource),
+ DEVMETHOD(bus_release_resource, bus_generic_release_resource),
+ DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
+ DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource),
+ DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource),
+ DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource),
+
+ /* OFW methods */
+ DEVMETHOD(ofw_bus_get_node, tegra_i2c_get_node),
+
+ /* iicbus interface */
+ DEVMETHOD(iicbus_callback, iicbus_null_callback),
+ DEVMETHOD(iicbus_reset, tegra_i2c_iicbus_reset),
+ DEVMETHOD(iicbus_transfer, tegra_i2c_transfer),
+
+ DEVMETHOD_END
+};
+
+static devclass_t tegra_i2c_devclass;
+static DEFINE_CLASS_0(iichb, tegra_i2c_driver, tegra_i2c_methods,
+ sizeof(struct tegra_i2c_softc));
+EARLY_DRIVER_MODULE(tegra_iic, simplebus, tegra_i2c_driver, tegra_i2c_devclass,
+ NULL, NULL, 73);
diff --git a/sys/arm/nvidia/tegra_lic.c b/sys/arm/nvidia/tegra_lic.c
new file mode 100644
index 000000000000..d457bd4b0f73
--- /dev/null
+++ b/sys/arm/nvidia/tegra_lic.c
@@ -0,0 +1,288 @@
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Local interrupt controller driver for Tegra SoCs.
+ */
+#include <sys/param.h>
+#include <sys/module.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+#include <machine/resource.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "pic_if.h"
+
+#define LIC_VIRQ_CPU 0x00
+#define LIC_VIRQ_COP 0x04
+#define LIC_VFRQ_CPU 0x08
+#define LIC_VFRQ_COP 0x0c
+#define LIC_ISR 0x10
+#define LIC_FIR 0x14
+#define LIC_FIR_SET 0x18
+#define LIC_FIR_CLR 0x1c
+#define LIC_CPU_IER 0x20
+#define LIC_CPU_IER_SET 0x24
+#define LIC_CPU_IER_CLR 0x28
+#define LIC_CPU_IEP_CLASS 0x2C
+#define LIC_COP_IER 0x30
+#define LIC_COP_IER_SET 0x34
+#define LIC_COP_IER_CLR 0x38
+#define LIC_COP_IEP_CLASS 0x3c
+
+#define WR4(_sc, _b, _r, _v) bus_write_4((_sc)->mem_res[_b], (_r), (_v))
+#define RD4(_sc, _b, _r) bus_read_4((_sc)->mem_res[_b], (_r))
+
+static struct resource_spec lic_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_MEMORY, 1, RF_ACTIVE },
+ { SYS_RES_MEMORY, 2, RF_ACTIVE },
+ { SYS_RES_MEMORY, 3, RF_ACTIVE },
+ { SYS_RES_MEMORY, 4, RF_ACTIVE },
+ { -1, 0 }
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"nvidia,tegra124-ictlr", 1},
+ {"nvidia,tegra210-ictlr", 1},
+ {NULL, 0}
+};
+
+struct tegra_lic_sc {
+ device_t dev;
+ struct resource *mem_res[nitems(lic_spec)];
+ device_t parent;
+};
+
+static int
+tegra_lic_activate_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ struct tegra_lic_sc *sc = device_get_softc(dev);
+
+ return (PIC_ACTIVATE_INTR(sc->parent, isrc, res, data));
+}
+
+static void
+tegra_lic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct tegra_lic_sc *sc = device_get_softc(dev);
+
+ PIC_DISABLE_INTR(sc->parent, isrc);
+}
+
+static void
+tegra_lic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct tegra_lic_sc *sc = device_get_softc(dev);
+
+ PIC_ENABLE_INTR(sc->parent, isrc);
+}
+
+static int
+tegra_lic_map_intr(device_t dev, struct intr_map_data *data,
+ struct intr_irqsrc **isrcp)
+{
+ struct tegra_lic_sc *sc = device_get_softc(dev);
+
+ return (PIC_MAP_INTR(sc->parent, data, isrcp));
+}
+
+static int
+tegra_lic_deactivate_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ struct tegra_lic_sc *sc = device_get_softc(dev);
+
+ return (PIC_DEACTIVATE_INTR(sc->parent, isrc, res, data));
+}
+
+static int
+tegra_lic_setup_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ struct tegra_lic_sc *sc = device_get_softc(dev);
+
+ return (PIC_SETUP_INTR(sc->parent, isrc, res, data));
+}
+
+static int
+tegra_lic_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ struct tegra_lic_sc *sc = device_get_softc(dev);
+
+ return (PIC_TEARDOWN_INTR(sc->parent, isrc, res, data));
+}
+
+static void
+tegra_lic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct tegra_lic_sc *sc = device_get_softc(dev);
+
+ PIC_PRE_ITHREAD(sc->parent, isrc);
+}
+
+static void
+tegra_lic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct tegra_lic_sc *sc = device_get_softc(dev);
+
+ PIC_POST_ITHREAD(sc->parent, isrc);
+}
+
+static void
+tegra_lic_post_filter(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct tegra_lic_sc *sc = device_get_softc(dev);
+
+ PIC_POST_FILTER(sc->parent, isrc);
+}
+
+#ifdef SMP
+static int
+tegra_lic_bind_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct tegra_lic_sc *sc = device_get_softc(dev);
+
+ return (PIC_BIND_INTR(sc->parent, isrc));
+}
+#endif
+
+static int
+tegra_lic_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+tegra_lic_attach(device_t dev)
+{
+ struct tegra_lic_sc *sc;
+ phandle_t node;
+ phandle_t parent_xref;
+ int i, rv;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ node = ofw_bus_get_node(dev);
+
+ rv = OF_getencprop(node, "interrupt-parent", &parent_xref,
+ sizeof(parent_xref));
+ if (rv <= 0) {
+ device_printf(dev, "Cannot read parent node property\n");
+ goto fail;
+ }
+ sc->parent = OF_device_from_xref(parent_xref);
+ if (sc->parent == NULL) {
+ device_printf(dev, "Cannott find parent controller\n");
+ goto fail;
+ }
+
+ if (bus_alloc_resources(dev, lic_spec, sc->mem_res)) {
+ device_printf(dev, "Cannott allocate resources\n");
+ goto fail;
+ }
+
+ /* Disable all interrupts, route all to irq */
+ for (i = 0; i < nitems(lic_spec); i++) {
+ if (sc->mem_res[i] == NULL)
+ continue;
+ WR4(sc, i, LIC_CPU_IER_CLR, 0xFFFFFFFF);
+ WR4(sc, i, LIC_CPU_IEP_CLASS, 0);
+ }
+
+ if (intr_pic_register(dev, OF_xref_from_node(node)) == NULL) {
+ device_printf(dev, "Cannot register PIC\n");
+ goto fail;
+ }
+ return (0);
+
+fail:
+ bus_release_resources(dev, lic_spec, sc->mem_res);
+ return (ENXIO);
+}
+
+static int
+tegra_lic_detach(device_t dev)
+{
+ struct tegra_lic_sc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+ for (i = 0; i < nitems(lic_spec); i++) {
+ if (sc->mem_res[i] == NULL)
+ continue;
+ bus_release_resource(dev, SYS_RES_MEMORY, i,
+ sc->mem_res[i]);
+ }
+ return (0);
+}
+
+static device_method_t tegra_lic_methods[] = {
+ DEVMETHOD(device_probe, tegra_lic_probe),
+ DEVMETHOD(device_attach, tegra_lic_attach),
+ DEVMETHOD(device_detach, tegra_lic_detach),
+
+ /* Interrupt controller interface */
+ DEVMETHOD(pic_activate_intr, tegra_lic_activate_intr),
+ DEVMETHOD(pic_disable_intr, tegra_lic_disable_intr),
+ DEVMETHOD(pic_enable_intr, tegra_lic_enable_intr),
+ DEVMETHOD(pic_map_intr, tegra_lic_map_intr),
+ DEVMETHOD(pic_deactivate_intr, tegra_lic_deactivate_intr),
+ DEVMETHOD(pic_setup_intr, tegra_lic_setup_intr),
+ DEVMETHOD(pic_teardown_intr, tegra_lic_teardown_intr),
+ DEVMETHOD(pic_pre_ithread, tegra_lic_pre_ithread),
+ DEVMETHOD(pic_post_ithread, tegra_lic_post_ithread),
+ DEVMETHOD(pic_post_filter, tegra_lic_post_filter),
+#ifdef SMP
+ DEVMETHOD(pic_bind_intr, tegra_lic_bind_intr),
+#endif
+ DEVMETHOD_END
+};
+
+devclass_t tegra_lic_devclass;
+static DEFINE_CLASS_0(lic, tegra_lic_driver, tegra_lic_methods,
+ sizeof(struct tegra_lic_sc));
+EARLY_DRIVER_MODULE(tegra_lic, simplebus, tegra_lic_driver, tegra_lic_devclass,
+ NULL, NULL, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE + 1);
diff --git a/sys/arm/nvidia/tegra_mc.c b/sys/arm/nvidia/tegra_mc.c
new file mode 100644
index 000000000000..7ff480b55dbd
--- /dev/null
+++ b/sys/arm/nvidia/tegra_mc.c
@@ -0,0 +1,311 @@
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Memory controller driver for Tegra SoCs.
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/module.h>
+#include <sys/resource.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "clock_if.h"
+
+#define MC_INTSTATUS 0x000
+#define MC_INTMASK 0x004
+#define MC_INT_DECERR_MTS (1 << 16)
+#define MC_INT_SECERR_SEC (1 << 13)
+#define MC_INT_DECERR_VPR (1 << 12)
+#define MC_INT_INVALID_APB_ASID_UPDATE (1 << 11)
+#define MC_INT_INVALID_SMMU_PAGE (1 << 10)
+#define MC_INT_ARBITRATION_EMEM (1 << 9)
+#define MC_INT_SECURITY_VIOLATION (1 << 8)
+#define MC_INT_DECERR_EMEM (1 << 6)
+#define MC_INT_INT_MASK (MC_INT_DECERR_MTS | \
+ MC_INT_SECERR_SEC | \
+ MC_INT_DECERR_VPR | \
+ MC_INT_INVALID_APB_ASID_UPDATE | \
+ MC_INT_INVALID_SMMU_PAGE | \
+ MC_INT_ARBITRATION_EMEM | \
+ MC_INT_SECURITY_VIOLATION | \
+ MC_INT_DECERR_EMEM)
+
+#define MC_ERR_STATUS 0x008
+#define MC_ERR_TYPE(x) (((x) >> 28) & 0x7)
+#define MC_ERR_TYPE_DECERR_EMEM 2
+#define MC_ERR_TYPE_SECURITY_TRUSTZONE 3
+#define MC_ERR_TYPE_SECURITY_CARVEOUT 4
+#define MC_ERR_TYPE_INVALID_SMMU_PAGE 6
+#define MC_ERR_INVALID_SMMU_PAGE_READABLE (1 << 27)
+#define MC_ERR_INVALID_SMMU_PAGE_WRITABLE (1 << 26)
+#define MC_ERR_INVALID_SMMU_PAGE_NONSECURE (1 << 25)
+#define MC_ERR_ADR_HI(x) (((x) >> 20) & 0x3)
+#define MC_ERR_SWAP (1 << 18)
+#define MC_ERR_SECURITY (1 << 17)
+#define MC_ERR_RW (1 << 16)
+#define MC_ERR_ADR1(x) (((x) >> 12) & 0x7)
+#define MC_ERR_ID(x) (((x) >> 0) & 07F)
+
+#define MC_ERR_ADDR 0x00C
+#define MC_EMEM_CFG 0x050
+#define MC_EMEM_ADR_CFG 0x054
+#define MC_EMEM_NUMDEV(x) (((x) >> 0 ) & 0x1)
+
+#define MC_EMEM_ADR_CFG_DEV0 0x058
+#define MC_EMEM_ADR_CFG_DEV1 0x05C
+#define EMEM_DEV_DEVSIZE(x) (((x) >> 16) & 0xF)
+#define EMEM_DEV_BANKWIDTH(x) (((x) >> 8) & 0x3)
+#define EMEM_DEV_COLWIDTH(x) (((x) >> 8) & 0x3)
+
+#define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v))
+#define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r))
+
+#define LOCK(_sc) mtx_lock(&(_sc)->mtx)
+#define UNLOCK(_sc) mtx_unlock(&(_sc)->mtx)
+#define SLEEP(_sc, timeout) mtx_sleep(sc, &sc->mtx, 0, "tegra_mc", timeout);
+#define LOCK_INIT(_sc) \
+ mtx_init(&_sc->mtx, device_get_nameunit(_sc->dev), "tegra_mc", MTX_DEF)
+#define LOCK_DESTROY(_sc) mtx_destroy(&_sc->mtx)
+#define ASSERT_LOCKED(_sc) mtx_assert(&_sc->mtx, MA_OWNED)
+#define ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED)
+
+static struct ofw_compat_data compat_data[] = {
+ {"nvidia,tegra124-mc", 1},
+ {"nvidia,tegra210-mc", 1},
+ {NULL, 0}
+};
+
+struct tegra_mc_softc {
+ device_t dev;
+ struct mtx mtx;
+
+ struct resource *mem_res;
+ struct resource *irq_res;
+ void *irq_h;
+
+ clk_t clk;
+};
+
+static char *smmu_err_tbl[16] = {
+ "reserved", /* 0 */
+ "reserved", /* 1 */
+ "DRAM decode", /* 2 */
+ "Trustzome Security", /* 3 */
+ "Security carveout", /* 4 */
+ "reserved", /* 5 */
+ "Invalid SMMU page", /* 6 */
+ "reserved", /* 7 */
+};
+
+static void
+tegra_mc_intr(void *arg)
+{
+ struct tegra_mc_softc *sc;
+ uint32_t stat, err;
+ uint64_t addr;
+
+ sc = (struct tegra_mc_softc *)arg;
+
+ stat = RD4(sc, MC_INTSTATUS);
+ if ((stat & MC_INT_INT_MASK) == 0) {
+ WR4(sc, MC_INTSTATUS, stat);
+ return;
+ }
+
+ device_printf(sc->dev, "Memory Controller Interrupt:\n");
+ if (stat & MC_INT_DECERR_MTS)
+ printf(" - MTS carveout violation\n");
+ if (stat & MC_INT_SECERR_SEC)
+ printf(" - SEC carveout violation\n");
+ if (stat & MC_INT_DECERR_VPR)
+ printf(" - VPR requirements violated\n");
+ if (stat & MC_INT_INVALID_APB_ASID_UPDATE)
+ printf(" - ivalid APB ASID update\n");
+ if (stat & MC_INT_INVALID_SMMU_PAGE)
+ printf(" - SMMU address translation error\n");
+ if (stat & MC_INT_ARBITRATION_EMEM)
+ printf(" - arbitration deadlock-prevention threshold hit\n");
+ if (stat & MC_INT_SECURITY_VIOLATION)
+ printf(" - SMMU address translation security error\n");
+ if (stat & MC_INT_DECERR_EMEM)
+ printf(" - SMMU address decode error\n");
+
+ if ((stat & (MC_INT_INVALID_SMMU_PAGE | MC_INT_SECURITY_VIOLATION |
+ MC_INT_DECERR_EMEM)) != 0) {
+ err = RD4(sc, MC_ERR_STATUS);
+ addr = RD4(sc, MC_ERR_STATUS);
+ addr |= (uint64_t)(MC_ERR_ADR_HI(err)) << 32;
+ printf(" at 0x%012jX [%s %s %s] - %s error.\n",
+ (uintmax_t)addr,
+ stat & MC_ERR_SWAP ? "Swap, " : "",
+ stat & MC_ERR_SECURITY ? "Sec, " : "",
+ stat & MC_ERR_RW ? "Write" : "Read",
+ smmu_err_tbl[MC_ERR_TYPE(err)]);
+ }
+ WR4(sc, MC_INTSTATUS, stat);
+}
+
+static void
+tegra_mc_init_hw(struct tegra_mc_softc *sc)
+{
+
+ /* Disable and acknowledge all interrupts */
+ WR4(sc, MC_INTMASK, 0);
+ WR4(sc, MC_INTSTATUS, MC_INT_INT_MASK);
+}
+
+static int
+tegra_mc_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+ device_set_desc(dev, "Tegra Memory Controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+tegra_mc_attach(device_t dev)
+{
+ int rv, rid;
+ struct tegra_mc_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ LOCK_INIT(sc);
+
+ /* Get the memory resource for the register mapping. */
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "Cannot map registers.\n");
+ rv = ENXIO;
+ goto fail;
+ }
+
+ /* Allocate our IRQ resource. */
+ rid = 0;
+ sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (sc->irq_res == NULL) {
+ device_printf(dev, "Cannot allocate interrupt.\n");
+ rv = ENXIO;
+ goto fail;
+ }
+
+ /* OFW resources. */
+ rv = clk_get_by_ofw_name(dev, 0, "mc", &sc->clk);
+ if (rv != 0) {
+ device_printf(dev, "Cannot get mc clock: %d\n", rv);
+ goto fail;
+ }
+ rv = clk_enable(sc->clk);
+ if (rv != 0) {
+ device_printf(dev, "Cannot enable clock: %d\n", rv);
+ goto fail;
+ }
+
+ /* Init hardware. */
+ tegra_mc_init_hw(sc);
+
+ /* Setup interrupt */
+ rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+ NULL, tegra_mc_intr, sc, &sc->irq_h);
+ if (rv) {
+ device_printf(dev, "Cannot setup interrupt.\n");
+ goto fail;
+ }
+
+ /* Enable Interrupts */
+ WR4(sc, MC_INTMASK, MC_INT_INT_MASK);
+
+ return (bus_generic_attach(dev));
+
+fail:
+ if (sc->clk != NULL)
+ clk_release(sc->clk);
+ if (sc->irq_h != NULL)
+ bus_teardown_intr(dev, sc->irq_res, sc->irq_h);
+ if (sc->irq_res != NULL)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+ if (sc->mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+ LOCK_DESTROY(sc);
+
+ return (rv);
+}
+
+static int
+tegra_mc_detach(device_t dev)
+{
+ struct tegra_mc_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (sc->irq_h != NULL)
+ bus_teardown_intr(dev, sc->irq_res, sc->irq_h);
+ if (sc->irq_res != NULL)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+ if (sc->mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+
+ LOCK_DESTROY(sc);
+ return (bus_generic_detach(dev));
+}
+
+static device_method_t tegra_mc_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, tegra_mc_probe),
+ DEVMETHOD(device_attach, tegra_mc_attach),
+ DEVMETHOD(device_detach, tegra_mc_detach),
+
+ DEVMETHOD_END
+};
+
+static devclass_t tegra_mc_devclass;
+static DEFINE_CLASS_0(mc, tegra_mc_driver, tegra_mc_methods,
+ sizeof(struct tegra_mc_softc));
+DRIVER_MODULE(tegra_mc, simplebus, tegra_mc_driver, tegra_mc_devclass,
+ NULL, NULL);
diff --git a/sys/arm/nvidia/tegra_pcie.c b/sys/arm/nvidia/tegra_pcie.c
new file mode 100644
index 000000000000..44eb682a40b4
--- /dev/null
+++ b/sys/arm/nvidia/tegra_pcie.c
@@ -0,0 +1,1630 @@
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Nvidia Integrated PCI/PCI-Express controller driver.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/devmap.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+
+#include <machine/intr.h>
+
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_kern.h>
+#include <vm/pmap.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/extres/phy/phy.h>
+#include <dev/extres/regulator/regulator.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/ofw_pci.h>
+#include <dev/ofw/ofwpci.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcib_private.h>
+
+#include <machine/resource.h>
+#include <machine/bus.h>
+
+#include <arm/nvidia/tegra_pmc.h>
+
+#include "ofw_bus_if.h"
+#include "msi_if.h"
+#include "pcib_if.h"
+#include "pic_if.h"
+
+#define AFI_AXI_BAR0_SZ 0x000
+#define AFI_AXI_BAR1_SZ 0x004
+#define AFI_AXI_BAR2_SZ 0x008
+#define AFI_AXI_BAR3_SZ 0x00c
+#define AFI_AXI_BAR4_SZ 0x010
+#define AFI_AXI_BAR5_SZ 0x014
+#define AFI_AXI_BAR0_START 0x018
+#define AFI_AXI_BAR1_START 0x01c
+#define AFI_AXI_BAR2_START 0x020
+#define AFI_AXI_BAR3_START 0x024
+#define AFI_AXI_BAR4_START 0x028
+#define AFI_AXI_BAR5_START 0x02c
+#define AFI_FPCI_BAR0 0x030
+#define AFI_FPCI_BAR1 0x034
+#define AFI_FPCI_BAR2 0x038
+#define AFI_FPCI_BAR3 0x03c
+#define AFI_FPCI_BAR4 0x040
+#define AFI_FPCI_BAR5 0x044
+#define AFI_MSI_BAR_SZ 0x060
+#define AFI_MSI_FPCI_BAR_ST 0x064
+#define AFI_MSI_AXI_BAR_ST 0x068
+#define AFI_MSI_VEC(x) (0x06c + 4 * (x))
+#define AFI_MSI_EN_VEC(x) (0x08c + 4 * (x))
+#define AFI_MSI_INTR_IN_REG 32
+#define AFI_MSI_REGS 8
+
+#define AFI_CONFIGURATION 0x0ac
+#define AFI_CONFIGURATION_EN_FPCI (1 << 0)
+
+#define AFI_FPCI_ERROR_MASKS 0x0b0
+#define AFI_INTR_MASK 0x0b4
+#define AFI_INTR_MASK_MSI_MASK (1 << 8)
+#define AFI_INTR_MASK_INT_MASK (1 << 0)
+
+#define AFI_INTR_CODE 0x0b8
+#define AFI_INTR_CODE_MASK 0xf
+#define AFI_INTR_CODE_INT_CODE_INI_SLVERR 1
+#define AFI_INTR_CODE_INT_CODE_INI_DECERR 2
+#define AFI_INTR_CODE_INT_CODE_TGT_SLVERR 3
+#define AFI_INTR_CODE_INT_CODE_TGT_DECERR 4
+#define AFI_INTR_CODE_INT_CODE_TGT_WRERR 5
+#define AFI_INTR_CODE_INT_CODE_SM_MSG 6
+#define AFI_INTR_CODE_INT_CODE_DFPCI_DECERR 7
+#define AFI_INTR_CODE_INT_CODE_AXI_DECERR 8
+#define AFI_INTR_CODE_INT_CODE_FPCI_TIMEOUT 9
+#define AFI_INTR_CODE_INT_CODE_PE_PRSNT_SENSE 10
+#define AFI_INTR_CODE_INT_CODE_PE_CLKREQ_SENSE 11
+#define AFI_INTR_CODE_INT_CODE_CLKCLAMP_SENSE 12
+#define AFI_INTR_CODE_INT_CODE_RDY4PD_SENSE 13
+#define AFI_INTR_CODE_INT_CODE_P2P_ERROR 14
+
+#define AFI_INTR_SIGNATURE 0x0bc
+#define AFI_UPPER_FPCI_ADDRESS 0x0c0
+#define AFI_SM_INTR_ENABLE 0x0c4
+#define AFI_SM_INTR_RP_DEASSERT (1 << 14)
+#define AFI_SM_INTR_RP_ASSERT (1 << 13)
+#define AFI_SM_INTR_HOTPLUG (1 << 12)
+#define AFI_SM_INTR_PME (1 << 11)
+#define AFI_SM_INTR_FATAL_ERROR (1 << 10)
+#define AFI_SM_INTR_UNCORR_ERROR (1 << 9)
+#define AFI_SM_INTR_CORR_ERROR (1 << 8)
+#define AFI_SM_INTR_INTD_DEASSERT (1 << 7)
+#define AFI_SM_INTR_INTC_DEASSERT (1 << 6)
+#define AFI_SM_INTR_INTB_DEASSERT (1 << 5)
+#define AFI_SM_INTR_INTA_DEASSERT (1 << 4)
+#define AFI_SM_INTR_INTD_ASSERT (1 << 3)
+#define AFI_SM_INTR_INTC_ASSERT (1 << 2)
+#define AFI_SM_INTR_INTB_ASSERT (1 << 1)
+#define AFI_SM_INTR_INTA_ASSERT (1 << 0)
+
+#define AFI_AFI_INTR_ENABLE 0x0c8
+#define AFI_AFI_INTR_ENABLE_CODE(code) (1 << (code))
+
+#define AFI_PCIE_CONFIG 0x0f8
+#define AFI_PCIE_CONFIG_PCIE_DISABLE(x) (1 << ((x) + 1))
+#define AFI_PCIE_CONFIG_PCIE_DISABLE_ALL 0x6
+#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK (0xf << 20)
+#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_XBAR2_1 (0x0 << 20)
+#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_XBAR4_1 (0x1 << 20)
+
+#define AFI_FUSE 0x104
+#define AFI_FUSE_PCIE_T0_GEN2_DIS (1 << 2)
+
+#define AFI_PEX0_CTRL 0x110
+#define AFI_PEX1_CTRL 0x118
+#define AFI_PEX2_CTRL 0x128
+#define AFI_PEX_CTRL_OVERRIDE_EN (1 << 4)
+#define AFI_PEX_CTRL_REFCLK_EN (1 << 3)
+#define AFI_PEX_CTRL_CLKREQ_EN (1 << 1)
+#define AFI_PEX_CTRL_RST_L (1 << 0)
+
+#define AFI_AXI_BAR6_SZ 0x134
+#define AFI_AXI_BAR7_SZ 0x138
+#define AFI_AXI_BAR8_SZ 0x13c
+#define AFI_AXI_BAR6_START 0x140
+#define AFI_AXI_BAR7_START 0x144
+#define AFI_AXI_BAR8_START 0x148
+#define AFI_FPCI_BAR6 0x14c
+#define AFI_FPCI_BAR7 0x150
+#define AFI_FPCI_BAR8 0x154
+#define AFI_PLLE_CONTROL 0x160
+#define AFI_PLLE_CONTROL_BYPASS_PADS2PLLE_CONTROL (1 << 9)
+#define AFI_PLLE_CONTROL_BYPASS_PCIE2PLLE_CONTROL (1 << 8)
+#define AFI_PLLE_CONTROL_PADS2PLLE_CONTROL_EN (1 << 1)
+#define AFI_PLLE_CONTROL_PCIE2PLLE_CONTROL_EN (1 << 0)
+
+#define AFI_PEXBIAS_CTRL 0x168
+
+/* Configuration space */
+#define RP_VEND_XP 0x0F00
+#define RP_VEND_XP_DL_UP (1 << 30)
+
+#define RP_VEND_CTL2 0x0fa8
+#define RP_VEND_CTL2_PCA_ENABLE (1 << 7)
+
+#define RP_PRIV_MISC 0x0FE0
+#define RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT (0xE << 0)
+#define RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT (0xF << 0)
+
+#define RP_LINK_CONTROL_STATUS 0x0090
+#define RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE 0x20000000
+#define RP_LINK_CONTROL_STATUS_LINKSTAT_MASK 0x3fff0000
+
+/* PADS space */
+#define PADS_REFCLK_CFG0 0x000c8
+#define PADS_REFCLK_CFG1 0x000cc
+
+
+/* Wait 50 ms (per port) for link. */
+#define TEGRA_PCIE_LINKUP_TIMEOUT 50000
+
+/* FPCI Address space */
+#define FPCI_MAP_IO 0xFDFC000000ULL
+#define FPCI_MAP_TYPE0_CONFIG 0xFDFC000000ULL
+#define FPCI_MAP_TYPE1_CONFIG 0xFDFF000000ULL
+#define FPCI_MAP_EXT_TYPE0_CONFIG 0xFE00000000ULL
+#define FPCI_MAP_EXT_TYPE1_CONFIG 0xFE10000000ULL
+
+#define TEGRA_PCIB_MSI_ENABLE
+
+#define DEBUG
+#ifdef DEBUG
+#define debugf(fmt, args...) do { printf(fmt,##args); } while (0)
+#else
+#define debugf(fmt, args...)
+#endif
+
+/*
+ * Configuration space format:
+ * [27:24] extended register
+ * [23:16] bus
+ * [15:11] slot (device)
+ * [10: 8] function
+ * [ 7: 0] register
+ */
+#define PCI_CFG_EXT_REG(reg) ((((reg) >> 8) & 0x0f) << 24)
+#define PCI_CFG_BUS(bus) (((bus) & 0xff) << 16)
+#define PCI_CFG_DEV(dev) (((dev) & 0x1f) << 11)
+#define PCI_CFG_FUN(fun) (((fun) & 0x07) << 8)
+#define PCI_CFG_BASE_REG(reg) ((reg) & 0xff)
+
+#define PADS_WR4(_sc, _r, _v) bus_write_4((_sc)->pads_mem_res, (_r), (_v))
+#define PADS_RD4(_sc, _r) bus_read_4((_sc)->pads_mem_res, (_r))
+#define AFI_WR4(_sc, _r, _v) bus_write_4((_sc)->afi_mem_res, (_r), (_v))
+#define AFI_RD4(_sc, _r) bus_read_4((_sc)->afi_mem_res, (_r))
+
+static struct {
+ bus_size_t axi_start;
+ bus_size_t fpci_start;
+ bus_size_t size;
+} bars[] = {
+ {AFI_AXI_BAR0_START, AFI_FPCI_BAR0, AFI_AXI_BAR0_SZ}, /* BAR 0 */
+ {AFI_AXI_BAR1_START, AFI_FPCI_BAR1, AFI_AXI_BAR1_SZ}, /* BAR 1 */
+ {AFI_AXI_BAR2_START, AFI_FPCI_BAR2, AFI_AXI_BAR2_SZ}, /* BAR 2 */
+ {AFI_AXI_BAR3_START, AFI_FPCI_BAR3, AFI_AXI_BAR3_SZ}, /* BAR 3 */
+ {AFI_AXI_BAR4_START, AFI_FPCI_BAR4, AFI_AXI_BAR4_SZ}, /* BAR 4 */
+ {AFI_AXI_BAR5_START, AFI_FPCI_BAR5, AFI_AXI_BAR5_SZ}, /* BAR 5 */
+ {AFI_AXI_BAR6_START, AFI_FPCI_BAR6, AFI_AXI_BAR6_SZ}, /* BAR 6 */
+ {AFI_AXI_BAR7_START, AFI_FPCI_BAR7, AFI_AXI_BAR7_SZ}, /* BAR 7 */
+ {AFI_AXI_BAR8_START, AFI_FPCI_BAR8, AFI_AXI_BAR8_SZ}, /* BAR 8 */
+ {AFI_MSI_AXI_BAR_ST, AFI_MSI_FPCI_BAR_ST, AFI_MSI_BAR_SZ}, /* MSI 9 */
+};
+
+
+struct pcie_soc {
+ char **regulator_names;
+ bool cml_clk;
+ bool pca_enable;
+ uint32_t pads_refclk_cfg0;
+ uint32_t pads_refclk_cfg1;
+};
+
+/* Tegra 124 config. */
+static char *tegra124_reg_names[] = {
+ "avddio-pex-supply",
+ "dvddio-pex-supply",
+ "avdd-pex-pll-supply",
+ "hvdd-pex-supply",
+ "hvdd-pex-pll-e-supply",
+ "vddio-pex-ctl-supply",
+ "avdd-pll-erefe-supply",
+ NULL
+};
+
+static struct pcie_soc tegra124_soc = {
+ .regulator_names = tegra124_reg_names,
+ .cml_clk = true,
+ .pca_enable = false,
+ .pads_refclk_cfg0 = 0x44ac44ac,
+};
+
+/* Tegra 210 config. */
+static char *tegra210_reg_names[] = {
+ "avdd-pll-uerefe-supply",
+ "hvddio-pex-supply",
+ "dvddio-pex-supply",
+ "dvdd-pex-pll-supply",
+ "hvdd-pex-pll-e-supply",
+ "vddio-pex-ctl-supply",
+ NULL
+};
+
+static struct pcie_soc tegra210_soc = {
+ .regulator_names = tegra210_reg_names,
+ .cml_clk = true,
+ .pca_enable = true,
+ .pads_refclk_cfg0 = 0x90b890b8,
+};
+
+/* Compatible devices. */
+static struct ofw_compat_data compat_data[] = {
+ {"nvidia,tegra124-pcie", (uintptr_t)&tegra124_soc},
+ {"nvidia,tegra210-pcie", (uintptr_t)&tegra210_soc},
+ {NULL, 0},
+};
+
+#define TEGRA_FLAG_MSI_USED 0x0001
+struct tegra_pcib_irqsrc {
+ struct intr_irqsrc isrc;
+ u_int irq;
+ u_int flags;
+};
+
+struct tegra_pcib_port {
+ int enabled;
+ int port_idx; /* chip port index */
+ int num_lanes; /* number of lanes */
+ bus_size_t afi_pex_ctrl; /* offset of afi_pex_ctrl */
+ phy_t phy; /* port phy */
+
+ /* Config space properties. */
+ bus_addr_t rp_base_addr; /* PA of config window */
+ bus_size_t rp_size; /* size of config window */
+ bus_space_handle_t cfg_handle; /* handle of config window */
+};
+
+#define TEGRA_PCIB_MAX_PORTS 3
+#define TEGRA_PCIB_MAX_MSI AFI_MSI_INTR_IN_REG * AFI_MSI_REGS
+struct tegra_pcib_softc {
+ struct ofw_pci_softc ofw_pci;
+ device_t dev;
+ struct pcie_soc *soc;
+ struct mtx mtx;
+ struct resource *pads_mem_res;
+ struct resource *afi_mem_res;
+ struct resource *cfg_mem_res;
+ struct resource *irq_res;
+ struct resource *msi_irq_res;
+ void *intr_cookie;
+ void *msi_intr_cookie;
+
+ struct ofw_pci_range mem_range;
+ struct ofw_pci_range pref_mem_range;
+ struct ofw_pci_range io_range;
+
+ clk_t clk_pex;
+ clk_t clk_afi;
+ clk_t clk_pll_e;
+ clk_t clk_cml;
+ hwreset_t hwreset_pex;
+ hwreset_t hwreset_afi;
+ hwreset_t hwreset_pcie_x;
+ regulator_t regulators[16]; /* Safe maximum */
+
+ vm_offset_t msi_page; /* VA of MSI page */
+ bus_addr_t cfg_base_addr; /* base address of config */
+ bus_size_t cfg_cur_offs; /* currently mapped window */
+ bus_space_handle_t cfg_handle; /* handle of config window */
+ bus_space_tag_t bus_tag; /* tag of config window */
+ int lanes_cfg;
+ int num_ports;
+ struct tegra_pcib_port *ports[TEGRA_PCIB_MAX_PORTS];
+ struct tegra_pcib_irqsrc *isrcs;
+};
+
+static int
+tegra_pcib_maxslots(device_t dev)
+{
+ return (16);
+}
+
+static int
+tegra_pcib_route_interrupt(device_t bus, device_t dev, int pin)
+{
+ struct tegra_pcib_softc *sc;
+ u_int irq;
+
+ sc = device_get_softc(bus);
+ irq = intr_map_clone_irq(rman_get_start(sc->irq_res));
+ device_printf(bus, "route pin %d for device %d.%d to %u\n",
+ pin, pci_get_slot(dev), pci_get_function(dev),
+ irq);
+
+ return (irq);
+}
+
+static int
+tegra_pcbib_map_cfg(struct tegra_pcib_softc *sc, u_int bus, u_int slot,
+ u_int func, u_int reg)
+{
+ bus_size_t offs;
+ int rv;
+
+ offs = sc->cfg_base_addr;
+ offs |= PCI_CFG_BUS(bus) | PCI_CFG_DEV(slot) | PCI_CFG_FUN(func) |
+ PCI_CFG_EXT_REG(reg);
+ if ((sc->cfg_handle != 0) && (sc->cfg_cur_offs == offs))
+ return (0);
+ if (sc->cfg_handle != 0)
+ bus_space_unmap(sc->bus_tag, sc->cfg_handle, 0x800);
+
+ rv = bus_space_map(sc->bus_tag, offs, 0x800, 0, &sc->cfg_handle);
+ if (rv != 0)
+ device_printf(sc->dev, "Cannot map config space\n");
+ else
+ sc->cfg_cur_offs = offs;
+ return (rv);
+}
+
+static uint32_t
+tegra_pcib_read_config(device_t dev, u_int bus, u_int slot, u_int func,
+ u_int reg, int bytes)
+{
+ struct tegra_pcib_softc *sc;
+ bus_space_handle_t hndl;
+ uint32_t off;
+ uint32_t val;
+ int rv, i;
+
+ sc = device_get_softc(dev);
+ if (bus == 0) {
+ if (func != 0)
+ return (0xFFFFFFFF);
+ for (i = 0; i < TEGRA_PCIB_MAX_PORTS; i++) {
+ if ((sc->ports[i] != NULL) &&
+ (sc->ports[i]->port_idx == slot)) {
+ hndl = sc->ports[i]->cfg_handle;
+ off = reg & 0xFFF;
+ break;
+ }
+ }
+ if (i >= TEGRA_PCIB_MAX_PORTS)
+ return (0xFFFFFFFF);
+ } else {
+ rv = tegra_pcbib_map_cfg(sc, bus, slot, func, reg);
+ if (rv != 0)
+ return (0xFFFFFFFF);
+ hndl = sc->cfg_handle;
+ off = PCI_CFG_BASE_REG(reg);
+ }
+
+ val = bus_space_read_4(sc->bus_tag, hndl, off & ~3);
+ switch (bytes) {
+ case 4:
+ break;
+ case 2:
+ if (off & 3)
+ val >>= 16;
+ val &= 0xffff;
+ break;
+ case 1:
+ val >>= ((off & 3) << 3);
+ val &= 0xff;
+ break;
+ }
+ return val;
+}
+
+static void
+tegra_pcib_write_config(device_t dev, u_int bus, u_int slot, u_int func,
+ u_int reg, uint32_t val, int bytes)
+{
+ struct tegra_pcib_softc *sc;
+ bus_space_handle_t hndl;
+ uint32_t off;
+ uint32_t val2;
+ int rv, i;
+
+ sc = device_get_softc(dev);
+ if (bus == 0) {
+ if (func != 0)
+ return;
+ for (i = 0; i < TEGRA_PCIB_MAX_PORTS; i++) {
+ if ((sc->ports[i] != NULL) &&
+ (sc->ports[i]->port_idx == slot)) {
+ hndl = sc->ports[i]->cfg_handle;
+ off = reg & 0xFFF;
+ break;
+ }
+ }
+ if (i >= TEGRA_PCIB_MAX_PORTS)
+ return;
+ } else {
+ rv = tegra_pcbib_map_cfg(sc, bus, slot, func, reg);
+ if (rv != 0)
+ return;
+ hndl = sc->cfg_handle;
+ off = PCI_CFG_BASE_REG(reg);
+ }
+
+ switch (bytes) {
+ case 4:
+ bus_space_write_4(sc->bus_tag, hndl, off, val);
+ break;
+ case 2:
+ val2 = bus_space_read_4(sc->bus_tag, hndl, off & ~3);
+ val2 &= ~(0xffff << ((off & 3) << 3));
+ val2 |= ((val & 0xffff) << ((off & 3) << 3));
+ bus_space_write_4(sc->bus_tag, hndl, off & ~3, val2);
+ break;
+ case 1:
+ val2 = bus_space_read_4(sc->bus_tag, hndl, off & ~3);
+ val2 &= ~(0xff << ((off & 3) << 3));
+ val2 |= ((val & 0xff) << ((off & 3) << 3));
+ bus_space_write_4(sc->bus_tag, hndl, off & ~3, val2);
+ break;
+ }
+}
+
+static int tegra_pci_intr(void *arg)
+{
+ struct tegra_pcib_softc *sc = arg;
+ uint32_t code, signature;
+
+ code = bus_read_4(sc->afi_mem_res, AFI_INTR_CODE) & AFI_INTR_CODE_MASK;
+ signature = bus_read_4(sc->afi_mem_res, AFI_INTR_SIGNATURE);
+ bus_write_4(sc->afi_mem_res, AFI_INTR_CODE, 0);
+ if (code == AFI_INTR_CODE_INT_CODE_SM_MSG)
+ return(FILTER_STRAY);
+
+ printf("tegra_pci_intr: code %x sig %x\n", code, signature);
+ return (FILTER_HANDLED);
+}
+
+/* -----------------------------------------------------------------------
+ *
+ * PCI MSI interface
+ */
+static int
+tegra_pcib_alloc_msi(device_t pci, device_t child, int count, int maxcount,
+ int *irqs)
+{
+ phandle_t msi_parent;
+
+ /* XXXX ofw_bus_msimap() don't works for Tegra DT.
+ ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent,
+ NULL);
+ */
+ msi_parent = OF_xref_from_node(ofw_bus_get_node(pci));
+ return (intr_alloc_msi(pci, child, msi_parent, count, maxcount,
+ irqs));
+}
+
+static int
+tegra_pcib_release_msi(device_t pci, device_t child, int count, int *irqs)
+{
+ phandle_t msi_parent;
+
+ /* XXXX ofw_bus_msimap() don't works for Tegra DT.
+ ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent,
+ NULL);
+ */
+ msi_parent = OF_xref_from_node(ofw_bus_get_node(pci));
+ return (intr_release_msi(pci, child, msi_parent, count, irqs));
+}
+
+static int
+tegra_pcib_map_msi(device_t pci, device_t child, int irq, uint64_t *addr,
+ uint32_t *data)
+{
+ phandle_t msi_parent;
+
+ /* XXXX ofw_bus_msimap() don't works for Tegra DT.
+ ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent,
+ NULL);
+ */
+ msi_parent = OF_xref_from_node(ofw_bus_get_node(pci));
+ return (intr_map_msi(pci, child, msi_parent, irq, addr, data));
+}
+
+#ifdef TEGRA_PCIB_MSI_ENABLE
+
+/* --------------------------------------------------------------------------
+ *
+ * Interrupts
+ *
+ */
+
+static inline void
+tegra_pcib_isrc_mask(struct tegra_pcib_softc *sc,
+ struct tegra_pcib_irqsrc *tgi, uint32_t val)
+{
+ uint32_t reg;
+ int offs, bit;
+
+ offs = tgi->irq / AFI_MSI_INTR_IN_REG;
+ bit = 1 << (tgi->irq % AFI_MSI_INTR_IN_REG);
+
+ if (val != 0)
+ AFI_WR4(sc, AFI_MSI_VEC(offs), bit);
+ reg = AFI_RD4(sc, AFI_MSI_EN_VEC(offs));
+ if (val != 0)
+ reg |= bit;
+ else
+ reg &= ~bit;
+ AFI_WR4(sc, AFI_MSI_EN_VEC(offs), reg);
+}
+
+static int
+tegra_pcib_msi_intr(void *arg)
+{
+ u_int irq, i, bit, reg;
+ struct tegra_pcib_softc *sc;
+ struct trapframe *tf;
+ struct tegra_pcib_irqsrc *tgi;
+
+ sc = (struct tegra_pcib_softc *)arg;
+ tf = curthread->td_intr_frame;
+
+ for (i = 0; i < AFI_MSI_REGS; i++) {
+ reg = AFI_RD4(sc, AFI_MSI_VEC(i));
+ /* Handle one vector. */
+ while (reg != 0) {
+ bit = ffs(reg) - 1;
+ /* Send EOI */
+ AFI_WR4(sc, AFI_MSI_VEC(i), 1 << bit);
+ irq = i * AFI_MSI_INTR_IN_REG + bit;
+ tgi = &sc->isrcs[irq];
+ if (intr_isrc_dispatch(&tgi->isrc, tf) != 0) {
+ /* Disable stray. */
+ tegra_pcib_isrc_mask(sc, tgi, 0);
+ device_printf(sc->dev,
+ "Stray irq %u disabled\n", irq);
+ }
+ reg = AFI_RD4(sc, AFI_MSI_VEC(i));
+ }
+ }
+ return (FILTER_HANDLED);
+}
+
+static int
+tegra_pcib_msi_attach(struct tegra_pcib_softc *sc)
+{
+ int error;
+ uint32_t irq;
+ const char *name;
+
+ sc->isrcs = malloc(sizeof(*sc->isrcs) * TEGRA_PCIB_MAX_MSI, M_DEVBUF,
+ M_WAITOK | M_ZERO);
+
+ name = device_get_nameunit(sc->dev);
+ for (irq = 0; irq < TEGRA_PCIB_MAX_MSI; irq++) {
+ sc->isrcs[irq].irq = irq;
+ error = intr_isrc_register(&sc->isrcs[irq].isrc,
+ sc->dev, 0, "%s,%u", name, irq);
+ if (error != 0)
+ return (error); /* XXX deregister ISRCs */
+ }
+ if (intr_msi_register(sc->dev,
+ OF_xref_from_node(ofw_bus_get_node(sc->dev))) != 0)
+ return (ENXIO);
+
+ return (0);
+}
+
+static int
+tegra_pcib_msi_detach(struct tegra_pcib_softc *sc)
+{
+
+ /*
+ * There has not been established any procedure yet
+ * how to detach PIC from living system correctly.
+ */
+ device_printf(sc->dev, "%s: not implemented yet\n", __func__);
+ return (EBUSY);
+}
+
+static void
+tegra_pcib_msi_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct tegra_pcib_softc *sc;
+ struct tegra_pcib_irqsrc *tgi;
+
+ sc = device_get_softc(dev);
+ tgi = (struct tegra_pcib_irqsrc *)isrc;
+ tegra_pcib_isrc_mask(sc, tgi, 0);
+}
+
+static void
+tegra_pcib_msi_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct tegra_pcib_softc *sc;
+ struct tegra_pcib_irqsrc *tgi;
+
+ sc = device_get_softc(dev);
+ tgi = (struct tegra_pcib_irqsrc *)isrc;
+ tegra_pcib_isrc_mask(sc, tgi, 1);
+}
+
+/* MSI interrupts are edge trigered -> do nothing */
+static void
+tegra_pcib_msi_post_filter(device_t dev, struct intr_irqsrc *isrc)
+{
+}
+
+static void
+tegra_pcib_msi_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+}
+
+static void
+tegra_pcib_msi_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+}
+
+static int
+tegra_pcib_msi_setup_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ struct tegra_pcib_softc *sc;
+ struct tegra_pcib_irqsrc *tgi;
+
+ sc = device_get_softc(dev);
+ tgi = (struct tegra_pcib_irqsrc *)isrc;
+
+ if (data == NULL || data->type != INTR_MAP_DATA_MSI)
+ return (ENOTSUP);
+
+ if (isrc->isrc_handlers == 0)
+ tegra_pcib_msi_enable_intr(dev, isrc);
+
+ return (0);
+}
+
+static int
+tegra_pcib_msi_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ struct tegra_pcib_softc *sc;
+ struct tegra_pcib_irqsrc *tgi;
+
+ sc = device_get_softc(dev);
+ tgi = (struct tegra_pcib_irqsrc *)isrc;
+
+ if (isrc->isrc_handlers == 0)
+ tegra_pcib_isrc_mask(sc, tgi, 0);
+ return (0);
+}
+
+static int
+tegra_pcib_msi_alloc_msi(device_t dev, device_t child, int count, int maxcount,
+ device_t *pic, struct intr_irqsrc **srcs)
+{
+ struct tegra_pcib_softc *sc;
+ int i, irq, end_irq;
+ bool found;
+
+ KASSERT(powerof2(count), ("%s: bad count", __func__));
+ KASSERT(powerof2(maxcount), ("%s: bad maxcount", __func__));
+
+ sc = device_get_softc(dev);
+ mtx_lock(&sc->mtx);
+
+ found = false;
+ for (irq = 0; (irq + count - 1) < TEGRA_PCIB_MAX_MSI; irq++) {
+ /* Start on an aligned interrupt */
+ if ((irq & (maxcount - 1)) != 0)
+ continue;
+
+ /* Assume we found a valid range until shown otherwise */
+ found = true;
+
+ /* Check this range is valid */
+ for (end_irq = irq; end_irq < irq + count; end_irq++) {
+ /* This is already used */
+ if ((sc->isrcs[end_irq].flags & TEGRA_FLAG_MSI_USED) ==
+ TEGRA_FLAG_MSI_USED) {
+ found = false;
+ break;
+ }
+ }
+
+ if (found)
+ break;
+ }
+
+ /* Not enough interrupts were found */
+ if (!found || irq == (TEGRA_PCIB_MAX_MSI - 1)) {
+ mtx_unlock(&sc->mtx);
+ return (ENXIO);
+ }
+
+ for (i = 0; i < count; i++) {
+ /* Mark the interrupt as used */
+ sc->isrcs[irq + i].flags |= TEGRA_FLAG_MSI_USED;
+ }
+ mtx_unlock(&sc->mtx);
+
+ for (i = 0; i < count; i++)
+ srcs[i] = (struct intr_irqsrc *)&sc->isrcs[irq + i];
+ *pic = device_get_parent(dev);
+ return (0);
+}
+
+static int
+tegra_pcib_msi_release_msi(device_t dev, device_t child, int count,
+ struct intr_irqsrc **isrc)
+{
+ struct tegra_pcib_softc *sc;
+ struct tegra_pcib_irqsrc *ti;
+ int i;
+
+ sc = device_get_softc(dev);
+ mtx_lock(&sc->mtx);
+ for (i = 0; i < count; i++) {
+ ti = (struct tegra_pcib_irqsrc *)isrc[i];
+
+ KASSERT((ti->flags & TEGRA_FLAG_MSI_USED) == TEGRA_FLAG_MSI_USED,
+ ("%s: Trying to release an unused MSI-X interrupt",
+ __func__));
+
+ ti->flags &= ~TEGRA_FLAG_MSI_USED;
+ }
+ mtx_unlock(&sc->mtx);
+ return (0);
+}
+
+static int
+tegra_pcib_msi_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc,
+ uint64_t *addr, uint32_t *data)
+{
+ struct tegra_pcib_softc *sc = device_get_softc(dev);
+ struct tegra_pcib_irqsrc *ti = (struct tegra_pcib_irqsrc *)isrc;
+
+ *addr = vtophys(sc->msi_page);
+ *data = ti->irq;
+ return (0);
+}
+#endif
+
+/* ------------------------------------------------------------------- */
+static bus_size_t
+tegra_pcib_pex_ctrl(struct tegra_pcib_softc *sc, int port)
+{
+ if (port >= TEGRA_PCIB_MAX_PORTS)
+ panic("invalid port number: %d\n", port);
+
+ if (port == 0)
+ return (AFI_PEX0_CTRL);
+ else if (port == 1)
+ return (AFI_PEX1_CTRL);
+ else if (port == 2)
+ return (AFI_PEX2_CTRL);
+ else
+ panic("invalid port number: %d\n", port);
+}
+
+static int
+tegra_pcib_enable_fdt_resources(struct tegra_pcib_softc *sc)
+{
+ int i, rv;
+
+ rv = hwreset_assert(sc->hwreset_pcie_x);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot assert 'pcie_x' reset\n");
+ return (rv);
+ }
+ rv = hwreset_assert(sc->hwreset_afi);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot assert 'afi' reset\n");
+ return (rv);
+ }
+ rv = hwreset_assert(sc->hwreset_pex);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot assert 'pex' reset\n");
+ return (rv);
+ }
+
+ tegra_powergate_power_off(TEGRA_POWERGATE_PCX);
+
+ /* Regulators. */
+ for (i = 0; i < nitems(sc->regulators); i++) {
+ if (sc->regulators[i] == NULL)
+ continue;
+ rv = regulator_enable(sc->regulators[i]);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot enable '%s' regulator\n",
+ sc->soc->regulator_names[i]);
+ return (rv);
+ }
+ }
+
+ rv = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_PCX,
+ sc->clk_pex, sc->hwreset_pex);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot enable 'PCX' powergate\n");
+ return (rv);
+ }
+
+ rv = hwreset_deassert(sc->hwreset_afi);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot unreset 'afi' reset\n");
+ return (rv);
+ }
+
+ rv = clk_enable(sc->clk_afi);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot enable 'afi' clock\n");
+ return (rv);
+ }
+ if (sc->soc->cml_clk) {
+ rv = clk_enable(sc->clk_cml);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot enable 'cml' clock\n");
+ return (rv);
+ }
+ }
+ rv = clk_enable(sc->clk_pll_e);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot enable 'pll_e' clock\n");
+ return (rv);
+ }
+
+ return (0);
+}
+
+static struct tegra_pcib_port *
+tegra_pcib_parse_port(struct tegra_pcib_softc *sc, phandle_t node)
+{
+ struct tegra_pcib_port *port;
+ uint32_t tmp[5];
+ char tmpstr[6];
+ int rv;
+
+ port = malloc(sizeof(struct tegra_pcib_port), M_DEVBUF, M_WAITOK);
+
+ rv = OF_getprop(node, "status", tmpstr, sizeof(tmpstr));
+ if (rv <= 0 || strcmp(tmpstr, "okay") == 0 ||
+ strcmp(tmpstr, "ok") == 0)
+ port->enabled = 1;
+ else
+ port->enabled = 0;
+
+ rv = OF_getencprop(node, "assigned-addresses", tmp, sizeof(tmp));
+ if (rv != sizeof(tmp)) {
+ device_printf(sc->dev, "Cannot parse assigned-address: %d\n",
+ rv);
+ goto fail;
+ }
+ port->rp_base_addr = tmp[2];
+ port->rp_size = tmp[4];
+ port->port_idx = OFW_PCI_PHYS_HI_DEVICE(tmp[0]) - 1;
+ if (port->port_idx >= TEGRA_PCIB_MAX_PORTS) {
+ device_printf(sc->dev, "Invalid port index: %d\n",
+ port->port_idx);
+ goto fail;
+ }
+ /* XXX - TODO:
+ * Implement proper function for parsing pci "reg" property:
+ * - it have PCI bus format
+ * - its relative to matching "assigned-addresses"
+ */
+ rv = OF_getencprop(node, "reg", tmp, sizeof(tmp));
+ if (rv != sizeof(tmp)) {
+ device_printf(sc->dev, "Cannot parse reg: %d\n", rv);
+ goto fail;
+ }
+ port->rp_base_addr += tmp[2];
+
+ rv = OF_getencprop(node, "nvidia,num-lanes", &port->num_lanes,
+ sizeof(port->num_lanes));
+ if (rv != sizeof(port->num_lanes)) {
+ device_printf(sc->dev, "Cannot parse nvidia,num-lanes: %d\n",
+ rv);
+ goto fail;
+ }
+ if (port->num_lanes > 4) {
+ device_printf(sc->dev, "Invalid nvidia,num-lanes: %d\n",
+ port->num_lanes);
+ goto fail;
+ }
+
+ port->afi_pex_ctrl = tegra_pcib_pex_ctrl(sc, port->port_idx);
+ sc->lanes_cfg |= port->num_lanes << (4 * port->port_idx);
+
+ /* Phy. */
+ rv = phy_get_by_ofw_name(sc->dev, node, "pcie-0", &port->phy);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot get 'pcie-0' phy for port %d\n",
+ port->port_idx);
+ goto fail;
+ }
+
+ return (port);
+fail:
+ free(port, M_DEVBUF);
+ return (NULL);
+}
+
+static int
+tegra_pcib_parse_fdt_resources(struct tegra_pcib_softc *sc, phandle_t node)
+{
+ phandle_t child;
+ struct tegra_pcib_port *port;
+ int i, rv;
+
+ /* Regulators. */
+ for (i = 0; sc->soc->regulator_names[i] != NULL; i++) {
+ if (i >= nitems(sc->regulators)) {
+ device_printf(sc->dev,
+ "Too many regulators present in DT.\n");
+ return (EOVERFLOW);
+ }
+ rv = regulator_get_by_ofw_property(sc->dev, 0,
+ sc->soc->regulator_names[i], sc->regulators + i);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot get '%s' regulator\n",
+ sc->soc->regulator_names[i]);
+ return (ENXIO);
+ }
+ }
+
+ /* Resets. */
+ rv = hwreset_get_by_ofw_name(sc->dev, 0, "pex", &sc->hwreset_pex);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'pex' reset\n");
+ return (ENXIO);
+ }
+ rv = hwreset_get_by_ofw_name(sc->dev, 0, "afi", &sc->hwreset_afi);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'afi' reset\n");
+ return (ENXIO);
+ }
+ rv = hwreset_get_by_ofw_name(sc->dev, 0, "pcie_x", &sc->hwreset_pcie_x);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'pcie_x' reset\n");
+ return (ENXIO);
+ }
+
+ /* Clocks. */
+ rv = clk_get_by_ofw_name(sc->dev, 0, "pex", &sc->clk_pex);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'pex' clock\n");
+ return (ENXIO);
+ }
+ rv = clk_get_by_ofw_name(sc->dev, 0, "afi", &sc->clk_afi);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'afi' clock\n");
+ return (ENXIO);
+ }
+ rv = clk_get_by_ofw_name(sc->dev, 0, "pll_e", &sc->clk_pll_e);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'pll_e' clock\n");
+ return (ENXIO);
+ }
+ if (sc->soc->cml_clk) {
+ rv = clk_get_by_ofw_name(sc->dev, 0, "cml", &sc->clk_cml);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'cml' clock\n");
+ return (ENXIO);
+ }
+ }
+
+ /* Ports */
+ sc->num_ports = 0;
+ for (child = OF_child(node); child != 0; child = OF_peer(child)) {
+ port = tegra_pcib_parse_port(sc, child);
+ if (port == NULL) {
+ device_printf(sc->dev, "Cannot parse PCIe port node\n");
+ return (ENXIO);
+ }
+ sc->ports[sc->num_ports++] = port;
+ }
+
+ return (0);
+}
+
+static int
+tegra_pcib_decode_ranges(struct tegra_pcib_softc *sc,
+ struct ofw_pci_range *ranges, int nranges)
+{
+ int i;
+
+ for (i = 2; i < nranges; i++) {
+ if ((ranges[i].pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) ==
+ OFW_PCI_PHYS_HI_SPACE_IO) {
+ if (sc->io_range.size != 0) {
+ device_printf(sc->dev,
+ "Duplicated IO range found in DT\n");
+ return (ENXIO);
+ }
+ sc->io_range = ranges[i];
+ }
+ if (((ranges[i].pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) ==
+ OFW_PCI_PHYS_HI_SPACE_MEM32)) {
+ if (ranges[i].pci_hi & OFW_PCI_PHYS_HI_PREFETCHABLE) {
+ if (sc->pref_mem_range.size != 0) {
+ device_printf(sc->dev,
+ "Duplicated memory range found "
+ "in DT\n");
+ return (ENXIO);
+ }
+ sc->pref_mem_range = ranges[i];
+ } else {
+ if (sc->mem_range.size != 0) {
+ device_printf(sc->dev,
+ "Duplicated memory range found "
+ "in DT\n");
+ return (ENXIO);
+ }
+ sc->mem_range = ranges[i];
+ }
+ }
+ }
+ if ((sc->io_range.size == 0) || (sc->mem_range.size == 0)
+ || (sc->pref_mem_range.size == 0)) {
+ device_printf(sc->dev,
+ " Not all required ranges are found in DT\n");
+ return (ENXIO);
+ }
+ return (0);
+}
+
+/*
+ * Hardware config.
+ */
+static int
+tegra_pcib_wait_for_link(struct tegra_pcib_softc *sc,
+ struct tegra_pcib_port *port)
+{
+ uint32_t reg;
+ int i;
+
+ /* Setup link detection. */
+ reg = tegra_pcib_read_config(sc->dev, 0, port->port_idx, 0,
+ RP_PRIV_MISC, 4);
+ reg &= ~RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT;
+ reg |= RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT;
+ tegra_pcib_write_config(sc->dev, 0, port->port_idx, 0,
+ RP_PRIV_MISC, reg, 4);
+
+ for (i = TEGRA_PCIE_LINKUP_TIMEOUT; i > 0; i--) {
+ reg = tegra_pcib_read_config(sc->dev, 0, port->port_idx, 0,
+ RP_VEND_XP, 4);
+ if (reg & RP_VEND_XP_DL_UP)
+ break;
+ DELAY(1);
+ }
+ if (i <= 0)
+ return (ETIMEDOUT);
+
+ for (i = TEGRA_PCIE_LINKUP_TIMEOUT; i > 0; i--) {
+ reg = tegra_pcib_read_config(sc->dev, 0, port->port_idx, 0,
+ RP_LINK_CONTROL_STATUS, 4);
+ if (reg & RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE)
+ break;
+
+ DELAY(1);
+ }
+ if (i <= 0)
+ return (ETIMEDOUT);
+ return (0);
+}
+
+static void
+tegra_pcib_port_enable(struct tegra_pcib_softc *sc, int port_num)
+{
+ struct tegra_pcib_port *port;
+ uint32_t reg;
+ int rv;
+
+ port = sc->ports[port_num];
+
+ /* Put port to reset. */
+ reg = AFI_RD4(sc, port->afi_pex_ctrl);
+ reg &= ~AFI_PEX_CTRL_RST_L;
+ AFI_WR4(sc, port->afi_pex_ctrl, reg);
+ AFI_RD4(sc, port->afi_pex_ctrl);
+ DELAY(10);
+
+ /* Enable clocks. */
+ reg |= AFI_PEX_CTRL_REFCLK_EN;
+ reg |= AFI_PEX_CTRL_CLKREQ_EN;
+ reg |= AFI_PEX_CTRL_OVERRIDE_EN;
+ AFI_WR4(sc, port->afi_pex_ctrl, reg);
+ AFI_RD4(sc, port->afi_pex_ctrl);
+ DELAY(100);
+
+ /* Release reset. */
+ reg |= AFI_PEX_CTRL_RST_L;
+ AFI_WR4(sc, port->afi_pex_ctrl, reg);
+
+ if (sc->soc->pca_enable) {
+ reg = tegra_pcib_read_config(sc->dev, 0, port->port_idx, 0,
+ RP_VEND_CTL2, 4);
+ reg |= RP_VEND_CTL2_PCA_ENABLE;
+ tegra_pcib_write_config(sc->dev, 0, port->port_idx, 0,
+ RP_VEND_CTL2, reg, 4);
+ }
+
+ rv = tegra_pcib_wait_for_link(sc, port);
+ if (bootverbose)
+ device_printf(sc->dev, " port %d (%d lane%s): Link is %s\n",
+ port->port_idx, port->num_lanes,
+ port->num_lanes > 1 ? "s": "",
+ rv == 0 ? "up": "down");
+}
+
+static void
+tegra_pcib_port_disable(struct tegra_pcib_softc *sc, uint32_t port_num)
+{
+ struct tegra_pcib_port *port;
+ uint32_t reg;
+
+ port = sc->ports[port_num];
+
+ /* Put port to reset. */
+ reg = AFI_RD4(sc, port->afi_pex_ctrl);
+ reg &= ~AFI_PEX_CTRL_RST_L;
+ AFI_WR4(sc, port->afi_pex_ctrl, reg);
+ AFI_RD4(sc, port->afi_pex_ctrl);
+ DELAY(10);
+
+ /* Disable clocks. */
+ reg &= ~AFI_PEX_CTRL_CLKREQ_EN;
+ reg &= ~AFI_PEX_CTRL_REFCLK_EN;
+ AFI_WR4(sc, port->afi_pex_ctrl, reg);
+
+ if (bootverbose)
+ device_printf(sc->dev, " port %d (%d lane%s): Disabled\n",
+ port->port_idx, port->num_lanes,
+ port->num_lanes > 1 ? "s": "");
+}
+
+static void
+tegra_pcib_set_bar(struct tegra_pcib_softc *sc, int bar, uint32_t axi,
+ uint64_t fpci, uint32_t size, int is_memory)
+{
+ uint32_t fpci_reg;
+ uint32_t axi_reg;
+ uint32_t size_reg;
+
+ axi_reg = axi & ~0xFFF;
+ size_reg = size >> 12;
+ fpci_reg = (uint32_t)(fpci >> 8) & ~0xF;
+ fpci_reg |= is_memory ? 0x1 : 0x0;
+ AFI_WR4(sc, bars[bar].axi_start, axi_reg);
+ AFI_WR4(sc, bars[bar].size, size_reg);
+ AFI_WR4(sc, bars[bar].fpci_start, fpci_reg);
+}
+
+static int
+tegra_pcib_enable(struct tegra_pcib_softc *sc)
+{
+ int rv;
+ int i;
+ uint32_t reg;
+
+ rv = tegra_pcib_enable_fdt_resources(sc);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot enable FDT resources\n");
+ return (rv);
+ }
+
+ /* Enable PLLE control. */
+ reg = AFI_RD4(sc, AFI_PLLE_CONTROL);
+ reg &= ~AFI_PLLE_CONTROL_BYPASS_PADS2PLLE_CONTROL;
+ reg |= AFI_PLLE_CONTROL_PADS2PLLE_CONTROL_EN;
+ AFI_WR4(sc, AFI_PLLE_CONTROL, reg);
+
+ /* Set bias pad. */
+ AFI_WR4(sc, AFI_PEXBIAS_CTRL, 0);
+
+ /* Configure mode and ports. */
+ reg = AFI_RD4(sc, AFI_PCIE_CONFIG);
+ reg &= ~AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK;
+ if (sc->lanes_cfg == 0x14) {
+ if (bootverbose)
+ device_printf(sc->dev,
+ "Using x1,x4 configuration\n");
+ reg |= AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_XBAR4_1;
+ } else if (sc->lanes_cfg == 0x12) {
+ if (bootverbose)
+ device_printf(sc->dev,
+ "Using x1,x2 configuration\n");
+ reg |= AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_XBAR2_1;
+ } else {
+ device_printf(sc->dev,
+ "Unsupported lanes configuration: 0x%X\n", sc->lanes_cfg);
+ }
+ reg |= AFI_PCIE_CONFIG_PCIE_DISABLE_ALL;
+ for (i = 0; i < TEGRA_PCIB_MAX_PORTS; i++) {
+ if ((sc->ports[i] != NULL))
+ reg &=
+ ~AFI_PCIE_CONFIG_PCIE_DISABLE(sc->ports[i]->port_idx);
+ }
+ AFI_WR4(sc, AFI_PCIE_CONFIG, reg);
+
+ /* Enable Gen2 support. */
+ reg = AFI_RD4(sc, AFI_FUSE);
+ reg &= ~AFI_FUSE_PCIE_T0_GEN2_DIS;
+ AFI_WR4(sc, AFI_FUSE, reg);
+
+ for (i = 0; i < TEGRA_PCIB_MAX_PORTS; i++) {
+ if (sc->ports[i] != NULL) {
+ rv = phy_enable(sc->ports[i]->phy);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot enable phy for port %d\n",
+ sc->ports[i]->port_idx);
+ return (rv);
+ }
+ }
+ }
+
+ /* Configure PCIe reference clock */
+ PADS_WR4(sc, PADS_REFCLK_CFG0, sc->soc->pads_refclk_cfg0);
+ if (sc->num_ports > 2)
+ PADS_WR4(sc, PADS_REFCLK_CFG1, sc->soc->pads_refclk_cfg1);
+
+ rv = hwreset_deassert(sc->hwreset_pcie_x);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot unreset 'pci_x' reset\n");
+ return (rv);
+ }
+
+ /* Enable config space. */
+ reg = AFI_RD4(sc, AFI_CONFIGURATION);
+ reg |= AFI_CONFIGURATION_EN_FPCI;
+ AFI_WR4(sc, AFI_CONFIGURATION, reg);
+
+ /* Enable AFI errors. */
+ reg = 0;
+ reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_INI_SLVERR);
+ reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_INI_DECERR);
+ reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_TGT_SLVERR);
+ reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_TGT_DECERR);
+ reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_TGT_WRERR);
+ reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_SM_MSG);
+ reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_DFPCI_DECERR);
+ reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_AXI_DECERR);
+ reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_FPCI_TIMEOUT);
+ reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_PE_PRSNT_SENSE);
+ reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_PE_CLKREQ_SENSE);
+ reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_CLKCLAMP_SENSE);
+ reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_RDY4PD_SENSE);
+ reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_P2P_ERROR);
+ AFI_WR4(sc, AFI_AFI_INTR_ENABLE, reg);
+ AFI_WR4(sc, AFI_SM_INTR_ENABLE, 0xffffffff);
+
+ /* Enable INT, disable MSI. */
+ AFI_WR4(sc, AFI_INTR_MASK, AFI_INTR_MASK_INT_MASK);
+
+ /* Mask all FPCI errors. */
+ AFI_WR4(sc, AFI_FPCI_ERROR_MASKS, 0);
+
+ /* Setup AFI translation windows. */
+ /* BAR 0 - type 1 extended configuration. */
+ tegra_pcib_set_bar(sc, 0, rman_get_start(sc->cfg_mem_res),
+ FPCI_MAP_EXT_TYPE1_CONFIG, rman_get_size(sc->cfg_mem_res), 0);
+
+ /* BAR 1 - downstream I/O. */
+ tegra_pcib_set_bar(sc, 1, sc->io_range.host, FPCI_MAP_IO,
+ sc->io_range.size, 0);
+
+ /* BAR 2 - downstream prefetchable memory 1:1. */
+ tegra_pcib_set_bar(sc, 2, sc->pref_mem_range.host,
+ sc->pref_mem_range.host, sc->pref_mem_range.size, 1);
+
+ /* BAR 3 - downstream not prefetchable memory 1:1 .*/
+ tegra_pcib_set_bar(sc, 3, sc->mem_range.host,
+ sc->mem_range.host, sc->mem_range.size, 1);
+
+ /* BAR 3-8 clear. */
+ tegra_pcib_set_bar(sc, 4, 0, 0, 0, 0);
+ tegra_pcib_set_bar(sc, 5, 0, 0, 0, 0);
+ tegra_pcib_set_bar(sc, 6, 0, 0, 0, 0);
+ tegra_pcib_set_bar(sc, 7, 0, 0, 0, 0);
+ tegra_pcib_set_bar(sc, 8, 0, 0, 0, 0);
+
+ /* MSI BAR - clear. */
+ tegra_pcib_set_bar(sc, 9, 0, 0, 0, 0);
+ return(0);
+}
+
+#ifdef TEGRA_PCIB_MSI_ENABLE
+static int
+tegra_pcib_attach_msi(device_t dev)
+{
+ struct tegra_pcib_softc *sc;
+ uint32_t reg;
+ int i, rv;
+
+ sc = device_get_softc(dev);
+
+ sc->msi_page = kmem_alloc_contig(PAGE_SIZE, M_WAITOK, 0,
+ BUS_SPACE_MAXADDR, PAGE_SIZE, 0, VM_MEMATTR_DEFAULT);
+
+ /* MSI BAR */
+ tegra_pcib_set_bar(sc, 9, vtophys(sc->msi_page), vtophys(sc->msi_page),
+ PAGE_SIZE, 0);
+
+ /* Disble and clear all interrupts. */
+ for (i = 0; i < AFI_MSI_REGS; i++) {
+ AFI_WR4(sc, AFI_MSI_EN_VEC(i), 0);
+ AFI_WR4(sc, AFI_MSI_VEC(i), 0xFFFFFFFF);
+ }
+ rv = bus_setup_intr(dev, sc->msi_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ tegra_pcib_msi_intr, NULL, sc, &sc->msi_intr_cookie);
+ if (rv != 0) {
+ device_printf(dev, "cannot setup MSI interrupt handler\n");
+ rv = ENXIO;
+ goto out;
+ }
+
+ if (tegra_pcib_msi_attach(sc) != 0) {
+ device_printf(dev, "WARNING: unable to attach PIC\n");
+ tegra_pcib_msi_detach(sc);
+ goto out;
+ }
+
+ /* Unmask MSI interrupt. */
+ reg = AFI_RD4(sc, AFI_INTR_MASK);
+ reg |= AFI_INTR_MASK_MSI_MASK;
+ AFI_WR4(sc, AFI_INTR_MASK, reg);
+
+out:
+ return (rv);
+}
+#endif
+
+static int
+tegra_pcib_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) {
+ device_set_desc(dev, "Nvidia Integrated PCI/PCI-E Controller");
+ return (BUS_PROBE_DEFAULT);
+ }
+ return (ENXIO);
+}
+
+static int
+tegra_pcib_attach(device_t dev)
+{
+ struct tegra_pcib_softc *sc;
+ phandle_t node;
+ int rv;
+ int rid;
+ struct tegra_pcib_port *port;
+ int i;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ mtx_init(&sc->mtx, "msi_mtx", NULL, MTX_DEF);
+
+ node = ofw_bus_get_node(dev);
+ sc->soc = (struct pcie_soc *)ofw_bus_search_compatible(dev,
+ compat_data)->ocd_data;
+
+ rv = tegra_pcib_parse_fdt_resources(sc, node);
+ if (rv != 0) {
+ device_printf(dev, "Cannot get FDT resources\n");
+ return (rv);
+ }
+
+ /* Allocate bus_space resources. */
+ rid = 0;
+ sc->pads_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->pads_mem_res == NULL) {
+ device_printf(dev, "Cannot allocate PADS register\n");
+ rv = ENXIO;
+ goto out;
+ }
+ /*
+ * XXX - FIXME
+ * tag for config space is not filled when RF_ALLOCATED flag is used.
+ */
+ sc->bus_tag = rman_get_bustag(sc->pads_mem_res);
+
+ rid = 1;
+ sc->afi_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->afi_mem_res == NULL) {
+ device_printf(dev, "Cannot allocate AFI register\n");
+ rv = ENXIO;
+ goto out;
+ }
+
+ rid = 2;
+ sc->cfg_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ALLOCATED);
+ if (sc->cfg_mem_res == NULL) {
+ device_printf(dev, "Cannot allocate config space memory\n");
+ rv = ENXIO;
+ goto out;
+ }
+ sc->cfg_base_addr = rman_get_start(sc->cfg_mem_res);
+
+ /* Map RP slots */
+ for (i = 0; i < TEGRA_PCIB_MAX_PORTS; i++) {
+ if (sc->ports[i] == NULL)
+ continue;
+ port = sc->ports[i];
+ rv = bus_space_map(sc->bus_tag, port->rp_base_addr,
+ port->rp_size, 0, &port->cfg_handle);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot allocate memory for "
+ "port: %d\n", i);
+ rv = ENXIO;
+ goto out;
+ }
+ }
+
+ /*
+ * Get PCI interrupt
+ */
+ rid = 0;
+ sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE | RF_SHAREABLE);
+ if (sc->irq_res == NULL) {
+ device_printf(dev, "Cannot allocate IRQ resources\n");
+ rv = ENXIO;
+ goto out;
+ }
+
+ rid = 1;
+ sc->msi_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (sc->irq_res == NULL) {
+ device_printf(dev, "Cannot allocate MSI IRQ resources\n");
+ rv = ENXIO;
+ goto out;
+ }
+
+ sc->ofw_pci.sc_range_mask = 0x3;
+ rv = ofw_pci_init(dev);
+ if (rv != 0)
+ goto out;
+
+ rv = tegra_pcib_decode_ranges(sc, sc->ofw_pci.sc_range,
+ sc->ofw_pci.sc_nrange);
+ if (rv != 0)
+ goto out;
+
+ if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ tegra_pci_intr, NULL, sc, &sc->intr_cookie)) {
+ device_printf(dev, "cannot setup interrupt handler\n");
+ rv = ENXIO;
+ goto out;
+ }
+
+ /*
+ * Enable PCIE device.
+ */
+ rv = tegra_pcib_enable(sc);
+ if (rv != 0)
+ goto out;
+ for (i = 0; i < TEGRA_PCIB_MAX_PORTS; i++) {
+ if (sc->ports[i] == NULL)
+ continue;
+ if (sc->ports[i]->enabled)
+ tegra_pcib_port_enable(sc, i);
+ else
+ tegra_pcib_port_disable(sc, i);
+ }
+
+#ifdef TEGRA_PCIB_MSI_ENABLE
+ rv = tegra_pcib_attach_msi(dev);
+ if (rv != 0)
+ goto out;
+#endif
+ device_add_child(dev, "pci", -1);
+
+ return (bus_generic_attach(dev));
+
+out:
+
+ return (rv);
+}
+
+static device_method_t tegra_pcib_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, tegra_pcib_probe),
+ DEVMETHOD(device_attach, tegra_pcib_attach),
+
+ /* Bus interface */
+ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
+ DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
+
+ /* pcib interface */
+ DEVMETHOD(pcib_maxslots, tegra_pcib_maxslots),
+ DEVMETHOD(pcib_read_config, tegra_pcib_read_config),
+ DEVMETHOD(pcib_write_config, tegra_pcib_write_config),
+ DEVMETHOD(pcib_route_interrupt, tegra_pcib_route_interrupt),
+ DEVMETHOD(pcib_alloc_msi, tegra_pcib_alloc_msi),
+ DEVMETHOD(pcib_release_msi, tegra_pcib_release_msi),
+ DEVMETHOD(pcib_map_msi, tegra_pcib_map_msi),
+ DEVMETHOD(pcib_request_feature, pcib_request_feature_allow),
+
+#ifdef TEGRA_PCIB_MSI_ENABLE
+ /* MSI/MSI-X */
+ DEVMETHOD(msi_alloc_msi, tegra_pcib_msi_alloc_msi),
+ DEVMETHOD(msi_release_msi, tegra_pcib_msi_release_msi),
+ DEVMETHOD(msi_map_msi, tegra_pcib_msi_map_msi),
+
+ /* Interrupt controller interface */
+ DEVMETHOD(pic_disable_intr, tegra_pcib_msi_disable_intr),
+ DEVMETHOD(pic_enable_intr, tegra_pcib_msi_enable_intr),
+ DEVMETHOD(pic_setup_intr, tegra_pcib_msi_setup_intr),
+ DEVMETHOD(pic_teardown_intr, tegra_pcib_msi_teardown_intr),
+ DEVMETHOD(pic_post_filter, tegra_pcib_msi_post_filter),
+ DEVMETHOD(pic_post_ithread, tegra_pcib_msi_post_ithread),
+ DEVMETHOD(pic_pre_ithread, tegra_pcib_msi_pre_ithread),
+#endif
+
+ /* OFW bus interface */
+ DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat),
+ DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model),
+ DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name),
+ DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node),
+ DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type),
+
+ DEVMETHOD_END
+};
+
+static devclass_t pcib_devclass;
+DEFINE_CLASS_1(pcib, tegra_pcib_driver, tegra_pcib_methods,
+ sizeof(struct tegra_pcib_softc), ofw_pci_driver);
+DRIVER_MODULE(tegra_pcib, simplebus, tegra_pcib_driver, pcib_devclass,
+ NULL, NULL);
diff --git a/sys/arm/nvidia/tegra_pinmux.c b/sys/arm/nvidia/tegra_pinmux.c
new file mode 100644
index 000000000000..17356233306f
--- /dev/null
+++ b/sys/arm/nvidia/tegra_pinmux.c
@@ -0,0 +1,796 @@
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Pin multiplexer driver for Tegra SoCs.
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/fdt/fdt_pinctrl.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+/* Pin multipexor register. */
+#define TEGRA_MUX_FUNCTION_MASK 0x03
+#define TEGRA_MUX_FUNCTION_SHIFT 0
+#define TEGRA_MUX_PUPD_MASK 0x03
+#define TEGRA_MUX_PUPD_SHIFT 2
+#define TEGRA_MUX_TRISTATE_SHIFT 4
+#define TEGRA_MUX_ENABLE_INPUT_SHIFT 5
+#define TEGRA_MUX_OPEN_DRAIN_SHIFT 6
+#define TEGRA_MUX_LOCK_SHIFT 7
+#define TEGRA_MUX_IORESET_SHIFT 8
+#define TEGRA_MUX_RCV_SEL_SHIFT 9
+
+/* Pin goup register. */
+#define TEGRA_GRP_HSM_SHIFT 2
+#define TEGRA_GRP_SCHMT_SHIFT 3
+#define TEGRA_GRP_DRV_TYPE_SHIFT 6
+#define TEGRA_GRP_DRV_TYPE_MASK 0x03
+#define TEGRA_GRP_DRV_DRVDN_SLWR_SHIFT 28
+#define TEGRA_GRP_DRV_DRVDN_SLWR_MASK 0x03
+#define TEGRA_GRP_DRV_DRVUP_SLWF_SHIFT 30
+#define TEGRA_GRP_DRV_DRVUP_SLWF_MASK 0x03
+
+struct pinmux_softc {
+ device_t dev;
+ struct resource *pad_mem_res;
+ struct resource *mux_mem_res;
+ struct resource *mipi_mem_res;
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"nvidia,tegra124-pinmux", 1},
+ {NULL, 0},
+};
+
+enum prop_id {
+ PROP_ID_PULL,
+ PROP_ID_TRISTATE,
+ PROP_ID_ENABLE_INPUT,
+ PROP_ID_OPEN_DRAIN,
+ PROP_ID_LOCK,
+ PROP_ID_IORESET,
+ PROP_ID_RCV_SEL,
+ PROP_ID_HIGH_SPEED_MODE,
+ PROP_ID_SCHMITT,
+ PROP_ID_LOW_POWER_MODE,
+ PROP_ID_DRIVE_DOWN_STRENGTH,
+ PROP_ID_DRIVE_UP_STRENGTH,
+ PROP_ID_SLEW_RATE_FALLING,
+ PROP_ID_SLEW_RATE_RISING,
+ PROP_ID_DRIVE_TYPE,
+
+ PROP_ID_MAX_ID
+};
+
+/* Numeric based parameters. */
+static const struct prop_name {
+ const char *name;
+ enum prop_id id;
+} prop_names[] = {
+ {"nvidia,pull", PROP_ID_PULL},
+ {"nvidia,tristate", PROP_ID_TRISTATE},
+ {"nvidia,enable-input", PROP_ID_ENABLE_INPUT},
+ {"nvidia,open-drain", PROP_ID_OPEN_DRAIN},
+ {"nvidia,lock", PROP_ID_LOCK},
+ {"nvidia,io-reset", PROP_ID_IORESET},
+ {"nvidia,rcv-sel", PROP_ID_RCV_SEL},
+ {"nvidia,high-speed-mode", PROP_ID_HIGH_SPEED_MODE},
+ {"nvidia,schmitt", PROP_ID_SCHMITT},
+ {"nvidia,low-power-mode", PROP_ID_LOW_POWER_MODE},
+ {"nvidia,pull-down-strength", PROP_ID_DRIVE_DOWN_STRENGTH},
+ {"nvidia,pull-up-strength", PROP_ID_DRIVE_UP_STRENGTH},
+ {"nvidia,slew-rate-falling", PROP_ID_SLEW_RATE_FALLING},
+ {"nvidia,slew-rate-rising", PROP_ID_SLEW_RATE_RISING},
+ {"nvidia,drive-type", PROP_ID_DRIVE_TYPE},
+};
+
+/*
+ * configuration for one pin group.
+ */
+struct pincfg {
+ char *function;
+ int params[PROP_ID_MAX_ID];
+};
+#define GPIO_BANK_A 0
+#define GPIO_BANK_B 1
+#define GPIO_BANK_C 2
+#define GPIO_BANK_D 3
+#define GPIO_BANK_E 4
+#define GPIO_BANK_F 5
+#define GPIO_BANK_G 6
+#define GPIO_BANK_H 7
+#define GPIO_BANK_I 8
+#define GPIO_BANK_J 9
+#define GPIO_BANK_K 10
+#define GPIO_BANK_L 11
+#define GPIO_BANK_M 12
+#define GPIO_BANK_N 13
+#define GPIO_BANK_O 14
+#define GPIO_BANK_P 15
+#define GPIO_BANK_Q 16
+#define GPIO_BANK_R 17
+#define GPIO_BANK_S 18
+#define GPIO_BANK_T 19
+#define GPIO_BANK_U 20
+#define GPIO_BANK_V 21
+#define GPIO_BANK_W 22
+#define GPIO_BANK_X 23
+#define GPIO_BANK_Y 24
+#define GPIO_BANK_Z 25
+#define GPIO_BANK_AA 26
+#define GPIO_BANK_BB 27
+#define GPIO_BANK_CC 28
+#define GPIO_BANK_DD 29
+#define GPIO_BANK_EE 30
+#define GPIO_BANK_FF 31
+#define GPIO_NUM(b, p) (8 * (b) + (p))
+
+struct tegra_mux {
+ char *name;
+ bus_size_t reg;
+ char *functions[4];
+ int gpio_num;
+};
+
+#define GMUX(r, gb, gi, nm, f1, f2, f3, f4) \
+{ \
+ .name = #nm, \
+ .reg = r, \
+ .gpio_num = GPIO_NUM(GPIO_BANK_##gb, gi), \
+ .functions = {#f1, #f2, #f3, #f4}, \
+}
+
+#define FMUX(r, nm, f1, f2, f3, f4) \
+{ \
+ .name = #nm, \
+ .reg = r, \
+ .gpio_num = -1, \
+ .functions = {#f1, #f2, #f3, #f4}, \
+}
+
+static const struct tegra_mux pin_mux_tbl[] = {
+ GMUX(0x000, O, 1, ulpi_data0_po1, spi3, hsi, uarta, ulpi),
+ GMUX(0x004, O, 2, ulpi_data1_po2, spi3, hsi, uarta, ulpi),
+ GMUX(0x008, O, 3, ulpi_data2_po3, spi3, hsi, uarta, ulpi),
+ GMUX(0x00C, O, 4, ulpi_data3_po4, spi3, hsi, uarta, ulpi),
+ GMUX(0x010, O, 5, ulpi_data4_po5, spi2, hsi, uarta, ulpi),
+ GMUX(0x014, O, 6, ulpi_data5_po6, spi2, hsi, uarta, ulpi),
+ GMUX(0x018, O, 7, ulpi_data6_po7, spi2, hsi, uarta, ulpi),
+ GMUX(0x01C, O, 0, ulpi_data7_po0, spi2, hsi, uarta, ulpi),
+ GMUX(0x020, P, 9, ulpi_clk_py0, spi1, spi5, uartd, ulpi),
+ GMUX(0x024, P, 1, ulpi_dir_py1, spi1, spi5, uartd, ulpi),
+ GMUX(0x028, P, 2, ulpi_nxt_py2, spi1, spi5, uartd, ulpi),
+ GMUX(0x02C, P, 3, ulpi_stp_py3, spi1, spi5, uartd, ulpi),
+ GMUX(0x030, P, 0, dap3_fs_pp0, i2s2, spi5, displaya, displayb),
+ GMUX(0x034, P, 1, dap3_din_pp1, i2s2, spi5, displaya, displayb),
+ GMUX(0x038, P, 2, dap3_dout_pp2, i2s2, spi5, displaya, rsvd4),
+ GMUX(0x03C, P, 3, dap3_sclk_pp3, i2s2, spi5, rsvd3, displayb),
+ GMUX(0x040, V, 0, pv0, rsvd1, rsvd2, rsvd3, rsvd4),
+ GMUX(0x044, V, 1, pv1, rsvd1, rsvd2, rsvd3, rsvd4),
+ GMUX(0x048, Z, 0, sdmmc1_clk_pz0, sdmmc1, clk12, rsvd3, rsvd4),
+ GMUX(0x04C, Z, 1, sdmmc1_cmd_pz1, sdmmc1, spdif, spi4, uarta),
+ GMUX(0x050, Y, 4, sdmmc1_dat3_py4, sdmmc1, spdif, spi4, uarta),
+ GMUX(0x054, Y, 5, sdmmc1_dat2_py5, sdmmc1, pwm0, spi4, uarta),
+ GMUX(0x058, Y, 6, sdmmc1_dat1_py6, sdmmc1, pwm1, spi4, uarta),
+ GMUX(0x05C, Y, 7, sdmmc1_dat0_py7, sdmmc1, rsvd2, spi4, uarta),
+ GMUX(0x068, W, 5, clk2_out_pw5, extperiph2, rsvd2, rsvd3, rsvd4),
+ GMUX(0x06C, CC, 5, clk2_req_pcc5, dap, rsvd2, rsvd3, rsvd4),
+ GMUX(0x110, N, 7, hdmi_int_pn7, rsvd1, rsvd2, rsvd3, rsvd4),
+ GMUX(0x114, V, 4, ddc_scl_pv4, i2c4, rsvd2, rsvd3, rsvd4),
+ GMUX(0x118, V, 5, ddc_sda_pv5, i2c4, rsvd2, rsvd3, rsvd4),
+ GMUX(0x164, V, 3, uart2_rxd_pc3, irda, spdif, uarta, spi4),
+ GMUX(0x168, C, 2, uart2_txd_pc2, irda, spdif, uarta, spi4),
+ GMUX(0x16C, J, 6, uart2_rts_n_pj6, uarta, uartb, gmi, spi4),
+ GMUX(0x170, J, 5, uart2_cts_n_pj5, uarta, uartb, gmi, spi4),
+ GMUX(0x174, W, 6, uart3_txd_pw6, uartc, rsvd2, gmi, spi4),
+ GMUX(0x178, W, 7, uart3_rxd_pw7, uartc, rsvd2, gmi, spi4),
+ GMUX(0x17C, S, 1, uart3_cts_n_pa1, uartc, sdmmc1, dtv, gmi),
+ GMUX(0x180, C, 0, uart3_rts_n_pc0, uartc, pwm0, dtv, gmi),
+ GMUX(0x184, U, 0, pu0, owr, uarta, gmi, rsvd4),
+ GMUX(0x188, U, 1, pu1, rsvd1, uarta, gmi, rsvd4),
+ GMUX(0x18C, U, 2, pu2, rsvd1, uarta, gmi, rsvd4),
+ GMUX(0x190, U, 3, pu3, pwm0, uarta, gmi, displayb),
+ GMUX(0x194, U, 4, pu4, pwm1, uarta, gmi, displayb),
+ GMUX(0x198, U, 5, pu5, pwm2, uarta, gmi, displayb),
+ GMUX(0x19C, U, 6, pu6, pwm3, uarta, rsvd3, gmi),
+ GMUX(0x1A0, C, 5, gen1_i2c_sda_pc5, i2c1, rsvd2, rsvd3, rsvd4),
+ GMUX(0x1A4, C, 4, gen1_i2c_scl_pc4, i2c1, rsvd2, rsvd3, rsvd4),
+ GMUX(0x1A8, P, 3, dap4_fs_pp4, i2s3, gmi, dtv, rsvd4),
+ GMUX(0x1AC, P, 4, dap4_din_pp5, i2s3, gmi, rsvd3, rsvd4),
+ GMUX(0x1B0, P, 5, dap4_dout_pp6, i2s3, gmi, dtv, rsvd4),
+ GMUX(0x1B4, P, 7, dap4_sclk_pp7, i2s3, gmi, rsvd3, rsvd4),
+ GMUX(0x1B8, P, 0, clk3_out_pee0, extperiph3, rsvd2, rsvd3, rsvd4),
+ GMUX(0x1BC, EE, 1, clk3_req_pee1, dev3, rsvd2, rsvd3, rsvd4),
+ GMUX(0x1C0, C, 7, pc7, rsvd1, rsvd2, gmi, gmi_alt),
+ GMUX(0x1C4, I, 5, pi5, sdmmc2, rsvd2, gmi, rsvd4),
+ GMUX(0x1C8, I, 7, pi7, rsvd1, trace, gmi, dtv),
+ GMUX(0x1CC, K, 0, pk0, rsvd1, sdmmc3, gmi, soc),
+ GMUX(0x1D0, K, 1, pk1, sdmmc2, trace, gmi, rsvd4),
+ GMUX(0x1D4, J, 0, pj0, rsvd1, rsvd2, gmi, usb),
+ GMUX(0x1D8, J, 2, pj2, rsvd1, rsvd2, gmi, soc),
+ GMUX(0x1DC, K, 3, pk3, sdmmc2, trace, gmi, ccla),
+ GMUX(0x1E0, K, 4, pk4, sdmmc2, rsvd2, gmi, gmi_alt),
+ GMUX(0x1E4, K, 2, pk2, rsvd1, rsvd2, gmi, rsvd4),
+ GMUX(0x1E8, I, 3, pi3, rsvd1, rsvd2, gmi, spi4),
+ GMUX(0x1EC, I, 6, pi6, rsvd1, rsvd2, gmi, sdmmc2),
+ GMUX(0x1F0, G, 0, pg0, rsvd1, rsvd2, gmi, rsvd4),
+ GMUX(0x1F4, G, 1, pg1, rsvd1, rsvd2, gmi, rsvd4),
+ GMUX(0x1F8, G, 2, pg2, rsvd1, trace, gmi, rsvd4),
+ GMUX(0x1FC, G, 3, pg3, rsvd1, trace, gmi, rsvd4),
+ GMUX(0x200, G, 4, pg4, rsvd1, tmds, gmi, spi4),
+ GMUX(0x204, G, 5, pg5, rsvd1, rsvd2, gmi, spi4),
+ GMUX(0x208, G, 6, pg6, rsvd1, rsvd2, gmi, spi4),
+ GMUX(0x20C, G, 7, pg7, rsvd1, rsvd2, gmi, spi4),
+ GMUX(0x210, H, 0, ph0, pwm0, trace, gmi, dtv),
+ GMUX(0x214, H, 1, ph1, pwm1, tmds, gmi, displaya),
+ GMUX(0x218, H, 2, ph2, pwm2, tmds, gmi, cldvfs),
+ GMUX(0x21C, H, 3, ph3, pwm3, spi4, gmi, cldvfs),
+ GMUX(0x220, H, 4, ph4, sdmmc2, rsvd2, gmi, rsvd4),
+ GMUX(0x224, H, 5, ph5, sdmmc2, rsvd2, gmi, rsvd4),
+ GMUX(0x228, H, 6, ph6, sdmmc2, trace, gmi, dtv),
+ GMUX(0x22C, H, 7, ph7, sdmmc2, trace, gmi, dtv),
+ GMUX(0x230, J, 7, pj7, uartd, rsvd2, gmi, gmi_alt),
+ GMUX(0x234, B, 0, pb0, uartd, rsvd2, gmi, rsvd4),
+ GMUX(0x238, B, 1, pb1, uartd, rsvd2, gmi, rsvd4),
+ GMUX(0x23C, K, 7, pk7, uartd, rsvd2, gmi, rsvd4),
+ GMUX(0x240, I, 0, pi0, rsvd1, rsvd2, gmi, rsvd4),
+ GMUX(0x244, I, 1, pi1, rsvd1, rsvd2, gmi, rsvd4),
+ GMUX(0x248, I, 2, pi2, sdmmc2, trace, gmi, rsvd4),
+ GMUX(0x24C, I, 4, pi4, spi4, trace, gmi, displaya),
+ GMUX(0x250, T, 5, gen2_i2c_scl_pt5, i2c2, rsvd2, gmi, rsvd4),
+ GMUX(0x254, T, 6, gen2_i2c_sda_pt6, i2c2, rsvd2, gmi, rsvd4),
+ GMUX(0x258, CC, 4, sdmmc4_clk_pcc4, sdmmc4, rsvd2, gmi, rsvd4),
+ GMUX(0x25C, T, 7, sdmmc4_cmd_pt7, sdmmc4, rsvd2, gmi, rsvd4),
+ GMUX(0x260, AA, 0, sdmmc4_dat0_paa0, sdmmc4, spi3, gmi, rsvd4),
+ GMUX(0x264, AA, 1, sdmmc4_dat1_paa1, sdmmc4, spi3, gmi, rsvd4),
+ GMUX(0x268, AA, 2, sdmmc4_dat2_paa2, sdmmc4, spi3, gmi, rsvd4),
+ GMUX(0x26C, AA, 3, sdmmc4_dat3_paa3, sdmmc4, spi3, gmi, rsvd4),
+ GMUX(0x270, AA, 4, sdmmc4_dat4_paa4, sdmmc4, spi3, gmi, rsvd4),
+ GMUX(0x274, AA, 5, sdmmc4_dat5_paa5, sdmmc4, spi3, rsvd3, rsvd4),
+ GMUX(0x278, AA, 6, sdmmc4_dat6_paa6, sdmmc4, spi3, gmi, rsvd4),
+ GMUX(0x27C, AA, 7, sdmmc4_dat7_paa7, sdmmc4, rsvd2, gmi, rsvd4),
+ GMUX(0x284, CC, 0, cam_mclk_pcc0, vi, vi_alt1, vi_alt3, sdmmc2),
+ GMUX(0x288, CC, 1, pcc1, i2s4, rsvd2, rsvd3, sdmmc2),
+ GMUX(0x28C, BB, 0, pbb0, vgp6, vimclk2, sdmmc2, vimclk2_alt),
+ GMUX(0x290, BB, 1, cam_i2c_scl_pbb1, vgp1, i2c3, rsvd3, sdmmc2),
+ GMUX(0x294, BB, 2, cam_i2c_sda_pbb2, vgp2, i2c3, rsvd3, sdmmc2),
+ GMUX(0x298, BB, 3, pbb3, vgp3, displaya, displayb, sdmmc2),
+ GMUX(0x29C, BB, 4, pbb4, vgp4, displaya, displayb, sdmmc2),
+ GMUX(0x2A0, BB, 5, pbb5, vgp5, displaya, rsvd3, sdmmc2),
+ GMUX(0x2A4, BB, 6, pbb6, i2s4, rsvd2, displayb, sdmmc2),
+ GMUX(0x2A8, BB, 7, pbb7, i2s4, rsvd2, rsvd3, sdmmc2),
+ GMUX(0x2AC, CC, 2, pcc2, i2s4, rsvd2, sdmmc3, sdmmc2),
+ FMUX(0x2B0, jtag_rtck, rtck, rsvd2, rsvd3, rsvd4),
+ GMUX(0x2B4, Z, 6, pwr_i2c_scl_pz6, i2cpwr, rsvd2, rsvd3, rsvd4),
+ GMUX(0x2B8, Z, 7, pwr_i2c_sda_pz7, i2cpwr, rsvd2, rsvd3, rsvd4),
+ GMUX(0x2BC, R, 0, kb_row0_pr0, kbc, rsvd2, rsvd3, rsvd4),
+ GMUX(0x2C0, R, 1, kb_row1_pr1, kbc, rsvd2, rsvd3, rsvd4),
+ GMUX(0x2C4, R, 2, kb_row2_pr2, kbc, rsvd2, rsvd3, rsvd4),
+ GMUX(0x2C8, R, 3, kb_row3_pr3, kbc, displaya, sys, displayb),
+ GMUX(0x2CC, R, 4, kb_row4_pr4, kbc, displaya, rsvd3, displayb),
+ GMUX(0x2D0, R, 5, kb_row5_pr5, kbc, displaya, rsvd3, displayb),
+ GMUX(0x2D4, R, 6, kb_row6_pr6, kbc, displaya, displaya_alt, displayb),
+ GMUX(0x2D8, R, 7, kb_row7_pr7, kbc, rsvd2, cldvfs, uarta),
+ GMUX(0x2DC, S, 0, kb_row8_ps0, kbc, rsvd2, cldvfs, uarta),
+ GMUX(0x2E0, S, 1, kb_row9_ps1, kbc, rsvd2, rsvd3, uarta),
+ GMUX(0x2E4, S, 2, kb_row10_ps2, kbc, rsvd2, rsvd3, uarta),
+ GMUX(0x2E8, S, 3, kb_row11_ps3, kbc, rsvd2, rsvd3, irda),
+ GMUX(0x2EC, S, 4, kb_row12_ps4, kbc, rsvd2, rsvd3, irda),
+ GMUX(0x2F0, S, 5, kb_row13_ps5, kbc, rsvd2, spi2, rsvd4),
+ GMUX(0x2F4, S, 6, kb_row14_ps6, kbc, rsvd2, spi2, rsvd4),
+ GMUX(0x2F8, S, 7, kb_row15_ps7, kbc, soc, rsvd3, rsvd4),
+ GMUX(0x2FC, Q, 0, kb_col0_pq0, kbc, rsvd2, spi2, rsvd4),
+ GMUX(0x300, Q, 1, kb_col1_pq1, kbc, rsvd2, spi2, rsvd4),
+ GMUX(0x304, Q, 2, kb_col2_pq2, kbc, rsvd2, spi2, rsvd4),
+ GMUX(0x308, Q, 3, kb_col3_pq3, kbc, displaya, pwm2, uarta),
+ GMUX(0x30C, Q, 4, kb_col4_pq4, kbc, owr, sdmmc3, uarta),
+ GMUX(0x310, Q, 5, kb_col5_pq5, kbc, rsvd2, sdmmc3, rsvd4),
+ GMUX(0x314, Q, 6, kb_col6_pq6, kbc, rsvd2, spi2, uartd),
+ GMUX(0x318, Q, 7, kb_col7_pq7, kbc, rsvd2, spi2, uartd),
+ GMUX(0x31C, A, 0, clk_32k_out_pa0, blink, soc, rsvd3, rsvd4),
+ FMUX(0x324, core_pwr_req, pwron, rsvd2, rsvd3, rsvd4),
+ FMUX(0x328, cpu_pwr_req, cpu, rsvd2, rsvd3, rsvd4),
+ FMUX(0x32C, pwr_int_n, pmi, rsvd2, rsvd3, rsvd4),
+ FMUX(0x330, clk_32k_in, clk, rsvd2, rsvd3, rsvd4),
+ FMUX(0x334, owr, owr, rsvd2, rsvd3, rsvd4),
+ GMUX(0x338, N, 0, dap1_fs_pn0, i2s0, hda, gmi, rsvd4),
+ GMUX(0x33C, N, 1, dap1_din_pn1, i2s0, hda, gmi, rsvd4),
+ GMUX(0x340, N, 2, dap1_dout_pn2, i2s0, hda, gmi, sata),
+ GMUX(0x344, N, 3, dap1_sclk_pn3, i2s0, hda, gmi, rsvd4),
+ GMUX(0x348, EE, 2, dap_mclk1_req_pee2, dap, dap1, sata, rsvd4),
+ GMUX(0x34C, W, 4, dap_mclk1_pw4, extperiph1, dap2, rsvd3, rsvd4),
+ GMUX(0x350, K, 6, spdif_in_pk6, spdif, rsvd2, rsvd3, i2c3),
+ GMUX(0x354, K, 5, spdif_out_pk5, spdif, rsvd2, rsvd3, i2c3),
+ GMUX(0x358, A, 2, dap2_fs_pa2, i2s1, hda, gmi, rsvd4),
+ GMUX(0x35C, A, 4, dap2_din_pa4, i2s1, hda, gmi, rsvd4),
+ GMUX(0x360, A, 5, dap2_dout_pa5, i2s1, hda, gmi, rsvd4),
+ GMUX(0x364, A, 3, dap2_sclk_pa3, i2s1, hda, gmi, rsvd4),
+ GMUX(0x368, X, 0, dvfs_pwm_px0, spi6, cldvfs, gmi, rsvd4),
+ GMUX(0x36C, X, 1, gpio_x1_aud_px1, spi6, rsvd2, gmi, rsvd4),
+ GMUX(0x370, X, 3, gpio_x3_aud_px3, spi6, spi1, gmi, rsvd4),
+ GMUX(0x374, X, 2, dvfs_clk_px2, spi6, cldvfs, gmi, rsvd4),
+ GMUX(0x378, X, 4, gpio_x4_aud_px4, gmi, spi1, spi2, dap2),
+ GMUX(0x37C, X, 5, gpio_x5_aud_px5, gmi, spi1, spi2, rsvd4),
+ GMUX(0x380, X, 6, gpio_x6_aud_px6, spi6, spi1, spi2, gmi),
+ GMUX(0x384, X, 7, gpio_x7_aud_px7, rsvd1, spi1, spi2, rsvd4),
+ GMUX(0x390, A, 6, sdmmc3_clk_pa6, sdmmc3, rsvd2, rsvd3, spi3),
+ GMUX(0x394, A, 7, sdmmc3_cmd_pa7, sdmmc3, pwm3, uarta, spi3),
+ GMUX(0x398, B, 7, sdmmc3_dat0_pb7, sdmmc3, rsvd2, rsvd3, spi3),
+ GMUX(0x39C, B, 6, sdmmc3_dat1_pb6, sdmmc3, pwm2, uarta, spi3),
+ GMUX(0x3A0, B, 5, sdmmc3_dat2_pb5, sdmmc3, pwm1, displaya, spi3),
+ GMUX(0x3A4, B, 4, sdmmc3_dat3_pb4, sdmmc3, pwm0, displayb, spi3),
+ GMUX(0x3BC, DD, 1, pex_l0_rst_n_pdd1, pe0, rsvd2, rsvd3, rsvd4),
+ GMUX(0x3C0, DD, 2, pex_l0_clkreq_n_pdd2, pe0, rsvd2, rsvd3, rsvd4),
+ GMUX(0x3C4, DD, 3, pex_wake_n_pdd3, pe, rsvd2, rsvd3, rsvd4),
+ GMUX(0x3CC, DD, 5, pex_l1_rst_n_pdd5, pe1, rsvd2, rsvd3, rsvd4),
+ GMUX(0x3D0, DD, 6, pex_l1_clkreq_n_pdd6, pe1, rsvd2, rsvd3, rsvd4),
+ GMUX(0x3E0, EE, 3, hdmi_cec_pee3, cec, rsvd2, rsvd3, rsvd4),
+ GMUX(0x3E4, V, 3, sdmmc1_wp_n_pv3, sdmmc1, clk12, spi4, uarta),
+ GMUX(0x3E8, V, 2, sdmmc3_cd_n_pv2, sdmmc3, owr, rsvd3, rsvd4),
+ GMUX(0x3EC, W, 2, gpio_w2_aud_pw2, spi6, rsvd2, spi2, i2c1),
+ GMUX(0x3F0, W, 3, gpio_w3_aud_pw3, spi6, spi1, spi2, i2c1),
+ GMUX(0x3F4, N, 4, usb_vbus_en0_pn4, usb, rsvd2, rsvd3, rsvd4),
+ GMUX(0x3F8, N, 5, usb_vbus_en1_pn5, usb, rsvd2, rsvd3, rsvd4),
+ GMUX(0x3FC, EE, 5, sdmmc3_clk_lb_in_pee5, sdmmc3, rsvd2, rsvd3, rsvd4),
+ GMUX(0x400, EE, 4, sdmmc3_clk_lb_out_pee4, sdmmc3, rsvd2, rsvd3, rsvd4),
+ FMUX(0x404, gmi_clk_lb, sdmmc2, rsvd2, gmi, rsvd4),
+ FMUX(0x408, reset_out_n, rsvd1, rsvd2, rsvd3, reset_out_n),
+ GMUX(0x40C, T, 0, kb_row16_pt0, kbc, rsvd2, rsvd3, uartc),
+ GMUX(0x410, T, 1, kb_row17_pt1, kbc, rsvd2, rsvd3, uartc),
+ GMUX(0x414, FF, 1, usb_vbus_en2_pff1, usb, rsvd2, rsvd3, rsvd4),
+ GMUX(0x418, FF, 2, pff2, sata, rsvd2, rsvd3, rsvd4),
+ GMUX(0x430, FF, 0, dp_hpd_pff0, dp, rsvd2, rsvd3, rsvd4),
+};
+
+struct tegra_grp {
+ char *name;
+ bus_size_t reg;
+ int drvdn_shift;
+ int drvdn_mask;
+ int drvup_shift;
+ int drvup_mask;
+};
+
+#define GRP(r, nm, dn_s, dn_w, up_s, up_w) \
+{ \
+ .name = #nm, \
+ .reg = r - 0x868, \
+ .drvdn_shift = dn_s, \
+ .drvdn_mask = (1 << dn_w) - 1, \
+ .drvup_shift = up_s, \
+ .drvup_mask = (1 << dn_w) - 1, \
+}
+
+/* Use register offsets from TRM */
+static const struct tegra_grp pin_grp_tbl[] = {
+ GRP(0x868, ao1, 12, 5, 20, 5),
+ GRP(0x86C, ao2, 12, 5, 20, 5),
+ GRP(0x870, at1, 12, 7, 20, 7),
+ GRP(0x874, at2, 12, 7, 20, 7),
+ GRP(0x878, at3, 12, 7, 20, 7),
+ GRP(0x87C, at4, 12, 7, 20, 7),
+ GRP(0x880, at5, 14, 5, 19, 5),
+ GRP(0x884, cdev1, 12, 5, 20, 5),
+ GRP(0x888, cdev2, 12, 5, 20, 5),
+ GRP(0x890, dap1, 12, 5, 20, 5),
+ GRP(0x894, dap2, 12, 5, 20, 5),
+ GRP(0x898, dap3, 12, 5, 20, 5),
+ GRP(0x89C, dap4, 12, 5, 20, 5),
+ GRP(0x8A0, dbg, 12, 5, 20, 5),
+ GRP(0x8B0, sdio3, 12, 7, 20, 7),
+ GRP(0x8B4, spi, 12, 5, 20, 5),
+ GRP(0x8B8, uaa, 12, 5, 20, 5),
+ GRP(0x8BC, uab, 12, 5, 20, 5),
+ GRP(0x8C0, uart2, 12, 5, 20, 5),
+ GRP(0x8C4, uart3, 12, 5, 20, 5),
+ GRP(0x8EC, sdio1, 12, 7, 20, 7),
+ GRP(0x8FC, ddc, 12, 5, 20, 5),
+ GRP(0x900, gma, 14, 5, 20, 5),
+ GRP(0x910, gme, 14, 5, 19, 5),
+ GRP(0x914, gmf, 14, 5, 19, 5),
+ GRP(0x918, gmg, 14, 5, 19, 5),
+ GRP(0x91C, gmh, 14, 5, 19, 5),
+ GRP(0x920, owr, 12, 5, 20, 5),
+ GRP(0x924, uda, 12, 5, 20, 5),
+ GRP(0x928, gpv, 12, 5, 20, 5),
+ GRP(0x92C, dev3, 12, 5, 20, 5),
+ GRP(0x938, cec, 12, 5, 20, 5),
+ GRP(0x994, at6, 12, 7, 20, 7),
+ GRP(0x998, dap5, 12, 5, 20, 5),
+ GRP(0x99C, usb_vbus_en, 12, 5, 20, 5),
+ GRP(0x9A8, ao3, 12, 5, -1, 0),
+ GRP(0x9B0, ao0, 12, 5, 20, 5),
+ GRP(0x9B4, hv0, 12, 5, -1, 0),
+ GRP(0x9C4, sdio4, 12, 5, 20, 5),
+ GRP(0x9C8, ao4, 12, 7, 20, 7),
+};
+
+static const struct tegra_grp *
+pinmux_search_grp(char *grp_name)
+{
+ int i;
+
+ for (i = 0; i < nitems(pin_grp_tbl); i++) {
+ if (strcmp(grp_name, pin_grp_tbl[i].name) == 0)
+ return (&pin_grp_tbl[i]);
+ }
+ return (NULL);
+}
+
+static const struct tegra_mux *
+pinmux_search_mux(char *pin_name)
+{
+ int i;
+
+ for (i = 0; i < nitems(pin_mux_tbl); i++) {
+ if (strcmp(pin_name, pin_mux_tbl[i].name) == 0)
+ return (&pin_mux_tbl[i]);
+ }
+ return (NULL);
+}
+
+static int
+pinmux_mux_function(const struct tegra_mux *mux, char *fnc_name)
+{
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ if (strcmp(fnc_name, mux->functions[i]) == 0)
+ return (i);
+ }
+ return (-1);
+}
+
+static int
+pinmux_config_mux(struct pinmux_softc *sc, char *pin_name,
+ const struct tegra_mux *mux, struct pincfg *cfg)
+{
+ int tmp;
+ uint32_t reg;
+
+ reg = bus_read_4(sc->mux_mem_res, mux->reg);
+
+ if (cfg->function != NULL) {
+ tmp = pinmux_mux_function(mux, cfg->function);
+ if (tmp == -1) {
+ device_printf(sc->dev,
+ "Unknown function %s for pin %s\n", cfg->function,
+ pin_name);
+ return (ENXIO);
+ }
+ reg &= ~(TEGRA_MUX_FUNCTION_MASK << TEGRA_MUX_FUNCTION_SHIFT);
+ reg |= (tmp & TEGRA_MUX_FUNCTION_MASK) <<
+ TEGRA_MUX_FUNCTION_SHIFT;
+ }
+ if (cfg->params[PROP_ID_PULL] != -1) {
+ reg &= ~(TEGRA_MUX_PUPD_MASK << TEGRA_MUX_PUPD_SHIFT);
+ reg |= (cfg->params[PROP_ID_PULL] & TEGRA_MUX_PUPD_MASK) <<
+ TEGRA_MUX_PUPD_SHIFT;
+ }
+ if (cfg->params[PROP_ID_TRISTATE] != -1) {
+ reg &= ~(1 << TEGRA_MUX_TRISTATE_SHIFT);
+ reg |= (cfg->params[PROP_ID_TRISTATE] & 1) <<
+ TEGRA_MUX_TRISTATE_SHIFT;
+ }
+ if (cfg->params[TEGRA_MUX_ENABLE_INPUT_SHIFT] != -1) {
+ reg &= ~(1 << TEGRA_MUX_ENABLE_INPUT_SHIFT);
+ reg |= (cfg->params[TEGRA_MUX_ENABLE_INPUT_SHIFT] & 1) <<
+ TEGRA_MUX_ENABLE_INPUT_SHIFT;
+ }
+ if (cfg->params[PROP_ID_ENABLE_INPUT] != -1) {
+ reg &= ~(1 << TEGRA_MUX_ENABLE_INPUT_SHIFT);
+ reg |= (cfg->params[PROP_ID_ENABLE_INPUT] & 1) <<
+ TEGRA_MUX_ENABLE_INPUT_SHIFT;
+ }
+ if (cfg->params[PROP_ID_ENABLE_INPUT] != -1) {
+ reg &= ~(1 << TEGRA_MUX_ENABLE_INPUT_SHIFT);
+ reg |= (cfg->params[PROP_ID_OPEN_DRAIN] & 1) <<
+ TEGRA_MUX_ENABLE_INPUT_SHIFT;
+ }
+ if (cfg->params[PROP_ID_LOCK] != -1) {
+ reg &= ~(1 << TEGRA_MUX_LOCK_SHIFT);
+ reg |= (cfg->params[PROP_ID_LOCK] & 1) <<
+ TEGRA_MUX_LOCK_SHIFT;
+ }
+ if (cfg->params[PROP_ID_IORESET] != -1) {
+ reg &= ~(1 << TEGRA_MUX_IORESET_SHIFT);
+ reg |= (cfg->params[PROP_ID_IORESET] & 1) <<
+ TEGRA_MUX_IORESET_SHIFT;
+ }
+ if (cfg->params[PROP_ID_RCV_SEL] != -1) {
+ reg &= ~(1 << TEGRA_MUX_RCV_SEL_SHIFT);
+ reg |= (cfg->params[PROP_ID_RCV_SEL] & 1) <<
+ TEGRA_MUX_RCV_SEL_SHIFT;
+ }
+ bus_write_4(sc->mux_mem_res, mux->reg, reg);
+ return (0);
+}
+
+static int
+pinmux_config_grp(struct pinmux_softc *sc, char *grp_name,
+ const struct tegra_grp *grp, struct pincfg *cfg)
+{
+ uint32_t reg;
+
+ reg = bus_read_4(sc->pad_mem_res, grp->reg);
+
+ if (cfg->params[PROP_ID_HIGH_SPEED_MODE] != -1) {
+ reg &= ~(1 << TEGRA_GRP_HSM_SHIFT);
+ reg |= (cfg->params[PROP_ID_HIGH_SPEED_MODE] & 1) <<
+ TEGRA_GRP_HSM_SHIFT;
+ }
+ if (cfg->params[PROP_ID_SCHMITT] != -1) {
+ reg &= ~(1 << TEGRA_GRP_SCHMT_SHIFT);
+ reg |= (cfg->params[PROP_ID_SCHMITT] & 1) <<
+ TEGRA_GRP_SCHMT_SHIFT;
+ }
+ if (cfg->params[PROP_ID_DRIVE_TYPE] != -1) {
+ reg &= ~(TEGRA_GRP_DRV_TYPE_MASK << TEGRA_GRP_DRV_TYPE_SHIFT);
+ reg |= (cfg->params[PROP_ID_DRIVE_TYPE] &
+ TEGRA_GRP_DRV_TYPE_MASK) << TEGRA_GRP_DRV_TYPE_SHIFT;
+ }
+ if (cfg->params[PROP_ID_SLEW_RATE_RISING] != -1) {
+ reg &= ~(TEGRA_GRP_DRV_DRVDN_SLWR_MASK <<
+ TEGRA_GRP_DRV_DRVDN_SLWR_SHIFT);
+ reg |= (cfg->params[PROP_ID_SLEW_RATE_RISING] &
+ TEGRA_GRP_DRV_DRVDN_SLWR_MASK) <<
+ TEGRA_GRP_DRV_DRVDN_SLWR_SHIFT;
+ }
+ if (cfg->params[PROP_ID_SLEW_RATE_FALLING] != -1) {
+ reg &= ~(TEGRA_GRP_DRV_DRVUP_SLWF_MASK <<
+ TEGRA_GRP_DRV_DRVUP_SLWF_SHIFT);
+ reg |= (cfg->params[PROP_ID_SLEW_RATE_FALLING] &
+ TEGRA_GRP_DRV_DRVUP_SLWF_MASK) <<
+ TEGRA_GRP_DRV_DRVUP_SLWF_SHIFT;
+ }
+ if ((cfg->params[PROP_ID_DRIVE_DOWN_STRENGTH] != -1) &&
+ (grp->drvdn_mask != -1)) {
+ reg &= ~(grp->drvdn_shift << grp->drvdn_mask);
+ reg |= (cfg->params[PROP_ID_DRIVE_DOWN_STRENGTH] &
+ grp->drvdn_mask) << grp->drvdn_shift;
+ }
+ if ((cfg->params[PROP_ID_DRIVE_UP_STRENGTH] != -1) &&
+ (grp->drvup_mask != -1)) {
+ reg &= ~(grp->drvup_shift << grp->drvup_mask);
+ reg |= (cfg->params[PROP_ID_DRIVE_UP_STRENGTH] &
+ grp->drvup_mask) << grp->drvup_shift;
+ }
+ bus_write_4(sc->pad_mem_res, grp->reg, reg);
+ return (0);
+}
+
+static int
+pinmux_config_node(struct pinmux_softc *sc, char *pin_name, struct pincfg *cfg)
+{
+ const struct tegra_mux *mux;
+ const struct tegra_grp *grp;
+ uint32_t reg;
+ int rv;
+
+ /* Handle MIPI special case first */
+ if (strcmp(pin_name, "dsi_b") == 0) {
+ if (cfg->function == NULL) {
+ /* nothing to set */
+ return (0);
+ }
+ reg = bus_read_4(sc->mipi_mem_res, 0); /* register 0x820 */
+ if (strcmp(cfg->function, "csi") == 0)
+ reg &= ~(1 << 1);
+ else if (strcmp(cfg->function, "dsi_b") == 0)
+ reg |= (1 << 1);
+ bus_write_4(sc->mipi_mem_res, 0, reg); /* register 0x820 */
+ }
+
+ /* Handle pin muxes */
+ mux = pinmux_search_mux(pin_name);
+ if (mux != NULL) {
+ if (mux->gpio_num != -1) {
+ /* XXXX TODO: Reserve gpio here */
+ }
+ rv = pinmux_config_mux(sc, pin_name, mux, cfg);
+ return (rv);
+ }
+
+ /* Handle pin groups */
+ grp = pinmux_search_grp(pin_name);
+ if (grp != NULL) {
+ rv = pinmux_config_grp(sc, pin_name, grp, cfg);
+ return (rv);
+ }
+
+ device_printf(sc->dev, "Unknown pin: %s\n", pin_name);
+ return (ENXIO);
+}
+
+static int
+pinmux_read_node(struct pinmux_softc *sc, phandle_t node, struct pincfg *cfg,
+ char **pins, int *lpins)
+{
+ int rv, i;
+
+ *lpins = OF_getprop_alloc(node, "nvidia,pins", (void **)pins);
+ if (*lpins <= 0)
+ return (ENOENT);
+
+ /* Read function (mux) settings. */
+ rv = OF_getprop_alloc(node, "nvidia,function",
+ (void **)&cfg->function);
+ if (rv <= 0)
+ cfg->function = NULL;
+
+ /* Read numeric properties. */
+ for (i = 0; i < PROP_ID_MAX_ID; i++) {
+ rv = OF_getencprop(node, prop_names[i].name, &cfg->params[i],
+ sizeof(cfg->params[i]));
+ if (rv <= 0)
+ cfg->params[i] = -1;
+ }
+ return (0);
+}
+
+static int
+pinmux_process_node(struct pinmux_softc *sc, phandle_t node)
+{
+ struct pincfg cfg;
+ char *pins, *pname;
+ int i, len, lpins, rv;
+
+ rv = pinmux_read_node(sc, node, &cfg, &pins, &lpins);
+ if (rv != 0)
+ return (rv);
+
+ len = 0;
+ pname = pins;
+ do {
+ i = strlen(pname) + 1;
+ rv = pinmux_config_node(sc, pname, &cfg);
+ if (rv != 0)
+ device_printf(sc->dev,
+ "Cannot configure pin: %s: %d\n", pname, rv);
+
+ len += i;
+ pname += i;
+ } while (len < lpins);
+
+ if (pins != NULL)
+ OF_prop_free(pins);
+ if (cfg.function != NULL)
+ OF_prop_free(cfg.function);
+ return (rv);
+}
+
+static int pinmux_configure(device_t dev, phandle_t cfgxref)
+{
+ struct pinmux_softc *sc;
+ phandle_t node, cfgnode;
+ int rv;
+
+ sc = device_get_softc(dev);
+ cfgnode = OF_node_from_xref(cfgxref);
+
+ for (node = OF_child(cfgnode); node != 0; node = OF_peer(node)) {
+ if (!ofw_bus_node_status_okay(node))
+ continue;
+ rv = pinmux_process_node(sc, node);
+ }
+ return (0);
+}
+
+static int
+pinmux_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+ return (ENXIO);
+
+ device_set_desc(dev, "Tegra pin configuration");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+pinmux_detach(device_t dev)
+{
+
+ /* This device is always present. */
+ return (EBUSY);
+}
+
+static int
+pinmux_attach(device_t dev)
+{
+ struct pinmux_softc * sc;
+ int rid;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ rid = 0;
+ sc->pad_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->pad_mem_res == NULL) {
+ device_printf(dev, "Cannot allocate memory resources\n");
+ return (ENXIO);
+ }
+
+ rid = 1;
+ sc->mux_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->mux_mem_res == NULL) {
+ device_printf(dev, "Cannot allocate memory resources\n");
+ return (ENXIO);
+ }
+
+ rid = 2;
+ sc->mipi_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->mipi_mem_res == NULL) {
+ device_printf(dev, "Cannot allocate memory resources\n");
+ return (ENXIO);
+ }
+
+ /* Register as a pinctrl device and process default configuration */
+ fdt_pinctrl_register(dev, NULL);
+ fdt_pinctrl_configure_by_name(dev, "boot");
+
+ return (0);
+}
+
+static device_method_t tegra_pinmux_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, pinmux_probe),
+ DEVMETHOD(device_attach, pinmux_attach),
+ DEVMETHOD(device_detach, pinmux_detach),
+
+ /* fdt_pinctrl interface */
+ DEVMETHOD(fdt_pinctrl_configure,pinmux_configure),
+
+ DEVMETHOD_END
+};
+
+static devclass_t tegra_pinmux_devclass;
+static DEFINE_CLASS_0(pinmux, tegra_pinmux_driver, tegra_pinmux_methods,
+ sizeof(struct pinmux_softc));
+EARLY_DRIVER_MODULE(tegra_pinmux, simplebus, tegra_pinmux_driver,
+ tegra_pinmux_devclass, NULL, NULL, 71);
diff --git a/sys/arm/nvidia/tegra_pmc.h b/sys/arm/nvidia/tegra_pmc.h
new file mode 100644
index 000000000000..4fe7299fb878
--- /dev/null
+++ b/sys/arm/nvidia/tegra_pmc.h
@@ -0,0 +1,119 @@
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _TEGRA_PMC_H_
+#define _TEGRA_PMC_H_
+
+enum tegra_suspend_mode {
+ TEGRA_SUSPEND_NONE = 0,
+ TEGRA_SUSPEND_LP2, /* CPU voltage off */
+ TEGRA_SUSPEND_LP1, /* CPU voltage off, DRAM self-refresh */
+ TEGRA_SUSPEND_LP0, /* CPU + core voltage off, DRAM self-refresh */
+};
+
+/* PARTIDs for powergate */
+enum tegra_powergate_id {
+ TEGRA_POWERGATE_CRAIL = 0,
+ TEGRA_POWERGATE_TD = 1, /* Tegra124 only */
+ TEGRA_POWERGATE_VE = 2,
+ TEGRA_POWERGATE_PCX = 3,
+ TEGRA_POWERGATE_VDE = 4, /* Tegra124 only */
+ TEGRA_POWERGATE_L2C = 5, /* Tegra124 only */
+ TEGRA_POWERGATE_MPE = 6,
+ TEGRA_POWERGATE_HEG = 7, /* Tegra124 only */
+ TEGRA_POWERGATE_SAX = 8,
+ TEGRA_POWERGATE_CE1 = 9,
+ TEGRA_POWERGATE_CE2 = 10,
+ TEGRA_POWERGATE_CE3 = 11,
+ TEGRA_POWERGATE_CELP = 12, /* Tegra124 only */
+ /* */
+ TEGRA_POWERGATE_CE0 = 14,
+ TEGRA_POWERGATE_C0NC = 15,
+ TEGRA_POWERGATE_C1NC = 16,
+ TEGRA_POWERGATE_SOR = 17,
+ TEGRA_POWERGATE_DIS = 18,
+ TEGRA_POWERGATE_DISB = 19,
+ TEGRA_POWERGATE_XUSBA = 20,
+ TEGRA_POWERGATE_XUSBB = 21,
+ TEGRA_POWERGATE_XUSBC = 22,
+ TEGRA_POWERGATE_VIC = 23,
+ TEGRA_POWERGATE_IRAM = 24,
+ TEGRA_POWERGATE_NVDEC = 25, /* Tegra210 only */
+ TEGRA_POWERGATE_NVJPG = 26, /* Tegra210 only */
+ TEGRA_POWERGATE_AUD = 27, /* Tegra210 only */
+ TEGRA_POWERGATE_DFD = 28, /* Tegra210 only */
+ TEGRA_POWERGATE_VE2 = 29, /* Tegra210 only */
+ /* */
+ TEGRA_POWERGATE_3D = 32
+};
+
+/* PARTIDs for power rails */
+enum tegra_powerrail_id {
+ TEGRA_IO_RAIL_CSIA = 0,
+ TEGRA_IO_RAIL_CSIB = 1,
+ TEGRA_IO_RAIL_DSI = 2,
+ TEGRA_IO_RAIL_MIPI_BIAS = 3,
+ TEGRA_IO_RAIL_PEX_BIAS = 4,
+ TEGRA_IO_RAIL_PEX_CLK1 = 5,
+ TEGRA_IO_RAIL_PEX_CLK2 = 6,
+ TEGRA_IO_RAIL_USB0 = 9,
+ TEGRA_IO_RAIL_USB1 = 10,
+ TEGRA_IO_RAIL_USB2 = 11,
+ TEGRA_IO_RAIL_USB_BIAS = 12,
+ TEGRA_IO_RAIL_NAND = 13,
+ TEGRA_IO_RAIL_UART = 14,
+ TEGRA_IO_RAIL_BB = 15,
+ TEGRA_IO_RAIL_AUDIO = 17,
+ TEGRA_IO_RAIL_HSIC = 19,
+ TEGRA_IO_RAIL_COMP = 22,
+ TEGRA_IO_RAIL_HDMI = 28,
+ TEGRA_IO_RAIL_PEX_CNTRL = 32,
+ TEGRA_IO_RAIL_SDMMC1 = 33,
+ TEGRA_IO_RAIL_SDMMC3 = 34,
+ TEGRA_IO_RAIL_SDMMC4 = 35,
+ TEGRA_IO_RAIL_CAM = 36,
+ TEGRA_IO_RAIL_RES = 37,
+ TEGRA_IO_RAIL_HV = 38,
+ TEGRA_IO_RAIL_DSIB = 39,
+ TEGRA_IO_RAIL_DSIC = 40,
+ TEGRA_IO_RAIL_DSID = 41,
+ TEGRA_IO_RAIL_CSIE = 44,
+ TEGRA_IO_RAIL_LVDS = 57,
+ TEGRA_IO_RAIL_SYS_DDC = 58,
+};
+
+int tegra_powergate_is_powered(enum tegra_powergate_id id);
+int tegra_powergate_power_on(enum tegra_powergate_id id);
+int tegra_powergate_power_off(enum tegra_powergate_id id);
+int tegra_powergate_remove_clamping(enum tegra_powergate_id id);
+int tegra_powergate_sequence_power_up(enum tegra_powergate_id id,
+ clk_t clk, hwreset_t rst);
+int tegra_io_rail_power_on(int tegra_powerrail_id);
+int tegra_io_rail_power_off(int tegra_powerrail_id);
+
+#endif /*_TEGRA_PMC_H_*/ \ No newline at end of file
diff --git a/sys/arm/nvidia/tegra_rtc.c b/sys/arm/nvidia/tegra_rtc.c
new file mode 100644
index 000000000000..070270367e1d
--- /dev/null
+++ b/sys/arm/nvidia/tegra_rtc.c
@@ -0,0 +1,301 @@
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * RTC driver for Tegra SoCs.
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/clock.h>
+#include <sys/kernel.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/module.h>
+#include <sys/resource.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "clock_if.h"
+
+#define RTC_CONTROL 0x00
+#define RTC_BUSY 0x04
+#define RTC_BUSY_STATUS (1 << 0)
+#define RTC_SECONDS 0x08
+#define RTC_SHADOW_SECONDS 0x0c
+#define RTC_MILLI_SECONDS 0x10
+#define RTC_SECONDS_ALARM0 0x14
+#define RTC_SECONDS_ALARM1 0x18
+#define RTC_MILLI_SECONDS_ALARM 0x1c
+#define RTC_SECONDS_COUNTDOWN_ALARM 0x20
+#define RTC_MILLI_SECONDS_COUNTDOW_ALARM 0x24
+#define RTC_INTR_MASK 0x28
+#define RTC_INTR_MSEC_CDN_ALARM (1 << 4)
+#define RTC_INTR_SEC_CDN_ALARM (1 << 3)
+#define RTC_INTR_MSEC_ALARM (1 << 2)
+#define RTC_INTR_SEC_ALARM1 (1 << 1)
+#define RTC_INTR_SEC_ALARM0 (1 << 0)
+
+#define RTC_INTR_STATUS 0x2c
+#define RTC_INTR_SOURCE 0x30
+#define RTC_INTR_SET 0x34
+#define RTC_CORRECTION_FACTOR 0x38
+
+#define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v))
+#define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r))
+
+#define LOCK(_sc) mtx_lock(&(_sc)->mtx)
+#define UNLOCK(_sc) mtx_unlock(&(_sc)->mtx)
+#define SLEEP(_sc, timeout) \
+ mtx_sleep(sc, &sc->mtx, 0, "rtcwait", timeout);
+#define LOCK_INIT(_sc) \
+ mtx_init(&_sc->mtx, device_get_nameunit(_sc->dev), "tegra_rtc", MTX_DEF)
+#define LOCK_DESTROY(_sc) mtx_destroy(&_sc->mtx)
+#define ASSERT_LOCKED(_sc) mtx_assert(&_sc->mtx, MA_OWNED)
+#define ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED)
+
+static struct ofw_compat_data compat_data[] = {
+ {"nvidia,tegra124-rtc", 1},
+ {NULL, 0}
+};
+
+struct tegra_rtc_softc {
+ device_t dev;
+ struct mtx mtx;
+
+ struct resource *mem_res;
+ struct resource *irq_res;
+ void *irq_h;
+
+ clk_t clk;
+ uint32_t core_freq;
+};
+
+static void
+tegra_rtc_wait(struct tegra_rtc_softc *sc)
+{
+ int timeout;
+
+ for (timeout = 500; timeout >0; timeout--) {
+ if ((RD4(sc, RTC_BUSY) & RTC_BUSY_STATUS) == 0)
+ break;
+ DELAY(1);
+ }
+ if (timeout <= 0)
+ device_printf(sc->dev, "Device busy timeouted\n");
+
+}
+
+/*
+ * Get the time of day clock and return it in ts.
+ * Return 0 on success, an error number otherwise.
+ */
+static int
+tegra_rtc_gettime(device_t dev, struct timespec *ts)
+{
+ struct tegra_rtc_softc *sc;
+ struct timeval tv;
+ uint32_t msec, sec;
+
+ sc = device_get_softc(dev);
+
+ LOCK(sc);
+ msec = RD4(sc, RTC_MILLI_SECONDS);
+ sec = RD4(sc, RTC_SHADOW_SECONDS);
+ UNLOCK(sc);
+ tv.tv_sec = sec;
+ tv.tv_usec = msec * 1000;
+ TIMEVAL_TO_TIMESPEC(&tv, ts);
+ return (0);
+}
+
+static int
+tegra_rtc_settime(device_t dev, struct timespec *ts)
+{
+ struct tegra_rtc_softc *sc;
+ struct timeval tv;
+
+ sc = device_get_softc(dev);
+
+ LOCK(sc);
+ TIMESPEC_TO_TIMEVAL(&tv, ts);
+ tegra_rtc_wait(sc);
+ WR4(sc, RTC_SECONDS, tv.tv_sec);
+ UNLOCK(sc);
+
+ return (0);
+}
+
+static void
+tegra_rtc_intr(void *arg)
+{
+ struct tegra_rtc_softc *sc;
+ uint32_t status;
+
+ sc = (struct tegra_rtc_softc *)arg;
+ LOCK(sc);
+ status = RD4(sc, RTC_INTR_STATUS);
+ WR4(sc, RTC_INTR_STATUS, status);
+ UNLOCK(sc);
+}
+
+static int
+tegra_rtc_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+tegra_rtc_attach(device_t dev)
+{
+ int rv, rid;
+ struct tegra_rtc_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ LOCK_INIT(sc);
+
+ /* Get the memory resource for the register mapping. */
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "Cannot map registers.\n");
+ rv = ENXIO;
+ goto fail;
+ }
+
+ /* Allocate our IRQ resource. */
+ rid = 0;
+ sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (sc->irq_res == NULL) {
+ device_printf(dev, "Cannot allocate interrupt.\n");
+ rv = ENXIO;
+ goto fail;
+ }
+
+ /* OFW resources. */
+ rv = clk_get_by_ofw_index(dev, 0, 0, &sc->clk);
+ if (rv != 0) {
+ device_printf(dev, "Cannot get i2c clock: %d\n", rv);
+ goto fail;
+ }
+ rv = clk_enable(sc->clk);
+ if (rv != 0) {
+ device_printf(dev, "Cannot enable clock: %d\n", rv);
+ goto fail;
+ }
+
+ /* Init hardware. */
+ WR4(sc, RTC_SECONDS_ALARM0, 0);
+ WR4(sc, RTC_SECONDS_ALARM1, 0);
+ WR4(sc, RTC_INTR_STATUS, 0xFFFFFFFF);
+ WR4(sc, RTC_INTR_MASK, 0);
+
+ /* Setup interrupt */
+ rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+ NULL, tegra_rtc_intr, sc, &sc->irq_h);
+ if (rv) {
+ device_printf(dev, "Cannot setup interrupt.\n");
+ goto fail;
+ }
+
+ /*
+ * Register as a time of day clock with 1-second resolution.
+ *
+ * XXXX Not yet, we don't have support for multiple RTCs
+ */
+ /* clock_register(dev, 1000000); */
+
+ return (bus_generic_attach(dev));
+
+fail:
+ if (sc->clk != NULL)
+ clk_release(sc->clk);
+ if (sc->irq_h != NULL)
+ bus_teardown_intr(dev, sc->irq_res, sc->irq_h);
+ if (sc->irq_res != NULL)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+ if (sc->mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+ LOCK_DESTROY(sc);
+
+ return (rv);
+}
+
+static int
+tegra_rtc_detach(device_t dev)
+{
+ struct tegra_rtc_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (sc->irq_h != NULL)
+ bus_teardown_intr(dev, sc->irq_res, sc->irq_h);
+ if (sc->irq_res != NULL)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+ if (sc->mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+
+ LOCK_DESTROY(sc);
+ return (bus_generic_detach(dev));
+}
+
+static device_method_t tegra_rtc_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, tegra_rtc_probe),
+ DEVMETHOD(device_attach, tegra_rtc_attach),
+ DEVMETHOD(device_detach, tegra_rtc_detach),
+
+ /* clock interface */
+ DEVMETHOD(clock_gettime, tegra_rtc_gettime),
+ DEVMETHOD(clock_settime, tegra_rtc_settime),
+
+ DEVMETHOD_END
+};
+
+static devclass_t tegra_rtc_devclass;
+static DEFINE_CLASS_0(rtc, tegra_rtc_driver, tegra_rtc_methods,
+ sizeof(struct tegra_rtc_softc));
+DRIVER_MODULE(tegra_rtc, simplebus, tegra_rtc_driver, tegra_rtc_devclass,
+ NULL, NULL);
diff --git a/sys/arm/nvidia/tegra_sdhci.c b/sys/arm/nvidia/tegra_sdhci.c
new file mode 100644
index 000000000000..9d7fbb0ddb1a
--- /dev/null
+++ b/sys/arm/nvidia/tegra_sdhci.c
@@ -0,0 +1,476 @@
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * SDHCI driver glue for NVIDIA Tegra family
+ *
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/types.h>
+#include <sys/bus.h>
+#include <sys/gpio.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+#include <sys/taskqueue.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/intr.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/gpio/gpiobusvar.h>
+#include <dev/mmc/bridge.h>
+#include <dev/mmc/mmcbrvar.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/sdhci/sdhci.h>
+#include <dev/sdhci/sdhci_fdt_gpio.h>
+
+#include "sdhci_if.h"
+
+#include "opt_mmccam.h"
+
+/* Tegra SDHOST controller vendor register definitions */
+#define SDMMC_VENDOR_CLOCK_CNTRL 0x100
+#define VENDOR_CLOCK_CNTRL_CLK_SHIFT 8
+#define VENDOR_CLOCK_CNTRL_CLK_MASK 0xFF
+#define SDMMC_VENDOR_SYS_SW_CNTRL 0x104
+#define SDMMC_VENDOR_CAP_OVERRIDES 0x10C
+#define SDMMC_VENDOR_BOOT_CNTRL 0x110
+#define SDMMC_VENDOR_BOOT_ACK_TIMEOUT 0x114
+#define SDMMC_VENDOR_BOOT_DAT_TIMEOUT 0x118
+#define SDMMC_VENDOR_DEBOUNCE_COUNT 0x11C
+#define SDMMC_VENDOR_MISC_CNTRL 0x120
+#define VENDOR_MISC_CTRL_ENABLE_SDR104 0x8
+#define VENDOR_MISC_CTRL_ENABLE_SDR50 0x10
+#define VENDOR_MISC_CTRL_ENABLE_SDHCI_SPEC_300 0x20
+#define VENDOR_MISC_CTRL_ENABLE_DDR50 0x200
+#define SDMMC_MAX_CURRENT_OVERRIDE 0x124
+#define SDMMC_MAX_CURRENT_OVERRIDE_HI 0x128
+#define SDMMC_VENDOR_CLK_GATE_HYSTERESIS_COUNT 0x1D0
+#define SDMMC_VENDOR_PHWRESET_VAL0 0x1D4
+#define SDMMC_VENDOR_PHWRESET_VAL1 0x1D8
+#define SDMMC_VENDOR_PHWRESET_VAL2 0x1DC
+#define SDMMC_SDMEMCOMPPADCTRL_0 0x1E0
+#define SDMMC_AUTO_CAL_CONFIG 0x1E4
+#define SDMMC_AUTO_CAL_INTERVAL 0x1E8
+#define SDMMC_AUTO_CAL_STATUS 0x1EC
+#define SDMMC_SDMMC_MCCIF_FIFOCTRL 0x1F4
+#define SDMMC_TIMEOUT_WCOAL_SDMMC 0x1F8
+
+/* Compatible devices. */
+static struct ofw_compat_data compat_data[] = {
+ {"nvidia,tegra124-sdhci", 1},
+ {"nvidia,tegra210-sdhci", 1},
+ {NULL, 0},
+};
+
+struct tegra_sdhci_softc {
+ device_t dev;
+ struct resource * mem_res;
+ struct resource * irq_res;
+ void * intr_cookie;
+ u_int quirks; /* Chip specific quirks */
+ u_int caps; /* If we override SDHCI_CAPABILITIES */
+ uint32_t max_clk; /* Max possible freq */
+ clk_t clk;
+ hwreset_t reset;
+ gpio_pin_t gpio_power;
+ struct sdhci_fdt_gpio *gpio;
+
+ int force_card_present;
+ struct sdhci_slot slot;
+
+};
+
+static inline uint32_t
+RD4(struct tegra_sdhci_softc *sc, bus_size_t off)
+{
+
+ return (bus_read_4(sc->mem_res, off));
+}
+
+static uint8_t
+tegra_sdhci_read_1(device_t dev, struct sdhci_slot *slot, bus_size_t off)
+{
+ struct tegra_sdhci_softc *sc;
+
+ sc = device_get_softc(dev);
+ return (bus_read_1(sc->mem_res, off));
+}
+
+static uint16_t
+tegra_sdhci_read_2(device_t dev, struct sdhci_slot *slot, bus_size_t off)
+{
+ struct tegra_sdhci_softc *sc;
+
+ sc = device_get_softc(dev);
+ return (bus_read_2(sc->mem_res, off));
+}
+
+static uint32_t
+tegra_sdhci_read_4(device_t dev, struct sdhci_slot *slot, bus_size_t off)
+{
+ struct tegra_sdhci_softc *sc;
+ uint32_t val32;
+
+ sc = device_get_softc(dev);
+ val32 = bus_read_4(sc->mem_res, off);
+ /* Force the card-present state if necessary. */
+ if (off == SDHCI_PRESENT_STATE && sc->force_card_present)
+ val32 |= SDHCI_CARD_PRESENT;
+ return (val32);
+}
+
+static void
+tegra_sdhci_read_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off,
+ uint32_t *data, bus_size_t count)
+{
+ struct tegra_sdhci_softc *sc;
+
+ sc = device_get_softc(dev);
+ bus_read_multi_4(sc->mem_res, off, data, count);
+}
+
+static void
+tegra_sdhci_write_1(device_t dev, struct sdhci_slot *slot, bus_size_t off,
+ uint8_t val)
+{
+ struct tegra_sdhci_softc *sc;
+
+ sc = device_get_softc(dev);
+ bus_write_1(sc->mem_res, off, val);
+}
+
+static void
+tegra_sdhci_write_2(device_t dev, struct sdhci_slot *slot, bus_size_t off,
+ uint16_t val)
+{
+ struct tegra_sdhci_softc *sc;
+
+ sc = device_get_softc(dev);
+ bus_write_2(sc->mem_res, off, val);
+}
+
+static void
+tegra_sdhci_write_4(device_t dev, struct sdhci_slot *slot, bus_size_t off,
+ uint32_t val)
+{
+ struct tegra_sdhci_softc *sc;
+
+ sc = device_get_softc(dev);
+ bus_write_4(sc->mem_res, off, val);
+}
+
+static void
+tegra_sdhci_write_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off,
+ uint32_t *data, bus_size_t count)
+{
+ struct tegra_sdhci_softc *sc;
+
+ sc = device_get_softc(dev);
+ bus_write_multi_4(sc->mem_res, off, data, count);
+}
+
+static void
+tegra_sdhci_intr(void *arg)
+{
+ struct tegra_sdhci_softc *sc = arg;
+
+ sdhci_generic_intr(&sc->slot);
+ RD4(sc, SDHCI_INT_STATUS);
+}
+
+static int
+tegra_sdhci_get_ro(device_t brdev, device_t reqdev)
+{
+ struct tegra_sdhci_softc *sc = device_get_softc(brdev);
+
+ return (sdhci_fdt_gpio_get_readonly(sc->gpio));
+}
+
+static bool
+tegra_sdhci_get_card_present(device_t dev, struct sdhci_slot *slot)
+{
+ struct tegra_sdhci_softc *sc = device_get_softc(dev);
+
+ return (sdhci_fdt_gpio_get_present(sc->gpio));
+}
+
+static int
+tegra_sdhci_probe(device_t dev)
+{
+ struct tegra_sdhci_softc *sc;
+ phandle_t node;
+ pcell_t cid;
+ const struct ofw_compat_data *cd;
+
+ sc = device_get_softc(dev);
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ cd = ofw_bus_search_compatible(dev, compat_data);
+ if (cd->ocd_data == 0)
+ return (ENXIO);
+
+ node = ofw_bus_get_node(dev);
+ device_set_desc(dev, "Tegra SDHCI controller");
+
+ /* Allow dts to patch quirks, slots, and max-frequency. */
+ if ((OF_getencprop(node, "quirks", &cid, sizeof(cid))) > 0)
+ sc->quirks = cid;
+ if ((OF_getencprop(node, "max-frequency", &cid, sizeof(cid))) > 0)
+ sc->max_clk = cid;
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+tegra_sdhci_attach(device_t dev)
+{
+ struct tegra_sdhci_softc *sc;
+ int rid, rv;
+ uint64_t freq;
+ phandle_t node, prop;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ node = ofw_bus_get_node(dev);
+
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (!sc->mem_res) {
+ device_printf(dev, "cannot allocate memory window\n");
+ rv = ENXIO;
+ goto fail;
+ }
+
+ rid = 0;
+ sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (!sc->irq_res) {
+ device_printf(dev, "cannot allocate interrupt\n");
+ rv = ENXIO;
+ goto fail;
+ }
+
+ rv = hwreset_get_by_ofw_name(sc->dev, 0, "sdhci", &sc->reset);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'sdhci' reset\n");
+ goto fail;
+ }
+ rv = hwreset_assert(sc->reset);
+ if (rv != 0) {
+ device_printf(dev, "Cannot reset 'sdhci' reset\n");
+ goto fail;
+ }
+
+ gpio_pin_get_by_ofw_property(sc->dev, node, "power-gpios",
+ &sc->gpio_power);
+
+ if (OF_hasprop(node, "assigned-clocks")) {
+ rv = clk_set_assigned(sc->dev, node);
+ if (rv != 0) {
+ device_printf(dev, "Cannot set assigned clocks\n");
+ goto fail;
+ }
+ }
+
+ rv = clk_get_by_ofw_index(dev, 0, 0, &sc->clk);
+ if (rv != 0) {
+ device_printf(dev, "Cannot get clock\n");
+ goto fail;
+ }
+ rv = clk_enable(sc->clk);
+ if (rv != 0) {
+ device_printf(dev, "Cannot enable clock\n");
+ goto fail;
+ }
+ rv = clk_set_freq(sc->clk, 48000000, CLK_SET_ROUND_DOWN);
+ if (rv != 0) {
+ device_printf(dev, "Cannot set clock\n");
+ }
+ rv = clk_get_freq(sc->clk, &freq);
+ if (rv != 0) {
+ device_printf(dev, "Cannot get clock frequency\n");
+ goto fail;
+ }
+ DELAY(4000);
+ rv = hwreset_deassert(sc->reset);
+ if (rv != 0) {
+ device_printf(dev, "Cannot unreset 'sdhci' reset\n");
+ goto fail;
+ }
+ if (bootverbose)
+ device_printf(dev, " Base MMC clock: %jd\n", (uintmax_t)freq);
+
+ /* Fill slot information. */
+ sc->max_clk = (int)freq;
+ sc->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
+ SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
+ SDHCI_QUIRK_MISSING_CAPS;
+
+ /* Limit real slot capabilities. */
+ sc->caps = RD4(sc, SDHCI_CAPABILITIES);
+ if (OF_getencprop(node, "bus-width", &prop, sizeof(prop)) > 0) {
+ sc->caps &= ~(MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA);
+ switch (prop) {
+ case 8:
+ sc->caps |= MMC_CAP_8_BIT_DATA;
+ /* FALLTHROUGH */
+ case 4:
+ sc->caps |= MMC_CAP_4_BIT_DATA;
+ break;
+ case 1:
+ break;
+ default:
+ device_printf(dev, "Bad bus-width value %u\n", prop);
+ break;
+ }
+ }
+ if (OF_hasprop(node, "non-removable"))
+ sc->force_card_present = 1;
+ /*
+ * Clear clock field, so SDHCI driver uses supplied frequency.
+ * in sc->slot.max_clk
+ */
+ sc->caps &= ~SDHCI_CLOCK_V3_BASE_MASK;
+
+ sc->slot.quirks = sc->quirks;
+ sc->slot.max_clk = sc->max_clk;
+ sc->slot.caps = sc->caps;
+
+ if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ NULL, tegra_sdhci_intr, sc, &sc->intr_cookie)) {
+ device_printf(dev, "cannot setup interrupt handler\n");
+ rv = ENXIO;
+ goto fail;
+ }
+ rv = sdhci_init_slot(dev, &sc->slot, 0);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ sc->gpio = sdhci_fdt_gpio_setup(sc->dev, &sc->slot);
+
+ bus_generic_probe(dev);
+ bus_generic_attach(dev);
+
+ sdhci_start_slot(&sc->slot);
+
+ return (0);
+
+fail:
+ if (sc->gpio != NULL)
+ sdhci_fdt_gpio_teardown(sc->gpio);
+ if (sc->intr_cookie != NULL)
+ bus_teardown_intr(dev, sc->irq_res, sc->intr_cookie);
+ if (sc->gpio_power != NULL)
+ gpio_pin_release(sc->gpio_power);
+ if (sc->clk != NULL)
+ clk_release(sc->clk);
+ if (sc->reset != NULL)
+ hwreset_release(sc->reset);
+ if (sc->irq_res != NULL)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+ if (sc->mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+
+ return (rv);
+}
+
+static int
+tegra_sdhci_detach(device_t dev)
+{
+ struct tegra_sdhci_softc *sc = device_get_softc(dev);
+ struct sdhci_slot *slot = &sc->slot;
+
+ bus_generic_detach(dev);
+ sdhci_fdt_gpio_teardown(sc->gpio);
+ clk_release(sc->clk);
+ bus_teardown_intr(dev, sc->irq_res, sc->intr_cookie);
+ bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->irq_res),
+ sc->irq_res);
+
+ sdhci_cleanup_slot(slot);
+ bus_release_resource(dev, SYS_RES_MEMORY,
+ rman_get_rid(sc->mem_res),
+ sc->mem_res);
+ return (0);
+}
+
+static device_method_t tegra_sdhci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, tegra_sdhci_probe),
+ DEVMETHOD(device_attach, tegra_sdhci_attach),
+ DEVMETHOD(device_detach, tegra_sdhci_detach),
+
+ /* Bus interface */
+ DEVMETHOD(bus_read_ivar, sdhci_generic_read_ivar),
+ DEVMETHOD(bus_write_ivar, sdhci_generic_write_ivar),
+
+ /* MMC bridge interface */
+ DEVMETHOD(mmcbr_update_ios, sdhci_generic_update_ios),
+ DEVMETHOD(mmcbr_request, sdhci_generic_request),
+ DEVMETHOD(mmcbr_get_ro, tegra_sdhci_get_ro),
+ DEVMETHOD(mmcbr_acquire_host, sdhci_generic_acquire_host),
+ DEVMETHOD(mmcbr_release_host, sdhci_generic_release_host),
+
+ /* SDHCI registers accessors */
+ DEVMETHOD(sdhci_read_1, tegra_sdhci_read_1),
+ DEVMETHOD(sdhci_read_2, tegra_sdhci_read_2),
+ DEVMETHOD(sdhci_read_4, tegra_sdhci_read_4),
+ DEVMETHOD(sdhci_read_multi_4, tegra_sdhci_read_multi_4),
+ DEVMETHOD(sdhci_write_1, tegra_sdhci_write_1),
+ DEVMETHOD(sdhci_write_2, tegra_sdhci_write_2),
+ DEVMETHOD(sdhci_write_4, tegra_sdhci_write_4),
+ DEVMETHOD(sdhci_write_multi_4, tegra_sdhci_write_multi_4),
+ DEVMETHOD(sdhci_get_card_present, tegra_sdhci_get_card_present),
+
+ DEVMETHOD_END
+};
+
+static devclass_t tegra_sdhci_devclass;
+static DEFINE_CLASS_0(sdhci, tegra_sdhci_driver, tegra_sdhci_methods,
+ sizeof(struct tegra_sdhci_softc));
+DRIVER_MODULE(sdhci_tegra, simplebus, tegra_sdhci_driver, tegra_sdhci_devclass,
+ NULL, NULL);
+SDHCI_DEPEND(sdhci_tegra);
+#ifndef MMCCAM
+MMC_DECLARE_BRIDGE(sdhci);
+#endif
diff --git a/sys/arm/nvidia/tegra_soctherm.c b/sys/arm/nvidia/tegra_soctherm.c
new file mode 100644
index 000000000000..d90e97e59159
--- /dev/null
+++ b/sys/arm/nvidia/tegra_soctherm.c
@@ -0,0 +1,844 @@
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Thermometer and thermal zones driver for Tegra SoCs.
+ * Calibration data and algo are taken from Linux, because this part of SoC
+ * is undocumented in TRM.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/gpio.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/nvidia/tegra_efuse.h>
+#include <dt-bindings/thermal/tegra124-soctherm.h>
+#include "tegra_soctherm_if.h"
+
+/* Per sensors registers - base is 0x0c0*/
+#define TSENSOR_CONFIG0 0x000
+#define TSENSOR_CONFIG0_TALL(x) (((x) & 0xFFFFF) << 8)
+#define TSENSOR_CONFIG0_STATUS_CLR (1 << 5)
+#define TSENSOR_CONFIG0_TCALC_OVERFLOW (1 << 4)
+#define TSENSOR_CONFIG0_OVERFLOW (1 << 3)
+#define TSENSOR_CONFIG0_CPTR_OVERFLOW (1 << 2)
+#define TSENSOR_CONFIG0_RO_SEL (1 << 1)
+#define TSENSOR_CONFIG0_STOP (1 << 0)
+
+#define TSENSOR_CONFIG1 0x004
+#define TSENSOR_CONFIG1_TEMP_ENABLE (1U << 31)
+#define TSENSOR_CONFIG1_TEN_COUNT(x) (((x) & 0x3F) << 24)
+#define TSENSOR_CONFIG1_TIDDQ_EN(x) (((x) & 0x3F) << 15)
+#define TSENSOR_CONFIG1_TSAMPLE(x) (((x) & 0x3FF) << 0)
+
+#define TSENSOR_CONFIG2 0x008
+#define TSENSOR_CONFIG2_THERMA(x) (((x) & 0xFFFF) << 16)
+#define TSENSOR_CONFIG2_THERMB(x) (((x) & 0xFFFF) << 0)
+
+#define TSENSOR_STATUS0 0x00c
+#define TSENSOR_STATUS0_CAPTURE_VALID (1U << 31)
+#define TSENSOR_STATUS0_CAPTURE(x) (((x) >> 0) & 0xffff)
+
+#define TSENSOR_STATUS1 0x010
+#define TSENSOR_STATUS1_TEMP_VALID (1U << 31)
+#define TSENSOR_STATUS1_TEMP(x) (((x) >> 0) & 0xffff)
+
+#define TSENSOR_STATUS2 0x014
+#define TSENSOR_STATUS2_TEMP_MAX(x) (((x) >> 16) & 0xffff)
+#define TSENSOR_STATUS2_TEMP_MIN(x) (((x) >> 0) & 0xffff)
+
+
+/* Readbacks */
+#define READBACK_VALUE(x) (((x) >> 8) & 0xff)
+#define READBACK_ADD_HALF (1 << 7)
+#define READBACK_NEGATE (1 << 0)
+
+/* Global registers */
+#define TSENSOR_PDIV 0x1c0
+#define TSENSOR_HOTSPOT_OFF 0x1c4
+#define TSENSOR_TEMP1 0x1c8
+#define TSENSOR_TEMP2 0x1cc
+
+/* Fuses */
+#define FUSE_TSENSOR_CALIB_CP_TS_BASE_SHIFT 0
+#define FUSE_TSENSOR_CALIB_CP_TS_BASE_BITS 13
+#define FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT 13
+#define FUSE_TSENSOR_CALIB_FT_TS_BASE_BITS 13
+
+/* Layout is different for Tegra124 and Tegra210 */
+#define FUSE_TSENSOR_COMMON 0x180
+#define TEGRA124_FUSE_COMMON_CP_TS_BASE(x) (((x) >> 0) & 0x3ff)
+#define TEGRA124_FUSE_COMMON_FT_TS_BASE(x) (((x) >> 10) & 0x7ff)
+#define TEGRA124_FUSE_COMMON_SHIFT_FT_SHIFT 21
+#define TEGRA124_FUSE_COMMON_SHIFT_FT_BITS 5
+
+#define TEGRA210_FUSE_COMMON_CP_TS_BASE(x) (((x) >> 11) & 0x3ff)
+#define TEGRA210_FUSE_COMMON_FT_TS_BASE(x) (((x) >> 21) & 0x7ff)
+#define TEGRA210_FUSE_COMMON_SHIFT_CP_SHIFT 0
+#define TEGRA210_FUSE_COMMON_SHIFT_CP_BITS 6
+#define TEGRA210_FUSE_COMMON_SHIFT_FT_SHIFT 6
+#define TEGRA210_FUSE_COMMON_SHIFT_FT_BITS 5
+
+
+/* Only for Tegra124 */
+#define FUSE_SPARE_REALIGNMENT_REG 0x1fc
+#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP_SHIFT 0
+#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP_BITS 6
+
+#define TEGRA124_NOMINAL_CALIB_FT 105
+#define TEGRA124_NOMINAL_CALIB_CP 25
+
+#define TEGRA210_NOMINAL_CALIB_FT 105
+#define TEGRA210_NOMINAL_CALIB_CP 25
+
+#define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v))
+#define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r))
+
+static struct sysctl_ctx_list soctherm_sysctl_ctx;
+
+struct tsensor_cfg {
+ uint32_t tall;
+ uint32_t tsample;
+ uint32_t tiddq_en;
+ uint32_t ten_count;
+ uint32_t pdiv;
+ uint32_t tsample_ate;
+ uint32_t pdiv_ate;
+};
+
+struct soctherm_shared_cal {
+ uint32_t base_cp;
+ uint32_t base_ft;
+ int32_t actual_temp_cp;
+ int32_t actual_temp_ft;
+};
+
+struct tsensor {
+ char *name;
+ int id;
+ bus_addr_t sensor_base;
+ bus_addr_t calib_fuse;
+ int fuse_corr_alpha;
+ int fuse_corr_beta;
+
+ int16_t therm_a;
+ int16_t therm_b;
+};
+
+struct soctherm_soc;
+struct soctherm_softc {
+ device_t dev;
+ struct resource *mem_res;
+ struct resource *irq_res;
+ void *irq_ih;
+
+ clk_t tsensor_clk;
+ clk_t soctherm_clk;
+ hwreset_t reset;
+
+ struct soctherm_soc *soc;
+ struct soctherm_shared_cal shared_cal;
+};
+
+struct soctherm_soc {
+ void (*shared_cal)(struct soctherm_softc *sc);
+ uint32_t tsensor_pdiv;
+ uint32_t tsensor_hotspot_off;
+ struct tsensor_cfg *tsensor_cfg;
+ struct tsensor *tsensors;
+ int ntsensors;
+};
+
+/* Tegra124 config */
+
+static struct tsensor_cfg t124_tsensor_config = {
+ .tall = 16300,
+ .tsample = 120,
+ .tiddq_en = 1,
+ .ten_count = 1,
+ .pdiv = 8,
+ .tsample_ate = 480,
+ .pdiv_ate = 8
+};
+
+static struct tsensor t124_tsensors[] = {
+ {
+ .name = "cpu0",
+ .id = TEGRA124_SOCTHERM_SENSOR_CPU,
+ .sensor_base = 0x0c0,
+ .calib_fuse = 0x098,
+ .fuse_corr_alpha = 1135400,
+ .fuse_corr_beta = -6266900,
+ },
+ {
+ .name = "cpu1",
+ .id = -1,
+ .sensor_base = 0x0e0,
+ .calib_fuse = 0x084,
+ .fuse_corr_alpha = 1122220,
+ .fuse_corr_beta = -5700700,
+ },
+ {
+ .name = "cpu2",
+ .id = -1,
+ .sensor_base = 0x100,
+ .calib_fuse = 0x088,
+ .fuse_corr_alpha = 1127000,
+ .fuse_corr_beta = -6768200,
+ },
+ {
+ .name = "cpu3",
+ .id = -1,
+ .sensor_base = 0x120,
+ .calib_fuse = 0x12c,
+ .fuse_corr_alpha = 1110900,
+ .fuse_corr_beta = -6232000,
+ },
+ {
+ .name = "mem0",
+ .id = TEGRA124_SOCTHERM_SENSOR_MEM,
+ .sensor_base = 0x140,
+ .calib_fuse = 0x158,
+ .fuse_corr_alpha = 1122300,
+ .fuse_corr_beta = -5936400,
+ },
+ {
+ .name = "mem1",
+ .id = -1,
+ .sensor_base = 0x160,
+ .calib_fuse = 0x15c,
+ .fuse_corr_alpha = 1145700,
+ .fuse_corr_beta = -7124600,
+ },
+ {
+ .name = "gpu",
+ .id = TEGRA124_SOCTHERM_SENSOR_GPU,
+ .sensor_base = 0x180,
+ .calib_fuse = 0x154,
+ .fuse_corr_alpha = 1120100,
+ .fuse_corr_beta = -6000500,
+ },
+ {
+ .name = "pllX",
+ .id = TEGRA124_SOCTHERM_SENSOR_PLLX,
+ .sensor_base = 0x1a0,
+ .calib_fuse = 0x160,
+ .fuse_corr_alpha = 1106500,
+ .fuse_corr_beta = -6729300,
+ },
+};
+
+static void tegra124_shared_cal(struct soctherm_softc *sc);
+
+static struct soctherm_soc tegra124_soc = {
+ .shared_cal = tegra124_shared_cal,
+ .tsensor_pdiv = 0x8888,
+ .tsensor_hotspot_off = 0x00060600 ,
+ .tsensor_cfg = &t124_tsensor_config,
+ .tsensors = t124_tsensors,
+ .ntsensors = nitems(t124_tsensors),
+};
+
+/* Tegra210 config */
+static struct tsensor_cfg t210_tsensor_config = {
+ .tall = 16300,
+ .tsample = 120,
+ .tiddq_en = 1,
+ .ten_count = 1,
+ .pdiv = 8,
+ .tsample_ate = 480,
+ .pdiv_ate = 8
+};
+
+static struct tsensor t210_tsensors[] = {
+ {
+ .name = "cpu0",
+ .id = TEGRA124_SOCTHERM_SENSOR_CPU,
+ .sensor_base = 0x0c0,
+ .calib_fuse = 0x098,
+ .fuse_corr_alpha = 1085000,
+ .fuse_corr_beta = 3244200,
+ },
+ {
+ .name = "cpu1",
+ .id = -1,
+ .sensor_base = 0x0e0,
+ .calib_fuse = 0x084,
+ .fuse_corr_alpha = 1126200,
+ .fuse_corr_beta = -67500,
+ },
+ {
+ .name = "cpu2",
+ .id = -1,
+ .sensor_base = 0x100,
+ .calib_fuse = 0x088,
+ .fuse_corr_alpha = 1098400,
+ .fuse_corr_beta = 2251100,
+ },
+ {
+ .name = "cpu3",
+ .id = -1,
+ .sensor_base = 0x120,
+ .calib_fuse = 0x12c,
+ .fuse_corr_alpha = 1108000,
+ .fuse_corr_beta = 602700,
+ },
+ {
+ .name = "mem0",
+ .id = TEGRA124_SOCTHERM_SENSOR_MEM,
+ .sensor_base = 0x140,
+ .calib_fuse = 0x158,
+ .fuse_corr_alpha = 1069200,
+ .fuse_corr_beta = 3549900,
+ },
+ {
+ .name = "mem1",
+ .id = -1,
+ .sensor_base = 0x160,
+ .calib_fuse = 0x15c,
+ .fuse_corr_alpha = 1173700,
+ .fuse_corr_beta = -6263600,
+ },
+ {
+ .name = "gpu",
+ .id = TEGRA124_SOCTHERM_SENSOR_GPU,
+ .sensor_base = 0x180,
+ .calib_fuse = 0x154,
+ .fuse_corr_alpha = 1074300,
+ .fuse_corr_beta = 2734900,
+ },
+ {
+ .name = "pllX",
+ .id = TEGRA124_SOCTHERM_SENSOR_PLLX,
+ .sensor_base = 0x1a0,
+ .calib_fuse = 0x160,
+ .fuse_corr_alpha = 1039700,
+ .fuse_corr_beta = 6829100,
+ },
+};
+
+static void tegra210_shared_cal(struct soctherm_softc *sc);
+
+static struct soctherm_soc tegra210_soc = {
+ .shared_cal = tegra210_shared_cal,
+ .tsensor_pdiv = 0x8888,
+ .tsensor_hotspot_off = 0x000A0500 ,
+ .tsensor_cfg = &t210_tsensor_config,
+ .tsensors = t210_tsensors,
+ .ntsensors = nitems(t210_tsensors),
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"nvidia,tegra124-soctherm", (uintptr_t)&tegra124_soc},
+ {"nvidia,tegra210-soctherm", (uintptr_t)&tegra210_soc},
+ {NULL, 0},
+};
+
+/* Extract signed integer bitfield from register */
+static int
+extract_signed(uint32_t reg, int shift, int bits)
+{
+ int32_t val;
+ uint32_t mask;
+
+ mask = (1 << bits) - 1;
+ val = ((reg >> shift) & mask) << (32 - bits);
+ val >>= 32 - bits;
+ return ((int32_t)val);
+}
+
+static inline
+int64_t div64_s64_precise(int64_t a, int64_t b)
+{
+ int64_t r, al;
+
+ al = a << 16;
+ r = (al * 2 + 1) / (2 * b);
+ return (r >> 16);
+}
+
+static void
+tegra124_shared_cal(struct soctherm_softc *sc)
+{
+ uint32_t val;
+ int calib_cp, calib_ft;
+ struct soctherm_shared_cal *cal;
+
+ cal = &sc->shared_cal;
+ val = tegra_fuse_read_4(FUSE_TSENSOR_COMMON);
+ cal->base_cp = TEGRA124_FUSE_COMMON_CP_TS_BASE(val);
+ cal->base_ft = TEGRA124_FUSE_COMMON_FT_TS_BASE(val);
+
+ calib_ft = extract_signed(val,
+ TEGRA124_FUSE_COMMON_SHIFT_FT_SHIFT,
+ TEGRA124_FUSE_COMMON_SHIFT_FT_BITS);
+
+ val = tegra_fuse_read_4(FUSE_SPARE_REALIGNMENT_REG);
+ calib_cp = extract_signed(val,
+ FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP_SHIFT,
+ FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP_BITS);
+
+ cal->actual_temp_cp = 2 * TEGRA124_NOMINAL_CALIB_CP + calib_cp;
+ cal->actual_temp_ft = 2 * TEGRA124_NOMINAL_CALIB_FT + calib_ft;
+#ifdef DEBUG
+ printf("%s: base_cp: %u, base_ft: %d,"
+ " actual_temp_cp: %d, actual_temp_ft: %d\n",
+ __func__, cal->base_cp, cal->base_ft,
+ cal->actual_temp_cp, cal->actual_temp_ft);
+#endif
+}
+
+static void
+tegra210_shared_cal(struct soctherm_softc *sc)
+{
+ uint32_t val;
+ int calib_cp, calib_ft;
+ struct soctherm_shared_cal *cal;
+
+ cal = &sc->shared_cal;
+
+ val = tegra_fuse_read_4(FUSE_TSENSOR_COMMON);
+ cal->base_cp = TEGRA210_FUSE_COMMON_CP_TS_BASE(val);
+ cal->base_ft = TEGRA210_FUSE_COMMON_FT_TS_BASE(val);
+
+ calib_ft = extract_signed(val,
+ TEGRA210_FUSE_COMMON_SHIFT_FT_SHIFT,
+ TEGRA210_FUSE_COMMON_SHIFT_FT_BITS);
+ calib_cp = extract_signed(val,
+ TEGRA210_FUSE_COMMON_SHIFT_CP_SHIFT,
+ TEGRA210_FUSE_COMMON_SHIFT_CP_BITS);
+
+ cal->actual_temp_cp = 2 * TEGRA210_NOMINAL_CALIB_CP + calib_cp;
+ cal->actual_temp_ft = 2 * TEGRA210_NOMINAL_CALIB_FT + calib_ft;
+#ifdef DEBUG
+ printf("%s: base_cp: %u, base_ft: %d,"
+ " actual_temp_cp: %d, actual_temp_ft: %d\n",
+ __func__, cal->base_cp, cal->base_ft,
+ cal->actual_temp_cp, cal->actual_temp_ft);
+#endif
+}
+
+static void
+tsensor_calibration(struct soctherm_softc *sc, struct tsensor *sensor)
+{
+ uint32_t val;
+ int mult, div, calib_cp, calib_ft;
+ int actual_tsensor_ft, actual_tsensor_cp, delta_sens, delta_temp;
+ int temp_a, temp_b;
+ struct tsensor_cfg *cfg;
+ struct soctherm_shared_cal *cal;
+ int64_t tmp;
+
+ cfg = sc->soc->tsensor_cfg;
+ cal = &sc->shared_cal;
+
+ val = tegra_fuse_read_4(sensor->calib_fuse);
+ calib_cp = extract_signed(val,
+ FUSE_TSENSOR_CALIB_CP_TS_BASE_SHIFT,
+ FUSE_TSENSOR_CALIB_CP_TS_BASE_BITS);
+ actual_tsensor_cp = cal->base_cp * 64 + calib_cp;
+
+ calib_ft = extract_signed(val,
+ FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT,
+ FUSE_TSENSOR_CALIB_FT_TS_BASE_BITS);
+ actual_tsensor_ft = cal->base_ft * 32 + calib_ft;
+
+ delta_sens = actual_tsensor_ft - actual_tsensor_cp;
+ delta_temp = cal->actual_temp_ft - cal->actual_temp_cp;
+ mult = cfg->pdiv * cfg->tsample_ate;
+ div = cfg->tsample * cfg->pdiv_ate;
+
+ temp_a = div64_s64_precise((int64_t) delta_temp * (1LL << 13) * mult,
+ (int64_t) delta_sens * div);
+
+ tmp = (int64_t)actual_tsensor_ft * cal->actual_temp_cp -
+ (int64_t)actual_tsensor_cp * cal->actual_temp_ft;
+ temp_b = div64_s64_precise(tmp, (int64_t)delta_sens);
+
+ temp_a = div64_s64_precise((int64_t)temp_a * sensor->fuse_corr_alpha,
+ 1000000);
+ temp_b = div64_s64_precise((int64_t)temp_b * sensor->fuse_corr_alpha +
+ sensor->fuse_corr_beta, 1000000);
+ sensor->therm_a = (int16_t)temp_a;
+ sensor->therm_b = (int16_t)temp_b;
+#ifdef DEBUG
+ printf("%s: sensor %s fuse: 0x%08X (0x%04X, 0x%04X)"
+ " calib_cp: %d(0x%04X), calib_ft: %d(0x%04X)\n",
+ __func__, sensor->name, val, val & 0x1FFF, (val >> 13) & 0x1FFF,
+ calib_cp, calib_cp, calib_ft, calib_ft);
+ printf("therma: 0x%04X(%d), thermb: 0x%04X(%d)\n",
+ (uint16_t)sensor->therm_a, sensor->therm_a,
+ (uint16_t)sensor->therm_b, sensor->therm_b);
+#endif
+}
+
+static void
+soctherm_init_tsensor(struct soctherm_softc *sc, struct tsensor *sensor)
+{
+ struct tsensor_cfg *cfg;
+ uint32_t val;
+
+ cfg = sc->soc->tsensor_cfg;
+ tsensor_calibration(sc, sensor);
+
+ val = RD4(sc, sensor->sensor_base + TSENSOR_CONFIG0);
+ val |= TSENSOR_CONFIG0_STOP;
+ val |= TSENSOR_CONFIG0_STATUS_CLR;
+ WR4(sc, sensor->sensor_base + TSENSOR_CONFIG0, val);
+
+ val = TSENSOR_CONFIG0_TALL(cfg->tall);
+ val |= TSENSOR_CONFIG0_STOP;
+ WR4(sc, sensor->sensor_base + TSENSOR_CONFIG0, val);
+
+ val = TSENSOR_CONFIG1_TSAMPLE(cfg->tsample - 1);
+ val |= TSENSOR_CONFIG1_TIDDQ_EN(cfg->tiddq_en);
+ val |= TSENSOR_CONFIG1_TEN_COUNT(cfg->ten_count);
+ val |= TSENSOR_CONFIG1_TEMP_ENABLE;
+ WR4(sc, sensor->sensor_base + TSENSOR_CONFIG1, val);
+
+ val = TSENSOR_CONFIG2_THERMA((uint16_t)sensor->therm_a) |
+ TSENSOR_CONFIG2_THERMB((uint16_t)sensor->therm_b);
+ WR4(sc, sensor->sensor_base + TSENSOR_CONFIG2, val);
+
+ val = RD4(sc, sensor->sensor_base + TSENSOR_CONFIG0);
+ val &= ~TSENSOR_CONFIG0_STOP;
+ WR4(sc, sensor->sensor_base + TSENSOR_CONFIG0, val);
+#ifdef DEBUG
+ printf(" Sensor: %s cfg:0x%08X, 0x%08X, 0x%08X,"
+ " sts:0x%08X, 0x%08X, 0x%08X\n", sensor->name,
+ RD4(sc, sensor->sensor_base + TSENSOR_CONFIG0),
+ RD4(sc, sensor->sensor_base + TSENSOR_CONFIG1),
+ RD4(sc, sensor->sensor_base + TSENSOR_CONFIG2),
+ RD4(sc, sensor->sensor_base + TSENSOR_STATUS0),
+ RD4(sc, sensor->sensor_base + TSENSOR_STATUS1),
+ RD4(sc, sensor->sensor_base + TSENSOR_STATUS2)
+ );
+#endif
+}
+
+static int
+soctherm_convert_raw(uint32_t val)
+{
+ int32_t t;
+
+ t = READBACK_VALUE(val) * 1000;
+ if (val & READBACK_ADD_HALF)
+ t += 500;
+ if (val & READBACK_NEGATE)
+ t *= -1;
+
+ return (t);
+}
+
+static int
+soctherm_read_temp(struct soctherm_softc *sc, struct tsensor *sensor, int *temp)
+{
+ int timeout;
+ uint32_t val;
+
+ /* wait for valid sample */
+ for (timeout = 100; timeout > 0; timeout--) {
+ val = RD4(sc, sensor->sensor_base + TSENSOR_STATUS1);
+ if ((val & TSENSOR_STATUS1_TEMP_VALID) != 0)
+ break;
+ DELAY(100);
+ }
+ if (timeout <= 0)
+ device_printf(sc->dev, "Sensor %s timeouted\n", sensor->name);
+ *temp = soctherm_convert_raw(val);
+#ifdef DEBUG
+ printf("%s: Raw: 0x%08X, temp: %d\n", __func__, val, *temp);
+ printf(" Sensor: %s cfg:0x%08X, 0x%08X, 0x%08X,"
+ " sts:0x%08X, 0x%08X, 0x%08X\n", sensor->name,
+ RD4(sc, sensor->sensor_base + TSENSOR_CONFIG0),
+ RD4(sc, sensor->sensor_base + TSENSOR_CONFIG1),
+ RD4(sc, sensor->sensor_base + TSENSOR_CONFIG2),
+ RD4(sc, sensor->sensor_base + TSENSOR_STATUS0),
+ RD4(sc, sensor->sensor_base + TSENSOR_STATUS1),
+ RD4(sc, sensor->sensor_base + TSENSOR_STATUS2)
+ );
+#endif
+ return (0);
+}
+
+static int
+soctherm_get_temp(device_t dev, device_t cdev, uintptr_t id, int *val)
+{
+ struct soctherm_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+ /* The direct sensor map starts at 0x100 */
+ if (id >= 0x100) {
+ id -= 0x100;
+ if (id >= sc->soc->ntsensors)
+ return (ERANGE);
+ return(soctherm_read_temp(sc, sc->soc->tsensors + id, val));
+ }
+ /* Linux (DT) compatible thermal zones */
+ for (i = 0; i < sc->soc->ntsensors; i++) {
+ if (sc->soc->tsensors->id == id) {
+ return(soctherm_read_temp(sc, sc->soc->tsensors + id,
+ val));
+ }
+ }
+ return (ERANGE);
+}
+
+static int
+soctherm_sysctl_temperature(SYSCTL_HANDLER_ARGS)
+{
+ struct soctherm_softc *sc;
+ int val;
+ int rv;
+ int id;
+
+ /* Write request */
+ if (req->newptr != NULL)
+ return (EINVAL);
+
+ sc = arg1;
+ id = arg2;
+
+ if (id >= sc->soc->ntsensors)
+ return (ERANGE);
+ rv = soctherm_read_temp(sc, sc->soc->tsensors + id, &val);
+ if (rv != 0)
+ return (rv);
+
+ val = val / 100;
+ val += 2731;
+ rv = sysctl_handle_int(oidp, &val, 0, req);
+ return (rv);
+}
+
+static int
+soctherm_init_sysctl(struct soctherm_softc *sc)
+{
+ int i;
+ struct sysctl_oid *oid, *tmp;
+
+ sysctl_ctx_init(&soctherm_sysctl_ctx);
+ /* create node for hw.temp */
+ oid = SYSCTL_ADD_NODE(&soctherm_sysctl_ctx,
+ SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, "temperature",
+ CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "");
+ if (oid == NULL)
+ return (ENXIO);
+
+ /* Add sensors */
+ for (i = sc->soc->ntsensors - 1; i >= 0; i--) {
+ tmp = SYSCTL_ADD_PROC(&soctherm_sysctl_ctx,
+ SYSCTL_CHILDREN(oid), OID_AUTO, sc->soc->tsensors[i].name,
+ CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, i,
+ soctherm_sysctl_temperature, "IK", "SoC Temperature");
+ if (tmp == NULL)
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static int
+soctherm_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Tegra temperature sensors");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+soctherm_attach(device_t dev)
+{
+ struct soctherm_softc *sc;
+ phandle_t node;
+ int i, rid, rv;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ sc->soc = (struct soctherm_soc *)ofw_bus_search_compatible(dev,
+ compat_data)->ocd_data;
+ node = ofw_bus_get_node(sc->dev);
+
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "Cannot allocate memory resources\n");
+ goto fail;
+ }
+
+ rid = 0;
+ sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
+ if (sc->irq_res == NULL) {
+ device_printf(dev, "Cannot allocate IRQ resources\n");
+ goto fail;
+ }
+
+/*
+ if ((bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC,
+ soctherm_intr, NULL, sc, &sc->irq_ih))) {
+ device_printf(dev,
+ "WARNING: unable to register interrupt handler\n");
+ goto fail;
+ }
+*/
+
+ /* OWF resources */
+ rv = hwreset_get_by_ofw_name(dev, 0, "soctherm", &sc->reset);
+ if (rv != 0) {
+ device_printf(dev, "Cannot get fuse reset\n");
+ goto fail;
+ }
+ rv = clk_get_by_ofw_name(dev, 0, "tsensor", &sc->tsensor_clk);
+ if (rv != 0) {
+ device_printf(dev, "Cannot get 'tsensor' clock: %d\n", rv);
+ goto fail;
+ }
+ rv = clk_get_by_ofw_name(dev, 0, "soctherm", &sc->soctherm_clk);
+ if (rv != 0) {
+ device_printf(dev, "Cannot get 'soctherm' clock: %d\n", rv);
+ goto fail;
+ }
+
+ rv = hwreset_assert(sc->reset);
+ if (rv != 0) {
+ device_printf(dev, "Cannot assert reset\n");
+ goto fail;
+ }
+ rv = clk_enable(sc->tsensor_clk);
+ if (rv != 0) {
+ device_printf(dev, "Cannot enable 'tsensor' clock: %d\n", rv);
+ goto fail;
+ }
+ rv = clk_enable(sc->soctherm_clk);
+ if (rv != 0) {
+ device_printf(dev, "Cannot enable 'soctherm' clock: %d\n", rv);
+ goto fail;
+ }
+ rv = hwreset_deassert(sc->reset);
+ if (rv != 0) {
+ device_printf(dev, "Cannot clear reset\n");
+ goto fail;
+ }
+
+ sc->soc->shared_cal(sc);
+
+ WR4(sc, TSENSOR_PDIV, sc->soc->tsensor_pdiv);
+ WR4(sc, TSENSOR_HOTSPOT_OFF, sc->soc->tsensor_hotspot_off);
+
+ for (i = 0; i < sc->soc->ntsensors; i++)
+ soctherm_init_tsensor(sc, sc->soc->tsensors + i);
+
+ rv = soctherm_init_sysctl(sc);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot initialize sysctls\n");
+ goto fail;
+ }
+
+ OF_device_register_xref(OF_xref_from_node(node), dev);
+ return (bus_generic_attach(dev));
+
+fail:
+ if (sc->irq_ih != NULL)
+ bus_teardown_intr(dev, sc->irq_res, sc->irq_ih);
+ sysctl_ctx_free(&soctherm_sysctl_ctx);
+ if (sc->tsensor_clk != NULL)
+ clk_release(sc->tsensor_clk);
+ if (sc->soctherm_clk != NULL)
+ clk_release(sc->soctherm_clk);
+ if (sc->reset != NULL)
+ hwreset_release(sc->reset);
+ if (sc->irq_res != NULL)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+ if (sc->mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+
+ return (ENXIO);
+}
+
+static int
+soctherm_detach(device_t dev)
+{
+ struct soctherm_softc *sc;
+ sc = device_get_softc(dev);
+
+ if (sc->irq_ih != NULL)
+ bus_teardown_intr(dev, sc->irq_res, sc->irq_ih);
+ sysctl_ctx_free(&soctherm_sysctl_ctx);
+ if (sc->tsensor_clk != NULL)
+ clk_release(sc->tsensor_clk);
+ if (sc->soctherm_clk != NULL)
+ clk_release(sc->soctherm_clk);
+ if (sc->reset != NULL)
+ hwreset_release(sc->reset);
+ if (sc->irq_res != NULL)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+ if (sc->mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+
+ return (ENXIO);
+}
+
+static device_method_t tegra_soctherm_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, soctherm_probe),
+ DEVMETHOD(device_attach, soctherm_attach),
+ DEVMETHOD(device_detach, soctherm_detach),
+
+ /* SOCTHERM interface */
+ DEVMETHOD(tegra_soctherm_get_temperature, soctherm_get_temp),
+
+ DEVMETHOD_END
+};
+
+static devclass_t tegra_soctherm_devclass;
+static DEFINE_CLASS_0(soctherm, tegra_soctherm_driver, tegra_soctherm_methods,
+ sizeof(struct soctherm_softc));
+EARLY_DRIVER_MODULE(tegra_soctherm, simplebus, tegra_soctherm_driver,
+ tegra_soctherm_devclass, NULL, NULL, 79);
diff --git a/sys/arm/nvidia/tegra_soctherm_if.m b/sys/arm/nvidia/tegra_soctherm_if.m
new file mode 100644
index 000000000000..55ae04437e72
--- /dev/null
+++ b/sys/arm/nvidia/tegra_soctherm_if.m
@@ -0,0 +1,42 @@
+#-
+# Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+
+#include <machine/bus.h>
+
+INTERFACE tegra_soctherm;
+
+
+/**
+ * Read temperature
+ */
+METHOD int get_temperature{
+ device_t dev;
+ device_t consumer;
+ uintptr_t id;
+ int *val;
+};
diff --git a/sys/arm/nvidia/tegra_uart.c b/sys/arm/nvidia/tegra_uart.c
new file mode 100644
index 000000000000..9ba567c6d55f
--- /dev/null
+++ b/sys/arm/nvidia/tegra_uart.c
@@ -0,0 +1,252 @@
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * UART driver for Tegra SoCs.
+ */
+#include "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+#include <machine/bus.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/uart/uart.h>
+#include <dev/uart/uart_cpu.h>
+#include <dev/uart/uart_cpu_fdt.h>
+#include <dev/uart/uart_bus.h>
+#include <dev/uart/uart_dev_ns8250.h>
+#include <dev/ic/ns16550.h>
+
+#include "uart_if.h"
+
+/*
+ * High-level UART interface.
+ */
+struct tegra_softc {
+ struct ns8250_softc ns8250_base;
+ clk_t clk;
+ hwreset_t reset;
+};
+
+/*
+ * UART class interface.
+ */
+static int
+tegra_uart_attach(struct uart_softc *sc)
+{
+ int rv;
+ struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc;
+ struct uart_bas *bas = &sc->sc_bas;
+
+ rv = ns8250_bus_attach(sc);
+ if (rv != 0)
+ return (rv);
+
+ ns8250->ier_rxbits = 0x1d;
+ ns8250->ier_mask = 0xc0;
+ ns8250->ier = uart_getreg(bas, REG_IER) & ns8250->ier_mask;
+ ns8250->ier |= ns8250->ier_rxbits;
+ uart_setreg(bas, REG_IER, ns8250->ier);
+ uart_barrier(bas);
+ return (0);
+}
+
+static void
+tegra_uart_grab(struct uart_softc *sc)
+{
+ struct uart_bas *bas = &sc->sc_bas;
+ struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc;
+ u_char ier;
+
+ /*
+ * turn off all interrupts to enter polling mode. Leave the
+ * saved mask alone. We'll restore whatever it was in ungrab.
+ * All pending interrupt signals are reset when IER is set to 0.
+ */
+ uart_lock(sc->sc_hwmtx);
+ ier = uart_getreg(bas, REG_IER);
+ uart_setreg(bas, REG_IER, ier & ns8250->ier_mask);
+
+ while ((uart_getreg(bas, REG_LSR) & LSR_TEMT) == 0)
+ ;
+
+ uart_setreg(bas, REG_FCR, 0);
+ uart_barrier(bas);
+ uart_unlock(sc->sc_hwmtx);
+}
+
+static void
+tegra_uart_ungrab(struct uart_softc *sc)
+{
+ struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc;
+ struct uart_bas *bas = &sc->sc_bas;
+
+ /*
+ * Restore previous interrupt mask
+ */
+ uart_lock(sc->sc_hwmtx);
+ uart_setreg(bas, REG_FCR, ns8250->fcr);
+ uart_setreg(bas, REG_IER, ns8250->ier);
+ uart_barrier(bas);
+ uart_unlock(sc->sc_hwmtx);
+}
+
+static kobj_method_t tegra_methods[] = {
+ KOBJMETHOD(uart_probe, ns8250_bus_probe),
+ KOBJMETHOD(uart_attach, tegra_uart_attach),
+ KOBJMETHOD(uart_detach, ns8250_bus_detach),
+ KOBJMETHOD(uart_flush, ns8250_bus_flush),
+ KOBJMETHOD(uart_getsig, ns8250_bus_getsig),
+ KOBJMETHOD(uart_ioctl, ns8250_bus_ioctl),
+ KOBJMETHOD(uart_ipend, ns8250_bus_ipend),
+ KOBJMETHOD(uart_param, ns8250_bus_param),
+ KOBJMETHOD(uart_receive, ns8250_bus_receive),
+ KOBJMETHOD(uart_setsig, ns8250_bus_setsig),
+ KOBJMETHOD(uart_transmit, ns8250_bus_transmit),
+ KOBJMETHOD(uart_grab, tegra_uart_grab),
+ KOBJMETHOD(uart_ungrab, tegra_uart_ungrab),
+ KOBJMETHOD_END
+};
+
+static struct uart_class tegra_uart_class = {
+ "tegra class",
+ tegra_methods,
+ sizeof(struct tegra_softc),
+ .uc_ops = &uart_ns8250_ops,
+ .uc_range = 8,
+ .uc_rclk = 0,
+};
+
+/* Compatible devices. */
+static struct ofw_compat_data compat_data[] = {
+ {"nvidia,tegra124-uart", (uintptr_t)&tegra_uart_class},
+ {"nvidia,tegra210-uart", (uintptr_t)&tegra_uart_class},
+ {NULL, (uintptr_t)NULL},
+};
+
+UART_FDT_CLASS(compat_data);
+
+/*
+ * UART Driver interface.
+ */
+static int
+uart_fdt_get_shift1(phandle_t node)
+{
+ pcell_t shift;
+
+ if ((OF_getencprop(node, "reg-shift", &shift, sizeof(shift))) <= 0)
+ shift = 2;
+ return ((int)shift);
+}
+
+static int
+tegra_uart_probe(device_t dev)
+{
+ struct tegra_softc *sc;
+ phandle_t node;
+ uint64_t freq;
+ int shift;
+ int rv;
+ const struct ofw_compat_data *cd;
+
+ sc = device_get_softc(dev);
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+ cd = ofw_bus_search_compatible(dev, compat_data);
+ if (cd->ocd_data == 0)
+ return (ENXIO);
+ sc->ns8250_base.base.sc_class = (struct uart_class *)cd->ocd_data;
+ rv = hwreset_get_by_ofw_name(dev, 0, "serial", &sc->reset);
+ if (rv != 0) {
+ device_printf(dev, "Cannot get 'serial' reset\n");
+ return (ENXIO);
+ }
+ rv = hwreset_deassert(sc->reset);
+ if (rv != 0) {
+ device_printf(dev, "Cannot unreset 'serial' reset\n");
+ return (ENXIO);
+ }
+ node = ofw_bus_get_node(dev);
+ shift = uart_fdt_get_shift1(node);
+ rv = clk_get_by_ofw_index(dev, 0, 0, &sc->clk);
+ if (rv != 0) {
+ device_printf(dev, "Cannot get UART clock: %d\n", rv);
+ return (ENXIO);
+ }
+ rv = clk_enable(sc->clk);
+ if (rv != 0) {
+ device_printf(dev, "Cannot enable UART clock: %d\n", rv);
+ return (ENXIO);
+ }
+ rv = clk_get_freq(sc->clk, &freq);
+ if (rv != 0) {
+ device_printf(dev, "Cannot enable UART clock: %d\n", rv);
+ return (ENXIO);
+ }
+ return (uart_bus_probe(dev, shift, 0, (int)freq, 0, 0, 0));
+}
+
+static int
+tegra_uart_detach(device_t dev)
+{
+ struct tegra_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (sc->clk != NULL) {
+ clk_release(sc->clk);
+ }
+
+ return (uart_bus_detach(dev));
+}
+
+static device_method_t tegra_uart_bus_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, tegra_uart_probe),
+ DEVMETHOD(device_attach, uart_bus_attach),
+ DEVMETHOD(device_detach, tegra_uart_detach),
+ { 0, 0 }
+};
+
+static driver_t tegra_uart_driver = {
+ uart_driver_name,
+ tegra_uart_bus_methods,
+ sizeof(struct tegra_softc),
+};
+
+DRIVER_MODULE(tegra_uart, simplebus, tegra_uart_driver, uart_devclass,
+ 0, 0);
diff --git a/sys/arm/nvidia/tegra_usbphy.c b/sys/arm/nvidia/tegra_usbphy.c
new file mode 100644
index 000000000000..1adc90e8418a
--- /dev/null
+++ b/sys/arm/nvidia/tegra_usbphy.c
@@ -0,0 +1,851 @@
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * USB phy driver for Tegra SoCs.
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/extres/phy/phy.h>
+#include <dev/extres/regulator/regulator.h>
+#include <dev/fdt/fdt_pinctrl.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "phynode_if.h"
+
+#define CTRL_ICUSB_CTRL 0x15c
+#define ICUSB_CTR_IC_ENB1 (1 << 3)
+
+#define CTRL_USB_USBMODE 0x1f8
+#define USB_USBMODE_MASK (3 << 0)
+#define USB_USBMODE_HOST (3 << 0)
+#define USB_USBMODE_DEVICE (2 << 0)
+
+#define CTRL_USB_HOSTPC1_DEVLC 0x1b4
+#define USB_HOSTPC1_DEVLC_PTS(x) (((x) & 0x7) << 29)
+#define USB_HOSTPC1_DEVLC_STS (1 << 28)
+#define USB_HOSTPC1_DEVLC_PHCD (1 << 22)
+
+#define IF_USB_SUSP_CTRL 0x400
+#define FAST_WAKEUP_RESP (1 << 26)
+#define UTMIP_SUSPL1_SET (1 << 25)
+#define USB_WAKEUP_DEBOUNCE_COUNT(x) (((x) & 0x7) << 16)
+#define USB_SUSP_SET (1 << 14)
+#define UTMIP_PHY_ENB (1 << 12)
+#define UTMIP_RESET (1 << 11)
+#define USB_SUSP_POL (1 << 10)
+#define USB_PHY_CLK_VALID_INT_ENB (1 << 9)
+#define USB_PHY_CLK_VALID_INT_STS (1 << 8)
+#define USB_PHY_CLK_VALID (1 << 7)
+#define USB_CLKEN (1 << 6)
+#define USB_SUSP_CLR (1 << 5)
+#define USB_WAKE_ON_DISCON_EN_DEV (1 << 4)
+#define USB_WAKE_ON_CNNT_EN_DEV (1 << 3)
+#define USB_WAKE_ON_RESUME_EN (1 << 2)
+#define USB_WAKEUP_INT_ENB (1 << 1)
+#define USB_WAKEUP_INT_STS (1 << 0)
+
+#define IF_USB_PHY_VBUS_SENSORS 0x404
+#define B_SESS_END_SW_VALUE (1 << 4)
+#define B_SESS_END_SW_EN (1 << 3)
+
+#define UTMIP_XCVR_CFG0 0x808
+#define UTMIP_XCVR_HSSLEW_MSB(x) ((((x) & 0x1fc) >> 2) << 25)
+#define UTMIP_XCVR_SETUP_MSB(x) ((((x) & 0x70) >> 4) << 22)
+#define UTMIP_XCVR_LSBIAS_SEL (1 << 21)
+#define UTMIP_XCVR_DISCON_METHOD (1 << 20)
+#define UTMIP_FORCE_PDZI_POWERUP (1 << 19)
+#define UTMIP_FORCE_PDZI_POWERDOWN (1 << 18)
+#define UTMIP_FORCE_PD2_POWERUP (1 << 17)
+#define UTMIP_FORCE_PD2_POWERDOWN (1 << 16)
+#define UTMIP_FORCE_PD_POWERUP (1 << 15)
+#define UTMIP_FORCE_PD_POWERDOWN (1 << 14)
+#define UTMIP_XCVR_TERMEN (1 << 13)
+#define UTMIP_XCVR_HSLOOPBACK (1 << 12)
+#define UTMIP_XCVR_LSFSLEW(x) (((x) & 0x3) << 10)
+#define UTMIP_XCVR_LSRSLEW(x) (((x) & 0x3) << 8)
+#define UTMIP_XCVR_FSSLEW(x) (((x) & 0x3) << 6)
+#define UTMIP_XCVR_HSSLEW(x) (((x) & 0x3) << 4)
+#define UTMIP_XCVR_SETUP(x) (((x) & 0xf) << 0)
+
+#define UTMIP_BIAS_CFG0 0x80C
+#define UTMIP_IDDIG_C_VAL (1 << 30)
+#define UTMIP_IDDIG_C_SEL (1 << 29)
+#define UTMIP_IDDIG_B_VAL (1 << 28)
+#define UTMIP_IDDIG_B_SEL (1 << 27)
+#define UTMIP_IDDIG_A_VAL (1 << 26)
+#define UTMIP_IDDIG_A_SEL (1 << 25)
+#define UTMIP_HSDISCON_LEVEL_MSB(x) ((((x) & 0x4) >> 2) << 24)
+#define UTMIP_IDPD_VAL (1 << 23)
+#define UTMIP_IDPD_SEL (1 << 22)
+#define UTMIP_IDDIG_VAL (1 << 21)
+#define UTMIP_IDDIG_SEL (1 << 20)
+#define UTMIP_GPI_VAL (1 << 19)
+#define UTMIP_GPI_SEL (1 << 18)
+#define UTMIP_ACTIVE_TERM_OFFSET(x) (((x) & 0x7) << 15)
+#define UTMIP_ACTIVE_PULLUP_OFFSET(x) (((x) & 0x7) << 12)
+#define UTMIP_OTGPD (1 << 11)
+#define UTMIP_BIASPD (1 << 10)
+#define UTMIP_VBUS_LEVEL_LEVEL(x) (((x) & 0x3) << 8)
+#define UTMIP_SESS_LEVEL_LEVEL(x) (((x) & 0x3) << 6)
+#define UTMIP_HSCHIRP_LEVEL(x) (((x) & 0x3) << 4)
+#define UTMIP_HSDISCON_LEVEL(x) (((x) & 0x3) << 2)
+#define UTMIP_HSSQUELCH_LEVEL(x) (((x) & 0x3) << 0)
+
+#define UTMIP_HSRX_CFG0 0x810
+#define UTMIP_KEEP_PATT_ON_ACTIVE(x) (((x) & 0x3) << 30)
+#define UTMIP_ALLOW_CONSEC_UPDN (1 << 29)
+#define UTMIP_REALIGN_ON_NEW_PKT (1 << 28)
+#define UTMIP_PCOUNT_UPDN_DIV(x) (((x) & 0xf) << 24)
+#define UTMIP_SQUELCH_EOP_DLY(x) (((x) & 0x7) << 21)
+#define UTMIP_NO_STRIPPING (1 << 20)
+#define UTMIP_IDLE_WAIT(x) (((x) & 0x1f) << 15)
+#define UTMIP_ELASTIC_LIMIT(x) (((x) & 0x1f) << 10)
+#define UTMIP_ELASTIC_OVERRUN_DISABLE (1 << 9)
+#define UTMIP_ELASTIC_UNDERRUN_DISABLE (1 << 8)
+#define UTMIP_PASS_CHIRP (1 << 7)
+#define UTMIP_PASS_FEEDBACK (1 << 6)
+#define UTMIP_PCOUNT_INERTIA(x) (((x) & 0x3) << 4)
+#define UTMIP_PHASE_ADJUST(x) (((x) & 0x3) << 2)
+#define UTMIP_THREE_SYNCBITS (1 << 1)
+#define UTMIP_USE4SYNC_TRAN (1 << 0)
+
+#define UTMIP_HSRX_CFG1 0x814
+#define UTMIP_HS_SYNC_START_DLY(x) (((x) & 0x1F) << 1)
+#define UTMIP_HS_ALLOW_KEEP_ALIVE (1 << 0)
+
+#define UTMIP_TX_CFG0 0x820
+#define UTMIP_FS_PREAMBLE_J (1 << 19)
+#define UTMIP_FS_POSTAMBLE_OUTPUT_ENABLE (1 << 18)
+#define UTMIP_FS_PREAMBLE_OUTPUT_ENABLE (1 << 17)
+#define UTMIP_FSLS_ALLOW_SOP_TX_STUFF_ERR (1 << 16)
+#define UTMIP_HS_READY_WAIT_FOR_VALID (1 << 15)
+#define UTMIP_HS_TX_IPG_DLY(x) (((x) & 0x1f) << 10)
+#define UTMIP_HS_DISCON_EOP_ONLY (1 << 9)
+#define UTMIP_HS_DISCON_DISABLE (1 << 8)
+#define UTMIP_HS_POSTAMBLE_OUTPUT_ENABLE (1 << 7)
+#define UTMIP_HS_PREAMBLE_OUTPUT_ENABLE (1 << 6)
+#define UTMIP_SIE_RESUME_ON_LINESTATE (1 << 5)
+#define UTMIP_SOF_ON_NO_STUFF (1 << 4)
+#define UTMIP_SOF_ON_NO_ENCODE (1 << 3)
+#define UTMIP_NO_STUFFING (1 << 2)
+#define UTMIP_NO_ENCODING (1 << 1)
+#define UTMIP_NO_SYNC_NO_EOP (1 << 0)
+
+#define UTMIP_MISC_CFG0 0x824
+#define UTMIP_DPDM_OBSERVE_SEL(x) (((x) & 0xf) << 27)
+#define UTMIP_DPDM_OBSERVE (1 << 26)
+#define UTMIP_KEEP_XCVR_PD_ON_SOFT_DISCON (1 << 25)
+#define UTMIP_ALLOW_LS_ON_SOFT_DISCON (1 << 24)
+#define UTMIP_FORCE_FS_DISABLE_ON_DEV_CHIRP (1 << 23)
+#define UTMIP_SUSPEND_EXIT_ON_EDGE (1 << 22)
+#define UTMIP_LS_TO_FS_SKIP_4MS (1 << 21)
+#define UTMIP_INJECT_ERROR_TYPE(x) (((x) & 0x3) << 19)
+#define UTMIP_FORCE_HS_CLOCK_ON (1 << 18)
+#define UTMIP_DISABLE_HS_TERM (1 << 17)
+#define UTMIP_FORCE_HS_TERM (1 << 16)
+#define UTMIP_DISABLE_PULLUP_DP (1 << 15)
+#define UTMIP_DISABLE_PULLUP_DM (1 << 14)
+#define UTMIP_DISABLE_PULLDN_DP (1 << 13)
+#define UTMIP_DISABLE_PULLDN_DM (1 << 12)
+#define UTMIP_FORCE_PULLUP_DP (1 << 11)
+#define UTMIP_FORCE_PULLUP_DM (1 << 10)
+#define UTMIP_FORCE_PULLDN_DP (1 << 9)
+#define UTMIP_FORCE_PULLDN_DM (1 << 8)
+#define UTMIP_STABLE_COUNT(x) (((x) & 0x7) << 5)
+#define UTMIP_STABLE_ALL (1 << 4)
+#define UTMIP_NO_FREE_ON_SUSPEND (1 << 3)
+#define UTMIP_NEVER_FREE_RUNNING_TERMS (1 << 2)
+#define UTMIP_ALWAYS_FREE_RUNNING_TERMS (1 << 1)
+#define UTMIP_COMB_TERMS (1 << 0)
+
+#define UTMIP_MISC_CFG1 0x828
+#define UTMIP_PHY_XTAL_CLOCKEN (1 << 30)
+
+#define UTMIP_DEBOUNCE_CFG0 0x82C
+#define UTMIP_BIAS_DEBOUNCE_B(x) (((x) & 0xffff) << 16)
+#define UTMIP_BIAS_DEBOUNCE_A(x) (((x) & 0xffff) << 0)
+
+#define UTMIP_BAT_CHRG_CFG0 0x830
+#define UTMIP_CHRG_DEBOUNCE_TIMESCALE(x) (((x) & 0x1f) << 8)
+#define UTMIP_OP_I_SRC_ENG (1 << 5)
+#define UTMIP_ON_SRC_ENG (1 << 4)
+#define UTMIP_OP_SRC_ENG (1 << 3)
+#define UTMIP_ON_SINK_ENG (1 << 2)
+#define UTMIP_OP_SINK_ENG (1 << 1)
+#define UTMIP_PD_CHRG (1 << 0)
+
+#define UTMIP_SPARE_CFG0 0x834
+#define FUSE_HS_IREF_CAP_CFG (1 << 7)
+#define FUSE_HS_SQUELCH_LEVEL (1 << 6)
+#define FUSE_SPARE (1 << 5)
+#define FUSE_TERM_RANGE_ADJ_SEL (1 << 4)
+#define FUSE_SETUP_SEL (1 << 3)
+#define HS_RX_LATE_SQUELCH (1 << 2)
+#define HS_RX_FLUSH_ALAP (1 << 1)
+#define HS_RX_IPG_ERROR_ENABLE (1 << 0)
+
+#define UTMIP_XCVR_CFG1 0x838
+#define UTMIP_XCVR_RPU_RANGE_ADJ(x) (((x) & 0x3) << 26)
+#define UTMIP_XCVR_HS_IREF_CAP(x) (((x) & 0x3) << 24)
+#define UTMIP_XCVR_SPARE(x) (((x) & 0x3) << 22)
+#define UTMIP_XCVR_TERM_RANGE_ADJ(x) (((x) & 0xf) << 18)
+#define UTMIP_RCTRL_SW_SET (1 << 17)
+#define UTMIP_RCTRL_SW_VAL(x) (((x) & 0x1f) << 12)
+#define UTMIP_TCTRL_SW_SET (1 << 11)
+#define UTMIP_TCTRL_SW_VAL(x) (((x) & 0x1f) << 6)
+#define UTMIP_FORCE_PDDR_POWERUP (1 << 5)
+#define UTMIP_FORCE_PDDR_POWERDOWN (1 << 4)
+#define UTMIP_FORCE_PDCHRP_POWERUP (1 << 3)
+#define UTMIP_FORCE_PDCHRP_POWERDOWN (1 << 2)
+#define UTMIP_FORCE_PDDISC_POWERUP (1 << 1)
+#define UTMIP_FORCE_PDDISC_POWERDOWN (1 << 0)
+
+#define UTMIP_BIAS_CFG1 0x83c
+#define UTMIP_BIAS_DEBOUNCE_TIMESCALE(x) (((x) & 0x3f) << 8)
+#define UTMIP_BIAS_PDTRK_COUNT(x) (((x) & 0x1f) << 3)
+#define UTMIP_VBUS_WAKEUP_POWERDOWN (1 << 2)
+#define UTMIP_FORCE_PDTRK_POWERUP (1 << 1)
+#define UTMIP_FORCE_PDTRK_POWERDOWN (1 << 0)
+
+static int usbpby_enable_cnt;
+
+enum usb_ifc_type {
+ USB_IFC_TYPE_UNKNOWN = 0,
+ USB_IFC_TYPE_UTMI,
+ USB_IFC_TYPE_ULPI
+};
+
+enum usb_dr_mode {
+ USB_DR_MODE_UNKNOWN = 0,
+ USB_DR_MODE_DEVICE,
+ USB_DR_MODE_HOST,
+ USB_DR_MODE_OTG
+};
+
+struct usbphy_softc {
+ device_t dev;
+ struct resource *mem_res;
+ struct resource *pads_res;
+ clk_t clk_reg;
+ clk_t clk_pads;
+ clk_t clk_pllu;
+ regulator_t supply_vbus;
+ hwreset_t reset_usb;
+ hwreset_t reset_pads;
+ enum usb_ifc_type ifc_type;
+ enum usb_dr_mode dr_mode;
+ bool have_utmi_regs;
+
+ /* UTMI params */
+ int hssync_start_delay;
+ int elastic_limit;
+ int idle_wait_delay;
+ int term_range_adj;
+ int xcvr_lsfslew;
+ int xcvr_lsrslew;
+ int xcvr_hsslew;
+ int hssquelch_level;
+ int hsdiscon_level;
+ int xcvr_setup;
+ int xcvr_setup_use_fuses;
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"nvidia,tegra210-usb-phy", 1},
+ {"nvidia,tegra30-usb-phy", 1},
+ {NULL, 0},
+};
+
+ /* Phy controller class and methods. */
+static int usbphy_phy_enable(struct phynode *phy, bool enable);
+static phynode_method_t usbphy_phynode_methods[] = {
+ PHYNODEMETHOD(phynode_enable, usbphy_phy_enable),
+
+ PHYNODEMETHOD_END
+};
+DEFINE_CLASS_1(usbphy_phynode, usbphy_phynode_class, usbphy_phynode_methods,
+ 0, phynode_class);
+
+#define RD4(sc, offs) \
+ bus_read_4(sc->mem_res, offs)
+
+#define WR4(sc, offs, val) \
+ bus_write_4(sc->mem_res, offs, val)
+
+static int
+reg_wait(struct usbphy_softc *sc, uint32_t reg, uint32_t mask, uint32_t val)
+{
+ int i;
+
+ for (i = 0; i < 1000; i++) {
+ if ((RD4(sc, reg) & mask) == val)
+ return (0);
+ DELAY(10);
+ }
+ return (ETIMEDOUT);
+}
+
+static int
+usbphy_utmi_phy_clk(struct usbphy_softc *sc, bool enable)
+{
+ uint32_t val;
+ int rv;
+
+ val = RD4(sc, CTRL_USB_HOSTPC1_DEVLC);
+ if (enable)
+ val &= ~USB_HOSTPC1_DEVLC_PHCD;
+ else
+ val |= USB_HOSTPC1_DEVLC_PHCD;
+ WR4(sc, CTRL_USB_HOSTPC1_DEVLC, val);
+
+ rv = reg_wait(sc, IF_USB_SUSP_CTRL, USB_PHY_CLK_VALID,
+ enable ? USB_PHY_CLK_VALID: 0);
+ if (rv != 0) {
+ device_printf(sc->dev, "USB phy clock timeout.\n");
+ return (ETIMEDOUT);
+ }
+ return (0);
+}
+
+static int
+usbphy_utmi_enable(struct usbphy_softc *sc)
+{
+ int rv;
+ uint32_t val;
+
+ /* Reset phy */
+ val = RD4(sc, IF_USB_SUSP_CTRL);
+ val |= UTMIP_RESET;
+ WR4(sc, IF_USB_SUSP_CTRL, val);
+
+ val = RD4(sc, UTMIP_TX_CFG0);
+ val |= UTMIP_FS_PREAMBLE_J;
+ WR4(sc, UTMIP_TX_CFG0, val);
+
+ val = RD4(sc, UTMIP_HSRX_CFG0);
+ val &= ~UTMIP_IDLE_WAIT(~0);
+ val &= ~UTMIP_ELASTIC_LIMIT(~0);
+ val |= UTMIP_IDLE_WAIT(sc->idle_wait_delay);
+ val |= UTMIP_ELASTIC_LIMIT(sc->elastic_limit);
+ WR4(sc, UTMIP_HSRX_CFG0, val);
+
+ val = RD4(sc, UTMIP_HSRX_CFG1);
+ val &= ~UTMIP_HS_SYNC_START_DLY(~0);
+ val |= UTMIP_HS_SYNC_START_DLY(sc->hssync_start_delay);
+ WR4(sc, UTMIP_HSRX_CFG1, val);
+
+ val = RD4(sc, UTMIP_DEBOUNCE_CFG0);
+ val &= ~UTMIP_BIAS_DEBOUNCE_A(~0);
+ val |= UTMIP_BIAS_DEBOUNCE_A(0x7530); /* For 12MHz */
+ WR4(sc, UTMIP_DEBOUNCE_CFG0, val);
+
+ val = RD4(sc, UTMIP_MISC_CFG0);
+ val &= ~UTMIP_SUSPEND_EXIT_ON_EDGE;
+ WR4(sc, UTMIP_MISC_CFG0, val);
+
+ if (sc->dr_mode == USB_DR_MODE_DEVICE) {
+ val = RD4(sc,IF_USB_SUSP_CTRL);
+ val &= ~USB_WAKE_ON_CNNT_EN_DEV;
+ val &= ~USB_WAKE_ON_DISCON_EN_DEV;
+ WR4(sc, IF_USB_SUSP_CTRL, val);
+
+ val = RD4(sc, UTMIP_BAT_CHRG_CFG0);
+ val &= ~UTMIP_PD_CHRG;
+ WR4(sc, UTMIP_BAT_CHRG_CFG0, val);
+ } else {
+ val = RD4(sc, UTMIP_BAT_CHRG_CFG0);
+ val |= UTMIP_PD_CHRG;
+ WR4(sc, UTMIP_BAT_CHRG_CFG0, val);
+ }
+
+ usbpby_enable_cnt++;
+ if (usbpby_enable_cnt == 1) {
+ rv = hwreset_deassert(sc->reset_pads);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot unreset 'utmi-pads' reset\n");
+ return (rv);
+ }
+ rv = clk_enable(sc->clk_pads);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot enable 'utmi-pads' clock\n");
+ return (rv);
+ }
+
+ val = bus_read_4(sc->pads_res, UTMIP_BIAS_CFG0);
+ val &= ~UTMIP_OTGPD;
+ val &= ~UTMIP_BIASPD;
+ val &= ~UTMIP_HSSQUELCH_LEVEL(~0);
+ val &= ~UTMIP_HSDISCON_LEVEL(~0);
+ val &= ~UTMIP_HSDISCON_LEVEL_MSB(~0);
+ val |= UTMIP_HSSQUELCH_LEVEL(sc->hssquelch_level);
+ val |= UTMIP_HSDISCON_LEVEL(sc->hsdiscon_level);
+ val |= UTMIP_HSDISCON_LEVEL_MSB(sc->hsdiscon_level);
+ bus_write_4(sc->pads_res, UTMIP_BIAS_CFG0, val);
+
+ rv = clk_disable(sc->clk_pads);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot disable 'utmi-pads' clock\n");
+ return (rv);
+ }
+ }
+
+ val = RD4(sc, UTMIP_XCVR_CFG0);
+ val &= ~UTMIP_FORCE_PD_POWERDOWN;
+ val &= ~UTMIP_FORCE_PD2_POWERDOWN ;
+ val &= ~UTMIP_FORCE_PDZI_POWERDOWN;
+ val &= ~UTMIP_XCVR_LSBIAS_SEL;
+ val &= ~UTMIP_XCVR_LSFSLEW(~0);
+ val &= ~UTMIP_XCVR_LSRSLEW(~0);
+ val &= ~UTMIP_XCVR_HSSLEW(~0);
+ val &= ~UTMIP_XCVR_HSSLEW_MSB(~0);
+ val |= UTMIP_XCVR_LSFSLEW(sc->xcvr_lsfslew);
+ val |= UTMIP_XCVR_LSRSLEW(sc->xcvr_lsrslew);
+ val |= UTMIP_XCVR_HSSLEW(sc->xcvr_hsslew);
+ val |= UTMIP_XCVR_HSSLEW_MSB(sc->xcvr_hsslew);
+ if (!sc->xcvr_setup_use_fuses) {
+ val &= ~UTMIP_XCVR_SETUP(~0);
+ val &= ~UTMIP_XCVR_SETUP_MSB(~0);
+ val |= UTMIP_XCVR_SETUP(sc->xcvr_setup);
+ val |= UTMIP_XCVR_SETUP_MSB(sc->xcvr_setup);
+ }
+ WR4(sc, UTMIP_XCVR_CFG0, val);
+
+ val = RD4(sc, UTMIP_XCVR_CFG1);
+ val &= ~UTMIP_FORCE_PDDISC_POWERDOWN;
+ val &= ~UTMIP_FORCE_PDCHRP_POWERDOWN;
+ val &= ~UTMIP_FORCE_PDDR_POWERDOWN;
+ val &= ~UTMIP_XCVR_TERM_RANGE_ADJ(~0);
+ val |= UTMIP_XCVR_TERM_RANGE_ADJ(sc->term_range_adj);
+ WR4(sc, UTMIP_XCVR_CFG1, val);
+
+ val = RD4(sc, UTMIP_BIAS_CFG1);
+ val &= ~UTMIP_BIAS_PDTRK_COUNT(~0);
+ val |= UTMIP_BIAS_PDTRK_COUNT(0x5);
+ WR4(sc, UTMIP_BIAS_CFG1, val);
+
+ val = RD4(sc, UTMIP_SPARE_CFG0);
+ if (sc->xcvr_setup_use_fuses)
+ val |= FUSE_SETUP_SEL;
+ else
+ val &= ~FUSE_SETUP_SEL;
+ WR4(sc, UTMIP_SPARE_CFG0, val);
+
+ val = RD4(sc, IF_USB_SUSP_CTRL);
+ val |= UTMIP_PHY_ENB;
+ WR4(sc, IF_USB_SUSP_CTRL, val);
+
+ val = RD4(sc, IF_USB_SUSP_CTRL);
+ val &= ~UTMIP_RESET;
+ WR4(sc, IF_USB_SUSP_CTRL, val);
+
+ usbphy_utmi_phy_clk(sc, true);
+
+ val = RD4(sc, CTRL_USB_USBMODE);
+ val &= ~USB_USBMODE_MASK;
+ if (sc->dr_mode == USB_DR_MODE_HOST)
+ val |= USB_USBMODE_HOST;
+ else
+ val |= USB_USBMODE_DEVICE;
+ WR4(sc, CTRL_USB_USBMODE, val);
+
+ val = RD4(sc, CTRL_USB_HOSTPC1_DEVLC);
+ val &= ~USB_HOSTPC1_DEVLC_PTS(~0);
+ val |= USB_HOSTPC1_DEVLC_PTS(0);
+ WR4(sc, CTRL_USB_HOSTPC1_DEVLC, val);
+
+ return (0);
+}
+
+static int
+usbphy_utmi_disable(struct usbphy_softc *sc)
+{
+ int rv;
+ uint32_t val;
+
+ usbphy_utmi_phy_clk(sc, false);
+
+ if (sc->dr_mode == USB_DR_MODE_DEVICE) {
+ val = RD4(sc, IF_USB_SUSP_CTRL);
+ val &= ~USB_WAKEUP_DEBOUNCE_COUNT(~0);
+ val |= USB_WAKE_ON_CNNT_EN_DEV;
+ val |= USB_WAKEUP_DEBOUNCE_COUNT(5);
+ WR4(sc, IF_USB_SUSP_CTRL, val);
+ }
+
+ val = RD4(sc, IF_USB_SUSP_CTRL);
+ val |= UTMIP_RESET;
+ WR4(sc, IF_USB_SUSP_CTRL, val);
+
+ val = RD4(sc, UTMIP_BAT_CHRG_CFG0);
+ val |= UTMIP_PD_CHRG;
+ WR4(sc, UTMIP_BAT_CHRG_CFG0, val);
+
+ val = RD4(sc, UTMIP_XCVR_CFG0);
+ val |= UTMIP_FORCE_PD_POWERDOWN;
+ val |= UTMIP_FORCE_PD2_POWERDOWN;
+ val |= UTMIP_FORCE_PDZI_POWERDOWN;
+ WR4(sc, UTMIP_XCVR_CFG0, val);
+
+ val = RD4(sc, UTMIP_XCVR_CFG1);
+ val |= UTMIP_FORCE_PDDISC_POWERDOWN;
+ val |= UTMIP_FORCE_PDCHRP_POWERDOWN;
+ val |= UTMIP_FORCE_PDDR_POWERDOWN;
+ WR4(sc, UTMIP_XCVR_CFG1, val);
+
+ usbpby_enable_cnt--;
+ if (usbpby_enable_cnt <= 0) {
+ rv = clk_enable(sc->clk_pads);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot enable 'utmi-pads' clock\n");
+ return (rv);
+ }
+ val =bus_read_4(sc->pads_res, UTMIP_BIAS_CFG0);
+ val |= UTMIP_OTGPD;
+ val |= UTMIP_BIASPD;
+ bus_write_4(sc->pads_res, UTMIP_BIAS_CFG0, val);
+
+ rv = clk_disable(sc->clk_pads);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot disable 'utmi-pads' clock\n");
+ return (rv);
+ }
+ }
+ return (0);
+}
+
+static int
+usbphy_phy_enable(struct phynode *phy, bool enable)
+{
+ device_t dev;
+ struct usbphy_softc *sc;
+ int rv = 0;
+
+ dev = phynode_get_device(phy);
+ sc = device_get_softc(dev);
+
+ if (sc->ifc_type != USB_IFC_TYPE_UTMI) {
+ device_printf(sc->dev,
+ "Only UTMI interface is supported.\n");
+ return (ENXIO);
+ }
+ if (enable)
+ rv = usbphy_utmi_enable(sc);
+ else
+ rv = usbphy_utmi_disable(sc);
+
+ return (rv);
+}
+
+static enum usb_ifc_type
+usb_get_ifc_mode(device_t dev, phandle_t node, char *name)
+{
+ char *tmpstr;
+ int rv;
+ enum usb_ifc_type ret;
+
+ rv = OF_getprop_alloc(node, name, (void **)&tmpstr);
+ if (rv <= 0)
+ return (USB_IFC_TYPE_UNKNOWN);
+
+ ret = USB_IFC_TYPE_UNKNOWN;
+ if (strcmp(tmpstr, "utmi") == 0)
+ ret = USB_IFC_TYPE_UTMI;
+ else if (strcmp(tmpstr, "ulpi") == 0)
+ ret = USB_IFC_TYPE_ULPI;
+ else
+ device_printf(dev, "Unsupported phy type: %s\n", tmpstr);
+ OF_prop_free(tmpstr);
+ return (ret);
+}
+
+static enum usb_dr_mode
+usb_get_dr_mode(device_t dev, phandle_t node, char *name)
+{
+ char *tmpstr;
+ int rv;
+ enum usb_dr_mode ret;
+
+ rv = OF_getprop_alloc(node, name, (void **)&tmpstr);
+ if (rv <= 0)
+ return (USB_DR_MODE_UNKNOWN);
+
+ ret = USB_DR_MODE_UNKNOWN;
+ if (strcmp(tmpstr, "device") == 0)
+ ret = USB_DR_MODE_DEVICE;
+ else if (strcmp(tmpstr, "host") == 0)
+ ret = USB_DR_MODE_HOST;
+ else if (strcmp(tmpstr, "otg") == 0)
+ ret = USB_DR_MODE_OTG;
+ else
+ device_printf(dev, "Unknown dr mode: %s\n", tmpstr);
+ OF_prop_free(tmpstr);
+ return (ret);
+}
+
+static int
+usbphy_utmi_read_params(struct usbphy_softc *sc, phandle_t node)
+{
+ int rv;
+
+ rv = OF_getencprop(node, "nvidia,hssync-start-delay",
+ &sc->hssync_start_delay, sizeof (sc->hssync_start_delay));
+ if (rv <= 0)
+ return (ENXIO);
+
+ rv = OF_getencprop(node, "nvidia,elastic-limit",
+ &sc->elastic_limit, sizeof (sc->elastic_limit));
+ if (rv <= 0)
+ return (ENXIO);
+
+ rv = OF_getencprop(node, "nvidia,idle-wait-delay",
+ &sc->idle_wait_delay, sizeof (sc->idle_wait_delay));
+ if (rv <= 0)
+ return (ENXIO);
+
+ rv = OF_getencprop(node, "nvidia,term-range-adj",
+ &sc->term_range_adj, sizeof (sc->term_range_adj));
+ if (rv <= 0)
+ return (ENXIO);
+
+ rv = OF_getencprop(node, "nvidia,xcvr-lsfslew",
+ &sc->xcvr_lsfslew, sizeof (sc->xcvr_lsfslew));
+ if (rv <= 0)
+ return (ENXIO);
+
+ rv = OF_getencprop(node, "nvidia,xcvr-lsrslew",
+ &sc->xcvr_lsrslew, sizeof (sc->xcvr_lsrslew));
+ if (rv <= 0)
+ return (ENXIO);
+
+ rv = OF_getencprop(node, "nvidia,xcvr-hsslew",
+ &sc->xcvr_hsslew, sizeof (sc->xcvr_hsslew));
+ if (rv <= 0)
+ return (ENXIO);
+
+ rv = OF_getencprop(node, "nvidia,hssquelch-level",
+ &sc->hssquelch_level, sizeof (sc->hssquelch_level));
+ if (rv <= 0)
+ return (ENXIO);
+
+ rv = OF_getencprop(node, "nvidia,hsdiscon-level",
+ &sc->hsdiscon_level, sizeof (sc->hsdiscon_level));
+ if (rv <= 0)
+ return (ENXIO);
+
+ rv = OF_getproplen(node, "nvidia,xcvr-setup-use-fuses");
+ if (rv >= 1) {
+ sc->xcvr_setup_use_fuses = 1;
+ } else {
+ rv = OF_getencprop(node, "nvidia,xcvr-setup",
+ &sc->xcvr_setup, sizeof (sc->xcvr_setup));
+ if (rv <= 0)
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static int
+usbphy_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+ return (ENXIO);
+
+ device_set_desc(dev, "Tegra USB phy");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+usbphy_attach(device_t dev)
+{
+ struct usbphy_softc *sc;
+ int rid, rv;
+ phandle_t node;
+ struct phynode *phynode;
+ struct phynode_init_def phy_init;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE | RF_SHAREABLE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "Cannot allocate memory resources\n");
+ return (ENXIO);
+ }
+
+ rid = 1;
+ sc->pads_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE | RF_SHAREABLE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "Cannot allocate memory resources\n");
+ return (ENXIO);
+ }
+
+ node = ofw_bus_get_node(dev);
+
+ rv = hwreset_get_by_ofw_name(sc->dev, 0, "usb", &sc->reset_usb);
+ if (rv != 0) {
+ device_printf(dev, "Cannot get 'usb' reset\n");
+ return (ENXIO);
+ }
+ rv = hwreset_get_by_ofw_name(sc->dev, 0, "utmi-pads", &sc->reset_pads);
+ if (rv != 0) {
+ device_printf(dev, "Cannot get 'utmi-pads' reset\n");
+ return (ENXIO);
+ }
+
+ rv = clk_get_by_ofw_name(sc->dev, 0, "reg", &sc->clk_reg);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'reg' clock\n");
+ return (ENXIO);
+ }
+ rv = clk_get_by_ofw_name(sc->dev, 0, "pll_u", &sc->clk_pllu);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'pll_u' clock\n");
+ return (ENXIO);
+ }
+ rv = clk_get_by_ofw_name(sc->dev, 0, "utmi-pads", &sc->clk_pads);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'utmi-pads' clock\n");
+ return (ENXIO);
+ }
+
+ rv = hwreset_deassert(sc->reset_usb);
+ if (rv != 0) {
+ device_printf(dev, "Cannot unreset 'usb' reset\n");
+ return (ENXIO);
+ }
+
+ rv = clk_enable(sc->clk_pllu);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot enable 'pllu' clock\n");
+ return (ENXIO);
+ }
+ rv = clk_enable(sc->clk_reg);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot enable 'reg' clock\n");
+ return (ENXIO);
+ }
+ if (OF_hasprop(node, "nvidia,has-utmi-pad-registers"))
+ sc->have_utmi_regs = true;
+
+ sc->dr_mode = usb_get_dr_mode(dev, node, "dr_mode");
+ if (sc->dr_mode == USB_DR_MODE_UNKNOWN)
+ sc->dr_mode = USB_DR_MODE_HOST;
+
+ sc->ifc_type = usb_get_ifc_mode(dev, node, "phy_type");
+
+ /* We supports only utmi phy mode for now .... */
+ if (sc->ifc_type != USB_IFC_TYPE_UTMI) {
+ device_printf(dev, "Unsupported phy type\n");
+ return (ENXIO);
+ }
+ rv = usbphy_utmi_read_params(sc, node);
+ if (rv < 0)
+ return rv;
+
+ if (OF_hasprop(node, "vbus-supply")) {
+ rv = regulator_get_by_ofw_property(sc->dev, 0, "vbus-supply",
+ &sc->supply_vbus);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot get \"vbus\" regulator\n");
+ return (ENXIO);
+ }
+ rv = regulator_enable(sc->supply_vbus);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot enable \"vbus\" regulator\n");
+ return (rv);
+ }
+ }
+
+ /* Create and register phy. */
+ bzero(&phy_init, sizeof(phy_init));
+ phy_init.id = 1;
+ phy_init.ofw_node = node;
+ phynode = phynode_create(dev, &usbphy_phynode_class, &phy_init);
+ if (phynode == NULL) {
+ device_printf(sc->dev, "Cannot create phy\n");
+ return (ENXIO);
+ }
+ if (phynode_register(phynode) == NULL) {
+ device_printf(sc->dev, "Cannot create phy\n");
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static int
+usbphy_detach(device_t dev)
+{
+
+ /* This device is always present. */
+ return (EBUSY);
+}
+
+static device_method_t tegra_usbphy_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, usbphy_probe),
+ DEVMETHOD(device_attach, usbphy_attach),
+ DEVMETHOD(device_detach, usbphy_detach),
+
+ DEVMETHOD_END
+};
+
+static devclass_t tegra_usbphy_devclass;
+static DEFINE_CLASS_0(usbphy, tegra_usbphy_driver, tegra_usbphy_methods,
+ sizeof(struct usbphy_softc));
+EARLY_DRIVER_MODULE(tegra_usbphy, simplebus, tegra_usbphy_driver,
+ tegra_usbphy_devclass, NULL, NULL, 79);
diff --git a/sys/arm/nvidia/tegra_xhci.c b/sys/arm/nvidia/tegra_xhci.c
new file mode 100644
index 000000000000..bfaf761232ee
--- /dev/null
+++ b/sys/arm/nvidia/tegra_xhci.c
@@ -0,0 +1,1125 @@
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * XHCI driver for Tegra SoCs.
+ */
+#include "opt_bus.h"
+#include "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/clock.h>
+#include <sys/condvar.h>
+#include <sys/firmware.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/systm.h>
+
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_kern.h>
+#include <vm/pmap.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/extres/phy/phy.h>
+#include <dev/extres/regulator/regulator.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/controller/xhci.h>
+#include <dev/usb/controller/xhcireg.h>
+
+#include <arm/nvidia/tegra_pmc.h>
+
+#include "usbdevs.h"
+
+/* FPCI address space */
+#define T_XUSB_CFG_0 0x000
+#define T_XUSB_CFG_1 0x004
+#define CFG_1_BUS_MASTER (1 << 2)
+#define CFG_1_MEMORY_SPACE (1 << 1)
+#define CFG_1_IO_SPACE (1 << 0)
+
+#define T_XUSB_CFG_2 0x008
+#define T_XUSB_CFG_3 0x00C
+#define T_XUSB_CFG_4 0x010
+#define CFG_4_BASE_ADDRESS(x) (((x) & 0x1FFFF) << 15)
+
+#define T_XUSB_CFG_5 0x014
+#define T_XUSB_CFG_ARU_MAILBOX_CMD 0x0E4
+#define ARU_MAILBOX_CMD_INT_EN (1U << 31)
+#define ARU_MAILBOX_CMD_DEST_XHCI (1 << 30)
+#define ARU_MAILBOX_CMD_DEST_SMI (1 << 29)
+#define ARU_MAILBOX_CMD_DEST_PME (1 << 28)
+#define ARU_MAILBOX_CMD_DEST_FALC (1 << 27)
+
+#define T_XUSB_CFG_ARU_MAILBOX_DATA_IN 0x0E8
+#define ARU_MAILBOX_DATA_IN_DATA(x) (((x) & 0xFFFFFF) << 0)
+#define ARU_MAILBOX_DATA_IN_TYPE(x) (((x) & 0x0000FF) << 24)
+
+#define T_XUSB_CFG_ARU_MAILBOX_DATA_OUT 0x0EC
+#define ARU_MAILBOX_DATA_OUT_DATA(x) (((x) >> 0) & 0xFFFFFF)
+#define ARU_MAILBOX_DATA_OUT_TYPE(x) (((x) >> 24) & 0x0000FF)
+
+#define T_XUSB_CFG_ARU_MAILBOX_OWNER 0x0F0
+#define ARU_MAILBOX_OWNER_SW 2
+#define ARU_MAILBOX_OWNER_FW 1
+#define ARU_MAILBOX_OWNER_NONE 0
+
+#define XUSB_CFG_ARU_C11_CSBRANGE 0x41C /* ! UNDOCUMENTED ! */
+#define ARU_C11_CSBRANGE_PAGE(x) ((x) >> 9)
+#define ARU_C11_CSBRANGE_ADDR(x) (0x800 + ((x) & 0x1FF))
+#define XUSB_CFG_ARU_SMI_INTR 0x428 /* ! UNDOCUMENTED ! */
+#define ARU_SMI_INTR_EN (1 << 3)
+#define ARU_SMI_INTR_FW_HANG (1 << 1)
+#define XUSB_CFG_ARU_RST 0x42C /* ! UNDOCUMENTED ! */
+#define ARU_RST_RESET (1 << 0)
+
+#define XUSB_HOST_CONFIGURATION 0x180
+#define CONFIGURATION_CLKEN_OVERRIDE (1U<< 31)
+#define CONFIGURATION_PW_NO_DEVSEL_ERR_CYA (1 << 19)
+#define CONFIGURATION_INITIATOR_READ_IDLE (1 << 18)
+#define CONFIGURATION_INITIATOR_WRITE_IDLE (1 << 17)
+#define CONFIGURATION_WDATA_LEAD_CYA (1 << 15)
+#define CONFIGURATION_WR_INTRLV_CYA (1 << 14)
+#define CONFIGURATION_TARGET_READ_IDLE (1 << 11)
+#define CONFIGURATION_TARGET_WRITE_IDLE (1 << 10)
+#define CONFIGURATION_MSI_VEC_EMPTY (1 << 9)
+#define CONFIGURATION_UFPCI_MSIAW (1 << 7)
+#define CONFIGURATION_UFPCI_PWPASSPW (1 << 6)
+#define CONFIGURATION_UFPCI_PASSPW (1 << 5)
+#define CONFIGURATION_UFPCI_PWPASSNPW (1 << 4)
+#define CONFIGURATION_DFPCI_PWPASSNPW (1 << 3)
+#define CONFIGURATION_DFPCI_RSPPASSPW (1 << 2)
+#define CONFIGURATION_DFPCI_PASSPW (1 << 1)
+#define CONFIGURATION_EN_FPCI (1 << 0)
+
+/* IPFS address space */
+#define XUSB_HOST_FPCI_ERROR_MASKS 0x184
+#define FPCI_ERROR_MASTER_ABORT (1 << 2)
+#define FPCI_ERRORI_DATA_ERROR (1 << 1)
+#define FPCI_ERROR_TARGET_ABORT (1 << 0)
+
+#define XUSB_HOST_INTR_MASK 0x188
+#define INTR_IP_INT_MASK (1 << 16)
+#define INTR_MSI_MASK (1 << 8)
+#define INTR_INT_MASK (1 << 0)
+
+#define XUSB_HOST_CLKGATE_HYSTERESIS 0x1BC
+
+ /* CSB Falcon CPU */
+#define XUSB_FALCON_CPUCTL 0x100
+#define CPUCTL_STOPPED (1 << 5)
+#define CPUCTL_HALTED (1 << 4)
+#define CPUCTL_HRESET (1 << 3)
+#define CPUCTL_SRESET (1 << 2)
+#define CPUCTL_STARTCPU (1 << 1)
+#define CPUCTL_IINVAL (1 << 0)
+
+#define XUSB_FALCON_BOOTVEC 0x104
+#define XUSB_FALCON_DMACTL 0x10C
+#define XUSB_FALCON_IMFILLRNG1 0x154
+#define IMFILLRNG1_TAG_HI(x) (((x) & 0xFFF) << 16)
+#define IMFILLRNG1_TAG_LO(x) (((x) & 0xFFF) << 0)
+#define XUSB_FALCON_IMFILLCTL 0x158
+
+/* CSB mempool */
+#define XUSB_CSB_MEMPOOL_APMAP 0x10181C
+#define APMAP_BOOTPATH (1U << 31)
+
+#define XUSB_CSB_MEMPOOL_ILOAD_ATTR 0x101A00
+#define XUSB_CSB_MEMPOOL_ILOAD_BASE_LO 0x101A04
+#define XUSB_CSB_MEMPOOL_ILOAD_BASE_HI 0x101A08
+#define XUSB_CSB_MEMPOOL_L2IMEMOP_SIZE 0x101A10
+#define L2IMEMOP_SIZE_OFFSET(x) (((x) & 0x3FF) << 8)
+#define L2IMEMOP_SIZE_SIZE(x) (((x) & 0x0FF) << 24)
+
+#define XUSB_CSB_MEMPOOL_L2IMEMOP_TRIG 0x101A14
+#define L2IMEMOP_INVALIDATE_ALL (0x40 << 24)
+#define L2IMEMOP_LOAD_LOCKED_RESULT (0x11 << 24)
+
+#define XUSB_CSB_MEMPOOL_L2IMEMOP_RESULT 0x101A18
+#define L2IMEMOP_RESULT_VLD (1U << 31)
+
+#define XUSB_CSB_IMEM_BLOCK_SIZE 256
+
+#define TEGRA_XHCI_SS_HIGH_SPEED 120000000
+#define TEGRA_XHCI_SS_LOW_SPEED 12000000
+
+/* MBOX commands. */
+#define MBOX_CMD_MSG_ENABLED 1
+#define MBOX_CMD_INC_FALC_CLOCK 2
+#define MBOX_CMD_DEC_FALC_CLOCK 3
+#define MBOX_CMD_INC_SSPI_CLOCK 4
+#define MBOX_CMD_DEC_SSPI_CLOCK 5
+#define MBOX_CMD_SET_BW 6
+#define MBOX_CMD_SET_SS_PWR_GATING 7
+#define MBOX_CMD_SET_SS_PWR_UNGATING 8
+#define MBOX_CMD_SAVE_DFE_CTLE_CTX 9
+#define MBOX_CMD_AIRPLANE_MODE_ENABLED 10
+#define MBOX_CMD_AIRPLANE_MODE_DISABLED 11
+#define MBOX_CMD_START_HSIC_IDLE 12
+#define MBOX_CMD_STOP_HSIC_IDLE 13
+#define MBOX_CMD_DBC_WAKE_STACK 14
+#define MBOX_CMD_HSIC_PRETEND_CONNECT 15
+#define MBOX_CMD_RESET_SSPI 16
+#define MBOX_CMD_DISABLE_SS_LFPS_DETECTION 17
+#define MBOX_CMD_ENABLE_SS_LFPS_DETECTION 18
+
+/* MBOX responses. */
+#define MBOX_CMD_ACK (0x80 + 0)
+#define MBOX_CMD_NAK (0x80 + 1)
+
+#define IPFS_WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res_ipfs, (_r), (_v))
+#define IPFS_RD4(_sc, _r) bus_read_4((_sc)->mem_res_ipfs, (_r))
+#define FPCI_WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res_fpci, (_r), (_v))
+#define FPCI_RD4(_sc, _r) bus_read_4((_sc)->mem_res_fpci, (_r))
+
+#define LOCK(_sc) mtx_lock(&(_sc)->mtx)
+#define UNLOCK(_sc) mtx_unlock(&(_sc)->mtx)
+#define SLEEP(_sc, timeout) \
+ mtx_sleep(sc, &sc->mtx, 0, "tegra_xhci", timeout);
+#define LOCK_INIT(_sc) \
+ mtx_init(&_sc->mtx, device_get_nameunit(_sc->dev), "tegra_xhci", MTX_DEF)
+#define LOCK_DESTROY(_sc) mtx_destroy(&_sc->mtx)
+#define ASSERT_LOCKED(_sc) mtx_assert(&_sc->mtx, MA_OWNED)
+#define ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED)
+
+struct tegra_xusb_fw_hdr {
+ uint32_t boot_loadaddr_in_imem;
+ uint32_t boot_codedfi_offset;
+ uint32_t boot_codetag;
+ uint32_t boot_codesize;
+
+ uint32_t phys_memaddr;
+ uint16_t reqphys_memsize;
+ uint16_t alloc_phys_memsize;
+
+ uint32_t rodata_img_offset;
+ uint32_t rodata_section_start;
+ uint32_t rodata_section_end;
+ uint32_t main_fnaddr;
+
+ uint32_t fwimg_cksum;
+ uint32_t fwimg_created_time;
+
+ uint32_t imem_resident_start;
+ uint32_t imem_resident_end;
+ uint32_t idirect_start;
+ uint32_t idirect_end;
+ uint32_t l2_imem_start;
+ uint32_t l2_imem_end;
+ uint32_t version_id;
+ uint8_t init_ddirect;
+ uint8_t reserved[3];
+ uint32_t phys_addr_log_buffer;
+ uint32_t total_log_entries;
+ uint32_t dequeue_ptr;
+ uint32_t dummy[2];
+ uint32_t fwimg_len;
+ uint8_t magic[8];
+ uint32_t ss_low_power_entry_timeout;
+ uint8_t num_hsic_port;
+ uint8_t ss_portmap;
+ uint8_t build;
+ uint8_t padding[137]; /* Pad to 256 bytes */
+};
+
+struct xhci_soc;
+struct tegra_xhci_softc {
+ struct xhci_softc xhci_softc;
+ device_t dev;
+ struct xhci_soc *soc;
+ struct mtx mtx;
+ struct resource *mem_res_fpci;
+ struct resource *mem_res_ipfs;
+ struct resource *irq_res_mbox;
+ void *irq_hdl_mbox;
+
+ clk_t clk_xusb_host;
+ clk_t clk_xusb_gate;
+ clk_t clk_xusb_falcon_src;
+ clk_t clk_xusb_ss;
+ clk_t clk_xusb_hs_src;
+ clk_t clk_xusb_fs_src;
+ hwreset_t hwreset_xusb_host;
+ hwreset_t hwreset_xusb_ss;
+ regulator_t regulators[16]; /* Safe maximum */
+ phy_t phys[8]; /* Safe maximum */
+
+ struct intr_config_hook irq_hook;
+ bool xhci_inited;
+ vm_offset_t fw_vaddr;
+ vm_size_t fw_size;
+};
+
+struct xhci_soc {
+ char *fw_name;
+ char **regulator_names;
+ char **phy_names;
+};
+
+/* Tegra 124 config */
+static char *tegra124_reg_names[] = {
+ "avddio-pex-supply",
+ "dvddio-pex-supply",
+ "avdd-usb-supply",
+ "avdd-pll-utmip-supply",
+ "avdd-pll-erefe-supply",
+ "avdd-usb-ss-pll-supply",
+ "hvdd-usb-ss-supply",
+ "hvdd-usb-ss-pll-e-supply",
+ NULL
+};
+
+static char *tegra124_phy_names[] = {
+ "usb2-0",
+ "usb2-1",
+ "usb2-2",
+ "usb3-0",
+ NULL
+};
+
+static struct xhci_soc tegra124_soc =
+{
+ .fw_name = "tegra124_xusb_fw",
+ .regulator_names = tegra124_reg_names,
+ .phy_names = tegra124_phy_names,
+};
+
+/* Tegra 210 config */
+static char *tegra210_reg_names[] = {
+ "dvddio-pex-supply",
+ "hvddio-pex-supply",
+ "avdd-usb-supply",
+ "avdd-pll-utmip-supply",
+ "avdd-pll-uerefe-supply",
+ "dvdd-usb-ss-pll-supply",
+ "hvdd-usb-ss-pll-e-supply",
+ NULL
+};
+
+static char *tegra210_phy_names[] = {
+ "usb2-0",
+ "usb2-1",
+ "usb2-2",
+ "usb2-3",
+ "usb3-0",
+ "usb3-1",
+ NULL
+};
+
+static struct xhci_soc tegra210_soc =
+{
+ .fw_name = "tegra210_xusb_fw",
+ .regulator_names = tegra210_reg_names,
+ .phy_names = tegra210_phy_names,
+};
+
+/* Compatible devices. */
+static struct ofw_compat_data compat_data[] = {
+ {"nvidia,tegra124-xusb", (uintptr_t)&tegra124_soc},
+ {"nvidia,tegra210-xusb", (uintptr_t)&tegra210_soc},
+ {NULL, 0}
+};
+
+
+static uint32_t
+CSB_RD4(struct tegra_xhci_softc *sc, uint32_t addr)
+{
+
+ FPCI_WR4(sc, XUSB_CFG_ARU_C11_CSBRANGE, ARU_C11_CSBRANGE_PAGE(addr));
+ return (FPCI_RD4(sc, ARU_C11_CSBRANGE_ADDR(addr)));
+}
+
+static void
+CSB_WR4(struct tegra_xhci_softc *sc, uint32_t addr, uint32_t val)
+{
+
+ FPCI_WR4(sc, XUSB_CFG_ARU_C11_CSBRANGE, ARU_C11_CSBRANGE_PAGE(addr));
+ FPCI_WR4(sc, ARU_C11_CSBRANGE_ADDR(addr), val);
+}
+
+static int
+get_fdt_resources(struct tegra_xhci_softc *sc, phandle_t node)
+{
+ int i, rv;
+
+ /* Regulators. */
+ for (i = 0; sc->soc->regulator_names[i] != NULL; i++) {
+ if (i >= nitems(sc->regulators)) {
+ device_printf(sc->dev,
+ "Too many regulators present in DT.\n");
+ return (EOVERFLOW);
+ }
+ rv = regulator_get_by_ofw_property(sc->dev, 0,
+ sc->soc->regulator_names[i], sc->regulators + i);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot get '%s' regulator\n",
+ sc->soc->regulator_names[i]);
+ return (ENXIO);
+ }
+ }
+
+ rv = hwreset_get_by_ofw_name(sc->dev, 0, "xusb_host",
+ &sc->hwreset_xusb_host);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'xusb_host' reset\n");
+ return (ENXIO);
+ }
+ rv = hwreset_get_by_ofw_name(sc->dev, 0, "xusb_ss",
+ &sc->hwreset_xusb_ss);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'xusb_ss' reset\n");
+ return (ENXIO);
+ }
+
+ /* Phys. */
+ for (i = 0; sc->soc->phy_names[i] != NULL; i++) {
+ if (i >= nitems(sc->phys)) {
+ device_printf(sc->dev,
+ "Too many phys present in DT.\n");
+ return (EOVERFLOW);
+ }
+ rv = phy_get_by_ofw_name(sc->dev, 0, sc->soc->phy_names[i],
+ sc->phys + i);
+ if (rv != 0 && rv != ENOENT) {
+ device_printf(sc->dev, "Cannot get '%s' phy.\n",
+ sc->soc->phy_names[i]);
+ return (ENXIO);
+ }
+ }
+
+ rv = clk_get_by_ofw_name(sc->dev, 0, "xusb_host",
+ &sc->clk_xusb_host);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'xusb_host' clock\n");
+ return (ENXIO);
+ }
+ rv = clk_get_by_ofw_name(sc->dev, 0, "xusb_falcon_src",
+ &sc->clk_xusb_falcon_src);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'xusb_falcon_src' clock\n");
+ return (ENXIO);
+ }
+ rv = clk_get_by_ofw_name(sc->dev, 0, "xusb_ss",
+ &sc->clk_xusb_ss);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'xusb_ss' clock\n");
+ return (ENXIO);
+ }
+ rv = clk_get_by_ofw_name(sc->dev, 0, "xusb_hs_src",
+ &sc->clk_xusb_hs_src);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'xusb_hs_src' clock\n");
+ return (ENXIO);
+ }
+ rv = clk_get_by_ofw_name(sc->dev, 0, "xusb_fs_src",
+ &sc->clk_xusb_fs_src);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'xusb_fs_src' clock\n");
+ return (ENXIO);
+ }
+ /* Clock xusb_gate is missing in mainstream DT */
+ rv = clk_get_by_name(sc->dev, "xusb_gate", &sc->clk_xusb_gate);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'xusb_gate' clock\n");
+ return (ENXIO);
+ }
+ return (0);
+}
+
+static int
+enable_fdt_resources(struct tegra_xhci_softc *sc)
+{
+ int i, rv;
+
+ rv = hwreset_assert(sc->hwreset_xusb_host);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot reset 'xusb_host' reset\n");
+ return (rv);
+ }
+ rv = hwreset_assert(sc->hwreset_xusb_ss);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot reset 'xusb_ss' reset\n");
+ return (rv);
+ }
+
+ /* Regulators. */
+ for (i = 0; i < nitems(sc->regulators); i++) {
+ if (sc->regulators[i] == NULL)
+ continue;
+ rv = regulator_enable(sc->regulators[i]);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot enable '%s' regulator\n",
+ sc->soc->regulator_names[i]);
+ return (rv);
+ }
+ }
+
+ /* Power off XUSB host and XUSB SS domains. */
+ rv = tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot powerdown 'xusba' domain\n");
+ return (rv);
+ }
+ rv = tegra_powergate_power_off(TEGRA_POWERGATE_XUSBC);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot powerdown 'xusbc' domain\n");
+ return (rv);
+ }
+
+ /* Setup XUSB ss_src clock first */
+ clk_set_freq(sc->clk_xusb_ss, TEGRA_XHCI_SS_HIGH_SPEED, 0);
+ if (rv != 0)
+ return (rv);
+
+ /* The XUSB gate clock must be enabled before XUSBA can be powered. */
+ rv = clk_enable(sc->clk_xusb_gate);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot enable 'xusb_gate' clock\n");
+ return (rv);
+ }
+
+ /* Power on XUSB host and XUSB SS domains. */
+ rv = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_XUSBC,
+ sc->clk_xusb_host, sc->hwreset_xusb_host);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot powerup 'xusbc' domain\n");
+ return (rv);
+ }
+ rv = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_XUSBA,
+ sc->clk_xusb_ss, sc->hwreset_xusb_ss);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot powerup 'xusba' domain\n");
+ return (rv);
+ }
+
+ /* Enable rest of clocks */
+ rv = clk_enable(sc->clk_xusb_falcon_src);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot enable 'xusb_falcon_src' clock\n");
+ return (rv);
+ }
+ rv = clk_enable(sc->clk_xusb_fs_src);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot enable 'xusb_fs_src' clock\n");
+ return (rv);
+ }
+ rv = clk_enable(sc->clk_xusb_hs_src);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot enable 'xusb_hs_src' clock\n");
+ return (rv);
+ }
+
+ /* Phys. */
+ for (i = 0; i < nitems(sc->phys); i++) {
+ if (sc->phys[i] == NULL)
+ continue;
+ rv = phy_enable(sc->phys[i]);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot enable '%s' phy\n",
+ sc->soc->phy_names[i]);
+ return (rv);
+ }
+ }
+
+ return (0);
+}
+
+/* Respond by ACK/NAK back to FW */
+static void
+mbox_send_ack(struct tegra_xhci_softc *sc, uint32_t cmd, uint32_t data)
+{
+ uint32_t reg;
+
+ reg = ARU_MAILBOX_DATA_IN_TYPE(cmd) | ARU_MAILBOX_DATA_IN_DATA(data);
+ FPCI_WR4(sc, T_XUSB_CFG_ARU_MAILBOX_DATA_IN, reg);
+
+ reg = FPCI_RD4(sc, T_XUSB_CFG_ARU_MAILBOX_CMD);
+ reg |= ARU_MAILBOX_CMD_DEST_FALC | ARU_MAILBOX_CMD_INT_EN;
+ FPCI_WR4(sc, T_XUSB_CFG_ARU_MAILBOX_CMD, reg);
+}
+
+/* Sent command to FW */
+static int
+mbox_send_cmd(struct tegra_xhci_softc *sc, uint32_t cmd, uint32_t data)
+{
+ uint32_t reg;
+ int i;
+
+ reg = FPCI_RD4(sc, T_XUSB_CFG_ARU_MAILBOX_OWNER);
+ if (reg != ARU_MAILBOX_OWNER_NONE) {
+ device_printf(sc->dev,
+ "CPU mailbox is busy: 0x%08X\n", reg);
+ return (EBUSY);
+ }
+ /* XXX Is this right? Retry loop? Wait before send? */
+ FPCI_WR4(sc, T_XUSB_CFG_ARU_MAILBOX_OWNER, ARU_MAILBOX_OWNER_SW);
+ reg = FPCI_RD4(sc, T_XUSB_CFG_ARU_MAILBOX_OWNER);
+ if (reg != ARU_MAILBOX_OWNER_SW) {
+ device_printf(sc->dev,
+ "Cannot acquire CPU mailbox: 0x%08X\n", reg);
+ return (EBUSY);
+ }
+ reg = ARU_MAILBOX_DATA_IN_TYPE(cmd) | ARU_MAILBOX_DATA_IN_DATA(data);
+ FPCI_WR4(sc, T_XUSB_CFG_ARU_MAILBOX_DATA_IN, reg);
+
+ reg = FPCI_RD4(sc, T_XUSB_CFG_ARU_MAILBOX_CMD);
+ reg |= ARU_MAILBOX_CMD_DEST_FALC | ARU_MAILBOX_CMD_INT_EN;
+ FPCI_WR4(sc, T_XUSB_CFG_ARU_MAILBOX_CMD, reg);
+
+ for (i = 250; i > 0; i--) {
+ reg = FPCI_RD4(sc, T_XUSB_CFG_ARU_MAILBOX_OWNER);
+ if (reg == ARU_MAILBOX_OWNER_NONE)
+ break;
+ DELAY(100);
+ }
+ if (i <= 0) {
+ device_printf(sc->dev,
+ "Command response timeout: 0x%08X\n", reg);
+ return (ETIMEDOUT);
+ }
+
+ return(0);
+}
+
+static void
+process_msg(struct tegra_xhci_softc *sc, uint32_t req_cmd, uint32_t req_data,
+ uint32_t *resp_cmd, uint32_t *resp_data)
+{
+ uint64_t freq;
+ int rv;
+
+ /* In most cases, data are echoed back. */
+ *resp_data = req_data;
+ switch (req_cmd) {
+ case MBOX_CMD_INC_FALC_CLOCK:
+ case MBOX_CMD_DEC_FALC_CLOCK:
+ rv = clk_set_freq(sc->clk_xusb_falcon_src, req_data * 1000ULL,
+ 0);
+ if (rv == 0) {
+ rv = clk_get_freq(sc->clk_xusb_falcon_src, &freq);
+ *resp_data = (uint32_t)(freq / 1000);
+ }
+ *resp_cmd = rv == 0 ? MBOX_CMD_ACK: MBOX_CMD_NAK;
+ break;
+
+ case MBOX_CMD_INC_SSPI_CLOCK:
+ case MBOX_CMD_DEC_SSPI_CLOCK:
+ rv = clk_set_freq(sc->clk_xusb_ss, req_data * 1000ULL,
+ 0);
+ if (rv == 0) {
+ rv = clk_get_freq(sc->clk_xusb_ss, &freq);
+ *resp_data = (uint32_t)(freq / 1000);
+ }
+ *resp_cmd = rv == 0 ? MBOX_CMD_ACK: MBOX_CMD_NAK;
+ break;
+
+ case MBOX_CMD_SET_BW:
+ /* No respense is expected. */
+ *resp_cmd = 0;
+ break;
+
+ case MBOX_CMD_SET_SS_PWR_GATING:
+ case MBOX_CMD_SET_SS_PWR_UNGATING:
+ *resp_cmd = MBOX_CMD_NAK;
+ break;
+
+ case MBOX_CMD_SAVE_DFE_CTLE_CTX:
+ /* Not implemented yet. */
+ *resp_cmd = MBOX_CMD_ACK;
+ break;
+
+ case MBOX_CMD_START_HSIC_IDLE:
+ case MBOX_CMD_STOP_HSIC_IDLE:
+ /* Not implemented yet. */
+ *resp_cmd = MBOX_CMD_NAK;
+ break;
+
+ case MBOX_CMD_DISABLE_SS_LFPS_DETECTION:
+ case MBOX_CMD_ENABLE_SS_LFPS_DETECTION:
+ /* Not implemented yet. */
+ *resp_cmd = MBOX_CMD_NAK;
+ break;
+
+ case MBOX_CMD_AIRPLANE_MODE_ENABLED:
+ case MBOX_CMD_AIRPLANE_MODE_DISABLED:
+ case MBOX_CMD_DBC_WAKE_STACK:
+ case MBOX_CMD_HSIC_PRETEND_CONNECT:
+ case MBOX_CMD_RESET_SSPI:
+ device_printf(sc->dev,
+ "Received unused/unexpected command: %u\n", req_cmd);
+ *resp_cmd = 0;
+ break;
+
+ default:
+ device_printf(sc->dev,
+ "Received unknown command: %u\n", req_cmd);
+ }
+}
+
+static void
+intr_mbox(void *arg)
+{
+ struct tegra_xhci_softc *sc;
+ uint32_t reg, msg, resp_cmd, resp_data;
+
+ sc = (struct tegra_xhci_softc *)arg;
+
+ /* Clear interrupt first */
+ reg = FPCI_RD4(sc, XUSB_CFG_ARU_SMI_INTR);
+ FPCI_WR4(sc, XUSB_CFG_ARU_SMI_INTR, reg);
+ if (reg & ARU_SMI_INTR_FW_HANG) {
+ device_printf(sc->dev,
+ "XUSB CPU firmware hang!!! CPUCTL: 0x%08X\n",
+ CSB_RD4(sc, XUSB_FALCON_CPUCTL));
+ }
+
+ msg = FPCI_RD4(sc, T_XUSB_CFG_ARU_MAILBOX_DATA_OUT);
+ resp_cmd = 0;
+ process_msg(sc, ARU_MAILBOX_DATA_OUT_TYPE(msg),
+ ARU_MAILBOX_DATA_OUT_DATA(msg), &resp_cmd, &resp_data);
+ if (resp_cmd != 0)
+ mbox_send_ack(sc, resp_cmd, resp_data);
+ else
+ FPCI_WR4(sc, T_XUSB_CFG_ARU_MAILBOX_OWNER,
+ ARU_MAILBOX_OWNER_NONE);
+
+ reg = FPCI_RD4(sc, T_XUSB_CFG_ARU_MAILBOX_CMD);
+ reg &= ~ARU_MAILBOX_CMD_DEST_SMI;
+ FPCI_WR4(sc, T_XUSB_CFG_ARU_MAILBOX_CMD, reg);
+
+}
+
+static int
+load_fw(struct tegra_xhci_softc *sc)
+{
+ const struct firmware *fw;
+ const struct tegra_xusb_fw_hdr *fw_hdr;
+ vm_paddr_t fw_paddr, fw_base;
+ vm_offset_t fw_vaddr;
+ vm_size_t fw_size;
+ uint32_t code_tags, code_size;
+ struct clocktime fw_clock;
+ struct timespec fw_timespec;
+ int i;
+
+ /* Reset ARU */
+ FPCI_WR4(sc, XUSB_CFG_ARU_RST, ARU_RST_RESET);
+ DELAY(3000);
+
+ /* Check if FALCON already runs */
+ if (CSB_RD4(sc, XUSB_CSB_MEMPOOL_ILOAD_BASE_LO) != 0) {
+ device_printf(sc->dev,
+ "XUSB CPU is already loaded, CPUCTL: 0x%08X\n",
+ CSB_RD4(sc, XUSB_FALCON_CPUCTL));
+ return (0);
+ }
+
+ fw = firmware_get(sc->soc->fw_name);
+ if (fw == NULL) {
+ device_printf(sc->dev, "Cannot read xusb firmware\n");
+ return (ENOENT);
+ }
+
+ /* Allocate uncached memory and copy firmware into. */
+ fw_hdr = (const struct tegra_xusb_fw_hdr *)fw->data;
+ fw_size = fw_hdr->fwimg_len;
+
+ fw_vaddr = kmem_alloc_contig(fw_size, M_WAITOK, 0, -1UL, PAGE_SIZE, 0,
+ VM_MEMATTR_UNCACHEABLE);
+ fw_paddr = vtophys(fw_vaddr);
+ fw_hdr = (const struct tegra_xusb_fw_hdr *)fw_vaddr;
+ memcpy((void *)fw_vaddr, fw->data, fw_size);
+
+ firmware_put(fw, FIRMWARE_UNLOAD);
+ sc->fw_vaddr = fw_vaddr;
+ sc->fw_size = fw_size;
+
+ /* Setup firmware physical address and size. */
+ fw_base = fw_paddr + sizeof(*fw_hdr);
+ CSB_WR4(sc, XUSB_CSB_MEMPOOL_ILOAD_ATTR, fw_size);
+ CSB_WR4(sc, XUSB_CSB_MEMPOOL_ILOAD_BASE_LO, fw_base & 0xFFFFFFFF);
+ CSB_WR4(sc, XUSB_CSB_MEMPOOL_ILOAD_BASE_HI, (uint64_t)fw_base >> 32);
+ CSB_WR4(sc, XUSB_CSB_MEMPOOL_APMAP, APMAP_BOOTPATH);
+
+ /* Invalidate full L2IMEM context. */
+ CSB_WR4(sc, XUSB_CSB_MEMPOOL_L2IMEMOP_TRIG,
+ L2IMEMOP_INVALIDATE_ALL);
+
+ /* Program load of L2IMEM by boot code. */
+ code_tags = howmany(fw_hdr->boot_codetag, XUSB_CSB_IMEM_BLOCK_SIZE);
+ code_size = howmany(fw_hdr->boot_codesize, XUSB_CSB_IMEM_BLOCK_SIZE);
+ CSB_WR4(sc, XUSB_CSB_MEMPOOL_L2IMEMOP_SIZE,
+ L2IMEMOP_SIZE_OFFSET(code_tags) |
+ L2IMEMOP_SIZE_SIZE(code_size));
+
+ /* Execute L2IMEM boot code fetch. */
+ CSB_WR4(sc, XUSB_CSB_MEMPOOL_L2IMEMOP_TRIG,
+ L2IMEMOP_LOAD_LOCKED_RESULT);
+
+ /* Program FALCON auto-fill range and block count */
+ CSB_WR4(sc, XUSB_FALCON_IMFILLCTL, code_size);
+ CSB_WR4(sc, XUSB_FALCON_IMFILLRNG1,
+ IMFILLRNG1_TAG_LO(code_tags) |
+ IMFILLRNG1_TAG_HI(code_tags + code_size));
+
+ CSB_WR4(sc, XUSB_FALCON_DMACTL, 0);
+ /* Wait for CPU */
+ for (i = 500; i > 0; i--) {
+ if (CSB_RD4(sc, XUSB_CSB_MEMPOOL_L2IMEMOP_RESULT) &
+ L2IMEMOP_RESULT_VLD)
+ break;
+ DELAY(100);
+ }
+ if (i <= 0) {
+ device_printf(sc->dev, "Timedout while wating for DMA, "
+ "state: 0x%08X\n",
+ CSB_RD4(sc, XUSB_CSB_MEMPOOL_L2IMEMOP_RESULT));
+ return (ETIMEDOUT);
+ }
+
+ /* Boot FALCON cpu */
+ CSB_WR4(sc, XUSB_FALCON_BOOTVEC, fw_hdr->boot_codetag);
+ CSB_WR4(sc, XUSB_FALCON_CPUCTL, CPUCTL_STARTCPU);
+
+ /* Wait for CPU */
+ for (i = 50; i > 0; i--) {
+ if (CSB_RD4(sc, XUSB_FALCON_CPUCTL) == CPUCTL_STOPPED)
+ break;
+ DELAY(100);
+ }
+ if (i <= 0) {
+ device_printf(sc->dev, "Timedout while wating for FALCON cpu, "
+ "state: 0x%08X\n", CSB_RD4(sc, XUSB_FALCON_CPUCTL));
+ return (ETIMEDOUT);
+ }
+
+ fw_timespec.tv_sec = fw_hdr->fwimg_created_time;
+ fw_timespec.tv_nsec = 0;
+ clock_ts_to_ct(&fw_timespec, &fw_clock);
+ device_printf(sc->dev,
+ " Falcon firmware version: %02X.%02X.%04X,"
+ " (%d/%d/%d %d:%02d:%02d UTC)\n",
+ (fw_hdr->version_id >> 24) & 0xFF,(fw_hdr->version_id >> 15) & 0xFF,
+ fw_hdr->version_id & 0xFFFF,
+ fw_clock.day, fw_clock.mon, fw_clock.year,
+ fw_clock.hour, fw_clock.min, fw_clock.sec);
+
+ return (0);
+}
+
+static int
+init_hw(struct tegra_xhci_softc *sc)
+{
+ int rv;
+ uint32_t reg;
+ rman_res_t base_addr;
+
+ base_addr = rman_get_start(sc->xhci_softc.sc_io_res);
+
+ /* Enable FPCI access */
+ reg = IPFS_RD4(sc, XUSB_HOST_CONFIGURATION);
+ reg |= CONFIGURATION_EN_FPCI;
+ IPFS_WR4(sc, XUSB_HOST_CONFIGURATION, reg);
+ IPFS_RD4(sc, XUSB_HOST_CONFIGURATION);
+
+ /* Program bar for XHCI base address */
+ reg = FPCI_RD4(sc, T_XUSB_CFG_4);
+ reg &= ~CFG_4_BASE_ADDRESS(~0);
+ reg |= CFG_4_BASE_ADDRESS((uint32_t)base_addr >> 15);
+ FPCI_WR4(sc, T_XUSB_CFG_4, reg);
+ FPCI_WR4(sc, T_XUSB_CFG_5, (uint32_t)((uint64_t)(base_addr) >> 32));
+
+ /* Enable bus master */
+ reg = FPCI_RD4(sc, T_XUSB_CFG_1);
+ reg |= CFG_1_IO_SPACE;
+ reg |= CFG_1_MEMORY_SPACE;
+ reg |= CFG_1_BUS_MASTER;
+ FPCI_WR4(sc, T_XUSB_CFG_1, reg);
+
+ /* Enable Interrupts */
+ reg = IPFS_RD4(sc, XUSB_HOST_INTR_MASK);
+ reg |= INTR_IP_INT_MASK;
+ IPFS_WR4(sc, XUSB_HOST_INTR_MASK, reg);
+
+ /* Set hysteresis */
+ IPFS_WR4(sc, XUSB_HOST_CLKGATE_HYSTERESIS, 128);
+
+ rv = load_fw(sc);
+ if (rv != 0)
+ return rv;
+ return (0);
+}
+
+static int
+tegra_xhci_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) {
+ device_set_desc(dev, "Nvidia Tegra XHCI controller");
+ return (BUS_PROBE_DEFAULT);
+ }
+ return (ENXIO);
+}
+
+static int
+tegra_xhci_detach(device_t dev)
+{
+ struct tegra_xhci_softc *sc;
+ struct xhci_softc *xsc;
+
+ sc = device_get_softc(dev);
+ xsc = &sc->xhci_softc;
+
+ /* during module unload there are lots of children leftover */
+ device_delete_children(dev);
+ if (sc->xhci_inited) {
+ usb_callout_drain(&xsc->sc_callout);
+ xhci_halt_controller(xsc);
+ }
+
+ if (xsc->sc_irq_res && xsc->sc_intr_hdl) {
+ bus_teardown_intr(dev, xsc->sc_irq_res, xsc->sc_intr_hdl);
+ xsc->sc_intr_hdl = NULL;
+ }
+ if (xsc->sc_irq_res) {
+ bus_release_resource(dev, SYS_RES_IRQ,
+ rman_get_rid(xsc->sc_irq_res), xsc->sc_irq_res);
+ xsc->sc_irq_res = NULL;
+ }
+ if (xsc->sc_io_res != NULL) {
+ bus_release_resource(dev, SYS_RES_MEMORY,
+ rman_get_rid(xsc->sc_io_res), xsc->sc_io_res);
+ xsc->sc_io_res = NULL;
+ }
+ if (sc->xhci_inited)
+ xhci_uninit(xsc);
+ if (sc->irq_hdl_mbox != NULL)
+ bus_teardown_intr(dev, sc->irq_res_mbox, sc->irq_hdl_mbox);
+ if (sc->fw_vaddr != 0)
+ kmem_free(sc->fw_vaddr, sc->fw_size);
+ LOCK_DESTROY(sc);
+ return (0);
+}
+
+static int
+tegra_xhci_attach(device_t dev)
+{
+ struct tegra_xhci_softc *sc;
+ struct xhci_softc *xsc;
+ int rv, rid;
+ phandle_t node;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ sc->soc = (struct xhci_soc *)ofw_bus_search_compatible(dev,
+ compat_data)->ocd_data;
+ node = ofw_bus_get_node(dev);
+ xsc = &sc->xhci_softc;
+ LOCK_INIT(sc);
+
+ rv = get_fdt_resources(sc, node);
+ if (rv != 0) {
+ rv = ENXIO;
+ goto error;
+ }
+ rv = enable_fdt_resources(sc);
+ if (rv != 0) {
+ rv = ENXIO;
+ goto error;
+ }
+
+ /* Allocate resources. */
+ rid = 0;
+ xsc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (xsc->sc_io_res == NULL) {
+ device_printf(dev,
+ "Could not allocate HCD memory resources\n");
+ rv = ENXIO;
+ goto error;
+ }
+ rid = 1;
+ sc->mem_res_fpci = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->mem_res_fpci == NULL) {
+ device_printf(dev,
+ "Could not allocate FPCI memory resources\n");
+ rv = ENXIO;
+ goto error;
+ }
+ rid = 2;
+ sc->mem_res_ipfs = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->mem_res_ipfs == NULL) {
+ device_printf(dev,
+ "Could not allocate IPFS memory resources\n");
+ rv = ENXIO;
+ goto error;
+ }
+
+ rid = 0;
+ xsc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (xsc->sc_irq_res == NULL) {
+ device_printf(dev, "Could not allocate HCD IRQ resources\n");
+ rv = ENXIO;
+ goto error;
+ }
+ rid = 1;
+ sc->irq_res_mbox = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (sc->irq_res_mbox == NULL) {
+ device_printf(dev, "Could not allocate MBOX IRQ resources\n");
+ rv = ENXIO;
+ goto error;
+ }
+
+ rv = init_hw(sc);
+ if (rv != 0) {
+ device_printf(dev, "Could not initialize XUSB hardware\n");
+ goto error;
+ }
+
+ /* Wakeup and enable firmaware */
+ rv = mbox_send_cmd(sc, MBOX_CMD_MSG_ENABLED, 0);
+ if (rv != 0) {
+ device_printf(sc->dev, "Could not enable XUSB firmware\n");
+ goto error;
+ }
+
+ /* Fill data for XHCI driver. */
+ xsc->sc_bus.parent = dev;
+ xsc->sc_bus.devices = xsc->sc_devices;
+ xsc->sc_bus.devices_max = XHCI_MAX_DEVICES;
+
+ xsc->sc_io_tag = rman_get_bustag(xsc->sc_io_res);
+ xsc->sc_io_hdl = rman_get_bushandle(xsc->sc_io_res);
+ xsc->sc_io_size = rman_get_size(xsc->sc_io_res);
+ strlcpy(xsc->sc_vendor, "Nvidia", sizeof(xsc->sc_vendor));
+
+ /* Add USB bus device. */
+ xsc->sc_bus.bdev = device_add_child(sc->dev, "usbus", -1);
+ if (xsc->sc_bus.bdev == NULL) {
+ device_printf(sc->dev, "Could not add USB device\n");
+ rv = ENXIO;
+ goto error;
+ }
+ device_set_ivars(xsc->sc_bus.bdev, &xsc->sc_bus);
+ device_set_desc(xsc->sc_bus.bdev, "Nvidia USB 3.0 controller");
+
+ rv = xhci_init(xsc, sc->dev, 1);
+ if (rv != 0) {
+ device_printf(sc->dev, "USB init failed: %d\n", rv);
+ goto error;
+ }
+ sc->xhci_inited = true;
+ rv = xhci_start_controller(xsc);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Could not start XHCI controller: %d\n", rv);
+ goto error;
+ }
+
+ rv = bus_setup_intr(dev, sc->irq_res_mbox, INTR_TYPE_MISC | INTR_MPSAFE,
+ NULL, intr_mbox, sc, &sc->irq_hdl_mbox);
+ if (rv != 0) {
+ device_printf(dev, "Could not setup error IRQ: %d\n",rv);
+ xsc->sc_intr_hdl = NULL;
+ goto error;
+ }
+
+ rv = bus_setup_intr(dev, xsc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ NULL, (driver_intr_t *)xhci_interrupt, xsc, &xsc->sc_intr_hdl);
+ if (rv != 0) {
+ device_printf(dev, "Could not setup error IRQ: %d\n",rv);
+ xsc->sc_intr_hdl = NULL;
+ goto error;
+ }
+
+ /* Probe the bus. */
+ rv = device_probe_and_attach(xsc->sc_bus.bdev);
+ if (rv != 0) {
+ device_printf(sc->dev, "Could not initialize USB: %d\n", rv);
+ goto error;
+ }
+
+ return (0);
+
+error:
+panic("XXXXX");
+ tegra_xhci_detach(dev);
+ return (rv);
+}
+
+static device_method_t xhci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, tegra_xhci_probe),
+ DEVMETHOD(device_attach, tegra_xhci_attach),
+ DEVMETHOD(device_detach, tegra_xhci_detach),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+
+ DEVMETHOD_END
+};
+
+static devclass_t xhci_devclass;
+static DEFINE_CLASS_0(xhci, xhci_driver, xhci_methods,
+ sizeof(struct tegra_xhci_softc));
+DRIVER_MODULE(tegra_xhci, simplebus, xhci_driver, xhci_devclass, NULL, NULL);
+MODULE_DEPEND(tegra_xhci, usb, 1, 1, 1);
diff --git a/sys/arm/qemu/files.qemu b/sys/arm/qemu/files.qemu
new file mode 100644
index 000000000000..91992f1b5104
--- /dev/null
+++ b/sys/arm/qemu/files.qemu
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+#
+# Standard qemu virt devices and support.
+#
+arm/qemu/virt_machdep.c standard
+arm/qemu/virt_mp.c optional smp
diff --git a/sys/arm/qemu/std.virt b/sys/arm/qemu/std.virt
new file mode 100644
index 000000000000..ac00fc9bc613
--- /dev/null
+++ b/sys/arm/qemu/std.virt
@@ -0,0 +1,10 @@
+# $FreeBSD$
+machine arm armv7
+cpu CPU_CORTEXA
+makeoptions CONF_CFLAGS="-march=armv7a"
+
+options KERNVIRTADDR = 0xc1000000
+makeoptions KERNVIRTADDR = 0xc1000000
+
+files "../qemu/files.qemu"
+
diff --git a/sys/arm/qemu/virt_machdep.c b/sys/arm/qemu/virt_machdep.c
new file mode 100644
index 000000000000..dc8e4fcba860
--- /dev/null
+++ b/sys/arm/qemu/virt_machdep.c
@@ -0,0 +1,84 @@
+/*-
+ * Copyright (c) 2015 Andrew Turner
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "opt_platform.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/devmap.h>
+
+#include <vm/vm.h>
+
+#include <machine/bus.h>
+#include <machine/platform.h>
+#include <machine/platformvar.h>
+
+#include <arm/qemu/virt_mp.h>
+
+#include "platform_if.h"
+
+/*
+ * Set up static device mappings.
+ */
+static int
+virt_devmap_init(platform_t plat)
+{
+
+ devmap_add_entry(0x09000000, 0x100000); /* Uart */
+ return (0);
+}
+
+static platform_method_t virt_methods[] = {
+ PLATFORMMETHOD(platform_devmap_init, virt_devmap_init),
+
+#ifdef SMP
+ PLATFORMMETHOD(platform_mp_start_ap, virt_mp_start_ap),
+#endif
+
+ PLATFORMMETHOD_END,
+};
+
+FDT_PLATFORM_DEF(virt, "virt", 0, "linux,dummy-virt", 1);
+
+static int
+gem5_devmap_init(platform_t plat)
+{
+
+ devmap_add_entry(0x1c090000, 0x100000); /* Uart */
+ return (0);
+}
+
+static platform_method_t gem5_methods[] = {
+ PLATFORMMETHOD(platform_devmap_init, gem5_devmap_init),
+
+ PLATFORMMETHOD_END,
+};
+
+FDT_PLATFORM_DEF(gem5, "gem5", 0, "arm,vexpress", 1);
diff --git a/sys/arm/qemu/virt_mp.c b/sys/arm/qemu/virt_mp.c
new file mode 100644
index 000000000000..12a957caa897
--- /dev/null
+++ b/sys/arm/qemu/virt_mp.c
@@ -0,0 +1,74 @@
+/*-
+ * Copyright (c) 2015 Andrew Turner
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/pcpu.h>
+#include <sys/smp.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/intr.h>
+#include <machine/platformvar.h>
+#include <machine/smp.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_cpu.h>
+#include <dev/psci/psci.h>
+
+#include <arm/qemu/virt_mp.h>
+
+static int running_cpus;
+
+static boolean_t
+virt_start_ap(u_int id, phandle_t node, u_int addr_cells, pcell_t *reg)
+{
+ int err;
+
+ if (running_cpus >= mp_ncpus)
+ return (false);
+ running_cpus++;
+
+ err = psci_cpu_on(*reg, pmap_kextract((vm_offset_t)mpentry), id);
+
+ if (err != PSCI_RETVAL_SUCCESS)
+ return (false);
+
+ return (true);
+}
+
+void
+virt_mp_start_ap(platform_t plat)
+{
+
+ ofw_cpu_early_foreach(virt_start_ap, true);
+}
diff --git a/sys/arm/qemu/virt_mp.h b/sys/arm/qemu/virt_mp.h
new file mode 100644
index 000000000000..e21a2453c6ef
--- /dev/null
+++ b/sys/arm/qemu/virt_mp.h
@@ -0,0 +1,35 @@
+/*-
+ * Copyright (c) 2016 Andrew Turner
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _QEMU_VIRT_MP_H_
+#define _QEMU_VIRT_MP_H_
+
+void virt_mp_start_ap(platform_t plat);
+void virt_mp_setmaxid(platform_t plat);
+
+#endif /* _QEMU_VIRT_MP_H_ */
diff --git a/sys/arm/rockchip/files.rk32xx b/sys/arm/rockchip/files.rk32xx
new file mode 100644
index 000000000000..b4f5d5ce9d3a
--- /dev/null
+++ b/sys/arm/rockchip/files.rk32xx
@@ -0,0 +1,31 @@
+# $FreeBSD$
+kern/kern_clocksource.c standard
+
+arm/rockchip/rk32xx_machdep.c standard
+arm/rockchip/rk32xx_mp.c optional smp
+arm64/rockchip/if_dwc_rk.c standard
+arm64/rockchip/rk_i2c.c standard
+arm64/rockchip/rk_iodomain.c standard
+arm64/rockchip/rk_gpio.c standard
+arm64/rockchip/rk_grf.c standard
+arm64/rockchip/rk_pinctrl.c standard
+arm64/rockchip/rk_pmu.c standard
+arm64/rockchip/rk_pwm.c standard
+arm64/rockchip/rk_tsadc.c standard
+arm64/rockchip/rk_tsadc_if.m standard
+arm64/rockchip/rk_usbphy.c standard
+arm64/rockchip/clk/rk_clk_armclk.c standard
+arm64/rockchip/clk/rk_clk_composite.c standard
+arm64/rockchip/clk/rk_clk_fract.c standard
+arm64/rockchip/clk/rk_clk_gate.c standard
+arm64/rockchip/clk/rk_clk_mux.c standard
+arm64/rockchip/clk/rk_clk_pll.c standard
+arm64/rockchip/clk/rk_cru.c standard
+arm64/rockchip/clk/rk3288_cru.c standard
+
+dev/iicbus/pmic/act8846.c standard
+dev/iicbus/pmic/act8846_regulator.c standard
+dev/iicbus/pmic/fan53555.c standard
+dev/iicbus/rtc/hym8563.c standard
+dev/mmc/host/dwmmc.c optional dwmmc
+dev/mmc/host/dwmmc_rockchip.c optional dwmmc
diff --git a/sys/arm/rockchip/rk32xx_machdep.c b/sys/arm/rockchip/rk32xx_machdep.c
new file mode 100644
index 000000000000..8ee27f22dacd
--- /dev/null
+++ b/sys/arm/rockchip/rk32xx_machdep.c
@@ -0,0 +1,126 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2019 Michal Meloun <mmel@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "opt_platform.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/devmap.h>
+#include <sys/lock.h>
+#include <sys/reboot.h>
+
+#include <vm/vm.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+#include <machine/intr.h>
+#include <machine/machdep.h>
+#include <machine/platformvar.h>
+
+#include <dev/ofw/openfirm.h>
+
+#include <arm/rockchip/rk32xx_mp.h>
+
+#include "platform_if.h"
+#define CRU_PHYSBASE 0xFF760000
+#define CRU_SIZE 0x00010000
+#define CRU_GLB_SRST_FST_VALUE 0x1B0
+
+static platform_def_t rk3288w_platform;
+
+static void
+rk32xx_late_init(platform_t plat)
+{
+
+}
+
+/*
+ * Set up static device mappings.
+ */
+static int
+rk32xx_devmap_init(platform_t plat)
+{
+
+ devmap_add_entry(0xFF000000, 0x00E00000);
+ return (0);
+}
+
+static void
+rk32xx_cpu_reset(platform_t plat)
+{
+ bus_space_handle_t cru;
+
+ printf("Resetting...\n");
+ bus_space_map(fdtbus_bs_tag, CRU_PHYSBASE, CRU_SIZE, 0, &cru);
+
+ spinlock_enter();
+ dsb();
+ /* Generate 'first global software reset' */
+ bus_space_write_4(fdtbus_bs_tag, cru, CRU_GLB_SRST_FST_VALUE, 0xfdb9);
+ while(1)
+ ;
+}
+
+/*
+ * Early putc routine for EARLY_PRINTF support. To use, add to kernel config:
+ * option SOCDEV_PA=0xFF600000
+ * option SOCDEV_VA=0x70000000
+ * option EARLY_PRINTF
+ */
+#if 0
+#ifdef EARLY_PRINTF
+static void
+rk32xx_early_putc(int c)
+{
+
+ volatile uint32_t * UART_STAT_REG = (uint32_t *)(0x7009007C);
+ volatile uint32_t * UART_TX_REG = (uint32_t *)(0x70090000);
+ const uint32_t UART_TXRDY = (1 << 2);
+ while ((*UART_STAT_REG & UART_TXRDY) == 0)
+ continue;
+ *UART_TX_REG = c;
+}
+early_putc_t *early_putc = rk32xx_early_putc;
+#endif
+#endif
+static platform_method_t rk32xx_methods[] = {
+ PLATFORMMETHOD(platform_devmap_init, rk32xx_devmap_init),
+ PLATFORMMETHOD(platform_late_init, rk32xx_late_init),
+ PLATFORMMETHOD(platform_cpu_reset, rk32xx_cpu_reset),
+
+#ifdef SMP
+ PLATFORMMETHOD(platform_mp_start_ap, rk32xx_mp_start_ap),
+ PLATFORMMETHOD(platform_mp_setmaxid, rk32xx_mp_setmaxid),
+#endif
+ PLATFORMMETHOD_END,
+};
+FDT_PLATFORM_DEF2(rk32xx, rk3288, "RK3288", 0, "rockchip,rk3288", 200);
+FDT_PLATFORM_DEF2(rk32xx, rk3288w, "RK3288W", 0, "rockchip,rk3288w", 200);
diff --git a/sys/arm/rockchip/rk32xx_mp.c b/sys/arm/rockchip/rk32xx_mp.c
new file mode 100644
index 000000000000..089db173a0e9
--- /dev/null
+++ b/sys/arm/rockchip/rk32xx_mp.c
@@ -0,0 +1,174 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2019 Michal Meloun <mmel@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/smp.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/cpu.h>
+#include <machine/fdt.h>
+#include <machine/smp.h>
+#include <machine/platformvar.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_cpu.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/psci/psci.h>
+
+#include <arm/rockchip/rk32xx_mp.h>
+
+#define IMEM_PHYSBASE 0xFF700000
+#define IMEM_SIZE 0x00018000
+
+#define PMU_PHYSBASE 0xFF730000
+#define PMU_SIZE 0x00010000
+#define PMU_PWRDN_CON 0x08
+
+static int running_cpus;
+static uint32_t psci_mask, pmu_mask;
+void
+rk32xx_mp_setmaxid(platform_t plat)
+{
+ int ncpu;
+
+ /* If we've already set the global vars don't bother to do it again. */
+ if (mp_ncpus != 0)
+ return;
+
+ /* Read current CP15 Cache Size ID Register */
+ ncpu = cp15_l2ctlr_get();
+ ncpu = CPUV7_L2CTLR_NPROC(ncpu);
+
+ mp_ncpus = ncpu;
+ mp_maxid = ncpu - 1;
+}
+
+static void
+rk32xx_mp_start_pmu(uint32_t mask)
+{
+ bus_space_handle_t imem;
+ bus_space_handle_t pmu;
+ uint32_t val;
+ int i, rv;
+
+ rv = bus_space_map(fdtbus_bs_tag, IMEM_PHYSBASE, IMEM_SIZE, 0, &imem);
+ if (rv != 0)
+ panic("Couldn't map the IMEM\n");
+ rv = bus_space_map(fdtbus_bs_tag, PMU_PHYSBASE, PMU_SIZE, 0, &pmu);
+ if (rv != 0)
+ panic("Couldn't map the PMU\n");
+
+ /* Power off all secondary cores first */
+ val = bus_space_read_4(fdtbus_bs_tag, pmu, PMU_PWRDN_CON);
+ for (i = 1; i < mp_ncpus; i++)
+ val |= 1 << i;
+ bus_space_write_4(fdtbus_bs_tag, pmu, PMU_PWRDN_CON, val);
+ DELAY(5000);
+
+ /* Power up all secondary cores */
+ val = bus_space_read_4(fdtbus_bs_tag, pmu, PMU_PWRDN_CON);
+ for (i = 1; i < mp_ncpus; i++)
+ val &= ~(1 << i);
+ bus_space_write_4(fdtbus_bs_tag, pmu, PMU_PWRDN_CON, val);
+ DELAY(5000);
+
+ /* Copy mpentry address then magic to sram */
+ val = pmap_kextract((vm_offset_t)mpentry);
+ bus_space_write_4(fdtbus_bs_tag, imem, 8, val);
+ dsb();
+ bus_space_write_4(fdtbus_bs_tag, imem, 4, 0xDEADBEAF);
+ dsb();
+
+ sev();
+
+ bus_space_unmap(fdtbus_bs_tag, imem, IMEM_SIZE);
+ bus_space_unmap(fdtbus_bs_tag, pmu, PMU_SIZE);
+}
+
+static boolean_t
+rk32xx_start_ap(u_int id, phandle_t node, u_int addr_cells, pcell_t *reg)
+{
+ int rv;
+ char method[16];
+ uint32_t mask;
+
+ if (!ofw_bus_node_status_okay(node))
+ return(false);
+
+ /* Skip boot CPU. */
+ if (id == 0)
+ return (true);
+
+ if (running_cpus >= mp_ncpus)
+ return (false);
+ running_cpus++;
+
+ mask = 1 << (*reg & 0x0f);
+
+#ifdef INVARIANTS
+ if ((mask & pmu_mask) || (mask & psci_mask))
+ printf("CPU: Duplicated register value: 0x%X for CPU(%d)\n",
+ *reg, id);
+#endif
+ rv = OF_getprop(node, "enable-method", method, sizeof(method));
+ if (rv > 0 && strcmp(method, "psci") == 0) {
+ psci_mask |= mask;
+ rv = psci_cpu_on(*reg, pmap_kextract((vm_offset_t)mpentry), id);
+ if (rv != PSCI_RETVAL_SUCCESS) {
+ printf("Failed to start CPU(%d)\n", id);
+ return (false);
+ }
+ return (true);
+ }
+
+ pmu_mask |= mask;
+ return (true);
+}
+
+void
+rk32xx_mp_start_ap(platform_t plat)
+{
+
+ ofw_cpu_early_foreach(rk32xx_start_ap, true);
+ if (pmu_mask != 0 && psci_mask != 0) {
+ printf("Inconsistent CPUs startup methods detected.\n");
+ printf("Only PSCI enabled cores will be started.\n");
+ return;
+ }
+ if (pmu_mask != 0)
+ rk32xx_mp_start_pmu(pmu_mask);
+}
diff --git a/sys/arm/rockchip/rk32xx_mp.h b/sys/arm/rockchip/rk32xx_mp.h
new file mode 100644
index 000000000000..bd63c3da756b
--- /dev/null
+++ b/sys/arm/rockchip/rk32xx_mp.h
@@ -0,0 +1,36 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2019 Michal Meloun <mmel@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _RK32XX_MP_H_
+#define _RK32XX_MP_H_
+
+void rk32xx_mp_setmaxid(platform_t plat);
+void rk32xx_mp_start_ap(platform_t plat);
+
+#endif /* _RK32XX_MP_H_ */
diff --git a/sys/arm/rockchip/std.rk32xx b/sys/arm/rockchip/std.rk32xx
new file mode 100644
index 000000000000..d96045bfdfbe
--- /dev/null
+++ b/sys/arm/rockchip/std.rk32xx
@@ -0,0 +1,8 @@
+# Rockchip rk32xx common options
+#$FreeBSD$
+
+cpu CPU_CORTEXA
+machine arm armv7
+makeoptions CONF_CFLAGS="-mcpu=cortex-a17"
+
+files "../rockchip/files.rk32xx"
diff --git a/sys/arm/ti/aintc.c b/sys/arm/ti/aintc.c
new file mode 100644
index 000000000000..118a97e9f989
--- /dev/null
+++ b/sys/arm/ti/aintc.c
@@ -0,0 +1,309 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Damjan Marion <dmarion@Freebsd.org>
+ * All rights reserved.
+ *
+ * Based on OMAP3 INTC code by Ben Gray
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/ktr.h>
+#include <sys/module.h>
+#include <sys/proc.h>
+#include <sys/rman.h>
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "pic_if.h"
+
+#define INTC_REVISION 0x00
+#define INTC_SYSCONFIG 0x10
+#define INTC_SYSSTATUS 0x14
+#define INTC_SIR_IRQ 0x40
+#define INTC_CONTROL 0x48
+#define INTC_THRESHOLD 0x68
+#define INTC_MIR_CLEAR(x) (0x88 + ((x) * 0x20))
+#define INTC_MIR_SET(x) (0x8C + ((x) * 0x20))
+#define INTC_ISR_SET(x) (0x90 + ((x) * 0x20))
+#define INTC_ISR_CLEAR(x) (0x94 + ((x) * 0x20))
+
+#define INTC_SIR_SPURIOUS_MASK 0xffffff80
+#define INTC_SIR_ACTIVE_MASK 0x7f
+
+#define INTC_NIRQS 128
+
+struct ti_aintc_irqsrc {
+ struct intr_irqsrc tai_isrc;
+ u_int tai_irq;
+};
+
+struct ti_aintc_softc {
+ device_t sc_dev;
+ struct resource * aintc_res[3];
+ bus_space_tag_t aintc_bst;
+ bus_space_handle_t aintc_bsh;
+ uint8_t ver;
+ struct ti_aintc_irqsrc aintc_isrcs[INTC_NIRQS];
+};
+
+static struct resource_spec ti_aintc_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+#define aintc_read_4(_sc, reg) \
+ bus_space_read_4((_sc)->aintc_bst, (_sc)->aintc_bsh, (reg))
+#define aintc_write_4(_sc, reg, val) \
+ bus_space_write_4((_sc)->aintc_bst, (_sc)->aintc_bsh, (reg), (val))
+
+/* List of compatible strings for FDT tree */
+static struct ofw_compat_data compat_data[] = {
+ {"ti,am33xx-intc", 1},
+ {"ti,omap2-intc", 1},
+ {NULL, 0},
+};
+
+static inline void
+ti_aintc_irq_eoi(struct ti_aintc_softc *sc)
+{
+
+ aintc_write_4(sc, INTC_CONTROL, 1);
+}
+
+static inline void
+ti_aintc_irq_mask(struct ti_aintc_softc *sc, u_int irq)
+{
+
+ aintc_write_4(sc, INTC_MIR_SET(irq >> 5), (1UL << (irq & 0x1F)));
+}
+
+static inline void
+ti_aintc_irq_unmask(struct ti_aintc_softc *sc, u_int irq)
+{
+
+ aintc_write_4(sc, INTC_MIR_CLEAR(irq >> 5), (1UL << (irq & 0x1F)));
+}
+
+static int
+ti_aintc_intr(void *arg)
+{
+ uint32_t irq;
+ struct ti_aintc_softc *sc = arg;
+
+ /* Get active interrupt */
+ irq = aintc_read_4(sc, INTC_SIR_IRQ);
+ if ((irq & INTC_SIR_SPURIOUS_MASK) != 0) {
+ device_printf(sc->sc_dev,
+ "Spurious interrupt detected (0x%08x)\n", irq);
+ ti_aintc_irq_eoi(sc);
+ return (FILTER_HANDLED);
+ }
+
+ /* Only level-sensitive interrupts detection is supported. */
+ irq &= INTC_SIR_ACTIVE_MASK;
+ if (intr_isrc_dispatch(&sc->aintc_isrcs[irq].tai_isrc,
+ curthread->td_intr_frame) != 0) {
+ ti_aintc_irq_mask(sc, irq);
+ ti_aintc_irq_eoi(sc);
+ device_printf(sc->sc_dev, "Stray irq %u disabled\n", irq);
+ }
+
+ arm_irq_memory_barrier(irq); /* XXX */
+ return (FILTER_HANDLED);
+}
+
+static void
+ti_aintc_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ u_int irq = ((struct ti_aintc_irqsrc *)isrc)->tai_irq;
+ struct ti_aintc_softc *sc = device_get_softc(dev);
+
+ arm_irq_memory_barrier(irq);
+ ti_aintc_irq_unmask(sc, irq);
+}
+
+static void
+ti_aintc_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ u_int irq = ((struct ti_aintc_irqsrc *)isrc)->tai_irq;
+ struct ti_aintc_softc *sc = device_get_softc(dev);
+
+ ti_aintc_irq_mask(sc, irq);
+}
+
+static int
+ti_aintc_map_intr(device_t dev, struct intr_map_data *data,
+ struct intr_irqsrc **isrcp)
+{
+ struct intr_map_data_fdt *daf;
+ struct ti_aintc_softc *sc;
+
+ if (data->type != INTR_MAP_DATA_FDT)
+ return (ENOTSUP);
+
+ daf = (struct intr_map_data_fdt *)data;
+ if (daf->ncells != 1 || daf->cells[0] >= INTC_NIRQS)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+ *isrcp = &sc->aintc_isrcs[daf->cells[0]].tai_isrc;
+ return (0);
+}
+
+static void
+ti_aintc_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+ u_int irq = ((struct ti_aintc_irqsrc *)isrc)->tai_irq;
+ struct ti_aintc_softc *sc = device_get_softc(dev);
+
+ ti_aintc_irq_mask(sc, irq);
+ ti_aintc_irq_eoi(sc);
+}
+
+static void
+ti_aintc_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+
+ ti_aintc_enable_intr(dev, isrc);
+}
+
+static void
+ti_aintc_post_filter(device_t dev, struct intr_irqsrc *isrc)
+{
+
+ ti_aintc_irq_eoi(device_get_softc(dev));
+}
+
+static int
+ti_aintc_pic_attach(struct ti_aintc_softc *sc)
+{
+ struct intr_pic *pic;
+ int error;
+ uint32_t irq;
+ const char *name;
+ intptr_t xref;
+
+ name = device_get_nameunit(sc->sc_dev);
+ for (irq = 0; irq < INTC_NIRQS; irq++) {
+ sc->aintc_isrcs[irq].tai_irq = irq;
+
+ error = intr_isrc_register(&sc->aintc_isrcs[irq].tai_isrc,
+ sc->sc_dev, 0, "%s,%u", name, irq);
+ if (error != 0)
+ return (error);
+ }
+
+ xref = OF_xref_from_node(ofw_bus_get_node(sc->sc_dev));
+ pic = intr_pic_register(sc->sc_dev, xref);
+ if (pic == NULL)
+ return (ENXIO);
+
+ return (intr_pic_claim_root(sc->sc_dev, xref, ti_aintc_intr, sc, 0));
+}
+
+static int
+ti_aintc_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "TI AINTC Interrupt Controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ti_aintc_attach(device_t dev)
+{
+ struct ti_aintc_softc *sc = device_get_softc(dev);
+ uint32_t x;
+
+ sc->sc_dev = dev;
+
+ if (bus_alloc_resources(dev, ti_aintc_spec, sc->aintc_res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ sc->aintc_bst = rman_get_bustag(sc->aintc_res[0]);
+ sc->aintc_bsh = rman_get_bushandle(sc->aintc_res[0]);
+
+ x = aintc_read_4(sc, INTC_REVISION);
+ device_printf(dev, "Revision %u.%u\n",(x >> 4) & 0xF, x & 0xF);
+
+ /* SoftReset */
+ aintc_write_4(sc, INTC_SYSCONFIG, 2);
+
+ /* Wait for reset to complete */
+ while(!(aintc_read_4(sc, INTC_SYSSTATUS) & 1));
+
+ /*Set Priority Threshold */
+ aintc_write_4(sc, INTC_THRESHOLD, 0xFF);
+
+ if (ti_aintc_pic_attach(sc) != 0) {
+ device_printf(dev, "could not attach PIC\n");
+ return (ENXIO);
+ }
+ return (0);
+}
+
+static device_method_t ti_aintc_methods[] = {
+ DEVMETHOD(device_probe, ti_aintc_probe),
+ DEVMETHOD(device_attach, ti_aintc_attach),
+
+ DEVMETHOD(pic_disable_intr, ti_aintc_disable_intr),
+ DEVMETHOD(pic_enable_intr, ti_aintc_enable_intr),
+ DEVMETHOD(pic_map_intr, ti_aintc_map_intr),
+ DEVMETHOD(pic_post_filter, ti_aintc_post_filter),
+ DEVMETHOD(pic_post_ithread, ti_aintc_post_ithread),
+ DEVMETHOD(pic_pre_ithread, ti_aintc_pre_ithread),
+ { 0, 0 }
+};
+
+static driver_t ti_aintc_driver = {
+ "ti_aintc",
+ ti_aintc_methods,
+ sizeof(struct ti_aintc_softc),
+};
+
+static devclass_t ti_aintc_devclass;
+
+EARLY_DRIVER_MODULE(ti_aintc, simplebus, ti_aintc_driver, ti_aintc_devclass,
+ 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
+SIMPLEBUS_PNP_INFO(compat_data);
diff --git a/sys/arm/ti/am335x/am3359_cppi41.c b/sys/arm/ti/am335x/am3359_cppi41.c
new file mode 100644
index 000000000000..dd5e668776ee
--- /dev/null
+++ b/sys/arm/ti/am335x/am3359_cppi41.c
@@ -0,0 +1,190 @@
+/*-
+ * Copyright (c) 2019 Emmanuel Vadot <manu@FreeBSD.org>
+ *
+ * Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+/* Based on sys/arm/ti/ti_sysc.c */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/fdt/simplebus.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/ti/ti_sysc.h>
+
+#if 0
+#define DPRINTF(dev, msg...) device_printf(dev, msg)
+#else
+#define DPRINTF(dev, msg...)
+#endif
+
+struct ti_am3359_cppi41_softc {
+ device_t dev;
+ struct syscon * syscon;
+ struct resource * res[4];
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+ struct mtx mtx;
+};
+
+static struct resource_spec ti_am3359_cppi41_res_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE | RF_SHAREABLE },
+ { SYS_RES_MEMORY, 1, RF_ACTIVE | RF_SHAREABLE },
+ { SYS_RES_MEMORY, 2, RF_ACTIVE | RF_SHAREABLE },
+ { SYS_RES_MEMORY, 3, RF_ACTIVE | RF_SHAREABLE },
+ { -1, 0 }
+};
+
+/* Device */
+static struct ofw_compat_data compat_data[] = {
+ { "ti,am3359-cppi41", 1 },
+ { NULL, 0 }
+};
+
+static int
+ti_am3359_cppi41_write_4(device_t dev, bus_addr_t addr, uint32_t val)
+{
+ struct ti_am3359_cppi41_softc *sc;
+
+ sc = device_get_softc(dev);
+ DPRINTF(sc->dev, "offset=%lx write %x\n", addr, val);
+ mtx_lock(&sc->mtx);
+ bus_space_write_4(sc->bst, sc->bsh, addr, val);
+ mtx_unlock(&sc->mtx);
+ return (0);
+}
+
+static uint32_t
+ti_am3359_cppi41_read_4(device_t dev, bus_addr_t addr)
+{
+ struct ti_am3359_cppi41_softc *sc;
+ uint32_t val;
+
+ sc = device_get_softc(dev);
+
+ mtx_lock(&sc->mtx);
+ val = bus_space_read_4(sc->bst, sc->bsh, addr);
+ mtx_unlock(&sc->mtx);
+ DPRINTF(sc->dev, "offset=%lx Read %x\n", addr, val);
+ return (val);
+}
+
+/* device interface */
+static int
+ti_am3359_cppi41_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "TI AM3359 CPPI 41");
+ return(BUS_PROBE_DEFAULT);
+}
+
+static int
+ti_am3359_cppi41_attach(device_t dev)
+{
+ struct ti_am3359_cppi41_softc *sc;
+ phandle_t node;
+ uint32_t reg, reset_bit, timeout=10;
+ uint64_t sysc_address;
+ device_t parent;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ if (bus_alloc_resources(dev, ti_am3359_cppi41_res_spec, sc->res)) {
+ device_printf(sc->dev, "Cant allocate resources\n");
+ return (ENXIO);
+ }
+
+ sc->dev = dev;
+ sc->bst = rman_get_bustag(sc->res[0]);
+ sc->bsh = rman_get_bushandle(sc->res[0]);
+
+ mtx_init(&sc->mtx, device_get_nameunit(sc->dev), NULL, MTX_DEF);
+ node = ofw_bus_get_node(sc->dev);
+
+ /* variant of am335x_usbss.c */
+ DPRINTF(dev, "-- RESET USB --\n");
+ parent = device_get_parent(dev);
+ reset_bit = ti_sysc_get_soft_reset_bit(parent);
+ if (reset_bit == 0) {
+ DPRINTF(dev, "Dont have reset bit\n");
+ return (0);
+ }
+ sysc_address = ti_sysc_get_sysc_address_offset_host(parent);
+ DPRINTF(dev, "sysc_address %x\n", sysc_address);
+ ti_am3359_cppi41_write_4(dev, sysc_address, reset_bit);
+ DELAY(100);
+ reg = ti_am3359_cppi41_read_4(dev, sysc_address);
+ if ((reg & reset_bit) && timeout--) {
+ DPRINTF(dev, "Reset still ongoing - wait a little bit longer\n");
+ DELAY(100);
+ reg = ti_am3359_cppi41_read_4(dev, sysc_address);
+ }
+ if (timeout == 0)
+ device_printf(dev, "USB Reset timeout\n");
+
+ return (0);
+}
+
+static device_method_t ti_am3359_cppi41_methods[] = {
+ DEVMETHOD(device_probe, ti_am3359_cppi41_probe),
+ DEVMETHOD(device_attach, ti_am3359_cppi41_attach),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(ti_am3359_cppi41, ti_am3359_cppi41_driver,
+ ti_am3359_cppi41_methods,sizeof(struct ti_am3359_cppi41_softc),
+ simplebus_driver);
+
+static devclass_t ti_am3359_cppi41_devclass;
+
+EARLY_DRIVER_MODULE(ti_am3359_cppi41, simplebus, ti_am3359_cppi41_driver,
+ ti_am3359_cppi41_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
+MODULE_VERSION(ti_am3359_cppi41, 1);
+MODULE_DEPEND(ti_am3359_cppi41, ti_sysc, 1, 1, 1);
diff --git a/sys/arm/ti/am335x/am335x_dmtimer.c b/sys/arm/ti/am335x/am335x_dmtimer.c
new file mode 100644
index 000000000000..eb869ca88b94
--- /dev/null
+++ b/sys/arm/ti/am335x/am335x_dmtimer.c
@@ -0,0 +1,408 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Damjan Marion <dmarion@Freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <machine/bus.h>
+
+#include <machine/machdep.h> /* For arm_set_delay */
+
+#include <dev/extres/clk/clk.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/ti/ti_sysc.h>
+
+#include "am335x_dmtreg.h"
+
+struct am335x_dmtimer_softc {
+ device_t dev;
+ int tmr_mem_rid;
+ struct resource * tmr_mem_res;
+ int tmr_irq_rid;
+ struct resource * tmr_irq_res;
+ void *tmr_irq_handler;
+ clk_t clk_fck;
+ uint64_t sysclk_freq;
+ uint32_t tclr; /* Cached TCLR register. */
+ union {
+ struct timecounter tc;
+ struct eventtimer et;
+ } func;
+ int tmr_num; /* Hardware unit number. */
+ char tmr_name[12]; /* "DMTimerN", N = tmr_num */
+};
+
+static struct am335x_dmtimer_softc *am335x_dmtimer_et_sc = NULL;
+static struct am335x_dmtimer_softc *am335x_dmtimer_tc_sc = NULL;
+
+static void am335x_dmtimer_delay(int, void *);
+
+/*
+ * We use dmtimer2 for eventtimer and dmtimer3 for timecounter.
+ */
+#define ET_TMR_NUM 2
+#define TC_TMR_NUM 3
+
+/* List of compatible strings for FDT tree */
+static struct ofw_compat_data compat_data[] = {
+ {"ti,am335x-timer", 1},
+ {"ti,am335x-timer-1ms", 1},
+ {NULL, 0},
+};
+
+#define DMTIMER_READ4(sc, reg) bus_read_4((sc)->tmr_mem_res, (reg))
+#define DMTIMER_WRITE4(sc, reg, val) bus_write_4((sc)->tmr_mem_res, (reg), (val))
+
+static int
+am335x_dmtimer_et_start(struct eventtimer *et, sbintime_t first, sbintime_t period)
+{
+ struct am335x_dmtimer_softc *sc;
+ uint32_t initial_count, reload_count;
+
+ sc = et->et_priv;
+
+ /*
+ * Stop the timer before changing it. This routine will often be called
+ * while the timer is still running, to either lengthen or shorten the
+ * current event time. We need to ensure the timer doesn't expire while
+ * we're working with it.
+ *
+ * Also clear any pending interrupt status, because it's at least
+ * theoretically possible that we're running in a primary interrupt
+ * context now, and a timer interrupt could be pending even before we
+ * stopped the timer. The more likely case is that we're being called
+ * from the et_event_cb() routine dispatched from our own handler, but
+ * it's not clear to me that that's the only case possible.
+ */
+ sc->tclr &= ~(DMT_TCLR_START | DMT_TCLR_AUTOLOAD);
+ DMTIMER_WRITE4(sc, DMT_TCLR, sc->tclr);
+ DMTIMER_WRITE4(sc, DMT_IRQSTATUS, DMT_IRQ_OVF);
+
+ if (period != 0) {
+ reload_count = ((uint32_t)et->et_frequency * period) >> 32;
+ sc->tclr |= DMT_TCLR_AUTOLOAD;
+ } else {
+ reload_count = 0;
+ }
+
+ if (first != 0)
+ initial_count = ((uint32_t)et->et_frequency * first) >> 32;
+ else
+ initial_count = reload_count;
+
+ /*
+ * Set auto-reload and current-count values. This timer hardware counts
+ * up from the initial/reload value and interrupts on the zero rollover.
+ */
+ DMTIMER_WRITE4(sc, DMT_TLDR, 0xFFFFFFFF - reload_count);
+ DMTIMER_WRITE4(sc, DMT_TCRR, 0xFFFFFFFF - initial_count);
+
+ /* Enable overflow interrupt, and start the timer. */
+ DMTIMER_WRITE4(sc, DMT_IRQENABLE_SET, DMT_IRQ_OVF);
+ sc->tclr |= DMT_TCLR_START;
+ DMTIMER_WRITE4(sc, DMT_TCLR, sc->tclr);
+
+ return (0);
+}
+
+static int
+am335x_dmtimer_et_stop(struct eventtimer *et)
+{
+ struct am335x_dmtimer_softc *sc;
+
+ sc = et->et_priv;
+
+ /* Stop timer, disable and clear interrupt. */
+ sc->tclr &= ~(DMT_TCLR_START | DMT_TCLR_AUTOLOAD);
+ DMTIMER_WRITE4(sc, DMT_TCLR, sc->tclr);
+ DMTIMER_WRITE4(sc, DMT_IRQENABLE_CLR, DMT_IRQ_OVF);
+ DMTIMER_WRITE4(sc, DMT_IRQSTATUS, DMT_IRQ_OVF);
+ return (0);
+}
+
+static int
+am335x_dmtimer_et_intr(void *arg)
+{
+ struct am335x_dmtimer_softc *sc;
+
+ sc = arg;
+
+ /* Ack the interrupt, and invoke the callback if it's still enabled. */
+ DMTIMER_WRITE4(sc, DMT_IRQSTATUS, DMT_IRQ_OVF);
+ if (sc->func.et.et_active)
+ sc->func.et.et_event_cb(&sc->func.et, sc->func.et.et_arg);
+
+ return (FILTER_HANDLED);
+}
+
+static int
+am335x_dmtimer_et_init(struct am335x_dmtimer_softc *sc)
+{
+ KASSERT(am335x_dmtimer_et_sc == NULL, ("already have an eventtimer"));
+
+ /*
+ * Setup eventtimer interrupt handling. Panic if anything goes wrong,
+ * because the system just isn't going to run without an eventtimer.
+ */
+ sc->tmr_irq_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ,
+ &sc->tmr_irq_rid, RF_ACTIVE);
+ if (sc->tmr_irq_res == NULL)
+ panic("am335x_dmtimer: could not allocate irq resources");
+ if (bus_setup_intr(sc->dev, sc->tmr_irq_res, INTR_TYPE_CLK,
+ am335x_dmtimer_et_intr, NULL, sc, &sc->tmr_irq_handler) != 0)
+ panic("am335x_dmtimer: count not setup irq handler");
+
+ sc->func.et.et_name = sc->tmr_name;
+ sc->func.et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT;
+ sc->func.et.et_quality = 500;
+ sc->func.et.et_frequency = sc->sysclk_freq;
+ sc->func.et.et_min_period =
+ ((0x00000005LLU << 32) / sc->func.et.et_frequency);
+ sc->func.et.et_max_period =
+ (0xfffffffeLLU << 32) / sc->func.et.et_frequency;
+ sc->func.et.et_start = am335x_dmtimer_et_start;
+ sc->func.et.et_stop = am335x_dmtimer_et_stop;
+ sc->func.et.et_priv = sc;
+
+ am335x_dmtimer_et_sc = sc;
+ et_register(&sc->func.et);
+
+ return (0);
+}
+
+static unsigned
+am335x_dmtimer_tc_get_timecount(struct timecounter *tc)
+{
+ struct am335x_dmtimer_softc *sc;
+
+ sc = tc->tc_priv;
+
+ return (DMTIMER_READ4(sc, DMT_TCRR));
+}
+
+static int
+am335x_dmtimer_tc_init(struct am335x_dmtimer_softc *sc)
+{
+ KASSERT(am335x_dmtimer_tc_sc == NULL, ("already have a timecounter"));
+
+ /* Set up timecounter, start it, register it. */
+ DMTIMER_WRITE4(sc, DMT_TSICR, DMT_TSICR_RESET);
+ while (DMTIMER_READ4(sc, DMT_TIOCP_CFG) & DMT_TIOCP_RESET)
+ continue;
+
+ sc->tclr |= DMT_TCLR_START | DMT_TCLR_AUTOLOAD;
+ DMTIMER_WRITE4(sc, DMT_TLDR, 0);
+ DMTIMER_WRITE4(sc, DMT_TCRR, 0);
+ DMTIMER_WRITE4(sc, DMT_TCLR, sc->tclr);
+
+ sc->func.tc.tc_name = sc->tmr_name;
+ sc->func.tc.tc_get_timecount = am335x_dmtimer_tc_get_timecount;
+ sc->func.tc.tc_counter_mask = ~0u;
+ sc->func.tc.tc_frequency = sc->sysclk_freq;
+ sc->func.tc.tc_quality = 500;
+ sc->func.tc.tc_priv = sc;
+
+ am335x_dmtimer_tc_sc = sc;
+ tc_init(&sc->func.tc);
+
+ arm_set_delay(am335x_dmtimer_delay, sc);
+
+ return (0);
+}
+
+static int
+am335x_dmtimer_probe(device_t dev)
+{
+ char strbuf[32];
+ int tmr_num;
+ uint64_t rev_address;
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ /*
+ * Get the hardware unit number from address of rev register.
+ * If this isn't the hardware unit we're going to use for either the
+ * eventtimer or the timecounter, no point in instantiating the device.
+ */
+ rev_address = ti_sysc_get_rev_address(device_get_parent(dev));
+ switch (rev_address) {
+ case DMTIMER2_REV:
+ tmr_num = 2;
+ break;
+ case DMTIMER3_REV:
+ tmr_num = 3;
+ break;
+ default:
+ /* Not DMTIMER2 or DMTIMER3 */
+ return (ENXIO);
+ }
+
+ snprintf(strbuf, sizeof(strbuf), "AM335x DMTimer%d", tmr_num);
+ device_set_desc_copy(dev, strbuf);
+
+ return(BUS_PROBE_DEFAULT);
+}
+
+static int
+am335x_dmtimer_attach(device_t dev)
+{
+ struct am335x_dmtimer_softc *sc;
+ int err;
+ uint64_t rev_address;
+ clk_t sys_clkin;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ /* expect one clock */
+ err = clk_get_by_ofw_index(dev, 0, 0, &sc->clk_fck);
+ if (err != 0) {
+ device_printf(dev, "Cant find clock index 0. err: %d\n", err);
+ return (ENXIO);
+ }
+
+ err = clk_get_by_name(dev, "sys_clkin_ck@40", &sys_clkin);
+ if (err != 0) {
+ device_printf(dev, "Cant find sys_clkin_ck@40 err: %d\n", err);
+ return (ENXIO);
+ }
+
+ /* Select M_OSC as DPLL parent */
+ err = clk_set_parent_by_clk(sc->clk_fck, sys_clkin);
+ if (err != 0) {
+ device_printf(dev, "Cant set mux to CLK_M_OSC\n");
+ return (ENXIO);
+ }
+
+ /* Enable clocks and power on the device. */
+ err = ti_sysc_clock_enable(device_get_parent(dev));
+ if (err != 0) {
+ device_printf(dev, "Cant enable sysc clkctrl, err %d\n", err);
+ return (ENXIO);
+ }
+
+ /* Get the base clock frequency. */
+ err = clk_get_freq(sc->clk_fck, &sc->sysclk_freq);
+ if (err != 0) {
+ device_printf(dev, "Cant get sysclk frequency, err %d\n", err);
+ return (ENXIO);
+ }
+
+ /* Request the memory resources. */
+ sc->tmr_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &sc->tmr_mem_rid, RF_ACTIVE);
+ if (sc->tmr_mem_res == NULL) {
+ return (ENXIO);
+ }
+
+ rev_address = ti_sysc_get_rev_address(device_get_parent(dev));
+ switch (rev_address) {
+ case DMTIMER2_REV:
+ sc->tmr_num = 2;
+ break;
+ case DMTIMER3_REV:
+ sc->tmr_num = 3;
+ break;
+ default:
+ device_printf(dev, "Not timer 2 or 3! %#jx\n",
+ rev_address);
+ return (ENXIO);
+ }
+
+ snprintf(sc->tmr_name, sizeof(sc->tmr_name), "DMTimer%d", sc->tmr_num);
+
+ /*
+ * Go set up either a timecounter or eventtimer. We wouldn't have
+ * attached if we weren't one or the other.
+ */
+ if (sc->tmr_num == ET_TMR_NUM)
+ am335x_dmtimer_et_init(sc);
+ else if (sc->tmr_num == TC_TMR_NUM)
+ am335x_dmtimer_tc_init(sc);
+ else
+ panic("am335x_dmtimer: bad timer number %d", sc->tmr_num);
+
+ return (0);
+}
+
+static device_method_t am335x_dmtimer_methods[] = {
+ DEVMETHOD(device_probe, am335x_dmtimer_probe),
+ DEVMETHOD(device_attach, am335x_dmtimer_attach),
+ { 0, 0 }
+};
+
+static driver_t am335x_dmtimer_driver = {
+ "am335x_dmtimer",
+ am335x_dmtimer_methods,
+ sizeof(struct am335x_dmtimer_softc),
+};
+
+static devclass_t am335x_dmtimer_devclass;
+
+DRIVER_MODULE(am335x_dmtimer, simplebus, am335x_dmtimer_driver, am335x_dmtimer_devclass, 0, 0);
+MODULE_DEPEND(am335x_dmtimer, ti_sysc, 1, 1, 1);
+
+static void
+am335x_dmtimer_delay(int usec, void *arg)
+{
+ struct am335x_dmtimer_softc *sc = arg;
+ int32_t counts;
+ uint32_t first, last;
+
+ /* Get the number of times to count */
+ counts = (usec + 1) * (sc->sysclk_freq / 1000000);
+
+ first = DMTIMER_READ4(sc, DMT_TCRR);
+
+ while (counts > 0) {
+ last = DMTIMER_READ4(sc, DMT_TCRR);
+ if (last > first) {
+ counts -= (int32_t)(last - first);
+ } else {
+ counts -= (int32_t)((0xFFFFFFFF - first) + last);
+ }
+ first = last;
+ }
+}
diff --git a/sys/arm/ti/am335x/am335x_dmtpps.c b/sys/arm/ti/am335x/am335x_dmtpps.c
new file mode 100644
index 000000000000..24d83f248eef
--- /dev/null
+++ b/sys/arm/ti/am335x/am335x_dmtpps.c
@@ -0,0 +1,621 @@
+/*-
+ * Copyright (c) 2015 Ian lepore <ian@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * AM335x PPS driver using DMTimer capture.
+ *
+ * Note that this PPS driver does not use an interrupt. Instead it uses the
+ * hardware's ability to latch the timer's count register in response to a
+ * signal on an IO pin. Each of timers 4-7 have an associated pin, and this
+ * code allows any one of those to be used.
+ *
+ * The timecounter routines in kern_tc.c call the pps poll routine periodically
+ * to see if a new counter value has been latched. When a new value has been
+ * latched, the only processing done in the poll routine is to capture the
+ * current set of timecounter timehands (done with pps_capture()) and the
+ * latched value from the timer. The remaining work (done by pps_event() while
+ * holding a mutex) is scheduled to be done later in a non-interrupt context.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/timepps.h>
+#include <sys/timetc.h>
+#include <machine/bus.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/extres/clk/clk.h>
+
+#include <arm/ti/ti_sysc.h>
+#include <arm/ti/ti_pinmux.h>
+#include <arm/ti/am335x/am335x_scm_padconf.h>
+
+#include "am335x_dmtreg.h"
+
+#define PPS_CDEV_NAME "dmtpps"
+
+struct dmtpps_softc {
+ device_t dev;
+ int mem_rid;
+ struct resource * mem_res;
+ int tmr_num; /* N from hwmod str "timerN" */
+ char tmr_name[12]; /* "DMTimerN" */
+ uint32_t tclr; /* Cached TCLR register. */
+ struct timecounter tc;
+ int pps_curmode; /* Edge mode now set in hw. */
+ struct cdev * pps_cdev;
+ struct pps_state pps_state;
+ struct mtx pps_mtx;
+ clk_t clk_fck;
+ uint64_t sysclk_freq;
+};
+
+static int dmtpps_tmr_num; /* Set by probe() */
+
+/* List of compatible strings for FDT tree */
+static struct ofw_compat_data compat_data[] = {
+ {"ti,am335x-timer", 1},
+ {"ti,am335x-timer-1ms", 1},
+ {NULL, 0},
+};
+SIMPLEBUS_PNP_INFO(compat_data);
+
+/*
+ * A table relating pad names to the hardware timer number they can be mux'd to.
+ */
+struct padinfo {
+ char * ballname;
+ int tmr_num;
+};
+static struct padinfo dmtpps_padinfo[] = {
+ {"GPMC_ADVn_ALE", 4},
+ {"I2C0_SDA", 4},
+ {"MII1_TX_EN", 4},
+ {"XDMA_EVENT_INTR0", 4},
+ {"GPMC_BEn0_CLE", 5},
+ {"MDC", 5},
+ {"MMC0_DAT3", 5},
+ {"UART1_RTSn", 5},
+ {"GPMC_WEn", 6},
+ {"MDIO", 6},
+ {"MMC0_DAT2", 6},
+ {"UART1_CTSn", 6},
+ {"GPMC_OEn_REn", 7},
+ {"I2C0_SCL", 7},
+ {"UART0_CTSn", 7},
+ {"XDMA_EVENT_INTR1", 7},
+ {NULL, 0}
+};
+
+/*
+ * This is either brilliantly user-friendly, or utterly lame...
+ *
+ * The am335x chip is used on the popular Beaglebone boards. Those boards have
+ * pins for all four capture-capable timers available on the P8 header. Allow
+ * users to configure the input pin by giving the name of the header pin.
+ */
+struct nicknames {
+ const char * nick;
+ const char * name;
+};
+static struct nicknames dmtpps_pin_nicks[] = {
+ {"P8-7", "GPMC_ADVn_ALE"},
+ {"P8-9", "GPMC_BEn0_CLE"},
+ {"P8-10", "GPMC_WEn"},
+ {"P8-8", "GPMC_OEn_REn",},
+ {NULL, NULL}
+};
+
+#define DMTIMER_READ4(sc, reg) bus_read_4((sc)->mem_res, (reg))
+#define DMTIMER_WRITE4(sc, reg, val) bus_write_4((sc)->mem_res, (reg), (val))
+
+/*
+ * Translate a short friendly case-insensitive name to its canonical name.
+ */
+static const char *
+dmtpps_translate_nickname(const char *nick)
+{
+ struct nicknames *nn;
+
+ for (nn = dmtpps_pin_nicks; nn->nick != NULL; nn++)
+ if (strcasecmp(nick, nn->nick) == 0)
+ return nn->name;
+ return (nick);
+}
+
+/*
+ * See if our tunable is set to the name of the input pin. If not, that's NOT
+ * an error, return 0. If so, try to configure that pin as a timer capture
+ * input pin, and if that works, then we have our timer unit number and if it
+ * fails that IS an error, return -1.
+ */
+static int
+dmtpps_find_tmr_num_by_tunable(void)
+{
+ struct padinfo *pi;
+ char iname[20];
+ char muxmode[12];
+ const char * ballname;
+ int err;
+
+ if (!TUNABLE_STR_FETCH("hw.am335x_dmtpps.input", iname, sizeof(iname)))
+ return (0);
+ ballname = dmtpps_translate_nickname(iname);
+ for (pi = dmtpps_padinfo; pi->ballname != NULL; pi++) {
+ if (strcmp(ballname, pi->ballname) != 0)
+ continue;
+ snprintf(muxmode, sizeof(muxmode), "timer%d", pi->tmr_num);
+ err = ti_pinmux_padconf_set(pi->ballname, muxmode,
+ PADCONF_INPUT);
+ if (err != 0) {
+ printf("am335x_dmtpps: unable to configure capture pin "
+ "for %s to input mode\n", muxmode);
+ return (-1);
+ } else if (bootverbose) {
+ printf("am335x_dmtpps: configured pin %s as input "
+ "for %s\n", iname, muxmode);
+ }
+ return (pi->tmr_num);
+ }
+
+ /* Invalid name in the tunable, that's an error. */
+ printf("am335x_dmtpps: unknown pin name '%s'\n", iname);
+ return (-1);
+}
+
+/*
+ * Ask the pinmux driver whether any pin has been configured as a TIMER4..TIMER7
+ * input pin. If so, return the timer number, if not return 0.
+ */
+static int
+dmtpps_find_tmr_num_by_padconf(void)
+{
+ int err;
+ unsigned int padstate;
+ const char * padmux;
+ struct padinfo *pi;
+ char muxmode[12];
+
+ for (pi = dmtpps_padinfo; pi->ballname != NULL; pi++) {
+ err = ti_pinmux_padconf_get(pi->ballname, &padmux, &padstate);
+ snprintf(muxmode, sizeof(muxmode), "timer%d", pi->tmr_num);
+ if (err == 0 && (padstate & RXACTIVE) != 0 &&
+ strcmp(muxmode, padmux) == 0)
+ return (pi->tmr_num);
+ }
+ /* Nothing found, not an error. */
+ return (0);
+}
+
+/*
+ * Figure out which hardware timer number to use based on input pin
+ * configuration. This is done just once, the first time probe() runs.
+ */
+static int
+dmtpps_find_tmr_num(void)
+{
+ int tmr_num;
+
+ if ((tmr_num = dmtpps_find_tmr_num_by_tunable()) == 0)
+ tmr_num = dmtpps_find_tmr_num_by_padconf();
+
+ if (tmr_num <= 0) {
+ printf("am335x_dmtpps: PPS driver not enabled: unable to find "
+ "or configure a capture input pin\n");
+ tmr_num = -1; /* Must return non-zero to prevent re-probing. */
+ }
+ return (tmr_num);
+}
+
+static void
+dmtpps_set_hw_capture(struct dmtpps_softc *sc, bool force_off)
+{
+ int newmode;
+
+ if (force_off)
+ newmode = 0;
+ else
+ newmode = sc->pps_state.ppsparam.mode & PPS_CAPTUREASSERT;
+
+ if (newmode == sc->pps_curmode)
+ return;
+ sc->pps_curmode = newmode;
+
+ if (newmode == PPS_CAPTUREASSERT)
+ sc->tclr |= DMT_TCLR_CAPTRAN_LOHI;
+ else
+ sc->tclr &= ~DMT_TCLR_CAPTRAN_MASK;
+ DMTIMER_WRITE4(sc, DMT_TCLR, sc->tclr);
+}
+
+static unsigned
+dmtpps_get_timecount(struct timecounter *tc)
+{
+ struct dmtpps_softc *sc;
+
+ sc = tc->tc_priv;
+
+ return (DMTIMER_READ4(sc, DMT_TCRR));
+}
+
+static void
+dmtpps_poll(struct timecounter *tc)
+{
+ struct dmtpps_softc *sc;
+
+ sc = tc->tc_priv;
+
+ /*
+ * If a new value has been latched we've got a PPS event. Capture the
+ * timecounter data, then override the capcount field (pps_capture()
+ * populates it from the current DMT_TCRR register) with the latched
+ * value from the TCAR1 register.
+ *
+ * Note that we don't have the TCAR interrupt enabled, but the hardware
+ * still provides the status bits in the "RAW" status register even when
+ * they're masked from generating an irq. However, when clearing the
+ * TCAR status to re-arm the capture for the next second, we have to
+ * write to the IRQ status register, not the RAW register. Quirky.
+ *
+ * We do not need to hold a lock while capturing the pps data, because
+ * it is captured into an area of the pps_state struct which is read
+ * only by pps_event(). We do need to hold a lock while calling
+ * pps_event(), because it manipulates data which is also accessed from
+ * the ioctl(2) context by userland processes.
+ */
+ if (DMTIMER_READ4(sc, DMT_IRQSTATUS_RAW) & DMT_IRQ_TCAR) {
+ pps_capture(&sc->pps_state);
+ sc->pps_state.capcount = DMTIMER_READ4(sc, DMT_TCAR1);
+ DMTIMER_WRITE4(sc, DMT_IRQSTATUS, DMT_IRQ_TCAR);
+
+ mtx_lock_spin(&sc->pps_mtx);
+ pps_event(&sc->pps_state, PPS_CAPTUREASSERT);
+ mtx_unlock_spin(&sc->pps_mtx);
+ }
+}
+
+static int
+dmtpps_open(struct cdev *dev, int flags, int fmt,
+ struct thread *td)
+{
+ struct dmtpps_softc *sc;
+
+ sc = dev->si_drv1;
+
+ /*
+ * Begin polling for pps and enable capture in the hardware whenever the
+ * device is open. Doing this stuff again is harmless if this isn't the
+ * first open.
+ */
+ sc->tc.tc_poll_pps = dmtpps_poll;
+ dmtpps_set_hw_capture(sc, false);
+
+ return 0;
+}
+
+static int
+dmtpps_close(struct cdev *dev, int flags, int fmt,
+ struct thread *td)
+{
+ struct dmtpps_softc *sc;
+
+ sc = dev->si_drv1;
+
+ /*
+ * Stop polling and disable capture on last close. Use the force-off
+ * flag to override the configured mode and turn off the hardware.
+ */
+ sc->tc.tc_poll_pps = NULL;
+ dmtpps_set_hw_capture(sc, true);
+
+ return 0;
+}
+
+static int
+dmtpps_ioctl(struct cdev *dev, u_long cmd, caddr_t data,
+ int flags, struct thread *td)
+{
+ struct dmtpps_softc *sc;
+ int err;
+
+ sc = dev->si_drv1;
+
+ /* Let the kernel do the heavy lifting for ioctl. */
+ mtx_lock_spin(&sc->pps_mtx);
+ err = pps_ioctl(cmd, data, &sc->pps_state);
+ mtx_unlock_spin(&sc->pps_mtx);
+ if (err != 0)
+ return (err);
+
+ /*
+ * The capture mode could have changed, set the hardware to whatever
+ * mode is now current. Effectively a no-op if nothing changed.
+ */
+ dmtpps_set_hw_capture(sc, false);
+
+ return (err);
+}
+
+static struct cdevsw dmtpps_cdevsw = {
+ .d_version = D_VERSION,
+ .d_open = dmtpps_open,
+ .d_close = dmtpps_close,
+ .d_ioctl = dmtpps_ioctl,
+ .d_name = PPS_CDEV_NAME,
+};
+
+static int
+dmtpps_probe(device_t dev)
+{
+ char strbuf[64];
+ int tmr_num;
+ uint64_t rev_address;
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ /*
+ * If we haven't chosen which hardware timer to use yet, go do that now.
+ * We need to know that to decide whether to return success for this
+ * hardware timer instance or not.
+ */
+ if (dmtpps_tmr_num == 0)
+ dmtpps_tmr_num = dmtpps_find_tmr_num();
+
+ /*
+ * Figure out which hardware timer is being probed and see if it matches
+ * the configured timer number determined earlier.
+ */
+ rev_address = ti_sysc_get_rev_address(device_get_parent(dev));
+ switch (rev_address) {
+ case DMTIMER1_1MS_REV:
+ tmr_num = 1;
+ break;
+ case DMTIMER2_REV:
+ tmr_num = 2;
+ break;
+ case DMTIMER3_REV:
+ tmr_num = 3;
+ break;
+ case DMTIMER4_REV:
+ tmr_num = 4;
+ break;
+ case DMTIMER5_REV:
+ tmr_num = 5;
+ break;
+ case DMTIMER6_REV:
+ tmr_num = 6;
+ break;
+ case DMTIMER7_REV:
+ tmr_num = 7;
+ break;
+ default:
+ return (ENXIO);
+ }
+
+ if (dmtpps_tmr_num != tmr_num)
+ return (ENXIO);
+
+ snprintf(strbuf, sizeof(strbuf), "AM335x PPS-Capture DMTimer%d",
+ tmr_num);
+ device_set_desc_copy(dev, strbuf);
+
+ return(BUS_PROBE_DEFAULT);
+}
+
+static int
+dmtpps_attach(device_t dev)
+{
+ struct dmtpps_softc *sc;
+ struct make_dev_args mda;
+ int err;
+ clk_t sys_clkin;
+ uint64_t rev_address;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ /* Figure out which hardware timer this is and set the name string. */
+ rev_address = ti_sysc_get_rev_address(device_get_parent(dev));
+ switch (rev_address) {
+ case DMTIMER1_1MS_REV:
+ sc->tmr_num = 1;
+ break;
+ case DMTIMER2_REV:
+ sc->tmr_num = 2;
+ break;
+ case DMTIMER3_REV:
+ sc->tmr_num = 3;
+ break;
+ case DMTIMER4_REV:
+ sc->tmr_num = 4;
+ break;
+ case DMTIMER5_REV:
+ sc->tmr_num = 5;
+ break;
+ case DMTIMER6_REV:
+ sc->tmr_num = 6;
+ break;
+ case DMTIMER7_REV:
+ sc->tmr_num = 7;
+ break;
+ }
+ snprintf(sc->tmr_name, sizeof(sc->tmr_name), "DMTimer%d", sc->tmr_num);
+
+ /* expect one clock */
+ err = clk_get_by_ofw_index(dev, 0, 0, &sc->clk_fck);
+ if (err != 0) {
+ device_printf(dev, "Cant find clock index 0. err: %d\n", err);
+ return (ENXIO);
+ }
+
+ err = clk_get_by_name(dev, "sys_clkin_ck@40", &sys_clkin);
+ if (err != 0) {
+ device_printf(dev, "Cant find sys_clkin_ck@40 err: %d\n", err);
+ return (ENXIO);
+ }
+
+ /* Select M_OSC as DPLL parent */
+ err = clk_set_parent_by_clk(sc->clk_fck, sys_clkin);
+ if (err != 0) {
+ device_printf(dev, "Cant set mux to CLK_M_OSC\n");
+ return (ENXIO);
+ }
+
+ /* Enable clocks and power on the device. */
+ err = ti_sysc_clock_enable(device_get_parent(dev));
+ if (err != 0) {
+ device_printf(dev, "Cant enable sysc clkctrl, err %d\n", err);
+ return (ENXIO);
+ }
+
+ /* Get the base clock frequency. */
+ err = clk_get_freq(sc->clk_fck, &sc->sysclk_freq);
+ if (err != 0) {
+ device_printf(dev, "Cant get sysclk frequency, err %d\n", err);
+ return (ENXIO);
+ }
+ /* Request the memory resources. */
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &sc->mem_rid, RF_ACTIVE);
+ if (sc->mem_res == NULL) {
+ return (ENXIO);
+ }
+
+ /*
+ * Configure the timer pulse/capture pin to input/capture mode. This is
+ * required in addition to configuring the pin as input with the pinmux
+ * controller (which was done via fdt data or tunable at probe time).
+ */
+ sc->tclr = DMT_TCLR_GPO_CFG;
+ DMTIMER_WRITE4(sc, DMT_TCLR, sc->tclr);
+
+ /* Set up timecounter hardware, start it. */
+ DMTIMER_WRITE4(sc, DMT_TSICR, DMT_TSICR_RESET);
+ while (DMTIMER_READ4(sc, DMT_TIOCP_CFG) & DMT_TIOCP_RESET)
+ continue;
+
+ sc->tclr |= DMT_TCLR_START | DMT_TCLR_AUTOLOAD;
+ DMTIMER_WRITE4(sc, DMT_TLDR, 0);
+ DMTIMER_WRITE4(sc, DMT_TCRR, 0);
+ DMTIMER_WRITE4(sc, DMT_TCLR, sc->tclr);
+
+ /* Register the timecounter. */
+ sc->tc.tc_name = sc->tmr_name;
+ sc->tc.tc_get_timecount = dmtpps_get_timecount;
+ sc->tc.tc_counter_mask = ~0u;
+ sc->tc.tc_frequency = sc->sysclk_freq;
+ sc->tc.tc_quality = 1000;
+ sc->tc.tc_priv = sc;
+
+ tc_init(&sc->tc);
+
+ /*
+ * Indicate our PPS capabilities. Have the kernel init its part of the
+ * pps_state struct and add its capabilities.
+ *
+ * While the hardware has a mode to capture each edge, it's not clear we
+ * can use it that way, because there's only a single interrupt/status
+ * bit to say something was captured, but not which edge it was. For
+ * now, just say we can only capture assert events (the positive-going
+ * edge of the pulse).
+ */
+ mtx_init(&sc->pps_mtx, "dmtpps", NULL, MTX_SPIN);
+ sc->pps_state.flags = PPSFLAG_MTX_SPIN;
+ sc->pps_state.ppscap = PPS_CAPTUREASSERT;
+ sc->pps_state.driver_abi = PPS_ABI_VERSION;
+ sc->pps_state.driver_mtx = &sc->pps_mtx;
+ pps_init_abi(&sc->pps_state);
+
+ /* Create the PPS cdev. */
+ make_dev_args_init(&mda);
+ mda.mda_flags = MAKEDEV_WAITOK;
+ mda.mda_devsw = &dmtpps_cdevsw;
+ mda.mda_cr = NULL;
+ mda.mda_uid = UID_ROOT;
+ mda.mda_gid = GID_WHEEL;
+ mda.mda_mode = 0600;
+ mda.mda_unit = device_get_unit(dev);
+ mda.mda_si_drv1 = sc;
+ if ((err = make_dev_s(&mda, &sc->pps_cdev, PPS_CDEV_NAME)) != 0) {
+ device_printf(dev, "Failed to create cdev %s\n", PPS_CDEV_NAME);
+ return (err);
+ }
+
+ if (bootverbose)
+ device_printf(sc->dev, "Using %s for PPS device /dev/%s\n",
+ sc->tmr_name, PPS_CDEV_NAME);
+
+ return (0);
+}
+
+static int
+dmtpps_detach(device_t dev)
+{
+
+ /*
+ * There is no way to remove a timecounter once it has been registered,
+ * even if it's not in use, so we can never detach. If we were
+ * dynamically loaded as a module this will prevent unloading.
+ */
+ return (EBUSY);
+}
+
+static device_method_t dmtpps_methods[] = {
+ DEVMETHOD(device_probe, dmtpps_probe),
+ DEVMETHOD(device_attach, dmtpps_attach),
+ DEVMETHOD(device_detach, dmtpps_detach),
+ { 0, 0 }
+};
+
+static driver_t dmtpps_driver = {
+ "am335x_dmtpps",
+ dmtpps_methods,
+ sizeof(struct dmtpps_softc),
+};
+
+static devclass_t dmtpps_devclass;
+
+DRIVER_MODULE(am335x_dmtpps, simplebus, dmtpps_driver, dmtpps_devclass, 0, 0);
+MODULE_DEPEND(am335x_dmtpps, ti_sysc, 1, 1, 1);
diff --git a/sys/arm/ti/am335x/am335x_dmtreg.h b/sys/arm/ti/am335x/am335x_dmtreg.h
new file mode 100644
index 000000000000..c91a30570bd8
--- /dev/null
+++ b/sys/arm/ti/am335x/am335x_dmtreg.h
@@ -0,0 +1,88 @@
+/*-
+ * Copyright (c) 2012 Damjan Marion <dmarion@Freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef AM335X_DMTREG_H
+#define AM335X_DMTREG_H
+
+#define AM335X_NUM_TIMERS 8
+
+#define DMT_TIDR 0x00 /* Identification Register */
+#define DMT_TIOCP_CFG 0x10 /* OCP Configuration Reg */
+#define DMT_TIOCP_RESET (1 << 0) /* TIOCP perform soft reset */
+#define DMT_IQR_EOI 0x20 /* IRQ End-Of-Interrupt Reg */
+#define DMT_IRQSTATUS_RAW 0x24 /* IRQSTATUS Raw Reg */
+#define DMT_IRQSTATUS 0x28 /* IRQSTATUS Reg */
+#define DMT_IRQENABLE_SET 0x2c /* IRQSTATUS Set Reg */
+#define DMT_IRQENABLE_CLR 0x30 /* IRQSTATUS Clear Reg */
+#define DMT_IRQWAKEEN 0x34 /* IRQ Wakeup Enable Reg */
+#define DMT_IRQ_MAT (1 << 0) /* IRQ: Match */
+#define DMT_IRQ_OVF (1 << 1) /* IRQ: Overflow */
+#define DMT_IRQ_TCAR (1 << 2) /* IRQ: Capture */
+#define DMT_IRQ_MASK (DMT_IRQ_TCAR | DMT_IRQ_OVF | DMT_IRQ_MAT)
+#define DMT_TCLR 0x38 /* Control Register */
+#define DMT_TCLR_START (1 << 0) /* Start timer */
+#define DMT_TCLR_AUTOLOAD (1 << 1) /* Auto-reload on overflow */
+#define DMT_TCLR_PRES_MASK (7 << 2) /* Prescaler mask */
+#define DMT_TCLR_PRES_ENABLE (1 << 5) /* Prescaler enable */
+#define DMT_TCLR_COMP_ENABLE (1 << 6) /* Compare enable */
+#define DMT_TCLR_PWM_HIGH (1 << 7) /* PWM default output high */
+#define DMT_TCLR_CAPTRAN_MASK (3 << 8) /* Capture transition mask */
+#define DMT_TCLR_CAPTRAN_NONE (0 << 8) /* Capture: none */
+#define DMT_TCLR_CAPTRAN_LOHI (1 << 8) /* Capture lo->hi transition */
+#define DMT_TCLR_CAPTRAN_HILO (2 << 8) /* Capture hi->lo transition */
+#define DMT_TCLR_CAPTRAN_BOTH (3 << 8) /* Capture both transitions */
+#define DMT_TCLR_TRGMODE_MASK (3 << 10) /* Trigger output mode mask */
+#define DMT_TCLR_TRGMODE_NONE (0 << 10) /* Trigger off */
+#define DMT_TCLR_TRGMODE_OVFL (1 << 10) /* Trigger on overflow */
+#define DMT_TCLR_TRGMODE_BOTH (2 << 10) /* Trigger on match + ovflow */
+#define DMT_TCLR_PWM_PTOGGLE (1 << 12) /* PWM toggles */
+#define DMT_TCLR_CAP_MODE_2ND (1 << 13) /* Capture second event mode */
+#define DMT_TCLR_GPO_CFG (1 << 14) /* Tmr pin conf, 0=out, 1=in */
+#define DMT_TCRR 0x3C /* Counter Register */
+#define DMT_TLDR 0x40 /* Load Reg */
+#define DMT_TTGR 0x44 /* Trigger Reg */
+#define DMT_TWPS 0x48 /* Write Posted Status Reg */
+#define DMT_TMAR 0x4C /* Match Reg */
+#define DMT_TCAR1 0x50 /* Capture Reg */
+#define DMT_TSICR 0x54 /* Synchr. Interface Ctrl Reg */
+#define DMT_TSICR_RESET (1 << 1) /* TSICR perform soft reset */
+#define DMT_TCAR2 0x48 /* Capture Reg */
+
+/* Location of revision register from TRM Memory map chapter 2 */
+/* L4_WKUP */
+#define DMTIMER0_REV 0x05000
+#define DMTIMER1_1MS_REV 0x31000
+/* L4_PER */
+#define DMTIMER2_REV 0x40000
+#define DMTIMER3_REV 0x42000
+#define DMTIMER4_REV 0x44000
+#define DMTIMER5_REV 0x46000
+#define DMTIMER6_REV 0x48000
+#define DMTIMER7_REV 0x4A000
+
+#endif /* AM335X_DMTREG_H */
diff --git a/sys/arm/ti/am335x/am335x_ecap.c b/sys/arm/ti/am335x/am335x_ecap.c
new file mode 100644
index 000000000000..31e340a69c33
--- /dev/null
+++ b/sys/arm/ti/am335x/am335x_ecap.c
@@ -0,0 +1,199 @@
+/*-
+ * Copyright (c) 2013 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "am335x_pwm.h"
+
+#define ECAP_TSCTR 0x00
+#define ECAP_CAP1 0x08
+#define ECAP_CAP2 0x0C
+#define ECAP_CAP3 0x10
+#define ECAP_CAP4 0x14
+#define ECAP_ECCTL2 0x2A
+#define ECCTL2_MODE_APWM (1 << 9)
+#define ECCTL2_SYNCO_SEL (3 << 6)
+#define ECCTL2_TSCTRSTOP_FREERUN (1 << 4)
+
+#define ECAP_READ2(_sc, reg) bus_read_2((_sc)->sc_mem_res, reg);
+#define ECAP_WRITE2(_sc, reg, value) \
+ bus_write_2((_sc)->sc_mem_res, reg, value);
+#define ECAP_READ4(_sc, reg) bus_read_4((_sc)->sc_mem_res, reg);
+#define ECAP_WRITE4(_sc, reg, value) \
+ bus_write_4((_sc)->sc_mem_res, reg, value);
+
+#define PWM_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define PWM_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#define PWM_LOCK_INIT(_sc) mtx_init(&(_sc)->sc_mtx, \
+ device_get_nameunit(_sc->sc_dev), "am335x_ecap softc", MTX_DEF)
+#define PWM_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx)
+
+static device_probe_t am335x_ecap_probe;
+static device_attach_t am335x_ecap_attach;
+static device_detach_t am335x_ecap_detach;
+
+struct am335x_ecap_softc {
+ device_t sc_dev;
+ struct mtx sc_mtx;
+ struct resource *sc_mem_res;
+ int sc_mem_rid;
+};
+
+static device_method_t am335x_ecap_methods[] = {
+ DEVMETHOD(device_probe, am335x_ecap_probe),
+ DEVMETHOD(device_attach, am335x_ecap_attach),
+ DEVMETHOD(device_detach, am335x_ecap_detach),
+
+ DEVMETHOD_END
+};
+
+static driver_t am335x_ecap_driver = {
+ "am335x_ecap",
+ am335x_ecap_methods,
+ sizeof(struct am335x_ecap_softc),
+};
+
+static devclass_t am335x_ecap_devclass;
+
+/*
+ * API function to set period/duty cycles for ECAPx
+ */
+int
+am335x_pwm_config_ecap(int unit, int period, int duty)
+{
+ device_t dev;
+ struct am335x_ecap_softc *sc;
+ uint16_t reg;
+
+ dev = devclass_get_device(am335x_ecap_devclass, unit);
+ if (dev == NULL)
+ return (ENXIO);
+
+ if (duty > period)
+ return (EINVAL);
+
+ if (period == 0)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+ PWM_LOCK(sc);
+
+ reg = ECAP_READ2(sc, ECAP_ECCTL2);
+ reg |= ECCTL2_MODE_APWM | ECCTL2_TSCTRSTOP_FREERUN | ECCTL2_SYNCO_SEL;
+ ECAP_WRITE2(sc, ECAP_ECCTL2, reg);
+
+ /* CAP3 in APWM mode is APRD shadow register */
+ ECAP_WRITE4(sc, ECAP_CAP3, period - 1);
+
+ /* CAP4 in APWM mode is ACMP shadow register */
+ ECAP_WRITE4(sc, ECAP_CAP4, duty);
+ /* Restart counter */
+ ECAP_WRITE4(sc, ECAP_TSCTR, 0);
+
+ PWM_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+am335x_ecap_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "ti,am33xx-ecap"))
+ return (ENXIO);
+
+ device_set_desc(dev, "AM335x eCAP");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+am335x_ecap_attach(device_t dev)
+{
+ struct am335x_ecap_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+
+ PWM_LOCK_INIT(sc);
+
+ sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &sc->sc_mem_rid, RF_ACTIVE);
+ if (sc->sc_mem_res == NULL) {
+ device_printf(dev, "cannot allocate memory resources\n");
+ goto fail;
+ }
+
+ return (0);
+
+fail:
+ PWM_LOCK_DESTROY(sc);
+ return (ENXIO);
+}
+
+static int
+am335x_ecap_detach(device_t dev)
+{
+ struct am335x_ecap_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ PWM_LOCK(sc);
+ if (sc->sc_mem_res)
+ bus_release_resource(dev, SYS_RES_MEMORY,
+ sc->sc_mem_rid, sc->sc_mem_res);
+ PWM_UNLOCK(sc);
+
+ PWM_LOCK_DESTROY(sc);
+
+ return (0);
+}
+
+DRIVER_MODULE(am335x_ecap, am335x_pwmss, am335x_ecap_driver, am335x_ecap_devclass, 0, 0);
+MODULE_VERSION(am335x_ecap, 1);
+MODULE_DEPEND(am335x_ecap, am335x_pwmss, 1, 1, 1);
diff --git a/sys/arm/ti/am335x/am335x_ehrpwm.c b/sys/arm/ti/am335x/am335x_ehrpwm.c
new file mode 100644
index 000000000000..619fdae2bcdb
--- /dev/null
+++ b/sys/arm/ti/am335x/am335x_ehrpwm.c
@@ -0,0 +1,718 @@
+/*-
+ * Copyright (c) 2013 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "pwmbus_if.h"
+
+#include "am335x_pwm.h"
+
+/*******************************************************************************
+ * Enhanced resolution PWM driver. Many of the advanced featues of the hardware
+ * are not supported by this driver. What is implemented here is simple
+ * variable-duty-cycle PWM output.
+ *
+ * Note that this driver was historically configured using a set of sysctl
+ * variables/procs, and later gained support for the PWM(9) API. The sysctl
+ * code is still present to support existing apps, but that interface is
+ * considered deprecated.
+ *
+ * An important caveat is that the original sysctl interface and the new PWM API
+ * cannot both be used at once. If both interfaces are used to change
+ * configuration, it's quite likely you won't get the expected results. Also,
+ * reading the sysctl values after configuring via PWM will not return the right
+ * results.
+ ******************************************************************************/
+
+/* In ticks */
+#define DEFAULT_PWM_PERIOD 1000
+#define PWM_CLOCK 100000000UL
+
+#define NS_PER_SEC 1000000000
+
+#define PWM_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define PWM_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#define PWM_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED)
+#define PWM_LOCK_INIT(_sc) mtx_init(&(_sc)->sc_mtx, \
+ device_get_nameunit(_sc->sc_dev), "am335x_ehrpwm softc", MTX_DEF)
+#define PWM_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx)
+
+#define EPWM_READ2(_sc, reg) bus_read_2((_sc)->sc_mem_res, reg)
+#define EPWM_WRITE2(_sc, reg, value) \
+ bus_write_2((_sc)->sc_mem_res, reg, value)
+
+#define EPWM_TBCTL 0x00
+#define TBCTL_FREERUN (2 << 14)
+#define TBCTL_PHDIR_UP (1 << 13)
+#define TBCTL_PHDIR_DOWN (0 << 13)
+#define TBCTL_CLKDIV(x) ((x) << 10)
+#define TBCTL_CLKDIV_MASK (3 << 10)
+#define TBCTL_HSPCLKDIV(x) ((x) << 7)
+#define TBCTL_HSPCLKDIV_MASK (3 << 7)
+#define TBCTL_SYNCOSEL_DISABLED (3 << 4)
+#define TBCTL_PRDLD_SHADOW (0 << 3)
+#define TBCTL_PRDLD_IMMEDIATE (0 << 3)
+#define TBCTL_PHSEN_ENABLED (1 << 2)
+#define TBCTL_PHSEN_DISABLED (0 << 2)
+#define TBCTL_CTRMODE_MASK (3)
+#define TBCTL_CTRMODE_UP (0 << 0)
+#define TBCTL_CTRMODE_DOWN (1 << 0)
+#define TBCTL_CTRMODE_UPDOWN (2 << 0)
+#define TBCTL_CTRMODE_FREEZE (3 << 0)
+
+#define EPWM_TBSTS 0x02
+#define EPWM_TBPHSHR 0x04
+#define EPWM_TBPHS 0x06
+#define EPWM_TBCNT 0x08
+#define EPWM_TBPRD 0x0a
+/* Counter-compare */
+#define EPWM_CMPCTL 0x0e
+#define CMPCTL_SHDWBMODE_SHADOW (1 << 6)
+#define CMPCTL_SHDWBMODE_IMMEDIATE (0 << 6)
+#define CMPCTL_SHDWAMODE_SHADOW (1 << 4)
+#define CMPCTL_SHDWAMODE_IMMEDIATE (0 << 4)
+#define CMPCTL_LOADBMODE_ZERO (0 << 2)
+#define CMPCTL_LOADBMODE_PRD (1 << 2)
+#define CMPCTL_LOADBMODE_EITHER (2 << 2)
+#define CMPCTL_LOADBMODE_FREEZE (3 << 2)
+#define CMPCTL_LOADAMODE_ZERO (0 << 0)
+#define CMPCTL_LOADAMODE_PRD (1 << 0)
+#define CMPCTL_LOADAMODE_EITHER (2 << 0)
+#define CMPCTL_LOADAMODE_FREEZE (3 << 0)
+#define EPWM_CMPAHR 0x10
+#define EPWM_CMPA 0x12
+#define EPWM_CMPB 0x14
+/* CMPCTL_LOADAMODE_ZERO */
+#define EPWM_AQCTLA 0x16
+#define EPWM_AQCTLB 0x18
+#define AQCTL_CBU_NONE (0 << 8)
+#define AQCTL_CBU_CLEAR (1 << 8)
+#define AQCTL_CBU_SET (2 << 8)
+#define AQCTL_CBU_TOGGLE (3 << 8)
+#define AQCTL_CAU_NONE (0 << 4)
+#define AQCTL_CAU_CLEAR (1 << 4)
+#define AQCTL_CAU_SET (2 << 4)
+#define AQCTL_CAU_TOGGLE (3 << 4)
+#define AQCTL_ZRO_NONE (0 << 0)
+#define AQCTL_ZRO_CLEAR (1 << 0)
+#define AQCTL_ZRO_SET (2 << 0)
+#define AQCTL_ZRO_TOGGLE (3 << 0)
+#define EPWM_AQSFRC 0x1a
+#define EPWM_AQCSFRC 0x1c
+#define AQCSFRC_OFF 0
+#define AQCSFRC_LO 1
+#define AQCSFRC_HI 2
+#define AQCSFRC_MASK 3
+#define AQCSFRC(chan, hilo) ((hilo) << (2 * chan))
+
+/* Trip-Zone module */
+#define EPWM_TZCTL 0x28
+#define EPWM_TZFLG 0x2C
+/* High-Resolution PWM */
+#define EPWM_HRCTL 0x40
+#define HRCTL_DELMODE_BOTH 3
+#define HRCTL_DELMODE_FALL 2
+#define HRCTL_DELMODE_RISE 1
+
+static device_probe_t am335x_ehrpwm_probe;
+static device_attach_t am335x_ehrpwm_attach;
+static device_detach_t am335x_ehrpwm_detach;
+
+static int am335x_ehrpwm_clkdiv[8] = { 1, 2, 4, 8, 16, 32, 64, 128 };
+
+struct ehrpwm_channel {
+ u_int duty; /* on duration, in ns */
+ bool enabled; /* channel enabled? */
+ bool inverted; /* signal inverted? */
+};
+#define NUM_CHANNELS 2
+
+struct am335x_ehrpwm_softc {
+ device_t sc_dev;
+ device_t sc_busdev;
+ struct mtx sc_mtx;
+ struct resource *sc_mem_res;
+ int sc_mem_rid;
+
+ /* Things used for configuration via sysctl [deprecated]. */
+ int sc_pwm_clkdiv;
+ int sc_pwm_freq;
+ struct sysctl_oid *sc_clkdiv_oid;
+ struct sysctl_oid *sc_freq_oid;
+ struct sysctl_oid *sc_period_oid;
+ struct sysctl_oid *sc_chanA_oid;
+ struct sysctl_oid *sc_chanB_oid;
+ uint32_t sc_pwm_period;
+ uint32_t sc_pwm_dutyA;
+ uint32_t sc_pwm_dutyB;
+
+ /* Things used for configuration via pwm(9) api. */
+ u_int sc_clkfreq; /* frequency in Hz */
+ u_int sc_clktick; /* duration in ns */
+ u_int sc_period; /* duration in ns */
+ struct ehrpwm_channel sc_channels[NUM_CHANNELS];
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"ti,am33xx-ehrpwm", true},
+ {NULL, false},
+};
+SIMPLEBUS_PNP_INFO(compat_data);
+
+static void
+am335x_ehrpwm_cfg_duty(struct am335x_ehrpwm_softc *sc, u_int chan, u_int duty)
+{
+ u_int tbcmp;
+
+ if (duty == 0)
+ tbcmp = 0;
+ else
+ tbcmp = max(1, duty / sc->sc_clktick);
+
+ sc->sc_channels[chan].duty = tbcmp * sc->sc_clktick;
+
+ PWM_LOCK_ASSERT(sc);
+ EPWM_WRITE2(sc, (chan == 0) ? EPWM_CMPA : EPWM_CMPB, tbcmp);
+}
+
+static void
+am335x_ehrpwm_cfg_enable(struct am335x_ehrpwm_softc *sc, u_int chan, bool enable)
+{
+ uint16_t regval;
+
+ sc->sc_channels[chan].enabled = enable;
+
+ /*
+ * Turn off any existing software-force of the channel, then force
+ * it in the right direction (high or low) if it's not being enabled.
+ */
+ PWM_LOCK_ASSERT(sc);
+ regval = EPWM_READ2(sc, EPWM_AQCSFRC);
+ regval &= ~AQCSFRC(chan, AQCSFRC_MASK);
+ if (!sc->sc_channels[chan].enabled) {
+ if (sc->sc_channels[chan].inverted)
+ regval |= AQCSFRC(chan, AQCSFRC_HI);
+ else
+ regval |= AQCSFRC(chan, AQCSFRC_LO);
+ }
+ EPWM_WRITE2(sc, EPWM_AQCSFRC, regval);
+}
+
+static bool
+am335x_ehrpwm_cfg_period(struct am335x_ehrpwm_softc *sc, u_int period)
+{
+ uint16_t regval;
+ u_int clkdiv, hspclkdiv, pwmclk, pwmtick, tbprd;
+
+ /* Can't do a period shorter than 2 clock ticks. */
+ if (period < 2 * NS_PER_SEC / PWM_CLOCK) {
+ sc->sc_clkfreq = 0;
+ sc->sc_clktick = 0;
+ sc->sc_period = 0;
+ return (false);
+ }
+
+ /*
+ * Figure out how much we have to divide down the base 100MHz clock so
+ * that we can express the requested period as a 16-bit tick count.
+ */
+ tbprd = 0;
+ for (clkdiv = 0; clkdiv < 8; ++clkdiv) {
+ const u_int cd = 1 << clkdiv;
+ for (hspclkdiv = 0; hspclkdiv < 8; ++hspclkdiv) {
+ const u_int cdhs = max(1, hspclkdiv * 2);
+ pwmclk = PWM_CLOCK / (cd * cdhs);
+ pwmtick = NS_PER_SEC / pwmclk;
+ if (period / pwmtick < 65536) {
+ tbprd = period / pwmtick;
+ break;
+ }
+ }
+ if (tbprd != 0)
+ break;
+ }
+
+ /* Handle requested period too long for available clock divisors. */
+ if (tbprd == 0)
+ return (false);
+
+ /*
+ * If anything has changed from the current settings, reprogram the
+ * clock divisors and period register.
+ */
+ if (sc->sc_clkfreq != pwmclk || sc->sc_clktick != pwmtick ||
+ sc->sc_period != tbprd * pwmtick) {
+ sc->sc_clkfreq = pwmclk;
+ sc->sc_clktick = pwmtick;
+ sc->sc_period = tbprd * pwmtick;
+
+ PWM_LOCK_ASSERT(sc);
+ regval = EPWM_READ2(sc, EPWM_TBCTL);
+ regval &= ~(TBCTL_CLKDIV_MASK | TBCTL_HSPCLKDIV_MASK);
+ regval |= TBCTL_CLKDIV(clkdiv) | TBCTL_HSPCLKDIV(hspclkdiv);
+ EPWM_WRITE2(sc, EPWM_TBCTL, regval);
+ EPWM_WRITE2(sc, EPWM_TBPRD, tbprd - 1);
+#if 0
+ device_printf(sc->sc_dev, "clkdiv %u hspclkdiv %u tbprd %u "
+ "clkfreq %u Hz clktick %u ns period got %u requested %u\n",
+ clkdiv, hspclkdiv, tbprd - 1,
+ sc->sc_clkfreq, sc->sc_clktick, sc->sc_period, period);
+#endif
+ /*
+ * If the period changed, that invalidates the current CMP
+ * registers (duty values), just zero them out.
+ */
+ am335x_ehrpwm_cfg_duty(sc, 0, 0);
+ am335x_ehrpwm_cfg_duty(sc, 1, 0);
+ }
+
+ return (true);
+}
+
+static void
+am335x_ehrpwm_freq(struct am335x_ehrpwm_softc *sc)
+{
+ int clkdiv;
+
+ clkdiv = am335x_ehrpwm_clkdiv[sc->sc_pwm_clkdiv];
+ sc->sc_pwm_freq = PWM_CLOCK / (1 * clkdiv) / sc->sc_pwm_period;
+}
+
+static int
+am335x_ehrpwm_sysctl_freq(SYSCTL_HANDLER_ARGS)
+{
+ int clkdiv, error, freq, i, period;
+ struct am335x_ehrpwm_softc *sc;
+ uint32_t reg;
+
+ sc = (struct am335x_ehrpwm_softc *)arg1;
+
+ PWM_LOCK(sc);
+ freq = sc->sc_pwm_freq;
+ PWM_UNLOCK(sc);
+
+ error = sysctl_handle_int(oidp, &freq, sizeof(freq), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ if (freq > PWM_CLOCK)
+ freq = PWM_CLOCK;
+
+ PWM_LOCK(sc);
+ if (freq != sc->sc_pwm_freq) {
+ for (i = nitems(am335x_ehrpwm_clkdiv) - 1; i >= 0; i--) {
+ clkdiv = am335x_ehrpwm_clkdiv[i];
+ period = PWM_CLOCK / clkdiv / freq;
+ if (period > USHRT_MAX)
+ break;
+ sc->sc_pwm_clkdiv = i;
+ sc->sc_pwm_period = period;
+ }
+ /* Reset the duty cycle settings. */
+ sc->sc_pwm_dutyA = 0;
+ sc->sc_pwm_dutyB = 0;
+ EPWM_WRITE2(sc, EPWM_CMPA, sc->sc_pwm_dutyA);
+ EPWM_WRITE2(sc, EPWM_CMPB, sc->sc_pwm_dutyB);
+ /* Update the clkdiv settings. */
+ reg = EPWM_READ2(sc, EPWM_TBCTL);
+ reg &= ~TBCTL_CLKDIV_MASK;
+ reg |= TBCTL_CLKDIV(sc->sc_pwm_clkdiv);
+ EPWM_WRITE2(sc, EPWM_TBCTL, reg);
+ /* Update the period settings. */
+ EPWM_WRITE2(sc, EPWM_TBPRD, sc->sc_pwm_period - 1);
+ am335x_ehrpwm_freq(sc);
+ }
+ PWM_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+am335x_ehrpwm_sysctl_clkdiv(SYSCTL_HANDLER_ARGS)
+{
+ int error, i, clkdiv;
+ struct am335x_ehrpwm_softc *sc;
+ uint32_t reg;
+
+ sc = (struct am335x_ehrpwm_softc *)arg1;
+
+ PWM_LOCK(sc);
+ clkdiv = am335x_ehrpwm_clkdiv[sc->sc_pwm_clkdiv];
+ PWM_UNLOCK(sc);
+
+ error = sysctl_handle_int(oidp, &clkdiv, sizeof(clkdiv), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ PWM_LOCK(sc);
+ if (clkdiv != am335x_ehrpwm_clkdiv[sc->sc_pwm_clkdiv]) {
+ for (i = 0; i < nitems(am335x_ehrpwm_clkdiv); i++)
+ if (clkdiv >= am335x_ehrpwm_clkdiv[i])
+ sc->sc_pwm_clkdiv = i;
+
+ reg = EPWM_READ2(sc, EPWM_TBCTL);
+ reg &= ~TBCTL_CLKDIV_MASK;
+ reg |= TBCTL_CLKDIV(sc->sc_pwm_clkdiv);
+ EPWM_WRITE2(sc, EPWM_TBCTL, reg);
+ am335x_ehrpwm_freq(sc);
+ }
+ PWM_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+am335x_ehrpwm_sysctl_duty(SYSCTL_HANDLER_ARGS)
+{
+ struct am335x_ehrpwm_softc *sc = (struct am335x_ehrpwm_softc*)arg1;
+ int error;
+ uint32_t duty;
+
+ if (oidp == sc->sc_chanA_oid)
+ duty = sc->sc_pwm_dutyA;
+ else
+ duty = sc->sc_pwm_dutyB;
+ error = sysctl_handle_int(oidp, &duty, 0, req);
+
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ if (duty > sc->sc_pwm_period) {
+ device_printf(sc->sc_dev, "Duty cycle can't be greater then period\n");
+ return (EINVAL);
+ }
+
+ PWM_LOCK(sc);
+ if (oidp == sc->sc_chanA_oid) {
+ sc->sc_pwm_dutyA = duty;
+ EPWM_WRITE2(sc, EPWM_CMPA, sc->sc_pwm_dutyA);
+ }
+ else {
+ sc->sc_pwm_dutyB = duty;
+ EPWM_WRITE2(sc, EPWM_CMPB, sc->sc_pwm_dutyB);
+ }
+ PWM_UNLOCK(sc);
+
+ return (error);
+}
+
+static int
+am335x_ehrpwm_sysctl_period(SYSCTL_HANDLER_ARGS)
+{
+ struct am335x_ehrpwm_softc *sc = (struct am335x_ehrpwm_softc*)arg1;
+ int error;
+ uint32_t period;
+
+ period = sc->sc_pwm_period;
+ error = sysctl_handle_int(oidp, &period, 0, req);
+
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ if (period < 1)
+ return (EINVAL);
+
+ if (period > USHRT_MAX)
+ period = USHRT_MAX;
+
+ PWM_LOCK(sc);
+ /* Reset the duty cycle settings. */
+ sc->sc_pwm_dutyA = 0;
+ sc->sc_pwm_dutyB = 0;
+ EPWM_WRITE2(sc, EPWM_CMPA, sc->sc_pwm_dutyA);
+ EPWM_WRITE2(sc, EPWM_CMPB, sc->sc_pwm_dutyB);
+ /* Update the period settings. */
+ sc->sc_pwm_period = period;
+ EPWM_WRITE2(sc, EPWM_TBPRD, period - 1);
+ am335x_ehrpwm_freq(sc);
+ PWM_UNLOCK(sc);
+
+ return (error);
+}
+
+static int
+am335x_ehrpwm_channel_count(device_t dev, u_int *nchannel)
+{
+
+ *nchannel = NUM_CHANNELS;
+
+ return (0);
+}
+
+static int
+am335x_ehrpwm_channel_config(device_t dev, u_int channel, u_int period, u_int duty)
+{
+ struct am335x_ehrpwm_softc *sc;
+ bool status;
+
+ if (channel >= NUM_CHANNELS)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+
+ PWM_LOCK(sc);
+ status = am335x_ehrpwm_cfg_period(sc, period);
+ if (status)
+ am335x_ehrpwm_cfg_duty(sc, channel, duty);
+ PWM_UNLOCK(sc);
+
+ return (status ? 0 : EINVAL);
+}
+
+static int
+am335x_ehrpwm_channel_get_config(device_t dev, u_int channel,
+ u_int *period, u_int *duty)
+{
+ struct am335x_ehrpwm_softc *sc;
+
+ if (channel >= NUM_CHANNELS)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+ *period = sc->sc_period;
+ *duty = sc->sc_channels[channel].duty;
+ return (0);
+}
+
+static int
+am335x_ehrpwm_channel_enable(device_t dev, u_int channel, bool enable)
+{
+ struct am335x_ehrpwm_softc *sc;
+
+ if (channel >= NUM_CHANNELS)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+
+ PWM_LOCK(sc);
+ am335x_ehrpwm_cfg_enable(sc, channel, enable);
+ PWM_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+am335x_ehrpwm_channel_is_enabled(device_t dev, u_int channel, bool *enabled)
+{
+ struct am335x_ehrpwm_softc *sc;
+
+ if (channel >= NUM_CHANNELS)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+
+ *enabled = sc->sc_channels[channel].enabled;
+
+ return (0);
+}
+
+static int
+am335x_ehrpwm_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+ return (ENXIO);
+
+ device_set_desc(dev, "AM335x EHRPWM");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+am335x_ehrpwm_attach(device_t dev)
+{
+ struct am335x_ehrpwm_softc *sc;
+ uint32_t reg;
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid *tree;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+
+ PWM_LOCK_INIT(sc);
+
+ sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &sc->sc_mem_rid, RF_ACTIVE);
+ if (sc->sc_mem_res == NULL) {
+ device_printf(dev, "cannot allocate memory resources\n");
+ goto fail;
+ }
+
+ /* Init sysctl interface */
+ ctx = device_get_sysctl_ctx(sc->sc_dev);
+ tree = device_get_sysctl_tree(sc->sc_dev);
+
+ sc->sc_clkdiv_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "clkdiv", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0,
+ am335x_ehrpwm_sysctl_clkdiv, "I", "PWM clock prescaler");
+
+ sc->sc_freq_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "freq", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0,
+ am335x_ehrpwm_sysctl_freq, "I", "PWM frequency");
+
+ sc->sc_period_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "period", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0,
+ am335x_ehrpwm_sysctl_period, "I", "PWM period");
+
+ sc->sc_chanA_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "dutyA", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0,
+ am335x_ehrpwm_sysctl_duty, "I", "Channel A duty cycles");
+
+ sc->sc_chanB_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "dutyB", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0,
+ am335x_ehrpwm_sysctl_duty, "I", "Channel B duty cycles");
+
+ /* CONFIGURE EPWM1 */
+ reg = EPWM_READ2(sc, EPWM_TBCTL);
+ reg &= ~(TBCTL_CLKDIV_MASK | TBCTL_HSPCLKDIV_MASK);
+ EPWM_WRITE2(sc, EPWM_TBCTL, reg);
+
+ sc->sc_pwm_period = DEFAULT_PWM_PERIOD;
+ sc->sc_pwm_dutyA = 0;
+ sc->sc_pwm_dutyB = 0;
+ am335x_ehrpwm_freq(sc);
+
+ EPWM_WRITE2(sc, EPWM_TBPRD, sc->sc_pwm_period - 1);
+ EPWM_WRITE2(sc, EPWM_CMPA, sc->sc_pwm_dutyA);
+ EPWM_WRITE2(sc, EPWM_CMPB, sc->sc_pwm_dutyB);
+
+ EPWM_WRITE2(sc, EPWM_AQCTLA, (AQCTL_ZRO_SET | AQCTL_CAU_CLEAR));
+ EPWM_WRITE2(sc, EPWM_AQCTLB, (AQCTL_ZRO_SET | AQCTL_CBU_CLEAR));
+
+ /* START EPWM */
+ reg &= ~TBCTL_CTRMODE_MASK;
+ reg |= TBCTL_CTRMODE_UP | TBCTL_FREERUN;
+ EPWM_WRITE2(sc, EPWM_TBCTL, reg);
+
+ EPWM_WRITE2(sc, EPWM_TZCTL, 0xf);
+ reg = EPWM_READ2(sc, EPWM_TZFLG);
+
+ if ((sc->sc_busdev = device_add_child(dev, "pwmbus", -1)) == NULL) {
+ device_printf(dev, "Cannot add child pwmbus\n");
+ // This driver can still do things even without the bus child.
+ }
+
+ bus_generic_probe(dev);
+ return (bus_generic_attach(dev));
+fail:
+ PWM_LOCK_DESTROY(sc);
+ if (sc->sc_mem_res)
+ bus_release_resource(dev, SYS_RES_MEMORY,
+ sc->sc_mem_rid, sc->sc_mem_res);
+
+ return(ENXIO);
+}
+
+static int
+am335x_ehrpwm_detach(device_t dev)
+{
+ struct am335x_ehrpwm_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ if ((error = bus_generic_detach(sc->sc_dev)) != 0)
+ return (error);
+
+ PWM_LOCK(sc);
+
+ if (sc->sc_busdev != NULL)
+ device_delete_child(dev, sc->sc_busdev);
+
+ if (sc->sc_mem_res)
+ bus_release_resource(dev, SYS_RES_MEMORY,
+ sc->sc_mem_rid, sc->sc_mem_res);
+
+ PWM_UNLOCK(sc);
+
+ PWM_LOCK_DESTROY(sc);
+
+ return (0);
+}
+
+static phandle_t
+am335x_ehrpwm_get_node(device_t bus, device_t dev)
+{
+
+ /*
+ * Share our controller node with our pwmbus child; it instantiates
+ * devices by walking the children contained within our node.
+ */
+ return ofw_bus_get_node(bus);
+}
+
+static device_method_t am335x_ehrpwm_methods[] = {
+ DEVMETHOD(device_probe, am335x_ehrpwm_probe),
+ DEVMETHOD(device_attach, am335x_ehrpwm_attach),
+ DEVMETHOD(device_detach, am335x_ehrpwm_detach),
+
+ /* ofw_bus_if */
+ DEVMETHOD(ofw_bus_get_node, am335x_ehrpwm_get_node),
+
+ /* pwm interface */
+ DEVMETHOD(pwmbus_channel_count, am335x_ehrpwm_channel_count),
+ DEVMETHOD(pwmbus_channel_config, am335x_ehrpwm_channel_config),
+ DEVMETHOD(pwmbus_channel_get_config, am335x_ehrpwm_channel_get_config),
+ DEVMETHOD(pwmbus_channel_enable, am335x_ehrpwm_channel_enable),
+ DEVMETHOD(pwmbus_channel_is_enabled, am335x_ehrpwm_channel_is_enabled),
+
+ DEVMETHOD_END
+};
+
+static driver_t am335x_ehrpwm_driver = {
+ "pwm",
+ am335x_ehrpwm_methods,
+ sizeof(struct am335x_ehrpwm_softc),
+};
+
+static devclass_t am335x_ehrpwm_devclass;
+
+DRIVER_MODULE(am335x_ehrpwm, am335x_pwmss, am335x_ehrpwm_driver, am335x_ehrpwm_devclass, 0, 0);
+MODULE_VERSION(am335x_ehrpwm, 1);
+MODULE_DEPEND(am335x_ehrpwm, am335x_pwmss, 1, 1, 1);
+MODULE_DEPEND(am335x_ehrpwm, pwmbus, 1, 1, 1);
diff --git a/sys/arm/ti/am335x/am335x_gpio.c b/sys/arm/ti/am335x/am335x_gpio.c
new file mode 100644
index 000000000000..beb169b3e4b5
--- /dev/null
+++ b/sys/arm/ti/am335x/am335x_gpio.c
@@ -0,0 +1,158 @@
+/*-
+ * Copyright (c) 2012 Damjan Marion <dmarion@FreeBSD.org>
+ * Copyright (c) 2014 Andrew Turner <andrew@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/intr.h>
+#include <sys/gpio.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/ti/ti_cpuid.h>
+#include <arm/ti/ti_gpio.h>
+#include <arm/ti/ti_pinmux.h>
+
+#include <arm/ti/am335x/am335x_scm_padconf.h>
+
+#include "ti_gpio_if.h"
+
+static struct ofw_compat_data compat_data[] = {
+ {"ti,am335x-gpio", 1},
+ /* Linux uses ti,omap4-gpio on am335x so we need to support it */
+ {"ti,omap4-gpio", 1},
+ {"ti,gpio", 1},
+ {NULL, 0},
+};
+
+static int
+am335x_gpio_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ if (ti_chip() != CHIP_AM335X)
+ return (ENXIO);
+
+ device_set_desc(dev, "TI AM335x General Purpose I/O (GPIO)");
+
+ return (0);
+}
+
+static int
+am335x_gpio_set_flags(device_t dev, uint32_t gpio, uint32_t flags)
+{
+ unsigned int state = 0;
+ struct ti_gpio_softc *sc = device_get_softc(dev);
+
+ if (flags & GPIO_PIN_OUTPUT) {
+ if (flags & GPIO_PIN_PULLUP)
+ state = PADCONF_OUTPUT_PULLUP;
+ else
+ state = PADCONF_OUTPUT;
+ } else if (flags & GPIO_PIN_INPUT) {
+ if (flags & GPIO_PIN_PULLUP)
+ state = PADCONF_INPUT_PULLUP;
+ else if (flags & GPIO_PIN_PULLDOWN)
+ state = PADCONF_INPUT_PULLDOWN;
+ else
+ state = PADCONF_INPUT;
+ }
+ return ti_pinmux_padconf_set_gpiomode(sc->sc_bank*32 + gpio, state);
+}
+
+static int
+am335x_gpio_get_flags(device_t dev, uint32_t gpio, uint32_t *flags)
+{
+ unsigned int state;
+ struct ti_gpio_softc *sc = device_get_softc(dev);
+
+ if (ti_pinmux_padconf_get_gpiomode(sc->sc_bank*32 + gpio, &state) != 0) {
+ *flags = 0;
+ return (EINVAL);
+ } else {
+ switch (state) {
+ case PADCONF_OUTPUT:
+ *flags = GPIO_PIN_OUTPUT;
+ break;
+ case PADCONF_OUTPUT_PULLUP:
+ *flags = GPIO_PIN_OUTPUT | GPIO_PIN_PULLUP;
+ break;
+ case PADCONF_INPUT:
+ *flags = GPIO_PIN_INPUT;
+ break;
+ case PADCONF_INPUT_PULLUP:
+ *flags = GPIO_PIN_INPUT | GPIO_PIN_PULLUP;
+ break;
+ case PADCONF_INPUT_PULLDOWN:
+ *flags = GPIO_PIN_INPUT | GPIO_PIN_PULLDOWN;
+ break;
+ default:
+ *flags = 0;
+ break;
+ }
+ }
+
+ return (0);
+}
+
+static device_method_t am335x_gpio_methods[] = {
+ /* bus interface */
+ DEVMETHOD(device_probe, am335x_gpio_probe),
+
+ /* ti_gpio interface */
+ DEVMETHOD(ti_gpio_set_flags, am335x_gpio_set_flags),
+ DEVMETHOD(ti_gpio_get_flags, am335x_gpio_get_flags),
+
+ DEVMETHOD_END
+};
+
+extern driver_t ti_gpio_driver;
+static devclass_t am335x_gpio_devclass;
+
+DEFINE_CLASS_1(gpio, am335x_gpio_driver, am335x_gpio_methods,
+ sizeof(struct ti_gpio_softc), ti_gpio_driver);
+DRIVER_MODULE(am335x_gpio, simplebus, am335x_gpio_driver, am335x_gpio_devclass,
+ 0, 0);
+MODULE_DEPEND(am335x_gpio, ti_sysc, 1, 1, 1);
diff --git a/sys/arm/ti/am335x/am335x_lcd.c b/sys/arm/ti/am335x/am335x_lcd.c
new file mode 100644
index 000000000000..387bd37e3ebf
--- /dev/null
+++ b/sys/arm/ti/am335x/am335x_lcd.c
@@ -0,0 +1,1104 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright 2013 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_syscons.h"
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/clock.h>
+#include <sys/eventhandler.h>
+#include <sys/time.h>
+#include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <sys/fbio.h>
+#include <sys/consio.h>
+
+#include <machine/bus.h>
+
+#include <dev/extres/clk/clk.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/videomode/videomode.h>
+#include <dev/videomode/edidvar.h>
+
+#include <dev/fb/fbreg.h>
+#ifdef DEV_SC
+#include <dev/syscons/syscons.h>
+#else /* VT */
+#include <dev/vt/vt.h>
+#endif
+
+#include <arm/ti/ti_sysc.h>
+#include <arm/ti/ti_scm.h>
+
+#include "am335x_lcd.h"
+#include "am335x_pwm.h"
+
+#include "fb_if.h"
+#include "hdmi_if.h"
+
+#define LCD_PID 0x00
+#define LCD_CTRL 0x04
+#define CTRL_DIV_MASK 0xff
+#define CTRL_DIV_SHIFT 8
+#define CTRL_AUTO_UFLOW_RESTART (1 << 1)
+#define CTRL_RASTER_MODE 1
+#define CTRL_LIDD_MODE 0
+#define LCD_LIDD_CTRL 0x0C
+#define LCD_LIDD_CS0_CONF 0x10
+#define LCD_LIDD_CS0_ADDR 0x14
+#define LCD_LIDD_CS0_DATA 0x18
+#define LCD_LIDD_CS1_CONF 0x1C
+#define LCD_LIDD_CS1_ADDR 0x20
+#define LCD_LIDD_CS1_DATA 0x24
+#define LCD_RASTER_CTRL 0x28
+#define RASTER_CTRL_TFT24_UNPACKED (1 << 26)
+#define RASTER_CTRL_TFT24 (1 << 25)
+#define RASTER_CTRL_STN565 (1 << 24)
+#define RASTER_CTRL_TFTPMAP (1 << 23)
+#define RASTER_CTRL_NIBMODE (1 << 22)
+#define RASTER_CTRL_PALMODE_SHIFT 20
+#define PALETTE_PALETTE_AND_DATA 0x00
+#define PALETTE_PALETTE_ONLY 0x01
+#define PALETTE_DATA_ONLY 0x02
+#define RASTER_CTRL_REQDLY_SHIFT 12
+#define RASTER_CTRL_MONO8B (1 << 9)
+#define RASTER_CTRL_RBORDER (1 << 8)
+#define RASTER_CTRL_LCDTFT (1 << 7)
+#define RASTER_CTRL_LCDBW (1 << 1)
+#define RASTER_CTRL_LCDEN (1 << 0)
+#define LCD_RASTER_TIMING_0 0x2C
+#define RASTER_TIMING_0_HBP_SHIFT 24
+#define RASTER_TIMING_0_HFP_SHIFT 16
+#define RASTER_TIMING_0_HSW_SHIFT 10
+#define RASTER_TIMING_0_PPLLSB_SHIFT 4
+#define RASTER_TIMING_0_PPLMSB_SHIFT 3
+#define LCD_RASTER_TIMING_1 0x30
+#define RASTER_TIMING_1_VBP_SHIFT 24
+#define RASTER_TIMING_1_VFP_SHIFT 16
+#define RASTER_TIMING_1_VSW_SHIFT 10
+#define RASTER_TIMING_1_LPP_SHIFT 0
+#define LCD_RASTER_TIMING_2 0x34
+#define RASTER_TIMING_2_HSWHI_SHIFT 27
+#define RASTER_TIMING_2_LPP_B10_SHIFT 26
+#define RASTER_TIMING_2_PHSVS (1 << 25)
+#define RASTER_TIMING_2_PHSVS_RISE (1 << 24)
+#define RASTER_TIMING_2_PHSVS_FALL (0 << 24)
+#define RASTER_TIMING_2_IOE (1 << 23)
+#define RASTER_TIMING_2_IPC (1 << 22)
+#define RASTER_TIMING_2_IHS (1 << 21)
+#define RASTER_TIMING_2_IVS (1 << 20)
+#define RASTER_TIMING_2_ACBI_SHIFT 16
+#define RASTER_TIMING_2_ACB_SHIFT 8
+#define RASTER_TIMING_2_HBPHI_SHIFT 4
+#define RASTER_TIMING_2_HFPHI_SHIFT 0
+#define LCD_RASTER_SUBPANEL 0x38
+#define LCD_RASTER_SUBPANEL2 0x3C
+#define LCD_LCDDMA_CTRL 0x40
+#define LCDDMA_CTRL_DMA_MASTER_PRIO_SHIFT 16
+#define LCDDMA_CTRL_TH_FIFO_RDY_SHIFT 8
+#define LCDDMA_CTRL_BURST_SIZE_SHIFT 4
+#define LCDDMA_CTRL_BYTES_SWAP (1 << 3)
+#define LCDDMA_CTRL_BE (1 << 1)
+#define LCDDMA_CTRL_FB0_ONLY 0
+#define LCDDMA_CTRL_FB0_FB1 (1 << 0)
+#define LCD_LCDDMA_FB0_BASE 0x44
+#define LCD_LCDDMA_FB0_CEILING 0x48
+#define LCD_LCDDMA_FB1_BASE 0x4C
+#define LCD_LCDDMA_FB1_CEILING 0x50
+#define LCD_SYSCONFIG 0x54
+#define SYSCONFIG_STANDBY_FORCE (0 << 4)
+#define SYSCONFIG_STANDBY_NONE (1 << 4)
+#define SYSCONFIG_STANDBY_SMART (2 << 4)
+#define SYSCONFIG_IDLE_FORCE (0 << 2)
+#define SYSCONFIG_IDLE_NONE (1 << 2)
+#define SYSCONFIG_IDLE_SMART (2 << 2)
+#define LCD_IRQSTATUS_RAW 0x58
+#define LCD_IRQSTATUS 0x5C
+#define LCD_IRQENABLE_SET 0x60
+#define LCD_IRQENABLE_CLEAR 0x64
+#define IRQ_EOF1 (1 << 9)
+#define IRQ_EOF0 (1 << 8)
+#define IRQ_PL (1 << 6)
+#define IRQ_FUF (1 << 5)
+#define IRQ_ACB (1 << 3)
+#define IRQ_SYNC_LOST (1 << 2)
+#define IRQ_RASTER_DONE (1 << 1)
+#define IRQ_FRAME_DONE (1 << 0)
+#define LCD_END_OF_INT_IND 0x68
+#define LCD_CLKC_ENABLE 0x6C
+#define CLKC_ENABLE_DMA (1 << 2)
+#define CLKC_ENABLE_LDID (1 << 1)
+#define CLKC_ENABLE_CORE (1 << 0)
+#define LCD_CLKC_RESET 0x70
+#define CLKC_RESET_MAIN (1 << 3)
+#define CLKC_RESET_DMA (1 << 2)
+#define CLKC_RESET_LDID (1 << 1)
+#define CLKC_RESET_CORE (1 << 0)
+
+#define LCD_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define LCD_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#define LCD_LOCK_INIT(_sc) mtx_init(&(_sc)->sc_mtx, \
+ device_get_nameunit(_sc->sc_dev), "am335x_lcd", MTX_DEF)
+#define LCD_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx);
+
+#define LCD_READ4(_sc, reg) bus_read_4((_sc)->sc_mem_res, reg);
+#define LCD_WRITE4(_sc, reg, value) \
+ bus_write_4((_sc)->sc_mem_res, reg, value);
+
+/* Backlight is controlled by eCAS interface on PWM unit 0 */
+#define PWM_UNIT 0
+#define PWM_PERIOD 100
+
+#define MODE_HBP(mode) ((mode)->htotal - (mode)->hsync_end)
+#define MODE_HFP(mode) ((mode)->hsync_start - (mode)->hdisplay)
+#define MODE_HSW(mode) ((mode)->hsync_end - (mode)->hsync_start)
+#define MODE_VBP(mode) ((mode)->vtotal - (mode)->vsync_end)
+#define MODE_VFP(mode) ((mode)->vsync_start - (mode)->vdisplay)
+#define MODE_VSW(mode) ((mode)->vsync_end - (mode)->vsync_start)
+
+#define MAX_PIXEL_CLOCK 126000
+#define MAX_BANDWIDTH (1280*1024*60)
+
+struct am335x_lcd_softc {
+ device_t sc_dev;
+ struct fb_info sc_fb_info;
+ struct resource *sc_mem_res;
+ struct resource *sc_irq_res;
+ void *sc_intr_hl;
+ struct mtx sc_mtx;
+ int sc_backlight;
+ struct sysctl_oid *sc_oid;
+
+ struct panel_info sc_panel;
+
+ /* Framebuffer */
+ bus_dma_tag_t sc_dma_tag;
+ bus_dmamap_t sc_dma_map;
+ size_t sc_fb_size;
+ bus_addr_t sc_fb_phys;
+ uint8_t *sc_fb_base;
+
+ /* HDMI framer */
+ phandle_t sc_hdmi_framer;
+ eventhandler_tag sc_hdmi_evh;
+
+ /* Clock */
+ clk_t sc_clk_dpll_disp_ck;
+};
+
+static void
+am335x_fb_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err)
+{
+ bus_addr_t *addr;
+
+ if (err)
+ return;
+
+ addr = (bus_addr_t*)arg;
+ *addr = segs[0].ds_addr;
+}
+
+static uint32_t
+am335x_lcd_calc_divisor(uint32_t reference, uint32_t freq)
+{
+ uint32_t div, i;
+ uint32_t delta, min_delta;
+
+ min_delta = freq;
+ div = 255;
+
+ /* Raster mode case: divisors are in range from 2 to 255 */
+ for (i = 2; i < 255; i++) {
+ delta = abs(reference/i - freq);
+ if (delta < min_delta) {
+ div = i;
+ min_delta = delta;
+ }
+ }
+
+ return (div);
+}
+
+static int
+am335x_lcd_sysctl_backlight(SYSCTL_HANDLER_ARGS)
+{
+ struct am335x_lcd_softc *sc = (struct am335x_lcd_softc*)arg1;
+ int error;
+ int backlight;
+
+ backlight = sc->sc_backlight;
+ error = sysctl_handle_int(oidp, &backlight, 0, req);
+
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ if (backlight < 0)
+ backlight = 0;
+ if (backlight > 100)
+ backlight = 100;
+
+ LCD_LOCK(sc);
+ error = am335x_pwm_config_ecap(PWM_UNIT, PWM_PERIOD,
+ backlight*PWM_PERIOD/100);
+ if (error == 0)
+ sc->sc_backlight = backlight;
+ LCD_UNLOCK(sc);
+
+ return (error);
+}
+
+static uint32_t
+am335x_mode_vrefresh(const struct videomode *mode)
+{
+ uint32_t refresh;
+
+ /* Calculate vertical refresh rate */
+ refresh = (mode->dot_clock * 1000 / mode->htotal);
+ refresh = (refresh + mode->vtotal / 2) / mode->vtotal;
+
+ if (mode->flags & VID_INTERLACE)
+ refresh *= 2;
+ if (mode->flags & VID_DBLSCAN)
+ refresh /= 2;
+
+ return refresh;
+}
+
+static int
+am335x_mode_is_valid(const struct videomode *mode)
+{
+ uint32_t hbp, hfp, hsw;
+ uint32_t vbp, vfp, vsw;
+
+ if (mode->dot_clock > MAX_PIXEL_CLOCK)
+ return (0);
+
+ if (mode->hdisplay & 0xf)
+ return (0);
+
+ if (mode->vdisplay > 2048)
+ return (0);
+
+ /* Check ranges for timing parameters */
+ hbp = MODE_HBP(mode) - 1;
+ hfp = MODE_HFP(mode) - 1;
+ hsw = MODE_HSW(mode) - 1;
+ vbp = MODE_VBP(mode);
+ vfp = MODE_VFP(mode);
+ vsw = MODE_VSW(mode) - 1;
+
+ if (hbp > 0x3ff)
+ return (0);
+ if (hfp > 0x3ff)
+ return (0);
+ if (hsw > 0x3ff)
+ return (0);
+
+ if (vbp > 0xff)
+ return (0);
+ if (vfp > 0xff)
+ return (0);
+ if (vsw > 0x3f)
+ return (0);
+ if (mode->vdisplay*mode->hdisplay*am335x_mode_vrefresh(mode)
+ > MAX_BANDWIDTH)
+ return (0);
+
+ return (1);
+}
+
+static void
+am335x_read_hdmi_property(device_t dev)
+{
+ phandle_t node, xref;
+ phandle_t endpoint;
+ phandle_t hdmi_xref;
+ struct am335x_lcd_softc *sc;
+
+ sc = device_get_softc(dev);
+ node = ofw_bus_get_node(dev);
+ sc->sc_hdmi_framer = 0;
+
+ /*
+ * Old FreeBSD way of referencing to HDMI framer
+ */
+ if (OF_getencprop(node, "hdmi", &hdmi_xref, sizeof(hdmi_xref)) != -1) {
+ sc->sc_hdmi_framer = hdmi_xref;
+ return;
+ }
+
+ /*
+ * Use bindings described in Linux docs:
+ * bindings/media/video-interfaces.txt
+ * We assume that the only endpoint in LCDC node
+ * is HDMI framer.
+ */
+ node = ofw_bus_find_child(node, "port");
+
+ /* No media bindings */
+ if (node == 0)
+ return;
+
+ for (endpoint = OF_child(node); endpoint != 0; endpoint = OF_peer(endpoint)) {
+ if (OF_getencprop(endpoint, "remote-endpoint", &xref, sizeof(xref)) != -1) {
+ /* port/port@0/endpoint@0 */
+ node = OF_node_from_xref(xref);
+ /* port/port@0 */
+ node = OF_parent(node);
+ /* port */
+ node = OF_parent(node);
+ /* actual owner of port, in our case HDMI framer */
+ sc->sc_hdmi_framer = OF_xref_from_node(OF_parent(node));
+ if (sc->sc_hdmi_framer != 0)
+ return;
+ }
+ }
+}
+
+static int
+am335x_read_property(device_t dev, phandle_t node, const char *name, uint32_t *val)
+{
+ pcell_t cell;
+
+ if ((OF_getencprop(node, name, &cell, sizeof(cell))) <= 0) {
+ device_printf(dev, "missing '%s' attribute in LCD panel info\n",
+ name);
+ return (ENXIO);
+ }
+
+ *val = cell;
+
+ return (0);
+}
+
+static int
+am335x_read_timing(device_t dev, phandle_t node, struct panel_info *panel)
+{
+ int error;
+ phandle_t timings_node, timing_node, native;
+
+ timings_node = ofw_bus_find_child(node, "display-timings");
+ if (timings_node == 0) {
+ device_printf(dev, "no \"display-timings\" node\n");
+ return (-1);
+ }
+
+ if (OF_searchencprop(timings_node, "native-mode", &native,
+ sizeof(native)) == -1) {
+ device_printf(dev, "no \"native-mode\" reference in \"timings\" node\n");
+ return (-1);
+ }
+
+ timing_node = OF_node_from_xref(native);
+
+ error = 0;
+ if ((error = am335x_read_property(dev, timing_node,
+ "hactive", &panel->panel_width)))
+ goto out;
+
+ if ((error = am335x_read_property(dev, timing_node,
+ "vactive", &panel->panel_height)))
+ goto out;
+
+ if ((error = am335x_read_property(dev, timing_node,
+ "hfront-porch", &panel->panel_hfp)))
+ goto out;
+
+ if ((error = am335x_read_property(dev, timing_node,
+ "hback-porch", &panel->panel_hbp)))
+ goto out;
+
+ if ((error = am335x_read_property(dev, timing_node,
+ "hsync-len", &panel->panel_hsw)))
+ goto out;
+
+ if ((error = am335x_read_property(dev, timing_node,
+ "vfront-porch", &panel->panel_vfp)))
+ goto out;
+
+ if ((error = am335x_read_property(dev, timing_node,
+ "vback-porch", &panel->panel_vbp)))
+ goto out;
+
+ if ((error = am335x_read_property(dev, timing_node,
+ "vsync-len", &panel->panel_vsw)))
+ goto out;
+
+ if ((error = am335x_read_property(dev, timing_node,
+ "clock-frequency", &panel->panel_pxl_clk)))
+ goto out;
+
+ if ((error = am335x_read_property(dev, timing_node,
+ "pixelclk-active", &panel->pixelclk_active)))
+ goto out;
+
+ if ((error = am335x_read_property(dev, timing_node,
+ "hsync-active", &panel->hsync_active)))
+ goto out;
+
+ if ((error = am335x_read_property(dev, timing_node,
+ "vsync-active", &panel->vsync_active)))
+ goto out;
+
+out:
+ return (error);
+}
+
+static int
+am335x_read_panel_info(device_t dev, phandle_t node, struct panel_info *panel)
+{
+ phandle_t panel_info_node;
+
+ panel_info_node = ofw_bus_find_child(node, "panel-info");
+ if (panel_info_node == 0)
+ return (-1);
+
+ am335x_read_property(dev, panel_info_node,
+ "ac-bias", &panel->ac_bias);
+
+ am335x_read_property(dev, panel_info_node,
+ "ac-bias-intrpt", &panel->ac_bias_intrpt);
+
+ am335x_read_property(dev, panel_info_node,
+ "dma-burst-sz", &panel->dma_burst_sz);
+
+ am335x_read_property(dev, panel_info_node,
+ "bpp", &panel->bpp);
+
+ am335x_read_property(dev, panel_info_node,
+ "fdd", &panel->fdd);
+
+ am335x_read_property(dev, panel_info_node,
+ "sync-edge", &panel->sync_edge);
+
+ am335x_read_property(dev, panel_info_node,
+ "sync-ctrl", &panel->sync_ctrl);
+
+ return (0);
+}
+
+static void
+am335x_lcd_intr(void *arg)
+{
+ struct am335x_lcd_softc *sc = arg;
+ uint32_t reg;
+
+ reg = LCD_READ4(sc, LCD_IRQSTATUS);
+ LCD_WRITE4(sc, LCD_IRQSTATUS, reg);
+ /* Read value back to make sure it reached the hardware */
+ reg = LCD_READ4(sc, LCD_IRQSTATUS);
+
+ if (reg & IRQ_SYNC_LOST) {
+ reg = LCD_READ4(sc, LCD_RASTER_CTRL);
+ reg &= ~RASTER_CTRL_LCDEN;
+ LCD_WRITE4(sc, LCD_RASTER_CTRL, reg);
+
+ reg = LCD_READ4(sc, LCD_RASTER_CTRL);
+ reg |= RASTER_CTRL_LCDEN;
+ LCD_WRITE4(sc, LCD_RASTER_CTRL, reg);
+ goto done;
+ }
+
+ if (reg & IRQ_PL) {
+ reg = LCD_READ4(sc, LCD_RASTER_CTRL);
+ reg &= ~RASTER_CTRL_LCDEN;
+ LCD_WRITE4(sc, LCD_RASTER_CTRL, reg);
+
+ reg = LCD_READ4(sc, LCD_RASTER_CTRL);
+ reg |= RASTER_CTRL_LCDEN;
+ LCD_WRITE4(sc, LCD_RASTER_CTRL, reg);
+ goto done;
+ }
+
+ if (reg & IRQ_EOF0) {
+ LCD_WRITE4(sc, LCD_LCDDMA_FB0_BASE, sc->sc_fb_phys);
+ LCD_WRITE4(sc, LCD_LCDDMA_FB0_CEILING, sc->sc_fb_phys + sc->sc_fb_size - 1);
+ reg &= ~IRQ_EOF0;
+ }
+
+ if (reg & IRQ_EOF1) {
+ LCD_WRITE4(sc, LCD_LCDDMA_FB1_BASE, sc->sc_fb_phys);
+ LCD_WRITE4(sc, LCD_LCDDMA_FB1_CEILING, sc->sc_fb_phys + sc->sc_fb_size - 1);
+ reg &= ~IRQ_EOF1;
+ }
+
+ if (reg & IRQ_FUF) {
+ /* TODO: Handle FUF */
+ }
+
+ if (reg & IRQ_ACB) {
+ /* TODO: Handle ACB */
+ }
+
+done:
+ LCD_WRITE4(sc, LCD_END_OF_INT_IND, 0);
+ /* Read value back to make sure it reached the hardware */
+ reg = LCD_READ4(sc, LCD_END_OF_INT_IND);
+}
+
+static const struct videomode *
+am335x_lcd_pick_mode(struct edid_info *ei)
+{
+ const struct videomode *videomode;
+ const struct videomode *m;
+ int n;
+
+ /* Get standard VGA as default */
+ videomode = NULL;
+
+ /*
+ * Pick a mode.
+ */
+ if (ei->edid_preferred_mode != NULL) {
+ if (am335x_mode_is_valid(ei->edid_preferred_mode))
+ videomode = ei->edid_preferred_mode;
+ }
+
+ if (videomode == NULL) {
+ m = ei->edid_modes;
+
+ sort_modes(ei->edid_modes,
+ &ei->edid_preferred_mode,
+ ei->edid_nmodes);
+ for (n = 0; n < ei->edid_nmodes; n++)
+ if (am335x_mode_is_valid(&m[n])) {
+ videomode = &m[n];
+ break;
+ }
+ }
+
+ return videomode;
+}
+
+static int
+am335x_lcd_configure(struct am335x_lcd_softc *sc)
+{
+ int div;
+ uint32_t reg, timing0, timing1, timing2;
+ uint32_t burst_log;
+ size_t dma_size;
+ uint32_t hbp, hfp, hsw;
+ uint32_t vbp, vfp, vsw;
+ uint32_t width, height;
+ uint64_t ref_freq;
+ int err;
+
+ /*
+ * try to adjust clock to get double of requested frequency
+ * HDMI/DVI displays are very sensitive to error in frequncy value
+ */
+
+ err = clk_set_freq(sc->sc_clk_dpll_disp_ck, sc->sc_panel.panel_pxl_clk*2,
+ CLK_SET_ROUND_ANY);
+ if (err != 0) {
+ device_printf(sc->sc_dev, "can't set source frequency\n");
+ return (ENXIO);
+ }
+
+ err = clk_get_freq(sc->sc_clk_dpll_disp_ck, &ref_freq);
+ if (err != 0) {
+ device_printf(sc->sc_dev, "can't get reference frequency\n");
+ return (ENXIO);
+ }
+
+ /* Panel initialization */
+ dma_size = round_page(sc->sc_panel.panel_width*sc->sc_panel.panel_height*sc->sc_panel.bpp/8);
+
+ /*
+ * Now allocate framebuffer memory
+ */
+ err = bus_dma_tag_create(
+ bus_get_dma_tag(sc->sc_dev),
+ 4, 0, /* alignment, boundary */
+ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ dma_size, 1, /* maxsize, nsegments */
+ dma_size, 0, /* maxsegsize, flags */
+ NULL, NULL, /* lockfunc, lockarg */
+ &sc->sc_dma_tag);
+ if (err)
+ goto done;
+
+ err = bus_dmamem_alloc(sc->sc_dma_tag, (void **)&sc->sc_fb_base,
+ BUS_DMA_COHERENT, &sc->sc_dma_map);
+
+ if (err) {
+ device_printf(sc->sc_dev, "cannot allocate framebuffer\n");
+ goto done;
+ }
+
+ err = bus_dmamap_load(sc->sc_dma_tag, sc->sc_dma_map, sc->sc_fb_base,
+ dma_size, am335x_fb_dmamap_cb, &sc->sc_fb_phys, BUS_DMA_NOWAIT);
+
+ if (err) {
+ device_printf(sc->sc_dev, "cannot load DMA map\n");
+ goto done;
+ }
+
+ /* Make sure it's blank */
+ memset(sc->sc_fb_base, 0x0, dma_size);
+
+ /* Calculate actual FB Size */
+ sc->sc_fb_size = sc->sc_panel.panel_width*sc->sc_panel.panel_height*sc->sc_panel.bpp/8;
+
+ /* Only raster mode is supported */
+ reg = CTRL_RASTER_MODE;
+ div = am335x_lcd_calc_divisor(ref_freq, sc->sc_panel.panel_pxl_clk);
+ reg |= (div << CTRL_DIV_SHIFT);
+ LCD_WRITE4(sc, LCD_CTRL, reg);
+
+ /* Set timing */
+ timing0 = timing1 = timing2 = 0;
+
+ hbp = sc->sc_panel.panel_hbp - 1;
+ hfp = sc->sc_panel.panel_hfp - 1;
+ hsw = sc->sc_panel.panel_hsw - 1;
+
+ vbp = sc->sc_panel.panel_vbp;
+ vfp = sc->sc_panel.panel_vfp;
+ vsw = sc->sc_panel.panel_vsw - 1;
+
+ height = sc->sc_panel.panel_height - 1;
+ width = sc->sc_panel.panel_width - 1;
+
+ /* Horizontal back porch */
+ timing0 |= (hbp & 0xff) << RASTER_TIMING_0_HBP_SHIFT;
+ timing2 |= ((hbp >> 8) & 3) << RASTER_TIMING_2_HBPHI_SHIFT;
+ /* Horizontal front porch */
+ timing0 |= (hfp & 0xff) << RASTER_TIMING_0_HFP_SHIFT;
+ timing2 |= ((hfp >> 8) & 3) << RASTER_TIMING_2_HFPHI_SHIFT;
+ /* Horizontal sync width */
+ timing0 |= (hsw & 0x3f) << RASTER_TIMING_0_HSW_SHIFT;
+ timing2 |= ((hsw >> 6) & 0xf) << RASTER_TIMING_2_HSWHI_SHIFT;
+
+ /* Vertical back porch, front porch, sync width */
+ timing1 |= (vbp & 0xff) << RASTER_TIMING_1_VBP_SHIFT;
+ timing1 |= (vfp & 0xff) << RASTER_TIMING_1_VFP_SHIFT;
+ timing1 |= (vsw & 0x3f) << RASTER_TIMING_1_VSW_SHIFT;
+
+ /* Pixels per line */
+ timing0 |= ((width >> 10) & 1)
+ << RASTER_TIMING_0_PPLMSB_SHIFT;
+ timing0 |= ((width >> 4) & 0x3f)
+ << RASTER_TIMING_0_PPLLSB_SHIFT;
+
+ /* Lines per panel */
+ timing1 |= (height & 0x3ff)
+ << RASTER_TIMING_1_LPP_SHIFT;
+ timing2 |= ((height >> 10 ) & 1)
+ << RASTER_TIMING_2_LPP_B10_SHIFT;
+
+ /* clock signal settings */
+ if (sc->sc_panel.sync_ctrl)
+ timing2 |= RASTER_TIMING_2_PHSVS;
+ if (sc->sc_panel.sync_edge)
+ timing2 |= RASTER_TIMING_2_PHSVS_RISE;
+ else
+ timing2 |= RASTER_TIMING_2_PHSVS_FALL;
+ if (sc->sc_panel.hsync_active == 0)
+ timing2 |= RASTER_TIMING_2_IHS;
+ if (sc->sc_panel.vsync_active == 0)
+ timing2 |= RASTER_TIMING_2_IVS;
+ if (sc->sc_panel.pixelclk_active == 0)
+ timing2 |= RASTER_TIMING_2_IPC;
+
+ /* AC bias */
+ timing2 |= (sc->sc_panel.ac_bias << RASTER_TIMING_2_ACB_SHIFT);
+ timing2 |= (sc->sc_panel.ac_bias_intrpt << RASTER_TIMING_2_ACBI_SHIFT);
+
+ LCD_WRITE4(sc, LCD_RASTER_TIMING_0, timing0);
+ LCD_WRITE4(sc, LCD_RASTER_TIMING_1, timing1);
+ LCD_WRITE4(sc, LCD_RASTER_TIMING_2, timing2);
+
+ /* DMA settings */
+ reg = LCDDMA_CTRL_FB0_FB1;
+ /* Find power of 2 for current burst size */
+ switch (sc->sc_panel.dma_burst_sz) {
+ case 1:
+ burst_log = 0;
+ break;
+ case 2:
+ burst_log = 1;
+ break;
+ case 4:
+ burst_log = 2;
+ break;
+ case 8:
+ burst_log = 3;
+ break;
+ case 16:
+ default:
+ burst_log = 4;
+ break;
+ }
+ reg |= (burst_log << LCDDMA_CTRL_BURST_SIZE_SHIFT);
+ /* XXX: FIFO TH */
+ reg |= (0 << LCDDMA_CTRL_TH_FIFO_RDY_SHIFT);
+ LCD_WRITE4(sc, LCD_LCDDMA_CTRL, reg);
+
+ LCD_WRITE4(sc, LCD_LCDDMA_FB0_BASE, sc->sc_fb_phys);
+ LCD_WRITE4(sc, LCD_LCDDMA_FB0_CEILING, sc->sc_fb_phys + sc->sc_fb_size - 1);
+ LCD_WRITE4(sc, LCD_LCDDMA_FB1_BASE, sc->sc_fb_phys);
+ LCD_WRITE4(sc, LCD_LCDDMA_FB1_CEILING, sc->sc_fb_phys + sc->sc_fb_size - 1);
+
+ /* Enable LCD */
+ reg = RASTER_CTRL_LCDTFT;
+ reg |= (sc->sc_panel.fdd << RASTER_CTRL_REQDLY_SHIFT);
+ reg |= (PALETTE_DATA_ONLY << RASTER_CTRL_PALMODE_SHIFT);
+ if (sc->sc_panel.bpp >= 24)
+ reg |= RASTER_CTRL_TFT24;
+ if (sc->sc_panel.bpp == 32)
+ reg |= RASTER_CTRL_TFT24_UNPACKED;
+ LCD_WRITE4(sc, LCD_RASTER_CTRL, reg);
+
+ LCD_WRITE4(sc, LCD_CLKC_ENABLE,
+ CLKC_ENABLE_DMA | CLKC_ENABLE_LDID | CLKC_ENABLE_CORE);
+
+ LCD_WRITE4(sc, LCD_CLKC_RESET, CLKC_RESET_MAIN);
+ DELAY(100);
+ LCD_WRITE4(sc, LCD_CLKC_RESET, 0);
+
+ reg = IRQ_EOF1 | IRQ_EOF0 | IRQ_FUF | IRQ_PL |
+ IRQ_ACB | IRQ_SYNC_LOST | IRQ_RASTER_DONE |
+ IRQ_FRAME_DONE;
+ LCD_WRITE4(sc, LCD_IRQENABLE_SET, reg);
+
+ reg = LCD_READ4(sc, LCD_RASTER_CTRL);
+ reg |= RASTER_CTRL_LCDEN;
+ LCD_WRITE4(sc, LCD_RASTER_CTRL, reg);
+
+ LCD_WRITE4(sc, LCD_SYSCONFIG,
+ SYSCONFIG_STANDBY_SMART | SYSCONFIG_IDLE_SMART);
+
+ sc->sc_fb_info.fb_name = device_get_nameunit(sc->sc_dev);
+ sc->sc_fb_info.fb_vbase = (intptr_t)sc->sc_fb_base;
+ sc->sc_fb_info.fb_pbase = sc->sc_fb_phys;
+ sc->sc_fb_info.fb_size = sc->sc_fb_size;
+ sc->sc_fb_info.fb_bpp = sc->sc_fb_info.fb_depth = sc->sc_panel.bpp;
+ sc->sc_fb_info.fb_stride = sc->sc_panel.panel_width*sc->sc_panel.bpp / 8;
+ sc->sc_fb_info.fb_width = sc->sc_panel.panel_width;
+ sc->sc_fb_info.fb_height = sc->sc_panel.panel_height;
+
+#ifdef DEV_SC
+ err = (sc_attach_unit(device_get_unit(sc->sc_dev),
+ device_get_flags(sc->sc_dev) | SC_AUTODETECT_KBD));
+
+ if (err) {
+ device_printf(sc->sc_dev, "failed to attach syscons\n");
+ goto fail;
+ }
+
+ am335x_lcd_syscons_setup((vm_offset_t)sc->sc_fb_base, sc->sc_fb_phys, &panel);
+#else /* VT */
+ device_t fbd = device_add_child(sc->sc_dev, "fbd",
+ device_get_unit(sc->sc_dev));
+ if (fbd != NULL) {
+ if (device_probe_and_attach(fbd) != 0)
+ device_printf(sc->sc_dev, "failed to attach fbd device\n");
+ } else
+ device_printf(sc->sc_dev, "failed to add fbd child\n");
+#endif
+
+done:
+ return (err);
+}
+
+static void
+am335x_lcd_hdmi_event(void *arg, device_t hdmi, int event)
+{
+ struct am335x_lcd_softc *sc;
+ const struct videomode *videomode;
+ struct videomode hdmi_mode;
+ device_t hdmi_dev;
+ uint8_t *edid;
+ uint32_t edid_len;
+ struct edid_info ei;
+
+ sc = arg;
+
+ /* Nothing to work with */
+ if (!sc->sc_hdmi_framer) {
+ device_printf(sc->sc_dev, "HDMI event without HDMI framer set\n");
+ return;
+ }
+
+ hdmi_dev = OF_device_from_xref(sc->sc_hdmi_framer);
+ if (!hdmi_dev) {
+ device_printf(sc->sc_dev, "no actual device for \"hdmi\" property\n");
+ return;
+ }
+
+ edid = NULL;
+ edid_len = 0;
+ if (HDMI_GET_EDID(hdmi_dev, &edid, &edid_len) != 0) {
+ device_printf(sc->sc_dev, "failed to get EDID info from HDMI framer\n");
+ return;
+ }
+
+ videomode = NULL;
+
+ if (edid_parse(edid, &ei) == 0) {
+ edid_print(&ei);
+ videomode = am335x_lcd_pick_mode(&ei);
+ } else
+ device_printf(sc->sc_dev, "failed to parse EDID\n");
+
+ /* Use standard VGA as fallback */
+ if (videomode == NULL)
+ videomode = pick_mode_by_ref(640, 480, 60);
+
+ if (videomode == NULL) {
+ device_printf(sc->sc_dev, "failed to find usable videomode");
+ return;
+ }
+
+ device_printf(sc->sc_dev, "detected videomode: %dx%d @ %dKHz\n", videomode->hdisplay,
+ videomode->vdisplay, am335x_mode_vrefresh(videomode));
+
+ sc->sc_panel.panel_width = videomode->hdisplay;
+ sc->sc_panel.panel_height = videomode->vdisplay;
+ sc->sc_panel.panel_hfp = videomode->hsync_start - videomode->hdisplay;
+ sc->sc_panel.panel_hbp = videomode->htotal - videomode->hsync_end;
+ sc->sc_panel.panel_hsw = videomode->hsync_end - videomode->hsync_start;
+ sc->sc_panel.panel_vfp = videomode->vsync_start - videomode->vdisplay;
+ sc->sc_panel.panel_vbp = videomode->vtotal - videomode->vsync_end;
+ sc->sc_panel.panel_vsw = videomode->vsync_end - videomode->vsync_start;
+ sc->sc_panel.pixelclk_active = 1;
+
+ /* logic for HSYNC should be reversed */
+ if (videomode->flags & VID_NHSYNC)
+ sc->sc_panel.hsync_active = 1;
+ else
+ sc->sc_panel.hsync_active = 0;
+
+ if (videomode->flags & VID_NVSYNC)
+ sc->sc_panel.vsync_active = 0;
+ else
+ sc->sc_panel.vsync_active = 1;
+
+ sc->sc_panel.panel_pxl_clk = videomode->dot_clock * 1000;
+
+ am335x_lcd_configure(sc);
+
+ memcpy(&hdmi_mode, videomode, sizeof(hdmi_mode));
+ hdmi_mode.hskew = videomode->hsync_end - videomode->hsync_start;
+ hdmi_mode.flags |= VID_HSKEW;
+
+ HDMI_SET_VIDEOMODE(hdmi_dev, &hdmi_mode);
+}
+
+static int
+am335x_lcd_probe(device_t dev)
+{
+#ifdef DEV_SC
+ int err;
+#endif
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "ti,am33xx-tilcdc"))
+ return (ENXIO);
+
+ device_set_desc(dev, "AM335x LCD controller");
+
+#ifdef DEV_SC
+ err = sc_probe_unit(device_get_unit(dev),
+ device_get_flags(dev) | SC_AUTODETECT_KBD);
+ if (err != 0)
+ return (err);
+#endif
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+am335x_lcd_attach(device_t dev)
+{
+ struct am335x_lcd_softc *sc;
+
+ int err;
+ int rid;
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid *tree;
+ phandle_t root, panel_node;
+
+ err = 0;
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+
+ am335x_read_hdmi_property(dev);
+
+ root = OF_finddevice("/");
+ if (root == -1) {
+ device_printf(dev, "failed to get FDT root node\n");
+ return (ENXIO);
+ }
+
+ /* Fixme: Cant find any reference in DTS for dpll_disp_ck@498 for now. */
+ err = clk_get_by_name(dev, "dpll_disp_ck@498", &sc->sc_clk_dpll_disp_ck);
+ if (err != 0) {
+ device_printf(dev, "Cant get dpll_disp_ck@49\n");
+ return (ENXIO);
+ }
+
+ sc->sc_panel.ac_bias = 255;
+ sc->sc_panel.ac_bias_intrpt = 0;
+ sc->sc_panel.dma_burst_sz = 16;
+ sc->sc_panel.bpp = 16;
+ sc->sc_panel.fdd = 128;
+ sc->sc_panel.sync_edge = 0;
+ sc->sc_panel.sync_ctrl = 1;
+
+ panel_node = fdt_find_compatible(root, "ti,tilcdc,panel", 1);
+ if (panel_node != 0) {
+ device_printf(dev, "using static panel info\n");
+ if (am335x_read_panel_info(dev, panel_node, &sc->sc_panel)) {
+ device_printf(dev, "failed to read panel info\n");
+ return (ENXIO);
+ }
+
+ if (am335x_read_timing(dev, panel_node, &sc->sc_panel)) {
+ device_printf(dev, "failed to read timings\n");
+ return (ENXIO);
+ }
+ }
+
+ err = ti_sysc_clock_enable(device_get_parent(dev));
+ if (err != 0) {
+ device_printf(dev, "Failed to enable sysc clkctrl, err %d\n", err);
+ return (ENXIO);
+ }
+
+ rid = 0;
+ sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (!sc->sc_mem_res) {
+ device_printf(dev, "cannot allocate memory window\n");
+ return (ENXIO);
+ }
+
+ rid = 0;
+ sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (!sc->sc_irq_res) {
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+ device_printf(dev, "cannot allocate interrupt\n");
+ return (ENXIO);
+ }
+
+ if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+ NULL, am335x_lcd_intr, sc,
+ &sc->sc_intr_hl) != 0) {
+ bus_release_resource(dev, SYS_RES_IRQ, rid,
+ sc->sc_irq_res);
+ bus_release_resource(dev, SYS_RES_MEMORY, rid,
+ sc->sc_mem_res);
+ device_printf(dev, "Unable to setup the irq handler.\n");
+ return (ENXIO);
+ }
+
+ LCD_LOCK_INIT(sc);
+
+ /* Init backlight interface */
+ ctx = device_get_sysctl_ctx(sc->sc_dev);
+ tree = device_get_sysctl_tree(sc->sc_dev);
+ sc->sc_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "backlight", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0,
+ am335x_lcd_sysctl_backlight, "I", "LCD backlight");
+ sc->sc_backlight = 0;
+ /* Check if eCAS interface is available at this point */
+ if (am335x_pwm_config_ecap(PWM_UNIT,
+ PWM_PERIOD, PWM_PERIOD) == 0)
+ sc->sc_backlight = 100;
+
+ if (panel_node != 0)
+ am335x_lcd_configure(sc);
+ else
+ sc->sc_hdmi_evh = EVENTHANDLER_REGISTER(hdmi_event,
+ am335x_lcd_hdmi_event, sc, EVENTHANDLER_PRI_ANY);
+
+ return (0);
+}
+
+static int
+am335x_lcd_detach(device_t dev)
+{
+ /* Do not let unload driver */
+ return (EBUSY);
+}
+
+static struct fb_info *
+am335x_lcd_fb_getinfo(device_t dev)
+{
+ struct am335x_lcd_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ return (&sc->sc_fb_info);
+}
+
+static device_method_t am335x_lcd_methods[] = {
+ DEVMETHOD(device_probe, am335x_lcd_probe),
+ DEVMETHOD(device_attach, am335x_lcd_attach),
+ DEVMETHOD(device_detach, am335x_lcd_detach),
+
+ /* Framebuffer service methods */
+ DEVMETHOD(fb_getinfo, am335x_lcd_fb_getinfo),
+
+ DEVMETHOD_END
+};
+
+static driver_t am335x_lcd_driver = {
+ "fb",
+ am335x_lcd_methods,
+ sizeof(struct am335x_lcd_softc),
+};
+
+static devclass_t am335x_lcd_devclass;
+
+DRIVER_MODULE(am335x_lcd, simplebus, am335x_lcd_driver, am335x_lcd_devclass, 0, 0);
+MODULE_VERSION(am335x_lcd, 1);
+MODULE_DEPEND(am335x_lcd, simplebus, 1, 1, 1);
+MODULE_DEPEND(am335x_lcd, ti_sysc, 1, 1, 1);
diff --git a/sys/arm/ti/am335x/am335x_lcd.h b/sys/arm/ti/am335x/am335x_lcd.h
new file mode 100644
index 000000000000..f230a930e42d
--- /dev/null
+++ b/sys/arm/ti/am335x/am335x_lcd.h
@@ -0,0 +1,60 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef __AM335X_LCD_H__
+#define __AM335X_LCD_H__
+
+struct panel_info {
+ /* Timing part */
+ uint32_t panel_width;
+ uint32_t panel_height;
+ uint32_t panel_hfp;
+ uint32_t panel_hbp;
+ uint32_t panel_hsw;
+ uint32_t panel_vfp;
+ uint32_t panel_vbp;
+ uint32_t panel_vsw;
+ uint32_t hsync_active;
+ uint32_t vsync_active;
+ uint32_t panel_pxl_clk;
+
+ uint32_t ac_bias;
+ uint32_t ac_bias_intrpt;
+ uint32_t dma_burst_sz;
+ uint32_t bpp;
+ uint32_t fdd;
+ uint32_t sync_edge;
+ uint32_t sync_ctrl;
+ uint32_t pixelclk_active;
+};
+
+int am335x_lcd_syscons_setup(vm_offset_t vaddr, vm_paddr_t paddr,
+ struct panel_info *panel);
+
+#endif /* __AM335X_LCD_H__ */
diff --git a/sys/arm/ti/am335x/am335x_lcd_syscons.c b/sys/arm/ti/am335x/am335x_lcd_syscons.c
new file mode 100644
index 000000000000..9efd872f4ee0
--- /dev/null
+++ b/sys/arm/ti/am335x/am335x_lcd_syscons.c
@@ -0,0 +1,771 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/fbio.h>
+#include <sys/consio.h>
+
+#include <sys/kdb.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/intr.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/fb/fbreg.h>
+#include <dev/syscons/syscons.h>
+
+#include "am335x_lcd.h"
+
+struct video_adapter_softc {
+ /* Videoadpater part */
+ video_adapter_t va;
+ int console;
+
+ intptr_t fb_addr;
+ intptr_t fb_paddr;
+ unsigned int fb_size;
+
+ unsigned int height;
+ unsigned int width;
+ unsigned int depth;
+ unsigned int stride;
+
+ unsigned int xmargin;
+ unsigned int ymargin;
+
+ unsigned char *font;
+ int initialized;
+};
+
+struct argb {
+ uint8_t a;
+ uint8_t r;
+ uint8_t g;
+ uint8_t b;
+};
+
+static struct argb am335x_syscons_palette[16] = {
+ {0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0xaa},
+ {0x00, 0x00, 0xaa, 0x00},
+ {0x00, 0x00, 0xaa, 0xaa},
+ {0x00, 0xaa, 0x00, 0x00},
+ {0x00, 0xaa, 0x00, 0xaa},
+ {0x00, 0xaa, 0x55, 0x00},
+ {0x00, 0xaa, 0xaa, 0xaa},
+ {0x00, 0x55, 0x55, 0x55},
+ {0x00, 0x55, 0x55, 0xff},
+ {0x00, 0x55, 0xff, 0x55},
+ {0x00, 0x55, 0xff, 0xff},
+ {0x00, 0xff, 0x55, 0x55},
+ {0x00, 0xff, 0x55, 0xff},
+ {0x00, 0xff, 0xff, 0x55},
+ {0x00, 0xff, 0xff, 0xff}
+};
+
+/* mouse pointer from dev/syscons/scgfbrndr.c */
+static u_char mouse_pointer[16] = {
+ 0x00, 0x40, 0x60, 0x70, 0x78, 0x7c, 0x7e, 0x68,
+ 0x0c, 0x0c, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00
+};
+
+#define AM335X_FONT_HEIGHT 16
+
+#define FB_WIDTH 640
+#define FB_HEIGHT 480
+#define FB_DEPTH 24
+
+static struct video_adapter_softc va_softc;
+
+static int am335x_syscons_configure(int flags);
+
+/*
+ * Video driver routines and glue.
+ */
+static vi_probe_t am335x_syscons_probe;
+static vi_init_t am335x_syscons_init;
+static vi_get_info_t am335x_syscons_get_info;
+static vi_query_mode_t am335x_syscons_query_mode;
+static vi_set_mode_t am335x_syscons_set_mode;
+static vi_save_font_t am335x_syscons_save_font;
+static vi_load_font_t am335x_syscons_load_font;
+static vi_show_font_t am335x_syscons_show_font;
+static vi_save_palette_t am335x_syscons_save_palette;
+static vi_load_palette_t am335x_syscons_load_palette;
+static vi_set_border_t am335x_syscons_set_border;
+static vi_save_state_t am335x_syscons_save_state;
+static vi_load_state_t am335x_syscons_load_state;
+static vi_set_win_org_t am335x_syscons_set_win_org;
+static vi_read_hw_cursor_t am335x_syscons_read_hw_cursor;
+static vi_set_hw_cursor_t am335x_syscons_set_hw_cursor;
+static vi_set_hw_cursor_shape_t am335x_syscons_set_hw_cursor_shape;
+static vi_blank_display_t am335x_syscons_blank_display;
+static vi_mmap_t am335x_syscons_mmap;
+static vi_ioctl_t am335x_syscons_ioctl;
+static vi_clear_t am335x_syscons_clear;
+static vi_fill_rect_t am335x_syscons_fill_rect;
+static vi_bitblt_t am335x_syscons_bitblt;
+static vi_diag_t am335x_syscons_diag;
+static vi_save_cursor_palette_t am335x_syscons_save_cursor_palette;
+static vi_load_cursor_palette_t am335x_syscons_load_cursor_palette;
+static vi_copy_t am335x_syscons_copy;
+static vi_putp_t am335x_syscons_putp;
+static vi_putc_t am335x_syscons_putc;
+static vi_puts_t am335x_syscons_puts;
+static vi_putm_t am335x_syscons_putm;
+
+static video_switch_t am335x_sysconsvidsw = {
+ .probe = am335x_syscons_probe,
+ .init = am335x_syscons_init,
+ .get_info = am335x_syscons_get_info,
+ .query_mode = am335x_syscons_query_mode,
+ .set_mode = am335x_syscons_set_mode,
+ .save_font = am335x_syscons_save_font,
+ .load_font = am335x_syscons_load_font,
+ .show_font = am335x_syscons_show_font,
+ .save_palette = am335x_syscons_save_palette,
+ .load_palette = am335x_syscons_load_palette,
+ .set_border = am335x_syscons_set_border,
+ .save_state = am335x_syscons_save_state,
+ .load_state = am335x_syscons_load_state,
+ .set_win_org = am335x_syscons_set_win_org,
+ .read_hw_cursor = am335x_syscons_read_hw_cursor,
+ .set_hw_cursor = am335x_syscons_set_hw_cursor,
+ .set_hw_cursor_shape = am335x_syscons_set_hw_cursor_shape,
+ .blank_display = am335x_syscons_blank_display,
+ .mmap = am335x_syscons_mmap,
+ .ioctl = am335x_syscons_ioctl,
+ .clear = am335x_syscons_clear,
+ .fill_rect = am335x_syscons_fill_rect,
+ .bitblt = am335x_syscons_bitblt,
+ .diag = am335x_syscons_diag,
+ .save_cursor_palette = am335x_syscons_save_cursor_palette,
+ .load_cursor_palette = am335x_syscons_load_cursor_palette,
+ .copy = am335x_syscons_copy,
+ .putp = am335x_syscons_putp,
+ .putc = am335x_syscons_putc,
+ .puts = am335x_syscons_puts,
+ .putm = am335x_syscons_putm,
+};
+
+VIDEO_DRIVER(am335x_syscons, am335x_sysconsvidsw, am335x_syscons_configure);
+
+static vr_init_t am335x_rend_init;
+static vr_clear_t am335x_rend_clear;
+static vr_draw_border_t am335x_rend_draw_border;
+static vr_draw_t am335x_rend_draw;
+static vr_set_cursor_t am335x_rend_set_cursor;
+static vr_draw_cursor_t am335x_rend_draw_cursor;
+static vr_blink_cursor_t am335x_rend_blink_cursor;
+static vr_set_mouse_t am335x_rend_set_mouse;
+static vr_draw_mouse_t am335x_rend_draw_mouse;
+
+/*
+ * We use our own renderer; this is because we must emulate a hardware
+ * cursor.
+ */
+static sc_rndr_sw_t am335x_rend = {
+ am335x_rend_init,
+ am335x_rend_clear,
+ am335x_rend_draw_border,
+ am335x_rend_draw,
+ am335x_rend_set_cursor,
+ am335x_rend_draw_cursor,
+ am335x_rend_blink_cursor,
+ am335x_rend_set_mouse,
+ am335x_rend_draw_mouse
+};
+
+RENDERER(am335x_syscons, 0, am335x_rend, gfb_set);
+RENDERER_MODULE(am335x_syscons, gfb_set);
+
+static void
+am335x_rend_init(scr_stat* scp)
+{
+}
+
+static void
+am335x_rend_clear(scr_stat* scp, int c, int attr)
+{
+}
+
+static void
+am335x_rend_draw_border(scr_stat* scp, int color)
+{
+}
+
+static void
+am335x_rend_draw(scr_stat* scp, int from, int count, int flip)
+{
+ video_adapter_t* adp = scp->sc->adp;
+ int i, c, a;
+
+ if (!flip) {
+ /* Normal printing */
+ vidd_puts(adp, from, (uint16_t*)sc_vtb_pointer(&scp->vtb, from), count);
+ } else {
+ /* This is for selections and such: invert the color attribute */
+ for (i = count; i-- > 0; ++from) {
+ c = sc_vtb_getc(&scp->vtb, from);
+ a = sc_vtb_geta(&scp->vtb, from) >> 8;
+ vidd_putc(adp, from, c, (a >> 4) | ((a & 0xf) << 4));
+ }
+ }
+}
+
+static void
+am335x_rend_set_cursor(scr_stat* scp, int base, int height, int blink)
+{
+}
+
+static void
+am335x_rend_draw_cursor(scr_stat* scp, int off, int blink, int on, int flip)
+{
+ video_adapter_t* adp = scp->sc->adp;
+ struct video_adapter_softc *sc;
+ int row, col;
+ uint8_t *addr;
+ int i, j, bytes;
+
+ sc = (struct video_adapter_softc *)adp;
+
+ if (scp->curs_attr.height <= 0)
+ return;
+
+ if (sc->fb_addr == 0)
+ return;
+
+ if (off >= adp->va_info.vi_width * adp->va_info.vi_height)
+ return;
+
+ /* calculate the coordinates in the video buffer */
+ row = (off / adp->va_info.vi_width) * adp->va_info.vi_cheight;
+ col = (off % adp->va_info.vi_width) * adp->va_info.vi_cwidth;
+
+ addr = (uint8_t *)sc->fb_addr
+ + (row + sc->ymargin)*(sc->stride)
+ + (sc->depth/8) * (col + sc->xmargin);
+
+ bytes = sc->depth/8;
+
+ /* our cursor consists of simply inverting the char under it */
+ for (i = 0; i < adp->va_info.vi_cheight; i++) {
+ for (j = 0; j < adp->va_info.vi_cwidth; j++) {
+ switch (sc->depth) {
+ case 32:
+ case 24:
+ addr[bytes*j + 2] ^= 0xff;
+ /* FALLTHROUGH */
+ case 16:
+ addr[bytes*j + 1] ^= 0xff;
+ addr[bytes*j] ^= 0xff;
+ break;
+ default:
+ break;
+ }
+ }
+
+ addr += sc->stride;
+ }
+}
+
+static void
+am335x_rend_blink_cursor(scr_stat* scp, int at, int flip)
+{
+}
+
+static void
+am335x_rend_set_mouse(scr_stat* scp)
+{
+}
+
+static void
+am335x_rend_draw_mouse(scr_stat* scp, int x, int y, int on)
+{
+ vidd_putm(scp->sc->adp, x, y, mouse_pointer, 0xffffffff, 16, 8);
+}
+
+static uint16_t am335x_syscons_static_window[ROW*COL];
+extern u_char dflt_font_16[];
+
+/*
+ * Update videoadapter settings after changing resolution
+ */
+static void
+am335x_syscons_update_margins(video_adapter_t *adp)
+{
+ struct video_adapter_softc *sc;
+ video_info_t *vi;
+
+ sc = (struct video_adapter_softc *)adp;
+ vi = &adp->va_info;
+
+ sc->xmargin = (sc->width - (vi->vi_width * vi->vi_cwidth)) / 2;
+ sc->ymargin = (sc->height - (vi->vi_height * vi->vi_cheight))/2;
+}
+
+static phandle_t
+am335x_syscons_find_panel_node(phandle_t start)
+{
+ phandle_t child;
+ phandle_t result;
+
+ for (child = OF_child(start); child != 0; child = OF_peer(child)) {
+ if (ofw_bus_node_is_compatible(child, "ti,am335x-lcd"))
+ return (child);
+ if ((result = am335x_syscons_find_panel_node(child)))
+ return (result);
+ }
+
+ return (0);
+}
+
+static int
+am335x_syscons_configure(int flags)
+{
+ struct video_adapter_softc *va_sc;
+
+ va_sc = &va_softc;
+ phandle_t display, root;
+ pcell_t cell;
+
+ if (va_sc->initialized)
+ return (0);
+
+ va_sc->width = 0;
+ va_sc->height = 0;
+
+ /*
+ * It seems there is no way to let syscons framework know
+ * that framebuffer resolution has changed. So just try
+ * to fetch data from FDT and go with defaults if failed
+ */
+ root = OF_finddevice("/");
+ if ((root != -1) &&
+ (display = am335x_syscons_find_panel_node(root))) {
+ if ((OF_getencprop(display, "panel_width", &cell,
+ sizeof(cell))) > 0)
+ va_sc->width = cell;
+
+ if ((OF_getencprop(display, "panel_height", &cell,
+ sizeof(cell))) > 0)
+ va_sc->height = cell;
+ }
+
+ if (va_sc->width == 0)
+ va_sc->width = FB_WIDTH;
+ if (va_sc->height == 0)
+ va_sc->height = FB_HEIGHT;
+
+ am335x_syscons_init(0, &va_sc->va, 0);
+
+ va_sc->initialized = 1;
+
+ return (0);
+}
+
+static int
+am335x_syscons_probe(int unit, video_adapter_t **adp, void *arg, int flags)
+{
+
+ return (0);
+}
+
+static int
+am335x_syscons_init(int unit, video_adapter_t *adp, int flags)
+{
+ struct video_adapter_softc *sc;
+ video_info_t *vi;
+
+ sc = (struct video_adapter_softc *)adp;
+ vi = &adp->va_info;
+
+ vid_init_struct(adp, "am335x_syscons", -1, unit);
+
+ sc->font = dflt_font_16;
+ vi->vi_cheight = AM335X_FONT_HEIGHT;
+ vi->vi_cwidth = 8;
+
+ vi->vi_width = sc->width/8;
+ vi->vi_height = sc->height/vi->vi_cheight;
+
+ /*
+ * Clamp width/height to syscons maximums
+ */
+ if (vi->vi_width > COL)
+ vi->vi_width = COL;
+ if (vi->vi_height > ROW)
+ vi->vi_height = ROW;
+
+ sc->xmargin = (sc->width - (vi->vi_width * vi->vi_cwidth)) / 2;
+ sc->ymargin = (sc->height - (vi->vi_height * vi->vi_cheight))/2;
+
+ adp->va_window = (vm_offset_t) am335x_syscons_static_window;
+ adp->va_flags |= V_ADP_FONT /* | V_ADP_COLOR | V_ADP_MODECHANGE */;
+
+ vid_register(&sc->va);
+
+ return (0);
+}
+
+static int
+am335x_syscons_get_info(video_adapter_t *adp, int mode, video_info_t *info)
+{
+ bcopy(&adp->va_info, info, sizeof(*info));
+ return (0);
+}
+
+static int
+am335x_syscons_query_mode(video_adapter_t *adp, video_info_t *info)
+{
+ return (0);
+}
+
+static int
+am335x_syscons_set_mode(video_adapter_t *adp, int mode)
+{
+ return (0);
+}
+
+static int
+am335x_syscons_save_font(video_adapter_t *adp, int page, int size, int width,
+ u_char *data, int c, int count)
+{
+ return (0);
+}
+
+static int
+am335x_syscons_load_font(video_adapter_t *adp, int page, int size, int width,
+ u_char *data, int c, int count)
+{
+ struct video_adapter_softc *sc = (struct video_adapter_softc *)adp;
+
+ sc->font = data;
+
+ return (0);
+}
+
+static int
+am335x_syscons_show_font(video_adapter_t *adp, int page)
+{
+ return (0);
+}
+
+static int
+am335x_syscons_save_palette(video_adapter_t *adp, u_char *palette)
+{
+ return (0);
+}
+
+static int
+am335x_syscons_load_palette(video_adapter_t *adp, u_char *palette)
+{
+ return (0);
+}
+
+static int
+am335x_syscons_set_border(video_adapter_t *adp, int border)
+{
+ return (am335x_syscons_blank_display(adp, border));
+}
+
+static int
+am335x_syscons_save_state(video_adapter_t *adp, void *p, size_t size)
+{
+ return (0);
+}
+
+static int
+am335x_syscons_load_state(video_adapter_t *adp, void *p)
+{
+ return (0);
+}
+
+static int
+am335x_syscons_set_win_org(video_adapter_t *adp, off_t offset)
+{
+ return (0);
+}
+
+static int
+am335x_syscons_read_hw_cursor(video_adapter_t *adp, int *col, int *row)
+{
+ *col = *row = 0;
+
+ return (0);
+}
+
+static int
+am335x_syscons_set_hw_cursor(video_adapter_t *adp, int col, int row)
+{
+ return (0);
+}
+
+static int
+am335x_syscons_set_hw_cursor_shape(video_adapter_t *adp, int base, int height,
+ int celsize, int blink)
+{
+ return (0);
+}
+
+static int
+am335x_syscons_blank_display(video_adapter_t *adp, int mode)
+{
+
+ struct video_adapter_softc *sc;
+
+ sc = (struct video_adapter_softc *)adp;
+ if (sc && sc->fb_addr)
+ memset((void*)sc->fb_addr, 0, sc->fb_size);
+
+ return (0);
+}
+
+static int
+am335x_syscons_mmap(video_adapter_t *adp, vm_ooffset_t offset, vm_paddr_t *paddr,
+ int prot, vm_memattr_t *memattr)
+{
+ struct video_adapter_softc *sc;
+
+ sc = (struct video_adapter_softc *)adp;
+
+ /*
+ * This might be a legacy VGA mem request: if so, just point it at the
+ * framebuffer, since it shouldn't be touched
+ */
+ if (offset < sc->stride*sc->height) {
+ *paddr = sc->fb_paddr + offset;
+ return (0);
+ }
+
+ return (EINVAL);
+}
+
+static int
+am335x_syscons_ioctl(video_adapter_t *adp, u_long cmd, caddr_t data)
+{
+ struct video_adapter_softc *sc;
+ struct fbtype *fb;
+
+ sc = (struct video_adapter_softc *)adp;
+
+ switch (cmd) {
+ case FBIOGTYPE:
+ fb = (struct fbtype *)data;
+ fb->fb_type = FBTYPE_PCIMISC;
+ fb->fb_height = sc->height;
+ fb->fb_width = sc->width;
+ fb->fb_depth = sc->depth;
+ if (sc->depth <= 1 || sc->depth > 8)
+ fb->fb_cmsize = 0;
+ else
+ fb->fb_cmsize = 1 << sc->depth;
+ fb->fb_size = sc->fb_size;
+ break;
+ default:
+ return (fb_commonioctl(adp, cmd, data));
+ }
+
+ return (0);
+}
+
+static int
+am335x_syscons_clear(video_adapter_t *adp)
+{
+
+ return (am335x_syscons_blank_display(adp, 0));
+}
+
+static int
+am335x_syscons_fill_rect(video_adapter_t *adp, int val, int x, int y, int cx, int cy)
+{
+
+ return (0);
+}
+
+static int
+am335x_syscons_bitblt(video_adapter_t *adp, ...)
+{
+
+ return (0);
+}
+
+static int
+am335x_syscons_diag(video_adapter_t *adp, int level)
+{
+
+ return (0);
+}
+
+static int
+am335x_syscons_save_cursor_palette(video_adapter_t *adp, u_char *palette)
+{
+
+ return (0);
+}
+
+static int
+am335x_syscons_load_cursor_palette(video_adapter_t *adp, u_char *palette)
+{
+
+ return (0);
+}
+
+static int
+am335x_syscons_copy(video_adapter_t *adp, vm_offset_t src, vm_offset_t dst, int n)
+{
+
+ return (0);
+}
+
+static int
+am335x_syscons_putp(video_adapter_t *adp, vm_offset_t off, uint32_t p, uint32_t a,
+ int size, int bpp, int bit_ltor, int byte_ltor)
+{
+
+ return (0);
+}
+
+static int
+am335x_syscons_putc(video_adapter_t *adp, vm_offset_t off, uint8_t c, uint8_t a)
+{
+ struct video_adapter_softc *sc;
+ int row;
+ int col;
+ int i, j, k;
+ uint8_t *addr;
+ u_char *p;
+ uint8_t fg, bg, color;
+ uint16_t rgb;
+
+ sc = (struct video_adapter_softc *)adp;
+
+ if (sc->fb_addr == 0)
+ return (0);
+
+ row = (off / adp->va_info.vi_width) * adp->va_info.vi_cheight;
+ col = (off % adp->va_info.vi_width) * adp->va_info.vi_cwidth;
+ p = sc->font + c*AM335X_FONT_HEIGHT;
+ addr = (uint8_t *)sc->fb_addr
+ + (row + sc->ymargin)*(sc->stride)
+ + (sc->depth/8) * (col + sc->xmargin);
+
+ fg = a & 0xf ;
+ bg = (a >> 4) & 0xf;
+
+ for (i = 0; i < AM335X_FONT_HEIGHT; i++) {
+ for (j = 0, k = 7; j < 8; j++, k--) {
+ if ((p[i] & (1 << k)) == 0)
+ color = bg;
+ else
+ color = fg;
+
+ switch (sc->depth) {
+ case 32:
+ addr[4*j+0] = am335x_syscons_palette[color].r;
+ addr[4*j+1] = am335x_syscons_palette[color].g;
+ addr[4*j+2] = am335x_syscons_palette[color].b;
+ addr[4*j+3] = am335x_syscons_palette[color].a;
+ break;
+ case 24:
+ addr[3*j] = am335x_syscons_palette[color].r;
+ addr[3*j+1] = am335x_syscons_palette[color].g;
+ addr[3*j+2] = am335x_syscons_palette[color].b;
+ break;
+ case 16:
+ rgb = (am335x_syscons_palette[color].r >> 3) << 11;
+ rgb |= (am335x_syscons_palette[color].g >> 2) << 5;
+ rgb |= (am335x_syscons_palette[color].b >> 3);
+ addr[2*j] = rgb & 0xff;
+ addr[2*j + 1] = (rgb >> 8) & 0xff;
+ default:
+ /* Not supported yet */
+ break;
+ }
+ }
+
+ addr += (sc->stride);
+ }
+
+ return (0);
+}
+
+static int
+am335x_syscons_puts(video_adapter_t *adp, vm_offset_t off, u_int16_t *s, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ am335x_syscons_putc(adp, off + i, s[i] & 0xff, (s[i] & 0xff00) >> 8);
+
+ return (0);
+}
+
+static int
+am335x_syscons_putm(video_adapter_t *adp, int x, int y, uint8_t *pixel_image,
+ uint32_t pixel_mask, int size, int width)
+{
+
+ return (0);
+}
+
+/* Initialization function */
+int am335x_lcd_syscons_setup(vm_offset_t vaddr, vm_paddr_t paddr,
+ struct panel_info *panel)
+{
+ struct video_adapter_softc *va_sc = &va_softc;
+
+ va_sc->fb_addr = vaddr;
+ va_sc->fb_paddr = paddr;
+ va_sc->depth = panel->bpp;
+ va_sc->stride = panel->bpp*panel->panel_width/8;
+
+ va_sc->width = panel->panel_width;
+ va_sc->height = panel->panel_height;
+ va_sc->fb_size = va_sc->width * va_sc->height
+ * va_sc->depth/8;
+ am335x_syscons_update_margins(&va_sc->va);
+
+ return (0);
+}
diff --git a/sys/arm/ti/am335x/am335x_musb.c b/sys/arm/ti/am335x/am335x_musb.c
new file mode 100644
index 000000000000..d868e8a64f8b
--- /dev/null
+++ b/sys/arm/ti/am335x/am335x_musb.c
@@ -0,0 +1,462 @@
+/*-
+ * Copyright (c) 2013 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_util.h>
+
+#define USB_DEBUG_VAR usbssdebug
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/controller/musb_otg.h>
+#include <dev/usb/usb_debug.h>
+
+#include <sys/rman.h>
+
+#include <arm/ti/am335x/am335x_scm.h>
+#include <arm/ti/ti_sysc.h>
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/syscon/syscon.h>
+#include "syscon_if.h"
+
+#define USBCTRL_REV 0x00
+#define USBCTRL_CTRL 0x14
+#define USBCTRL_STAT 0x18
+#define USBCTRL_IRQ_STAT0 0x30
+#define IRQ_STAT0_RXSHIFT 16
+#define IRQ_STAT0_TXSHIFT 0
+#define USBCTRL_IRQ_STAT1 0x34
+#define IRQ_STAT1_DRVVBUS (1 << 8)
+#define USBCTRL_INTEN_SET0 0x38
+#define USBCTRL_INTEN_SET1 0x3C
+#define USBCTRL_INTEN_USB_ALL 0x1ff
+#define USBCTRL_INTEN_USB_SOF (1 << 3)
+#define USBCTRL_INTEN_CLR0 0x40
+#define USBCTRL_INTEN_CLR1 0x44
+#define USBCTRL_UTMI 0xE0
+#define USBCTRL_UTMI_FSDATAEXT (1 << 1)
+#define USBCTRL_MODE 0xE8
+#define USBCTRL_MODE_IDDIG (1 << 8)
+#define USBCTRL_MODE_IDDIGMUX (1 << 7)
+
+/* USBSS resource + 2 MUSB ports */
+
+#define RES_USBCORE 0
+#define RES_USBCTRL 1
+
+#define USB_WRITE4(sc, idx, reg, val) do { \
+ bus_write_4((sc)->sc_mem_res[idx], (reg), (val)); \
+} while (0)
+
+#define USB_READ4(sc, idx, reg) bus_read_4((sc)->sc_mem_res[idx], (reg))
+
+#define USBCTRL_WRITE4(sc, reg, val) \
+ USB_WRITE4((sc), RES_USBCTRL, (reg), (val))
+#define USBCTRL_READ4(sc, reg) \
+ USB_READ4((sc), RES_USBCTRL, (reg))
+
+static struct resource_spec am335x_musbotg_mem_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_MEMORY, 1, RF_ACTIVE },
+ { -1, 0, 0 }
+};
+
+#ifdef USB_DEBUG
+static int usbssdebug = 0;
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, am335x_usbss,
+ CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
+ "AM335x USBSS");
+SYSCTL_INT(_hw_usb_am335x_usbss, OID_AUTO, debug, CTLFLAG_RW,
+ &usbssdebug, 0, "Debug level");
+#endif
+
+static device_probe_t musbotg_probe;
+static device_attach_t musbotg_attach;
+static device_detach_t musbotg_detach;
+
+struct musbotg_super_softc {
+ struct musbotg_softc sc_otg;
+ struct resource *sc_mem_res[2];
+ int sc_irq_rid;
+ struct syscon *syscon;
+};
+
+static void
+musbotg_vbus_poll(struct musbotg_super_softc *sc)
+{
+ uint32_t stat;
+
+ if (sc->sc_otg.sc_mode == MUSB2_DEVICE_MODE)
+ musbotg_vbus_interrupt(&sc->sc_otg, 1);
+ else {
+ stat = USBCTRL_READ4(sc, USBCTRL_STAT);
+ musbotg_vbus_interrupt(&sc->sc_otg, stat & 1);
+ }
+}
+
+/*
+ * Arg to musbotg_clocks_on and musbot_clocks_off is
+ * a uint32_t * pointing to the SCM register offset.
+ */
+static uint32_t USB_CTRL[] = {SCM_USB_CTRL0, SCM_USB_CTRL1};
+
+static void
+musbotg_clocks_on(void *arg)
+{
+ struct musbotg_softc *sc;
+ struct musbotg_super_softc *ssc;
+ uint32_t reg;
+
+ sc = arg;
+ ssc = sc->sc_platform_data;
+
+ reg = SYSCON_READ_4(ssc->syscon, USB_CTRL[sc->sc_id]);
+ reg &= ~3; /* Enable power */
+ reg |= 1 << 19; /* VBUS detect enable */
+ reg |= 1 << 20; /* Session end enable */
+
+ SYSCON_WRITE_4(ssc->syscon, USB_CTRL[sc->sc_id], reg);
+}
+
+static void
+musbotg_clocks_off(void *arg)
+{
+ struct musbotg_softc *sc;
+ struct musbotg_super_softc *ssc;
+ uint32_t reg;
+
+ sc = arg;
+ ssc = sc->sc_platform_data;
+
+ /* Disable power to PHY */
+ reg = SYSCON_READ_4(ssc->syscon, USB_CTRL[sc->sc_id]);
+ SYSCON_WRITE_4(ssc->syscon, USB_CTRL[sc->sc_id], reg | 3);
+}
+
+static void
+musbotg_ep_int_set(struct musbotg_softc *sc, int ep, int on)
+{
+ struct musbotg_super_softc *ssc = sc->sc_platform_data;
+ uint32_t epmask;
+
+ epmask = ((1 << ep) << IRQ_STAT0_RXSHIFT);
+ epmask |= ((1 << ep) << IRQ_STAT0_TXSHIFT);
+ if (on)
+ USBCTRL_WRITE4(ssc, USBCTRL_INTEN_SET0, epmask);
+ else
+ USBCTRL_WRITE4(ssc, USBCTRL_INTEN_CLR0, epmask);
+}
+
+static void
+musbotg_wrapper_interrupt(void *arg)
+{
+ struct musbotg_softc *sc = arg;
+ struct musbotg_super_softc *ssc = sc->sc_platform_data;
+ uint32_t stat, stat0, stat1;
+
+ stat = USBCTRL_READ4(ssc, USBCTRL_STAT);
+ stat0 = USBCTRL_READ4(ssc, USBCTRL_IRQ_STAT0);
+ stat1 = USBCTRL_READ4(ssc, USBCTRL_IRQ_STAT1);
+ if (stat0)
+ USBCTRL_WRITE4(ssc, USBCTRL_IRQ_STAT0, stat0);
+ if (stat1)
+ USBCTRL_WRITE4(ssc, USBCTRL_IRQ_STAT1, stat1);
+
+ DPRINTFN(4, "port%d: stat0=%08x stat1=%08x, stat=%08x\n",
+ sc->sc_id, stat0, stat1, stat);
+
+ if (stat1 & IRQ_STAT1_DRVVBUS)
+ musbotg_vbus_interrupt(sc, stat & 1);
+
+ musbotg_interrupt(arg, ((stat0 >> 16) & 0xffff),
+ stat0 & 0xffff, stat1 & 0xff);
+}
+
+static int
+musbotg_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "ti,musb-am33xx"))
+ return (ENXIO);
+
+ device_set_desc(dev, "TI AM33xx integrated USB OTG controller");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+musbotg_attach(device_t dev)
+{
+ struct musbotg_super_softc *sc = device_get_softc(dev);
+ char mode[16];
+ int err;
+ uint32_t reg;
+ phandle_t opp_table;
+ clk_t clk_usbotg_fck;
+
+ sc->sc_otg.sc_id = device_get_unit(dev);
+
+ /* FIXME: The devicetree needs to be updated to get a handle to the gate
+ * usbotg_fck@47c. see TRM 8.1.12.2 CM_WKUP CM_CLKDCOLDO_DPLL_PER.
+ */
+ err = clk_get_by_name(dev, "usbotg_fck@47c", &clk_usbotg_fck);
+ if (err) {
+ device_printf(dev, "Can not find usbotg_fck@47c\n");
+ return (ENXIO);
+ }
+
+ err = clk_enable(clk_usbotg_fck);
+ if (err) {
+ device_printf(dev, "Can not enable usbotg_fck@47c\n");
+ return (ENXIO);
+ }
+
+ /* FIXME: For now; Go and kidnap syscon from opp-table */
+ opp_table = OF_finddevice("/opp-table");
+ if (opp_table == -1) {
+ device_printf(dev, "Cant find /opp-table\n");
+ return (ENXIO);
+ }
+ if (!OF_hasprop(opp_table, "syscon")) {
+ device_printf(dev, "/opp-table missing syscon property\n");
+ return (ENXIO);
+ }
+ err = syscon_get_by_ofw_property(dev, opp_table, "syscon", &sc->syscon);
+ if (err) {
+ device_printf(dev, "Failed to get syscon\n");
+ return (ENXIO);
+ }
+
+ /* Request the memory resources */
+ err = bus_alloc_resources(dev, am335x_musbotg_mem_spec,
+ sc->sc_mem_res);
+ if (err) {
+ device_printf(dev,
+ "Error: could not allocate mem resources\n");
+ return (ENXIO);
+ }
+
+ /* Request the IRQ resources */
+ sc->sc_otg.sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
+ &sc->sc_irq_rid, RF_ACTIVE);
+ if (sc->sc_otg.sc_irq_res == NULL) {
+ device_printf(dev,
+ "Error: could not allocate irq resources\n");
+ return (ENXIO);
+ }
+
+ /* setup MUSB OTG USB controller interface softc */
+ sc->sc_otg.sc_clocks_on = &musbotg_clocks_on;
+ sc->sc_otg.sc_clocks_off = &musbotg_clocks_off;
+ sc->sc_otg.sc_clocks_arg = &sc->sc_otg;
+
+ sc->sc_otg.sc_ep_int_set = musbotg_ep_int_set;
+
+ /* initialise some bus fields */
+ sc->sc_otg.sc_bus.parent = dev;
+ sc->sc_otg.sc_bus.devices = sc->sc_otg.sc_devices;
+ sc->sc_otg.sc_bus.devices_max = MUSB2_MAX_DEVICES;
+ sc->sc_otg.sc_bus.dma_bits = 32;
+
+ /* get all DMA memory */
+ if (usb_bus_mem_alloc_all(&sc->sc_otg.sc_bus,
+ USB_GET_DMA_TAG(dev), NULL)) {
+ device_printf(dev,
+ "Failed allocate bus mem for musb\n");
+ return (ENOMEM);
+ }
+ sc->sc_otg.sc_io_res = sc->sc_mem_res[RES_USBCORE];
+ sc->sc_otg.sc_io_tag =
+ rman_get_bustag(sc->sc_otg.sc_io_res);
+ sc->sc_otg.sc_io_hdl =
+ rman_get_bushandle(sc->sc_otg.sc_io_res);
+ sc->sc_otg.sc_io_size =
+ rman_get_size(sc->sc_otg.sc_io_res);
+
+ sc->sc_otg.sc_bus.bdev = device_add_child(dev, "usbus", -1);
+ if (!(sc->sc_otg.sc_bus.bdev)) {
+ device_printf(dev, "No busdev for musb\n");
+ goto error;
+ }
+ device_set_ivars(sc->sc_otg.sc_bus.bdev,
+ &sc->sc_otg.sc_bus);
+
+ err = bus_setup_intr(dev, sc->sc_otg.sc_irq_res,
+ INTR_TYPE_BIO | INTR_MPSAFE,
+ NULL, (driver_intr_t *)musbotg_wrapper_interrupt,
+ &sc->sc_otg, &sc->sc_otg.sc_intr_hdl);
+ if (err) {
+ sc->sc_otg.sc_intr_hdl = NULL;
+ device_printf(dev,
+ "Failed to setup interrupt for musb\n");
+ goto error;
+ }
+
+ sc->sc_otg.sc_platform_data = sc;
+ if (OF_getprop(ofw_bus_get_node(dev), "dr_mode", mode,
+ sizeof(mode)) > 0) {
+ if (strcasecmp(mode, "host") == 0)
+ sc->sc_otg.sc_mode = MUSB2_HOST_MODE;
+ else
+ sc->sc_otg.sc_mode = MUSB2_DEVICE_MODE;
+ } else {
+ /* Beaglebone defaults: USB0 device, USB1 HOST. */
+ if (sc->sc_otg.sc_id == 0)
+ sc->sc_otg.sc_mode = MUSB2_DEVICE_MODE;
+ else
+ sc->sc_otg.sc_mode = MUSB2_HOST_MODE;
+ }
+
+ /*
+ * software-controlled function
+ */
+
+ if (sc->sc_otg.sc_mode == MUSB2_HOST_MODE) {
+ reg = USBCTRL_READ4(sc, USBCTRL_MODE);
+ reg |= USBCTRL_MODE_IDDIGMUX;
+ reg &= ~USBCTRL_MODE_IDDIG;
+ USBCTRL_WRITE4(sc, USBCTRL_MODE, reg);
+ USBCTRL_WRITE4(sc, USBCTRL_UTMI,
+ USBCTRL_UTMI_FSDATAEXT);
+ } else {
+ reg = USBCTRL_READ4(sc, USBCTRL_MODE);
+ reg |= USBCTRL_MODE_IDDIGMUX;
+ reg |= USBCTRL_MODE_IDDIG;
+ USBCTRL_WRITE4(sc, USBCTRL_MODE, reg);
+ }
+
+ reg = USBCTRL_INTEN_USB_ALL & ~USBCTRL_INTEN_USB_SOF;
+ USBCTRL_WRITE4(sc, USBCTRL_INTEN_SET1, reg);
+ USBCTRL_WRITE4(sc, USBCTRL_INTEN_CLR0, 0xffffffff);
+
+ err = musbotg_init(&sc->sc_otg);
+ if (!err)
+ err = device_probe_and_attach(sc->sc_otg.sc_bus.bdev);
+
+ if (err)
+ goto error;
+
+ /* poll VBUS one time */
+ musbotg_vbus_poll(sc);
+
+ return (0);
+
+error:
+ musbotg_detach(dev);
+ return (ENXIO);
+}
+
+static int
+musbotg_detach(device_t dev)
+{
+ struct musbotg_super_softc *sc = device_get_softc(dev);
+ int err;
+
+ /* during module unload there are lots of children leftover */
+ device_delete_children(dev);
+
+ if (sc->sc_otg.sc_irq_res && sc->sc_otg.sc_intr_hdl) {
+ /*
+ * only call musbotg_uninit() after musbotg_init()
+ */
+ musbotg_uninit(&sc->sc_otg);
+
+ err = bus_teardown_intr(dev, sc->sc_otg.sc_irq_res,
+ sc->sc_otg.sc_intr_hdl);
+ sc->sc_otg.sc_intr_hdl = NULL;
+ }
+
+ usb_bus_mem_free_all(&sc->sc_otg.sc_bus, NULL);
+
+ /* Free resources if any */
+ if (sc->sc_mem_res[0])
+ bus_release_resources(dev, am335x_musbotg_mem_spec,
+ sc->sc_mem_res);
+
+ if (sc->sc_otg.sc_irq_res)
+ bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid,
+ sc->sc_otg.sc_irq_res);
+
+ return (0);
+}
+
+static device_method_t musbotg_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, musbotg_probe),
+ DEVMETHOD(device_attach, musbotg_attach),
+ DEVMETHOD(device_detach, musbotg_detach),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ DEVMETHOD_END
+};
+
+static driver_t musbotg_driver = {
+ .name = "musbotg",
+ .methods = musbotg_methods,
+ .size = sizeof(struct musbotg_super_softc),
+};
+
+static devclass_t musbotg_devclass;
+
+DRIVER_MODULE(musbotg, ti_sysc, musbotg_driver, musbotg_devclass, 0, 0);
+MODULE_DEPEND(musbotg, ti_sysc, 1, 1, 1);
+MODULE_DEPEND(musbotg, ti_am3359_cppi41, 1, 1, 1);
+MODULE_DEPEND(usbss, usb, 1, 1, 1);
diff --git a/sys/arm/ti/am335x/am335x_pmic.c b/sys/arm/ti/am335x/am335x_pmic.c
new file mode 100644
index 000000000000..0b0782ab72a6
--- /dev/null
+++ b/sys/arm/ti/am335x/am335x_pmic.c
@@ -0,0 +1,328 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Damjan Marion <dmarion@Freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+/*
+* TI TPS65217 PMIC companion chip for AM335x SoC sitting on I2C bus
+*/
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/eventhandler.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/clock.h>
+#include <sys/time.h>
+#include <sys/bus.h>
+#include <sys/proc.h>
+#include <sys/reboot.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+
+#include <dev/iicbus/iicbus.h>
+#include <dev/iicbus/iiconf.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/ti/am335x/am335x_rtcvar.h>
+#include <arm/ti/am335x/tps65217x.h>
+
+#include "iicbus_if.h"
+
+struct am335x_pmic_softc {
+ device_t sc_dev;
+ uint32_t sc_addr;
+ struct resource *sc_irq_res;
+ void *sc_intrhand;
+};
+
+static const char *tps65217_voreg_c[4] = {"4.10V", "4.15V", "4.20V", "4.25V"};
+
+static int am335x_pmic_bootverbose = 0;
+TUNABLE_INT("hw.am335x_pmic.bootverbose", &am335x_pmic_bootverbose);
+static char am335x_pmic_vo[6];
+TUNABLE_STR("hw.am335x_pmic.vo", am335x_pmic_vo, sizeof(am335x_pmic_vo));
+
+static void am335x_pmic_shutdown(void *, int);
+
+static int
+am335x_pmic_read(device_t dev, uint8_t addr, uint8_t *data, uint8_t size)
+{
+ return (iicdev_readfrom(dev, addr, data, size, IIC_INTRWAIT));
+}
+
+static int
+am335x_pmic_write(device_t dev, uint8_t address, uint8_t *data, uint8_t size)
+{
+ return (iicdev_writeto(dev, address, data, size, IIC_INTRWAIT));
+}
+
+static void
+am335x_pmic_intr(void *arg)
+{
+ struct am335x_pmic_softc *sc = (struct am335x_pmic_softc *)arg;
+ struct tps65217_status_reg status_reg;
+ struct tps65217_int_reg int_reg;
+ int rv;
+ char notify_buf[16];
+
+ THREAD_SLEEPING_OK();
+ rv = am335x_pmic_read(sc->sc_dev, TPS65217_INT_REG, (uint8_t *)&int_reg, 1);
+ if (rv != 0) {
+ device_printf(sc->sc_dev, "Cannot read interrupt register\n");
+ THREAD_NO_SLEEPING();
+ return;
+ }
+ rv = am335x_pmic_read(sc->sc_dev, TPS65217_STATUS_REG, (uint8_t *)&status_reg, 1);
+ if (rv != 0) {
+ device_printf(sc->sc_dev, "Cannot read status register\n");
+ THREAD_NO_SLEEPING();
+ return;
+ }
+ THREAD_NO_SLEEPING();
+
+ if (int_reg.pbi && status_reg.pb)
+ shutdown_nice(RB_POWEROFF);
+ if (int_reg.aci) {
+ snprintf(notify_buf, sizeof(notify_buf), "notify=0x%02x",
+ status_reg.acpwr);
+ devctl_notify("ACPI", "ACAD", "power", notify_buf);
+ }
+}
+
+static int
+am335x_pmic_probe(device_t dev)
+{
+ struct am335x_pmic_softc *sc;
+
+ if (!ofw_bus_is_compatible(dev, "ti,tps65217"))
+ return (ENXIO);
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+ /* Convert to 8-bit addressing */
+ sc->sc_addr = iicbus_get_addr(dev);
+
+ device_set_desc(dev, "TI TPS65217 Power Management IC");
+
+ return (0);
+}
+
+static void
+am335x_pmic_dump_chgconfig(device_t dev)
+{
+ struct tps65217_chgconfig0_reg reg0;
+ struct tps65217_chgconfig1_reg reg1;
+ struct tps65217_chgconfig2_reg reg2;
+ struct tps65217_chgconfig3_reg reg3;
+ const char *e_d[] = {"enabled", "disabled"};
+ const char *d_e[] = {"disabled", "enabled"};
+ const char *i_a[] = {"inactive", "active"};
+ const char *f_t[] = {"false", "true"};
+ const char *timer_c[] = {"4h", "5h", "6h", "8h"};
+ const char *ntc_type_c[] = {"100k", "10k"};
+ const char *vprechg_c[] = {"2.9V", "2.5V"};
+ const char *trange_c[] = {"0-45 C", "0-60 C"};
+ const char *termif_c[] = {"2.5%", "7.5%", "15%", "18%"};
+ const char *pchrgt_c[] = {"30 min", "60 min"};
+ const char *dppmth_c[] = {"3.50V", "3.75V", "4.00V", "4.25V"};
+ const char *ichrg_c[] = {"300mA", "400mA", "500mA", "700mA"};
+
+ am335x_pmic_read(dev, TPS65217_CHGCONFIG0_REG, (uint8_t *)&reg0, 1);
+ device_printf(dev, " BAT TEMP/NTC ERROR: %s\n", f_t[reg0.battemp]);
+ device_printf(dev, " Pre-charge timer time-out: %s\n", f_t[reg0.pchgtout]);
+ device_printf(dev, " Charge timer time-out: %s\n", f_t[reg0.chgtout]);
+ device_printf(dev, " Charger active: %s\n", f_t[reg0.active]);
+ device_printf(dev, " Termination current detected: %s\n", f_t[reg0.termi]);
+ device_printf(dev, " Thermal suspend: %s\n", f_t[reg0.tsusp]);
+ device_printf(dev, " DPPM active: %s\n", f_t[reg0.dppm]);
+ device_printf(dev, " Thermal regulation: %s\n", i_a[reg0.treg]);
+
+ am335x_pmic_read(dev, TPS65217_CHGCONFIG1_REG, (uint8_t *)&reg1, 1);
+ device_printf(dev, " Charger: %s\n", d_e[reg1.chg_en]);
+ device_printf(dev, " Suspend charge: %s\n", i_a[reg1.susp]);
+ device_printf(dev, " Charge termination: %s\n", e_d[reg1.term]);
+ device_printf(dev, " Charger reset: %s\n", i_a[reg1.reset]);
+ device_printf(dev, " NTC TYPE: %s\n", ntc_type_c[reg1.ntc_type]);
+ device_printf(dev, " Safety timer: %s\n", d_e[reg1.tmr_en]);
+ device_printf(dev, " Charge safety timer: %s\n", timer_c[reg1.timer]);
+
+ am335x_pmic_read(dev, TPS65217_CHGCONFIG2_REG, (uint8_t *)&reg2, 1);
+ device_printf(dev, " Charge voltage: %s\n", tps65217_voreg_c[reg2.voreg]);
+ device_printf(dev, " Pre-charge to fast charge transition voltage: %s\n",
+ vprechg_c[reg2.vprechg]);
+ device_printf(dev, " Dynamic timer function: %s\n", d_e[reg2.dyntmr]);
+
+ am335x_pmic_read(dev, TPS65217_CHGCONFIG3_REG, (uint8_t *)&reg3, 1);
+ device_printf(dev, " Temperature range for charging: %s\n", trange_c[reg3.trange]);
+ device_printf(dev, " Termination current factor: %s\n", termif_c[reg3.termif]);
+ device_printf(dev, " Pre-charge time: %s\n", pchrgt_c[reg3.pchrgt]);
+ device_printf(dev, " Power path DPPM threshold: %s\n", dppmth_c[reg3.dppmth]);
+ device_printf(dev, " Charge current: %s\n", ichrg_c[reg3.ichrg]);
+}
+
+static void
+am335x_pmic_setvo(device_t dev, uint8_t vo)
+{
+ struct tps65217_chgconfig2_reg reg2;
+
+ am335x_pmic_read(dev, TPS65217_CHGCONFIG2_REG, (uint8_t *)&reg2, 1);
+ reg2.voreg = vo;
+ am335x_pmic_write(dev, TPS65217_CHGCONFIG2_REG, (uint8_t *)&reg2, 1);
+}
+
+static void
+am335x_pmic_start(struct am335x_pmic_softc *sc)
+{
+ device_t dev;
+ struct tps65217_status_reg status_reg;
+ struct tps65217_chipid_reg chipid_reg;
+ uint8_t reg, vo;
+ char name[20];
+ char pwr[4][11] = {"Battery", "USB", "AC", "USB and AC"};
+ int rv;
+ phandle_t node;
+
+ dev = sc->sc_dev;
+ am335x_pmic_read(dev, TPS65217_CHIPID_REG, (uint8_t *)&chipid_reg, 1);
+ switch (chipid_reg.chip) {
+ case TPS65217A:
+ sprintf(name, "TPS65217A ver 1.%u", chipid_reg.rev);
+ break;
+ case TPS65217B:
+ sprintf(name, "TPS65217B ver 1.%u", chipid_reg.rev);
+ break;
+ case TPS65217C:
+ sprintf(name, "TPS65217C ver 1.%u", chipid_reg.rev);
+ break;
+ case TPS65217D:
+ sprintf(name, "TPS65217D ver 1.%u", chipid_reg.rev);
+ break;
+ default:
+ sprintf(name, "Unknown PMIC");
+ }
+
+ am335x_pmic_read(dev, TPS65217_STATUS_REG, (uint8_t *)&status_reg, 1);
+ device_printf(dev, "%s powered by %s\n", name,
+ pwr[status_reg.usbpwr | (status_reg.acpwr << 1)]);
+
+ /* Check devicetree for ti,pmic-shutdown-controller
+ * if present; PMIC will go to shutdown state on PWR_EN toggle
+ * if not present; PMIC will enter sleep state on PWR_EN toggle (default on reset)
+ */
+ node = ofw_bus_get_node(dev);
+ if (OF_hasprop(node, "ti,pmic-shutdown-controller")) {
+ status_reg.off = 1;
+ am335x_pmic_write(dev, TPS65217_STATUS_REG, (uint8_t *)&status_reg, 1);
+ }
+
+ if (am335x_pmic_vo[0] != '\0') {
+ for (vo = 0; vo < 4; vo++) {
+ if (strcmp(tps65217_voreg_c[vo], am335x_pmic_vo) == 0)
+ break;
+ }
+ if (vo == 4) {
+ device_printf(dev, "WARNING: hw.am335x_pmic.vo=\"%s\""
+ ": unsupported value\n", am335x_pmic_vo);
+ } else {
+ am335x_pmic_setvo(dev, vo);
+ }
+ }
+
+ if (bootverbose || am335x_pmic_bootverbose) {
+ am335x_pmic_dump_chgconfig(dev);
+ }
+
+ EVENTHANDLER_REGISTER(shutdown_final, am335x_pmic_shutdown, dev,
+ SHUTDOWN_PRI_LAST);
+
+ /* Unmask all interrupts and clear pending status */
+ reg = 0;
+ am335x_pmic_write(dev, TPS65217_INT_REG, &reg, 1);
+ am335x_pmic_read(dev, TPS65217_INT_REG, &reg, 1);
+
+ if (sc->sc_irq_res != NULL) {
+ rv = bus_setup_intr(dev, sc->sc_irq_res,
+ INTR_TYPE_MISC | INTR_MPSAFE, NULL, am335x_pmic_intr,
+ sc, &sc->sc_intrhand);
+ if (rv != 0)
+ device_printf(dev,
+ "Unable to setup the irq handler.\n");
+ }
+}
+
+static int
+am335x_pmic_attach(device_t dev)
+{
+ struct am335x_pmic_softc *sc;
+ int rid;
+
+ sc = device_get_softc(dev);
+
+ rid = 0;
+ sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (!sc->sc_irq_res) {
+ device_printf(dev, "cannot allocate interrupt\n");
+ /* return (ENXIO); */
+ }
+
+ am335x_pmic_start(sc);
+
+ return (0);
+}
+
+static void
+am335x_pmic_shutdown(void *xdev, int howto)
+{
+ if (!(howto & RB_POWEROFF))
+ return;
+
+ /* Toggle pmic_pwr_enable to shutdown the PMIC. */
+ am335x_rtc_pmic_pwr_toggle();
+}
+
+static device_method_t am335x_pmic_methods[] = {
+ DEVMETHOD(device_probe, am335x_pmic_probe),
+ DEVMETHOD(device_attach, am335x_pmic_attach),
+ {0, 0},
+};
+
+static driver_t am335x_pmic_driver = {
+ "am335x_pmic",
+ am335x_pmic_methods,
+ sizeof(struct am335x_pmic_softc),
+};
+
+static devclass_t am335x_pmic_devclass;
+
+DRIVER_MODULE(am335x_pmic, iicbus, am335x_pmic_driver, am335x_pmic_devclass, 0, 0);
+MODULE_VERSION(am335x_pmic, 1);
+MODULE_DEPEND(am335x_pmic, iicbus, 1, 1, 1);
diff --git a/sys/arm/ti/am335x/am335x_pwm.h b/sys/arm/ti/am335x/am335x_pwm.h
new file mode 100644
index 000000000000..956913dcc292
--- /dev/null
+++ b/sys/arm/ti/am335x/am335x_pwm.h
@@ -0,0 +1,35 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef __AM335X_PWM_H__
+#define __AM335X_PWM_H__
+
+int am335x_pwm_config_ecap(int unit, int period, int duty);
+
+#endif /* __AM335X_PWM_H__ */
diff --git a/sys/arm/ti/am335x/am335x_pwmss.c b/sys/arm/ti/am335x/am335x_pwmss.c
new file mode 100644
index 000000000000..287b6ce9e8fe
--- /dev/null
+++ b/sys/arm/ti/am335x/am335x_pwmss.c
@@ -0,0 +1,179 @@
+/*-
+ * Copyright (c) 2013 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+
+#include <dev/fdt/simplebus.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/ti/ti_sysc.h>
+
+#include <dev/extres/syscon/syscon.h>
+#include "syscon_if.h"
+
+#include "am335x_pwm.h"
+#include "am335x_scm.h"
+
+#define PWMSS_IDVER 0x00
+#define PWMSS_SYSCONFIG 0x04
+#define PWMSS_CLKCONFIG 0x08
+#define CLKCONFIG_EPWMCLK_EN (1 << 8)
+#define PWMSS_CLKSTATUS 0x0C
+
+/* TRM chapter 2 memory map table 2-3 + VER register location */
+#define PWMSS_REV_0 0x0000
+#define PWMSS_REV_1 0x2000
+#define PWMSS_REV_2 0x4000
+
+static device_probe_t am335x_pwmss_probe;
+static device_attach_t am335x_pwmss_attach;
+static device_detach_t am335x_pwmss_detach;
+
+struct am335x_pwmss_softc {
+ struct simplebus_softc sc_simplebus;
+ device_t sc_dev;
+ struct syscon *syscon;
+};
+
+static device_method_t am335x_pwmss_methods[] = {
+ DEVMETHOD(device_probe, am335x_pwmss_probe),
+ DEVMETHOD(device_attach, am335x_pwmss_attach),
+ DEVMETHOD(device_detach, am335x_pwmss_detach),
+
+ DEVMETHOD_END
+};
+
+static int
+am335x_pwmss_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "ti,am33xx-pwmss"))
+ return (ENXIO);
+
+ device_set_desc(dev, "AM335x PWM");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+am335x_pwmss_attach(device_t dev)
+{
+ struct am335x_pwmss_softc *sc;
+ uint32_t reg, id;
+ uint64_t rev_address;
+ phandle_t node, opp_table;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+
+ /* FIXME: For now; Go and kidnap syscon from opp-table */
+ opp_table = OF_finddevice("/opp-table");
+ if (opp_table == -1) {
+ device_printf(dev, "Cant find /opp-table\n");
+ return (ENXIO);
+ }
+ if (!OF_hasprop(opp_table, "syscon")) {
+ device_printf(dev, "/opp-table doesnt have required syscon property\n");
+ return (ENXIO);
+ }
+ if (syscon_get_by_ofw_property(dev, opp_table, "syscon", &sc->syscon) != 0) {
+ device_printf(dev, "Failed to get syscon\n");
+ return (ENXIO);
+ }
+
+ ti_sysc_clock_enable(device_get_parent(dev));
+
+ rev_address = ti_sysc_get_rev_address(device_get_parent(dev));
+ switch (rev_address) {
+ case PWMSS_REV_0:
+ id = 0;
+ break;
+ case PWMSS_REV_1:
+ id = 1;
+ break;
+ case PWMSS_REV_2:
+ id = 2;
+ break;
+ }
+
+ reg = SYSCON_READ_4(sc->syscon, SCM_PWMSS_CTRL);
+ reg |= (1 << id);
+ SYSCON_WRITE_4(sc->syscon, SCM_PWMSS_CTRL, reg);
+
+ node = ofw_bus_get_node(dev);
+
+ if (node == -1)
+ return (ENXIO);
+
+ simplebus_init(dev, node);
+
+ /*
+ * Allow devices to identify.
+ */
+ bus_generic_probe(dev);
+
+ /*
+ * Now walk the OFW tree and attach top-level devices.
+ */
+ for (node = OF_child(node); node > 0; node = OF_peer(node))
+ simplebus_add_device(dev, node, 0, NULL, -1, NULL);
+
+ return (bus_generic_attach(dev));
+}
+
+static int
+am335x_pwmss_detach(device_t dev)
+{
+
+ return (0);
+}
+
+DEFINE_CLASS_1(am335x_pwmss, am335x_pwmss_driver, am335x_pwmss_methods,
+ sizeof(struct am335x_pwmss_softc), simplebus_driver);
+static devclass_t am335x_pwmss_devclass;
+DRIVER_MODULE(am335x_pwmss, simplebus, am335x_pwmss_driver, am335x_pwmss_devclass, 0, 0);
+MODULE_VERSION(am335x_pwmss, 1);
+MODULE_DEPEND(am335x_pwmss, ti_sysc, 1, 1, 1);
diff --git a/sys/arm/ti/am335x/am335x_reg.h b/sys/arm/ti/am335x/am335x_reg.h
new file mode 100644
index 000000000000..962238e7677b
--- /dev/null
+++ b/sys/arm/ti/am335x/am335x_reg.h
@@ -0,0 +1,42 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Damjan Marion <dmarion@Freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _AM335X_REG_H_
+#define _AM335X_REG_H_
+
+#define AM335X_L4_WKUP_BASE 0x44C00000
+#define AM335X_L4_WKUP_SIZE 0x400000
+
+#define AM335X_CONTROL_BASE AM335X_L4_WKUP_BASE + 0x210000
+#define AM335X_CONTROL_SIZE 0x2000
+#define AM335X_CONTROL_DEVICE_ID 0x0600
+#define AM335X_CONTROL_DEV_FEATURE 0x0604
+
+#endif
diff --git a/sys/arm/ti/am335x/am335x_rtc.c b/sys/arm/ti/am335x/am335x_rtc.c
new file mode 100644
index 000000000000..c2612d2fc7a9
--- /dev/null
+++ b/sys/arm/ti/am335x/am335x_rtc.c
@@ -0,0 +1,213 @@
+/*-
+ * Copyright (c) 2015 Luiz Otavio O Souza <loos@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/clock.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <arm/ti/ti_sysc.h>
+#include <arm/ti/am335x/am335x_rtcvar.h>
+#include <arm/ti/am335x/am335x_rtcreg.h>
+
+#define RTC_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define RTC_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#define RTC_LOCK_INIT(_sc) mtx_init(&(_sc)->sc_mtx, \
+ device_get_nameunit(_sc->sc_dev), "am335x_rtc", MTX_DEF)
+#define RTC_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx)
+
+#define RTC_READ4(_sc, reg) \
+ bus_read_4((_sc)->sc_mem_res, reg)
+#define RTC_WRITE4(_sc, reg, value) \
+ bus_write_4((_sc)->sc_mem_res, reg, value)
+
+#define RTC_MAXIRQS 2
+
+struct am335x_rtc_softc {
+ device_t sc_dev;
+ struct mtx sc_mtx;
+ struct resource *sc_irq_res[RTC_MAXIRQS];
+ struct resource *sc_mem_res;
+};
+
+static struct am335x_rtc_softc *rtc_sc = NULL;
+static struct resource_spec am335x_rtc_irq_spec[] = {
+ { SYS_RES_IRQ, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 1, RF_ACTIVE },
+ { -1, 0, 0 }
+};
+
+static int
+am335x_rtc_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+ if (!ofw_bus_is_compatible(dev, "ti,da830-rtc"))
+ return (ENXIO);
+ device_set_desc(dev, "AM335x RTC (power management mode)");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+am335x_rtc_attach(device_t dev)
+{
+ int rid;
+ struct am335x_rtc_softc *sc;
+ uint32_t rev;
+
+ if (rtc_sc != NULL)
+ return (ENXIO);
+ rtc_sc = sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+ rid = 0;
+ sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (!sc->sc_mem_res) {
+ device_printf(dev, "cannot allocate memory resources\n");
+ return (ENXIO);
+ }
+ if (bus_alloc_resources(dev, am335x_rtc_irq_spec, sc->sc_irq_res) != 0) {
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+ device_printf(dev, "cannot allocate irq resources\n");
+ return (ENXIO);
+ }
+ RTC_LOCK_INIT(sc);
+
+ /* Enable the RTC module. */
+ ti_sysc_clock_enable(device_get_parent(dev));
+ rev = RTC_READ4(sc, RTC_REVISION);
+ device_printf(dev, "AM335X RTC v%d.%d.%d\n",
+ (rev >> 8) & 0x7, (rev >> 6) & 0x3, rev & 0x3f);
+ /* Unlock the RTC. */
+ RTC_WRITE4(sc, RTC_KICK0R, RTC_KICK0R_PASS);
+ RTC_WRITE4(sc, RTC_KICK1R, RTC_KICK1R_PASS);
+ /* Stop the RTC, we don't need it right now. */
+ RTC_WRITE4(sc, RTC_CTRL, 0);
+ /* Disable interrupts. */
+ RTC_WRITE4(sc, RTC_INTR, 0);
+ /* Ack any pending interrupt. */
+ RTC_WRITE4(sc, RTC_STATUS, RTC_STATUS_ALARM2 | RTC_STATUS_ALARM);
+ /* Enable external clock (xtal) and 32 kHz clock. */
+ RTC_WRITE4(sc, RTC_OSC, RTC_OSC_32KCLK_EN | RTC_OSC_32KCLK_SEL);
+ /* Enable pmic_pwr_enable. */
+ RTC_WRITE4(sc, RTC_PMIC, PMIC_PWR_ENABLE);
+
+ return (0);
+}
+
+static int
+am335x_rtc_detach(device_t dev)
+{
+ struct am335x_rtc_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (sc->sc_irq_res[0] != NULL)
+ bus_release_resources(dev, am335x_rtc_irq_spec, sc->sc_irq_res);
+ if (sc->sc_mem_res)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+ RTC_LOCK_DESTROY(sc);
+
+ return (0);
+}
+
+void
+am335x_rtc_pmic_pwr_toggle(void)
+{
+ int timeout;
+ struct clocktime ct;
+ struct timespec ts;
+
+ /*
+ * We stop the RTC so we don't need to check the STATUS.BUSY bit
+ * before update ALARM2 registers.
+ */
+ timeout = 10;
+ RTC_WRITE4(rtc_sc, RTC_CTRL, 0);
+ while (--timeout && RTC_READ4(rtc_sc, RTC_STATUS) & RTC_STATUS_RUN)
+ DELAY(100);
+ if (timeout == 0) {
+ device_printf(rtc_sc->sc_dev, "RTC does not stop.\n");
+ return;
+ }
+ /* Program the ALARM2 to fire in 2 seconds. */
+ ct.dow = 0;
+ ct.nsec = 0;
+ ct.sec = FROMBCD(RTC_READ4(rtc_sc, RTC_SECONDS) & 0x7f);
+ ct.min = FROMBCD(RTC_READ4(rtc_sc, RTC_MINUTES) & 0x7f);
+ ct.hour = FROMBCD(RTC_READ4(rtc_sc, RTC_HOURS) & 0x3f);
+ ct.day = FROMBCD(RTC_READ4(rtc_sc, RTC_DAYS) & 0x3f);
+ ct.mon = FROMBCD(RTC_READ4(rtc_sc, RTC_MONTHS) & 0x1f);
+ ct.year = FROMBCD(RTC_READ4(rtc_sc, RTC_YEARS) & 0xff);
+ ct.year += POSIX_BASE_YEAR;
+ clock_ct_to_ts(&ct, &ts);
+ ts.tv_sec += 2;
+ clock_ts_to_ct(&ts, &ct);
+ RTC_WRITE4(rtc_sc, RTC_ALARM2_SECONDS, TOBCD(ct.sec));
+ RTC_WRITE4(rtc_sc, RTC_ALARM2_MINUTES, TOBCD(ct.min));
+ RTC_WRITE4(rtc_sc, RTC_ALARM2_HOURS, TOBCD(ct.hour));
+ RTC_WRITE4(rtc_sc, RTC_ALARM2_DAYS, TOBCD(ct.day));
+ RTC_WRITE4(rtc_sc, RTC_ALARM2_MONTHS, TOBCD(ct.mon));
+ RTC_WRITE4(rtc_sc, RTC_ALARM2_YEARS, TOBCD(ct.year - POSIX_BASE_YEAR));
+ /* Enable ALARM2 interrupt. */
+ RTC_WRITE4(rtc_sc, RTC_INTR, RTC_INTR_ALARM2);
+ /* Start count. */
+ RTC_WRITE4(rtc_sc, RTC_CTRL, RTC_CTRL_RUN);
+}
+
+static device_method_t am335x_rtc_methods[] = {
+ DEVMETHOD(device_probe, am335x_rtc_probe),
+ DEVMETHOD(device_attach, am335x_rtc_attach),
+ DEVMETHOD(device_detach, am335x_rtc_detach),
+
+ DEVMETHOD_END
+};
+
+static driver_t am335x_rtc_driver = {
+ "am335x_rtc",
+ am335x_rtc_methods,
+ sizeof(struct am335x_rtc_softc),
+};
+
+static devclass_t am335x_rtc_devclass;
+
+DRIVER_MODULE(am335x_rtc, simplebus, am335x_rtc_driver, am335x_rtc_devclass, 0, 0);
+MODULE_VERSION(am335x_rtc, 1);
+MODULE_DEPEND(am335x_rtc, simplebus, 1, 1, 1);
+MODULE_DEPEND(am335x_rtc, ti_sysc, 1, 1, 1);
diff --git a/sys/arm/ti/am335x/am335x_rtcreg.h b/sys/arm/ti/am335x/am335x_rtcreg.h
new file mode 100644
index 000000000000..a4b5cc811ab0
--- /dev/null
+++ b/sys/arm/ti/am335x/am335x_rtcreg.h
@@ -0,0 +1,76 @@
+/*-
+ * Copyright 2015 Luiz Otavio O Souza <loos@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _AM335X_RTCREG_H_
+#define _AM335X_RTCREG_H_
+
+#define RTC_SECONDS 0x00
+#define RTC_MINUTES 0x04
+#define RTC_HOURS 0x08
+#define RTC_DAYS 0x0c
+#define RTC_MONTHS 0x10
+#define RTC_YEARS 0x14
+#define RTC_WEEK 0x18
+#define RTC_CTRL 0x40
+#define RTC_CTRL_DISABLE (1U << 6)
+#define RTC_CTRL_RUN (1U << 0)
+#define RTC_STATUS 0x44
+#define RTC_STATUS_ALARM2 (1U << 7)
+#define RTC_STATUS_ALARM (1U << 6)
+#define RTC_STATUS_1D_EVENT (1U << 5)
+#define RTC_STATUS_1H_EVENT (1U << 4)
+#define RTC_STATUS_1M_EVENT (1U << 3)
+#define RTC_STATUS_1S_EVENT (1U << 2)
+#define RTC_STATUS_RUN (1U << 1)
+#define RTC_STATUS_BUSY (1U << 0)
+#define RTC_INTR 0x48
+#define RTC_INTR_ALARM2 (1U << 4)
+#define RTC_INTR_ALARM (1U << 3)
+#define RTC_INTR_TIMER (1U << 2)
+#define RTC_OSC 0x54
+#define RTC_OSC_32KCLK_EN (1U << 6)
+#define RTC_OSC_OSC32K_GZ (1U << 4)
+#define RTC_OSC_32KCLK_SEL (1U << 3)
+#define RTC_OSC_RES_SELECT (1U << 2)
+#define RTC_OSC_SW2 (1U << 1)
+#define RTC_OSC_SW1 (1U << 0)
+#define RTC_KICK0R 0x6c
+#define RTC_KICK0R_PASS 0x83e70b13
+#define RTC_KICK1R 0x70
+#define RTC_KICK1R_PASS 0x95a4f1e0
+#define RTC_REVISION 0x74
+#define RTC_ALARM2_SECONDS 0x80
+#define RTC_ALARM2_MINUTES 0x84
+#define RTC_ALARM2_HOURS 0x88
+#define RTC_ALARM2_DAYS 0x8c
+#define RTC_ALARM2_MONTHS 0x90
+#define RTC_ALARM2_YEARS 0x94
+#define RTC_PMIC 0x98
+#define PMIC_PWR_ENABLE (1U << 16)
+
+#endif /* _AM335X_RTCREG_H_ */
diff --git a/sys/arm/ti/am335x/am335x_rtcvar.h b/sys/arm/ti/am335x/am335x_rtcvar.h
new file mode 100644
index 000000000000..330f082aff76
--- /dev/null
+++ b/sys/arm/ti/am335x/am335x_rtcvar.h
@@ -0,0 +1,34 @@
+/*-
+ * Copyright 2015 Luiz Otavio O Souza <loos@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _AM335X_RTCVAR_H_
+#define _AM335X_RTCVAR_H_
+
+void am335x_rtc_pmic_pwr_toggle(void);
+
+#endif /* _AM335X_RTCVAR_H_ */
diff --git a/sys/arm/ti/am335x/am335x_scm.c b/sys/arm/ti/am335x/am335x_scm.c
new file mode 100644
index 000000000000..e72c14ba58ad
--- /dev/null
+++ b/sys/arm/ti/am335x/am335x_scm.c
@@ -0,0 +1,198 @@
+/*-
+ * Copyright (c) 2016 Rubicon Communications, LLC (Netgate)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+
+#include <arm/ti/am335x/am335x_scm.h>
+#include <arm/ti/ti_cpuid.h>
+#include <arm/ti/ti_scm.h>
+
+#include <dev/extres/syscon/syscon.h>
+#include "syscon_if.h"
+
+#define TZ_ZEROC 2731
+
+struct am335x_scm_softc {
+ int sc_last_temp;
+ struct sysctl_oid *sc_temp_oid;
+ struct syscon *syscon;
+};
+
+static int
+am335x_scm_temp_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ device_t dev;
+ int i, temp;
+ struct am335x_scm_softc *sc;
+ uint32_t reg;
+
+ dev = (device_t)arg1;
+ sc = device_get_softc(dev);
+
+ /* Read the temperature and convert to Kelvin. */
+ for(i = 50; i > 0; i--) {
+ reg = SYSCON_READ_4(sc->syscon, SCM_BGAP_CTRL);
+ if ((reg & SCM_BGAP_EOCZ) == 0)
+ break;
+ DELAY(50);
+ }
+ if ((reg & SCM_BGAP_EOCZ) == 0) {
+ sc->sc_last_temp =
+ (reg >> SCM_BGAP_TEMP_SHIFT) & SCM_BGAP_TEMP_MASK;
+ sc->sc_last_temp *= 10;
+ }
+ temp = sc->sc_last_temp + TZ_ZEROC;
+
+ return (sysctl_handle_int(oidp, &temp, 0, req));
+}
+
+static void
+am335x_scm_identify(driver_t *driver, device_t parent)
+{
+ device_t child;
+
+ /* AM335x only. */
+ if (ti_chip() != CHIP_AM335X)
+ return;
+
+ /* Make sure we attach only once. */
+ if (device_find_child(parent, "am335x_scm", -1) != NULL)
+ return;
+
+ child = device_add_child(parent, "am335x_scm", -1);
+ if (child == NULL)
+ device_printf(parent, "cannot add ti_scm child\n");
+}
+
+static int
+am335x_scm_probe(device_t dev)
+{
+ /* Just allow the first one */
+ if (strcmp(device_get_nameunit(dev), "am335x_scm0") != 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "AM335x Control Module Extension");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+am335x_scm_attach(device_t dev)
+{
+ struct am335x_scm_softc *sc;
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid_list *tree;
+ uint32_t reg;
+ phandle_t opp_table;
+ int err;
+
+ sc = device_get_softc(dev);
+
+ /* FIXME: For now; Go and kidnap syscon from opp-table */
+ opp_table = OF_finddevice("/opp-table");
+ if (opp_table == -1) {
+ device_printf(dev, "Cant find /opp-table\n");
+ return (ENXIO);
+ }
+ if (!OF_hasprop(opp_table, "syscon")) {
+ device_printf(dev, "/opp-table missing syscon property\n");
+ return (ENXIO);
+ }
+ err = syscon_get_by_ofw_property(dev, opp_table, "syscon", &sc->syscon);
+ if (err) {
+ device_printf(dev, "Failed to get syscon\n");
+ return (ENXIO);
+ }
+
+ /* Reset the digital outputs. */
+ SYSCON_WRITE_4(sc->syscon, SCM_BGAP_CTRL, 0);
+ reg = SYSCON_READ_4(sc->syscon, SCM_BGAP_CTRL);
+ DELAY(500);
+ /* Set continous mode. */
+ SYSCON_WRITE_4(sc->syscon, SCM_BGAP_CTRL, SCM_BGAP_CONTCONV);
+ reg = SYSCON_READ_4(sc->syscon, SCM_BGAP_CTRL);
+ DELAY(500);
+ /* Start the ADC conversion. */
+ reg = SCM_BGAP_CLRZ | SCM_BGAP_CONTCONV | SCM_BGAP_SOC;
+ SYSCON_WRITE_4(sc->syscon, SCM_BGAP_CTRL, reg);
+
+ /* Temperature sysctl. */
+ ctx = device_get_sysctl_ctx(dev);
+ tree = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
+ sc->sc_temp_oid = SYSCTL_ADD_PROC(ctx, tree, OID_AUTO,
+ "temperature", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
+ dev, 0, am335x_scm_temp_sysctl, "IK", "Current temperature");
+
+ return (0);
+}
+
+static int
+am335x_scm_detach(device_t dev)
+{
+ struct am335x_scm_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ /* Remove temperature sysctl. */
+ if (sc->sc_temp_oid != NULL)
+ sysctl_remove_oid(sc->sc_temp_oid, 1, 0);
+
+ /* Stop the bandgap ADC. */
+ SYSCON_WRITE_4(sc->syscon, SCM_BGAP_CTRL, SCM_BGAP_BGOFF);
+
+ return (0);
+}
+
+static device_method_t am335x_scm_methods[] = {
+ DEVMETHOD(device_identify, am335x_scm_identify),
+ DEVMETHOD(device_probe, am335x_scm_probe),
+ DEVMETHOD(device_attach, am335x_scm_attach),
+ DEVMETHOD(device_detach, am335x_scm_detach),
+
+ DEVMETHOD_END
+};
+
+static driver_t am335x_scm_driver = {
+ "am335x_scm",
+ am335x_scm_methods,
+ sizeof(struct am335x_scm_softc),
+};
+
+static devclass_t am335x_scm_devclass;
+
+DRIVER_MODULE(am335x_scm, ti_scm, am335x_scm_driver, am335x_scm_devclass, 0, 0);
+MODULE_VERSION(am335x_scm, 1);
+MODULE_DEPEND(am335x_scm, ti_scm_syscon, 1, 1, 1);
diff --git a/sys/arm/ti/am335x/am335x_scm.h b/sys/arm/ti/am335x/am335x_scm.h
new file mode 100644
index 000000000000..ff7af52fe3b8
--- /dev/null
+++ b/sys/arm/ti/am335x/am335x_scm.h
@@ -0,0 +1,51 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef __AM335X_SCM_H__
+#define __AM335X_SCM_H__
+
+/* AM335x-specific registers for control module (scm) */
+#define SCM_CTRL_STATUS 0x40
+#define SCM_BGAP_CTRL 0x448
+#define SCM_BGAP_TEMP_MASK 0xff
+#define SCM_BGAP_TEMP_SHIFT 8
+#define SCM_BGAP_BGOFF (1 << 6)
+#define SCM_BGAP_SOC (1 << 4)
+#define SCM_BGAP_CLRZ (1 << 3)
+#define SCM_BGAP_CONTCONV (1 << 2)
+#define SCM_BGAP_EOCZ (1 << 1)
+#define SCM_USB_CTRL0 0x620
+#define SCM_USB_STS0 0x624
+#define SCM_USB_CTRL1 0x628
+#define SCM_USB_STS1 0x62C
+#define SCM_MAC_ID0_LO 0x630
+#define SCM_MAC_ID0_HI 0x634
+#define SCM_PWMSS_CTRL 0x664
+
+#endif /* __AM335X_SCM_H__ */
diff --git a/sys/arm/ti/am335x/am335x_scm_padconf.c b/sys/arm/ti/am335x/am335x_scm_padconf.c
new file mode 100644
index 000000000000..06be33278332
--- /dev/null
+++ b/sys/arm/ti/am335x/am335x_scm_padconf.c
@@ -0,0 +1,303 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Damjan Marion <dmarion@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/intr.h>
+#include <sys/gpio.h>
+
+#include <arm/ti/tivar.h>
+#include <arm/ti/ti_pinmux.h>
+
+#include <arm/ti/am335x/am335x_scm_padconf.h>
+
+#define _PIN(r, b, gp, gm, m0, m1, m2, m3, m4, m5, m6, m7) \
+ { .reg_off = r, \
+ .gpio_pin = gp, \
+ .gpio_mode = gm, \
+ .ballname = b, \
+ .muxmodes[0] = m0, \
+ .muxmodes[1] = m1, \
+ .muxmodes[2] = m2, \
+ .muxmodes[3] = m3, \
+ .muxmodes[4] = m4, \
+ .muxmodes[5] = m5, \
+ .muxmodes[6] = m6, \
+ .muxmodes[7] = m7, \
+ }
+
+const static struct ti_pinmux_padstate ti_padstate_devmap[] = {
+ {"output", PADCONF_OUTPUT },
+ {"output_pullup", PADCONF_OUTPUT_PULLUP },
+ {"input", PADCONF_INPUT },
+ {"input_pulldown", PADCONF_INPUT_PULLDOWN },
+ {"input_pullup", PADCONF_INPUT_PULLUP },
+ {"i2c", PADCONF_INPUT_PULLUP_SLOW },
+ { .state = NULL }
+};
+
+const static struct ti_pinmux_padconf ti_padconf_devmap[] = {
+ _PIN(0x000, "GPMC_AD0", 32, 7,"gpmc_ad0", "mmc1_dat0", NULL, NULL, NULL, NULL, NULL, "gpio1_0"),
+ _PIN(0x004, "GPMC_AD1", 33, 7,"gpmc_ad1", "mmc1_dat1", NULL, NULL, NULL, NULL, NULL, "gpio1_1"),
+ _PIN(0x008, "GPMC_AD2", 34, 7,"gpmc_ad2", "mmc1_dat2", NULL, NULL, NULL, NULL, NULL, "gpio1_2"),
+ _PIN(0x00C, "GPMC_AD3", 35, 7,"gpmc_ad3", "mmc1_dat3", NULL, NULL, NULL, NULL, NULL, "gpio1_3"),
+ _PIN(0x010, "GPMC_AD4", 36, 7,"gpmc_ad4", "mmc1_dat4", NULL, NULL, NULL, NULL, NULL, "gpio1_4"),
+ _PIN(0x014, "GPMC_AD5", 37, 7,"gpmc_ad5", "mmc1_dat5", NULL, NULL, NULL, NULL, NULL, "gpio1_5"),
+ _PIN(0x018, "GPMC_AD6", 38, 7,"gpmc_ad6", "mmc1_dat6", NULL, NULL, NULL, NULL, NULL, "gpio1_6"),
+ _PIN(0x01C, "GPMC_AD7", 39, 7,"gpmc_ad7", "mmc1_dat7", NULL, NULL, NULL, NULL, NULL, "gpio1_7"),
+ _PIN(0x020, "GPMC_AD8", 22, 7, "gpmc_ad8", "lcd_data23", "mmc1_dat0", "mmc2_dat4", "ehrpwm2A", NULL, NULL, "gpio0_22"),
+ _PIN(0x024, "GPMC_AD9", 23, 7, "gpmc_ad9", "lcd_data22", "mmc1_dat1", "mmc2_dat5", "ehrpwm2B", NULL, NULL, "gpio0_23"),
+ _PIN(0x028, "GPMC_AD10", 26, 7, "gpmc_ad10", "lcd_data21", "mmc1_dat2", "mmc2_dat6", "ehrpwm2_tripzone_in", NULL, NULL, "gpio0_26"),
+ _PIN(0x02C, "GPMC_AD11", 27, 7, "gpmc_ad11", "lcd_data20", "mmc1_dat3", "mmc2_dat7", "ehrpwm0_synco", NULL, NULL, "gpio0_27"),
+ _PIN(0x030, "GPMC_AD12", 44, 7, "gpmc_ad12", "lcd_data19", "mmc1_dat4", "mmc2_dat0", "eQEP2A_in", "pr1_mii0_txd2", "pr1_pru0_pru_r30_14", "gpio1_12"),
+ _PIN(0x034, "GPMC_AD13", 45, 7, "gpmc_ad13", "lcd_data18", "mmc1_dat5", "mmc2_dat1", "eQEP2B_in", "pr1_mii0_txd1", "pr1_pru0_pru_r30_15", "gpio1_13"),
+ _PIN(0x038, "GPMC_AD14", 46, 7, "gpmc_ad14", "lcd_data17", "mmc1_dat6", "mmc2_dat2", "eQEP2_index", "pr1_mii0_txd0", "pr1_pru0_pru_r31_14", "gpio1_14"),
+ _PIN(0x03C, "GPMC_AD15", 47, 7, "gpmc_ad15", "lcd_data16", "mmc1_dat7", "mmc2_dat3", "eQEP2_strobe", "pr1_ecap0_ecap_capin_apwm_o", "pr1_pru0_pru_r31_15", "gpio1_15"),
+ _PIN(0x040, "GPMC_A0", 48, 7, "gpmc_a0", "gmii2_txen", "rgmii2_tctl", "rmii2_txen", "gpmc_a16", "pr1_mii_mt1_clk", "ehrpwm1_tripzone_input", "gpio1_16"),
+ _PIN(0x044, "GPMC_A1", 49, 7, "gpmc_a1", "gmii2_rxdv", "rgmii2_rctl", "mmc2_dat0", "gpmc_a17", "pr1_mii1_txd3", "ehrpwm0_synco", "gpio1_17"),
+ _PIN(0x048, "GPMC_A2", 50, 7, "gpmc_a2", "gmii2_txd3", "rgmii2_td3", "mmc2_dat1", "gpmc_a18", "pr1_mii1_txd2", "ehrpwm1A", "gpio1_18"),
+ _PIN(0x04C, "GPMC_A3", 51, 7, "gpmc_a3", "gmii2_txd2", "rgmii2_td2", "mmc2_dat2", "gpmc_a19", "pr1_mii1_txd1", "ehrpwm1B", "gpio1_19"),
+ _PIN(0x050, "GPMC_A4", 52, 7, "gpmc_a4", "gmii2_txd1", "rgmii2_td1", "rmii2_tdx1", "gpmc_a20", "pr1_mii1_txd0", "eQEP1A_in", "gpio1_20"),
+ _PIN(0x054, "GPMC_A5", 53, 7, "gpmc_a5", "gmii2_txd0", "rgmii2_td0", "rmii2_txd0", "gpmc_a21", "pr1_mii1_rxd3", "eQEP1B_in", "gpio1_21"),
+ _PIN(0x058, "GPMC_A6", 54, 7, "gpmc_a6", "gmii2_txclk", "rgmii2_tclk", "mmc2_dat4", "gpmc_a22", "pr1_mii1_rxd2", "eQEP1_index", "gpio1_22"),
+ _PIN(0x05C, "GPMC_A7", 55, 7, "gpmc_a7", "gmii2_rxclk", "rgmii2_rclk", "mmc2_dat5", "gpmc_a23", "pr1_mii1_rxd1", "eQEP1_strobe", "gpio1_23"),
+ _PIN(0x060, "GPMC_A8", 56, 7, "gpmc_a8", "gmii2_rxd3", "rgmii2_rd3", "mmc2_dat6", "gpmc_a24", "pr1_mii1_rxd0", "mcasp0_aclkx", "gpio1_24"),
+ _PIN(0x064, "GPMC_A9", 57, 7, "gmpc_a9", "gmii2_rxd2", "rgmii2_rd2", "mmc2_dat7 / rmii2_crs_dv", "gpmc_a25", "pr1_mii_mr1_clk", "mcasp0_fsx", "gpio1_25"),
+ _PIN(0x068, "GPMC_A10", 58, 7, "gmpc_a10", "gmii2_rxd1", "rgmii2_rd1", "rmii2_rxd1", "gpmc_a26", "pr1_mii1_rxdv", "mcasp0_arx0", "gpio1_26"),
+ _PIN(0x06C, "GPMC_A11", 59, 7, "gmpc_a11", "gmii2_rxd0", "rgmii2_rd0", "rmii2_rxd0", "gpmc_a27", "pr1_mii1_rxer", "mcasp0_axr1", "gpio1_27"),
+ _PIN(0x070, "GPMC_WAIT0", 30, 7, "gpmc_wait0", "gmii2_crs", "gpmc_csn4", "rmii2_crs_dv", "mmc1_sdcd", "pr1_mii1_col", "uart4_rxd", "gpio0_30"),
+ _PIN(0x074, "GPMC_WPn", 31, 7, "gpmc_wpn", "gmii2_rxerr", "gpmc_csn5", "rmii2_rxerr", "mmc2_sdcd", "pr1_mii1_txen", "uart4_txd", "gpio0_31"),
+ _PIN(0x078, "GPMC_BEn1", 60, 7, "gpmc_be1n", "gmii2_col", "gmpc_csn6","mmc2_dat3", "gpmc_dir", "pr1_mii1_rxlink", "mcasp0_aclkr", "gpio1_28"),
+ _PIN(0x07c, "GPMC_CSn0", 61, 7, "gpmc_csn0", NULL, NULL, NULL, NULL, NULL, NULL, "gpio1_29"),
+ _PIN(0x080, "GPMC_CSn1", 62, 7, "gpmc_csn1", "gpmc_clk", "mmc1_clk", "pr1_edio_data_in6", "pr1_edio_data_out6", "pr1_pru1_pru_r30_12", "pr1_pru1_pru_r31_12", "gpio1_30"),
+ _PIN(0x084, "GPMC_CSn2", 63, 7, "gpmc_csn2", "gpmc_be1n", "mmc1_cmd", "pr1_edio_data_in7", "pr1_edio_data_out7", "pr1_pru1_pru_r30_13", "pr1_pru1_pru_r31_13", "gpio1_31"),
+ _PIN(0x088, "GPMC_CSn3", 64, 7, "gpmc_csn3", "gpmc_a3", "rmii2_crs_dv", "mmc2_cmd", "pr1_mii0_crs", "pr1_mdio_data", "EMU4", "gpio2_0"),
+ _PIN(0x08c, "GPMC_CLK", 65, 7, "gpmc_clk", "lcd_memory_clk", "gpmc_wait1", "mmc2_clk", "pr1_mii1_crs", "pr1_mdio_mdclk", "mcasp0_fsr", "gpio2_1"),
+ _PIN(0x090, "GPMC_ADVn_ALE", 66, 7, "gpmc_advn_ale", NULL, "timer4", NULL, NULL, NULL, NULL, "gpio2_2"),
+ _PIN(0x094, "GPMC_OEn_REn", 67, 7, "gpmc_oen_ren", NULL, "timer7", NULL, NULL, NULL, NULL, "gpio2_3"),
+ _PIN(0x098, "GPMC_WEn", 68, 7, "gpmc_wen", NULL, "timer6", NULL, NULL, NULL, NULL, "gpio2_4"),
+ _PIN(0x09c, "GPMC_BEn0_CLE", 67, 7, "gpmc_ben0_cle", NULL, "timer5", NULL, NULL, NULL, NULL, "gpio2_5"),
+ _PIN(0x0a0, "LCD_DATA0", 68, 7, "lcd_data0", "gpmc_a0", "pr1_mii_mt0_clk", "ehrpwm2A", NULL, "pr1_pru1_pru_r30_0", "pr1_pru1_pru_r31_0", "gpio2_6"),
+ _PIN(0x0a4, "LCD_DATA1", 69, 7, "lcd_data1", "gpmc_a1", "pr1_mii0_txen", "ehrpwm2B", NULL, "pr1_pru1_pru_r30_1", "pr1_pru1_pru_r31_1", "gpio2_7"),
+ _PIN(0x0a8, "LCD_DATA2", 70, 7, "lcd_data2", "gpmc_a2", "pr1_mii0_txd3", "ehrpwm2_tripzone_input", NULL, "pr1_pru1_pru_r30_2", "pr1_pru1_pru_r31_2", "gpio2_8"),
+ _PIN(0x0ac, "LCD_DATA3", 71, 7, "lcd_data3", "gpmc_a3", "pr1_mii0_txd2", "ehrpwm0_synco", NULL, "pr1_pru1_pru_r30_3", "pr1_pru1_pru_r31_3", "gpio2_9"),
+ _PIN(0x0b0, "LCD_DATA4", 72, 7, "lcd_data4", "gpmc_a4", "pr1_mii0_txd1", "eQEP2A_in", NULL, "pr1_pru1_pru_r30_4", "pr1_pru1_pru_r31_4", "gpio2_10"),
+ _PIN(0x0b4, "LCD_DATA5", 73, 7, "lcd_data5", "gpmc_a5", "pr1_mii0_txd0", "eQEP2B_in", NULL, "pr1_pru1_pru_r30_5", "pr1_pru1_pru_r31_5", "gpio2_11"),
+ _PIN(0x0b8, "LCD_DATA6", 74, 7, "lcd_data6", "gpmc_a6", "pr1_edio_data_in6", "eQEP2_index", "pr1_edio_data_out6", "pr1_pru1_pru_r30_6", "pr1_pru1_pru_r31_6", "gpio2_12"),
+ _PIN(0x0bc, "LCD_DATA7", 75, 7, "lcd_data7", "gpmc_a7", "pr1_edio_data_in7", "eQEP2_strobe", "pr1_edio_data_out7", "pr1_pru1_pru_r30_7", "pr1_pru1_pru_r31_7", "gpio2_13"),
+ _PIN(0x0c0, "LCD_DATA8", 76, 7, "lcd_data8", "gpmc_a12", "ehrpwm1_tripzone_input", "mcasp0_aclkx", "uart5_txd", "pr1_mii0_rxd3", "uart2_ctsn", "gpio2_14"),
+ _PIN(0x0c4, "LCD_DATA9", 76, 7, "lcd_data9", "gpmc_a13", "ehrpwm0_synco", "mcasp0_fsx", "uart5_rxd", "pr1_mii0_rxd2", "uart2_rtsn", "gpio2_15"),
+ _PIN(0x0c8, "LCD_DATA10", 77, 7, "lcd_data10", "gpmc_a14", "ehrpwm1A", "mcasp0_axr0", NULL, "pr1_mii0_rxd1", "uart3_ctsn", "gpio2_16"),
+ _PIN(0x0cc, "LCD_DATA11", 78, 7, "lcd_data11", "gpmc_a15", "ehrpwm1B", "mcasp0_ahclkr", "mcasp0_axr2", "pr1_mii0_rxd0", "uart3_rtsn", "gpio2_17"),
+ _PIN(0x0d0, "LCD_DATA12", 8, 7, "lcd_data12", "gpmc_a16", "eQEP1A_in", "mcasp0_aclkr", "mcasp0_axr2", "pr1_mii0_rxlink", "uart4_ctsn", "gpio0_8"),
+ _PIN(0x0d4, "LCD_DATA13", 9, 7, "lcd_data13", "gpmc_a17", "eQEP1B_in", "mcasp0_fsr", "mcasp0_axr3", "pr1_mii0_rxer", "uart4_rtsn", "gpio0_9"),
+ _PIN(0x0d8, "LCD_DATA14", 10, 7, "lcd_data14", "gpmc_a18", "eQEP1_index", "mcasp0_axr1", "uart5_rxd", "pr1_mii_mr0_clk", "uart5_ctsn", "gpio0_10"),
+ _PIN(0x0dc, "LCD_DATA15", 11, 7, "lcd_data15", "gpmc_a19", "eQEP1_strobe", "mcasp0_ahclkx", "mcasp0_axr3", "pr1_mii0_rxdv", "uart5_rtsn", "gpio0_11"),
+ _PIN(0x0e0, "LCD_VSYNC", 86, 7, "lcd_vsync", "gpmc_a8", "gpmc_a1", "pr1_edio_data_in2", "pr1_edio_data_out2", "pr1_pru1_pru_r30_8", "pr1_pru1_pru_r31_8", "gpio2_22"),
+ _PIN(0x0e4, "LCD_HSYNC", 87, 7, "lcd_hsync", "gmpc_a9", "gpmc_a2", "pr1_edio_data_in3", "pr1_edio_data_out3", "pr1_pru1_pru_r30_9", "pr1_pru1_pru_r31_9", "gpio2_23"),
+ _PIN(0x0e8, "LCD_PCLK", 88, 7, "lcd_pclk", "gpmc_a10", "pr1_mii0_crs", "pr1_edio_data_in4", "pr1_edio_data_out4", "pr1_pru1_pru_r30_10", "pr1_pru1_pru_r31_10", "gpio2_24"),
+ _PIN(0x0ec, "LCD_AC_BIAS_EN", 89, 7, "lcd_ac_bias_en", "gpmc_a11", "pr1_mii1_crs", "pr1_edio_data_in5", "pr1_edio_data_out5", "pr1_pru1_pru_r30_11", "pr1_pru1_pru_r31_11", "gpio2_25"),
+ _PIN(0x0f0, "MMC0_DAT3", 90, 7, "mmc0_dat3", "gpmc_a20", "uart4_ctsn", "timer5", "uart1_dcdn", "pr1_pru0_pru_r30_8", "pr1_pru0_pru_r31_8", "gpio2_26"),
+ _PIN(0x0f4, "MMC0_DAT2", 91, 7, "mmc0_dat2", "gpmc_a21", "uart4_rtsn", "timer6", "uart1_dsrn", "pr1_pru0_pru_r30_9", "pr1_pru0_pru_r31_9", "gpio2_27"),
+ _PIN(0x0f8, "MMC0_DAT1", 92, 7, "mmc0_dat1", "gpmc_a22", "uart5_ctsn", "uart3_rxd", "uart1_dtrn", "pr1_pru0_pru_r30_10", "pr1_pru0_pru_r31_10", "gpio2_28"),
+ _PIN(0x0fc, "MMC0_DAT0", 93, 7, "mmc0_dat0", "gpmc_a23", "uart5_rtsn", "uart3_txd", "uart1_rin", "pr1_pru0_pru_r30_11", "pr1_pru0_pru_r31_11", "gpio2_29"),
+ _PIN(0x100, "MMC0_CLK", 94, 7, "mmc0_clk", "gpmc_a24", "uart3_ctsn", "uart2_rxd", "dcan1_tx", "pr1_pru0_pru_r30_12", "pr1_pru0_pru_r31_12", "gpio2_30"),
+ _PIN(0x104, "MMC0_CMD", 95, 7, "mmc0_cmd", "gpmc_a25", "uart3_rtsn", "uart2_txd", "dcan1_rx", "pr1_pru0_pru_r30_13", "pr1_pru0_pru_r31_13", "gpio2_31"),
+ _PIN(0x108, "MII1_COL", 96, 7, "gmii1_col", "rmii2_refclk", "spi1_sclk", "uart5_rxd", "mcasp1_axr2", "mmc2_dat3", "mcasp0_axr2", "gpio3_0"),
+ _PIN(0x10c, "MII1_CRS", 97, 7, "gmii1_crs", "rmii1_crs_dv", "spi1_d0", "I2C1_SDA", "mcasp1_aclkx", "uart5_ctsn", "uart2_rxd", "gpio3_1"),
+ _PIN(0x110, "MII1_RX_ER", 98, 7, "gmii1_rxerr", "rmii1_rxerr", "spi1_d1", "I2C1_SCL", "mcasp1_fsx", "uart5_rtsn", "uart2_txd", "gpio3_2"),
+ _PIN(0x114, "MII1_TX_EN", 99, 7, "gmii1_txen", "rmii1_txen", "rgmii1_tctl", "timer4", "mcasp1_axr0", "eQEP0_index", "mmc2_cmd", "gpio3_3"),
+ _PIN(0x118, "MII1_RX_DV", 100, 7, "gmii1_rxdv", "cd_memory_clk", "rgmii1_rctl", "uart5_txd", "mcasp1_aclkx", "mmc2_dat0", "mcasp0_aclkr", "gpio3_4"),
+ _PIN(0x11c, "MII1_TXD3", 16, 7, "gmii1_txd3", "dcan0_tx", "rgmii1_td3", "uart4_rxd", "mcasp1_fsx", "mmc2_dat1", "mcasp0_fsr", "gpio0_16"),
+ _PIN(0x120, "MII1_TXD2", 17, 7, "gmii1_txd2", "dcan0_rx", "rgmii1_td2", "uart4_txd", "mcasp1_axr0", "mmc2_dat2", "mcasp0_ahclkx", "gpio0_17"),
+ _PIN(0x124, "MII1_TXD1", 21, 7, "gmii1_txd1", "rmii1_txd1", "rgmii1_td1", "mcasp1_fsr", "mcasp1_axr1", "eQEP0A_in", "mmc1_cmd", "gpio0_21"),
+ _PIN(0x128, "MII1_TXD0", 28, 7, "gmii1_txd0", "rmii1_txd0", "rgmii1_td0", "mcasp1_axr2", "mcasp1_aclkr", "eQEP0B_in", "mmc1_clk", "gpio0_28"),
+ _PIN(0x12c, "MII1_TX_CLK", 105, 7, "gmii1_txclk", "uart2_rxd", "rgmii1_tclk", "mmc0_dat7", "mmc1_dat0", "uart1_dcdn", "mcasp0_aclkx", "gpio3_9"),
+ _PIN(0x130, "MII1_RX_CLK", 106, 7, "gmii1_rxclk", "uart2_txd", "rgmii1_rclk", "mmc0_dat6", "mmc1_dat1", "uart1_dsrn", "mcasp0_fsx", "gpio3_10"),
+ _PIN(0x134, "MII1_RXD3", 82, 7, "gmii1_rxd3", "uart3_rxd", "rgmii1_rd3", "mmc0_dat5", "mmc1_dat2", "uart1_dtrn", "mcasp0_axr0", "gpio2_18"),
+ _PIN(0x138, "MII1_RXD2", 83, 7, "gmii1_rxd2", "uart3_txd", "rgmii1_rd2", "mmc0_dat4", "mmc1_dat3", "uart1_rin", "mcasp0_axr1", "gpio2_19"),
+ _PIN(0x13c, "MII1_RXD1", 84, 7, "gmii1_rxd1", "rmii1_rxd1", "rgmii1_rd1", "mcasp1_axr3", "mcasp1_fsr", "eQEP0_strobe", "mmc2_clk", "gpio2_20"),
+ _PIN(0x140, "MII1_RXD0", 85, 7, "gmii1_rxd0", "rmii1_rxd0", "rgmii1_rd0", "mcasp1_ahclkx", "mcasp1_ahclkr", "mcasp1_aclkr", "mcasp0_axr3", "gpio2_21"),
+ _PIN(0x144, "RMII1_REF_CLK", 29, 7, "rmii1_refclk", "xdma_event_intr2", "spi1_cs0", "uart5_txd", "mcasp1_axr3", "mmc0_pow", "mcasp1_ahclkx", "gpio0_29"),
+ _PIN(0x148, "MDIO", 0, 7, "mdio_data", "timer6", "uart5_rxd", "uart3_ctsn", "mmc0_sdcd","mmc1_cmd", "mmc2_cmd","gpio0_0"),
+ _PIN(0x14c, "MDC", 1, 7, "mdio_clk", "timer5", "uart5_txd", "uart3_rtsn", "mmc0_sdwp", "mmc1_clk", "mmc2_clk", "gpio0_1"),
+ _PIN(0x150, "SPI0_SCLK", 2, 7, "spi0_sclk", "uart2_rxd", "I2C2_SDA", "ehrpwm0A", "pr1_uart0_cts_n", "pr1_edio_sof", "EMU2", "gpio0_2"),
+ _PIN(0x154, "SPI0_D0", 3, 7, "spi0_d0", "uart2_txd", "I2C2_SCL", "ehrpwm0B", "pr1_uart0_rts_n", "pr1_edio_latch_in", "EMU3", "gpio0_3"),
+ _PIN(0x158, "SPI0_D1", 4, 7, "spi0_d1", "mmc1_sdwp", "I2C1_SDA", "ehrpwm0_tripzone_input", "pr1_uart0_rxd", "pr1_edio_data_in0", "pr1_edio_data_out0", "gpio0_4"),
+ _PIN(0x15c, "SPI0_CS0", 5, 7, "spi0_cs0", "mmc2_sdwp", "I2C1_SCL", "ehrpwm0_synci", "pr1_uart0_txd", "pr1_edio_data_in1", "pr1_edio_data_out1", "gpio0_5"),
+ _PIN(0x160, "SPI0_CS1", 6, 7, "spi0_cs1", "uart3_rxd", "eCAP1_in_PWM1_out", "mcc0_pow", "xdm_event_intr2", "mmc0_sdcd", "EMU4", "gpio0_6"),
+ _PIN(0x164, "ECAP0_IN_PWM0_OUT",7, 7, "eCAP0_in_PWM0_out", "uart3_txd", "spi1_cs1", "pr1_ecap0_ecap_capin_apwm_o", "spi1_sclk", "mmc0_sdwp", "xdma_event_intr2", "gpio0_7"),
+ _PIN(0x168, "UART0_CTSn", 40, 7, "uart0_ctsn", "uart4_rxd", "dcan1_tx", "I2C1_SDA", "spi1_d0", "timer7", "pr1_edc_sync0_out", "gpio1_8"),
+ _PIN(0x16c, "UART0_RTSn", 41, 7, "uart0_rtsn", "uart4_txd", "dcan1_rx", "I2C1_SCL", "spi1_d1", "spi1_cs0", "pr1_edc_sync1_out", "gpio1_9"),
+ _PIN(0x170, "UART0_rxd", 42, 7, "uart0_rxd", "spi1_cs0", "dcan0_tx", "I2C2_SDA", "eCAP2_in_PWM2_out", "pr1_pru1_pru_r30_14", "pr1_pru1_pru_r31_14", "gpio1_10"),
+ _PIN(0x174, "UART0_txd", 43, 7, "uart0_txd", "spi1_cs1", "dcan0_rx", "I2C2_SCL", "eCAP1_in_PWM1_out", "pr1_pru1_pru_r30_15", "pr1_pru1_pru_r31_15", "gpio1_11"),
+ _PIN(0x178, "UART1_CTSn", 12, 7, "uart1_ctsn", "timer6_mux1", "dcan0_tx", "I2C2_SDA", "spi1_cs0", "pr1_uart0_cts_n", "pr1_edc_latch0_in", "gpio0_12"),
+ _PIN(0x17c, "UART1_RTSn", 13, 7, "uart1_rtsn", "timer5_mux1", "dcan0_rx", "I2C2_SCL", "spi1_cs1", "pr1_uart0_rts_n", "pr1_edc_latch1_in", "gpio0_13"),
+ _PIN(0x180, "UART1_RXD", 14, 7, "uart1_rxd", "mmc1_sdwp", "dcan1_tx", "I2C1_SDA", NULL, "pr1_uart0_rxd", "pr1_pru1_pru_r31_16", "gpio0_14"),
+ _PIN(0x184, "UART1_TXD", 15, 7, "uart1_txd", "mmc2_sdwp", "dcan1_rx", "I2C1_SCL", NULL, "pr1_uart0_txd", "pr1_pru0_pru_r31_16", "gpio0_15"),
+ _PIN(0x188, "I2C0_SDA", 101, 7, "I2C0_SDA", "timer4", "uart2_ctsn", "eCAP2_in_PWM2_out", NULL, NULL, NULL, "gpio3_5"),
+ _PIN(0x18c, "I2C0_SCL", 102, 7, "I2C0_SCL", "timer7", "uart2_rtsn", "eCAP1_in_PWM1_out", NULL, NULL, NULL, "gpio3_6"),
+ _PIN(0x190, "MCASP0_ACLKX", 110, 7, "mcasp0_aclkx", "ehrpwm0A", NULL, "spi1_sclk", "mmc0_sdcd", "pr1_pru0_pru_r30_0", "pr1_pru0_pru_r31_0", "gpio3_14"),
+ _PIN(0x194, "MCASP0_FSX", 111, 7, "mcasp0_fsx", "ehrpwm0B", NULL, "spi1_d0", "mmc1_sdcd", "pr1_pru0_pru_r30_1", "pr1_pru0_pru_r31_1", "gpio3_15"),
+ _PIN(0x198, "MCASP0_AXR0", 112, 7, "mcasp0_axr0", "ehrpwm0_tripzone_input", NULL, "spi1_d1", "mmc2_sdcd", "pr1_pru0_pru_r30_2", "pr1_pru0_pru_r31_2", "gpio3_16"),
+ _PIN(0x19c, "MCASP0_AHCLKR", 113, 7, "mcasp0_ahclkr", "ehrpwm0_synci", "mcasp0_axr2", "spi1_cs0", "eCAP2_in_PWM2_out", "pr1_pru0_pru_r30_3", "pr1_pru0_pru_r31_3", "gpio3_17"),
+ _PIN(0x1a0, "MCASP0_ACLKR", 114, 7, "mcasp0_aclkr", "eQEP0A_in", "mcasp0_axr2", "mcasp1_aclkx", "mmc0_sdwp", "pr1_pru0_pru_r30_4", "pr1_pru0_pru_r31_4", "gpio3_18"),
+ _PIN(0x1a4, "MCASP0_FSR", 115, 7, "mcasp0_fsr", "eQEP0B_in", "mcasp0_axr3", "mcasp1_fsx", "EMU2", "pr1_pru0_pru_r30_5", "pr1_pru0_pru_r31_5", "gpio3_19"),
+ _PIN(0x1a8, "MCASP0_AXR1", 116, 7, "mcasp0_axr1", "eQEP0_index", NULL, "mcasp1_axr0", "EMU3", "pr1_pru0_pru_r30_6", "pr1_pru0_pru_r31_6", "gpio3_20"),
+ _PIN(0x1ac, "MCASP0_AHCLKX", 117, 7, "mcasp0_ahclkx", "eQEP0_strobe", "mcasp0_axr3", "mcasp1_axr1", "EMU4", "pr1_pru0_pru_r30_7", "pr1_pru0_pru_r31_7", "gpio3_21"),
+ _PIN(0x1b0, "XDMA_EVENT_INTR0", 19, 7, "xdma_event_intr0", NULL, "timer4", "clkout1", "spi1_cs1", "pr1_pru1_pru_r31_16", "EMU2", "gpio0_19"),
+ _PIN(0x1b4, "XDMA_EVENT_INTR1", 20, 7, "xdma_event_intr1", NULL, "tclkin", "clkout2", "timer7", "pr1_pru0_pru_r31_16", "EMU3", "gpio0_20"),
+#if 0
+ _PIN(0x1b8, "nresetin_out", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x1bc, "porz", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x1c0, "nnmi", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x1c4, "osc0_in", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x1c8, "osc0_out", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x1cc, "osc0_vss", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x1d0, "tms", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x1d4, "tdi", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x1d8, "tdo", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x1dc, "tck", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x1e0, "ntrst", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+#endif
+ _PIN(0x1e4, "EMU0", 103, 7, "EMU0", NULL, NULL, NULL, NULL, NULL, NULL, "gpio3_7"),
+ _PIN(0x1e8, "EMU1", 104, 0, "EMU1", NULL, NULL, NULL, NULL, NULL, NULL, "gpio3_8"),
+#if 0
+ _PIN(0x1ec, "osc1_in", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x1f0, "osc1_out", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x1f4, "osc1_vss", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x1f8, "rtc_porz", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x1fc, "pmic_power_en", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x200, "ext_wakeup", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x204, "enz_kaldo_1p8v", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+#endif
+ _PIN(0x208, "USB0_DM", 0, 0, "USB0_DM", NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x20c, "USB0_DP", 0, 0, "USB0_DP", NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x210, "USB0_CE", 0, 0, "USB0_CE", NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x214, "USB0_ID", 0, 0, "USB0_ID", NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x218, "USB0_VBUS", 0, 0, "USB0_VBUS", NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x21c, "USB0_DRVVBUS", 18, 7, "USB0_DRVVBUS", NULL, NULL, NULL, NULL, NULL, NULL, "gpio0_18"),
+ _PIN(0x220, "USB1_DM", 0, 0, "USB1_DM", NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x224, "USB1_DP", 0, 0, "USB1_DP", NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x228, "USB1_CE", 0, 0, "USB1_CE", NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x22c, "USB1_ID", 0, 0, "USB1_ID", NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x230, "USB1_VBUS", 0, 0, "USB1_VBUS", NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x234, "USB1_DRVVBUS", 109, 7, "USB1_DRVVBUS", NULL, NULL, NULL, NULL, NULL, NULL, "gpio3_13"),
+#if 0
+ _PIN(0x238, "ddr_resetn", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x23c, "ddr_csn0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x240, "ddr_cke", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x244, "ddr_ck", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x248, "ddr_nck", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x24c, "ddr_casn", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x250, "ddr_rasn", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x254, "ddr_wen", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x258, "ddr_ba0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x25c, "ddr_ba1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x260, "ddr_ba2", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x264, "ddr_a0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x268, "ddr_a1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x26c, "ddr_a2", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x270, "ddr_a3", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x274, "ddr_a4", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x278, "ddr_a5", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x27c, "ddr_a6", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x280, "ddr_a7", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x284, "ddr_a8", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x288, "ddr_a9", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x28c, "ddr_a10", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x290, "ddr_a11", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x294, "ddr_a12", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x298, "ddr_a13", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x29c, "ddr_a14", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x2a0, "ddr_a15", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x2a4, "ddr_odt", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x2a8, "ddr_d0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x2ac, "ddr_d1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x2b0, "ddr_d2", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x2b4, "ddr_d3", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x2b8, "ddr_d4", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x2bc, "ddr_d5", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x2c0, "ddr_d6", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x2c4, "ddr_d7", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x2c8, "ddr_d8", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x2cc, "ddr_d9", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x2d0, "ddr_d10", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x2d4, "ddr_d11", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x2d8, "ddr_d12", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x2dc, "ddr_d13", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x2e0, "ddr_d14", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x2e4, "ddr_d15", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x2e8, "ddr_dqm0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x2ec, "ddr_dqm1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x2f0, "ddr_dqs0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x2f4, "ddr_dqsn0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x2f8, "ddr_dqs1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x2fc, "ddr_dqsn1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x300, "ddr_vref", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x304, "ddr_vtp", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x308, "ddr_strben0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x30c, "ddr_strben1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x32c, "ain0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x328, "ain1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x324, "ain2", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x320, "ain3", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x31c, "ain4", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x318, "ain5", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x314, "ain6", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x310, "ain7", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x330, "vrefp", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x334, "vrefn", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x338, "avdd", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x33c, "avss", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x340, "iforce", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x344, "vsense", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PIN(0x348, "testout", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+#endif
+ { .ballname = NULL },
+};
+
+const struct ti_pinmux_device ti_am335x_pinmux_dev = {
+ .padconf_muxmode_mask = 0x7,
+ .padconf_sate_mask = 0x78,
+ .padstate = ti_padstate_devmap,
+ .padconf = ti_padconf_devmap,
+};
diff --git a/sys/arm/ti/am335x/am335x_scm_padconf.h b/sys/arm/ti/am335x/am335x_scm_padconf.h
new file mode 100644
index 000000000000..b1fa22bac6c7
--- /dev/null
+++ b/sys/arm/ti/am335x/am335x_scm_padconf.h
@@ -0,0 +1,47 @@
+/*-
+ * Copyright (c) 2012 Damjan Marion <dmarion@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef AM335X_SCM_PADCONF_H
+#define AM335X_SCM_PADCONF_H
+
+#define SLEWCTRL (0x01 << 6) /* faster(0) or slower(1) slew rate. */
+#define RXACTIVE (0x01 << 5) /* Input enable value for the Pad */
+#define PULLTYPESEL (0x01 << 4) /* Pad pullup/pulldown type selection */
+#define PULLUDEN (0x01 << 3) /* Pullup/pulldown disabled */
+
+#define PADCONF_OUTPUT (PULLUDEN)
+#define PADCONF_OUTPUT_PULLUP (PULLTYPESEL)
+#define PADCONF_OUTPUT_PULLDOWN (0)
+#define PADCONF_INPUT (RXACTIVE | PULLUDEN)
+#define PADCONF_INPUT_PULLUP (RXACTIVE | PULLTYPESEL)
+#define PADCONF_INPUT_PULLDOWN (RXACTIVE)
+#define PADCONF_INPUT_PULLUP_SLOW (PADCONF_INPUT_PULLUP | SLEWCTRL)
+
+extern const struct ti_pinmux_device ti_am335x_pinmux_dev;
+
+#endif /* AM335X_SCM_PADCONF_H */
diff --git a/sys/arm/ti/am335x/am335x_usb_phy.c b/sys/arm/ti/am335x/am335x_usb_phy.c
new file mode 100644
index 000000000000..00e28122dcec
--- /dev/null
+++ b/sys/arm/ti/am335x/am335x_usb_phy.c
@@ -0,0 +1,121 @@
+/*-
+ *
+ * Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/fbio.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/resource.h>
+#include <machine/bus.h>
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_kern.h>
+#include <vm/pmap.h>
+
+#include <dev/fdt/simplebus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#define TI_AM335X_USB_PHY 1
+#define TI_AM335X_USB_PHY_END 0
+
+static struct ofw_compat_data compat_data[] = {
+ { "ti,am335x-usb-phy", TI_AM335X_USB_PHY },
+ { NULL, TI_AM335X_USB_PHY_END }
+};
+
+struct ti_usb_phy_softc {
+ device_t dev;
+};
+
+static int ti_usb_phy_probe(device_t dev);
+static int ti_usb_phy_attach(device_t dev);
+static int ti_usb_phy_detach(device_t dev);
+
+static int
+ti_usb_phy_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "TI AM335x USB PHY");
+ if (!bootverbose)
+ device_quiet(dev);
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ti_usb_phy_attach(device_t dev)
+{
+ struct ti_usb_phy_softc *sc;
+ phandle_t node;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ node = ofw_bus_get_node(dev);
+
+ /* FIXME: Add dev/extres/phy/ interface */
+
+ return (bus_generic_attach(dev));
+}
+
+static int
+ti_usb_phy_detach(device_t dev)
+{
+ return (EBUSY);
+}
+
+static device_method_t ti_usb_phy_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ti_usb_phy_probe),
+ DEVMETHOD(device_attach, ti_usb_phy_attach),
+ DEVMETHOD(device_detach, ti_usb_phy_detach),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(ti_usb_phy, ti_usb_phy_driver, ti_usb_phy_methods,
+ sizeof(struct ti_usb_phy_softc), simplebus_driver);
+
+static devclass_t ti_usb_phy_devclass;
+
+EARLY_DRIVER_MODULE(ti_usb_phy, simplebus, ti_usb_phy_driver,
+ ti_usb_phy_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_FIRST);
+MODULE_VERSION(ti_usb_phy, 1);
+MODULE_DEPEND(ti_usb_phy, ti_sysc, 1, 1, 1);
diff --git a/sys/arm/ti/am335x/files.am335x b/sys/arm/ti/am335x/files.am335x
new file mode 100644
index 000000000000..4ecdc1e35b43
--- /dev/null
+++ b/sys/arm/ti/am335x/files.am335x
@@ -0,0 +1,25 @@
+#$FreeBSD$
+
+arm/ti/aintc.c standard
+
+arm/ti/am335x/am335x_dmtimer.c standard
+arm/ti/am335x/am335x_dmtpps.c optional am335x_dmtpps
+arm/ti/am335x/am335x_gpio.c optional gpio
+arm/ti/am335x/am335x_lcd.c optional sc | vt
+arm/ti/am335x/am335x_lcd_syscons.c optional sc
+arm/ti/am335x/am335x_pmic.c optional am335x_pmic
+arm/ti/am335x/am335x_pwmss.c standard
+dev/pwm/pwmbus_if.m standard
+arm/ti/am335x/am335x_ehrpwm.c standard
+arm/ti/am335x/am335x_ecap.c standard
+arm/ti/am335x/am335x_rtc.c optional am335x_rtc
+arm/ti/am335x/am335x_scm.c standard
+arm/ti/am335x/am335x_scm_padconf.c standard
+arm/ti/am335x/am335x_musb.c optional musb fdt
+arm/ti/am335x/am335x_usb_phy.c optional musb fdt
+arm/ti/am335x/am3359_cppi41.c optional musb fdt
+
+arm/ti/am335x/tda19988.c optional hdmi
+
+arm/ti/ti_edma3.c standard
+arm/ti/cpsw/if_cpsw.c optional cpsw
diff --git a/sys/arm/ti/am335x/std.am335x b/sys/arm/ti/am335x/std.am335x
new file mode 100644
index 000000000000..98b1d3c310a6
--- /dev/null
+++ b/sys/arm/ti/am335x/std.am335x
@@ -0,0 +1,8 @@
+# AM335x generic configuration
+#$FreeBSD$
+files "../ti/am335x/files.am335x"
+include "../ti/std.ti"
+
+cpu CPU_CORTEXA
+
+options SOC_TI_AM335X
diff --git a/sys/arm/ti/am335x/tda19988.c b/sys/arm/ti/am335x/tda19988.c
new file mode 100644
index 000000000000..0796da96bdd7
--- /dev/null
+++ b/sys/arm/ti/am335x/tda19988.c
@@ -0,0 +1,801 @@
+/*-
+ * Copyright (c) 2015 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+/*
+* NXP TDA19988 HDMI encoder
+*/
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/clock.h>
+#include <sys/eventhandler.h>
+#include <sys/time.h>
+#include <sys/bus.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+
+#include <dev/iicbus/iicbus.h>
+#include <dev/iicbus/iiconf.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/videomode/videomode.h>
+#include <dev/videomode/edidvar.h>
+
+#include "iicbus_if.h"
+#include "hdmi_if.h"
+
+#define MKREG(page, addr) (((page) << 8) | (addr))
+
+#define REGPAGE(reg) (((reg) >> 8) & 0xff)
+#define REGADDR(reg) ((reg) & 0xff)
+
+#define TDA_VERSION MKREG(0x00, 0x00)
+#define TDA_MAIN_CNTRL0 MKREG(0x00, 0x01)
+#define MAIN_CNTRL0_SR (1 << 0)
+#define TDA_VERSION_MSB MKREG(0x00, 0x02)
+#define TDA_SOFTRESET MKREG(0x00, 0x0a)
+#define SOFTRESET_I2C (1 << 1)
+#define SOFTRESET_AUDIO (1 << 0)
+#define TDA_DDC_CTRL MKREG(0x00, 0x0b)
+#define DDC_ENABLE 0
+#define TDA_CCLK MKREG(0x00, 0x0c)
+#define CCLK_ENABLE 1
+#define TDA_INT_FLAGS_2 MKREG(0x00, 0x11)
+#define INT_FLAGS_2_EDID_BLK_RD (1 << 1)
+
+#define TDA_VIP_CNTRL_0 MKREG(0x00, 0x20)
+#define TDA_VIP_CNTRL_1 MKREG(0x00, 0x21)
+#define TDA_VIP_CNTRL_2 MKREG(0x00, 0x22)
+#define TDA_VIP_CNTRL_3 MKREG(0x00, 0x23)
+#define VIP_CNTRL_3_SYNC_HS (2 << 4)
+#define VIP_CNTRL_3_V_TGL (1 << 2)
+#define VIP_CNTRL_3_H_TGL (1 << 1)
+
+#define TDA_VIP_CNTRL_4 MKREG(0x00, 0x24)
+#define VIP_CNTRL_4_BLANKIT_NDE (0 << 2)
+#define VIP_CNTRL_4_BLANKIT_HS_VS (1 << 2)
+#define VIP_CNTRL_4_BLANKIT_NHS_VS (2 << 2)
+#define VIP_CNTRL_4_BLANKIT_HE_VE (3 << 2)
+#define VIP_CNTRL_4_BLC_NONE (0 << 0)
+#define VIP_CNTRL_4_BLC_RGB444 (1 << 0)
+#define VIP_CNTRL_4_BLC_YUV444 (2 << 0)
+#define VIP_CNTRL_4_BLC_YUV422 (3 << 0)
+#define TDA_VIP_CNTRL_5 MKREG(0x00, 0x25)
+#define VIP_CNTRL_5_SP_CNT(n) (((n) & 3) << 1)
+#define TDA_MUX_VP_VIP_OUT MKREG(0x00, 0x27)
+#define TDA_MAT_CONTRL MKREG(0x00, 0x80)
+#define MAT_CONTRL_MAT_BP (1 << 2)
+#define TDA_VIDFORMAT MKREG(0x00, 0xa0)
+#define TDA_REFPIX_MSB MKREG(0x00, 0xa1)
+#define TDA_REFPIX_LSB MKREG(0x00, 0xa2)
+#define TDA_REFLINE_MSB MKREG(0x00, 0xa3)
+#define TDA_REFLINE_LSB MKREG(0x00, 0xa4)
+#define TDA_NPIX_MSB MKREG(0x00, 0xa5)
+#define TDA_NPIX_LSB MKREG(0x00, 0xa6)
+#define TDA_NLINE_MSB MKREG(0x00, 0xa7)
+#define TDA_NLINE_LSB MKREG(0x00, 0xa8)
+#define TDA_VS_LINE_STRT_1_MSB MKREG(0x00, 0xa9)
+#define TDA_VS_LINE_STRT_1_LSB MKREG(0x00, 0xaa)
+#define TDA_VS_PIX_STRT_1_MSB MKREG(0x00, 0xab)
+#define TDA_VS_PIX_STRT_1_LSB MKREG(0x00, 0xac)
+#define TDA_VS_LINE_END_1_MSB MKREG(0x00, 0xad)
+#define TDA_VS_LINE_END_1_LSB MKREG(0x00, 0xae)
+#define TDA_VS_PIX_END_1_MSB MKREG(0x00, 0xaf)
+#define TDA_VS_PIX_END_1_LSB MKREG(0x00, 0xb0)
+#define TDA_VS_LINE_STRT_2_MSB MKREG(0x00, 0xb1)
+#define TDA_VS_LINE_STRT_2_LSB MKREG(0x00, 0xb2)
+#define TDA_VS_PIX_STRT_2_MSB MKREG(0x00, 0xb3)
+#define TDA_VS_PIX_STRT_2_LSB MKREG(0x00, 0xb4)
+#define TDA_VS_LINE_END_2_MSB MKREG(0x00, 0xb5)
+#define TDA_VS_LINE_END_2_LSB MKREG(0x00, 0xb6)
+#define TDA_VS_PIX_END_2_MSB MKREG(0x00, 0xb7)
+#define TDA_VS_PIX_END_2_LSB MKREG(0x00, 0xb8)
+#define TDA_HS_PIX_START_MSB MKREG(0x00, 0xb9)
+#define TDA_HS_PIX_START_LSB MKREG(0x00, 0xba)
+#define TDA_HS_PIX_STOP_MSB MKREG(0x00, 0xbb)
+#define TDA_HS_PIX_STOP_LSB MKREG(0x00, 0xbc)
+#define TDA_VWIN_START_1_MSB MKREG(0x00, 0xbd)
+#define TDA_VWIN_START_1_LSB MKREG(0x00, 0xbe)
+#define TDA_VWIN_END_1_MSB MKREG(0x00, 0xbf)
+#define TDA_VWIN_END_1_LSB MKREG(0x00, 0xc0)
+#define TDA_VWIN_START_2_MSB MKREG(0x00, 0xc1)
+#define TDA_VWIN_START_2_LSB MKREG(0x00, 0xc2)
+#define TDA_VWIN_END_2_MSB MKREG(0x00, 0xc3)
+#define TDA_VWIN_END_2_LSB MKREG(0x00, 0xc4)
+#define TDA_DE_START_MSB MKREG(0x00, 0xc5)
+#define TDA_DE_START_LSB MKREG(0x00, 0xc6)
+#define TDA_DE_STOP_MSB MKREG(0x00, 0xc7)
+#define TDA_DE_STOP_LSB MKREG(0x00, 0xc8)
+
+#define TDA_TBG_CNTRL_0 MKREG(0x00, 0xca)
+#define TBG_CNTRL_0_SYNC_ONCE (1 << 7)
+#define TBG_CNTRL_0_SYNC_MTHD (1 << 6)
+
+#define TDA_TBG_CNTRL_1 MKREG(0x00, 0xcb)
+#define TBG_CNTRL_1_DWIN_DIS (1 << 6)
+#define TBG_CNTRL_1_TGL_EN (1 << 2)
+#define TBG_CNTRL_1_V_TGL (1 << 1)
+#define TBG_CNTRL_1_H_TGL (1 << 0)
+
+#define TDA_HVF_CNTRL_0 MKREG(0x00, 0xe4)
+#define HVF_CNTRL_0_PREFIL_NONE (0 << 2)
+#define HVF_CNTRL_0_INTPOL_BYPASS (0 << 0)
+#define TDA_HVF_CNTRL_1 MKREG(0x00, 0xe5)
+#define HVF_CNTRL_1_VQR(x) (((x) & 3) << 2)
+#define HVF_CNTRL_1_VQR_FULL HVF_CNTRL_1_VQR(0)
+#define TDA_ENABLE_SPACE MKREG(0x00, 0xd6)
+#define TDA_RPT_CNTRL MKREG(0x00, 0xf0)
+
+#define TDA_PLL_SERIAL_1 MKREG(0x02, 0x00)
+#define PLL_SERIAL_1_SRL_MAN_IP (1 << 6)
+#define TDA_PLL_SERIAL_2 MKREG(0x02, 0x01)
+#define PLL_SERIAL_2_SRL_PR(x) (((x) & 0xf) << 4)
+#define PLL_SERIAL_2_SRL_NOSC(x) (((x) & 0x3) << 0)
+#define TDA_PLL_SERIAL_3 MKREG(0x02, 0x02)
+#define PLL_SERIAL_3_SRL_PXIN_SEL (1 << 4)
+#define PLL_SERIAL_3_SRL_DE (1 << 2)
+#define PLL_SERIAL_3_SRL_CCIR (1 << 0)
+#define TDA_SERIALIZER MKREG(0x02, 0x03)
+#define TDA_BUFFER_OUT MKREG(0x02, 0x04)
+#define TDA_PLL_SCG1 MKREG(0x02, 0x05)
+#define TDA_PLL_SCG2 MKREG(0x02, 0x06)
+#define TDA_PLL_SCGN1 MKREG(0x02, 0x07)
+#define TDA_PLL_SCGN2 MKREG(0x02, 0x08)
+#define TDA_PLL_SCGR1 MKREG(0x02, 0x09)
+#define TDA_PLL_SCGR2 MKREG(0x02, 0x0a)
+
+#define TDA_SEL_CLK MKREG(0x02, 0x11)
+#define SEL_CLK_ENA_SC_CLK (1 << 3)
+#define SEL_CLK_SEL_VRF_CLK(x) (((x) & 3) << 1)
+#define SEL_CLK_SEL_CLK1 (1 << 0)
+#define TDA_ANA_GENERAL MKREG(0x02, 0x12)
+
+#define TDA_EDID_DATA0 MKREG(0x09, 0x00)
+#define TDA_EDID_CTRL MKREG(0x09, 0xfa)
+#define TDA_DDC_ADDR MKREG(0x09, 0xfb)
+#define TDA_DDC_OFFS MKREG(0x09, 0xfc)
+#define TDA_DDC_SEGM_ADDR MKREG(0x09, 0xfd)
+#define TDA_DDC_SEGM MKREG(0x09, 0xfe)
+
+#define TDA_IF_VSP MKREG(0x10, 0x20)
+#define TDA_IF_AVI MKREG(0x10, 0x40)
+#define TDA_IF_SPD MKREG(0x10, 0x60)
+#define TDA_IF_AUD MKREG(0x10, 0x80)
+#define TDA_IF_MPS MKREG(0x10, 0xa0)
+
+#define TDA_ENC_CNTRL MKREG(0x11, 0x0d)
+#define ENC_CNTRL_DVI_MODE (0 << 2)
+#define ENC_CNTRL_HDMI_MODE (1 << 2)
+#define TDA_DIP_IF_FLAGS MKREG(0x11, 0x0f)
+#define DIP_IF_FLAGS_IF5 (1 << 5)
+#define DIP_IF_FLAGS_IF4 (1 << 4)
+#define DIP_IF_FLAGS_IF3 (1 << 3)
+#define DIP_IF_FLAGS_IF2 (1 << 2) /* AVI IF on page 10h */
+#define DIP_IF_FLAGS_IF1 (1 << 1)
+
+#define TDA_TX3 MKREG(0x12, 0x9a)
+#define TDA_TX4 MKREG(0x12, 0x9b)
+#define TX4_PD_RAM (1 << 1)
+#define TDA_HDCP_TX33 MKREG(0x12, 0xb8)
+#define HDCP_TX33_HDMI (1 << 1)
+
+#define TDA_CURPAGE_ADDR 0xff
+
+#define TDA_CEC_ENAMODS 0xff
+#define ENAMODS_RXSENS (1 << 2)
+#define ENAMODS_HDMI (1 << 1)
+#define TDA_CEC_FRO_IM_CLK_CTRL 0xfb
+#define CEC_FRO_IM_CLK_CTRL_GHOST_DIS (1 << 7)
+#define CEC_FRO_IM_CLK_CTRL_IMCLK_SEL (1 << 1)
+
+/* EDID reading */
+#define EDID_LENGTH 0x80
+#define MAX_READ_ATTEMPTS 100
+
+/* EDID fields */
+#define EDID_MODES0 35
+#define EDID_MODES1 36
+#define EDID_TIMING_START 38
+#define EDID_TIMING_END 54
+#define EDID_TIMING_X(v) (((v) + 31) * 8)
+#define EDID_FREQ(v) (((v) & 0x3f) + 60)
+#define EDID_RATIO(v) (((v) >> 6) & 0x3)
+#define EDID_RATIO_10x16 0
+#define EDID_RATIO_3x4 1
+#define EDID_RATIO_4x5 2
+#define EDID_RATIO_9x16 3
+
+#define TDA19988 0x0301
+
+struct tda19988_softc {
+ device_t sc_dev;
+ uint32_t sc_addr;
+ uint32_t sc_cec_addr;
+ uint16_t sc_version;
+ int sc_current_page;
+ uint8_t *sc_edid;
+ uint32_t sc_edid_len;
+};
+
+static int
+tda19988_set_page(struct tda19988_softc *sc, uint8_t page)
+{
+ uint8_t addr = TDA_CURPAGE_ADDR;
+ uint8_t cmd[2];
+ int result;
+ struct iic_msg msg[] = {
+ { sc->sc_addr, IIC_M_WR, 2, cmd },
+ };
+
+ cmd[0] = addr;
+ cmd[1] = page;
+
+ result = (iicbus_transfer(sc->sc_dev, msg, 1));
+ if (result)
+ printf("tda19988_set_page failed: %d\n", result);
+ else
+ sc->sc_current_page = page;
+
+ return (result);
+}
+
+static int
+tda19988_cec_read(struct tda19988_softc *sc, uint8_t addr, uint8_t *data)
+{
+ int result;
+ struct iic_msg msg[] = {
+ { sc->sc_cec_addr, IIC_M_WR, 1, &addr },
+ { sc->sc_cec_addr, IIC_M_RD, 1, data },
+ };
+
+ result = iicbus_transfer(sc->sc_dev, msg, 2);
+ if (result)
+ printf("tda19988_cec_read failed: %d\n", result);
+ return (result);
+}
+
+static int
+tda19988_cec_write(struct tda19988_softc *sc, uint8_t address, uint8_t data)
+{
+ uint8_t cmd[2];
+ int result;
+ struct iic_msg msg[] = {
+ { sc->sc_cec_addr, IIC_M_WR, 2, cmd },
+ };
+
+ cmd[0] = address;
+ cmd[1] = data;
+
+ result = iicbus_transfer(sc->sc_dev, msg, 1);
+ if (result)
+ printf("tda19988_cec_write failed: %d\n", result);
+ return (result);
+}
+
+static int
+tda19988_block_read(struct tda19988_softc *sc, uint16_t addr, uint8_t *data, int len)
+{
+ uint8_t reg;
+ int result;
+ struct iic_msg msg[] = {
+ { sc->sc_addr, IIC_M_WR, 1, &reg },
+ { sc->sc_addr, IIC_M_RD, len, data },
+ };
+
+ reg = REGADDR(addr);
+
+ if (sc->sc_current_page != REGPAGE(addr))
+ tda19988_set_page(sc, REGPAGE(addr));
+
+ result = (iicbus_transfer(sc->sc_dev, msg, 2));
+ if (result)
+ device_printf(sc->sc_dev, "tda19988_block_read failed: %d\n", result);
+ return (result);
+}
+
+static int
+tda19988_reg_read(struct tda19988_softc *sc, uint16_t addr, uint8_t *data)
+{
+ uint8_t reg;
+ int result;
+ struct iic_msg msg[] = {
+ { sc->sc_addr, IIC_M_WR, 1, &reg },
+ { sc->sc_addr, IIC_M_RD, 1, data },
+ };
+
+ reg = REGADDR(addr);
+
+ if (sc->sc_current_page != REGPAGE(addr))
+ tda19988_set_page(sc, REGPAGE(addr));
+
+ result = (iicbus_transfer(sc->sc_dev, msg, 2));
+ if (result)
+ device_printf(sc->sc_dev, "tda19988_reg_read failed: %d\n", result);
+ return (result);
+}
+
+static int
+tda19988_reg_write(struct tda19988_softc *sc, uint16_t address, uint8_t data)
+{
+ uint8_t cmd[2];
+ int result;
+ struct iic_msg msg[] = {
+ { sc->sc_addr, IIC_M_WR, 2, cmd },
+ };
+
+ cmd[0] = REGADDR(address);
+ cmd[1] = data;
+
+ if (sc->sc_current_page != REGPAGE(address))
+ tda19988_set_page(sc, REGPAGE(address));
+
+ result = iicbus_transfer(sc->sc_dev, msg, 1);
+ if (result)
+ device_printf(sc->sc_dev, "tda19988_reg_write failed: %d\n", result);
+
+ return (result);
+}
+
+static int
+tda19988_reg_write2(struct tda19988_softc *sc, uint16_t address, uint16_t data)
+{
+ uint8_t cmd[3];
+ int result;
+ struct iic_msg msg[] = {
+ { sc->sc_addr, IIC_M_WR, 3, cmd },
+ };
+
+ cmd[0] = REGADDR(address);
+ cmd[1] = (data >> 8);
+ cmd[2] = (data & 0xff);
+
+ if (sc->sc_current_page != REGPAGE(address))
+ tda19988_set_page(sc, REGPAGE(address));
+
+ result = iicbus_transfer(sc->sc_dev, msg, 1);
+ if (result)
+ device_printf(sc->sc_dev, "tda19988_reg_write2 failed: %d\n", result);
+
+ return (result);
+}
+
+static void
+tda19988_reg_set(struct tda19988_softc *sc, uint16_t addr, uint8_t flags)
+{
+ uint8_t data;
+
+ tda19988_reg_read(sc, addr, &data);
+ data |= flags;
+ tda19988_reg_write(sc, addr, data);
+}
+
+static void
+tda19988_reg_clear(struct tda19988_softc *sc, uint16_t addr, uint8_t flags)
+{
+ uint8_t data;
+
+ tda19988_reg_read(sc, addr, &data);
+ data &= ~flags;
+ tda19988_reg_write(sc, addr, data);
+}
+
+static int
+tda19988_probe(device_t dev)
+{
+
+ if (!ofw_bus_is_compatible(dev, "nxp,tda998x"))
+ return (ENXIO);
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static void
+tda19988_init_encoder(struct tda19988_softc *sc, const struct videomode *mode)
+{
+ uint16_t ref_pix, ref_line, n_pix, n_line;
+ uint16_t hs_pix_start, hs_pix_stop;
+ uint16_t vs1_pix_start, vs1_pix_stop;
+ uint16_t vs1_line_start, vs1_line_end;
+ uint16_t vs2_pix_start, vs2_pix_stop;
+ uint16_t vs2_line_start, vs2_line_end;
+ uint16_t vwin1_line_start, vwin1_line_end;
+ uint16_t vwin2_line_start, vwin2_line_end;
+ uint16_t de_start, de_stop;
+ uint8_t reg, div;
+
+ n_pix = mode->htotal;
+ n_line = mode->vtotal;
+
+ hs_pix_stop = mode->hsync_end - mode->hdisplay;
+ hs_pix_start = mode->hsync_start - mode->hdisplay;
+
+ de_stop = mode->htotal;
+ de_start = mode->htotal - mode->hdisplay;
+ ref_pix = hs_pix_start + 3;
+
+ if (mode->flags & VID_HSKEW)
+ ref_pix += mode->hskew;
+
+ if ((mode->flags & VID_INTERLACE) == 0) {
+ ref_line = 1 + mode->vsync_start - mode->vdisplay;
+ vwin1_line_start = mode->vtotal - mode->vdisplay - 1;
+ vwin1_line_end = vwin1_line_start + mode->vdisplay;
+
+ vs1_pix_start = vs1_pix_stop = hs_pix_start;
+ vs1_line_start = mode->vsync_start - mode->vdisplay;
+ vs1_line_end = vs1_line_start + mode->vsync_end - mode->vsync_start;
+
+ vwin2_line_start = vwin2_line_end = 0;
+ vs2_pix_start = vs2_pix_stop = 0;
+ vs2_line_start = vs2_line_end = 0;
+ } else {
+ ref_line = 1 + (mode->vsync_start - mode->vdisplay)/2;
+ vwin1_line_start = (mode->vtotal - mode->vdisplay)/2;
+ vwin1_line_end = vwin1_line_start + mode->vdisplay/2;
+
+ vs1_pix_start = vs1_pix_stop = hs_pix_start;
+ vs1_line_start = (mode->vsync_start - mode->vdisplay)/2;
+ vs1_line_end = vs1_line_start + (mode->vsync_end - mode->vsync_start)/2;
+
+ vwin2_line_start = vwin1_line_start + mode->vtotal/2;
+ vwin2_line_end = vwin2_line_start + mode->vdisplay/2;
+
+ vs2_pix_start = vs2_pix_stop = hs_pix_start + mode->htotal/2;
+ vs2_line_start = vs1_line_start + mode->vtotal/2 ;
+ vs2_line_end = vs2_line_start + (mode->vsync_end - mode->vsync_start)/2;
+ }
+
+ div = 148500 / mode->dot_clock;
+ if (div != 0) {
+ div--;
+ if (div > 3)
+ div = 3;
+ }
+
+ /* set HDMI HDCP mode off */
+ tda19988_reg_set(sc, TDA_TBG_CNTRL_1, TBG_CNTRL_1_DWIN_DIS);
+ tda19988_reg_clear(sc, TDA_HDCP_TX33, HDCP_TX33_HDMI);
+ tda19988_reg_write(sc, TDA_ENC_CNTRL, ENC_CNTRL_DVI_MODE);
+
+ /* no pre-filter or interpolator */
+ tda19988_reg_write(sc, TDA_HVF_CNTRL_0,
+ HVF_CNTRL_0_INTPOL_BYPASS | HVF_CNTRL_0_PREFIL_NONE);
+ tda19988_reg_write(sc, TDA_VIP_CNTRL_5, VIP_CNTRL_5_SP_CNT(0));
+ tda19988_reg_write(sc, TDA_VIP_CNTRL_4,
+ VIP_CNTRL_4_BLANKIT_NDE | VIP_CNTRL_4_BLC_NONE);
+
+ tda19988_reg_clear(sc, TDA_PLL_SERIAL_3, PLL_SERIAL_3_SRL_CCIR);
+ tda19988_reg_clear(sc, TDA_PLL_SERIAL_1, PLL_SERIAL_1_SRL_MAN_IP);
+ tda19988_reg_clear(sc, TDA_PLL_SERIAL_3, PLL_SERIAL_3_SRL_DE);
+ tda19988_reg_write(sc, TDA_SERIALIZER, 0);
+ tda19988_reg_write(sc, TDA_HVF_CNTRL_1, HVF_CNTRL_1_VQR_FULL);
+
+ tda19988_reg_write(sc, TDA_RPT_CNTRL, 0);
+ tda19988_reg_write(sc, TDA_SEL_CLK, SEL_CLK_SEL_VRF_CLK(0) |
+ SEL_CLK_SEL_CLK1 | SEL_CLK_ENA_SC_CLK);
+
+ tda19988_reg_write(sc, TDA_PLL_SERIAL_2, PLL_SERIAL_2_SRL_NOSC(div) |
+ PLL_SERIAL_2_SRL_PR(0));
+
+ tda19988_reg_set(sc, TDA_MAT_CONTRL, MAT_CONTRL_MAT_BP);
+
+ tda19988_reg_write(sc, TDA_ANA_GENERAL, 0x09);
+
+ tda19988_reg_clear(sc, TDA_TBG_CNTRL_0, TBG_CNTRL_0_SYNC_MTHD);
+
+ /*
+ * Sync on rising HSYNC/VSYNC
+ */
+ reg = VIP_CNTRL_3_SYNC_HS;
+ if (mode->flags & VID_NHSYNC)
+ reg |= VIP_CNTRL_3_H_TGL;
+ if (mode->flags & VID_NVSYNC)
+ reg |= VIP_CNTRL_3_V_TGL;
+ tda19988_reg_write(sc, TDA_VIP_CNTRL_3, reg);
+
+ reg = TBG_CNTRL_1_TGL_EN;
+ if (mode->flags & VID_NHSYNC)
+ reg |= TBG_CNTRL_1_H_TGL;
+ if (mode->flags & VID_NVSYNC)
+ reg |= TBG_CNTRL_1_V_TGL;
+ tda19988_reg_write(sc, TDA_TBG_CNTRL_1, reg);
+
+ /* Program timing */
+ tda19988_reg_write(sc, TDA_VIDFORMAT, 0x00);
+
+ tda19988_reg_write2(sc, TDA_REFPIX_MSB, ref_pix);
+ tda19988_reg_write2(sc, TDA_REFLINE_MSB, ref_line);
+ tda19988_reg_write2(sc, TDA_NPIX_MSB, n_pix);
+ tda19988_reg_write2(sc, TDA_NLINE_MSB, n_line);
+
+ tda19988_reg_write2(sc, TDA_VS_LINE_STRT_1_MSB, vs1_line_start);
+ tda19988_reg_write2(sc, TDA_VS_PIX_STRT_1_MSB, vs1_pix_start);
+ tda19988_reg_write2(sc, TDA_VS_LINE_END_1_MSB, vs1_line_end);
+ tda19988_reg_write2(sc, TDA_VS_PIX_END_1_MSB, vs1_pix_stop);
+ tda19988_reg_write2(sc, TDA_VS_LINE_STRT_2_MSB, vs2_line_start);
+ tda19988_reg_write2(sc, TDA_VS_PIX_STRT_2_MSB, vs2_pix_start);
+ tda19988_reg_write2(sc, TDA_VS_LINE_END_2_MSB, vs2_line_end);
+ tda19988_reg_write2(sc, TDA_VS_PIX_END_2_MSB, vs2_pix_stop);
+ tda19988_reg_write2(sc, TDA_HS_PIX_START_MSB, hs_pix_start);
+ tda19988_reg_write2(sc, TDA_HS_PIX_STOP_MSB, hs_pix_stop);
+ tda19988_reg_write2(sc, TDA_VWIN_START_1_MSB, vwin1_line_start);
+ tda19988_reg_write2(sc, TDA_VWIN_END_1_MSB, vwin1_line_end);
+ tda19988_reg_write2(sc, TDA_VWIN_START_2_MSB, vwin2_line_start);
+ tda19988_reg_write2(sc, TDA_VWIN_END_2_MSB, vwin2_line_end);
+ tda19988_reg_write2(sc, TDA_DE_START_MSB, de_start);
+ tda19988_reg_write2(sc, TDA_DE_STOP_MSB, de_stop);
+
+ if (sc->sc_version == TDA19988)
+ tda19988_reg_write(sc, TDA_ENABLE_SPACE, 0x00);
+
+ /* must be last register set */
+ tda19988_reg_clear(sc, TDA_TBG_CNTRL_0, TBG_CNTRL_0_SYNC_ONCE);
+}
+
+static int
+tda19988_read_edid_block(struct tda19988_softc *sc, uint8_t *buf, int block)
+{
+ int attempt, err;
+ uint8_t data;
+
+ err = 0;
+
+ tda19988_reg_set(sc, TDA_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD);
+
+ /* Block 0 */
+ tda19988_reg_write(sc, TDA_DDC_ADDR, 0xa0);
+ tda19988_reg_write(sc, TDA_DDC_OFFS, (block % 2) ? 128 : 0);
+ tda19988_reg_write(sc, TDA_DDC_SEGM_ADDR, 0x60);
+ tda19988_reg_write(sc, TDA_DDC_SEGM, block / 2);
+
+ tda19988_reg_write(sc, TDA_EDID_CTRL, 1);
+ tda19988_reg_write(sc, TDA_EDID_CTRL, 0);
+
+ data = 0;
+ for (attempt = 0; attempt < MAX_READ_ATTEMPTS; attempt++) {
+ tda19988_reg_read(sc, TDA_INT_FLAGS_2, &data);
+ if (data & INT_FLAGS_2_EDID_BLK_RD)
+ break;
+ pause("EDID", 1);
+ }
+
+ if (attempt == MAX_READ_ATTEMPTS) {
+ err = -1;
+ goto done;
+ }
+
+ if (tda19988_block_read(sc, TDA_EDID_DATA0, buf, EDID_LENGTH) != 0) {
+ err = -1;
+ goto done;
+ }
+
+done:
+ tda19988_reg_clear(sc, TDA_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD);
+
+ return (err);
+}
+
+static int
+tda19988_read_edid(struct tda19988_softc *sc)
+{
+ int err;
+ int blocks, i;
+ uint8_t *buf;
+
+ err = 0;
+ if (sc->sc_version == TDA19988)
+ tda19988_reg_clear(sc, TDA_TX4, TX4_PD_RAM);
+
+ err = tda19988_read_edid_block(sc, sc->sc_edid, 0);
+ if (err)
+ goto done;
+
+ blocks = sc->sc_edid[0x7e];
+ if (blocks > 0) {
+ sc->sc_edid = realloc(sc->sc_edid,
+ EDID_LENGTH*(blocks+1), M_DEVBUF, M_WAITOK);
+ sc->sc_edid_len = EDID_LENGTH*(blocks+1);
+ for (i = 0; i < blocks; i++) {
+ /* TODO: check validity */
+ buf = sc->sc_edid + EDID_LENGTH*(i+1);
+ err = tda19988_read_edid_block(sc, buf, i);
+ if (err)
+ goto done;
+ }
+ }
+
+ EVENTHANDLER_INVOKE(hdmi_event, sc->sc_dev, HDMI_EVENT_CONNECTED);
+done:
+ if (sc->sc_version == TDA19988)
+ tda19988_reg_set(sc, TDA_TX4, TX4_PD_RAM);
+
+ return (err);
+}
+
+static void
+tda19988_start(struct tda19988_softc *sc)
+{
+ device_t dev;
+ uint8_t data;
+ uint16_t version;
+
+ dev = sc->sc_dev;
+
+ tda19988_cec_write(sc, TDA_CEC_ENAMODS, ENAMODS_RXSENS | ENAMODS_HDMI);
+ DELAY(1000);
+ tda19988_cec_read(sc, 0xfe, &data);
+
+ /* Reset core */
+ tda19988_reg_set(sc, TDA_SOFTRESET, 3);
+ DELAY(100);
+ tda19988_reg_clear(sc, TDA_SOFTRESET, 3);
+ DELAY(100);
+
+ /* reset transmitter: */
+ tda19988_reg_set(sc, TDA_MAIN_CNTRL0, MAIN_CNTRL0_SR);
+ tda19988_reg_clear(sc, TDA_MAIN_CNTRL0, MAIN_CNTRL0_SR);
+
+ /* PLL registers common configuration */
+ tda19988_reg_write(sc, TDA_PLL_SERIAL_1, 0x00);
+ tda19988_reg_write(sc, TDA_PLL_SERIAL_2, PLL_SERIAL_2_SRL_NOSC(1));
+ tda19988_reg_write(sc, TDA_PLL_SERIAL_3, 0x00);
+ tda19988_reg_write(sc, TDA_SERIALIZER, 0x00);
+ tda19988_reg_write(sc, TDA_BUFFER_OUT, 0x00);
+ tda19988_reg_write(sc, TDA_PLL_SCG1, 0x00);
+ tda19988_reg_write(sc, TDA_SEL_CLK, SEL_CLK_SEL_CLK1 | SEL_CLK_ENA_SC_CLK);
+ tda19988_reg_write(sc, TDA_PLL_SCGN1, 0xfa);
+ tda19988_reg_write(sc, TDA_PLL_SCGN2, 0x00);
+ tda19988_reg_write(sc, TDA_PLL_SCGR1, 0x5b);
+ tda19988_reg_write(sc, TDA_PLL_SCGR2, 0x00);
+ tda19988_reg_write(sc, TDA_PLL_SCG2, 0x10);
+
+ /* Write the default value MUX register */
+ tda19988_reg_write(sc, TDA_MUX_VP_VIP_OUT, 0x24);
+
+ version = 0;
+ tda19988_reg_read(sc, TDA_VERSION, &data);
+ version |= data;
+ tda19988_reg_read(sc, TDA_VERSION_MSB, &data);
+ version |= (data << 8);
+
+ /* Clear feature bits */
+ sc->sc_version = version & ~0x30;
+ switch (sc->sc_version) {
+ case TDA19988:
+ device_printf(dev, "TDA19988\n");
+ break;
+ default:
+ device_printf(dev, "Unknown device: %04x\n", sc->sc_version);
+ return;
+ }
+
+ tda19988_reg_write(sc, TDA_DDC_CTRL, DDC_ENABLE);
+ tda19988_reg_write(sc, TDA_TX3, 39);
+
+ tda19988_cec_write(sc, TDA_CEC_FRO_IM_CLK_CTRL,
+ CEC_FRO_IM_CLK_CTRL_GHOST_DIS | CEC_FRO_IM_CLK_CTRL_IMCLK_SEL);
+
+ if (tda19988_read_edid(sc) < 0) {
+ device_printf(dev, "failed to read EDID\n");
+ return;
+ }
+
+ /* Default values for RGB 4:4:4 mapping */
+ tda19988_reg_write(sc, TDA_VIP_CNTRL_0, 0x23);
+ tda19988_reg_write(sc, TDA_VIP_CNTRL_1, 0x01);
+ tda19988_reg_write(sc, TDA_VIP_CNTRL_2, 0x45);
+}
+
+static int
+tda19988_attach(device_t dev)
+{
+ struct tda19988_softc *sc;
+ phandle_t node;
+
+ sc = device_get_softc(dev);
+
+ sc->sc_dev = dev;
+ sc->sc_addr = iicbus_get_addr(dev);
+ sc->sc_cec_addr = (0x34 << 1); /* hardcoded */
+ sc->sc_edid = malloc(EDID_LENGTH, M_DEVBUF, M_WAITOK | M_ZERO);
+ sc->sc_edid_len = EDID_LENGTH;
+
+ device_set_desc(dev, "NXP TDA19988 HDMI transmitter");
+
+ node = ofw_bus_get_node(dev);
+ OF_device_register_xref(OF_xref_from_node(node), dev);
+
+ tda19988_start(sc);
+
+ return (0);
+}
+
+static int
+tda19988_detach(device_t dev)
+{
+
+ /* XXX: Do not let unload drive */
+ return (EBUSY);
+}
+
+static int
+tda19988_get_edid(device_t dev, uint8_t **edid, uint32_t *edid_len)
+{
+ struct tda19988_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (sc->sc_edid) {
+ *edid = sc->sc_edid;
+ *edid_len = sc->sc_edid_len;
+ } else
+ return (ENXIO);
+
+ return (0);
+}
+
+static int
+tda19988_set_videomode(device_t dev, const struct videomode *mode)
+{
+ struct tda19988_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ tda19988_init_encoder(sc, mode);
+
+ return (0);
+}
+
+static device_method_t tda_methods[] = {
+ DEVMETHOD(device_probe, tda19988_probe),
+ DEVMETHOD(device_attach, tda19988_attach),
+ DEVMETHOD(device_detach, tda19988_detach),
+
+ /* HDMI methods */
+ DEVMETHOD(hdmi_get_edid, tda19988_get_edid),
+ DEVMETHOD(hdmi_set_videomode, tda19988_set_videomode),
+ {0, 0},
+};
+
+static driver_t tda_driver = {
+ "tda",
+ tda_methods,
+ sizeof(struct tda19988_softc),
+};
+
+static devclass_t tda_devclass;
+
+DRIVER_MODULE(tda, iicbus, tda_driver, tda_devclass, 0, 0);
+MODULE_VERSION(tda, 1);
+MODULE_DEPEND(tda, iicbus, 1, 1, 1);
diff --git a/sys/arm/ti/am335x/tps65217x.h b/sys/arm/ti/am335x/tps65217x.h
new file mode 100644
index 000000000000..cdeca2515df8
--- /dev/null
+++ b/sys/arm/ti/am335x/tps65217x.h
@@ -0,0 +1,114 @@
+/*-
+ * Copyright (c) 2012 Damjan Marion <dmarion@FreeBSD.org>
+ * Copyright (c) 2015 Maksym Sobolyev <sobomax@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __TPS65217X_H__
+#define __TPS65217X_H__
+
+/*
+ * TPS65217 PMIC is a companion chip for AM335x SoC sitting on I2C bus
+ */
+
+/* TPS65217 Registers */
+#define TPS65217_CHIPID_REG 0x00
+struct tps65217_chipid_reg {
+ unsigned int rev:4;
+ unsigned int chip:4;
+#define TPS65217A 0x7
+#define TPS65217B 0xF
+#define TPS65217C 0xE
+#define TPS65217D 0x6
+} __attribute__((__packed__));
+
+#define TPS65217_INT_REG 0x02
+struct tps65217_int_reg {
+ unsigned int usbi:1;
+ unsigned int aci:1;
+ unsigned int pbi:1;
+ unsigned int reserved3:1;
+ unsigned int usbm:1;
+ unsigned int acm:1;
+ unsigned int pbm:1;
+ unsigned int reserved7:1;
+} __attribute__((__packed__));
+
+#define TPS65217_STATUS_REG 0x0A
+struct tps65217_status_reg {
+ unsigned int pb:1;
+ unsigned int reserved1:1;
+ unsigned int usbpwr:1;
+ unsigned int acpwr:1;
+ unsigned int reserved4:3;
+ unsigned int off:1;
+} __attribute__((__packed__));
+
+#define TPS65217_CHGCONFIG0_REG 0x03
+struct tps65217_chgconfig0_reg {
+ unsigned int battemp:1;
+ unsigned int pchgtout:1;
+ unsigned int chgtout:1;
+ unsigned int active:1;
+ unsigned int termi:1;
+ unsigned int tsusp:1;
+ unsigned int dppm:1;
+ unsigned int treg:1;
+} __attribute__((__packed__));
+
+#define TPS65217_CHGCONFIG1_REG 0x04
+struct tps65217_chgconfig1_reg {
+ unsigned int chg_en:1;
+ unsigned int susp:1;
+ unsigned int term:1;
+ unsigned int reset:1;
+ unsigned int ntc_type:1;
+ unsigned int tmr_en:1;
+ unsigned int timer:2;
+} __attribute__((__packed__));
+
+#define TPS65217_CHGCONFIG2_REG 0x05
+struct tps65217_chgconfig2_reg {
+ unsigned int reserved:4;
+ unsigned int voreg:2;
+#define TPS65217_VO_410V 0b00
+#define TPS65217_VO_415V 0b01
+#define TPS65217_VO_420V 0b10
+#define TPS65217_VO_425V 0b11
+ unsigned int vprechg:1;
+ unsigned int dyntmr:1;
+} __attribute__((__packed__));
+
+#define TPS65217_CHGCONFIG3_REG 0x06
+struct tps65217_chgconfig3_reg {
+ unsigned int trange:1;
+ unsigned int termif:2;
+ unsigned int pchrgt:1;
+ unsigned int dppmth:2;
+ unsigned int ichrg:2;
+} __attribute__((__packed__));
+
+#endif /* __TPS65217X_H__ */
diff --git a/sys/arm/ti/clk/clock_common.c b/sys/arm/ti/clk/clock_common.c
new file mode 100644
index 000000000000..15b0e75a8a1e
--- /dev/null
+++ b/sys/arm/ti/clk/clock_common.c
@@ -0,0 +1,152 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/systm.h>
+#include <sys/libkern.h>
+#include <sys/types.h>
+#include <sys/malloc.h>
+
+#include <machine/bus.h>
+#include <dev/fdt/simplebus.h>
+
+#include <dev/extres/clk/clk_mux.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "clock_common.h"
+
+#if 0
+#define DPRINTF(dev, msg...) device_printf(dev, msg)
+#else
+#define DPRINTF(dev, msg...)
+#endif
+
+void
+read_clock_cells(device_t dev, struct clock_cell_info *clk) {
+ ssize_t numbytes_clocks;
+ phandle_t node, parent, *cells;
+ int index, ncells, rv;
+
+ node = ofw_bus_get_node(dev);
+
+ /* Get names of parent clocks */
+ numbytes_clocks = OF_getproplen(node, "clocks");
+ clk->num_clock_cells = numbytes_clocks / sizeof(cell_t);
+
+ /* Allocate space and get clock cells content */
+ /* clock_cells / clock_cells_ncells will be freed in
+ * find_parent_clock_names()
+ */
+ clk->clock_cells = malloc(numbytes_clocks, M_DEVBUF, M_WAITOK|M_ZERO);
+ clk->clock_cells_ncells = malloc(clk->num_clock_cells*sizeof(uint8_t),
+ M_DEVBUF, M_WAITOK|M_ZERO);
+ OF_getencprop(node, "clocks", clk->clock_cells, numbytes_clocks);
+
+ /* Count number of clocks */
+ clk->num_real_clocks = 0;
+ for (index = 0; index < clk->num_clock_cells; index++) {
+ rv = ofw_bus_parse_xref_list_alloc(node, "clocks", "#clock-cells",
+ clk->num_real_clocks, &parent, &ncells, &cells);
+ if (rv != 0)
+ continue;
+
+ if (cells != NULL)
+ OF_prop_free(cells);
+
+ clk->clock_cells_ncells[index] = ncells;
+ index += ncells;
+ clk->num_real_clocks++;
+ }
+}
+
+int
+find_parent_clock_names(device_t dev, struct clock_cell_info *clk, struct clknode_init_def *def) {
+ int index, clock_index, err;
+ bool found_all = true;
+ clk_t parent;
+
+ /* Figure out names */
+ for (index = 0, clock_index = 0; index < clk->num_clock_cells; index++) {
+ /* Get name of parent clock */
+ err = clk_get_by_ofw_index(dev, 0, clock_index, &parent);
+ if (err != 0) {
+ clock_index++;
+ found_all = false;
+ DPRINTF(dev, "Failed to find clock_cells[%d]=0x%x\n",
+ index, clk->clock_cells[index]);
+
+ index += clk->clock_cells_ncells[index];
+ continue;
+ }
+
+ def->parent_names[clock_index] = clk_get_name(parent);
+ clk_release(parent);
+
+ DPRINTF(dev, "Found parent clock[%d/%d]: %s\n",
+ clock_index, clk->num_real_clocks,
+ def->parent_names[clock_index]);
+
+ clock_index++;
+ index += clk->clock_cells_ncells[index];
+ }
+
+ if (!found_all) {
+ return 1;
+ }
+
+ free(clk->clock_cells, M_DEVBUF);
+ free(clk->clock_cells_ncells, M_DEVBUF);
+ return 0;
+}
+
+void
+create_clkdef(device_t dev, struct clock_cell_info *clk, struct clknode_init_def *def) {
+ def->id = 1;
+
+ clk_parse_ofw_clk_name(dev, ofw_bus_get_node(dev), &def->name);
+
+ DPRINTF(dev, "node name: %s\n", def->name);
+
+ def->parent_cnt = clk->num_real_clocks;
+ def->parent_names = malloc(clk->num_real_clocks*sizeof(char *),
+ M_OFWPROP, M_WAITOK);
+}
+void
+free_clkdef(struct clknode_init_def *def) {
+ OF_prop_free(__DECONST(char *, def->name));
+ OF_prop_free(def->parent_names);
+}
diff --git a/sys/arm/ti/clk/clock_common.h b/sys/arm/ti/clk/clock_common.h
new file mode 100644
index 000000000000..148494f90331
--- /dev/null
+++ b/sys/arm/ti/clk/clock_common.h
@@ -0,0 +1,43 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+struct clock_cell_info {
+ cell_t *clock_cells;
+ uint8_t *clock_cells_ncells;
+ uint32_t num_clock_cells;
+ uint8_t num_real_clocks;
+};
+
+void read_clock_cells(device_t dev, struct clock_cell_info *clk);
+int find_parent_clock_names(device_t dev, struct clock_cell_info *clk, struct clknode_init_def *def);
+void create_clkdef(device_t dev, struct clock_cell_info *clk, struct clknode_init_def *def);
+void free_clkdef(struct clknode_init_def *def);
diff --git a/sys/arm/ti/clk/ti_clk_clkctrl.c b/sys/arm/ti/clk/ti_clk_clkctrl.c
new file mode 100644
index 000000000000..6b2fff5e12bb
--- /dev/null
+++ b/sys/arm/ti/clk/ti_clk_clkctrl.c
@@ -0,0 +1,219 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+
+#include <dev/extres/clk/clk.h>
+
+#include <arm/ti/clk/ti_clk_clkctrl.h>
+
+#include "clkdev_if.h"
+
+#if 0
+#define DPRINTF(dev, msg...) device_printf(dev, msg)
+#else
+#define DPRINTF(dev, msg...)
+#endif
+
+/*
+ * clknode for clkctrl, implements gate and mux (for gpioc)
+ */
+
+#define GPIO_X_GDBCLK_MASK 0x00040000
+#define IDLEST_MASK 0x00030000
+#define MODULEMODE_MASK 0x00000003
+
+#define GPIOX_GDBCLK_ENABLE 0x00040000
+#define GPIOX_GDBCLK_DISABLE 0x00000000
+#define IDLEST_FUNC 0x00000000
+#define IDLEST_TRANS 0x00010000
+#define IDLEST_IDLE 0x00020000
+#define IDLEST_DISABLE 0x00030000
+
+#define MODULEMODE_DISABLE 0x0
+#define MODULEMODE_ENABLE 0x2
+
+struct ti_clkctrl_clknode_sc {
+ device_t dev;
+ bool gdbclk;
+ /* omap4-cm range.host + ti,clkctrl reg[0] */
+ uint32_t register_offset;
+};
+
+#define WRITE4(_clk, off, val) \
+ CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)
+#define READ4(_clk, off, val) \
+ CLKDEV_READ_4(clknode_get_device(_clk), off, val)
+#define DEVICE_LOCK(_clk) \
+ CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
+#define DEVICE_UNLOCK(_clk) \
+ CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
+
+static int
+ti_clkctrl_init(struct clknode *clk, device_t dev)
+{
+ struct ti_clkctrl_clknode_sc *sc;
+
+ sc = clknode_get_softc(clk);
+ sc->dev = dev;
+
+ clknode_init_parent_idx(clk, 0);
+ return (0);
+}
+
+static int
+ti_clkctrl_set_gdbclk_gate(struct clknode *clk, bool enable)
+{
+ struct ti_clkctrl_clknode_sc *sc;
+ uint32_t val, gpio_x_gdbclk;
+ uint32_t timeout = 100;
+
+ sc = clknode_get_softc(clk);
+
+ READ4(clk, sc->register_offset, &val);
+ DPRINTF(sc->dev, "val(%x) & (%x | %x = %x)\n",
+ val, GPIO_X_GDBCLK_MASK, MODULEMODE_MASK,
+ GPIO_X_GDBCLK_MASK | MODULEMODE_MASK);
+
+ if (enable) {
+ val = val & MODULEMODE_MASK;
+ val |= GPIOX_GDBCLK_ENABLE;
+ } else {
+ val = val & MODULEMODE_MASK;
+ val |= GPIOX_GDBCLK_DISABLE;
+ }
+
+ DPRINTF(sc->dev, "val %x\n", val);
+ WRITE4(clk, sc->register_offset, val);
+
+ /* Wait */
+ while (timeout) {
+ READ4(clk, sc->register_offset, &val);
+ gpio_x_gdbclk = val & GPIO_X_GDBCLK_MASK;
+ if (enable && (gpio_x_gdbclk == GPIOX_GDBCLK_ENABLE))
+ break;
+ else if (!enable && (gpio_x_gdbclk == GPIOX_GDBCLK_DISABLE))
+ break;
+ DELAY(10);
+ timeout--;
+ }
+ if (timeout == 0) {
+ device_printf(sc->dev, "ti_clkctrl_set_gdbclk_gate: Timeout\n");
+ return (1);
+ }
+
+ return (0);
+}
+
+static int
+ti_clkctrl_set_gate(struct clknode *clk, bool enable)
+{
+ struct ti_clkctrl_clknode_sc *sc;
+ uint32_t val, idlest, module;
+ uint32_t timeout=100;
+ int err;
+
+ sc = clknode_get_softc(clk);
+
+ if (sc->gdbclk) {
+ err = ti_clkctrl_set_gdbclk_gate(clk, enable);
+ return (err);
+ }
+
+ READ4(clk, sc->register_offset, &val);
+
+ if (enable)
+ WRITE4(clk, sc->register_offset, MODULEMODE_ENABLE);
+ else
+ WRITE4(clk, sc->register_offset, MODULEMODE_DISABLE);
+
+ while (timeout) {
+ READ4(clk, sc->register_offset, &val);
+ idlest = val & IDLEST_MASK;
+ module = val & MODULEMODE_MASK;
+ if (enable &&
+ (idlest == IDLEST_FUNC || idlest == IDLEST_TRANS) &&
+ module == MODULEMODE_ENABLE)
+ break;
+ else if (!enable &&
+ idlest == IDLEST_DISABLE &&
+ module == MODULEMODE_DISABLE)
+ break;
+ DELAY(10);
+ timeout--;
+ }
+
+ if (timeout == 0) {
+ device_printf(sc->dev, "ti_clkctrl_set_gate: Timeout\n");
+ return (1);
+ }
+
+ return (0);
+}
+
+static clknode_method_t ti_clkctrl_clknode_methods[] = {
+ /* Device interface */
+ CLKNODEMETHOD(clknode_init, ti_clkctrl_init),
+ CLKNODEMETHOD(clknode_set_gate, ti_clkctrl_set_gate),
+ CLKNODEMETHOD_END
+};
+
+DEFINE_CLASS_1(ti_clkctrl_clknode, ti_clkctrl_clknode_class,
+ ti_clkctrl_clknode_methods, sizeof(struct ti_clkctrl_clknode_sc),
+ clknode_class);
+
+int
+ti_clknode_clkctrl_register(struct clkdom *clkdom,
+ struct ti_clk_clkctrl_def *clkdef)
+{
+ struct clknode *clk;
+ struct ti_clkctrl_clknode_sc *sc;
+
+ clk = clknode_create(clkdom, &ti_clkctrl_clknode_class,
+ &clkdef->clkdef);
+
+ if (clk == NULL) {
+ return (1);
+ }
+
+ sc = clknode_get_softc(clk);
+ sc->register_offset = clkdef->register_offset;
+ sc->gdbclk = clkdef->gdbclk;
+
+ if (clknode_register(clkdom, clk) == NULL) {
+ return (2);
+ }
+ return (0);
+}
diff --git a/sys/arm/ti/clk/ti_clk_clkctrl.h b/sys/arm/ti/clk/ti_clk_clkctrl.h
new file mode 100644
index 000000000000..d78736244815
--- /dev/null
+++ b/sys/arm/ti/clk/ti_clk_clkctrl.h
@@ -0,0 +1,43 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _TI_CLK_CLKCTRL_H_
+#define _TI_CLK_CLKCTRL_H_
+
+#include <dev/extres/clk/clk.h>
+
+struct ti_clk_clkctrl_def {
+ struct clknode_init_def clkdef;
+ bool gdbclk;
+ uint32_t register_offset;
+};
+
+int ti_clknode_clkctrl_register(struct clkdom *clkdom, struct ti_clk_clkctrl_def *clkdef);
+
+#endif /* _TI_CLK_CLKCTRL_H_ */
diff --git a/sys/arm/ti/clk/ti_clk_dpll.c b/sys/arm/ti/clk/ti_clk_dpll.c
new file mode 100644
index 000000000000..ed1c78b19dcb
--- /dev/null
+++ b/sys/arm/ti/clk/ti_clk_dpll.c
@@ -0,0 +1,340 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2019 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * based on sys/arm/allwinner/clkng/aw_clk_np.c
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <dev/extres/clk/clk.h>
+
+#include <arm/ti/clk/ti_clk_dpll.h>
+
+#include "clkdev_if.h"
+
+/*
+ * clknode for clocks matching the formula :
+ *
+ * clk = clkin * n / p
+ *
+ */
+
+struct ti_dpll_clknode_sc {
+ uint32_t ti_clkmode_offset; /* control */
+ uint8_t ti_clkmode_flags;
+
+ uint32_t ti_idlest_offset;
+
+ uint32_t ti_clksel_offset; /* mult-div1 */
+ struct ti_clk_factor n; /* ti_clksel_mult */
+ struct ti_clk_factor p; /* ti_clksel_div */
+
+ uint32_t ti_autoidle_offset;
+};
+
+#define WRITE4(_clk, off, val) \
+ CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)
+#define READ4(_clk, off, val) \
+ CLKDEV_READ_4(clknode_get_device(_clk), off, val)
+#define DEVICE_LOCK(_clk) \
+ CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
+#define DEVICE_UNLOCK(_clk) \
+ CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
+
+static int
+ti_dpll_clk_init(struct clknode *clk, device_t dev)
+{
+ struct ti_dpll_clknode_sc *sc;
+
+ sc = clknode_get_softc(clk);
+
+ clknode_init_parent_idx(clk, 0);
+ return (0);
+}
+
+/* helper to keep aw_clk_np_find_best "intact" */
+static inline uint32_t
+ti_clk_factor_get_max(struct ti_clk_factor *factor)
+{
+ uint32_t max;
+
+ if (factor->flags & TI_CLK_FACTOR_FIXED)
+ max = factor->value;
+ else {
+ max = (1 << factor->width);
+ }
+
+ return (max);
+}
+
+static inline uint32_t
+ti_clk_factor_get_min(struct ti_clk_factor *factor)
+{
+ uint32_t min;
+
+ if (factor->flags & TI_CLK_FACTOR_FIXED)
+ min = factor->value;
+ else if (factor->flags & TI_CLK_FACTOR_ZERO_BASED)
+ min = 0;
+ else if (factor->flags & TI_CLK_FACTOR_MIN_VALUE)
+ min = factor->min_value;
+ else
+ min = 1;
+
+ return (min);
+}
+
+static uint64_t
+ti_dpll_clk_find_best(struct ti_dpll_clknode_sc *sc, uint64_t fparent,
+ uint64_t *fout, uint32_t *factor_n, uint32_t *factor_p)
+{
+ uint64_t cur, best;
+ uint32_t n, p, max_n, max_p, min_n, min_p;
+
+ *factor_n = *factor_p = 0;
+
+ max_n = ti_clk_factor_get_max(&sc->n);
+ max_p = ti_clk_factor_get_max(&sc->p);
+ min_n = ti_clk_factor_get_min(&sc->n);
+ min_p = ti_clk_factor_get_min(&sc->p);
+
+ for (p = min_p; p <= max_p; ) {
+ for (n = min_n; n <= max_n; ) {
+ cur = fparent * n / p;
+ if (abs(*fout - cur) < abs(*fout - best)) {
+ best = cur;
+ *factor_n = n;
+ *factor_p = p;
+ }
+
+ n++;
+ }
+ p++;
+ }
+
+ return (best);
+}
+
+static inline uint32_t
+ti_clk_get_factor(uint32_t val, struct ti_clk_factor *factor)
+{
+ uint32_t factor_val;
+
+ if (factor->flags & TI_CLK_FACTOR_FIXED)
+ return (factor->value);
+
+ factor_val = (val & factor->mask) >> factor->shift;
+ if (!(factor->flags & TI_CLK_FACTOR_ZERO_BASED))
+ factor_val += 1;
+
+ return (factor_val);
+}
+
+static inline uint32_t
+ti_clk_factor_get_value(struct ti_clk_factor *factor, uint32_t raw)
+{
+ uint32_t val;
+
+ if (factor->flags & TI_CLK_FACTOR_FIXED)
+ return (factor->value);
+
+ if (factor->flags & TI_CLK_FACTOR_ZERO_BASED)
+ val = raw;
+ else if (factor->flags & TI_CLK_FACTOR_MAX_VALUE &&
+ raw > factor->max_value)
+ val = factor->max_value;
+ else
+ val = raw - 1;
+
+ return (val);
+}
+
+static int
+ti_dpll_clk_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,
+ int flags, int *stop)
+{
+ struct ti_dpll_clknode_sc *sc;
+ uint64_t cur, best;
+ uint32_t val, n, p, best_n, best_p, timeout;
+
+ sc = clknode_get_softc(clk);
+
+ best = cur = 0;
+
+ best = ti_dpll_clk_find_best(sc, fparent, fout,
+ &best_n, &best_p);
+
+ if ((flags & CLK_SET_DRYRUN) != 0) {
+ *fout = best;
+ *stop = 1;
+ return (0);
+ }
+
+ if ((best < *fout) &&
+ (flags == CLK_SET_ROUND_DOWN)) {
+ *stop = 1;
+ return (ERANGE);
+ }
+ if ((best > *fout) &&
+ (flags == CLK_SET_ROUND_UP)) {
+ *stop = 1;
+ return (ERANGE);
+ }
+
+ DEVICE_LOCK(clk);
+ /* 1 switch PLL to bypass mode */
+ WRITE4(clk, sc->ti_clkmode_offset, DPLL_EN_MN_BYPASS_MODE);
+
+ /* 2 Ensure PLL is in bypass */
+ timeout = 10000;
+ do {
+ DELAY(10);
+ READ4(clk, sc->ti_idlest_offset, &val);
+ } while (!(val & ST_MN_BYPASS_MASK) && timeout--);
+
+ if (timeout == 0) {
+ DEVICE_UNLOCK(clk);
+ return (ERANGE); // FIXME: Better return value?
+ }
+
+ /* 3 Set DPLL_MULT & DPLL_DIV bits */
+ READ4(clk, sc->ti_clksel_offset, &val);
+
+ n = ti_clk_factor_get_value(&sc->n, best_n);
+ p = ti_clk_factor_get_value(&sc->p, best_p);
+ val &= ~sc->n.mask;
+ val &= ~sc->p.mask;
+ val |= n << sc->n.shift;
+ val |= p << sc->p.shift;
+
+ WRITE4(clk, sc->ti_clksel_offset, val);
+
+ /* 4. configure M2, M4, M5 and M6 */
+ /*
+ * FIXME: According to documentation M2/M4/M5/M6 can be set "later"
+ * See note in TRM 8.1.6.7.1
+ */
+
+ /* 5 Switch over to lock mode */
+ WRITE4(clk, sc->ti_clkmode_offset, DPLL_EN_LOCK_MODE);
+
+ /* 6 Ensure PLL is locked */
+ timeout = 10000;
+ do {
+ DELAY(10);
+ READ4(clk, sc->ti_idlest_offset, &val);
+ } while (!(val & ST_DPLL_CLK_MASK) && timeout--);
+
+ DEVICE_UNLOCK(clk);
+ if (timeout == 0) {
+ return (ERANGE); // FIXME: Better return value?
+ }
+
+ *fout = best;
+ *stop = 1;
+
+ return (0);
+}
+
+static int
+ti_dpll_clk_recalc(struct clknode *clk, uint64_t *freq)
+{
+ struct ti_dpll_clknode_sc *sc;
+ uint32_t val, n, p;
+
+ sc = clknode_get_softc(clk);
+
+ DEVICE_LOCK(clk);
+ READ4(clk, sc->ti_clksel_offset, &val);
+ DEVICE_UNLOCK(clk);
+
+ n = ti_clk_get_factor(val, &sc->n);
+ p = ti_clk_get_factor(val, &sc->p);
+
+ *freq = *freq * n / p;
+
+ return (0);
+}
+
+static clknode_method_t ti_dpll_clknode_methods[] = {
+ /* Device interface */
+ CLKNODEMETHOD(clknode_init, ti_dpll_clk_init),
+ CLKNODEMETHOD(clknode_recalc_freq, ti_dpll_clk_recalc),
+ CLKNODEMETHOD(clknode_set_freq, ti_dpll_clk_set_freq),
+ CLKNODEMETHOD_END
+};
+
+DEFINE_CLASS_1(ti_dpll_clknode, ti_dpll_clknode_class, ti_dpll_clknode_methods,
+ sizeof(struct ti_dpll_clknode_sc), clknode_class);
+
+int
+ti_clknode_dpll_register(struct clkdom *clkdom, struct ti_clk_dpll_def *clkdef)
+{
+ struct clknode *clk;
+ struct ti_dpll_clknode_sc *sc;
+
+ clk = clknode_create(clkdom, &ti_dpll_clknode_class, &clkdef->clkdef);
+ if (clk == NULL)
+ return (1);
+
+ sc = clknode_get_softc(clk);
+
+ sc->ti_clkmode_offset = clkdef->ti_clkmode_offset;
+ sc->ti_clkmode_flags = clkdef->ti_clkmode_flags;
+ sc->ti_idlest_offset = clkdef->ti_idlest_offset;
+ sc->ti_clksel_offset = clkdef->ti_clksel_offset;
+
+ sc->n.shift = clkdef->ti_clksel_mult.shift;
+ sc->n.mask = clkdef->ti_clksel_mult.mask;
+ sc->n.width = clkdef->ti_clksel_mult.width;
+ sc->n.value = clkdef->ti_clksel_mult.value;
+ sc->n.min_value = clkdef->ti_clksel_mult.min_value;
+ sc->n.max_value = clkdef->ti_clksel_mult.max_value;
+ sc->n.flags = clkdef->ti_clksel_mult.flags;
+
+ sc->p.shift = clkdef->ti_clksel_div.shift;
+ sc->p.mask = clkdef->ti_clksel_div.mask;
+ sc->p.width = clkdef->ti_clksel_div.width;
+ sc->p.value = clkdef->ti_clksel_div.value;
+ sc->p.min_value = clkdef->ti_clksel_div.min_value;
+ sc->p.max_value = clkdef->ti_clksel_div.max_value;
+ sc->p.flags = clkdef->ti_clksel_div.flags;
+
+ sc->ti_autoidle_offset = clkdef->ti_autoidle_offset;
+
+ clknode_register(clkdom, clk);
+
+ return (0);
+}
diff --git a/sys/arm/ti/clk/ti_clk_dpll.h b/sys/arm/ti/clk/ti_clk_dpll.h
new file mode 100644
index 000000000000..7505ac9abd63
--- /dev/null
+++ b/sys/arm/ti/clk/ti_clk_dpll.h
@@ -0,0 +1,96 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2017 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _TI_DPLL_CLOCK_H_
+#define _TI_DPLL_CLOCK_H_
+
+#include <dev/extres/clk/clk.h>
+
+/* Registers are described in AM335x TRM chapter 8.1.12.2.* */
+
+/* Register offsets */
+#define CM_CLKSEL_DPLL_PERIPH 0x49C
+
+/* CM_IDLEST_DPLL_xxx */
+#define ST_MN_BYPASS_MASK 0x0100
+#define ST_MN_BYPASS_SHIFT 8
+#define ST_DPLL_CLK_MASK 0x0001
+
+/* CM_CLKMODE_DPLL_DPLL_EN feature flag */
+#define LOW_POWER_STOP_MODE_FLAG 0x01
+#define MN_BYPASS_MODE_FLAG 0x02
+#define IDLE_BYPASS_LOW_POWER_MODE_FLAG 0x04
+#define IDLE_BYPASS_FAST_RELOCK_MODE_FLAG 0x08
+#define LOCK_MODE_FLAG 0x10
+
+/* CM_CLKMODE_DPLL_xxx */
+#define DPLL_EN_LOW_POWER_STOP_MODE 0x01
+#define DPLL_EN_MN_BYPASS_MODE 0x04
+#define DPLL_EN_IDLE_BYPASS_LOW_POWER_MODE 0x05
+#define DPLL_EN_IDLE_BYPASS_FAST_RELOCK_MODE 0x06
+#define DPLL_EN_LOCK_MODE 0x07
+
+#define TI_CLK_FACTOR_ZERO_BASED 0x0002
+#define TI_CLK_FACTOR_FIXED 0x0008
+#define TI_CLK_FACTOR_MIN_VALUE 0x0020
+#define TI_CLK_FACTOR_MAX_VALUE 0x0040
+
+/* Based on aw_clk_factor sys/arm/allwinner/clkng/aw_clk.h */
+struct ti_clk_factor {
+ uint32_t shift; /* Shift bits for the factor */
+ uint32_t mask; /* Mask to get the factor */
+ uint32_t width; /* Number of bits for the factor */
+ uint32_t value; /* Fixed value */
+
+ uint32_t min_value;
+ uint32_t max_value;
+
+ uint32_t flags; /* Flags */
+};
+
+struct ti_clk_dpll_def {
+ struct clknode_init_def clkdef;
+
+ uint32_t ti_clkmode_offset; /* control */
+ uint8_t ti_clkmode_flags;
+
+ uint32_t ti_idlest_offset;
+
+ uint32_t ti_clksel_offset; /* mult-div1 */
+ struct ti_clk_factor ti_clksel_mult;
+ struct ti_clk_factor ti_clksel_div;
+
+ uint32_t ti_autoidle_offset;
+};
+
+int ti_clknode_dpll_register(struct clkdom *clkdom, struct ti_clk_dpll_def *clkdef);
+
+#endif /* _TI_DPLL_CLOCK_H_ */
diff --git a/sys/arm/ti/clk/ti_clkctrl.c b/sys/arm/ti/clk/ti_clkctrl.c
new file mode 100644
index 000000000000..ee7d18fb0af8
--- /dev/null
+++ b/sys/arm/ti/clk/ti_clkctrl.c
@@ -0,0 +1,352 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright 2016 Michal Meloun <mmel@FreeBSD.org>
+ *
+ * Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/fbio.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/resource.h>
+#include <machine/bus.h>
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_kern.h>
+#include <vm/pmap.h>
+
+#include <dev/fdt/simplebus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/ti/clk/ti_clk_clkctrl.h>
+#include <arm/ti/ti_omap4_cm.h>
+#include <arm/ti/ti_cpuid.h>
+
+#if 0
+#define DPRINTF(dev, msg...) device_printf(dev, msg)
+#else
+#define DPRINTF(dev, msg...)
+#endif
+
+#define L4LS_CLKCTRL_38 2
+#define L4_WKUP_CLKCTRL_0 1
+#define NO_SPECIAL_REG 0
+
+/* Documentation/devicetree/bindings/clock/ti-clkctrl.txt */
+
+#define TI_CLKCTRL_L4_WKUP 5
+#define TI_CLKCTRL_L4_SECURE 4
+#define TI_CLKCTRL_L4_PER 3
+#define TI_CLKCTRL_L4_CFG 2
+#define TI_CLKCTRL 1
+#define TI_CLKCTRL_END 0
+
+static struct ofw_compat_data compat_data[] = {
+ { "ti,clkctrl-l4-wkup", TI_CLKCTRL_L4_WKUP },
+ { "ti,clkctrl-l4-secure", TI_CLKCTRL_L4_SECURE },
+ { "ti,clkctrl-l4-per", TI_CLKCTRL_L4_PER },
+ { "ti,clkctrl-l4-cfg", TI_CLKCTRL_L4_CFG },
+ { "ti,clkctrl", TI_CLKCTRL },
+ { NULL, TI_CLKCTRL_END }
+};
+
+struct ti_clkctrl_softc {
+ device_t dev;
+
+ struct clkdom *clkdom;
+};
+
+static int ti_clkctrl_probe(device_t dev);
+static int ti_clkctrl_attach(device_t dev);
+static int ti_clkctrl_detach(device_t dev);
+int clkctrl_ofw_map(struct clkdom *clkdom, uint32_t ncells,
+ phandle_t *cells, struct clknode **clk);
+static int
+create_clkctrl(struct ti_clkctrl_softc *sc, cell_t *reg, uint32_t index, uint32_t reg_offset,
+ uint64_t parent_offset, const char *org_name, bool special_gdbclk_reg);
+
+static int
+ti_clkctrl_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "TI clkctrl");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ti_clkctrl_attach(device_t dev)
+{
+ struct ti_clkctrl_softc *sc;
+ phandle_t node;
+ cell_t *reg;
+ ssize_t numbytes_reg;
+ int num_reg, err, ti_clock_cells;
+ uint32_t index, reg_offset, reg_address;
+ const char *org_name;
+ uint64_t parent_offset;
+ uint8_t special_reg = NO_SPECIAL_REG;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ node = ofw_bus_get_node(dev);
+
+ /* Sanity check */
+ err = OF_searchencprop(node, "#clock-cells",
+ &ti_clock_cells, sizeof(ti_clock_cells));
+ if (err == -1) {
+ device_printf(sc->dev, "Failed to get #clock-cells\n");
+ return (ENXIO);
+ }
+
+ if (ti_clock_cells != 2) {
+ device_printf(sc->dev, "clock cells(%d) != 2\n",
+ ti_clock_cells);
+ return (ENXIO);
+ }
+
+ /* Grab the content of reg properties */
+ numbytes_reg = OF_getproplen(node, "reg");
+ if (numbytes_reg == 0) {
+ device_printf(sc->dev, "reg property empty - check your devicetree\n");
+ return (ENXIO);
+ }
+ num_reg = numbytes_reg / sizeof(cell_t);
+
+ reg = malloc(numbytes_reg, M_DEVBUF, M_WAITOK);
+ OF_getencprop(node, "reg", reg, numbytes_reg);
+
+ /* Create clock domain */
+ sc->clkdom = clkdom_create(sc->dev);
+ if (sc->clkdom == NULL) {
+ free(reg, M_DEVBUF);
+ DPRINTF(sc->dev, "Failed to create clkdom\n");
+ return (ENXIO);
+ }
+ clkdom_set_ofw_mapper(sc->clkdom, clkctrl_ofw_map);
+
+ /* Create clock nodes */
+ /* name */
+ clk_parse_ofw_clk_name(sc->dev, node, &org_name);
+
+ /* Get parent range */
+ parent_offset = ti_omap4_cm_get_simplebus_base_host(device_get_parent(dev));
+
+ /* Check if this is a clkctrl with special registers like gpio */
+ switch (ti_chip()) {
+#ifdef SOC_OMAP4
+ case CHIP_OMAP_4:
+ /* FIXME: Todo */
+ break;
+
+#endif /* SOC_OMAP4 */
+#ifdef SOC_TI_AM335X
+ /* Checkout TRM 8.1.12.1.29 - 8.1.12.31 and 8.1.12.2.3
+ * and the DTS.
+ */
+ case CHIP_AM335X:
+ if (strcmp(org_name, "l4ls-clkctrl@38") == 0)
+ special_reg = L4LS_CLKCTRL_38;
+ else if (strcmp(org_name, "l4-wkup-clkctrl@0") == 0)
+ special_reg = L4_WKUP_CLKCTRL_0;
+ break;
+#endif /* SOC_TI_AM335X */
+ default:
+ break;
+ }
+
+ /* reg property has a pair of (base address, length) */
+ for (index = 0; index < num_reg; index += 2) {
+ for (reg_offset = 0; reg_offset < reg[index+1]; reg_offset += sizeof(cell_t)) {
+ err = create_clkctrl(sc, reg, index, reg_offset, parent_offset,
+ org_name, false);
+ if (err)
+ goto cleanup;
+
+ /* Create special clkctrl for GDBCLK in GPIO registers */
+ switch (special_reg) {
+ case NO_SPECIAL_REG:
+ break;
+ case L4LS_CLKCTRL_38:
+ reg_address = reg[index] + reg_offset-reg[0];
+ if (reg_address == 0x74 ||
+ reg_address == 0x78 ||
+ reg_address == 0x7C)
+ {
+ err = create_clkctrl(sc, reg, index, reg_offset,
+ parent_offset, org_name, true);
+ if (err)
+ goto cleanup;
+ }
+ break;
+ case L4_WKUP_CLKCTRL_0:
+ reg_address = reg[index] + reg_offset - reg[0];
+ if (reg_address == 0x8)
+ {
+ err = create_clkctrl(sc, reg, index, reg_offset,
+ parent_offset, org_name, true);
+ if (err)
+ goto cleanup;
+ }
+ break;
+ } /* switch (special_reg) */
+ } /* inner for */
+ } /* for */
+
+ err = clkdom_finit(sc->clkdom);
+ if (err) {
+ DPRINTF(sc->dev, "Clk domain finit fails %x.\n", err);
+ err = ENXIO;
+ goto cleanup;
+ }
+
+cleanup:
+ OF_prop_free(__DECONST(char *, org_name));
+
+ free(reg, M_DEVBUF);
+
+ if (err)
+ return (err);
+
+ return (bus_generic_attach(dev));
+}
+
+static int
+ti_clkctrl_detach(device_t dev)
+{
+ return (EBUSY);
+}
+
+/* modified version of default mapper from clk.c */
+int
+clkctrl_ofw_map(struct clkdom *clkdom, uint32_t ncells,
+ phandle_t *cells, struct clknode **clk) {
+ if (ncells == 0)
+ *clk = clknode_find_by_id(clkdom, 1);
+ else if (ncells == 1)
+ *clk = clknode_find_by_id(clkdom, cells[0]);
+ else if (ncells == 2) {
+ /* To avoid collision with other IDs just add one.
+ * All other registers has an offset of 4 from each other.
+ */
+ if (cells[1])
+ *clk = clknode_find_by_id(clkdom, cells[0]+1);
+ else
+ *clk = clknode_find_by_id(clkdom, cells[0]);
+ }
+ else
+ return (ERANGE);
+
+ if (*clk == NULL)
+ return (ENXIO);
+
+ return (0);
+}
+
+static int
+create_clkctrl(struct ti_clkctrl_softc *sc, cell_t *reg, uint32_t index, uint32_t reg_offset,
+ uint64_t parent_offset, const char *org_name, bool special_gdbclk_reg) {
+ struct ti_clk_clkctrl_def def;
+ char *name;
+ size_t name_len;
+ int err;
+
+ name_len = strlen(org_name) + 1 + 5; /* 5 = _xxxx */
+ name = malloc(name_len, M_OFWPROP, M_WAITOK);
+
+ /*
+ * Check out XX_CLKCTRL-INDEX(offset)-macro dance in
+ * sys/gnu/dts/dts/include/dt-bindings/clock/am3.h
+ * sys/gnu/dts/dts/include/dt-bindings/clock/am4.h
+ * sys/gnu/dts/dts/include/dt-bindings/clock/dra7.h
+ * reg[0] are in practice the same as the offset described in the dts.
+ */
+ /* special_gdbclk_reg are 0 or 1 */
+ def.clkdef.id = reg[index] + reg_offset - reg[0] + special_gdbclk_reg;
+ def.register_offset = parent_offset + reg[index] + reg_offset;
+
+ /* Indicate this clkctrl is special and dont use IDLEST/MODULEMODE */
+ def.gdbclk = special_gdbclk_reg;
+
+ /* Make up an uniq name in the namespace for each clkctrl */
+ snprintf(name, name_len, "%s_%x",
+ org_name, def.clkdef.id);
+ def.clkdef.name = (const char *) name;
+
+ DPRINTF(sc->dev, "ti_clkctrl_attach: reg[%d]: %s %x\n",
+ index, def.clkdef.name, def.clkdef.id);
+
+ /* No parent name */
+ def.clkdef.parent_cnt = 0;
+
+ /* set flags */
+ def.clkdef.flags = 0x0;
+
+ /* Register the clkctrl */
+ err = ti_clknode_clkctrl_register(sc->clkdom, &def);
+ if (err) {
+ DPRINTF(sc->dev,
+ "ti_clknode_clkctrl_register[%d:%d] failed %x\n",
+ index, reg_offset, err);
+ err = ENXIO;
+ }
+ OF_prop_free(name);
+ return (err);
+}
+
+static device_method_t ti_clkctrl_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ti_clkctrl_probe),
+ DEVMETHOD(device_attach, ti_clkctrl_attach),
+ DEVMETHOD(device_detach, ti_clkctrl_detach),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_0(ti_clkctrl, ti_clkctrl_driver, ti_clkctrl_methods,
+ sizeof(struct ti_clkctrl_softc));
+
+static devclass_t ti_clkctrl_devclass;
+
+EARLY_DRIVER_MODULE(ti_clkctrl, simplebus, ti_clkctrl_driver,
+ti_clkctrl_devclass, 0, 0, BUS_PASS_BUS+BUS_PASS_ORDER_MIDDLE);
+
+MODULE_VERSION(ti_clkctrl, 1);
diff --git a/sys/arm/ti/clk/ti_divider_clock.c b/sys/arm/ti/clk/ti_divider_clock.c
new file mode 100644
index 000000000000..753b5f535d29
--- /dev/null
+++ b/sys/arm/ti/clk/ti_divider_clock.c
@@ -0,0 +1,264 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/systm.h>
+#include <sys/libkern.h>
+
+#include <machine/bus.h>
+#include <dev/fdt/simplebus.h>
+
+#include <dev/extres/clk/clk_div.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "clock_common.h"
+
+#if 0
+#define DPRINTF(dev, msg...) device_printf(dev, msg)
+#else
+#define DPRINTF(dev, msg...)
+#endif
+
+/*
+ * Devicetree description
+ * Documentation/devicetree/bindings/clock/ti/divider.txt
+ */
+
+struct ti_divider_softc {
+ device_t sc_dev;
+ bool attach_done;
+ struct clk_div_def div_def;
+
+ struct clock_cell_info clock_cell;
+ struct clkdom *clkdom;
+};
+
+static int ti_divider_probe(device_t dev);
+static int ti_divider_attach(device_t dev);
+static int ti_divider_detach(device_t dev);
+
+#define TI_DIVIDER_CLOCK 2
+#define TI_COMPOSITE_DIVIDER_CLOCK 1
+#define TI_DIVIDER_END 0
+
+static struct ofw_compat_data compat_data[] = {
+ { "ti,divider-clock", TI_DIVIDER_CLOCK },
+ { "ti,composite-divider-clock", TI_COMPOSITE_DIVIDER_CLOCK },
+ { NULL, TI_DIVIDER_END }
+};
+
+static int
+register_clk(struct ti_divider_softc *sc) {
+ int err;
+
+ sc->clkdom = clkdom_create(sc->sc_dev);
+ if (sc->clkdom == NULL) {
+ DPRINTF(sc->sc_dev, "Failed to create clkdom\n");
+ return (ENXIO);
+ }
+
+ err = clknode_div_register(sc->clkdom, &sc->div_def);
+ if (err) {
+ DPRINTF(sc->sc_dev, "clknode_div_register failed %x\n", err);
+ return (ENXIO);
+ }
+
+ err = clkdom_finit(sc->clkdom);
+ if (err) {
+ DPRINTF(sc->sc_dev, "Clk domain finit fails %x.\n", err);
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static int
+ti_divider_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "TI Divider Clock");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ti_divider_attach(device_t dev)
+{
+ struct ti_divider_softc *sc;
+ phandle_t node;
+ int err;
+ cell_t value;
+ uint32_t ti_max_div;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+ node = ofw_bus_get_node(dev);
+
+ /* Grab the content of reg properties */
+ OF_getencprop(node, "reg", &value, sizeof(value));
+ sc->div_def.offset = value;
+
+ if (OF_hasprop(node, "ti,bit-shift")) {
+ OF_getencprop(node, "ti,bit-shift", &value, sizeof(value));
+ sc->div_def.i_shift = value;
+ }
+
+ if (OF_hasprop(node, "ti,index-starts-at-one")) {
+ sc->div_def.div_flags = CLK_DIV_ZERO_BASED;
+ }
+
+ if (OF_hasprop(node, "ti,index-power-of-two")) {
+ /* FIXME: later */
+ device_printf(sc->sc_dev, "ti,index-power-of-two - Not implemented\n");
+ /* remember to update i_width a few lines below */
+ }
+ if (OF_hasprop(node, "ti,max-div")) {
+ OF_getencprop(node, "ti,max-div", &value, sizeof(value));
+ ti_max_div = value;
+ }
+
+ if (OF_hasprop(node, "clock-output-names"))
+ device_printf(sc->sc_dev, "clock-output-names\n");
+ if (OF_hasprop(node, "ti,dividers"))
+ device_printf(sc->sc_dev, "ti,dividers\n");
+ if (OF_hasprop(node, "ti,min-div"))
+ device_printf(sc->sc_dev, "ti,min-div - Not implemented\n");
+
+ if (OF_hasprop(node, "ti,autoidle-shift"))
+ device_printf(sc->sc_dev, "ti,autoidle-shift - Not implemented\n");
+ if (OF_hasprop(node, "ti,set-rate-parent"))
+ device_printf(sc->sc_dev, "ti,set-rate-parent - Not implemented\n");
+ if (OF_hasprop(node, "ti,latch-bit"))
+ device_printf(sc->sc_dev, "ti,latch-bit - Not implemented\n");
+
+ /* Figure out the width from ti_max_div */
+ if (sc->div_def.div_flags)
+ sc->div_def.i_width = fls(ti_max_div-1);
+ else
+ sc->div_def.i_width = fls(ti_max_div);
+
+ DPRINTF(sc->sc_dev, "div_def.i_width %x\n", sc->div_def.i_width);
+
+ read_clock_cells(sc->sc_dev, &sc->clock_cell);
+
+ create_clkdef(sc->sc_dev, &sc->clock_cell, &sc->div_def.clkdef);
+
+ err = find_parent_clock_names(sc->sc_dev, &sc->clock_cell, &sc->div_def.clkdef);
+
+ if (err) {
+ /* free_clkdef will be called in ti_divider_new_pass */
+ DPRINTF(sc->sc_dev, "find_parent_clock_names failed\n");
+ return (bus_generic_attach(sc->sc_dev));
+ }
+
+ err = register_clk(sc);
+
+ if (err) {
+ /* free_clkdef will be called in ti_divider_new_pass */
+ DPRINTF(sc->sc_dev, "register_clk failed\n");
+ return (bus_generic_attach(sc->sc_dev));
+ }
+
+ sc->attach_done = true;
+
+ free_clkdef(&sc->div_def.clkdef);
+
+ return (bus_generic_attach(sc->sc_dev));
+}
+
+static int
+ti_divider_detach(device_t dev)
+{
+ return (EBUSY);
+}
+
+static void
+ti_divider_new_pass(device_t dev)
+{
+ struct ti_divider_softc *sc;
+ int err;
+
+ sc = device_get_softc(dev);
+
+ if (sc->attach_done) {
+ return;
+ }
+
+ err = find_parent_clock_names(sc->sc_dev, &sc->clock_cell, &sc->div_def.clkdef);
+ if (err) {
+ /* free_clkdef will be called in a later call to ti_divider_new_pass */
+ DPRINTF(sc->sc_dev, "new_pass find_parent_clock_names failed\n");
+ return;
+ }
+
+ err = register_clk(sc);
+ if (err) {
+ /* free_clkdef will be called in a later call to ti_divider_new_pass */
+ DPRINTF(sc->sc_dev, "new_pass register_clk failed\n");
+ return;
+ }
+
+ sc->attach_done = true;
+
+ free_clkdef(&sc->div_def.clkdef);
+}
+
+static device_method_t ti_divider_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ti_divider_probe),
+ DEVMETHOD(device_attach, ti_divider_attach),
+ DEVMETHOD(device_detach, ti_divider_detach),
+
+ /* Bus interface */
+ DEVMETHOD(bus_new_pass, ti_divider_new_pass),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_0(ti_divider, ti_divider_driver, ti_divider_methods,
+ sizeof(struct ti_divider_softc));
+
+static devclass_t ti_divider_devclass;
+
+EARLY_DRIVER_MODULE(ti_divider, simplebus, ti_divider_driver,
+ ti_divider_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
+MODULE_VERSION(ti_divider, 1);
diff --git a/sys/arm/ti/clk/ti_dpll_clock.c b/sys/arm/ti/clk/ti_dpll_clock.c
new file mode 100644
index 000000000000..91127c570c4d
--- /dev/null
+++ b/sys/arm/ti/clk/ti_dpll_clock.c
@@ -0,0 +1,375 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/systm.h>
+#include <sys/libkern.h>
+
+#include <machine/bus.h>
+#include <dev/fdt/simplebus.h>
+
+#include <dev/extres/clk/clk_div.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/ti/clk/ti_clk_dpll.h>
+#include "clock_common.h"
+
+#if 0
+#define DPRINTF(dev, msg...) device_printf(dev, msg)
+#else
+#define DPRINTF(dev, msg...)
+#endif
+
+/*
+ * Devicetree description
+ * Documentation/devicetree/bindings/clock/ti/dpll.txt
+ */
+
+struct ti_dpll_softc {
+ device_t dev;
+ uint8_t dpll_type;
+
+ bool attach_done;
+ struct ti_clk_dpll_def dpll_def;
+
+ struct clock_cell_info clock_cell;
+ struct clkdom *clkdom;
+};
+
+static int ti_dpll_probe(device_t dev);
+static int ti_dpll_attach(device_t dev);
+static int ti_dpll_detach(device_t dev);
+
+#define TI_OMAP3_DPLL_CLOCK 17
+#define TI_OMAP3_DPLL_CORE_CLOCK 16
+#define TI_OMAP3_DPLL_PER_CLOCK 15
+#define TI_OMAP3_DPLL_PER_J_TYPE_CLOCK 14
+#define TI_OMAP4_DPLL_CLOCK 13
+#define TI_OMAP4_DPLL_X2_CLOCK 12
+#define TI_OMAP4_DPLL_CORE_CLOCK 11
+#define TI_OMAP4_DPLL_M4XEN_CLOCK 10
+#define TI_OMAP4_DPLL_J_TYPE_CLOCK 9
+#define TI_OMAP5_MPU_DPLL_CLOCK 8
+#define TI_AM3_DPLL_NO_GATE_CLOCK 7
+#define TI_AM3_DPLL_J_TYPE_CLOCK 6
+#define TI_AM3_DPLL_NO_GATE_J_TYPE_CLOCK 5
+#define TI_AM3_DPLL_CLOCK 4
+#define TI_AM3_DPLL_CORE_CLOCK 3
+#define TI_AM3_DPLL_X2_CLOCK 2
+#define TI_OMAP2_DPLL_CORE_CLOCK 1
+#define TI_DPLL_END 0
+
+static struct ofw_compat_data compat_data[] = {
+ { "ti,omap3-dpll-clock", TI_OMAP3_DPLL_CLOCK },
+ { "ti,omap3-dpll-core-clock", TI_OMAP3_DPLL_CORE_CLOCK },
+ { "ti,omap3-dpll-per-clock", TI_OMAP3_DPLL_PER_CLOCK },
+ { "ti,omap3-dpll-per-j-type-clock",TI_OMAP3_DPLL_PER_J_TYPE_CLOCK },
+ { "ti,omap4-dpll-clock", TI_OMAP4_DPLL_CLOCK },
+ { "ti,omap4-dpll-x2-clock", TI_OMAP4_DPLL_X2_CLOCK },
+ { "ti,omap4-dpll-core-clock", TI_OMAP4_DPLL_CORE_CLOCK },
+ { "ti,omap4-dpll-m4xen-clock", TI_OMAP4_DPLL_M4XEN_CLOCK },
+ { "ti,omap4-dpll-j-type-clock", TI_OMAP4_DPLL_J_TYPE_CLOCK },
+ { "ti,omap5-mpu-dpll-clock", TI_OMAP5_MPU_DPLL_CLOCK },
+ { "ti,am3-dpll-no-gate-clock", TI_AM3_DPLL_NO_GATE_CLOCK },
+ { "ti,am3-dpll-j-type-clock", TI_AM3_DPLL_J_TYPE_CLOCK },
+ { "ti,am3-dpll-no-gate-j-type-clock",TI_AM3_DPLL_NO_GATE_J_TYPE_CLOCK },
+ { "ti,am3-dpll-clock", TI_AM3_DPLL_CLOCK },
+ { "ti,am3-dpll-core-clock", TI_AM3_DPLL_CORE_CLOCK },
+ { "ti,am3-dpll-x2-clock", TI_AM3_DPLL_X2_CLOCK },
+ { "ti,omap2-dpll-core-clock", TI_OMAP2_DPLL_CORE_CLOCK },
+ { NULL, TI_DPLL_END }
+};
+
+static int
+register_clk(struct ti_dpll_softc *sc) {
+ int err;
+
+ sc->clkdom = clkdom_create(sc->dev);
+ if (sc->clkdom == NULL) {
+ DPRINTF(sc->dev, "Failed to create clkdom\n");
+ return (ENXIO);
+ }
+
+ err = ti_clknode_dpll_register(sc->clkdom, &sc->dpll_def);
+ if (err) {
+ DPRINTF(sc->dev,
+ "ti_clknode_dpll_register failed %x\n", err);
+ return (ENXIO);
+ }
+
+ err = clkdom_finit(sc->clkdom);
+ if (err) {
+ DPRINTF(sc->dev, "Clk domain finit fails %x.\n", err);
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static int
+ti_dpll_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "TI DPLL Clock");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+parse_dpll_reg(struct ti_dpll_softc *sc) {
+ ssize_t numbytes_regs;
+ uint32_t num_regs;
+ phandle_t node;
+ cell_t reg_cells[4];
+
+ if (sc->dpll_type == TI_AM3_DPLL_X2_CLOCK ||
+ sc->dpll_type == TI_OMAP4_DPLL_X2_CLOCK) {
+ sc->dpll_def.ti_clksel_mult.value = 2;
+ sc->dpll_def.ti_clksel_mult.flags = TI_CLK_FACTOR_FIXED;
+
+ sc->dpll_def.ti_clksel_div.value = 1;
+ sc->dpll_def.ti_clksel_div.flags = TI_CLK_FACTOR_FIXED;
+ return (0);
+ }
+
+ node = ofw_bus_get_node(sc->dev);
+
+ numbytes_regs = OF_getproplen(node, "reg");
+ num_regs = numbytes_regs / sizeof(cell_t);
+
+ /* Sanity check */
+ if (num_regs > 4)
+ return (ENXIO);
+
+ OF_getencprop(node, "reg", reg_cells, numbytes_regs);
+
+ switch (sc->dpll_type) {
+ case TI_AM3_DPLL_NO_GATE_CLOCK:
+ case TI_AM3_DPLL_J_TYPE_CLOCK:
+ case TI_AM3_DPLL_NO_GATE_J_TYPE_CLOCK:
+ case TI_AM3_DPLL_CLOCK:
+ case TI_AM3_DPLL_CORE_CLOCK:
+ case TI_AM3_DPLL_X2_CLOCK:
+ if (num_regs != 3)
+ return (ENXIO);
+ sc->dpll_def.ti_clkmode_offset = reg_cells[0];
+ sc->dpll_def.ti_idlest_offset = reg_cells[1];
+ sc->dpll_def.ti_clksel_offset = reg_cells[2];
+ break;
+
+ case TI_OMAP2_DPLL_CORE_CLOCK:
+ if (num_regs != 2)
+ return (ENXIO);
+ sc->dpll_def.ti_clkmode_offset = reg_cells[0];
+ sc->dpll_def.ti_clksel_offset = reg_cells[1];
+ break;
+
+ default:
+ sc->dpll_def.ti_clkmode_offset = reg_cells[0];
+ sc->dpll_def.ti_idlest_offset = reg_cells[1];
+ sc->dpll_def.ti_clksel_offset = reg_cells[2];
+ sc->dpll_def.ti_autoidle_offset = reg_cells[3];
+ break;
+ }
+
+ /* AM335x */
+ if (sc->dpll_def.ti_clksel_offset == CM_CLKSEL_DPLL_PERIPH) {
+ sc->dpll_def.ti_clksel_mult.shift = 8;
+ sc->dpll_def.ti_clksel_mult.mask = 0x000FFF00;
+ sc->dpll_def.ti_clksel_mult.width = 12;
+ sc->dpll_def.ti_clksel_mult.value = 0;
+ sc->dpll_def.ti_clksel_mult.min_value = 2;
+ sc->dpll_def.ti_clksel_mult.max_value = 4095;
+ sc->dpll_def.ti_clksel_mult.flags = TI_CLK_FACTOR_ZERO_BASED |
+ TI_CLK_FACTOR_MIN_VALUE |
+ TI_CLK_FACTOR_MAX_VALUE;
+
+ sc->dpll_def.ti_clksel_div.shift = 0;
+ sc->dpll_def.ti_clksel_div.mask = 0x000000FF;
+ sc->dpll_def.ti_clksel_div.width = 8;
+ sc->dpll_def.ti_clksel_div.value = 0;
+ sc->dpll_def.ti_clksel_div.min_value = 0;
+ sc->dpll_def.ti_clksel_div.max_value = 255;
+ sc->dpll_def.ti_clksel_div.flags = TI_CLK_FACTOR_MIN_VALUE |
+ TI_CLK_FACTOR_MAX_VALUE;
+ } else {
+ sc->dpll_def.ti_clksel_mult.shift = 8;
+ sc->dpll_def.ti_clksel_mult.mask = 0x0007FF00;
+ sc->dpll_def.ti_clksel_mult.width = 11;
+ sc->dpll_def.ti_clksel_mult.value = 0;
+ sc->dpll_def.ti_clksel_mult.min_value = 2;
+ sc->dpll_def.ti_clksel_mult.max_value = 2047;
+ sc->dpll_def.ti_clksel_mult.flags = TI_CLK_FACTOR_ZERO_BASED |
+ TI_CLK_FACTOR_MIN_VALUE |
+ TI_CLK_FACTOR_MAX_VALUE;
+
+ sc->dpll_def.ti_clksel_div.shift = 0;
+ sc->dpll_def.ti_clksel_div.mask = 0x0000007F;
+ sc->dpll_def.ti_clksel_div.width = 7;
+ sc->dpll_def.ti_clksel_div.value = 0;
+ sc->dpll_def.ti_clksel_div.min_value = 0;
+ sc->dpll_def.ti_clksel_div.max_value = 127;
+ sc->dpll_def.ti_clksel_div.flags = TI_CLK_FACTOR_MIN_VALUE |
+ TI_CLK_FACTOR_MAX_VALUE;
+ }
+ DPRINTF(sc->dev, "clkmode %x idlest %x clksel %x autoidle %x\n",
+ sc->dpll_def.ti_clkmode_offset, sc->dpll_def.ti_idlest_offset,
+ sc->dpll_def.ti_clksel_offset,
+ sc->dpll_def.ti_autoidle_offset);
+
+ return (0);
+}
+static int
+ti_dpll_attach(device_t dev)
+{
+ struct ti_dpll_softc *sc;
+ phandle_t node;
+ int err;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ sc->dpll_type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+ node = ofw_bus_get_node(dev);
+
+ /* Grab the content of reg properties */
+ parse_dpll_reg(sc);
+
+ /* default flags (OMAP4&AM335x) not present in the dts at moment */
+ sc->dpll_def.ti_clkmode_flags = MN_BYPASS_MODE_FLAG | LOCK_MODE_FLAG;
+
+ if (OF_hasprop(node, "ti,low-power-stop")) {
+ sc->dpll_def.ti_clkmode_flags |= LOW_POWER_STOP_MODE_FLAG;
+ }
+ if (OF_hasprop(node, "ti,low-power-bypass")) {
+ sc->dpll_def.ti_clkmode_flags |= IDLE_BYPASS_LOW_POWER_MODE_FLAG;
+ }
+ if (OF_hasprop(node, "ti,lock")) {
+ sc->dpll_def.ti_clkmode_flags |= LOCK_MODE_FLAG;
+ }
+
+ read_clock_cells(sc->dev, &sc->clock_cell);
+
+ create_clkdef(sc->dev, &sc->clock_cell, &sc->dpll_def.clkdef);
+
+ err = find_parent_clock_names(sc->dev, &sc->clock_cell,
+ &sc->dpll_def.clkdef);
+
+ if (err) {
+ /* free_clkdef will be called in ti_dpll_new_pass */
+ DPRINTF(sc->dev, "find_parent_clock_names failed\n");
+ return (bus_generic_attach(sc->dev));
+ }
+
+ err = register_clk(sc);
+
+ if (err) {
+ /* free_clkdef will be called in ti_dpll_new_pass */
+ DPRINTF(sc->dev, "register_clk failed\n");
+ return (bus_generic_attach(sc->dev));
+ }
+
+ sc->attach_done = true;
+
+ free_clkdef(&sc->dpll_def.clkdef);
+
+ return (bus_generic_attach(sc->dev));
+}
+
+static int
+ti_dpll_detach(device_t dev)
+{
+ return (EBUSY);
+}
+
+static void
+ti_dpll_new_pass(device_t dev)
+{
+ struct ti_dpll_softc *sc;
+ int err;
+
+ sc = device_get_softc(dev);
+
+ if (sc->attach_done) {
+ return;
+ }
+
+ err = find_parent_clock_names(sc->dev, &sc->clock_cell,
+ &sc->dpll_def.clkdef);
+ if (err) {
+ /* free_clkdef will be called in a later call to ti_dpll_new_pass */
+ DPRINTF(sc->dev,
+ "new_pass find_parent_clock_names failed\n");
+ return;
+ }
+
+ err = register_clk(sc);
+ if (err) {
+ /* free_clkdef will be called in a later call to ti_dpll_new_pass */
+ DPRINTF(sc->dev, "new_pass register_clk failed\n");
+ return;
+ }
+
+ sc->attach_done = true;
+ free_clkdef(&sc->dpll_def.clkdef);
+}
+
+static device_method_t ti_dpll_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ti_dpll_probe),
+ DEVMETHOD(device_attach, ti_dpll_attach),
+ DEVMETHOD(device_detach, ti_dpll_detach),
+
+ /* Bus interface */
+ DEVMETHOD(bus_new_pass, ti_dpll_new_pass),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_0(ti_dpll, ti_dpll_driver, ti_dpll_methods,
+ sizeof(struct ti_dpll_softc));
+
+static devclass_t ti_dpll_devclass;
+
+EARLY_DRIVER_MODULE(ti_dpll, simplebus, ti_dpll_driver,
+ ti_dpll_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
+MODULE_VERSION(ti_dpll, 1);
diff --git a/sys/arm/ti/clk/ti_gate_clock.c b/sys/arm/ti/clk/ti_gate_clock.c
new file mode 100644
index 000000000000..b4fb65995e74
--- /dev/null
+++ b/sys/arm/ti/clk/ti_gate_clock.c
@@ -0,0 +1,266 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/systm.h>
+#include <sys/libkern.h>
+
+#include <machine/bus.h>
+#include <dev/fdt/simplebus.h>
+
+#include <dev/extres/clk/clk_gate.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "clock_common.h"
+
+#define DEBUG_GATE 0
+
+#if DEBUG_GATE
+#define DPRINTF(dev, msg...) device_printf(dev, msg)
+#else
+#define DPRINTF(dev, msg...)
+#endif
+
+/*
+ * Devicetree description
+ * Documentation/devicetree/bindings/clock/ti/gate.txt
+ */
+
+struct ti_gate_softc {
+ device_t sc_dev;
+ bool attach_done;
+ uint8_t sc_type;
+
+ struct clk_gate_def gate_def;
+ struct clock_cell_info clock_cell;
+ struct clkdom *clkdom;
+};
+
+static int ti_gate_probe(device_t dev);
+static int ti_gate_attach(device_t dev);
+static int ti_gate_detach(device_t dev);
+
+#define TI_GATE_CLOCK 7
+#define TI_WAIT_GATE_CLOCK 6
+#define TI_DSS_GATE_CLOCK 5
+#define TI_AM35XX_GATE_CLOCK 4
+#define TI_CLKDM_GATE_CLOCK 3
+#define TI_HSDIV_GATE_CLOCK 2
+#define TI_COMPOSITE_NO_WAIT_GATE_CLOCK 1
+#define TI_GATE_END 0
+
+static struct ofw_compat_data compat_data[] = {
+ { "ti,gate-clock", TI_GATE_CLOCK },
+ { "ti,wait-gate-clock", TI_WAIT_GATE_CLOCK },
+ { "ti,dss-gate-clock", TI_DSS_GATE_CLOCK },
+ { "ti,am35xx-gate-clock", TI_AM35XX_GATE_CLOCK },
+ { "ti,clkdm-gate-clock", TI_CLKDM_GATE_CLOCK },
+ { "ti,hsdiv-gate-cloc", TI_HSDIV_GATE_CLOCK },
+ { "ti,composite-no-wait-gate-clock", TI_COMPOSITE_NO_WAIT_GATE_CLOCK },
+ { NULL, TI_GATE_END }
+};
+
+static int
+ti_gate_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "TI Gate Clock");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+register_clk(struct ti_gate_softc *sc) {
+ int err;
+ sc->clkdom = clkdom_create(sc->sc_dev);
+ if (sc->clkdom == NULL) {
+ DPRINTF(sc->sc_dev, "Failed to create clkdom\n");
+ return ENXIO;
+ }
+
+ err = clknode_gate_register(sc->clkdom, &sc->gate_def);
+ if (err) {
+ DPRINTF(sc->sc_dev, "clknode_gate_register failed %x\n", err);
+ return ENXIO;
+ }
+
+ err = clkdom_finit(sc->clkdom);
+ if (err) {
+ DPRINTF(sc->sc_dev, "Clk domain finit fails %x.\n", err);
+ return ENXIO;
+ }
+
+ return (0);
+}
+
+static int
+ti_gate_attach(device_t dev)
+{
+ struct ti_gate_softc *sc;
+ phandle_t node;
+ int err;
+ cell_t value;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+ node = ofw_bus_get_node(dev);
+
+ /* Get the compatible type */
+ sc->sc_type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+
+ /* Get the content of reg properties */
+ if (sc->sc_type != TI_CLKDM_GATE_CLOCK) {
+ OF_getencprop(node, "reg", &value, sizeof(value));
+ sc->gate_def.offset = value;
+ }
+#if DEBUG_GATE
+ else {
+ DPRINTF(sc->sc_dev, "no reg (TI_CLKDM_GATE_CLOCK)\n");
+ }
+#endif
+
+ if (OF_hasprop(node, "ti,bit-shift")) {
+ OF_getencprop(node, "ti,bit-shift", &value, sizeof(value));
+ sc->gate_def.shift = value;
+ DPRINTF(sc->sc_dev, "ti,bit-shift => shift %x\n", sc->gate_def.shift);
+ }
+ if (OF_hasprop(node, "ti,set-bit-to-disable")) {
+ sc->gate_def.on_value = 0;
+ sc->gate_def.off_value = 1;
+ DPRINTF(sc->sc_dev,
+ "on_value = 0, off_value = 1 (ti,set-bit-to-disable)\n");
+ } else {
+ sc->gate_def.on_value = 1;
+ sc->gate_def.off_value = 0;
+ DPRINTF(sc->sc_dev, "on_value = 1, off_value = 0\n");
+ }
+
+ sc->gate_def.gate_flags = 0x0;
+
+ read_clock_cells(sc->sc_dev, &sc->clock_cell);
+
+ create_clkdef(sc->sc_dev, &sc->clock_cell, &sc->gate_def.clkdef);
+
+ /* Calculate mask */
+ sc->gate_def.mask = (1 << fls(sc->clock_cell.num_real_clocks)) - 1;
+ DPRINTF(sc->sc_dev, "num_real_clocks %x gate_def.mask %x\n",
+ sc->clock_cell.num_real_clocks, sc->gate_def.mask);
+
+ err = find_parent_clock_names(sc->sc_dev, &sc->clock_cell, &sc->gate_def.clkdef);
+
+ if (err) {
+ /* free_clkdef will be called in ti_gate_new_pass */
+ DPRINTF(sc->sc_dev, "find_parent_clock_names failed\n");
+ return (bus_generic_attach(sc->sc_dev));
+ }
+
+ err = register_clk(sc);
+
+ if (err) {
+ /* free_clkdef will be called in ti_gate_new_pass */
+ DPRINTF(sc->sc_dev, "register_clk failed\n");
+ return (bus_generic_attach(sc->sc_dev));
+ }
+
+ sc->attach_done = true;
+
+ free_clkdef(&sc->gate_def.clkdef);
+
+ return (bus_generic_attach(sc->sc_dev));
+}
+
+static int
+ti_gate_detach(device_t dev)
+{
+ return (EBUSY);
+}
+
+static void
+ti_gate_new_pass(device_t dev) {
+ struct ti_gate_softc *sc;
+ int err;
+
+ sc = device_get_softc(dev);
+
+ if (sc->attach_done) {
+ return;
+ }
+
+ err = find_parent_clock_names(sc->sc_dev, &sc->clock_cell, &sc->gate_def.clkdef);
+ if (err) {
+ /* free_clkdef will be called in later call to ti_gate_new_pass */
+ DPRINTF(sc->sc_dev, "new_pass find_parent_clock_names failed\n");
+ return;
+ }
+
+ err = register_clk(sc);
+ if (err) {
+ /* free_clkdef will be called in later call to ti_gate_new_pass */
+ DPRINTF(sc->sc_dev, "new_pass register_clk failed\n");
+ return;
+ }
+
+ sc->attach_done = true;
+
+ free_clkdef(&sc->gate_def.clkdef);
+}
+
+static device_method_t ti_gate_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ti_gate_probe),
+ DEVMETHOD(device_attach, ti_gate_attach),
+ DEVMETHOD(device_detach, ti_gate_detach),
+
+ /* Bus interface */
+ DEVMETHOD(bus_new_pass, ti_gate_new_pass),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_0(ti_gate, ti_gate_driver, ti_gate_methods,
+ sizeof(struct ti_gate_softc));
+
+static devclass_t ti_gate_devclass;
+
+EARLY_DRIVER_MODULE(ti_gate, simplebus, ti_gate_driver,
+ ti_gate_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
+MODULE_VERSION(ti_gate, 1);
diff --git a/sys/arm/ti/clk/ti_mux_clock.c b/sys/arm/ti/clk/ti_mux_clock.c
new file mode 100644
index 000000000000..bd232290e6a0
--- /dev/null
+++ b/sys/arm/ti/clk/ti_mux_clock.c
@@ -0,0 +1,249 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/systm.h>
+#include <sys/libkern.h>
+#include <sys/types.h>
+#include <sys/malloc.h>
+
+#include <machine/bus.h>
+#include <dev/fdt/simplebus.h>
+
+#include <dev/extres/clk/clk_mux.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "clock_common.h"
+
+#if 0
+#define DPRINTF(dev, msg...) device_printf(dev, msg)
+#else
+#define DPRINTF(dev, msg...)
+#endif
+
+/*
+ * Devicetree description
+ * Documentation/devicetree/bindings/clock/ti/mux.txt
+ */
+
+struct ti_mux_softc {
+ device_t sc_dev;
+ bool attach_done;
+
+ struct clk_mux_def mux_def;
+ struct clock_cell_info clock_cell;
+ struct clkdom *clkdom;
+};
+
+static int ti_mux_probe(device_t dev);
+static int ti_mux_attach(device_t dev);
+static int ti_mux_detach(device_t dev);
+
+#define TI_MUX_CLOCK 2
+#define TI_COMPOSITE_MUX_CLOCK 1
+#define TI_MUX_END 0
+
+static struct ofw_compat_data compat_data[] = {
+ { "ti,mux-clock", TI_MUX_CLOCK },
+ { "ti,composite-mux-clock", TI_COMPOSITE_MUX_CLOCK },
+ { NULL, TI_MUX_END }
+};
+
+static int
+ti_mux_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "TI Mux Clock");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+register_clk(struct ti_mux_softc *sc) {
+ int err;
+
+ sc->clkdom = clkdom_create(sc->sc_dev);
+ if (sc->clkdom == NULL) {
+ DPRINTF(sc->sc_dev, "Failed to create clkdom\n");
+ return ENXIO;
+ }
+
+ err = clknode_mux_register(sc->clkdom, &sc->mux_def);
+ if (err) {
+ DPRINTF(sc->sc_dev, "clknode_mux_register failed %x\n", err);
+ return ENXIO;
+ }
+
+ err = clkdom_finit(sc->clkdom);
+ if (err) {
+ DPRINTF(sc->sc_dev, "Clk domain finit fails %x.\n", err);
+ return ENXIO;
+ }
+
+ return 0;
+}
+
+static int
+ti_mux_attach(device_t dev)
+{
+ struct ti_mux_softc *sc;
+ phandle_t node;
+ int err;
+ cell_t value;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+ node = ofw_bus_get_node(dev);
+
+ /* Grab the content of reg properties */
+ OF_getencprop(node, "reg", &value, sizeof(value));
+ sc->mux_def.offset = value;
+
+ if (OF_hasprop(node, "ti,bit-shift")) {
+ OF_getencprop(node, "ti,bit-shift", &value, sizeof(value));
+ sc->mux_def.shift = value;
+ DPRINTF(sc->sc_dev, "ti,bit-shift => shift %x\n", sc->mux_def.shift);
+ }
+ if (OF_hasprop(node, "ti,index-starts-at-one")) {
+ /* FIXME: Add support in dev/extres/clk */
+ /*sc->mux_def.mux_flags = ... */
+ device_printf(sc->sc_dev, "ti,index-starts-at-one - Not implemented\n");
+ }
+
+ if (OF_hasprop(node, "ti,set-rate-parent"))
+ device_printf(sc->sc_dev, "ti,set-rate-parent - Not implemented\n");
+ if (OF_hasprop(node, "ti,latch-bit"))
+ device_printf(sc->sc_dev, "ti,latch-bit - Not implemented\n");
+
+ read_clock_cells(sc->sc_dev, &sc->clock_cell);
+
+ create_clkdef(sc->sc_dev, &sc->clock_cell, &sc->mux_def.clkdef);
+
+ /* Figure out the width from ti_max_div */
+ if (sc->mux_def.mux_flags)
+ sc->mux_def.width = fls(sc->clock_cell.num_real_clocks-1);
+ else
+ sc->mux_def.width = fls(sc->clock_cell.num_real_clocks);
+
+ DPRINTF(sc->sc_dev, "sc->clock_cell.num_real_clocks %x def.width %x\n",
+ sc->clock_cell.num_real_clocks, sc->mux_def.width);
+
+ err = find_parent_clock_names(sc->sc_dev, &sc->clock_cell, &sc->mux_def.clkdef);
+
+ if (err) {
+ /* free_clkdef will be called in ti_mux_new_pass */
+ DPRINTF(sc->sc_dev, "find_parent_clock_names failed\n");
+ return (bus_generic_attach(sc->sc_dev));
+ }
+
+ err = register_clk(sc);
+
+ if (err) {
+ /* free_clkdef will be called in ti_mux_new_pass */
+ DPRINTF(sc->sc_dev, "register_clk failed\n");
+ return (bus_generic_attach(sc->sc_dev));
+ }
+
+ sc->attach_done = true;
+
+ free_clkdef(&sc->mux_def.clkdef);
+
+ return (bus_generic_attach(sc->sc_dev));
+}
+
+static void
+ti_mux_new_pass(device_t dev)
+{
+ struct ti_mux_softc *sc;
+ int err;
+
+ sc = device_get_softc(dev);
+
+ if (sc->attach_done) {
+ return;
+ }
+
+ err = find_parent_clock_names(sc->sc_dev, &sc->clock_cell, &sc->mux_def.clkdef);
+ if (err) {
+ /* free_clkdef will be called in later call to ti_mux_new_pass */
+ DPRINTF(sc->sc_dev, "ti_mux_new_pass find_parent_clock_names failed\n");
+ return;
+ }
+
+ err = register_clk(sc);
+ if (err) {
+ /* free_clkdef will be called in later call to ti_mux_new_pass */
+ DPRINTF(sc->sc_dev, "ti_mux_new_pass register_clk failed\n");
+ return;
+ }
+
+ sc->attach_done = true;
+
+ free_clkdef(&sc->mux_def.clkdef);
+}
+
+static int
+ti_mux_detach(device_t dev)
+{
+ return (EBUSY);
+}
+
+static device_method_t ti_mux_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ti_mux_probe),
+ DEVMETHOD(device_attach, ti_mux_attach),
+ DEVMETHOD(device_detach, ti_mux_detach),
+
+ /* Bus interface */
+ DEVMETHOD(bus_new_pass, ti_mux_new_pass),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_0(ti_mux, ti_mux_driver, ti_mux_methods,
+ sizeof(struct ti_mux_softc));
+
+static devclass_t ti_mux_devclass;
+
+EARLY_DRIVER_MODULE(ti_mux, simplebus, ti_mux_driver,
+ ti_mux_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
+MODULE_VERSION(ti_mux, 1);
diff --git a/sys/arm/ti/cpsw/if_cpsw.c b/sys/arm/ti/cpsw/if_cpsw.c
new file mode 100644
index 000000000000..ebf5644883f7
--- /dev/null
+++ b/sys/arm/ti/cpsw/if_cpsw.c
@@ -0,0 +1,3028 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Damjan Marion <dmarion@Freebsd.org>
+ * Copyright (c) 2016 Rubicon Communications, LLC (Netgate)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * TI Common Platform Ethernet Switch (CPSW) Driver
+ * Found in TI8148 "DaVinci" and AM335x "Sitara" SoCs.
+ *
+ * This controller is documented in the AM335x Technical Reference
+ * Manual, in the TMS320DM814x DaVinci Digital Video Processors TRM
+ * and in the TMS320C6452 3 Port Switch Ethernet Subsystem TRM.
+ *
+ * It is basically a single Ethernet port (port 0) wired internally to
+ * a 3-port store-and-forward switch connected to two independent
+ * "sliver" controllers (port 1 and port 2). You can operate the
+ * controller in a variety of different ways by suitably configuring
+ * the slivers and the Address Lookup Engine (ALE) that routes packets
+ * between the ports.
+ *
+ * This code was developed and tested on a BeagleBone with
+ * an AM335x SoC.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_cpsw.h"
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/mbuf.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/stdarg.h>
+
+#include <net/ethernet.h>
+#include <net/bpf.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#include <dev/extres/syscon/syscon.h>
+#include "syscon_if.h"
+#include <arm/ti/am335x/am335x_scm.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/fdt/fdt_common.h>
+
+#ifdef CPSW_ETHERSWITCH
+#include <dev/etherswitch/etherswitch.h>
+#include "etherswitch_if.h"
+#endif
+
+#include "if_cpswreg.h"
+#include "if_cpswvar.h"
+
+#include "miibus_if.h"
+
+/* Device probe/attach/detach. */
+static int cpsw_probe(device_t);
+static int cpsw_attach(device_t);
+static int cpsw_detach(device_t);
+static int cpswp_probe(device_t);
+static int cpswp_attach(device_t);
+static int cpswp_detach(device_t);
+
+static phandle_t cpsw_get_node(device_t, device_t);
+
+/* Device Init/shutdown. */
+static int cpsw_shutdown(device_t);
+static void cpswp_init(void *);
+static void cpswp_init_locked(void *);
+static void cpswp_stop_locked(struct cpswp_softc *);
+
+/* Device Suspend/Resume. */
+static int cpsw_suspend(device_t);
+static int cpsw_resume(device_t);
+
+/* Ioctl. */
+static int cpswp_ioctl(struct ifnet *, u_long command, caddr_t data);
+
+static int cpswp_miibus_readreg(device_t, int phy, int reg);
+static int cpswp_miibus_writereg(device_t, int phy, int reg, int value);
+static void cpswp_miibus_statchg(device_t);
+
+/* Send/Receive packets. */
+static void cpsw_intr_rx(void *arg);
+static struct mbuf *cpsw_rx_dequeue(struct cpsw_softc *);
+static void cpsw_rx_enqueue(struct cpsw_softc *);
+static void cpswp_start(struct ifnet *);
+static void cpsw_intr_tx(void *);
+static void cpswp_tx_enqueue(struct cpswp_softc *);
+static int cpsw_tx_dequeue(struct cpsw_softc *);
+
+/* Misc interrupts and watchdog. */
+static void cpsw_intr_rx_thresh(void *);
+static void cpsw_intr_misc(void *);
+static void cpswp_tick(void *);
+static void cpswp_ifmedia_sts(struct ifnet *, struct ifmediareq *);
+static int cpswp_ifmedia_upd(struct ifnet *);
+static void cpsw_tx_watchdog(void *);
+
+/* ALE support */
+static void cpsw_ale_read_entry(struct cpsw_softc *, uint16_t, uint32_t *);
+static void cpsw_ale_write_entry(struct cpsw_softc *, uint16_t, uint32_t *);
+static int cpsw_ale_mc_entry_set(struct cpsw_softc *, uint8_t, int, uint8_t *);
+static void cpsw_ale_dump_table(struct cpsw_softc *);
+static int cpsw_ale_update_vlan_table(struct cpsw_softc *, int, int, int, int,
+ int);
+static int cpswp_ale_update_addresses(struct cpswp_softc *, int);
+
+/* Statistics and sysctls. */
+static void cpsw_add_sysctls(struct cpsw_softc *);
+static void cpsw_stats_collect(struct cpsw_softc *);
+static int cpsw_stats_sysctl(SYSCTL_HANDLER_ARGS);
+
+#ifdef CPSW_ETHERSWITCH
+static etherswitch_info_t *cpsw_getinfo(device_t);
+static int cpsw_getport(device_t, etherswitch_port_t *);
+static int cpsw_setport(device_t, etherswitch_port_t *);
+static int cpsw_getconf(device_t, etherswitch_conf_t *);
+static int cpsw_getvgroup(device_t, etherswitch_vlangroup_t *);
+static int cpsw_setvgroup(device_t, etherswitch_vlangroup_t *);
+static int cpsw_readreg(device_t, int);
+static int cpsw_writereg(device_t, int, int);
+static int cpsw_readphy(device_t, int, int);
+static int cpsw_writephy(device_t, int, int, int);
+#endif
+
+/*
+ * Arbitrary limit on number of segments in an mbuf to be transmitted.
+ * Packets with more segments than this will be defragmented before
+ * they are queued.
+ */
+#define CPSW_TXFRAGS 16
+
+/* Shared resources. */
+static device_method_t cpsw_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, cpsw_probe),
+ DEVMETHOD(device_attach, cpsw_attach),
+ DEVMETHOD(device_detach, cpsw_detach),
+ DEVMETHOD(device_shutdown, cpsw_shutdown),
+ DEVMETHOD(device_suspend, cpsw_suspend),
+ DEVMETHOD(device_resume, cpsw_resume),
+ /* Bus interface */
+ DEVMETHOD(bus_add_child, device_add_child_ordered),
+ /* OFW methods */
+ DEVMETHOD(ofw_bus_get_node, cpsw_get_node),
+#ifdef CPSW_ETHERSWITCH
+ /* etherswitch interface */
+ DEVMETHOD(etherswitch_getinfo, cpsw_getinfo),
+ DEVMETHOD(etherswitch_readreg, cpsw_readreg),
+ DEVMETHOD(etherswitch_writereg, cpsw_writereg),
+ DEVMETHOD(etherswitch_readphyreg, cpsw_readphy),
+ DEVMETHOD(etherswitch_writephyreg, cpsw_writephy),
+ DEVMETHOD(etherswitch_getport, cpsw_getport),
+ DEVMETHOD(etherswitch_setport, cpsw_setport),
+ DEVMETHOD(etherswitch_getvgroup, cpsw_getvgroup),
+ DEVMETHOD(etherswitch_setvgroup, cpsw_setvgroup),
+ DEVMETHOD(etherswitch_getconf, cpsw_getconf),
+#endif
+ DEVMETHOD_END
+};
+
+static driver_t cpsw_driver = {
+ "cpswss",
+ cpsw_methods,
+ sizeof(struct cpsw_softc),
+};
+
+static devclass_t cpsw_devclass;
+
+DRIVER_MODULE(cpswss, simplebus, cpsw_driver, cpsw_devclass, 0, 0);
+
+/* Port/Slave resources. */
+static device_method_t cpswp_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, cpswp_probe),
+ DEVMETHOD(device_attach, cpswp_attach),
+ DEVMETHOD(device_detach, cpswp_detach),
+ /* MII interface */
+ DEVMETHOD(miibus_readreg, cpswp_miibus_readreg),
+ DEVMETHOD(miibus_writereg, cpswp_miibus_writereg),
+ DEVMETHOD(miibus_statchg, cpswp_miibus_statchg),
+ DEVMETHOD_END
+};
+
+static driver_t cpswp_driver = {
+ "cpsw",
+ cpswp_methods,
+ sizeof(struct cpswp_softc),
+};
+
+static devclass_t cpswp_devclass;
+
+#ifdef CPSW_ETHERSWITCH
+DRIVER_MODULE(etherswitch, cpswss, etherswitch_driver, etherswitch_devclass, 0, 0);
+MODULE_DEPEND(cpswss, etherswitch, 1, 1, 1);
+#endif
+
+DRIVER_MODULE(cpsw, cpswss, cpswp_driver, cpswp_devclass, 0, 0);
+DRIVER_MODULE(miibus, cpsw, miibus_driver, miibus_devclass, 0, 0);
+MODULE_DEPEND(cpsw, ether, 1, 1, 1);
+MODULE_DEPEND(cpsw, miibus, 1, 1, 1);
+
+#ifdef CPSW_ETHERSWITCH
+static struct cpsw_vlangroups cpsw_vgroups[CPSW_VLANS];
+#endif
+
+static uint32_t slave_mdio_addr[] = { 0x4a100200, 0x4a100300 };
+
+static struct resource_spec irq_res_spec[] = {
+ { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE },
+ { SYS_RES_IRQ, 1, RF_ACTIVE | RF_SHAREABLE },
+ { SYS_RES_IRQ, 2, RF_ACTIVE | RF_SHAREABLE },
+ { SYS_RES_IRQ, 3, RF_ACTIVE | RF_SHAREABLE },
+ { -1, 0 }
+};
+
+static struct {
+ void (*cb)(void *);
+} cpsw_intr_cb[] = {
+ { cpsw_intr_rx_thresh },
+ { cpsw_intr_rx },
+ { cpsw_intr_tx },
+ { cpsw_intr_misc },
+};
+
+/* Number of entries here must match size of stats
+ * array in struct cpswp_softc. */
+static struct cpsw_stat {
+ int reg;
+ char *oid;
+} cpsw_stat_sysctls[CPSW_SYSCTL_COUNT] = {
+ {0x00, "GoodRxFrames"},
+ {0x04, "BroadcastRxFrames"},
+ {0x08, "MulticastRxFrames"},
+ {0x0C, "PauseRxFrames"},
+ {0x10, "RxCrcErrors"},
+ {0x14, "RxAlignErrors"},
+ {0x18, "OversizeRxFrames"},
+ {0x1c, "RxJabbers"},
+ {0x20, "ShortRxFrames"},
+ {0x24, "RxFragments"},
+ {0x30, "RxOctets"},
+ {0x34, "GoodTxFrames"},
+ {0x38, "BroadcastTxFrames"},
+ {0x3c, "MulticastTxFrames"},
+ {0x40, "PauseTxFrames"},
+ {0x44, "DeferredTxFrames"},
+ {0x48, "CollisionsTxFrames"},
+ {0x4c, "SingleCollisionTxFrames"},
+ {0x50, "MultipleCollisionTxFrames"},
+ {0x54, "ExcessiveCollisions"},
+ {0x58, "LateCollisions"},
+ {0x5c, "TxUnderrun"},
+ {0x60, "CarrierSenseErrors"},
+ {0x64, "TxOctets"},
+ {0x68, "RxTx64OctetFrames"},
+ {0x6c, "RxTx65to127OctetFrames"},
+ {0x70, "RxTx128to255OctetFrames"},
+ {0x74, "RxTx256to511OctetFrames"},
+ {0x78, "RxTx512to1024OctetFrames"},
+ {0x7c, "RxTx1024upOctetFrames"},
+ {0x80, "NetOctets"},
+ {0x84, "RxStartOfFrameOverruns"},
+ {0x88, "RxMiddleOfFrameOverruns"},
+ {0x8c, "RxDmaOverruns"}
+};
+
+/*
+ * Basic debug support.
+ */
+
+static void
+cpsw_debugf_head(const char *funcname)
+{
+ int t = (int)(time_second % (24 * 60 * 60));
+
+ printf("%02d:%02d:%02d %s ", t / (60 * 60), (t / 60) % 60, t % 60, funcname);
+}
+
+static void
+cpsw_debugf(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ printf("\n");
+
+}
+
+#define CPSW_DEBUGF(_sc, a) do { \
+ if ((_sc)->debug) { \
+ cpsw_debugf_head(__func__); \
+ cpsw_debugf a; \
+ } \
+} while (0)
+
+/*
+ * Locking macros
+ */
+#define CPSW_TX_LOCK(sc) do { \
+ mtx_assert(&(sc)->rx.lock, MA_NOTOWNED); \
+ mtx_lock(&(sc)->tx.lock); \
+} while (0)
+
+#define CPSW_TX_UNLOCK(sc) mtx_unlock(&(sc)->tx.lock)
+#define CPSW_TX_LOCK_ASSERT(sc) mtx_assert(&(sc)->tx.lock, MA_OWNED)
+
+#define CPSW_RX_LOCK(sc) do { \
+ mtx_assert(&(sc)->tx.lock, MA_NOTOWNED); \
+ mtx_lock(&(sc)->rx.lock); \
+} while (0)
+
+#define CPSW_RX_UNLOCK(sc) mtx_unlock(&(sc)->rx.lock)
+#define CPSW_RX_LOCK_ASSERT(sc) mtx_assert(&(sc)->rx.lock, MA_OWNED)
+
+#define CPSW_PORT_LOCK(_sc) do { \
+ mtx_assert(&(_sc)->lock, MA_NOTOWNED); \
+ mtx_lock(&(_sc)->lock); \
+} while (0)
+
+#define CPSW_PORT_UNLOCK(_sc) mtx_unlock(&(_sc)->lock)
+#define CPSW_PORT_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->lock, MA_OWNED)
+
+/*
+ * Read/Write macros
+ */
+#define cpsw_read_4(_sc, _reg) bus_read_4((_sc)->mem_res, (_reg))
+#define cpsw_write_4(_sc, _reg, _val) \
+ bus_write_4((_sc)->mem_res, (_reg), (_val))
+
+#define cpsw_cpdma_bd_offset(i) (CPSW_CPPI_RAM_OFFSET + ((i)*16))
+
+#define cpsw_cpdma_bd_paddr(sc, slot) \
+ BUS_SPACE_PHYSADDR(sc->mem_res, slot->bd_offset)
+#define cpsw_cpdma_read_bd(sc, slot, val) \
+ bus_read_region_4(sc->mem_res, slot->bd_offset, (uint32_t *) val, 4)
+#define cpsw_cpdma_write_bd(sc, slot, val) \
+ bus_write_region_4(sc->mem_res, slot->bd_offset, (uint32_t *) val, 4)
+#define cpsw_cpdma_write_bd_next(sc, slot, next_slot) \
+ cpsw_write_4(sc, slot->bd_offset, cpsw_cpdma_bd_paddr(sc, next_slot))
+#define cpsw_cpdma_write_bd_flags(sc, slot, val) \
+ bus_write_2(sc->mem_res, slot->bd_offset + 14, val)
+#define cpsw_cpdma_read_bd_flags(sc, slot) \
+ bus_read_2(sc->mem_res, slot->bd_offset + 14)
+#define cpsw_write_hdp_slot(sc, queue, slot) \
+ cpsw_write_4(sc, (queue)->hdp_offset, cpsw_cpdma_bd_paddr(sc, slot))
+#define CP_OFFSET (CPSW_CPDMA_TX_CP(0) - CPSW_CPDMA_TX_HDP(0))
+#define cpsw_read_cp(sc, queue) \
+ cpsw_read_4(sc, (queue)->hdp_offset + CP_OFFSET)
+#define cpsw_write_cp(sc, queue, val) \
+ cpsw_write_4(sc, (queue)->hdp_offset + CP_OFFSET, (val))
+#define cpsw_write_cp_slot(sc, queue, slot) \
+ cpsw_write_cp(sc, queue, cpsw_cpdma_bd_paddr(sc, slot))
+
+#if 0
+/* XXX temporary function versions for debugging. */
+static void
+cpsw_write_hdp_slotX(struct cpsw_softc *sc, struct cpsw_queue *queue, struct cpsw_slot *slot)
+{
+ uint32_t reg = queue->hdp_offset;
+ uint32_t v = cpsw_cpdma_bd_paddr(sc, slot);
+ CPSW_DEBUGF(("HDP <=== 0x%08x (was 0x%08x)", v, cpsw_read_4(sc, reg)));
+ cpsw_write_4(sc, reg, v);
+}
+
+static void
+cpsw_write_cp_slotX(struct cpsw_softc *sc, struct cpsw_queue *queue, struct cpsw_slot *slot)
+{
+ uint32_t v = cpsw_cpdma_bd_paddr(sc, slot);
+ CPSW_DEBUGF(("CP <=== 0x%08x (expecting 0x%08x)", v, cpsw_read_cp(sc, queue)));
+ cpsw_write_cp(sc, queue, v);
+}
+#endif
+
+/*
+ * Expanded dump routines for verbose debugging.
+ */
+static void
+cpsw_dump_slot(struct cpsw_softc *sc, struct cpsw_slot *slot)
+{
+ static const char *flags[] = {"SOP", "EOP", "Owner", "EOQ",
+ "TDownCmplt", "PassCRC", "Long", "Short", "MacCtl", "Overrun",
+ "PktErr1", "PortEn/PktErr0", "RxVlanEncap", "Port2", "Port1",
+ "Port0"};
+ struct cpsw_cpdma_bd bd;
+ const char *sep;
+ int i;
+
+ cpsw_cpdma_read_bd(sc, slot, &bd);
+ printf("BD Addr : 0x%08x Next : 0x%08x\n",
+ cpsw_cpdma_bd_paddr(sc, slot), bd.next);
+ printf(" BufPtr: 0x%08x BufLen: 0x%08x\n", bd.bufptr, bd.buflen);
+ printf(" BufOff: 0x%08x PktLen: 0x%08x\n", bd.bufoff, bd.pktlen);
+ printf(" Flags: ");
+ sep = "";
+ for (i = 0; i < 16; ++i) {
+ if (bd.flags & (1 << (15 - i))) {
+ printf("%s%s", sep, flags[i]);
+ sep = ",";
+ }
+ }
+ printf("\n");
+ if (slot->mbuf) {
+ printf(" Ether: %14D\n",
+ (char *)(slot->mbuf->m_data), " ");
+ printf(" Packet: %16D\n",
+ (char *)(slot->mbuf->m_data) + 14, " ");
+ }
+}
+
+#define CPSW_DUMP_SLOT(cs, slot) do { \
+ IF_DEBUG(sc) { \
+ cpsw_dump_slot(sc, slot); \
+ } \
+} while (0)
+
+static void
+cpsw_dump_queue(struct cpsw_softc *sc, struct cpsw_slots *q)
+{
+ struct cpsw_slot *slot;
+ int i = 0;
+ int others = 0;
+
+ STAILQ_FOREACH(slot, q, next) {
+ if (i > CPSW_TXFRAGS)
+ ++others;
+ else
+ cpsw_dump_slot(sc, slot);
+ ++i;
+ }
+ if (others)
+ printf(" ... and %d more.\n", others);
+ printf("\n");
+}
+
+#define CPSW_DUMP_QUEUE(sc, q) do { \
+ IF_DEBUG(sc) { \
+ cpsw_dump_queue(sc, q); \
+ } \
+} while (0)
+
+static void
+cpsw_init_slots(struct cpsw_softc *sc)
+{
+ struct cpsw_slot *slot;
+ int i;
+
+ STAILQ_INIT(&sc->avail);
+
+ /* Put the slot descriptors onto the global avail list. */
+ for (i = 0; i < nitems(sc->_slots); i++) {
+ slot = &sc->_slots[i];
+ slot->bd_offset = cpsw_cpdma_bd_offset(i);
+ STAILQ_INSERT_TAIL(&sc->avail, slot, next);
+ }
+}
+
+static int
+cpsw_add_slots(struct cpsw_softc *sc, struct cpsw_queue *queue, int requested)
+{
+ const int max_slots = nitems(sc->_slots);
+ struct cpsw_slot *slot;
+ int i;
+
+ if (requested < 0)
+ requested = max_slots;
+
+ for (i = 0; i < requested; ++i) {
+ slot = STAILQ_FIRST(&sc->avail);
+ if (slot == NULL)
+ return (0);
+ if (bus_dmamap_create(sc->mbuf_dtag, 0, &slot->dmamap)) {
+ device_printf(sc->dev, "failed to create dmamap\n");
+ return (ENOMEM);
+ }
+ STAILQ_REMOVE_HEAD(&sc->avail, next);
+ STAILQ_INSERT_TAIL(&queue->avail, slot, next);
+ ++queue->avail_queue_len;
+ ++queue->queue_slots;
+ }
+ return (0);
+}
+
+static void
+cpsw_free_slot(struct cpsw_softc *sc, struct cpsw_slot *slot)
+{
+ int error;
+
+ if (slot->dmamap) {
+ if (slot->mbuf)
+ bus_dmamap_unload(sc->mbuf_dtag, slot->dmamap);
+ error = bus_dmamap_destroy(sc->mbuf_dtag, slot->dmamap);
+ KASSERT(error == 0, ("Mapping still active"));
+ slot->dmamap = NULL;
+ }
+ if (slot->mbuf) {
+ m_freem(slot->mbuf);
+ slot->mbuf = NULL;
+ }
+}
+
+static void
+cpsw_reset(struct cpsw_softc *sc)
+{
+ int i;
+
+ callout_stop(&sc->watchdog.callout);
+
+ /* Reset RMII/RGMII wrapper. */
+ cpsw_write_4(sc, CPSW_WR_SOFT_RESET, 1);
+ while (cpsw_read_4(sc, CPSW_WR_SOFT_RESET) & 1)
+ ;
+
+ /* Disable TX and RX interrupts for all cores. */
+ for (i = 0; i < 3; ++i) {
+ cpsw_write_4(sc, CPSW_WR_C_RX_THRESH_EN(i), 0x00);
+ cpsw_write_4(sc, CPSW_WR_C_TX_EN(i), 0x00);
+ cpsw_write_4(sc, CPSW_WR_C_RX_EN(i), 0x00);
+ cpsw_write_4(sc, CPSW_WR_C_MISC_EN(i), 0x00);
+ }
+
+ /* Reset CPSW subsystem. */
+ cpsw_write_4(sc, CPSW_SS_SOFT_RESET, 1);
+ while (cpsw_read_4(sc, CPSW_SS_SOFT_RESET) & 1)
+ ;
+
+ /* Reset Sliver port 1 and 2 */
+ for (i = 0; i < 2; i++) {
+ /* Reset */
+ cpsw_write_4(sc, CPSW_SL_SOFT_RESET(i), 1);
+ while (cpsw_read_4(sc, CPSW_SL_SOFT_RESET(i)) & 1)
+ ;
+ }
+
+ /* Reset DMA controller. */
+ cpsw_write_4(sc, CPSW_CPDMA_SOFT_RESET, 1);
+ while (cpsw_read_4(sc, CPSW_CPDMA_SOFT_RESET) & 1)
+ ;
+
+ /* Disable TX & RX DMA */
+ cpsw_write_4(sc, CPSW_CPDMA_TX_CONTROL, 0);
+ cpsw_write_4(sc, CPSW_CPDMA_RX_CONTROL, 0);
+
+ /* Clear all queues. */
+ for (i = 0; i < 8; i++) {
+ cpsw_write_4(sc, CPSW_CPDMA_TX_HDP(i), 0);
+ cpsw_write_4(sc, CPSW_CPDMA_RX_HDP(i), 0);
+ cpsw_write_4(sc, CPSW_CPDMA_TX_CP(i), 0);
+ cpsw_write_4(sc, CPSW_CPDMA_RX_CP(i), 0);
+ }
+
+ /* Clear all interrupt Masks */
+ cpsw_write_4(sc, CPSW_CPDMA_RX_INTMASK_CLEAR, 0xFFFFFFFF);
+ cpsw_write_4(sc, CPSW_CPDMA_TX_INTMASK_CLEAR, 0xFFFFFFFF);
+}
+
+static void
+cpsw_init(struct cpsw_softc *sc)
+{
+ struct cpsw_slot *slot;
+ uint32_t reg;
+
+ /* Disable the interrupt pacing. */
+ reg = cpsw_read_4(sc, CPSW_WR_INT_CONTROL);
+ reg &= ~(CPSW_WR_INT_PACE_EN | CPSW_WR_INT_PRESCALE_MASK);
+ cpsw_write_4(sc, CPSW_WR_INT_CONTROL, reg);
+
+ /* Clear ALE */
+ cpsw_write_4(sc, CPSW_ALE_CONTROL, CPSW_ALE_CTL_CLEAR_TBL);
+
+ /* Enable ALE */
+ reg = CPSW_ALE_CTL_ENABLE;
+ if (sc->dualemac)
+ reg |= CPSW_ALE_CTL_VLAN_AWARE;
+ cpsw_write_4(sc, CPSW_ALE_CONTROL, reg);
+
+ /* Set Host Port Mapping. */
+ cpsw_write_4(sc, CPSW_PORT_P0_CPDMA_TX_PRI_MAP, 0x76543210);
+ cpsw_write_4(sc, CPSW_PORT_P0_CPDMA_RX_CH_MAP, 0);
+
+ /* Initialize ALE: set host port to forwarding(3). */
+ cpsw_write_4(sc, CPSW_ALE_PORTCTL(0),
+ ALE_PORTCTL_INGRESS | ALE_PORTCTL_FORWARD);
+
+ cpsw_write_4(sc, CPSW_SS_PTYPE, 0);
+
+ /* Enable statistics for ports 0, 1 and 2 */
+ cpsw_write_4(sc, CPSW_SS_STAT_PORT_EN, 7);
+
+ /* Turn off flow control. */
+ cpsw_write_4(sc, CPSW_SS_FLOW_CONTROL, 0);
+
+ /* Make IP hdr aligned with 4 */
+ cpsw_write_4(sc, CPSW_CPDMA_RX_BUFFER_OFFSET, 2);
+
+ /* Initialize RX Buffer Descriptors */
+ cpsw_write_4(sc, CPSW_CPDMA_RX_PENDTHRESH(0), 0);
+ cpsw_write_4(sc, CPSW_CPDMA_RX_FREEBUFFER(0), 0);
+
+ /* Enable TX & RX DMA */
+ cpsw_write_4(sc, CPSW_CPDMA_TX_CONTROL, 1);
+ cpsw_write_4(sc, CPSW_CPDMA_RX_CONTROL, 1);
+
+ /* Enable Interrupts for core 0 */
+ cpsw_write_4(sc, CPSW_WR_C_RX_THRESH_EN(0), 0xFF);
+ cpsw_write_4(sc, CPSW_WR_C_RX_EN(0), 0xFF);
+ cpsw_write_4(sc, CPSW_WR_C_TX_EN(0), 0xFF);
+ cpsw_write_4(sc, CPSW_WR_C_MISC_EN(0), 0x1F);
+
+ /* Enable host Error Interrupt */
+ cpsw_write_4(sc, CPSW_CPDMA_DMA_INTMASK_SET, 3);
+
+ /* Enable interrupts for RX and TX on Channel 0 */
+ cpsw_write_4(sc, CPSW_CPDMA_RX_INTMASK_SET,
+ CPSW_CPDMA_RX_INT(0) | CPSW_CPDMA_RX_INT_THRESH(0));
+ cpsw_write_4(sc, CPSW_CPDMA_TX_INTMASK_SET, 1);
+
+ /* Initialze MDIO - ENABLE, PREAMBLE=0, FAULTENB, CLKDIV=0xFF */
+ /* TODO Calculate MDCLK=CLK/(CLKDIV+1) */
+ cpsw_write_4(sc, MDIOCONTROL, MDIOCTL_ENABLE | MDIOCTL_FAULTENB | 0xff);
+
+ /* Select MII in GMII_SEL, Internal Delay mode */
+ //ti_scm_reg_write_4(0x650, 0);
+
+ /* Initialize active queues. */
+ slot = STAILQ_FIRST(&sc->tx.active);
+ if (slot != NULL)
+ cpsw_write_hdp_slot(sc, &sc->tx, slot);
+ slot = STAILQ_FIRST(&sc->rx.active);
+ if (slot != NULL)
+ cpsw_write_hdp_slot(sc, &sc->rx, slot);
+ cpsw_rx_enqueue(sc);
+ cpsw_write_4(sc, CPSW_CPDMA_RX_FREEBUFFER(0), sc->rx.active_queue_len);
+ cpsw_write_4(sc, CPSW_CPDMA_RX_PENDTHRESH(0), CPSW_TXFRAGS);
+
+ /* Activate network interface. */
+ sc->rx.running = 1;
+ sc->tx.running = 1;
+ sc->watchdog.timer = 0;
+ callout_init(&sc->watchdog.callout, 0);
+ callout_reset(&sc->watchdog.callout, hz, cpsw_tx_watchdog, sc);
+}
+
+/*
+ *
+ * Device Probe, Attach, Detach.
+ *
+ */
+
+static int
+cpsw_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "ti,cpsw"))
+ return (ENXIO);
+
+ device_set_desc(dev, "3-port Switch Ethernet Subsystem");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+cpsw_intr_attach(struct cpsw_softc *sc)
+{
+ int i;
+
+ for (i = 0; i < CPSW_INTR_COUNT; i++) {
+ if (bus_setup_intr(sc->dev, sc->irq_res[i],
+ INTR_TYPE_NET | INTR_MPSAFE, NULL,
+ cpsw_intr_cb[i].cb, sc, &sc->ih_cookie[i]) != 0) {
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+static void
+cpsw_intr_detach(struct cpsw_softc *sc)
+{
+ int i;
+
+ for (i = 0; i < CPSW_INTR_COUNT; i++) {
+ if (sc->ih_cookie[i]) {
+ bus_teardown_intr(sc->dev, sc->irq_res[i],
+ sc->ih_cookie[i]);
+ }
+ }
+}
+
+static int
+cpsw_get_fdt_data(struct cpsw_softc *sc, int port)
+{
+ char *name;
+ int len, phy, vlan;
+ pcell_t phy_id[3], vlan_id;
+ phandle_t child;
+ unsigned long mdio_child_addr;
+
+ /* Find any slave with phy-handle/phy_id */
+ phy = -1;
+ vlan = -1;
+ for (child = OF_child(sc->node); child != 0; child = OF_peer(child)) {
+ if (OF_getprop_alloc(child, "name", (void **)&name) < 0)
+ continue;
+ if (sscanf(name, "slave@%lx", &mdio_child_addr) != 1) {
+ OF_prop_free(name);
+ continue;
+ }
+ OF_prop_free(name);
+
+ if (mdio_child_addr != slave_mdio_addr[port] &&
+ mdio_child_addr != (slave_mdio_addr[port] & 0xFFF))
+ continue;
+
+ if (fdt_get_phyaddr(child, NULL, &phy, NULL) != 0){
+ /* Users with old DTB will have phy_id instead */
+ phy = -1;
+ len = OF_getproplen(child, "phy_id");
+ if (len / sizeof(pcell_t) == 2) {
+ /* Get phy address from fdt */
+ if (OF_getencprop(child, "phy_id", phy_id, len) > 0)
+ phy = phy_id[1];
+ }
+ }
+
+ len = OF_getproplen(child, "dual_emac_res_vlan");
+ if (len / sizeof(pcell_t) == 1) {
+ /* Get phy address from fdt */
+ if (OF_getencprop(child, "dual_emac_res_vlan",
+ &vlan_id, len) > 0) {
+ vlan = vlan_id;
+ }
+ }
+
+ break;
+ }
+ if (phy == -1)
+ return (ENXIO);
+ sc->port[port].phy = phy;
+ sc->port[port].vlan = vlan;
+
+ return (0);
+}
+
+static int
+cpsw_attach(device_t dev)
+{
+ int error, i;
+ struct cpsw_softc *sc;
+ uint32_t reg;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ sc->node = ofw_bus_get_node(dev);
+ getbinuptime(&sc->attach_uptime);
+
+ if (OF_getencprop(sc->node, "active_slave", &sc->active_slave,
+ sizeof(sc->active_slave)) <= 0) {
+ sc->active_slave = 0;
+ }
+ if (sc->active_slave > 1)
+ sc->active_slave = 1;
+
+ if (OF_hasprop(sc->node, "dual_emac"))
+ sc->dualemac = 1;
+
+ for (i = 0; i < CPSW_PORTS; i++) {
+ if (!sc->dualemac && i != sc->active_slave)
+ continue;
+ if (cpsw_get_fdt_data(sc, i) != 0) {
+ device_printf(dev,
+ "failed to get PHY address from FDT\n");
+ return (ENXIO);
+ }
+ }
+
+ /* Initialize mutexes */
+ mtx_init(&sc->tx.lock, device_get_nameunit(dev),
+ "cpsw TX lock", MTX_DEF);
+ mtx_init(&sc->rx.lock, device_get_nameunit(dev),
+ "cpsw RX lock", MTX_DEF);
+
+ /* Allocate IRQ resources */
+ error = bus_alloc_resources(dev, irq_res_spec, sc->irq_res);
+ if (error) {
+ device_printf(dev, "could not allocate IRQ resources\n");
+ cpsw_detach(dev);
+ return (ENXIO);
+ }
+
+ sc->mem_rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &sc->mem_rid, RF_ACTIVE);
+ if (sc->mem_res == NULL) {
+ device_printf(sc->dev, "failed to allocate memory resource\n");
+ cpsw_detach(dev);
+ return (ENXIO);
+ }
+
+ reg = cpsw_read_4(sc, CPSW_SS_IDVER);
+ device_printf(dev, "CPSW SS Version %d.%d (%d)\n", (reg >> 8 & 0x7),
+ reg & 0xFF, (reg >> 11) & 0x1F);
+
+ cpsw_add_sysctls(sc);
+
+ /* Allocate a busdma tag and DMA safe memory for mbufs. */
+ error = bus_dma_tag_create(
+ bus_get_dma_tag(sc->dev), /* parent */
+ 1, 0, /* alignment, boundary */
+ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filtfunc, filtfuncarg */
+ MCLBYTES, CPSW_TXFRAGS, /* maxsize, nsegments */
+ MCLBYTES, 0, /* maxsegsz, flags */
+ NULL, NULL, /* lockfunc, lockfuncarg */
+ &sc->mbuf_dtag); /* dmatag */
+ if (error) {
+ device_printf(dev, "bus_dma_tag_create failed\n");
+ cpsw_detach(dev);
+ return (error);
+ }
+
+ /* Allocate a NULL buffer for padding. */
+ sc->nullpad = malloc(ETHER_MIN_LEN, M_DEVBUF, M_WAITOK | M_ZERO);
+
+ cpsw_init_slots(sc);
+
+ /* Allocate slots to TX and RX queues. */
+ STAILQ_INIT(&sc->rx.avail);
+ STAILQ_INIT(&sc->rx.active);
+ STAILQ_INIT(&sc->tx.avail);
+ STAILQ_INIT(&sc->tx.active);
+ // For now: 128 slots to TX, rest to RX.
+ // XXX TODO: start with 32/64 and grow dynamically based on demand.
+ if (cpsw_add_slots(sc, &sc->tx, 128) ||
+ cpsw_add_slots(sc, &sc->rx, -1)) {
+ device_printf(dev, "failed to allocate dmamaps\n");
+ cpsw_detach(dev);
+ return (ENOMEM);
+ }
+ device_printf(dev, "Initial queue size TX=%d RX=%d\n",
+ sc->tx.queue_slots, sc->rx.queue_slots);
+
+ sc->tx.hdp_offset = CPSW_CPDMA_TX_HDP(0);
+ sc->rx.hdp_offset = CPSW_CPDMA_RX_HDP(0);
+
+ if (cpsw_intr_attach(sc) == -1) {
+ device_printf(dev, "failed to setup interrupts\n");
+ cpsw_detach(dev);
+ return (ENXIO);
+ }
+
+#ifdef CPSW_ETHERSWITCH
+ for (i = 0; i < CPSW_VLANS; i++)
+ cpsw_vgroups[i].vid = -1;
+#endif
+
+ /* Reset the controller. */
+ cpsw_reset(sc);
+ cpsw_init(sc);
+
+ for (i = 0; i < CPSW_PORTS; i++) {
+ if (!sc->dualemac && i != sc->active_slave)
+ continue;
+ sc->port[i].dev = device_add_child(dev, "cpsw", i);
+ if (sc->port[i].dev == NULL) {
+ cpsw_detach(dev);
+ return (ENXIO);
+ }
+ }
+ bus_generic_probe(dev);
+ bus_generic_attach(dev);
+
+ return (0);
+}
+
+static int
+cpsw_detach(device_t dev)
+{
+ struct cpsw_softc *sc;
+ int error, i;
+
+ bus_generic_detach(dev);
+ sc = device_get_softc(dev);
+
+ for (i = 0; i < CPSW_PORTS; i++) {
+ if (sc->port[i].dev)
+ device_delete_child(dev, sc->port[i].dev);
+ }
+
+ if (device_is_attached(dev)) {
+ callout_stop(&sc->watchdog.callout);
+ callout_drain(&sc->watchdog.callout);
+ }
+
+ /* Stop and release all interrupts */
+ cpsw_intr_detach(sc);
+
+ /* Free dmamaps and mbufs */
+ for (i = 0; i < nitems(sc->_slots); ++i)
+ cpsw_free_slot(sc, &sc->_slots[i]);
+
+ /* Free null padding buffer. */
+ if (sc->nullpad)
+ free(sc->nullpad, M_DEVBUF);
+
+ /* Free DMA tag */
+ if (sc->mbuf_dtag) {
+ error = bus_dma_tag_destroy(sc->mbuf_dtag);
+ KASSERT(error == 0, ("Unable to destroy DMA tag"));
+ }
+
+ /* Free IO memory handler */
+ if (sc->mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem_res);
+ bus_release_resources(dev, irq_res_spec, sc->irq_res);
+
+ /* Destroy mutexes */
+ mtx_destroy(&sc->rx.lock);
+ mtx_destroy(&sc->tx.lock);
+
+ /* Detach the switch device, if present. */
+ error = bus_generic_detach(dev);
+ if (error != 0)
+ return (error);
+
+ return (device_delete_children(dev));
+}
+
+static phandle_t
+cpsw_get_node(device_t bus, device_t dev)
+{
+
+ /* Share controller node with port device. */
+ return (ofw_bus_get_node(bus));
+}
+
+static int
+cpswp_probe(device_t dev)
+{
+
+ if (device_get_unit(dev) > 1) {
+ device_printf(dev, "Only two ports are supported.\n");
+ return (ENXIO);
+ }
+ device_set_desc(dev, "Ethernet Switch Port");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+cpswp_attach(device_t dev)
+{
+ int error;
+ struct ifnet *ifp;
+ struct cpswp_softc *sc;
+ uint32_t reg;
+ uint8_t mac_addr[ETHER_ADDR_LEN];
+ phandle_t opp_table;
+ struct syscon *syscon;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ sc->pdev = device_get_parent(dev);
+ sc->swsc = device_get_softc(sc->pdev);
+ sc->unit = device_get_unit(dev);
+ sc->phy = sc->swsc->port[sc->unit].phy;
+ sc->vlan = sc->swsc->port[sc->unit].vlan;
+ if (sc->swsc->dualemac && sc->vlan == -1)
+ sc->vlan = sc->unit + 1;
+
+ if (sc->unit == 0) {
+ sc->physel = MDIOUSERPHYSEL0;
+ sc->phyaccess = MDIOUSERACCESS0;
+ } else {
+ sc->physel = MDIOUSERPHYSEL1;
+ sc->phyaccess = MDIOUSERACCESS1;
+ }
+
+ mtx_init(&sc->lock, device_get_nameunit(dev), "cpsw port lock",
+ MTX_DEF);
+
+ /* Allocate network interface */
+ ifp = sc->ifp = if_alloc(IFT_ETHER);
+ if (ifp == NULL) {
+ cpswp_detach(dev);
+ return (ENXIO);
+ }
+
+ if_initname(ifp, device_get_name(sc->dev), sc->unit);
+ ifp->if_softc = sc;
+ ifp->if_flags = IFF_SIMPLEX | IFF_MULTICAST | IFF_BROADCAST;
+ ifp->if_capabilities = IFCAP_VLAN_MTU | IFCAP_HWCSUM; //FIXME VLAN?
+ ifp->if_capenable = ifp->if_capabilities;
+
+ ifp->if_init = cpswp_init;
+ ifp->if_start = cpswp_start;
+ ifp->if_ioctl = cpswp_ioctl;
+
+ ifp->if_snd.ifq_drv_maxlen = sc->swsc->tx.queue_slots;
+ IFQ_SET_MAXLEN(&ifp->if_snd, ifp->if_snd.ifq_drv_maxlen);
+ IFQ_SET_READY(&ifp->if_snd);
+
+ /* FIXME: For now; Go and kidnap syscon from opp-table */
+ /* ti,cpsw actually have an optional syscon reference but only for am33xx?? */
+ opp_table = OF_finddevice("/opp-table");
+ if (opp_table == -1) {
+ device_printf(dev, "Cant find /opp-table\n");
+ cpswp_detach(dev);
+ return (ENXIO);
+ }
+ if (!OF_hasprop(opp_table, "syscon")) {
+ device_printf(dev, "/opp-table doesnt have required syscon property\n");
+ cpswp_detach(dev);
+ return (ENXIO);
+ }
+ if (syscon_get_by_ofw_property(dev, opp_table, "syscon", &syscon) != 0) {
+ device_printf(dev, "Failed to get syscon\n");
+ cpswp_detach(dev);
+ return (ENXIO);
+ }
+
+ /* Get high part of MAC address from control module (mac_id[0|1]_hi) */
+ reg = SYSCON_READ_4(syscon, SCM_MAC_ID0_HI + sc->unit * 8);
+ mac_addr[0] = reg & 0xFF;
+ mac_addr[1] = (reg >> 8) & 0xFF;
+ mac_addr[2] = (reg >> 16) & 0xFF;
+ mac_addr[3] = (reg >> 24) & 0xFF;
+
+ /* Get low part of MAC address from control module (mac_id[0|1]_lo) */
+ reg = SYSCON_READ_4(syscon, SCM_MAC_ID0_LO + sc->unit * 8);
+ mac_addr[4] = reg & 0xFF;
+ mac_addr[5] = (reg >> 8) & 0xFF;
+
+ error = mii_attach(dev, &sc->miibus, ifp, cpswp_ifmedia_upd,
+ cpswp_ifmedia_sts, BMSR_DEFCAPMASK, sc->phy, MII_OFFSET_ANY, 0);
+ if (error) {
+ device_printf(dev, "attaching PHYs failed\n");
+ cpswp_detach(dev);
+ return (error);
+ }
+ sc->mii = device_get_softc(sc->miibus);
+
+ /* Select PHY and enable interrupts */
+ cpsw_write_4(sc->swsc, sc->physel,
+ MDIO_PHYSEL_LINKINTENB | (sc->phy & 0x1F));
+
+ ether_ifattach(sc->ifp, mac_addr);
+ callout_init(&sc->mii_callout, 0);
+
+ return (0);
+}
+
+static int
+cpswp_detach(device_t dev)
+{
+ struct cpswp_softc *sc;
+
+ sc = device_get_softc(dev);
+ CPSW_DEBUGF(sc->swsc, (""));
+ if (device_is_attached(dev)) {
+ ether_ifdetach(sc->ifp);
+ CPSW_PORT_LOCK(sc);
+ cpswp_stop_locked(sc);
+ CPSW_PORT_UNLOCK(sc);
+ callout_drain(&sc->mii_callout);
+ }
+
+ bus_generic_detach(dev);
+
+ if_free(sc->ifp);
+ mtx_destroy(&sc->lock);
+
+ return (0);
+}
+
+/*
+ *
+ * Init/Shutdown.
+ *
+ */
+
+static int
+cpsw_ports_down(struct cpsw_softc *sc)
+{
+ struct cpswp_softc *psc;
+ struct ifnet *ifp1, *ifp2;
+
+ if (!sc->dualemac)
+ return (1);
+ psc = device_get_softc(sc->port[0].dev);
+ ifp1 = psc->ifp;
+ psc = device_get_softc(sc->port[1].dev);
+ ifp2 = psc->ifp;
+ if ((ifp1->if_flags & IFF_UP) == 0 && (ifp2->if_flags & IFF_UP) == 0)
+ return (1);
+
+ return (0);
+}
+
+static void
+cpswp_init(void *arg)
+{
+ struct cpswp_softc *sc = arg;
+
+ CPSW_DEBUGF(sc->swsc, (""));
+ CPSW_PORT_LOCK(sc);
+ cpswp_init_locked(arg);
+ CPSW_PORT_UNLOCK(sc);
+}
+
+static void
+cpswp_init_locked(void *arg)
+{
+#ifdef CPSW_ETHERSWITCH
+ int i;
+#endif
+ struct cpswp_softc *sc = arg;
+ struct ifnet *ifp;
+ uint32_t reg;
+
+ CPSW_DEBUGF(sc->swsc, (""));
+ CPSW_PORT_LOCK_ASSERT(sc);
+ ifp = sc->ifp;
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
+ return;
+
+ getbinuptime(&sc->init_uptime);
+
+ if (!sc->swsc->rx.running && !sc->swsc->tx.running) {
+ /* Reset the controller. */
+ cpsw_reset(sc->swsc);
+ cpsw_init(sc->swsc);
+ }
+
+ /* Set Slave Mapping. */
+ cpsw_write_4(sc->swsc, CPSW_SL_RX_PRI_MAP(sc->unit), 0x76543210);
+ cpsw_write_4(sc->swsc, CPSW_PORT_P_TX_PRI_MAP(sc->unit + 1),
+ 0x33221100);
+ cpsw_write_4(sc->swsc, CPSW_SL_RX_MAXLEN(sc->unit), 0x5f2);
+ /* Enable MAC RX/TX modules. */
+ /* TODO: Docs claim that IFCTL_B and IFCTL_A do the same thing? */
+ /* Huh? Docs call bit 0 "Loopback" some places, "FullDuplex" others. */
+ reg = cpsw_read_4(sc->swsc, CPSW_SL_MACCONTROL(sc->unit));
+ reg |= CPSW_SL_MACTL_GMII_ENABLE;
+ cpsw_write_4(sc->swsc, CPSW_SL_MACCONTROL(sc->unit), reg);
+
+ /* Initialize ALE: set port to forwarding, initialize addrs */
+ cpsw_write_4(sc->swsc, CPSW_ALE_PORTCTL(sc->unit + 1),
+ ALE_PORTCTL_INGRESS | ALE_PORTCTL_FORWARD);
+ cpswp_ale_update_addresses(sc, 1);
+
+ if (sc->swsc->dualemac) {
+ /* Set Port VID. */
+ cpsw_write_4(sc->swsc, CPSW_PORT_P_VLAN(sc->unit + 1),
+ sc->vlan & 0xfff);
+ cpsw_ale_update_vlan_table(sc->swsc, sc->vlan,
+ (1 << (sc->unit + 1)) | (1 << 0), /* Member list */
+ (1 << (sc->unit + 1)) | (1 << 0), /* Untagged egress */
+ (1 << (sc->unit + 1)) | (1 << 0), 0); /* mcast reg flood */
+#ifdef CPSW_ETHERSWITCH
+ for (i = 0; i < CPSW_VLANS; i++) {
+ if (cpsw_vgroups[i].vid != -1)
+ continue;
+ cpsw_vgroups[i].vid = sc->vlan;
+ break;
+ }
+#endif
+ }
+
+ mii_mediachg(sc->mii);
+ callout_reset(&sc->mii_callout, hz, cpswp_tick, sc);
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+}
+
+static int
+cpsw_shutdown(device_t dev)
+{
+ struct cpsw_softc *sc;
+ struct cpswp_softc *psc;
+ int i;
+
+ sc = device_get_softc(dev);
+ CPSW_DEBUGF(sc, (""));
+ for (i = 0; i < CPSW_PORTS; i++) {
+ if (!sc->dualemac && i != sc->active_slave)
+ continue;
+ psc = device_get_softc(sc->port[i].dev);
+ CPSW_PORT_LOCK(psc);
+ cpswp_stop_locked(psc);
+ CPSW_PORT_UNLOCK(psc);
+ }
+
+ return (0);
+}
+
+static void
+cpsw_rx_teardown(struct cpsw_softc *sc)
+{
+ int i = 0;
+
+ CPSW_RX_LOCK(sc);
+ CPSW_DEBUGF(sc, ("starting RX teardown"));
+ sc->rx.teardown = 1;
+ cpsw_write_4(sc, CPSW_CPDMA_RX_TEARDOWN, 0);
+ CPSW_RX_UNLOCK(sc);
+ while (sc->rx.running) {
+ if (++i > 10) {
+ device_printf(sc->dev,
+ "Unable to cleanly shutdown receiver\n");
+ return;
+ }
+ DELAY(200);
+ }
+ if (!sc->rx.running)
+ CPSW_DEBUGF(sc, ("finished RX teardown (%d retries)", i));
+}
+
+static void
+cpsw_tx_teardown(struct cpsw_softc *sc)
+{
+ int i = 0;
+
+ CPSW_TX_LOCK(sc);
+ CPSW_DEBUGF(sc, ("starting TX teardown"));
+ /* Start the TX queue teardown if queue is not empty. */
+ if (STAILQ_FIRST(&sc->tx.active) != NULL)
+ cpsw_write_4(sc, CPSW_CPDMA_TX_TEARDOWN, 0);
+ else
+ sc->tx.teardown = 1;
+ cpsw_tx_dequeue(sc);
+ while (sc->tx.running && ++i < 10) {
+ DELAY(200);
+ cpsw_tx_dequeue(sc);
+ }
+ if (sc->tx.running) {
+ device_printf(sc->dev,
+ "Unable to cleanly shutdown transmitter\n");
+ }
+ CPSW_DEBUGF(sc,
+ ("finished TX teardown (%d retries, %d idle buffers)", i,
+ sc->tx.active_queue_len));
+ CPSW_TX_UNLOCK(sc);
+}
+
+static void
+cpswp_stop_locked(struct cpswp_softc *sc)
+{
+ struct ifnet *ifp;
+ uint32_t reg;
+
+ ifp = sc->ifp;
+ CPSW_DEBUGF(sc->swsc, (""));
+ CPSW_PORT_LOCK_ASSERT(sc);
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ return;
+
+ /* Disable interface */
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+
+ /* Stop ticker */
+ callout_stop(&sc->mii_callout);
+
+ /* Tear down the RX/TX queues. */
+ if (cpsw_ports_down(sc->swsc)) {
+ cpsw_rx_teardown(sc->swsc);
+ cpsw_tx_teardown(sc->swsc);
+ }
+
+ /* Stop MAC RX/TX modules. */
+ reg = cpsw_read_4(sc->swsc, CPSW_SL_MACCONTROL(sc->unit));
+ reg &= ~CPSW_SL_MACTL_GMII_ENABLE;
+ cpsw_write_4(sc->swsc, CPSW_SL_MACCONTROL(sc->unit), reg);
+
+ if (cpsw_ports_down(sc->swsc)) {
+ /* Capture stats before we reset controller. */
+ cpsw_stats_collect(sc->swsc);
+
+ cpsw_reset(sc->swsc);
+ cpsw_init(sc->swsc);
+ }
+}
+
+/*
+ * Suspend/Resume.
+ */
+
+static int
+cpsw_suspend(device_t dev)
+{
+ struct cpsw_softc *sc;
+ struct cpswp_softc *psc;
+ int i;
+
+ sc = device_get_softc(dev);
+ CPSW_DEBUGF(sc, (""));
+ for (i = 0; i < CPSW_PORTS; i++) {
+ if (!sc->dualemac && i != sc->active_slave)
+ continue;
+ psc = device_get_softc(sc->port[i].dev);
+ CPSW_PORT_LOCK(psc);
+ cpswp_stop_locked(psc);
+ CPSW_PORT_UNLOCK(psc);
+ }
+
+ return (0);
+}
+
+static int
+cpsw_resume(device_t dev)
+{
+ struct cpsw_softc *sc;
+
+ sc = device_get_softc(dev);
+ CPSW_DEBUGF(sc, ("UNIMPLEMENTED"));
+
+ return (0);
+}
+
+/*
+ *
+ * IOCTL
+ *
+ */
+
+static void
+cpsw_set_promisc(struct cpswp_softc *sc, int set)
+{
+ uint32_t reg;
+
+ /*
+ * Enabling promiscuous mode requires ALE_BYPASS to be enabled.
+ * That disables the ALE forwarding logic and causes every
+ * packet to be sent only to the host port. In bypass mode,
+ * the ALE processes host port transmit packets the same as in
+ * normal mode.
+ */
+ reg = cpsw_read_4(sc->swsc, CPSW_ALE_CONTROL);
+ reg &= ~CPSW_ALE_CTL_BYPASS;
+ if (set)
+ reg |= CPSW_ALE_CTL_BYPASS;
+ cpsw_write_4(sc->swsc, CPSW_ALE_CONTROL, reg);
+}
+
+static void
+cpsw_set_allmulti(struct cpswp_softc *sc, int set)
+{
+ if (set) {
+ printf("All-multicast mode unimplemented\n");
+ }
+}
+
+static int
+cpswp_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
+{
+ struct cpswp_softc *sc;
+ struct ifreq *ifr;
+ int error;
+ uint32_t changed;
+
+ error = 0;
+ sc = ifp->if_softc;
+ ifr = (struct ifreq *)data;
+
+ switch (command) {
+ case SIOCSIFCAP:
+ changed = ifp->if_capenable ^ ifr->ifr_reqcap;
+ if (changed & IFCAP_HWCSUM) {
+ if ((ifr->ifr_reqcap & changed) & IFCAP_HWCSUM)
+ ifp->if_capenable |= IFCAP_HWCSUM;
+ else
+ ifp->if_capenable &= ~IFCAP_HWCSUM;
+ }
+ error = 0;
+ break;
+ case SIOCSIFFLAGS:
+ CPSW_PORT_LOCK(sc);
+ if (ifp->if_flags & IFF_UP) {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ changed = ifp->if_flags ^ sc->if_flags;
+ CPSW_DEBUGF(sc->swsc,
+ ("SIOCSIFFLAGS: UP & RUNNING (changed=0x%x)",
+ changed));
+ if (changed & IFF_PROMISC)
+ cpsw_set_promisc(sc,
+ ifp->if_flags & IFF_PROMISC);
+ if (changed & IFF_ALLMULTI)
+ cpsw_set_allmulti(sc,
+ ifp->if_flags & IFF_ALLMULTI);
+ } else {
+ CPSW_DEBUGF(sc->swsc,
+ ("SIOCSIFFLAGS: starting up"));
+ cpswp_init_locked(sc);
+ }
+ } else if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ CPSW_DEBUGF(sc->swsc, ("SIOCSIFFLAGS: shutting down"));
+ cpswp_stop_locked(sc);
+ }
+
+ sc->if_flags = ifp->if_flags;
+ CPSW_PORT_UNLOCK(sc);
+ break;
+ case SIOCADDMULTI:
+ cpswp_ale_update_addresses(sc, 0);
+ break;
+ case SIOCDELMULTI:
+ /* Ugh. DELMULTI doesn't provide the specific address
+ being removed, so the best we can do is remove
+ everything and rebuild it all. */
+ cpswp_ale_update_addresses(sc, 1);
+ break;
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &sc->mii->mii_media, command);
+ break;
+ default:
+ error = ether_ioctl(ifp, command, data);
+ }
+ return (error);
+}
+
+/*
+ *
+ * MIIBUS
+ *
+ */
+static int
+cpswp_miibus_ready(struct cpsw_softc *sc, uint32_t reg)
+{
+ uint32_t r, retries = CPSW_MIIBUS_RETRIES;
+
+ while (--retries) {
+ r = cpsw_read_4(sc, reg);
+ if ((r & MDIO_PHYACCESS_GO) == 0)
+ return (1);
+ DELAY(CPSW_MIIBUS_DELAY);
+ }
+
+ return (0);
+}
+
+static int
+cpswp_miibus_readreg(device_t dev, int phy, int reg)
+{
+ struct cpswp_softc *sc;
+ uint32_t cmd, r;
+
+ sc = device_get_softc(dev);
+ if (!cpswp_miibus_ready(sc->swsc, sc->phyaccess)) {
+ device_printf(dev, "MDIO not ready to read\n");
+ return (0);
+ }
+
+ /* Set GO, reg, phy */
+ cmd = MDIO_PHYACCESS_GO | (reg & 0x1F) << 21 | (phy & 0x1F) << 16;
+ cpsw_write_4(sc->swsc, sc->phyaccess, cmd);
+
+ if (!cpswp_miibus_ready(sc->swsc, sc->phyaccess)) {
+ device_printf(dev, "MDIO timed out during read\n");
+ return (0);
+ }
+
+ r = cpsw_read_4(sc->swsc, sc->phyaccess);
+ if ((r & MDIO_PHYACCESS_ACK) == 0) {
+ device_printf(dev, "Failed to read from PHY.\n");
+ r = 0;
+ }
+ return (r & 0xFFFF);
+}
+
+static int
+cpswp_miibus_writereg(device_t dev, int phy, int reg, int value)
+{
+ struct cpswp_softc *sc;
+ uint32_t cmd;
+
+ sc = device_get_softc(dev);
+ if (!cpswp_miibus_ready(sc->swsc, sc->phyaccess)) {
+ device_printf(dev, "MDIO not ready to write\n");
+ return (0);
+ }
+
+ /* Set GO, WRITE, reg, phy, and value */
+ cmd = MDIO_PHYACCESS_GO | MDIO_PHYACCESS_WRITE |
+ (reg & 0x1F) << 21 | (phy & 0x1F) << 16 | (value & 0xFFFF);
+ cpsw_write_4(sc->swsc, sc->phyaccess, cmd);
+
+ if (!cpswp_miibus_ready(sc->swsc, sc->phyaccess)) {
+ device_printf(dev, "MDIO timed out during write\n");
+ return (0);
+ }
+
+ return (0);
+}
+
+static void
+cpswp_miibus_statchg(device_t dev)
+{
+ struct cpswp_softc *sc;
+ uint32_t mac_control, reg;
+
+ sc = device_get_softc(dev);
+ CPSW_DEBUGF(sc->swsc, (""));
+
+ reg = CPSW_SL_MACCONTROL(sc->unit);
+ mac_control = cpsw_read_4(sc->swsc, reg);
+ mac_control &= ~(CPSW_SL_MACTL_GIG | CPSW_SL_MACTL_IFCTL_A |
+ CPSW_SL_MACTL_IFCTL_B | CPSW_SL_MACTL_FULLDUPLEX);
+
+ switch(IFM_SUBTYPE(sc->mii->mii_media_active)) {
+ case IFM_1000_SX:
+ case IFM_1000_LX:
+ case IFM_1000_CX:
+ case IFM_1000_T:
+ mac_control |= CPSW_SL_MACTL_GIG;
+ break;
+
+ case IFM_100_TX:
+ mac_control |= CPSW_SL_MACTL_IFCTL_A;
+ break;
+ }
+ if (sc->mii->mii_media_active & IFM_FDX)
+ mac_control |= CPSW_SL_MACTL_FULLDUPLEX;
+
+ cpsw_write_4(sc->swsc, reg, mac_control);
+}
+
+/*
+ *
+ * Transmit/Receive Packets.
+ *
+ */
+static void
+cpsw_intr_rx(void *arg)
+{
+ struct cpsw_softc *sc;
+ struct ifnet *ifp;
+ struct mbuf *received, *next;
+
+ sc = (struct cpsw_softc *)arg;
+ CPSW_RX_LOCK(sc);
+ if (sc->rx.teardown) {
+ sc->rx.running = 0;
+ sc->rx.teardown = 0;
+ cpsw_write_cp(sc, &sc->rx, 0xfffffffc);
+ }
+ received = cpsw_rx_dequeue(sc);
+ cpsw_rx_enqueue(sc);
+ cpsw_write_4(sc, CPSW_CPDMA_CPDMA_EOI_VECTOR, 1);
+ CPSW_RX_UNLOCK(sc);
+
+ while (received != NULL) {
+ next = received->m_nextpkt;
+ received->m_nextpkt = NULL;
+ ifp = received->m_pkthdr.rcvif;
+ (*ifp->if_input)(ifp, received);
+ if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
+ received = next;
+ }
+}
+
+static struct mbuf *
+cpsw_rx_dequeue(struct cpsw_softc *sc)
+{
+ int nsegs, port, removed;
+ struct cpsw_cpdma_bd bd;
+ struct cpsw_slot *last, *slot;
+ struct cpswp_softc *psc;
+ struct mbuf *m, *m0, *mb_head, *mb_tail;
+ uint16_t m0_flags;
+
+ nsegs = 0;
+ m0 = NULL;
+ last = NULL;
+ mb_head = NULL;
+ mb_tail = NULL;
+ removed = 0;
+
+ /* Pull completed packets off hardware RX queue. */
+ while ((slot = STAILQ_FIRST(&sc->rx.active)) != NULL) {
+ cpsw_cpdma_read_bd(sc, slot, &bd);
+
+ /*
+ * Stop on packets still in use by hardware, but do not stop
+ * on packets with the teardown complete flag, they will be
+ * discarded later.
+ */
+ if ((bd.flags & (CPDMA_BD_OWNER | CPDMA_BD_TDOWNCMPLT)) ==
+ CPDMA_BD_OWNER)
+ break;
+
+ last = slot;
+ ++removed;
+ STAILQ_REMOVE_HEAD(&sc->rx.active, next);
+ STAILQ_INSERT_TAIL(&sc->rx.avail, slot, next);
+
+ bus_dmamap_sync(sc->mbuf_dtag, slot->dmamap, BUS_DMASYNC_POSTREAD);
+ bus_dmamap_unload(sc->mbuf_dtag, slot->dmamap);
+
+ m = slot->mbuf;
+ slot->mbuf = NULL;
+
+ if (bd.flags & CPDMA_BD_TDOWNCMPLT) {
+ CPSW_DEBUGF(sc, ("RX teardown is complete"));
+ m_freem(m);
+ sc->rx.running = 0;
+ sc->rx.teardown = 0;
+ break;
+ }
+
+ port = (bd.flags & CPDMA_BD_PORT_MASK) - 1;
+ KASSERT(port >= 0 && port <= 1,
+ ("patcket received with invalid port: %d", port));
+ psc = device_get_softc(sc->port[port].dev);
+
+ /* Set up mbuf */
+ m->m_data += bd.bufoff;
+ m->m_len = bd.buflen;
+ if (bd.flags & CPDMA_BD_SOP) {
+ m->m_pkthdr.len = bd.pktlen;
+ m->m_pkthdr.rcvif = psc->ifp;
+ m->m_flags |= M_PKTHDR;
+ m0_flags = bd.flags;
+ m0 = m;
+ }
+ nsegs++;
+ m->m_next = NULL;
+ m->m_nextpkt = NULL;
+ if (bd.flags & CPDMA_BD_EOP && m0 != NULL) {
+ if (m0_flags & CPDMA_BD_PASS_CRC)
+ m_adj(m0, -ETHER_CRC_LEN);
+ m0_flags = 0;
+ m0 = NULL;
+ if (nsegs > sc->rx.longest_chain)
+ sc->rx.longest_chain = nsegs;
+ nsegs = 0;
+ }
+
+ if ((psc->ifp->if_capenable & IFCAP_RXCSUM) != 0) {
+ /* check for valid CRC by looking into pkt_err[5:4] */
+ if ((bd.flags &
+ (CPDMA_BD_SOP | CPDMA_BD_PKT_ERR_MASK)) ==
+ CPDMA_BD_SOP) {
+ m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED;
+ m->m_pkthdr.csum_flags |= CSUM_IP_VALID;
+ m->m_pkthdr.csum_data = 0xffff;
+ }
+ }
+
+ if (STAILQ_FIRST(&sc->rx.active) != NULL &&
+ (bd.flags & (CPDMA_BD_EOP | CPDMA_BD_EOQ)) ==
+ (CPDMA_BD_EOP | CPDMA_BD_EOQ)) {
+ cpsw_write_hdp_slot(sc, &sc->rx,
+ STAILQ_FIRST(&sc->rx.active));
+ sc->rx.queue_restart++;
+ }
+
+ /* Add mbuf to packet list to be returned. */
+ if (mb_tail != NULL && (bd.flags & CPDMA_BD_SOP)) {
+ mb_tail->m_nextpkt = m;
+ } else if (mb_tail != NULL) {
+ mb_tail->m_next = m;
+ } else if (mb_tail == NULL && (bd.flags & CPDMA_BD_SOP) == 0) {
+ if (bootverbose)
+ printf(
+ "%s: %s: discanding fragment packet w/o header\n",
+ __func__, psc->ifp->if_xname);
+ m_freem(m);
+ continue;
+ } else {
+ mb_head = m;
+ }
+ mb_tail = m;
+ }
+
+ if (removed != 0) {
+ cpsw_write_cp_slot(sc, &sc->rx, last);
+ sc->rx.queue_removes += removed;
+ sc->rx.avail_queue_len += removed;
+ sc->rx.active_queue_len -= removed;
+ if (sc->rx.avail_queue_len > sc->rx.max_avail_queue_len)
+ sc->rx.max_avail_queue_len = sc->rx.avail_queue_len;
+ CPSW_DEBUGF(sc, ("Removed %d received packet(s) from RX queue", removed));
+ }
+
+ return (mb_head);
+}
+
+static void
+cpsw_rx_enqueue(struct cpsw_softc *sc)
+{
+ bus_dma_segment_t seg[1];
+ struct cpsw_cpdma_bd bd;
+ struct cpsw_slot *first_new_slot, *last_old_slot, *next, *slot;
+ int error, nsegs, added = 0;
+
+ /* Register new mbufs with hardware. */
+ first_new_slot = NULL;
+ last_old_slot = STAILQ_LAST(&sc->rx.active, cpsw_slot, next);
+ while ((slot = STAILQ_FIRST(&sc->rx.avail)) != NULL) {
+ if (first_new_slot == NULL)
+ first_new_slot = slot;
+ if (slot->mbuf == NULL) {
+ slot->mbuf = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
+ if (slot->mbuf == NULL) {
+ device_printf(sc->dev,
+ "Unable to fill RX queue\n");
+ break;
+ }
+ slot->mbuf->m_len =
+ slot->mbuf->m_pkthdr.len =
+ slot->mbuf->m_ext.ext_size;
+ }
+
+ error = bus_dmamap_load_mbuf_sg(sc->mbuf_dtag, slot->dmamap,
+ slot->mbuf, seg, &nsegs, BUS_DMA_NOWAIT);
+
+ KASSERT(nsegs == 1, ("More than one segment (nsegs=%d)", nsegs));
+ KASSERT(error == 0, ("DMA error (error=%d)", error));
+ if (error != 0 || nsegs != 1) {
+ device_printf(sc->dev,
+ "%s: Can't prep RX buf for DMA (nsegs=%d, error=%d)\n",
+ __func__, nsegs, error);
+ bus_dmamap_unload(sc->mbuf_dtag, slot->dmamap);
+ m_freem(slot->mbuf);
+ slot->mbuf = NULL;
+ break;
+ }
+
+ bus_dmamap_sync(sc->mbuf_dtag, slot->dmamap, BUS_DMASYNC_PREREAD);
+
+ /* Create and submit new rx descriptor. */
+ if ((next = STAILQ_NEXT(slot, next)) != NULL)
+ bd.next = cpsw_cpdma_bd_paddr(sc, next);
+ else
+ bd.next = 0;
+ bd.bufptr = seg->ds_addr;
+ bd.bufoff = 0;
+ bd.buflen = MCLBYTES - 1;
+ bd.pktlen = bd.buflen;
+ bd.flags = CPDMA_BD_OWNER;
+ cpsw_cpdma_write_bd(sc, slot, &bd);
+ ++added;
+
+ STAILQ_REMOVE_HEAD(&sc->rx.avail, next);
+ STAILQ_INSERT_TAIL(&sc->rx.active, slot, next);
+ }
+
+ if (added == 0 || first_new_slot == NULL)
+ return;
+
+ CPSW_DEBUGF(sc, ("Adding %d buffers to RX queue", added));
+
+ /* Link new entries to hardware RX queue. */
+ if (last_old_slot == NULL) {
+ /* Start a fresh queue. */
+ cpsw_write_hdp_slot(sc, &sc->rx, first_new_slot);
+ } else {
+ /* Add buffers to end of current queue. */
+ cpsw_cpdma_write_bd_next(sc, last_old_slot, first_new_slot);
+ }
+ sc->rx.queue_adds += added;
+ sc->rx.avail_queue_len -= added;
+ sc->rx.active_queue_len += added;
+ cpsw_write_4(sc, CPSW_CPDMA_RX_FREEBUFFER(0), added);
+ if (sc->rx.active_queue_len > sc->rx.max_active_queue_len)
+ sc->rx.max_active_queue_len = sc->rx.active_queue_len;
+}
+
+static void
+cpswp_start(struct ifnet *ifp)
+{
+ struct cpswp_softc *sc;
+
+ sc = ifp->if_softc;
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 ||
+ sc->swsc->tx.running == 0) {
+ return;
+ }
+ CPSW_TX_LOCK(sc->swsc);
+ cpswp_tx_enqueue(sc);
+ cpsw_tx_dequeue(sc->swsc);
+ CPSW_TX_UNLOCK(sc->swsc);
+}
+
+static void
+cpsw_intr_tx(void *arg)
+{
+ struct cpsw_softc *sc;
+
+ sc = (struct cpsw_softc *)arg;
+ CPSW_TX_LOCK(sc);
+ if (cpsw_read_4(sc, CPSW_CPDMA_TX_CP(0)) == 0xfffffffc)
+ cpsw_write_cp(sc, &sc->tx, 0xfffffffc);
+ cpsw_tx_dequeue(sc);
+ cpsw_write_4(sc, CPSW_CPDMA_CPDMA_EOI_VECTOR, 2);
+ CPSW_TX_UNLOCK(sc);
+}
+
+static void
+cpswp_tx_enqueue(struct cpswp_softc *sc)
+{
+ bus_dma_segment_t segs[CPSW_TXFRAGS];
+ struct cpsw_cpdma_bd bd;
+ struct cpsw_slot *first_new_slot, *last, *last_old_slot, *next, *slot;
+ struct mbuf *m0;
+ int error, nsegs, seg, added = 0, padlen;
+
+ /* Pull pending packets from IF queue and prep them for DMA. */
+ last = NULL;
+ first_new_slot = NULL;
+ last_old_slot = STAILQ_LAST(&sc->swsc->tx.active, cpsw_slot, next);
+ while ((slot = STAILQ_FIRST(&sc->swsc->tx.avail)) != NULL) {
+ IF_DEQUEUE(&sc->ifp->if_snd, m0);
+ if (m0 == NULL)
+ break;
+
+ slot->mbuf = m0;
+ padlen = ETHER_MIN_LEN - ETHER_CRC_LEN - m0->m_pkthdr.len;
+ if (padlen < 0)
+ padlen = 0;
+ else if (padlen > 0)
+ m_append(slot->mbuf, padlen, sc->swsc->nullpad);
+
+ /* Create mapping in DMA memory */
+ error = bus_dmamap_load_mbuf_sg(sc->swsc->mbuf_dtag,
+ slot->dmamap, slot->mbuf, segs, &nsegs, BUS_DMA_NOWAIT);
+ /* If the packet is too fragmented, try to simplify. */
+ if (error == EFBIG ||
+ (error == 0 && nsegs > sc->swsc->tx.avail_queue_len)) {
+ bus_dmamap_unload(sc->swsc->mbuf_dtag, slot->dmamap);
+ m0 = m_defrag(slot->mbuf, M_NOWAIT);
+ if (m0 == NULL) {
+ device_printf(sc->dev,
+ "Can't defragment packet; dropping\n");
+ m_freem(slot->mbuf);
+ } else {
+ CPSW_DEBUGF(sc->swsc,
+ ("Requeueing defragmented packet"));
+ IF_PREPEND(&sc->ifp->if_snd, m0);
+ }
+ slot->mbuf = NULL;
+ continue;
+ }
+ if (error != 0) {
+ device_printf(sc->dev,
+ "%s: Can't setup DMA (error=%d), dropping packet\n",
+ __func__, error);
+ bus_dmamap_unload(sc->swsc->mbuf_dtag, slot->dmamap);
+ m_freem(slot->mbuf);
+ slot->mbuf = NULL;
+ break;
+ }
+
+ bus_dmamap_sync(sc->swsc->mbuf_dtag, slot->dmamap,
+ BUS_DMASYNC_PREWRITE);
+
+ CPSW_DEBUGF(sc->swsc,
+ ("Queueing TX packet: %d segments + %d pad bytes",
+ nsegs, padlen));
+
+ if (first_new_slot == NULL)
+ first_new_slot = slot;
+
+ /* Link from the previous descriptor. */
+ if (last != NULL)
+ cpsw_cpdma_write_bd_next(sc->swsc, last, slot);
+
+ slot->ifp = sc->ifp;
+
+ /* If there is only one segment, the for() loop
+ * gets skipped and the single buffer gets set up
+ * as both SOP and EOP. */
+ if (nsegs > 1) {
+ next = STAILQ_NEXT(slot, next);
+ bd.next = cpsw_cpdma_bd_paddr(sc->swsc, next);
+ } else
+ bd.next = 0;
+ /* Start by setting up the first buffer. */
+ bd.bufptr = segs[0].ds_addr;
+ bd.bufoff = 0;
+ bd.buflen = segs[0].ds_len;
+ bd.pktlen = m_length(slot->mbuf, NULL);
+ bd.flags = CPDMA_BD_SOP | CPDMA_BD_OWNER;
+ if (sc->swsc->dualemac) {
+ bd.flags |= CPDMA_BD_TO_PORT;
+ bd.flags |= ((sc->unit + 1) & CPDMA_BD_PORT_MASK);
+ }
+ for (seg = 1; seg < nsegs; ++seg) {
+ /* Save the previous buffer (which isn't EOP) */
+ cpsw_cpdma_write_bd(sc->swsc, slot, &bd);
+ STAILQ_REMOVE_HEAD(&sc->swsc->tx.avail, next);
+ STAILQ_INSERT_TAIL(&sc->swsc->tx.active, slot, next);
+ slot = STAILQ_FIRST(&sc->swsc->tx.avail);
+
+ /* Setup next buffer (which isn't SOP) */
+ if (nsegs > seg + 1) {
+ next = STAILQ_NEXT(slot, next);
+ bd.next = cpsw_cpdma_bd_paddr(sc->swsc, next);
+ } else
+ bd.next = 0;
+ bd.bufptr = segs[seg].ds_addr;
+ bd.bufoff = 0;
+ bd.buflen = segs[seg].ds_len;
+ bd.pktlen = 0;
+ bd.flags = CPDMA_BD_OWNER;
+ }
+
+ /* Save the final buffer. */
+ bd.flags |= CPDMA_BD_EOP;
+ cpsw_cpdma_write_bd(sc->swsc, slot, &bd);
+ STAILQ_REMOVE_HEAD(&sc->swsc->tx.avail, next);
+ STAILQ_INSERT_TAIL(&sc->swsc->tx.active, slot, next);
+
+ last = slot;
+ added += nsegs;
+ if (nsegs > sc->swsc->tx.longest_chain)
+ sc->swsc->tx.longest_chain = nsegs;
+
+ BPF_MTAP(sc->ifp, m0);
+ }
+
+ if (first_new_slot == NULL)
+ return;
+
+ /* Attach the list of new buffers to the hardware TX queue. */
+ if (last_old_slot != NULL &&
+ (cpsw_cpdma_read_bd_flags(sc->swsc, last_old_slot) &
+ CPDMA_BD_EOQ) == 0) {
+ /* Add buffers to end of current queue. */
+ cpsw_cpdma_write_bd_next(sc->swsc, last_old_slot,
+ first_new_slot);
+ } else {
+ /* Start a fresh queue. */
+ cpsw_write_hdp_slot(sc->swsc, &sc->swsc->tx, first_new_slot);
+ }
+ sc->swsc->tx.queue_adds += added;
+ sc->swsc->tx.avail_queue_len -= added;
+ sc->swsc->tx.active_queue_len += added;
+ if (sc->swsc->tx.active_queue_len > sc->swsc->tx.max_active_queue_len) {
+ sc->swsc->tx.max_active_queue_len = sc->swsc->tx.active_queue_len;
+ }
+ CPSW_DEBUGF(sc->swsc, ("Queued %d TX packet(s)", added));
+}
+
+static int
+cpsw_tx_dequeue(struct cpsw_softc *sc)
+{
+ struct cpsw_slot *slot, *last_removed_slot = NULL;
+ struct cpsw_cpdma_bd bd;
+ uint32_t flags, removed = 0;
+
+ /* Pull completed buffers off the hardware TX queue. */
+ slot = STAILQ_FIRST(&sc->tx.active);
+ while (slot != NULL) {
+ flags = cpsw_cpdma_read_bd_flags(sc, slot);
+
+ /* TearDown complete is only marked on the SOP for the packet. */
+ if ((flags & (CPDMA_BD_SOP | CPDMA_BD_TDOWNCMPLT)) ==
+ (CPDMA_BD_SOP | CPDMA_BD_TDOWNCMPLT)) {
+ sc->tx.teardown = 1;
+ }
+
+ if ((flags & (CPDMA_BD_SOP | CPDMA_BD_OWNER)) ==
+ (CPDMA_BD_SOP | CPDMA_BD_OWNER) && sc->tx.teardown == 0)
+ break; /* Hardware is still using this packet. */
+
+ bus_dmamap_sync(sc->mbuf_dtag, slot->dmamap, BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(sc->mbuf_dtag, slot->dmamap);
+ m_freem(slot->mbuf);
+ slot->mbuf = NULL;
+
+ if (slot->ifp) {
+ if (sc->tx.teardown == 0)
+ if_inc_counter(slot->ifp, IFCOUNTER_OPACKETS, 1);
+ else
+ if_inc_counter(slot->ifp, IFCOUNTER_OQDROPS, 1);
+ }
+
+ /* Dequeue any additional buffers used by this packet. */
+ while (slot != NULL && slot->mbuf == NULL) {
+ STAILQ_REMOVE_HEAD(&sc->tx.active, next);
+ STAILQ_INSERT_TAIL(&sc->tx.avail, slot, next);
+ ++removed;
+ last_removed_slot = slot;
+ slot = STAILQ_FIRST(&sc->tx.active);
+ }
+
+ cpsw_write_cp_slot(sc, &sc->tx, last_removed_slot);
+
+ /* Restart the TX queue if necessary. */
+ cpsw_cpdma_read_bd(sc, last_removed_slot, &bd);
+ if (slot != NULL && bd.next != 0 && (bd.flags &
+ (CPDMA_BD_EOP | CPDMA_BD_OWNER | CPDMA_BD_EOQ)) ==
+ (CPDMA_BD_EOP | CPDMA_BD_EOQ)) {
+ cpsw_write_hdp_slot(sc, &sc->tx, slot);
+ sc->tx.queue_restart++;
+ break;
+ }
+ }
+
+ if (removed != 0) {
+ sc->tx.queue_removes += removed;
+ sc->tx.active_queue_len -= removed;
+ sc->tx.avail_queue_len += removed;
+ if (sc->tx.avail_queue_len > sc->tx.max_avail_queue_len)
+ sc->tx.max_avail_queue_len = sc->tx.avail_queue_len;
+ CPSW_DEBUGF(sc, ("TX removed %d completed packet(s)", removed));
+ }
+
+ if (sc->tx.teardown && STAILQ_EMPTY(&sc->tx.active)) {
+ CPSW_DEBUGF(sc, ("TX teardown is complete"));
+ sc->tx.teardown = 0;
+ sc->tx.running = 0;
+ }
+
+ return (removed);
+}
+
+/*
+ *
+ * Miscellaneous interrupts.
+ *
+ */
+
+static void
+cpsw_intr_rx_thresh(void *arg)
+{
+ struct cpsw_softc *sc;
+ struct ifnet *ifp;
+ struct mbuf *received, *next;
+
+ sc = (struct cpsw_softc *)arg;
+ CPSW_RX_LOCK(sc);
+ received = cpsw_rx_dequeue(sc);
+ cpsw_rx_enqueue(sc);
+ cpsw_write_4(sc, CPSW_CPDMA_CPDMA_EOI_VECTOR, 0);
+ CPSW_RX_UNLOCK(sc);
+
+ while (received != NULL) {
+ next = received->m_nextpkt;
+ received->m_nextpkt = NULL;
+ ifp = received->m_pkthdr.rcvif;
+ (*ifp->if_input)(ifp, received);
+ if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
+ received = next;
+ }
+}
+
+static void
+cpsw_intr_misc_host_error(struct cpsw_softc *sc)
+{
+ uint32_t intstat;
+ uint32_t dmastat;
+ int txerr, rxerr, txchan, rxchan;
+
+ printf("\n\n");
+ device_printf(sc->dev,
+ "HOST ERROR: PROGRAMMING ERROR DETECTED BY HARDWARE\n");
+ printf("\n\n");
+ intstat = cpsw_read_4(sc, CPSW_CPDMA_DMA_INTSTAT_MASKED);
+ device_printf(sc->dev, "CPSW_CPDMA_DMA_INTSTAT_MASKED=0x%x\n", intstat);
+ dmastat = cpsw_read_4(sc, CPSW_CPDMA_DMASTATUS);
+ device_printf(sc->dev, "CPSW_CPDMA_DMASTATUS=0x%x\n", dmastat);
+
+ txerr = (dmastat >> 20) & 15;
+ txchan = (dmastat >> 16) & 7;
+ rxerr = (dmastat >> 12) & 15;
+ rxchan = (dmastat >> 8) & 7;
+
+ switch (txerr) {
+ case 0: break;
+ case 1: printf("SOP error on TX channel %d\n", txchan);
+ break;
+ case 2: printf("Ownership bit not set on SOP buffer on TX channel %d\n", txchan);
+ break;
+ case 3: printf("Zero Next Buffer but not EOP on TX channel %d\n", txchan);
+ break;
+ case 4: printf("Zero Buffer Pointer on TX channel %d\n", txchan);
+ break;
+ case 5: printf("Zero Buffer Length on TX channel %d\n", txchan);
+ break;
+ case 6: printf("Packet length error on TX channel %d\n", txchan);
+ break;
+ default: printf("Unknown error on TX channel %d\n", txchan);
+ break;
+ }
+
+ if (txerr != 0) {
+ printf("CPSW_CPDMA_TX%d_HDP=0x%x\n",
+ txchan, cpsw_read_4(sc, CPSW_CPDMA_TX_HDP(txchan)));
+ printf("CPSW_CPDMA_TX%d_CP=0x%x\n",
+ txchan, cpsw_read_4(sc, CPSW_CPDMA_TX_CP(txchan)));
+ cpsw_dump_queue(sc, &sc->tx.active);
+ }
+
+ switch (rxerr) {
+ case 0: break;
+ case 2: printf("Ownership bit not set on RX channel %d\n", rxchan);
+ break;
+ case 4: printf("Zero Buffer Pointer on RX channel %d\n", rxchan);
+ break;
+ case 5: printf("Zero Buffer Length on RX channel %d\n", rxchan);
+ break;
+ case 6: printf("Buffer offset too big on RX channel %d\n", rxchan);
+ break;
+ default: printf("Unknown RX error on RX channel %d\n", rxchan);
+ break;
+ }
+
+ if (rxerr != 0) {
+ printf("CPSW_CPDMA_RX%d_HDP=0x%x\n",
+ rxchan, cpsw_read_4(sc,CPSW_CPDMA_RX_HDP(rxchan)));
+ printf("CPSW_CPDMA_RX%d_CP=0x%x\n",
+ rxchan, cpsw_read_4(sc, CPSW_CPDMA_RX_CP(rxchan)));
+ cpsw_dump_queue(sc, &sc->rx.active);
+ }
+
+ printf("\nALE Table\n");
+ cpsw_ale_dump_table(sc);
+
+ // XXX do something useful here??
+ panic("CPSW HOST ERROR INTERRUPT");
+
+ // Suppress this interrupt in the future.
+ cpsw_write_4(sc, CPSW_CPDMA_DMA_INTMASK_CLEAR, intstat);
+ printf("XXX HOST ERROR INTERRUPT SUPPRESSED\n");
+ // The watchdog will probably reset the controller
+ // in a little while. It will probably fail again.
+}
+
+static void
+cpsw_intr_misc(void *arg)
+{
+ struct cpsw_softc *sc = arg;
+ uint32_t stat = cpsw_read_4(sc, CPSW_WR_C_MISC_STAT(0));
+
+ if (stat & CPSW_WR_C_MISC_EVNT_PEND)
+ CPSW_DEBUGF(sc, ("Time sync event interrupt unimplemented"));
+ if (stat & CPSW_WR_C_MISC_STAT_PEND)
+ cpsw_stats_collect(sc);
+ if (stat & CPSW_WR_C_MISC_HOST_PEND)
+ cpsw_intr_misc_host_error(sc);
+ if (stat & CPSW_WR_C_MISC_MDIOLINK) {
+ cpsw_write_4(sc, MDIOLINKINTMASKED,
+ cpsw_read_4(sc, MDIOLINKINTMASKED));
+ }
+ if (stat & CPSW_WR_C_MISC_MDIOUSER) {
+ CPSW_DEBUGF(sc,
+ ("MDIO operation completed interrupt unimplemented"));
+ }
+ cpsw_write_4(sc, CPSW_CPDMA_CPDMA_EOI_VECTOR, 3);
+}
+
+/*
+ *
+ * Periodic Checks and Watchdog.
+ *
+ */
+
+static void
+cpswp_tick(void *msc)
+{
+ struct cpswp_softc *sc = msc;
+
+ /* Check for media type change */
+ mii_tick(sc->mii);
+ if (sc->media_status != sc->mii->mii_media.ifm_media) {
+ printf("%s: media type changed (ifm_media=%x)\n", __func__,
+ sc->mii->mii_media.ifm_media);
+ cpswp_ifmedia_upd(sc->ifp);
+ }
+
+ /* Schedule another timeout one second from now */
+ callout_reset(&sc->mii_callout, hz, cpswp_tick, sc);
+}
+
+static void
+cpswp_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ struct cpswp_softc *sc;
+ struct mii_data *mii;
+
+ sc = ifp->if_softc;
+ CPSW_DEBUGF(sc->swsc, (""));
+ CPSW_PORT_LOCK(sc);
+
+ mii = sc->mii;
+ mii_pollstat(mii);
+
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+ CPSW_PORT_UNLOCK(sc);
+}
+
+static int
+cpswp_ifmedia_upd(struct ifnet *ifp)
+{
+ struct cpswp_softc *sc;
+
+ sc = ifp->if_softc;
+ CPSW_DEBUGF(sc->swsc, (""));
+ CPSW_PORT_LOCK(sc);
+ mii_mediachg(sc->mii);
+ sc->media_status = sc->mii->mii_media.ifm_media;
+ CPSW_PORT_UNLOCK(sc);
+
+ return (0);
+}
+
+static void
+cpsw_tx_watchdog_full_reset(struct cpsw_softc *sc)
+{
+ struct cpswp_softc *psc;
+ int i;
+
+ cpsw_debugf_head("CPSW watchdog");
+ device_printf(sc->dev, "watchdog timeout\n");
+ printf("CPSW_CPDMA_TX%d_HDP=0x%x\n", 0,
+ cpsw_read_4(sc, CPSW_CPDMA_TX_HDP(0)));
+ printf("CPSW_CPDMA_TX%d_CP=0x%x\n", 0,
+ cpsw_read_4(sc, CPSW_CPDMA_TX_CP(0)));
+ cpsw_dump_queue(sc, &sc->tx.active);
+ for (i = 0; i < CPSW_PORTS; i++) {
+ if (!sc->dualemac && i != sc->active_slave)
+ continue;
+ psc = device_get_softc(sc->port[i].dev);
+ CPSW_PORT_LOCK(psc);
+ cpswp_stop_locked(psc);
+ CPSW_PORT_UNLOCK(psc);
+ }
+}
+
+static void
+cpsw_tx_watchdog(void *msc)
+{
+ struct cpsw_softc *sc;
+
+ sc = msc;
+ CPSW_TX_LOCK(sc);
+ if (sc->tx.active_queue_len == 0 || !sc->tx.running) {
+ sc->watchdog.timer = 0; /* Nothing to do. */
+ } else if (sc->tx.queue_removes > sc->tx.queue_removes_at_last_tick) {
+ sc->watchdog.timer = 0; /* Stuff done while we weren't looking. */
+ } else if (cpsw_tx_dequeue(sc) > 0) {
+ sc->watchdog.timer = 0; /* We just did something. */
+ } else {
+ /* There was something to do but it didn't get done. */
+ ++sc->watchdog.timer;
+ if (sc->watchdog.timer > 5) {
+ sc->watchdog.timer = 0;
+ ++sc->watchdog.resets;
+ cpsw_tx_watchdog_full_reset(sc);
+ }
+ }
+ sc->tx.queue_removes_at_last_tick = sc->tx.queue_removes;
+ CPSW_TX_UNLOCK(sc);
+
+ /* Schedule another timeout one second from now */
+ callout_reset(&sc->watchdog.callout, hz, cpsw_tx_watchdog, sc);
+}
+
+/*
+ *
+ * ALE support routines.
+ *
+ */
+
+static void
+cpsw_ale_read_entry(struct cpsw_softc *sc, uint16_t idx, uint32_t *ale_entry)
+{
+ cpsw_write_4(sc, CPSW_ALE_TBLCTL, idx & 1023);
+ ale_entry[0] = cpsw_read_4(sc, CPSW_ALE_TBLW0);
+ ale_entry[1] = cpsw_read_4(sc, CPSW_ALE_TBLW1);
+ ale_entry[2] = cpsw_read_4(sc, CPSW_ALE_TBLW2);
+}
+
+static void
+cpsw_ale_write_entry(struct cpsw_softc *sc, uint16_t idx, uint32_t *ale_entry)
+{
+ cpsw_write_4(sc, CPSW_ALE_TBLW0, ale_entry[0]);
+ cpsw_write_4(sc, CPSW_ALE_TBLW1, ale_entry[1]);
+ cpsw_write_4(sc, CPSW_ALE_TBLW2, ale_entry[2]);
+ cpsw_write_4(sc, CPSW_ALE_TBLCTL, 1 << 31 | (idx & 1023));
+}
+
+static void
+cpsw_ale_remove_all_mc_entries(struct cpsw_softc *sc)
+{
+ int i;
+ uint32_t ale_entry[3];
+
+ /* First four entries are link address and broadcast. */
+ for (i = 10; i < CPSW_MAX_ALE_ENTRIES; i++) {
+ cpsw_ale_read_entry(sc, i, ale_entry);
+ if ((ALE_TYPE(ale_entry) == ALE_TYPE_ADDR ||
+ ALE_TYPE(ale_entry) == ALE_TYPE_VLAN_ADDR) &&
+ ALE_MCAST(ale_entry) == 1) { /* MCast link addr */
+ ale_entry[0] = ale_entry[1] = ale_entry[2] = 0;
+ cpsw_ale_write_entry(sc, i, ale_entry);
+ }
+ }
+}
+
+static int
+cpsw_ale_mc_entry_set(struct cpsw_softc *sc, uint8_t portmap, int vlan,
+ uint8_t *mac)
+{
+ int free_index = -1, matching_index = -1, i;
+ uint32_t ale_entry[3], ale_type;
+
+ /* Find a matching entry or a free entry. */
+ for (i = 10; i < CPSW_MAX_ALE_ENTRIES; i++) {
+ cpsw_ale_read_entry(sc, i, ale_entry);
+
+ /* Entry Type[61:60] is 0 for free entry */
+ if (free_index < 0 && ALE_TYPE(ale_entry) == 0)
+ free_index = i;
+
+ if ((((ale_entry[1] >> 8) & 0xFF) == mac[0]) &&
+ (((ale_entry[1] >> 0) & 0xFF) == mac[1]) &&
+ (((ale_entry[0] >>24) & 0xFF) == mac[2]) &&
+ (((ale_entry[0] >>16) & 0xFF) == mac[3]) &&
+ (((ale_entry[0] >> 8) & 0xFF) == mac[4]) &&
+ (((ale_entry[0] >> 0) & 0xFF) == mac[5])) {
+ matching_index = i;
+ break;
+ }
+ }
+
+ if (matching_index < 0) {
+ if (free_index < 0)
+ return (ENOMEM);
+ i = free_index;
+ }
+
+ if (vlan != -1)
+ ale_type = ALE_TYPE_VLAN_ADDR << 28 | vlan << 16;
+ else
+ ale_type = ALE_TYPE_ADDR << 28;
+
+ /* Set MAC address */
+ ale_entry[0] = mac[2] << 24 | mac[3] << 16 | mac[4] << 8 | mac[5];
+ ale_entry[1] = mac[0] << 8 | mac[1];
+
+ /* Entry type[61:60] and Mcast fwd state[63:62] is fw(3). */
+ ale_entry[1] |= ALE_MCAST_FWD | ale_type;
+
+ /* Set portmask [68:66] */
+ ale_entry[2] = (portmap & 7) << 2;
+
+ cpsw_ale_write_entry(sc, i, ale_entry);
+
+ return 0;
+}
+
+static void
+cpsw_ale_dump_table(struct cpsw_softc *sc) {
+ int i;
+ uint32_t ale_entry[3];
+ for (i = 0; i < CPSW_MAX_ALE_ENTRIES; i++) {
+ cpsw_ale_read_entry(sc, i, ale_entry);
+ switch (ALE_TYPE(ale_entry)) {
+ case ALE_TYPE_VLAN:
+ printf("ALE[%4u] %08x %08x %08x ", i, ale_entry[2],
+ ale_entry[1], ale_entry[0]);
+ printf("type: %u ", ALE_TYPE(ale_entry));
+ printf("vlan: %u ", ALE_VLAN(ale_entry));
+ printf("untag: %u ", ALE_VLAN_UNTAG(ale_entry));
+ printf("reg flood: %u ", ALE_VLAN_REGFLOOD(ale_entry));
+ printf("unreg flood: %u ", ALE_VLAN_UNREGFLOOD(ale_entry));
+ printf("members: %u ", ALE_VLAN_MEMBERS(ale_entry));
+ printf("\n");
+ break;
+ case ALE_TYPE_ADDR:
+ case ALE_TYPE_VLAN_ADDR:
+ printf("ALE[%4u] %08x %08x %08x ", i, ale_entry[2],
+ ale_entry[1], ale_entry[0]);
+ printf("type: %u ", ALE_TYPE(ale_entry));
+ printf("mac: %02x:%02x:%02x:%02x:%02x:%02x ",
+ (ale_entry[1] >> 8) & 0xFF,
+ (ale_entry[1] >> 0) & 0xFF,
+ (ale_entry[0] >>24) & 0xFF,
+ (ale_entry[0] >>16) & 0xFF,
+ (ale_entry[0] >> 8) & 0xFF,
+ (ale_entry[0] >> 0) & 0xFF);
+ printf(ALE_MCAST(ale_entry) ? "mcast " : "ucast ");
+ if (ALE_TYPE(ale_entry) == ALE_TYPE_VLAN_ADDR)
+ printf("vlan: %u ", ALE_VLAN(ale_entry));
+ printf("port: %u ", ALE_PORTS(ale_entry));
+ printf("\n");
+ break;
+ }
+ }
+ printf("\n");
+}
+
+static u_int
+cpswp_set_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
+{
+ struct cpswp_softc *sc = arg;
+ uint32_t portmask;
+
+ if (sc->swsc->dualemac)
+ portmask = 1 << (sc->unit + 1) | 1 << 0;
+ else
+ portmask = 7;
+
+ cpsw_ale_mc_entry_set(sc->swsc, portmask, sc->vlan, LLADDR(sdl));
+
+ return (1);
+}
+
+static int
+cpswp_ale_update_addresses(struct cpswp_softc *sc, int purge)
+{
+ uint8_t *mac;
+ uint32_t ale_entry[3], ale_type, portmask;
+
+ if (sc->swsc->dualemac) {
+ ale_type = ALE_TYPE_VLAN_ADDR << 28 | sc->vlan << 16;
+ portmask = 1 << (sc->unit + 1) | 1 << 0;
+ } else {
+ ale_type = ALE_TYPE_ADDR << 28;
+ portmask = 7;
+ }
+
+ /*
+ * Route incoming packets for our MAC address to Port 0 (host).
+ * For simplicity, keep this entry at table index 0 for port 1 and
+ * at index 2 for port 2 in the ALE.
+ */
+ mac = LLADDR((struct sockaddr_dl *)sc->ifp->if_addr->ifa_addr);
+ ale_entry[0] = mac[2] << 24 | mac[3] << 16 | mac[4] << 8 | mac[5];
+ ale_entry[1] = ale_type | mac[0] << 8 | mac[1]; /* addr entry + mac */
+ ale_entry[2] = 0; /* port = 0 */
+ cpsw_ale_write_entry(sc->swsc, 0 + 2 * sc->unit, ale_entry);
+
+ /* Set outgoing MAC Address for slave port. */
+ cpsw_write_4(sc->swsc, CPSW_PORT_P_SA_HI(sc->unit + 1),
+ mac[3] << 24 | mac[2] << 16 | mac[1] << 8 | mac[0]);
+ cpsw_write_4(sc->swsc, CPSW_PORT_P_SA_LO(sc->unit + 1),
+ mac[5] << 8 | mac[4]);
+
+ /* Keep the broadcast address at table entry 1 (or 3). */
+ ale_entry[0] = 0xffffffff; /* Lower 32 bits of MAC */
+ /* ALE_MCAST_FWD, Addr type, upper 16 bits of Mac */
+ ale_entry[1] = ALE_MCAST_FWD | ale_type | 0xffff;
+ ale_entry[2] = portmask << 2;
+ cpsw_ale_write_entry(sc->swsc, 1 + 2 * sc->unit, ale_entry);
+
+ /* SIOCDELMULTI doesn't specify the particular address
+ being removed, so we have to remove all and rebuild. */
+ if (purge)
+ cpsw_ale_remove_all_mc_entries(sc->swsc);
+
+ /* Set other multicast addrs desired. */
+ if_foreach_llmaddr(sc->ifp, cpswp_set_maddr, sc);
+
+ return (0);
+}
+
+static int
+cpsw_ale_update_vlan_table(struct cpsw_softc *sc, int vlan, int ports,
+ int untag, int mcregflood, int mcunregflood)
+{
+ int free_index, i, matching_index;
+ uint32_t ale_entry[3];
+
+ free_index = matching_index = -1;
+ /* Find a matching entry or a free entry. */
+ for (i = 5; i < CPSW_MAX_ALE_ENTRIES; i++) {
+ cpsw_ale_read_entry(sc, i, ale_entry);
+
+ /* Entry Type[61:60] is 0 for free entry */
+ if (free_index < 0 && ALE_TYPE(ale_entry) == 0)
+ free_index = i;
+
+ if (ALE_VLAN(ale_entry) == vlan) {
+ matching_index = i;
+ break;
+ }
+ }
+
+ if (matching_index < 0) {
+ if (free_index < 0)
+ return (-1);
+ i = free_index;
+ }
+
+ ale_entry[0] = (untag & 7) << 24 | (mcregflood & 7) << 16 |
+ (mcunregflood & 7) << 8 | (ports & 7);
+ ale_entry[1] = ALE_TYPE_VLAN << 28 | vlan << 16;
+ ale_entry[2] = 0;
+ cpsw_ale_write_entry(sc, i, ale_entry);
+
+ return (0);
+}
+
+/*
+ *
+ * Statistics and Sysctls.
+ *
+ */
+
+#if 0
+static void
+cpsw_stats_dump(struct cpsw_softc *sc)
+{
+ int i;
+ uint32_t r;
+
+ for (i = 0; i < CPSW_SYSCTL_COUNT; ++i) {
+ r = cpsw_read_4(sc, CPSW_STATS_OFFSET +
+ cpsw_stat_sysctls[i].reg);
+ CPSW_DEBUGF(sc, ("%s: %ju + %u = %ju", cpsw_stat_sysctls[i].oid,
+ (intmax_t)sc->shadow_stats[i], r,
+ (intmax_t)sc->shadow_stats[i] + r));
+ }
+}
+#endif
+
+static void
+cpsw_stats_collect(struct cpsw_softc *sc)
+{
+ int i;
+ uint32_t r;
+
+ CPSW_DEBUGF(sc, ("Controller shadow statistics updated."));
+
+ for (i = 0; i < CPSW_SYSCTL_COUNT; ++i) {
+ r = cpsw_read_4(sc, CPSW_STATS_OFFSET +
+ cpsw_stat_sysctls[i].reg);
+ sc->shadow_stats[i] += r;
+ cpsw_write_4(sc, CPSW_STATS_OFFSET + cpsw_stat_sysctls[i].reg,
+ r);
+ }
+}
+
+static int
+cpsw_stats_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ struct cpsw_softc *sc;
+ struct cpsw_stat *stat;
+ uint64_t result;
+
+ sc = (struct cpsw_softc *)arg1;
+ stat = &cpsw_stat_sysctls[oidp->oid_number];
+ result = sc->shadow_stats[oidp->oid_number];
+ result += cpsw_read_4(sc, CPSW_STATS_OFFSET + stat->reg);
+ return (sysctl_handle_64(oidp, &result, 0, req));
+}
+
+static int
+cpsw_stat_attached(SYSCTL_HANDLER_ARGS)
+{
+ struct cpsw_softc *sc;
+ struct bintime t;
+ unsigned result;
+
+ sc = (struct cpsw_softc *)arg1;
+ getbinuptime(&t);
+ bintime_sub(&t, &sc->attach_uptime);
+ result = t.sec;
+ return (sysctl_handle_int(oidp, &result, 0, req));
+}
+
+static int
+cpsw_intr_coalesce(SYSCTL_HANDLER_ARGS)
+{
+ int error;
+ struct cpsw_softc *sc;
+ uint32_t ctrl, intr_per_ms;
+
+ sc = (struct cpsw_softc *)arg1;
+ error = sysctl_handle_int(oidp, &sc->coal_us, 0, req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ ctrl = cpsw_read_4(sc, CPSW_WR_INT_CONTROL);
+ ctrl &= ~(CPSW_WR_INT_PACE_EN | CPSW_WR_INT_PRESCALE_MASK);
+ if (sc->coal_us == 0) {
+ /* Disable the interrupt pace hardware. */
+ cpsw_write_4(sc, CPSW_WR_INT_CONTROL, ctrl);
+ cpsw_write_4(sc, CPSW_WR_C_RX_IMAX(0), 0);
+ cpsw_write_4(sc, CPSW_WR_C_TX_IMAX(0), 0);
+ return (0);
+ }
+
+ if (sc->coal_us > CPSW_WR_C_IMAX_US_MAX)
+ sc->coal_us = CPSW_WR_C_IMAX_US_MAX;
+ if (sc->coal_us < CPSW_WR_C_IMAX_US_MIN)
+ sc->coal_us = CPSW_WR_C_IMAX_US_MIN;
+ intr_per_ms = 1000 / sc->coal_us;
+ /* Just to make sure... */
+ if (intr_per_ms > CPSW_WR_C_IMAX_MAX)
+ intr_per_ms = CPSW_WR_C_IMAX_MAX;
+ if (intr_per_ms < CPSW_WR_C_IMAX_MIN)
+ intr_per_ms = CPSW_WR_C_IMAX_MIN;
+
+ /* Set the prescale to produce 4us pulses from the 125 Mhz clock. */
+ ctrl |= (125 * 4) & CPSW_WR_INT_PRESCALE_MASK;
+
+ /* Enable the interrupt pace hardware. */
+ cpsw_write_4(sc, CPSW_WR_C_RX_IMAX(0), intr_per_ms);
+ cpsw_write_4(sc, CPSW_WR_C_TX_IMAX(0), intr_per_ms);
+ ctrl |= CPSW_WR_INT_C0_RX_PULSE | CPSW_WR_INT_C0_TX_PULSE;
+ cpsw_write_4(sc, CPSW_WR_INT_CONTROL, ctrl);
+
+ return (0);
+}
+
+static int
+cpsw_stat_uptime(SYSCTL_HANDLER_ARGS)
+{
+ struct cpsw_softc *swsc;
+ struct cpswp_softc *sc;
+ struct bintime t;
+ unsigned result;
+
+ swsc = arg1;
+ sc = device_get_softc(swsc->port[arg2].dev);
+ if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ getbinuptime(&t);
+ bintime_sub(&t, &sc->init_uptime);
+ result = t.sec;
+ } else
+ result = 0;
+ return (sysctl_handle_int(oidp, &result, 0, req));
+}
+
+static void
+cpsw_add_queue_sysctls(struct sysctl_ctx_list *ctx, struct sysctl_oid *node,
+ struct cpsw_queue *queue)
+{
+ struct sysctl_oid_list *parent;
+
+ parent = SYSCTL_CHILDREN(node);
+ SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "totalBuffers",
+ CTLFLAG_RD, &queue->queue_slots, 0,
+ "Total buffers currently assigned to this queue");
+ SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "activeBuffers",
+ CTLFLAG_RD, &queue->active_queue_len, 0,
+ "Buffers currently registered with hardware controller");
+ SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "maxActiveBuffers",
+ CTLFLAG_RD, &queue->max_active_queue_len, 0,
+ "Max value of activeBuffers since last driver reset");
+ SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "availBuffers",
+ CTLFLAG_RD, &queue->avail_queue_len, 0,
+ "Buffers allocated to this queue but not currently "
+ "registered with hardware controller");
+ SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "maxAvailBuffers",
+ CTLFLAG_RD, &queue->max_avail_queue_len, 0,
+ "Max value of availBuffers since last driver reset");
+ SYSCTL_ADD_UINT(ctx, parent, OID_AUTO, "totalEnqueued",
+ CTLFLAG_RD, &queue->queue_adds, 0,
+ "Total buffers added to queue");
+ SYSCTL_ADD_UINT(ctx, parent, OID_AUTO, "totalDequeued",
+ CTLFLAG_RD, &queue->queue_removes, 0,
+ "Total buffers removed from queue");
+ SYSCTL_ADD_UINT(ctx, parent, OID_AUTO, "queueRestart",
+ CTLFLAG_RD, &queue->queue_restart, 0,
+ "Total times the queue has been restarted");
+ SYSCTL_ADD_UINT(ctx, parent, OID_AUTO, "longestChain",
+ CTLFLAG_RD, &queue->longest_chain, 0,
+ "Max buffers used for a single packet");
+}
+
+static void
+cpsw_add_watchdog_sysctls(struct sysctl_ctx_list *ctx, struct sysctl_oid *node,
+ struct cpsw_softc *sc)
+{
+ struct sysctl_oid_list *parent;
+
+ parent = SYSCTL_CHILDREN(node);
+ SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "resets",
+ CTLFLAG_RD, &sc->watchdog.resets, 0,
+ "Total number of watchdog resets");
+}
+
+static void
+cpsw_add_sysctls(struct cpsw_softc *sc)
+{
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid *stats_node, *queue_node, *node;
+ struct sysctl_oid_list *parent, *stats_parent, *queue_parent;
+ struct sysctl_oid_list *ports_parent, *port_parent;
+ char port[16];
+ int i;
+
+ ctx = device_get_sysctl_ctx(sc->dev);
+ parent = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev));
+
+ SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "debug",
+ CTLFLAG_RW, &sc->debug, 0, "Enable switch debug messages");
+
+ SYSCTL_ADD_PROC(ctx, parent, OID_AUTO, "attachedSecs",
+ CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
+ sc, 0, cpsw_stat_attached, "IU",
+ "Time since driver attach");
+
+ SYSCTL_ADD_PROC(ctx, parent, OID_AUTO, "intr_coalesce_us",
+ CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
+ sc, 0, cpsw_intr_coalesce, "IU",
+ "minimum time between interrupts");
+
+ node = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "ports",
+ CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "CPSW Ports Statistics");
+ ports_parent = SYSCTL_CHILDREN(node);
+ for (i = 0; i < CPSW_PORTS; i++) {
+ if (!sc->dualemac && i != sc->active_slave)
+ continue;
+ port[0] = '0' + i;
+ port[1] = '\0';
+ node = SYSCTL_ADD_NODE(ctx, ports_parent, OID_AUTO,
+ port, CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
+ "CPSW Port Statistics");
+ port_parent = SYSCTL_CHILDREN(node);
+ SYSCTL_ADD_PROC(ctx, port_parent, OID_AUTO, "uptime",
+ CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, i,
+ cpsw_stat_uptime, "IU", "Seconds since driver init");
+ }
+
+ stats_node = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "stats",
+ CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "CPSW Statistics");
+ stats_parent = SYSCTL_CHILDREN(stats_node);
+ for (i = 0; i < CPSW_SYSCTL_COUNT; ++i) {
+ SYSCTL_ADD_PROC(ctx, stats_parent, i,
+ cpsw_stat_sysctls[i].oid,
+ CTLTYPE_U64 | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
+ sc, 0, cpsw_stats_sysctl, "IU",
+ cpsw_stat_sysctls[i].oid);
+ }
+
+ queue_node = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "queue",
+ CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "CPSW Queue Statistics");
+ queue_parent = SYSCTL_CHILDREN(queue_node);
+
+ node = SYSCTL_ADD_NODE(ctx, queue_parent, OID_AUTO, "tx",
+ CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "TX Queue Statistics");
+ cpsw_add_queue_sysctls(ctx, node, &sc->tx);
+
+ node = SYSCTL_ADD_NODE(ctx, queue_parent, OID_AUTO, "rx",
+ CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "RX Queue Statistics");
+ cpsw_add_queue_sysctls(ctx, node, &sc->rx);
+
+ node = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "watchdog",
+ CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "Watchdog Statistics");
+ cpsw_add_watchdog_sysctls(ctx, node, sc);
+}
+
+#ifdef CPSW_ETHERSWITCH
+static etherswitch_info_t etherswitch_info = {
+ .es_nports = CPSW_PORTS + 1,
+ .es_nvlangroups = CPSW_VLANS,
+ .es_name = "TI Common Platform Ethernet Switch (CPSW)",
+ .es_vlan_caps = ETHERSWITCH_VLAN_DOT1Q,
+};
+
+static etherswitch_info_t *
+cpsw_getinfo(device_t dev)
+{
+ return (&etherswitch_info);
+}
+
+static int
+cpsw_getport(device_t dev, etherswitch_port_t *p)
+{
+ int err;
+ struct cpsw_softc *sc;
+ struct cpswp_softc *psc;
+ struct ifmediareq *ifmr;
+ uint32_t reg;
+
+ if (p->es_port < 0 || p->es_port > CPSW_PORTS)
+ return (ENXIO);
+
+ err = 0;
+ sc = device_get_softc(dev);
+ if (p->es_port == CPSW_CPU_PORT) {
+ p->es_flags |= ETHERSWITCH_PORT_CPU;
+ ifmr = &p->es_ifmr;
+ ifmr->ifm_current = ifmr->ifm_active =
+ IFM_ETHER | IFM_1000_T | IFM_FDX;
+ ifmr->ifm_mask = 0;
+ ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID;
+ ifmr->ifm_count = 0;
+ } else {
+ psc = device_get_softc(sc->port[p->es_port - 1].dev);
+ err = ifmedia_ioctl(psc->ifp, &p->es_ifr,
+ &psc->mii->mii_media, SIOCGIFMEDIA);
+ }
+ reg = cpsw_read_4(sc, CPSW_PORT_P_VLAN(p->es_port));
+ p->es_pvid = reg & ETHERSWITCH_VID_MASK;
+
+ reg = cpsw_read_4(sc, CPSW_ALE_PORTCTL(p->es_port));
+ if (reg & ALE_PORTCTL_DROP_UNTAGGED)
+ p->es_flags |= ETHERSWITCH_PORT_DROPUNTAGGED;
+ if (reg & ALE_PORTCTL_INGRESS)
+ p->es_flags |= ETHERSWITCH_PORT_INGRESS;
+
+ return (err);
+}
+
+static int
+cpsw_setport(device_t dev, etherswitch_port_t *p)
+{
+ struct cpsw_softc *sc;
+ struct cpswp_softc *psc;
+ struct ifmedia *ifm;
+ uint32_t reg;
+
+ if (p->es_port < 0 || p->es_port > CPSW_PORTS)
+ return (ENXIO);
+
+ sc = device_get_softc(dev);
+ if (p->es_pvid != 0) {
+ cpsw_write_4(sc, CPSW_PORT_P_VLAN(p->es_port),
+ p->es_pvid & ETHERSWITCH_VID_MASK);
+ }
+
+ reg = cpsw_read_4(sc, CPSW_ALE_PORTCTL(p->es_port));
+ if (p->es_flags & ETHERSWITCH_PORT_DROPUNTAGGED)
+ reg |= ALE_PORTCTL_DROP_UNTAGGED;
+ else
+ reg &= ~ALE_PORTCTL_DROP_UNTAGGED;
+ if (p->es_flags & ETHERSWITCH_PORT_INGRESS)
+ reg |= ALE_PORTCTL_INGRESS;
+ else
+ reg &= ~ALE_PORTCTL_INGRESS;
+ cpsw_write_4(sc, CPSW_ALE_PORTCTL(p->es_port), reg);
+
+ /* CPU port does not allow media settings. */
+ if (p->es_port == CPSW_CPU_PORT)
+ return (0);
+
+ psc = device_get_softc(sc->port[p->es_port - 1].dev);
+ ifm = &psc->mii->mii_media;
+
+ return (ifmedia_ioctl(psc->ifp, &p->es_ifr, ifm, SIOCSIFMEDIA));
+}
+
+static int
+cpsw_getconf(device_t dev, etherswitch_conf_t *conf)
+{
+
+ /* Return the VLAN mode. */
+ conf->cmd = ETHERSWITCH_CONF_VLAN_MODE;
+ conf->vlan_mode = ETHERSWITCH_VLAN_DOT1Q;
+
+ return (0);
+}
+
+static int
+cpsw_getvgroup(device_t dev, etherswitch_vlangroup_t *vg)
+{
+ int i, vid;
+ uint32_t ale_entry[3];
+ struct cpsw_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (vg->es_vlangroup >= CPSW_VLANS)
+ return (EINVAL);
+
+ vg->es_vid = 0;
+ vid = cpsw_vgroups[vg->es_vlangroup].vid;
+ if (vid == -1)
+ return (0);
+
+ for (i = 0; i < CPSW_MAX_ALE_ENTRIES; i++) {
+ cpsw_ale_read_entry(sc, i, ale_entry);
+ if (ALE_TYPE(ale_entry) != ALE_TYPE_VLAN)
+ continue;
+ if (vid != ALE_VLAN(ale_entry))
+ continue;
+
+ vg->es_fid = 0;
+ vg->es_vid = ALE_VLAN(ale_entry) | ETHERSWITCH_VID_VALID;
+ vg->es_member_ports = ALE_VLAN_MEMBERS(ale_entry);
+ vg->es_untagged_ports = ALE_VLAN_UNTAG(ale_entry);
+ }
+
+ return (0);
+}
+
+static void
+cpsw_remove_vlan(struct cpsw_softc *sc, int vlan)
+{
+ int i;
+ uint32_t ale_entry[3];
+
+ for (i = 0; i < CPSW_MAX_ALE_ENTRIES; i++) {
+ cpsw_ale_read_entry(sc, i, ale_entry);
+ if (ALE_TYPE(ale_entry) != ALE_TYPE_VLAN)
+ continue;
+ if (vlan != ALE_VLAN(ale_entry))
+ continue;
+ ale_entry[0] = ale_entry[1] = ale_entry[2] = 0;
+ cpsw_ale_write_entry(sc, i, ale_entry);
+ break;
+ }
+}
+
+static int
+cpsw_setvgroup(device_t dev, etherswitch_vlangroup_t *vg)
+{
+ int i;
+ struct cpsw_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ for (i = 0; i < CPSW_VLANS; i++) {
+ /* Is this Vlan ID in use by another vlangroup ? */
+ if (vg->es_vlangroup != i && cpsw_vgroups[i].vid == vg->es_vid)
+ return (EINVAL);
+ }
+
+ if (vg->es_vid == 0) {
+ if (cpsw_vgroups[vg->es_vlangroup].vid == -1)
+ return (0);
+ cpsw_remove_vlan(sc, cpsw_vgroups[vg->es_vlangroup].vid);
+ cpsw_vgroups[vg->es_vlangroup].vid = -1;
+ vg->es_untagged_ports = 0;
+ vg->es_member_ports = 0;
+ vg->es_vid = 0;
+ return (0);
+ }
+
+ vg->es_vid &= ETHERSWITCH_VID_MASK;
+ vg->es_member_ports &= CPSW_PORTS_MASK;
+ vg->es_untagged_ports &= CPSW_PORTS_MASK;
+
+ if (cpsw_vgroups[vg->es_vlangroup].vid != -1 &&
+ cpsw_vgroups[vg->es_vlangroup].vid != vg->es_vid)
+ return (EINVAL);
+
+ cpsw_vgroups[vg->es_vlangroup].vid = vg->es_vid;
+ cpsw_ale_update_vlan_table(sc, vg->es_vid, vg->es_member_ports,
+ vg->es_untagged_ports, vg->es_member_ports, 0);
+
+ return (0);
+}
+
+static int
+cpsw_readreg(device_t dev, int addr)
+{
+
+ /* Not supported. */
+ return (0);
+}
+
+static int
+cpsw_writereg(device_t dev, int addr, int value)
+{
+
+ /* Not supported. */
+ return (0);
+}
+
+static int
+cpsw_readphy(device_t dev, int phy, int reg)
+{
+
+ /* Not supported. */
+ return (0);
+}
+
+static int
+cpsw_writephy(device_t dev, int phy, int reg, int data)
+{
+
+ /* Not supported. */
+ return (0);
+}
+#endif
diff --git a/sys/arm/ti/cpsw/if_cpswreg.h b/sys/arm/ti/cpsw/if_cpswreg.h
new file mode 100644
index 000000000000..9f73df19a064
--- /dev/null
+++ b/sys/arm/ti/cpsw/if_cpswreg.h
@@ -0,0 +1,210 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Damjan Marion <dmarion@Freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _IF_CPSWREG_H
+#define _IF_CPSWREG_H
+
+#define CPSW_SS_OFFSET 0x0000
+#define CPSW_SS_IDVER (CPSW_SS_OFFSET + 0x00)
+#define CPSW_SS_SOFT_RESET (CPSW_SS_OFFSET + 0x08)
+#define CPSW_SS_STAT_PORT_EN (CPSW_SS_OFFSET + 0x0C)
+#define CPSW_SS_PTYPE (CPSW_SS_OFFSET + 0x10)
+#define CPSW_SS_FLOW_CONTROL (CPSW_SS_OFFSET + 0x24)
+
+#define CPSW_PORT_OFFSET 0x0100
+#define CPSW_PORT_P_MAX_BLKS(p) (CPSW_PORT_OFFSET + 0x08 + ((p) * 0x100))
+#define CPSW_PORT_P_BLK_CNT(p) (CPSW_PORT_OFFSET + 0x0C + ((p) * 0x100))
+#define CPSW_PORT_P_VLAN(p) (CPSW_PORT_OFFSET + 0x14 + ((p) * 0x100))
+#define CPSW_PORT_P_TX_PRI_MAP(p) (CPSW_PORT_OFFSET + 0x118 + ((p-1) * 0x100))
+#define CPSW_PORT_P0_CPDMA_TX_PRI_MAP (CPSW_PORT_OFFSET + 0x01C)
+#define CPSW_PORT_P0_CPDMA_RX_CH_MAP (CPSW_PORT_OFFSET + 0x020)
+#define CPSW_PORT_P_SA_LO(p) (CPSW_PORT_OFFSET + 0x120 + ((p-1) * 0x100))
+#define CPSW_PORT_P_SA_HI(p) (CPSW_PORT_OFFSET + 0x124 + ((p-1) * 0x100))
+
+#define CPSW_CPDMA_OFFSET 0x0800
+#define CPSW_CPDMA_TX_CONTROL (CPSW_CPDMA_OFFSET + 0x04)
+#define CPSW_CPDMA_TX_TEARDOWN (CPSW_CPDMA_OFFSET + 0x08)
+#define CPSW_CPDMA_RX_CONTROL (CPSW_CPDMA_OFFSET + 0x14)
+#define CPSW_CPDMA_RX_TEARDOWN (CPSW_CPDMA_OFFSET + 0x18)
+#define CPSW_CPDMA_SOFT_RESET (CPSW_CPDMA_OFFSET + 0x1c)
+#define CPSW_CPDMA_DMACONTROL (CPSW_CPDMA_OFFSET + 0x20)
+#define CPSW_CPDMA_DMASTATUS (CPSW_CPDMA_OFFSET + 0x24)
+#define CPSW_CPDMA_RX_BUFFER_OFFSET (CPSW_CPDMA_OFFSET + 0x28)
+#define CPSW_CPDMA_TX_INTSTAT_RAW (CPSW_CPDMA_OFFSET + 0x80)
+#define CPSW_CPDMA_TX_INTSTAT_MASKED (CPSW_CPDMA_OFFSET + 0x84)
+#define CPSW_CPDMA_TX_INTMASK_SET (CPSW_CPDMA_OFFSET + 0x88)
+#define CPSW_CPDMA_TX_INTMASK_CLEAR (CPSW_CPDMA_OFFSET + 0x8C)
+#define CPSW_CPDMA_CPDMA_EOI_VECTOR (CPSW_CPDMA_OFFSET + 0x94)
+#define CPSW_CPDMA_RX_INTSTAT_RAW (CPSW_CPDMA_OFFSET + 0xA0)
+#define CPSW_CPDMA_RX_INTSTAT_MASKED (CPSW_CPDMA_OFFSET + 0xA4)
+#define CPSW_CPDMA_RX_INTMASK_SET (CPSW_CPDMA_OFFSET + 0xA8)
+#define CPSW_CPDMA_RX_INTMASK_CLEAR (CPSW_CPDMA_OFFSET + 0xAc)
+#define CPSW_CPDMA_RX_INT_THRESH(_ch) (1 << (8 + ((_ch) & 7)))
+#define CPSW_CPDMA_RX_INT(_ch) (1 << (0 + ((_ch) & 7)))
+#define CPSW_CPDMA_DMA_INTSTAT_RAW (CPSW_CPDMA_OFFSET + 0xB0)
+#define CPSW_CPDMA_DMA_INTSTAT_MASKED (CPSW_CPDMA_OFFSET + 0xB4)
+#define CPSW_CPDMA_DMA_INTMASK_SET (CPSW_CPDMA_OFFSET + 0xB8)
+#define CPSW_CPDMA_DMA_INTMASK_CLEAR (CPSW_CPDMA_OFFSET + 0xBC)
+#define CPSW_CPDMA_RX_PENDTHRESH(p) (CPSW_CPDMA_OFFSET + 0x0c0 + ((p) * 0x04))
+#define CPSW_CPDMA_RX_FREEBUFFER(p) (CPSW_CPDMA_OFFSET + 0x0e0 + ((p) * 0x04))
+
+#define CPSW_STATS_OFFSET 0x0900
+
+#define CPSW_STATERAM_OFFSET 0x0A00
+#define CPSW_CPDMA_TX_HDP(p) (CPSW_STATERAM_OFFSET + 0x00 + ((p) * 0x04))
+#define CPSW_CPDMA_RX_HDP(p) (CPSW_STATERAM_OFFSET + 0x20 + ((p) * 0x04))
+#define CPSW_CPDMA_TX_CP(p) (CPSW_STATERAM_OFFSET + 0x40 + ((p) * 0x04))
+#define CPSW_CPDMA_RX_CP(p) (CPSW_STATERAM_OFFSET + 0x60 + ((p) * 0x04))
+
+#define CPSW_CPTS_OFFSET 0x0C00
+
+#define CPSW_ALE_OFFSET 0x0D00
+#define CPSW_ALE_CONTROL (CPSW_ALE_OFFSET + 0x08)
+#define CPSW_ALE_CTL_ENABLE (1U << 31)
+#define CPSW_ALE_CTL_CLEAR_TBL (1 << 30)
+#define CPSW_ALE_CTL_BYPASS (1 << 4)
+#define CPSW_ALE_CTL_VLAN_AWARE (1 << 2)
+#define CPSW_ALE_TBLCTL (CPSW_ALE_OFFSET + 0x20)
+#define CPSW_ALE_TBLW2 (CPSW_ALE_OFFSET + 0x34)
+#define CPSW_ALE_TBLW1 (CPSW_ALE_OFFSET + 0x38)
+#define CPSW_ALE_TBLW0 (CPSW_ALE_OFFSET + 0x3C)
+#define ALE_MCAST(_a) ((_a[1] >> 8) & 1)
+#define ALE_MCAST_FWD (3 << 30)
+#define ALE_PORTS(_a) ((_a[2] >> 2) & 7)
+#define ALE_TYPE(_a) ((_a[1] >> 28) & 3)
+#define ALE_TYPE_ADDR 1
+#define ALE_TYPE_VLAN 2
+#define ALE_TYPE_VLAN_ADDR 3
+#define ALE_VLAN(_a) ((_a[1] >> 16) & 0xfff)
+#define ALE_VLAN_UNREGFLOOD(_a) ((_a[0] >> 8) & 7)
+#define ALE_VLAN_REGFLOOD(_a) ((_a[0] >> 16) & 7)
+#define ALE_VLAN_UNTAG(_a) ((_a[0] >> 24) & 7)
+#define ALE_VLAN_MEMBERS(_a) (_a[0] & 7)
+#define CPSW_ALE_PORTCTL(p) (CPSW_ALE_OFFSET + 0x40 + ((p) * 0x04))
+#define ALE_PORTCTL_NO_SA_UPDATE (1 << 5)
+#define ALE_PORTCTL_NO_LEARN (1 << 4)
+#define ALE_PORTCTL_INGRESS (1 << 3)
+#define ALE_PORTCTL_DROP_UNTAGGED (1 << 2)
+#define ALE_PORTCTL_FORWARD 3
+#define ALE_PORTCTL_LEARN 2
+#define ALE_PORTCTL_BLOCKED 1
+#define ALE_PORTCTL_DISABLED 0
+
+/* SL1 is at 0x0D80, SL2 is at 0x0DC0 */
+#define CPSW_SL_OFFSET 0x0D80
+#define CPSW_SL_MACCONTROL(p) (CPSW_SL_OFFSET + (0x40 * (p)) + 0x04)
+#define CPSW_SL_MACTL_IFCTL_B (1 << 16)
+#define CPSW_SL_MACTL_IFCTL_A (1 << 15)
+#define CPSW_SL_MACTL_GIG (1 << 7)
+#define CPSW_SL_MACTL_GMII_ENABLE (1 << 5)
+#define CPSW_SL_MACTL_FULLDUPLEX (1 << 0)
+#define CPSW_SL_MACSTATUS(p) (CPSW_SL_OFFSET + (0x40 * (p)) + 0x08)
+#define CPSW_SL_SOFT_RESET(p) (CPSW_SL_OFFSET + (0x40 * (p)) + 0x0C)
+#define CPSW_SL_RX_MAXLEN(p) (CPSW_SL_OFFSET + (0x40 * (p)) + 0x10)
+#define CPSW_SL_RX_PAUSE(p) (CPSW_SL_OFFSET + (0x40 * (p)) + 0x18)
+#define CPSW_SL_TX_PAUSE(p) (CPSW_SL_OFFSET + (0x40 * (p)) + 0x1C)
+#define CPSW_SL_RX_PRI_MAP(p) (CPSW_SL_OFFSET + (0x40 * (p)) + 0x24)
+
+#define MDIO_OFFSET 0x1000
+#define MDIOCONTROL (MDIO_OFFSET + 0x04)
+#define MDIOCTL_ENABLE (1 << 30)
+#define MDIOCTL_FAULTENB (1 << 18)
+#define MDIOLINKINTRAW (MDIO_OFFSET + 0x10)
+#define MDIOLINKINTMASKED (MDIO_OFFSET + 0x14)
+#define MDIOUSERACCESS0 (MDIO_OFFSET + 0x80)
+#define MDIOUSERPHYSEL0 (MDIO_OFFSET + 0x84)
+#define MDIOUSERACCESS1 (MDIO_OFFSET + 0x88)
+#define MDIOUSERPHYSEL1 (MDIO_OFFSET + 0x8C)
+#define MDIO_PHYSEL_LINKINTENB (1 << 6)
+#define MDIO_PHYACCESS_GO (1U << 31)
+#define MDIO_PHYACCESS_WRITE (1 << 30)
+#define MDIO_PHYACCESS_ACK (1 << 29)
+
+#define CPSW_WR_OFFSET 0x1200
+#define CPSW_WR_SOFT_RESET (CPSW_WR_OFFSET + 0x04)
+#define CPSW_WR_CONTROL (CPSW_WR_OFFSET + 0x08)
+#define CPSW_WR_INT_CONTROL (CPSW_WR_OFFSET + 0x0c)
+#define CPSW_WR_INT_C0_RX_PULSE (1 << 16)
+#define CPSW_WR_INT_C0_TX_PULSE (1 << 17)
+#define CPSW_WR_INT_C1_RX_PULSE (1 << 18)
+#define CPSW_WR_INT_C1_TX_PULSE (1 << 19)
+#define CPSW_WR_INT_C2_RX_PULSE (1 << 20)
+#define CPSW_WR_INT_C2_TX_PULSE (1 << 21)
+#define CPSW_WR_INT_PACE_EN \
+ (CPSW_WR_INT_C0_RX_PULSE | CPSW_WR_INT_C0_TX_PULSE | \
+ CPSW_WR_INT_C1_RX_PULSE | CPSW_WR_INT_C1_TX_PULSE | \
+ CPSW_WR_INT_C2_RX_PULSE | CPSW_WR_INT_C2_TX_PULSE)
+#define CPSW_WR_INT_PRESCALE_MASK 0xfff
+#define CPSW_WR_C_RX_THRESH_EN(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x10)
+#define CPSW_WR_C_RX_EN(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x14)
+#define CPSW_WR_C_TX_EN(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x18)
+#define CPSW_WR_C_MISC_EN(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x1C)
+#define CPSW_WR_C_RX_THRESH_STAT(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x40)
+#define CPSW_WR_C_RX_STAT(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x44)
+#define CPSW_WR_C_TX_STAT(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x48)
+#define CPSW_WR_C_MISC_STAT(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x4C)
+#define CPSW_WR_C_MISC_EVNT_PEND (1 << 4)
+#define CPSW_WR_C_MISC_STAT_PEND (1 << 3)
+#define CPSW_WR_C_MISC_HOST_PEND (1 << 2)
+#define CPSW_WR_C_MISC_MDIOLINK (1 << 1)
+#define CPSW_WR_C_MISC_MDIOUSER (1 << 0)
+#define CPSW_WR_C_RX_IMAX(p) (CPSW_WR_OFFSET + (0x08 * (p)) + 0x70)
+#define CPSW_WR_C_TX_IMAX(p) (CPSW_WR_OFFSET + (0x08 * (p)) + 0x74)
+#define CPSW_WR_C_IMAX_MASK 0x3f
+#define CPSW_WR_C_IMAX_MAX 63
+#define CPSW_WR_C_IMAX_MIN 2
+#define CPSW_WR_C_IMAX_US_MAX 500
+#define CPSW_WR_C_IMAX_US_MIN 16
+
+#define CPSW_CPPI_RAM_OFFSET 0x2000
+#define CPSW_CPPI_RAM_SIZE 0x2000
+
+#define CPSW_MEMWINDOW_SIZE 0x4000
+
+#define CPDMA_BD_SOP (1 << 15)
+#define CPDMA_BD_EOP (1 << 14)
+#define CPDMA_BD_OWNER (1 << 13)
+#define CPDMA_BD_EOQ (1 << 12)
+#define CPDMA_BD_TDOWNCMPLT (1 << 11)
+#define CPDMA_BD_PASS_CRC (1 << 10)
+#define CPDMA_BD_PKT_ERR_MASK (3 << 4)
+#define CPDMA_BD_TO_PORT (1 << 4)
+#define CPDMA_BD_PORT_MASK 3
+
+struct cpsw_cpdma_bd {
+ volatile uint32_t next;
+ volatile uint32_t bufptr;
+ volatile uint16_t buflen;
+ volatile uint16_t bufoff;
+ volatile uint16_t pktlen;
+ volatile uint16_t flags;
+};
+
+#endif /*_IF_CPSWREG_H */
diff --git a/sys/arm/ti/cpsw/if_cpswvar.h b/sys/arm/ti/cpsw/if_cpswvar.h
new file mode 100644
index 000000000000..9c51b76b9db4
--- /dev/null
+++ b/sys/arm/ti/cpsw/if_cpswvar.h
@@ -0,0 +1,151 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Damjan Marion <dmarion@Freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _IF_CPSWVAR_H
+#define _IF_CPSWVAR_H
+
+#define CPSW_PORTS 2
+#define CPSW_INTR_COUNT 4
+
+/* MII BUS */
+#define CPSW_MIIBUS_RETRIES 20
+#define CPSW_MIIBUS_DELAY 100
+
+#define CPSW_MAX_ALE_ENTRIES 1024
+
+#define CPSW_SYSCTL_COUNT 34
+
+#ifdef CPSW_ETHERSWITCH
+#define CPSW_CPU_PORT 0
+#define CPSW_PORTS_MASK 0x7
+#define CPSW_VLANS 128 /* Arbitrary number. */
+
+struct cpsw_vlangroups {
+ int vid;
+};
+#endif
+
+struct cpsw_slot {
+ uint32_t bd_offset; /* Offset of corresponding BD within CPPI RAM. */
+ bus_dmamap_t dmamap;
+ struct ifnet *ifp;
+ struct mbuf *mbuf;
+ STAILQ_ENTRY(cpsw_slot) next;
+};
+STAILQ_HEAD(cpsw_slots, cpsw_slot);
+
+struct cpsw_queue {
+ struct mtx lock;
+ int running;
+ int teardown;
+ struct cpsw_slots active;
+ struct cpsw_slots avail;
+ uint32_t queue_adds; /* total bufs added */
+ uint32_t queue_removes; /* total bufs removed */
+ uint32_t queue_removes_at_last_tick; /* Used by watchdog */
+ uint32_t queue_restart;
+ int queue_slots;
+ int active_queue_len;
+ int max_active_queue_len;
+ int avail_queue_len;
+ int max_avail_queue_len;
+ int longest_chain; /* Largest # segments in a single packet. */
+ int hdp_offset;
+};
+
+struct cpsw_port {
+ device_t dev;
+ int phy;
+ int vlan;
+};
+
+struct cpsw_softc {
+ device_t dev;
+ int active_slave;
+ int debug;
+ int dualemac;
+ phandle_t node;
+ struct bintime attach_uptime; /* system uptime when attach happened. */
+ struct cpsw_port port[2];
+ unsigned coal_us;
+
+ /* RX and TX buffer tracking */
+ struct cpsw_queue rx, tx;
+
+ /* We expect 1 memory resource and 4 interrupts from the device tree. */
+ int mem_rid;
+ struct resource *mem_res;
+ struct resource *irq_res[CPSW_INTR_COUNT];
+ void *ih_cookie[CPSW_INTR_COUNT];
+
+ /* A buffer full of nulls for TX padding. */
+ void *nullpad;
+
+ bus_dma_tag_t mbuf_dtag;
+
+ struct {
+ int resets;
+ int timer;
+ struct callout callout;
+ } watchdog;
+
+ /* 64-bit versions of 32-bit hardware statistics counters */
+ uint64_t shadow_stats[CPSW_SYSCTL_COUNT];
+
+ /* CPPI STATERAM has 512 slots for building TX/RX queues. */
+ /* TODO: Size here supposedly varies with different versions
+ of the controller. Check DaVinci specs and find a good
+ way to adjust this. One option is to have a separate
+ Device Tree parameter for number slots; another option
+ is to calculate it from the memory size in the device tree. */
+ struct cpsw_slot _slots[CPSW_CPPI_RAM_SIZE / sizeof(struct cpsw_cpdma_bd)];
+ struct cpsw_slots avail;
+};
+
+struct cpswp_softc {
+ device_t dev;
+ device_t miibus;
+ device_t pdev;
+ int media_status;
+ int unit;
+ int vlan;
+ struct bintime init_uptime; /* system uptime when init happened. */
+ struct callout mii_callout;
+ struct cpsw_softc *swsc;
+ struct ifnet *ifp;
+ struct mii_data *mii;
+ struct mtx lock;
+ uint32_t if_flags;
+ uint32_t phy;
+ uint32_t phyaccess;
+ uint32_t physel;
+};
+
+#endif /*_IF_CPSWVAR_H */
diff --git a/sys/arm/ti/files.ti b/sys/arm/ti/files.ti
new file mode 100644
index 000000000000..87beccd120a0
--- /dev/null
+++ b/sys/arm/ti/files.ti
@@ -0,0 +1,34 @@
+#$FreeBSD$
+
+arm/ti/ti_cpuid.c standard
+arm/ti/ti_machdep.c standard
+arm/ti/ti_prcm.c standard
+arm/ti/ti_omap4_cm.c standard
+arm/ti/ti_scm.c standard
+arm/ti/ti_scm_syscon.c standard
+arm/ti/ti_pinmux.c standard
+dev/mbox/mbox_if.m optional ti_mbox
+arm/ti/ti_mbox.c optional ti_mbox
+arm/ti/ti_pruss.c optional ti_pruss
+arm/ti/ti_prm.c optional ti_pruss
+arm/ti/ti_wdt.c optional ti_wdt
+arm/ti/ti_adc.c optional ti_adc
+arm/ti/ti_gpio.c optional gpio
+arm/ti/ti_gpio_if.m optional gpio
+arm/ti/ti_i2c.c optional ti_i2c
+arm/ti/ti_sdhci.c optional sdhci
+arm/ti/ti_spi.c optional ti_spi
+arm/ti/ti_sysc.c standard
+
+arm/ti/clk/clock_common.c standard
+arm/ti/clk/ti_clk_clkctrl.c standard
+arm/ti/clk/ti_clkctrl.c standard
+arm/ti/clk/ti_clk_dpll.c standard
+arm/ti/clk/ti_dpll_clock.c standard
+arm/ti/clk/ti_mux_clock.c standard
+arm/ti/clk/ti_divider_clock.c standard
+arm/ti/clk/ti_gate_clock.c standard
+
+dev/uart/uart_dev_ti8250.c optional uart
+dev/uart/uart_dev_ns8250.c optional uart
+
diff --git a/sys/arm/ti/omap4/files.omap4 b/sys/arm/ti/omap4/files.omap4
new file mode 100644
index 000000000000..0b2f2d3bf26d
--- /dev/null
+++ b/sys/arm/ti/omap4/files.omap4
@@ -0,0 +1,22 @@
+#$FreeBSD$
+
+arm/ti/ti_smc.S standard
+
+arm/ti/usb/omap_ehci.c optional usb ehci
+arm/ti/usb/omap_host.c optional usb
+arm/ti/usb/omap_tll.c optional usb
+arm/ti/ti_sdma.c optional ti_sdma
+
+arm/ti/omap4/omap4_gpio.c optional gpio
+arm/ti/omap4/omap4_l2cache.c optional pl310
+#arm/ti/omap4/omap4_prcm_clks.c standard
+arm/ti/omap4/omap4_scm_padconf.c standard
+arm/ti/omap4/omap4_mp.c optional smp
+arm/ti/omap4/omap4_wugen.c standard
+
+arm/ti/omap4/pandaboard/pandaboard.c standard
+
+arm/ti/twl/twl.c optional twl
+arm/ti/twl/twl_vreg.c optional twl twl_vreg
+arm/ti/twl/twl_clks.c optional twl twl_clks
+
diff --git a/sys/arm/ti/omap4/omap4_gpio.c b/sys/arm/ti/omap4/omap4_gpio.c
new file mode 100644
index 000000000000..faf2339411b6
--- /dev/null
+++ b/sys/arm/ti/omap4/omap4_gpio.c
@@ -0,0 +1,149 @@
+/*-
+ * Copyright (c) 2011 Ben Gray <ben.r.gray@gmail.com>.
+ * Copyright (c) 2014 Andrew Turner <andrew@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BEN GRAY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/gpio.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/ti/ti_cpuid.h>
+#include <arm/ti/ti_gpio.h>
+#include <arm/ti/ti_pinmux.h>
+
+#include <arm/ti/omap4/omap4_scm_padconf.h>
+
+#include "ti_gpio_if.h"
+
+static struct ofw_compat_data compat_data[] = {
+ {"ti,omap4-gpio", 1},
+ {"ti,gpio", 1},
+ {NULL, 0},
+};
+
+static int
+omap4_gpio_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+ if (ti_chip() != CHIP_OMAP_4)
+ return (ENXIO);
+
+ device_set_desc(dev, "TI OMAP4 General Purpose I/O (GPIO)");
+
+ return (0);
+}
+
+static int
+omap4_gpio_set_flags(device_t dev, uint32_t gpio, uint32_t flags)
+{
+ unsigned int state = 0;
+ struct ti_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+ /* First the SCM driver needs to be told to put the pad into GPIO mode */
+ if (flags & GPIO_PIN_OUTPUT)
+ state = PADCONF_PIN_OUTPUT;
+ else if (flags & GPIO_PIN_INPUT) {
+ if (flags & GPIO_PIN_PULLUP)
+ state = PADCONF_PIN_INPUT_PULLUP;
+ else if (flags & GPIO_PIN_PULLDOWN)
+ state = PADCONF_PIN_INPUT_PULLDOWN;
+ else
+ state = PADCONF_PIN_INPUT;
+ }
+ return ti_pinmux_padconf_set_gpiomode((sc->sc_bank-1)*32 + gpio, state);
+}
+
+static int
+omap4_gpio_get_flags(device_t dev, uint32_t gpio, uint32_t *flags)
+{
+ unsigned int state;
+ struct ti_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ /* Get the current pin state */
+ if (ti_pinmux_padconf_get_gpiomode((sc->sc_bank-1)*32 + gpio, &state) != 0) {
+ *flags = 0;
+ return (EINVAL);
+ } else {
+ switch (state) {
+ case PADCONF_PIN_OUTPUT:
+ *flags = GPIO_PIN_OUTPUT;
+ break;
+ case PADCONF_PIN_INPUT:
+ *flags = GPIO_PIN_INPUT;
+ break;
+ case PADCONF_PIN_INPUT_PULLUP:
+ *flags = GPIO_PIN_INPUT | GPIO_PIN_PULLUP;
+ break;
+ case PADCONF_PIN_INPUT_PULLDOWN:
+ *flags = GPIO_PIN_INPUT | GPIO_PIN_PULLDOWN;
+ break;
+ default:
+ *flags = 0;
+ break;
+ }
+ }
+
+ return (0);
+}
+
+static device_method_t omap4_gpio_methods[] = {
+ /* bus interface */
+ DEVMETHOD(device_probe, omap4_gpio_probe),
+
+ /* ti_gpio interface */
+ DEVMETHOD(ti_gpio_set_flags, omap4_gpio_set_flags),
+ DEVMETHOD(ti_gpio_get_flags, omap4_gpio_get_flags),
+
+ DEVMETHOD_END
+};
+
+extern driver_t ti_gpio_driver;
+static devclass_t omap4_gpio_devclass;
+
+DEFINE_CLASS_1(gpio, omap4_gpio_driver, omap4_gpio_methods,
+ sizeof(struct ti_gpio_softc), ti_gpio_driver);
+DRIVER_MODULE(omap4_gpio, simplebus, omap4_gpio_driver, omap4_gpio_devclass,
+ 0, 0);
diff --git a/sys/arm/ti/omap4/omap4_l2cache.c b/sys/arm/ti/omap4/omap4_l2cache.c
new file mode 100644
index 000000000000..35ffe1aee293
--- /dev/null
+++ b/sys/arm/ti/omap4/omap4_l2cache.c
@@ -0,0 +1,91 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Olivier Houchard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <machine/bus.h>
+#include <machine/pl310.h>
+#include <machine/platformvar.h>
+
+#include <arm/ti/ti_smc.h>
+#include <arm/ti/omap4/omap4_machdep.h>
+#include <arm/ti/omap4/omap4_smc.h>
+
+#include "platform_pl310_if.h"
+
+void
+omap4_pl310_init(platform_t plat, struct pl310_softc *sc)
+{
+ uint32_t aux, prefetch;
+
+ aux = pl310_read4(sc, PL310_AUX_CTRL);
+ prefetch = pl310_read4(sc, PL310_PREFETCH_CTRL);
+
+ /*
+ * Disable instruction prefetch
+ */
+ prefetch &= ~PREFETCH_CTRL_INSTR_PREFETCH;
+ aux &= ~AUX_CTRL_INSTR_PREFETCH;
+
+ // prefetch &= ~PREFETCH_CTRL_DATA_PREFETCH;
+ // aux &= ~AUX_CTRL_DATA_PREFETCH;
+
+ /*
+ * Make sure data prefetch is on
+ */
+ prefetch |= PREFETCH_CTRL_DATA_PREFETCH;
+ aux |= AUX_CTRL_DATA_PREFETCH;
+
+ /*
+ * TODO: add tunable for prefetch offset
+ * and experiment with performance
+ */
+
+ ti_smc0(aux, 0, WRITE_AUXCTRL_REG);
+ ti_smc0(prefetch, 0, WRITE_PREFETCH_CTRL_REG);
+}
+
+void
+omap4_pl310_write_ctrl(platform_t plat, struct pl310_softc *sc, uint32_t val)
+{
+
+ ti_smc0(val, 0, L2CACHE_WRITE_CTRL_REG);
+}
+
+void
+omap4_pl310_write_debug(platform_t plat, struct pl310_softc *sc, uint32_t val)
+{
+
+ ti_smc0(val, 0, L2CACHE_WRITE_DEBUG_REG);
+}
diff --git a/sys/arm/ti/omap4/omap4_machdep.h b/sys/arm/ti/omap4/omap4_machdep.h
new file mode 100644
index 000000000000..50c370ebe24b
--- /dev/null
+++ b/sys/arm/ti/omap4/omap4_machdep.h
@@ -0,0 +1,40 @@
+/*-
+ * Copyright (c) 2016 Olivier Houchard <cognet@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _OMAP4_MACHDEP_H_
+#define _OMAP4_MACHDEP_H_
+
+struct pl310_softc;
+
+void omap4_mp_setmaxid(platform_t plat);
+void omap4_mp_start_ap(platform_t plat);
+
+void omap4_pl310_init(platform_t, struct pl310_softc *);
+void omap4_pl310_write_ctrl(platform_t, struct pl310_softc *, uint32_t);
+void omap4_pl310_write_debug(platform_t, struct pl310_softc *, uint32_t);
+
+#endif /* _OMAP4_MACHDEP_H_ */
diff --git a/sys/arm/ti/omap4/omap4_mp.c b/sys/arm/ti/omap4/omap4_mp.c
new file mode 100644
index 000000000000..ca4a9258ec27
--- /dev/null
+++ b/sys/arm/ti/omap4/omap4_mp.c
@@ -0,0 +1,76 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Olivier Houchard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/smp.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/cpu.h>
+#include <machine/smp.h>
+#include <machine/fdt.h>
+#include <machine/intr.h>
+#include <machine/platformvar.h>
+
+#include <arm/ti/ti_smc.h>
+#include <arm/ti/omap4/omap4_machdep.h>
+#include <arm/ti/omap4/omap4_smc.h>
+
+void
+omap4_mp_setmaxid(platform_t plat)
+{
+
+ if (mp_ncpus != 0)
+ return;
+ mp_maxid = 1;
+ mp_ncpus = 2;
+}
+
+void
+omap4_mp_start_ap(platform_t plat)
+{
+ bus_addr_t scu_addr;
+
+ if (bus_space_map(fdtbus_bs_tag, 0x48240000, 0x1000, 0, &scu_addr) != 0)
+ panic("Couldn't map the SCU\n");
+ /* Enable the SCU */
+ *(volatile unsigned int *)scu_addr |= 1;
+ //*(volatile unsigned int *)(scu_addr + 0x30) |= 1;
+ dcache_wbinv_poc_all();
+
+ ti_smc0(0x200, 0xfffffdff, MODIFY_AUX_CORE_0);
+ ti_smc0(pmap_kextract((vm_offset_t)mpentry), 0, WRITE_AUX_CORE_1);
+ dsb();
+ sev();
+ bus_space_unmap(fdtbus_bs_tag, scu_addr, 0x1000);
+}
diff --git a/sys/arm/ti/omap4/omap4_prcm_clks.c b/sys/arm/ti/omap4/omap4_prcm_clks.c
new file mode 100644
index 000000000000..56368e44059e
--- /dev/null
+++ b/sys/arm/ti/omap4/omap4_prcm_clks.c
@@ -0,0 +1,1504 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 2011
+ * Ben Gray <ben.r.gray@gmail.com>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BEN GRAY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/intr.h>
+
+#include <arm/arm/mpcore_timervar.h>
+#include <arm/ti/tivar.h>
+#include <arm/ti/ti_prcm.h>
+#include <arm/ti/omap4/omap4_reg.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+/*
+ * This file defines the clock configuration for the OMAP4xxx series of
+ * devices.
+ *
+ * How This is Suppose to Work
+ * ===========================
+ * - There is a top level omap_prcm module that defines all OMAP SoC drivers
+ * should use to enable/disable the system clocks regardless of the version
+ * of OMAP device they are running on. This top level PRCM module is just
+ * a thin shim to chip specific functions that perform the donkey work of
+ * configuring the clock - this file is the 'donkey' for OMAP44xx devices.
+ *
+ * - The key bit in this file is the omap_clk_devmap array, it's
+ * used by the omap_prcm driver to determine what clocks are valid and which
+ * functions to call to manipulate them.
+ *
+ * - In essence you just need to define some callbacks for each of the
+ * clocks and then you're done.
+ *
+ * - The other thing that is worth noting is that when the omap_prcm device
+ * is registered you typically pass in some memory ranges which are the
+ * SYS_MEMORY resources. These resources are in turn allocated using
+ * bus_allocate_resources(...) and the resource handles are passed to all
+ * individual clock callback handlers.
+ *
+ *
+ *
+ * OMAP4 devices are different from the previous OMAP3 devices in that there
+ * is no longer a separate functional and interface clock for each module,
+ * instead there is typically an interface clock that spans many modules.
+ */
+
+#define FREQ_96MHZ 96000000
+#define FREQ_64MHZ 64000000
+#define FREQ_48MHZ 48000000
+#define FREQ_32KHZ 32000
+
+#define PRM_INSTANCE 1
+#define CM1_INSTANCE 2
+#define CM2_INSTANCE 3
+
+/**
+ * Address offsets from the PRM memory region to the top level clock control
+ * registers.
+ */
+#define CKGEN_PRM_OFFSET 0x00000100UL
+#define MPU_PRM_OFFSET 0x00000300UL
+#define DSP_PRM_OFFSET 0x00000400UL
+#define ABE_PRM_OFFSET 0x00000500UL
+#define ALWAYS_ON_PRM_OFFSET 0x00000600UL
+#define CORE_PRM_OFFSET 0x00000700UL
+#define IVAHD_PRM_OFFSET 0x00000F00UL
+#define CAM_PRM_OFFSET 0x00001000UL
+#define DSS_PRM_OFFSET 0x00001100UL
+#define SGX_PRM_OFFSET 0x00001200UL
+#define L3INIT_PRM_OFFSET 0x00001300UL
+#define L4PER_PRM_OFFSET 0x00001400UL
+#define WKUP_PRM_OFFSET 0x00001700UL
+#define WKUP_CM_OFFSET 0x00001800UL
+#define EMU_PRM_OFFSET 0x00001900UL
+#define EMU_CM_OFFSET 0x00001A00UL
+#define DEVICE_PRM_OFFSET 0x00001B00UL
+#define INSTR_PRM_OFFSET 0x00001F00UL
+
+#define CM_ABE_DSS_SYS_CLKSEL_OFFSET (CKGEN_PRM_OFFSET + 0x0000UL)
+#define CM_L4_WKUP_CLKSELL_OFFSET (CKGEN_PRM_OFFSET + 0x0008UL)
+#define CM_ABE_PLL_REF_CLKSEL_OFFSET (CKGEN_PRM_OFFSET + 0x000CUL)
+#define CM_SYS_CLKSEL_OFFSET (CKGEN_PRM_OFFSET + 0x0010UL)
+
+/**
+ * Address offsets from the CM1 memory region to the top level clock control
+ * registers.
+ */
+#define CKGEN_CM1_OFFSET 0x00000100UL
+#define MPU_CM1_OFFSET 0x00000300UL
+#define DSP_CM1_OFFSET 0x00000400UL
+#define ABE_CM1_OFFSET 0x00000500UL
+#define RESTORE_CM1_OFFSET 0x00000E00UL
+#define INSTR_CM1_OFFSET 0x00000F00UL
+
+#define CM_CLKSEL_DPLL_MPU (CKGEN_CM1_OFFSET + 0x006CUL)
+
+/**
+ * Address offsets from the CM2 memory region to the top level clock control
+ * registers.
+ */
+#define INTRCONN_SOCKET_CM2_OFFSET 0x00000000UL
+#define CKGEN_CM2_OFFSET 0x00000100UL
+#define ALWAYS_ON_CM2_OFFSET 0x00000600UL
+#define CORE_CM2_OFFSET 0x00000700UL
+#define IVAHD_CM2_OFFSET 0x00000F00UL
+#define CAM_CM2_OFFSET 0x00001000UL
+#define DSS_CM2_OFFSET 0x00001100UL
+#define SGX_CM2_OFFSET 0x00001200UL
+#define L3INIT_CM2_OFFSET 0x00001300UL
+#define L4PER_CM2_OFFSET 0x00001400UL
+#define RESTORE_CM2_OFFSET 0x00001E00UL
+#define INSTR_CM2_OFFSET 0x00001F00UL
+
+#define CLKCTRL_MODULEMODE_MASK 0x00000003UL
+#define CLKCTRL_MODULEMODE_DISABLE 0x00000000UL
+#define CLKCTRL_MODULEMODE_AUTO 0x00000001UL
+#define CLKCTRL_MODULEMODE_ENABLE 0x00000001UL
+
+#define CLKCTRL_IDLEST_MASK 0x00030000UL
+#define CLKCTRL_IDLEST_ENABLED 0x00000000UL
+#define CLKCTRL_IDLEST_WAKING 0x00010000UL
+#define CLKCTRL_IDLEST_IDLE 0x00020000UL
+#define CLKCTRL_IDLEST_DISABLED 0x00030000UL
+
+static struct ofw_compat_data compat_data[] = {
+ {"ti,omap4-cm1", (uintptr_t)CM1_INSTANCE},
+ {"ti,omap4-cm2", (uintptr_t)CM2_INSTANCE},
+ {"ti,omap4-prm", (uintptr_t)PRM_INSTANCE},
+ {NULL, (uintptr_t)0},
+};
+
+struct omap4_prcm_softc {
+ struct resource *sc_res;
+ int sc_rid;
+ int sc_instance;
+ int attach_done;
+};
+
+static int omap4_clk_generic_activate(struct ti_clock_dev *clkdev);
+static int omap4_clk_generic_deactivate(struct ti_clock_dev *clkdev);
+static int omap4_clk_generic_accessible(struct ti_clock_dev *clkdev);
+static int omap4_clk_generic_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc);
+static int omap4_clk_generic_get_source_freq(struct ti_clock_dev *clkdev, unsigned int *freq);
+
+static int omap4_clk_gptimer_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc);
+static int omap4_clk_gptimer_get_source_freq(struct ti_clock_dev *clkdev, unsigned int *freq);
+
+static int omap4_clk_hsmmc_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc);
+static int omap4_clk_hsmmc_get_source_freq(struct ti_clock_dev *clkdev, unsigned int *freq);
+
+static int omap4_clk_hsusbhost_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc);
+static int omap4_clk_hsusbhost_activate(struct ti_clock_dev *clkdev);
+static int omap4_clk_hsusbhost_deactivate(struct ti_clock_dev *clkdev);
+static int omap4_clk_hsusbhost_accessible(struct ti_clock_dev *clkdev);
+
+static int omap4_clk_get_sysclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq);
+static int omap4_clk_get_arm_fclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq);
+
+/**
+ * omap_clk_devmap - Array of clock devices available on OMAP4xxx devices
+ *
+ * This map only defines which clocks are valid and the callback functions
+ * for clock activate, deactivate, etc. It is used by the top level omap_prcm
+ * driver.
+ *
+ * The actual details of the clocks (config registers, bit fields, sources,
+ * etc) are in the private g_omap3_clk_details array below.
+ *
+ */
+
+#define OMAP4_GENERIC_CLOCK_DEV(i) \
+ { .id = (i), \
+ .clk_activate = omap4_clk_generic_activate, \
+ .clk_deactivate = omap4_clk_generic_deactivate, \
+ .clk_set_source = omap4_clk_generic_set_source, \
+ .clk_accessible = omap4_clk_generic_accessible, \
+ .clk_get_source_freq = omap4_clk_generic_get_source_freq, \
+ .clk_set_source_freq = NULL \
+ }
+
+#define OMAP4_GPTIMER_CLOCK_DEV(i) \
+ { .id = (i), \
+ .clk_activate = omap4_clk_generic_activate, \
+ .clk_deactivate = omap4_clk_generic_deactivate, \
+ .clk_set_source = omap4_clk_gptimer_set_source, \
+ .clk_accessible = omap4_clk_generic_accessible, \
+ .clk_get_source_freq = omap4_clk_gptimer_get_source_freq, \
+ .clk_set_source_freq = NULL \
+ }
+
+#define OMAP4_HSMMC_CLOCK_DEV(i) \
+ { .id = (i), \
+ .clk_activate = omap4_clk_generic_activate, \
+ .clk_deactivate = omap4_clk_generic_deactivate, \
+ .clk_set_source = omap4_clk_hsmmc_set_source, \
+ .clk_accessible = omap4_clk_generic_accessible, \
+ .clk_get_source_freq = omap4_clk_hsmmc_get_source_freq, \
+ .clk_set_source_freq = NULL \
+ }
+
+#define OMAP4_HSUSBHOST_CLOCK_DEV(i) \
+ { .id = (i), \
+ .clk_activate = omap4_clk_hsusbhost_activate, \
+ .clk_deactivate = omap4_clk_hsusbhost_deactivate, \
+ .clk_set_source = omap4_clk_hsusbhost_set_source, \
+ .clk_accessible = omap4_clk_hsusbhost_accessible, \
+ .clk_get_source_freq = NULL, \
+ .clk_set_source_freq = NULL \
+ }
+
+struct ti_clock_dev ti_omap4_clk_devmap[] = {
+ /* System clocks */
+ { .id = SYS_CLK,
+ .clk_activate = NULL,
+ .clk_deactivate = NULL,
+ .clk_set_source = NULL,
+ .clk_accessible = NULL,
+ .clk_get_source_freq = omap4_clk_get_sysclk_freq,
+ .clk_set_source_freq = NULL,
+ },
+ /* MPU (ARM) core clocks */
+ { .id = MPU_CLK,
+ .clk_activate = NULL,
+ .clk_deactivate = NULL,
+ .clk_set_source = NULL,
+ .clk_accessible = NULL,
+ .clk_get_source_freq = omap4_clk_get_arm_fclk_freq,
+ .clk_set_source_freq = NULL,
+ },
+
+ /* UART device clocks */
+ OMAP4_GENERIC_CLOCK_DEV(UART1_CLK),
+ OMAP4_GENERIC_CLOCK_DEV(UART2_CLK),
+ OMAP4_GENERIC_CLOCK_DEV(UART3_CLK),
+ OMAP4_GENERIC_CLOCK_DEV(UART4_CLK),
+
+ /* Timer device source clocks */
+ OMAP4_GPTIMER_CLOCK_DEV(TIMER1_CLK),
+ OMAP4_GPTIMER_CLOCK_DEV(TIMER2_CLK),
+ OMAP4_GPTIMER_CLOCK_DEV(TIMER3_CLK),
+ OMAP4_GPTIMER_CLOCK_DEV(TIMER4_CLK),
+ OMAP4_GPTIMER_CLOCK_DEV(TIMER5_CLK),
+ OMAP4_GPTIMER_CLOCK_DEV(TIMER6_CLK),
+ OMAP4_GPTIMER_CLOCK_DEV(TIMER7_CLK),
+ OMAP4_GPTIMER_CLOCK_DEV(TIMER8_CLK),
+ OMAP4_GPTIMER_CLOCK_DEV(TIMER9_CLK),
+ OMAP4_GPTIMER_CLOCK_DEV(TIMER10_CLK),
+ OMAP4_GPTIMER_CLOCK_DEV(TIMER11_CLK),
+
+ /* MMC device clocks (MMC1 and MMC2 can have different input clocks) */
+ OMAP4_HSMMC_CLOCK_DEV(MMC1_CLK),
+ OMAP4_HSMMC_CLOCK_DEV(MMC2_CLK),
+ OMAP4_GENERIC_CLOCK_DEV(MMC3_CLK),
+ OMAP4_GENERIC_CLOCK_DEV(MMC4_CLK),
+ OMAP4_GENERIC_CLOCK_DEV(MMC5_CLK),
+
+ /* USB HS (high speed TLL, EHCI and OHCI) */
+ OMAP4_HSUSBHOST_CLOCK_DEV(USBTLL_CLK),
+ OMAP4_HSUSBHOST_CLOCK_DEV(USBHSHOST_CLK),
+ OMAP4_HSUSBHOST_CLOCK_DEV(USBFSHOST_CLK),
+ OMAP4_HSUSBHOST_CLOCK_DEV(USBP1_PHY_CLK),
+ OMAP4_HSUSBHOST_CLOCK_DEV(USBP2_PHY_CLK),
+ OMAP4_HSUSBHOST_CLOCK_DEV(USBP1_UTMI_CLK),
+ OMAP4_HSUSBHOST_CLOCK_DEV(USBP2_UTMI_CLK),
+ OMAP4_HSUSBHOST_CLOCK_DEV(USBP1_HSIC_CLK),
+ OMAP4_HSUSBHOST_CLOCK_DEV(USBP2_HSIC_CLK),
+
+ /* GPIO */
+ OMAP4_GENERIC_CLOCK_DEV(GPIO1_CLK),
+ OMAP4_GENERIC_CLOCK_DEV(GPIO2_CLK),
+ OMAP4_GENERIC_CLOCK_DEV(GPIO3_CLK),
+ OMAP4_GENERIC_CLOCK_DEV(GPIO4_CLK),
+ OMAP4_GENERIC_CLOCK_DEV(GPIO5_CLK),
+ OMAP4_GENERIC_CLOCK_DEV(GPIO6_CLK),
+
+ /* sDMA */
+ OMAP4_GENERIC_CLOCK_DEV(SDMA_CLK),
+
+ /* I2C */
+ OMAP4_GENERIC_CLOCK_DEV(I2C1_CLK),
+ OMAP4_GENERIC_CLOCK_DEV(I2C2_CLK),
+ OMAP4_GENERIC_CLOCK_DEV(I2C3_CLK),
+ OMAP4_GENERIC_CLOCK_DEV(I2C4_CLK),
+ { INVALID_CLK_IDENT, NULL, NULL, NULL, NULL }
+};
+
+/**
+ * omap4_clk_details - Stores details for all the different clocks supported
+ *
+ * Whenever an operation on a clock is being performed (activated, deactivated,
+ * etc) this array is looked up to find the correct register and bit(s) we
+ * should be modifying.
+ *
+ */
+struct omap4_clk_details {
+ clk_ident_t id;
+
+ uint32_t instance;
+ uint32_t clksel_reg;
+
+ int32_t src_freq;
+
+ uint32_t enable_mode;
+};
+
+#define OMAP4_GENERIC_CLOCK_DETAILS(i, f, di, r, e) \
+ { .id = (i), \
+ .instance = (di), \
+ .clksel_reg = (r), \
+ .src_freq = (f), \
+ .enable_mode = (e), \
+ }
+
+static struct omap4_clk_details g_omap4_clk_details[] = {
+ /* UART */
+ OMAP4_GENERIC_CLOCK_DETAILS(UART1_CLK, FREQ_48MHZ, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x0140), CLKCTRL_MODULEMODE_ENABLE),
+ OMAP4_GENERIC_CLOCK_DETAILS(UART2_CLK, FREQ_48MHZ, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x0148), CLKCTRL_MODULEMODE_ENABLE),
+ OMAP4_GENERIC_CLOCK_DETAILS(UART3_CLK, FREQ_48MHZ, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x0150), CLKCTRL_MODULEMODE_ENABLE),
+ OMAP4_GENERIC_CLOCK_DETAILS(UART4_CLK, FREQ_48MHZ, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x0158), CLKCTRL_MODULEMODE_ENABLE),
+
+ /* General purpose timers */
+ OMAP4_GENERIC_CLOCK_DETAILS(TIMER1_CLK, -1, PRM_INSTANCE,
+ (WKUP_CM_OFFSET + 0x040), CLKCTRL_MODULEMODE_ENABLE),
+ OMAP4_GENERIC_CLOCK_DETAILS(TIMER2_CLK, -1, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x038), CLKCTRL_MODULEMODE_ENABLE),
+ OMAP4_GENERIC_CLOCK_DETAILS(TIMER3_CLK, -1, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x040), CLKCTRL_MODULEMODE_ENABLE),
+ OMAP4_GENERIC_CLOCK_DETAILS(TIMER4_CLK, -1, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x048), CLKCTRL_MODULEMODE_ENABLE),
+ OMAP4_GENERIC_CLOCK_DETAILS(TIMER5_CLK, -1, CM1_INSTANCE,
+ (ABE_CM1_OFFSET + 0x068), CLKCTRL_MODULEMODE_ENABLE),
+ OMAP4_GENERIC_CLOCK_DETAILS(TIMER6_CLK, -1, CM1_INSTANCE,
+ (ABE_CM1_OFFSET + 0x070), CLKCTRL_MODULEMODE_ENABLE),
+ OMAP4_GENERIC_CLOCK_DETAILS(TIMER7_CLK, -1, CM1_INSTANCE,
+ (ABE_CM1_OFFSET + 0x078), CLKCTRL_MODULEMODE_ENABLE),
+ OMAP4_GENERIC_CLOCK_DETAILS(TIMER8_CLK, -1, CM1_INSTANCE,
+ (ABE_CM1_OFFSET + 0x080), CLKCTRL_MODULEMODE_ENABLE),
+ OMAP4_GENERIC_CLOCK_DETAILS(TIMER9_CLK, -1, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x050), CLKCTRL_MODULEMODE_ENABLE),
+ OMAP4_GENERIC_CLOCK_DETAILS(TIMER10_CLK, -1, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x028), CLKCTRL_MODULEMODE_ENABLE),
+ OMAP4_GENERIC_CLOCK_DETAILS(TIMER11_CLK, -1, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x030), CLKCTRL_MODULEMODE_ENABLE),
+
+ /* HSMMC (MMC1 and MMC2 can have different input clocks) */
+ OMAP4_GENERIC_CLOCK_DETAILS(MMC1_CLK, -1, CM2_INSTANCE,
+ (L3INIT_CM2_OFFSET + 0x028), /*CLKCTRL_MODULEMODE_ENABLE*/2),
+ OMAP4_GENERIC_CLOCK_DETAILS(MMC2_CLK, -1, CM2_INSTANCE,
+ (L3INIT_CM2_OFFSET + 0x030), /*CLKCTRL_MODULEMODE_ENABLE*/2),
+ OMAP4_GENERIC_CLOCK_DETAILS(MMC3_CLK, FREQ_48MHZ, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x120), /*CLKCTRL_MODULEMODE_ENABLE*/2),
+ OMAP4_GENERIC_CLOCK_DETAILS(MMC4_CLK, FREQ_48MHZ, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x128), /*CLKCTRL_MODULEMODE_ENABLE*/2),
+ OMAP4_GENERIC_CLOCK_DETAILS(MMC5_CLK, FREQ_48MHZ, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x160), /*CLKCTRL_MODULEMODE_ENABLE*/1),
+
+ /* GPIO modules */
+ OMAP4_GENERIC_CLOCK_DETAILS(GPIO1_CLK, -1, PRM_INSTANCE,
+ (WKUP_CM_OFFSET + 0x038), CLKCTRL_MODULEMODE_AUTO),
+ OMAP4_GENERIC_CLOCK_DETAILS(GPIO2_CLK, -1, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x060), CLKCTRL_MODULEMODE_AUTO),
+ OMAP4_GENERIC_CLOCK_DETAILS(GPIO3_CLK, -1, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x068), CLKCTRL_MODULEMODE_AUTO),
+ OMAP4_GENERIC_CLOCK_DETAILS(GPIO4_CLK, -1, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x070), CLKCTRL_MODULEMODE_AUTO),
+ OMAP4_GENERIC_CLOCK_DETAILS(GPIO5_CLK, -1, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x078), CLKCTRL_MODULEMODE_AUTO),
+ OMAP4_GENERIC_CLOCK_DETAILS(GPIO6_CLK, -1, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x080), CLKCTRL_MODULEMODE_AUTO),
+
+ /* sDMA block */
+ OMAP4_GENERIC_CLOCK_DETAILS(SDMA_CLK, -1, CM2_INSTANCE,
+ (CORE_CM2_OFFSET + 0x300), CLKCTRL_MODULEMODE_AUTO),
+
+ /* I2C modules */
+ OMAP4_GENERIC_CLOCK_DETAILS(I2C1_CLK, -1, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x0A0), CLKCTRL_MODULEMODE_ENABLE),
+ OMAP4_GENERIC_CLOCK_DETAILS(I2C2_CLK, -1, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x0A8), CLKCTRL_MODULEMODE_ENABLE),
+ OMAP4_GENERIC_CLOCK_DETAILS(I2C3_CLK, -1, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x0B0), CLKCTRL_MODULEMODE_ENABLE),
+ OMAP4_GENERIC_CLOCK_DETAILS(I2C4_CLK, -1, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x0B8), CLKCTRL_MODULEMODE_ENABLE),
+
+ { INVALID_CLK_IDENT, 0, 0, 0, 0 },
+};
+
+/**
+ * MAX_MODULE_ENABLE_WAIT - the number of loops to wait for the module to come
+ * alive.
+ *
+ */
+#define MAX_MODULE_ENABLE_WAIT 100
+
+/**
+ * ARRAY_SIZE - Macro to return the number of elements in a static const array.
+ *
+ */
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+
+/**
+ * omap4_clk_details - writes a 32-bit value to one of the timer registers
+ * @timer: Timer device context
+ * @off: The offset of a register from the timer register address range
+ * @val: The value to write into the register
+ *
+ *
+ * RETURNS:
+ * nothing
+ */
+static struct omap4_clk_details*
+omap4_clk_details(clk_ident_t id)
+{
+ struct omap4_clk_details *walker;
+
+ for (walker = g_omap4_clk_details; walker->id != INVALID_CLK_IDENT; walker++) {
+ if (id == walker->id)
+ return (walker);
+ }
+
+ return NULL;
+}
+
+static struct omap4_prcm_softc *
+omap4_prcm_get_instance_softc(int module_instance)
+{
+ int i, maxunit;
+ devclass_t prcm_devclass;
+ device_t dev;
+ struct omap4_prcm_softc *sc;
+
+ prcm_devclass = devclass_find("omap4_prcm");
+ maxunit = devclass_get_maxunit(prcm_devclass);
+
+ for (i = 0; i < maxunit; i++) {
+ dev = devclass_get_device(prcm_devclass, i);
+ sc = device_get_softc(dev);
+ if (sc->sc_instance == module_instance)
+ return (sc);
+ }
+
+ return (NULL);
+}
+
+/**
+ * omap4_clk_generic_activate - checks if a module is accessible
+ * @module: identifier for the module to check, see omap3_prcm.h for a list
+ * of possible modules.
+ * Example: OMAP3_MODULE_MMC1
+ *
+ *
+ *
+ * LOCKING:
+ * Inherits the locks from the omap_prcm driver, no internal locking.
+ *
+ * RETURNS:
+ * Returns 0 on success or a positive error code on failure.
+ */
+static int
+omap4_clk_generic_activate(struct ti_clock_dev *clkdev)
+{
+ struct omap4_prcm_softc *sc;
+ struct omap4_clk_details* clk_details;
+ struct resource* clk_mem_res;
+ uint32_t clksel;
+ unsigned int i;
+ clk_details = omap4_clk_details(clkdev->id);
+
+ if (clk_details == NULL)
+ return (ENXIO);
+
+ sc = omap4_prcm_get_instance_softc(clk_details->instance);
+ if (sc == NULL)
+ return ENXIO;
+
+ clk_mem_res = sc->sc_res;
+
+ if (clk_mem_res == NULL)
+ return (EINVAL);
+
+ /* All the 'generic' clocks have a CLKCTRL register which is more or less
+ * generic - the have at least two fielda called MODULEMODE and IDLEST.
+ */
+ clksel = bus_read_4(clk_mem_res, clk_details->clksel_reg);
+ clksel &= ~CLKCTRL_MODULEMODE_MASK;
+ clksel |= clk_details->enable_mode;
+ bus_write_4(clk_mem_res, clk_details->clksel_reg, clksel);
+
+ /* Now poll on the IDLEST register to tell us if the module has come up.
+ * TODO: We need to take into account the parent clocks.
+ */
+
+ /* Try MAX_MODULE_ENABLE_WAIT number of times to check if enabled */
+ for (i = 0; i < MAX_MODULE_ENABLE_WAIT; i++) {
+ clksel = bus_read_4(clk_mem_res, clk_details->clksel_reg);
+ if ((clksel & CLKCTRL_IDLEST_MASK) == CLKCTRL_IDLEST_ENABLED)
+ break;
+ DELAY(10);
+ }
+
+ /* Check the enabled state */
+ if ((clksel & CLKCTRL_IDLEST_MASK) != CLKCTRL_IDLEST_ENABLED) {
+ printf("Error: failed to enable module with clock %d\n", clkdev->id);
+ printf("Error: 0x%08x => 0x%08x\n", clk_details->clksel_reg, clksel);
+ return (ETIMEDOUT);
+ }
+
+ return (0);
+}
+
+/**
+ * omap4_clk_generic_deactivate - checks if a module is accessible
+ * @module: identifier for the module to check, see omap3_prcm.h for a list
+ * of possible modules.
+ * Example: OMAP3_MODULE_MMC1
+ *
+ *
+ *
+ * LOCKING:
+ * Inherits the locks from the omap_prcm driver, no internal locking.
+ *
+ * RETURNS:
+ * Returns 0 on success or a positive error code on failure.
+ */
+static int
+omap4_clk_generic_deactivate(struct ti_clock_dev *clkdev)
+{
+ struct omap4_prcm_softc *sc;
+ struct omap4_clk_details* clk_details;
+ struct resource* clk_mem_res;
+ uint32_t clksel;
+
+ clk_details = omap4_clk_details(clkdev->id);
+
+ if (clk_details == NULL)
+ return (ENXIO);
+
+ sc = omap4_prcm_get_instance_softc(clk_details->instance);
+ if (sc == NULL)
+ return ENXIO;
+
+ clk_mem_res = sc->sc_res;
+
+ if (clk_mem_res == NULL)
+ return (EINVAL);
+
+ /* All the 'generic' clocks have a CLKCTRL register which is more or less
+ * generic - the have at least two fielda called MODULEMODE and IDLEST.
+ */
+ clksel = bus_read_4(clk_mem_res, clk_details->clksel_reg);
+ clksel &= ~CLKCTRL_MODULEMODE_MASK;
+ clksel |= CLKCTRL_MODULEMODE_DISABLE;
+ bus_write_4(clk_mem_res, clk_details->clksel_reg, clksel);
+
+ return (0);
+}
+
+/**
+ * omap4_clk_generic_set_source - checks if a module is accessible
+ * @module: identifier for the module to check, see omap3_prcm.h for a list
+ * of possible modules.
+ * Example: OMAP3_MODULE_MMC1
+ *
+ *
+ *
+ * LOCKING:
+ * Inherits the locks from the omap_prcm driver, no internal locking.
+ *
+ * RETURNS:
+ * Returns 0 on success or a positive error code on failure.
+ */
+static int
+omap4_clk_generic_set_source(struct ti_clock_dev *clkdev,
+ clk_src_t clksrc)
+{
+
+ return (0);
+}
+
+/**
+ * omap4_clk_generic_accessible - checks if a module is accessible
+ * @module: identifier for the module to check, see omap3_prcm.h for a list
+ * of possible modules.
+ * Example: OMAP3_MODULE_MMC1
+ *
+ *
+ *
+ * LOCKING:
+ * Inherits the locks from the omap_prcm driver, no internal locking.
+ *
+ * RETURNS:
+ * Returns 0 on success or a negative error code on failure.
+ */
+static int
+omap4_clk_generic_accessible(struct ti_clock_dev *clkdev)
+{
+ struct omap4_prcm_softc *sc;
+ struct omap4_clk_details* clk_details;
+ struct resource* clk_mem_res;
+ uint32_t clksel;
+
+ clk_details = omap4_clk_details(clkdev->id);
+
+ if (clk_details == NULL)
+ return (ENXIO);
+
+ sc = omap4_prcm_get_instance_softc(clk_details->instance);
+ if (sc == NULL)
+ return ENXIO;
+
+ clk_mem_res = sc->sc_res;
+
+ if (clk_mem_res == NULL)
+ return (EINVAL);
+
+ clksel = bus_read_4(clk_mem_res, clk_details->clksel_reg);
+
+ /* Check the enabled state */
+ if ((clksel & CLKCTRL_IDLEST_MASK) != CLKCTRL_IDLEST_ENABLED)
+ return (0);
+
+ return (1);
+}
+
+/**
+ * omap4_clk_generic_get_source_freq - checks if a module is accessible
+ * @module: identifier for the module to check, see omap3_prcm.h for a list
+ * of possible modules.
+ * Example: OMAP3_MODULE_MMC1
+ *
+ *
+ *
+ * LOCKING:
+ * Inherits the locks from the omap_prcm driver, no internal locking.
+ *
+ * RETURNS:
+ * Returns 0 on success or a negative error code on failure.
+ */
+static int
+omap4_clk_generic_get_source_freq(struct ti_clock_dev *clkdev,
+ unsigned int *freq
+ )
+{
+ struct omap4_clk_details* clk_details = omap4_clk_details(clkdev->id);
+
+ if (clk_details == NULL)
+ return (ENXIO);
+
+ /* Simply return the stored frequency */
+ if (freq)
+ *freq = (unsigned int)clk_details->src_freq;
+
+ return (0);
+}
+
+/**
+ * omap4_clk_gptimer_set_source - checks if a module is accessible
+ * @module: identifier for the module to check, see omap3_prcm.h for a list
+ * of possible modules.
+ * Example: OMAP3_MODULE_MMC1
+ *
+ *
+ *
+ * LOCKING:
+ * Inherits the locks from the omap_prcm driver, no internal locking.
+ *
+ * RETURNS:
+ * Returns 0 on success or a negative error code on failure.
+ */
+static int
+omap4_clk_gptimer_set_source(struct ti_clock_dev *clkdev,
+ clk_src_t clksrc)
+{
+ struct omap4_prcm_softc *sc;
+ struct omap4_clk_details* clk_details;
+ struct resource* clk_mem_res;
+
+ clk_details = omap4_clk_details(clkdev->id);
+
+ if (clk_details == NULL)
+ return (ENXIO);
+
+ sc = omap4_prcm_get_instance_softc(clk_details->instance);
+ if (sc == NULL)
+ return ENXIO;
+
+ clk_mem_res = sc->sc_res;
+
+ if (clk_mem_res == NULL)
+ return (EINVAL);
+
+ /* TODO: Implement */
+
+ return (0);
+}
+
+/**
+ * omap4_clk_gptimer_get_source_freq - checks if a module is accessible
+ * @module: identifier for the module to check, see omap3_prcm.h for a list
+ * of possible modules.
+ * Example: OMAP3_MODULE_MMC1
+ *
+ *
+ *
+ * LOCKING:
+ * Inherits the locks from the omap_prcm driver, no internal locking.
+ *
+ * RETURNS:
+ * Returns 0 on success or a negative error code on failure.
+ */
+static int
+omap4_clk_gptimer_get_source_freq(struct ti_clock_dev *clkdev,
+ unsigned int *freq
+ )
+{
+ struct omap4_prcm_softc *sc;
+ struct omap4_clk_details* clk_details;
+ struct resource* clk_mem_res;
+ uint32_t clksel;
+ unsigned int src_freq;
+
+ clk_details = omap4_clk_details(clkdev->id);
+
+ if (clk_details == NULL)
+ return (ENXIO);
+
+ sc = omap4_prcm_get_instance_softc(clk_details->instance);
+ if (sc == NULL)
+ return ENXIO;
+
+ clk_mem_res = sc->sc_res;
+
+ if (clk_mem_res == NULL)
+ return (EINVAL);
+
+ /* Need to read the CLKSEL field to determine the clock source */
+ clksel = bus_read_4(clk_mem_res, clk_details->clksel_reg);
+ if (clksel & (0x1UL << 24))
+ src_freq = FREQ_32KHZ;
+ else
+ omap4_clk_get_sysclk_freq(NULL, &src_freq);
+
+ /* Return the frequency */
+ if (freq)
+ *freq = src_freq;
+
+ return (0);
+}
+
+/**
+ * omap4_clk_hsmmc_set_source - sets the source clock (freq)
+ * @clkdev: pointer to the clockdev structure (id field will contain clock id)
+ *
+ * The MMC 1 and 2 clocks can be source from either a 64MHz or 96MHz clock.
+ *
+ * LOCKING:
+ * Inherits the locks from the omap_prcm driver, no internal locking.
+ *
+ * RETURNS:
+ * Returns 0 on success or a negative error code on failure.
+ */
+static int
+omap4_clk_hsmmc_set_source(struct ti_clock_dev *clkdev,
+ clk_src_t clksrc)
+{
+ struct omap4_prcm_softc *sc;
+ struct omap4_clk_details* clk_details;
+ struct resource* clk_mem_res;
+ uint32_t clksel;
+
+ clk_details = omap4_clk_details(clkdev->id);
+
+ if (clk_details == NULL)
+ return (ENXIO);
+
+ sc = omap4_prcm_get_instance_softc(clk_details->instance);
+ if (sc == NULL)
+ return ENXIO;
+
+ clk_mem_res = sc->sc_res;
+
+ if (clk_mem_res == NULL)
+ return (EINVAL);
+
+ /* For MMC modules 3, 4 & 5 you can't change the freq, it's always 48MHz */
+ if ((clkdev->id == MMC3_CLK) || (clkdev->id == MMC4_CLK) ||
+ (clkdev->id == MMC5_CLK)) {
+ if (clksrc != F48MHZ_CLK)
+ return (EINVAL);
+ return 0;
+ }
+
+ clksel = bus_read_4(clk_mem_res, clk_details->clksel_reg);
+
+ /* Bit 24 is set if 96MHz clock or cleared for 64MHz clock */
+ if (clksrc == F64MHZ_CLK)
+ clksel &= ~(0x1UL << 24);
+ else if (clksrc == F96MHZ_CLK)
+ clksel |= (0x1UL << 24);
+ else
+ return (EINVAL);
+
+ bus_write_4(clk_mem_res, clk_details->clksel_reg, clksel);
+
+ return (0);
+}
+
+/**
+ * omap4_clk_hsmmc_get_source_freq - checks if a module is accessible
+ * @clkdev: pointer to the clockdev structure (id field will contain clock id)
+ *
+ *
+ *
+ * LOCKING:
+ * Inherits the locks from the omap_prcm driver, no internal locking.
+ *
+ * RETURNS:
+ * Returns 0 on success or a negative error code on failure.
+ */
+static int
+omap4_clk_hsmmc_get_source_freq(struct ti_clock_dev *clkdev,
+ unsigned int *freq
+ )
+{
+ struct omap4_prcm_softc *sc;
+ struct omap4_clk_details* clk_details;
+ struct resource* clk_mem_res;
+ uint32_t clksel;
+ unsigned int src_freq;
+
+ clk_details = omap4_clk_details(clkdev->id);
+
+ if (clk_details == NULL)
+ return (ENXIO);
+
+ sc = omap4_prcm_get_instance_softc(clk_details->instance);
+ if (sc == NULL)
+ return ENXIO;
+
+ clk_mem_res = sc->sc_res;
+
+ if (clk_mem_res == NULL)
+ return (EINVAL);
+
+ switch (clkdev->id) {
+ case MMC1_CLK:
+ case MMC2_CLK:
+ /* Need to read the CLKSEL field to determine the clock source */
+ clksel = bus_read_4(clk_mem_res, clk_details->clksel_reg);
+ if (clksel & (0x1UL << 24))
+ src_freq = FREQ_96MHZ;
+ else
+ src_freq = FREQ_64MHZ;
+ break;
+ case MMC3_CLK:
+ case MMC4_CLK:
+ case MMC5_CLK:
+ src_freq = FREQ_48MHZ;
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ /* Return the frequency */
+ if (freq)
+ *freq = src_freq;
+
+ return (0);
+}
+
+/**
+ * omap4_clk_get_sysclk_freq - gets the sysclk frequency
+ * @sc: pointer to the clk module/device context
+ *
+ * Read the clocking information from the power-control/boot-strap registers,
+ * and stored in two global variables.
+ *
+ * RETURNS:
+ * nothing, values are saved in global variables
+ */
+static int
+omap4_clk_get_sysclk_freq(struct ti_clock_dev *clkdev,
+ unsigned int *freq)
+{
+ uint32_t clksel;
+ uint32_t sysclk;
+ struct omap4_prcm_softc *sc;
+
+ sc = omap4_prcm_get_instance_softc(PRM_INSTANCE);
+ if (sc == NULL)
+ return ENXIO;
+
+ /* Read the input clock freq from the configuration register (CM_SYS_CLKSEL) */
+ clksel = bus_read_4(sc->sc_res, CM_SYS_CLKSEL_OFFSET);
+ switch (clksel & 0x7) {
+ case 0x1:
+ /* 12Mhz */
+ sysclk = 12000000;
+ break;
+ case 0x3:
+ /* 16.8Mhz */
+ sysclk = 16800000;
+ break;
+ case 0x4:
+ /* 19.2Mhz */
+ sysclk = 19200000;
+ break;
+ case 0x5:
+ /* 26Mhz */
+ sysclk = 26000000;
+ break;
+ case 0x7:
+ /* 38.4Mhz */
+ sysclk = 38400000;
+ break;
+ default:
+ panic("%s: Invalid clock freq", __func__);
+ }
+
+ /* Return the value */
+ if (freq)
+ *freq = sysclk;
+
+ return (0);
+}
+
+/**
+ * omap4_clk_get_arm_fclk_freq - gets the MPU clock frequency
+ * @clkdev: ignored
+ * @freq: pointer which upon return will contain the freq in hz
+ * @mem_res: array of allocated memory resources
+ *
+ * Reads the frequency setting information registers and returns the value
+ * in the freq variable.
+ *
+ * RETURNS:
+ * returns 0 on success, a positive error code on failure.
+ */
+static int
+omap4_clk_get_arm_fclk_freq(struct ti_clock_dev *clkdev,
+ unsigned int *freq)
+{
+ uint32_t clksel;
+ uint32_t pll_mult, pll_div;
+ uint32_t mpuclk, sysclk;
+ struct omap4_prcm_softc *sc;
+
+ sc = omap4_prcm_get_instance_softc(CM1_INSTANCE);
+ if (sc == NULL)
+ return ENXIO;
+
+ /* Read the clksel register which contains the DPLL multiple and divide
+ * values. These are applied to the sysclk.
+ */
+ clksel = bus_read_4(sc->sc_res, CM_CLKSEL_DPLL_MPU);
+
+ pll_mult = ((clksel >> 8) & 0x7ff);
+ pll_div = (clksel & 0x7f) + 1;
+
+ /* Get the system clock freq */
+ omap4_clk_get_sysclk_freq(NULL, &sysclk);
+
+ /* Calculate the MPU freq */
+ mpuclk = ((uint64_t)sysclk * pll_mult) / pll_div;
+
+ /* Return the value */
+ if (freq)
+ *freq = mpuclk;
+
+ return (0);
+}
+
+/**
+ * omap4_clk_hsusbhost_activate - activates the USB clocks for the given module
+ * @clkdev: pointer to the clock device structure.
+ * @mem_res: array of memory resources allocated by the top level PRCM driver.
+ *
+ * The USB clocking setup seems to be a bit more tricky than the other modules,
+ * to start with the clocking diagram for the HS host module shows 13 different
+ * clocks. So to try and make it easier to follow the clocking activation
+ * and deactivation is handled in its own set of callbacks.
+ *
+ * LOCKING:
+ * Inherits the locks from the omap_prcm driver, no internal locking.
+ *
+ * RETURNS:
+ * Returns 0 on success or a positive error code on failure.
+ */
+
+struct dpll_param {
+ unsigned int m;
+ unsigned int n;
+ unsigned int m2;
+ unsigned int m3;
+ unsigned int m4;
+ unsigned int m5;
+ unsigned int m6;
+ unsigned int m7;
+};
+/* USB parameters */
+struct dpll_param usb_dpll_param[7] = {
+ /* 12M values */
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ /* 13M values */
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ /* 16.8M values */
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ /* 19.2M values */
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ /* 26M values */
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ /* 27M values */
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ /* 38.4M values */
+#ifdef CONFIG_OMAP4_SDC
+ {0x32, 0x1, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0},
+#else
+ {0x32, 0x1, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0},
+#endif
+};
+static int
+omap4_clk_hsusbhost_activate(struct ti_clock_dev *clkdev)
+{
+ struct omap4_prcm_softc *sc;
+ struct resource* clk_mem_res;
+ uint32_t clksel_reg_off;
+ uint32_t clksel;
+ unsigned int i;
+
+ sc = omap4_prcm_get_instance_softc(CM2_INSTANCE);
+ if (sc == NULL)
+ return ENXIO;
+
+ switch (clkdev->id) {
+ case USBTLL_CLK:
+ /* For the USBTLL module we need to enable the following clocks:
+ * - INIT_L4_ICLK (will be enabled by bootloader)
+ * - TLL_CH0_FCLK
+ * - TLL_CH1_FCLK
+ */
+
+ /* We need the CM_L3INIT_HSUSBTLL_CLKCTRL register in CM2 register set */
+ clk_mem_res = sc->sc_res;
+ clksel_reg_off = L3INIT_CM2_OFFSET + 0x68;
+
+ /* Enable the module and also enable the optional func clocks for
+ * channels 0 & 1 (is this needed ?)
+ */
+ clksel = bus_read_4(clk_mem_res, clksel_reg_off);
+ clksel &= ~CLKCTRL_MODULEMODE_MASK;
+ clksel |= CLKCTRL_MODULEMODE_ENABLE;
+
+ clksel |= (0x1 << 8); /* USB-HOST optional clock: USB_CH0_CLK */
+ clksel |= (0x1 << 9); /* USB-HOST optional clock: USB_CH1_CLK */
+ break;
+
+ case USBHSHOST_CLK:
+ case USBP1_PHY_CLK:
+ case USBP2_PHY_CLK:
+ case USBP1_UTMI_CLK:
+ case USBP2_UTMI_CLK:
+ case USBP1_HSIC_CLK:
+ case USBP2_HSIC_CLK:
+ /* For the USB HS HOST module we need to enable the following clocks:
+ * - INIT_L4_ICLK (will be enabled by bootloader)
+ * - INIT_L3_ICLK (will be enabled by bootloader)
+ * - INIT_48MC_FCLK
+ * - UTMI_ROOT_GFCLK (UTMI only, create a new clock for that ?)
+ * - UTMI_P1_FCLK (UTMI only, create a new clock for that ?)
+ * - UTMI_P2_FCLK (UTMI only, create a new clock for that ?)
+ * - HSIC_P1_60 (HSIC only, create a new clock for that ?)
+ * - HSIC_P1_480 (HSIC only, create a new clock for that ?)
+ * - HSIC_P2_60 (HSIC only, create a new clock for that ?)
+ * - HSIC_P2_480 (HSIC only, create a new clock for that ?)
+ */
+
+ /* We need the CM_L3INIT_HSUSBHOST_CLKCTRL register in CM2 register set */
+ clk_mem_res = sc->sc_res;
+ clksel_reg_off = L3INIT_CM2_OFFSET + 0x58;
+ clksel = bus_read_4(clk_mem_res, clksel_reg_off);
+ /* Enable the module and also enable the optional func clocks */
+ if (clkdev->id == USBHSHOST_CLK) {
+ clksel &= ~CLKCTRL_MODULEMODE_MASK;
+ clksel |= /*CLKCTRL_MODULEMODE_ENABLE*/2;
+
+ clksel |= (0x1 << 15); /* USB-HOST clock control: FUNC48MCLK */
+ }
+
+ else if (clkdev->id == USBP1_UTMI_CLK)
+ clksel |= (0x1 << 8); /* UTMI_P1_CLK */
+ else if (clkdev->id == USBP2_UTMI_CLK)
+ clksel |= (0x1 << 9); /* UTMI_P2_CLK */
+
+ else if (clkdev->id == USBP1_HSIC_CLK)
+ clksel |= (0x5 << 11); /* HSIC60M_P1_CLK + HSIC480M_P1_CLK */
+ else if (clkdev->id == USBP2_HSIC_CLK)
+ clksel |= (0x5 << 12); /* HSIC60M_P2_CLK + HSIC480M_P2_CLK */
+
+ break;
+
+ default:
+ return (EINVAL);
+ }
+
+ bus_write_4(clk_mem_res, clksel_reg_off, clksel);
+
+ /* Try MAX_MODULE_ENABLE_WAIT number of times to check if enabled */
+ for (i = 0; i < MAX_MODULE_ENABLE_WAIT; i++) {
+ clksel = bus_read_4(clk_mem_res, clksel_reg_off);
+ if ((clksel & CLKCTRL_IDLEST_MASK) == CLKCTRL_IDLEST_ENABLED)
+ break;
+ }
+
+ /* Check the enabled state */
+ if ((clksel & CLKCTRL_IDLEST_MASK) != CLKCTRL_IDLEST_ENABLED) {
+ printf("Error: HERE failed to enable module with clock %d\n", clkdev->id);
+ printf("Error: 0x%08x => 0x%08x\n", clksel_reg_off, clksel);
+ return (ETIMEDOUT);
+ }
+
+ return (0);
+}
+
+/**
+ * omap4_clk_generic_deactivate - checks if a module is accessible
+ * @clkdev: pointer to the clock device structure.
+ * @mem_res: array of memory resources allocated by the top level PRCM driver.
+ *
+ *
+ *
+ * LOCKING:
+ * Inherits the locks from the omap_prcm driver, no internal locking.
+ *
+ * RETURNS:
+ * Returns 0 on success or a positive error code on failure.
+ */
+static int
+omap4_clk_hsusbhost_deactivate(struct ti_clock_dev *clkdev)
+{
+ struct omap4_prcm_softc *sc;
+ struct resource* clk_mem_res;
+ uint32_t clksel_reg_off;
+ uint32_t clksel;
+
+ sc = omap4_prcm_get_instance_softc(CM2_INSTANCE);
+ if (sc == NULL)
+ return ENXIO;
+
+ switch (clkdev->id) {
+ case USBTLL_CLK:
+ /* We need the CM_L3INIT_HSUSBTLL_CLKCTRL register in CM2 register set */
+ clk_mem_res = sc->sc_res;
+ clksel_reg_off = L3INIT_CM2_OFFSET + 0x68;
+
+ clksel = bus_read_4(clk_mem_res, clksel_reg_off);
+ clksel &= ~CLKCTRL_MODULEMODE_MASK;
+ clksel |= CLKCTRL_MODULEMODE_DISABLE;
+ break;
+
+ case USBHSHOST_CLK:
+ case USBP1_PHY_CLK:
+ case USBP2_PHY_CLK:
+ case USBP1_UTMI_CLK:
+ case USBP2_UTMI_CLK:
+ case USBP1_HSIC_CLK:
+ case USBP2_HSIC_CLK:
+ /* For the USB HS HOST module we need to enable the following clocks:
+ * - INIT_L4_ICLK (will be enabled by bootloader)
+ * - INIT_L3_ICLK (will be enabled by bootloader)
+ * - INIT_48MC_FCLK
+ * - UTMI_ROOT_GFCLK (UTMI only, create a new clock for that ?)
+ * - UTMI_P1_FCLK (UTMI only, create a new clock for that ?)
+ * - UTMI_P2_FCLK (UTMI only, create a new clock for that ?)
+ * - HSIC_P1_60 (HSIC only, create a new clock for that ?)
+ * - HSIC_P1_480 (HSIC only, create a new clock for that ?)
+ * - HSIC_P2_60 (HSIC only, create a new clock for that ?)
+ * - HSIC_P2_480 (HSIC only, create a new clock for that ?)
+ */
+
+ /* We need the CM_L3INIT_HSUSBHOST_CLKCTRL register in CM2 register set */
+ clk_mem_res = sc->sc_res;
+ clksel_reg_off = L3INIT_CM2_OFFSET + 0x58;
+ clksel = bus_read_4(clk_mem_res, clksel_reg_off);
+
+ /* Enable the module and also enable the optional func clocks */
+ if (clkdev->id == USBHSHOST_CLK) {
+ clksel &= ~CLKCTRL_MODULEMODE_MASK;
+ clksel |= CLKCTRL_MODULEMODE_DISABLE;
+
+ clksel &= ~(0x1 << 15); /* USB-HOST clock control: FUNC48MCLK */
+ }
+
+ else if (clkdev->id == USBP1_UTMI_CLK)
+ clksel &= ~(0x1 << 8); /* UTMI_P1_CLK */
+ else if (clkdev->id == USBP2_UTMI_CLK)
+ clksel &= ~(0x1 << 9); /* UTMI_P2_CLK */
+
+ else if (clkdev->id == USBP1_HSIC_CLK)
+ clksel &= ~(0x5 << 11); /* HSIC60M_P1_CLK + HSIC480M_P1_CLK */
+ else if (clkdev->id == USBP2_HSIC_CLK)
+ clksel &= ~(0x5 << 12); /* HSIC60M_P2_CLK + HSIC480M_P2_CLK */
+
+ break;
+
+ default:
+ return (EINVAL);
+ }
+
+ bus_write_4(clk_mem_res, clksel_reg_off, clksel);
+
+ return (0);
+}
+
+/**
+ * omap4_clk_hsusbhost_accessible - checks if a module is accessible
+ * @clkdev: pointer to the clock device structure.
+ * @mem_res: array of memory resources allocated by the top level PRCM driver.
+ *
+ *
+ *
+ * LOCKING:
+ * Inherits the locks from the omap_prcm driver, no internal locking.
+ *
+ * RETURNS:
+ * Returns 0 if module is not enable, 1 if module is enabled or a negative
+ * error code on failure.
+ */
+static int
+omap4_clk_hsusbhost_accessible(struct ti_clock_dev *clkdev)
+{
+ struct omap4_prcm_softc *sc;
+ struct resource* clk_mem_res;
+ uint32_t clksel_reg_off;
+ uint32_t clksel;
+
+ sc = omap4_prcm_get_instance_softc(CM2_INSTANCE);
+ if (sc == NULL)
+ return ENXIO;
+
+ if (clkdev->id == USBTLL_CLK) {
+ /* We need the CM_L3INIT_HSUSBTLL_CLKCTRL register in CM2 register set */
+ clk_mem_res = sc->sc_res;
+ clksel_reg_off = L3INIT_CM2_OFFSET + 0x68;
+ }
+ else if (clkdev->id == USBHSHOST_CLK) {
+ /* We need the CM_L3INIT_HSUSBHOST_CLKCTRL register in CM2 register set */
+ clk_mem_res = sc->sc_res;
+ clksel_reg_off = L3INIT_CM2_OFFSET + 0x58;
+ }
+ else {
+ return (EINVAL);
+ }
+
+ clksel = bus_read_4(clk_mem_res, clksel_reg_off);
+
+ /* Check the enabled state */
+ if ((clksel & CLKCTRL_IDLEST_MASK) != CLKCTRL_IDLEST_ENABLED)
+ return (0);
+
+ return (1);
+}
+
+/**
+ * omap4_clk_hsusbhost_set_source - sets the source clocks
+ * @clkdev: pointer to the clock device structure.
+ * @clksrc: the clock source ID for the given clock.
+ * @mem_res: array of memory resources allocated by the top level PRCM driver.
+ *
+ *
+ *
+ * LOCKING:
+ * Inherits the locks from the omap_prcm driver, no internal locking.
+ *
+ * RETURNS:
+ * Returns 0 if successful otherwise a negative error code on failure.
+ */
+static int
+omap4_clk_hsusbhost_set_source(struct ti_clock_dev *clkdev,
+ clk_src_t clksrc)
+{
+ struct omap4_prcm_softc *sc;
+ struct resource* clk_mem_res;
+ uint32_t clksel_reg_off;
+ uint32_t clksel;
+ unsigned int bit;
+
+ sc = omap4_prcm_get_instance_softc(CM2_INSTANCE);
+ if (sc == NULL)
+ return ENXIO;
+
+ if (clkdev->id == USBP1_PHY_CLK)
+ bit = 24;
+ else if (clkdev->id != USBP2_PHY_CLK)
+ bit = 25;
+ else
+ return (EINVAL);
+
+ /* We need the CM_L3INIT_HSUSBHOST_CLKCTRL register in CM2 register set */
+ clk_mem_res = sc->sc_res;
+ clksel_reg_off = L3INIT_CM2_OFFSET + 0x58;
+ clksel = bus_read_4(clk_mem_res, clksel_reg_off);
+
+ /* Set the clock source to either external or internal */
+ if (clksrc == EXT_CLK)
+ clksel |= (0x1 << bit);
+ else
+ clksel &= ~(0x1 << bit);
+
+ bus_write_4(clk_mem_res, clksel_reg_off, clksel);
+
+ return (0);
+}
+
+#define PRM_RSTCTRL 0x1b00
+#define PRM_RSTCTRL_RESET 0x2
+
+static void
+omap4_prcm_reset(void)
+{
+ struct omap4_prcm_softc *sc;
+
+ sc = omap4_prcm_get_instance_softc(PRM_INSTANCE);
+ if (sc == NULL)
+ return;
+
+ bus_write_4(sc->sc_res, PRM_RSTCTRL,
+ bus_read_4(sc->sc_res, PRM_RSTCTRL) | PRM_RSTCTRL_RESET);
+ bus_read_4(sc->sc_res, PRM_RSTCTRL);
+}
+
+/**
+ * omap4_prcm_probe - probe function for the driver
+ * @dev: prcm device handle
+ *
+ * Simply sets the name of the driver module.
+ *
+ * LOCKING:
+ * None
+ *
+ * RETURNS:
+ * Always returns 0
+ */
+static int
+omap4_prcm_probe(device_t dev)
+{
+ const struct ofw_compat_data *ocd;
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ ocd = ofw_bus_search_compatible(dev, compat_data);
+ if ((int)ocd->ocd_data == 0)
+ return (ENXIO);
+
+ switch ((int)ocd->ocd_data) {
+ case PRM_INSTANCE:
+ device_set_desc(dev, "TI OMAP Power, Reset and Clock Management (PRM)");
+ break;
+ case CM1_INSTANCE:
+ device_set_desc(dev, "TI OMAP Power, Reset and Clock Management (C1)");
+ break;
+ case CM2_INSTANCE:
+ device_set_desc(dev, "TI OMAP Power, Reset and Clock Management (C2)");
+ break;
+ default:
+ device_printf(dev, "unknown instance type: %d\n", (int)ocd->ocd_data);
+ return (ENXIO);
+ }
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+/**
+ * omap_prcm_attach - attach function for the driver
+ * @dev: prcm device handle
+ *
+ * Allocates and sets up the driver context, this simply entails creating a
+ * bus mappings for the PRCM register set.
+ *
+ * LOCKING:
+ * None
+ *
+ * RETURNS:
+ * Always returns 0
+ */
+
+extern uint32_t platform_arm_tmr_freq;
+
+static int
+omap4_prcm_attach(device_t dev)
+{
+ struct omap4_prcm_softc *sc;
+ const struct ofw_compat_data *ocd;
+
+ sc = device_get_softc(dev);
+ ocd = ofw_bus_search_compatible(dev, compat_data);
+ sc->sc_instance = (int)ocd->ocd_data;
+
+ sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid,
+ RF_ACTIVE);
+ if (sc->sc_res == NULL) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ ti_cpu_reset = omap4_prcm_reset;
+
+ return (0);
+}
+
+static void
+omap4_prcm_new_pass(device_t dev)
+{
+ struct omap4_prcm_softc *sc = device_get_softc(dev);
+ unsigned int freq;
+
+ if (sc->attach_done ||
+ bus_current_pass < (BUS_PASS_TIMER + BUS_PASS_ORDER_EARLY)) {
+ bus_generic_new_pass(dev);
+ return;
+ }
+ sc->attach_done = 1;
+
+ /*
+ * In order to determine ARM frequency we need both RPM and CM1
+ * instances up and running. So wait until all CRM devices are
+ * initialized. Should be replaced with proper clock framework
+ */
+ if (device_get_unit(dev) == 2) {
+ omap4_clk_get_arm_fclk_freq(NULL, &freq);
+ arm_tmr_change_frequency(freq / 2);
+ }
+
+ return;
+}
+
+static device_method_t omap4_prcm_methods[] = {
+ DEVMETHOD(device_probe, omap4_prcm_probe),
+ DEVMETHOD(device_attach, omap4_prcm_attach),
+
+ /* Bus interface */
+ DEVMETHOD(bus_new_pass, omap4_prcm_new_pass),
+
+ {0, 0},
+};
+
+static driver_t omap4_prcm_driver = {
+ "omap4_prcm",
+ omap4_prcm_methods,
+ sizeof(struct omap4_prcm_softc),
+};
+
+static devclass_t omap4_prcm_devclass;
+
+EARLY_DRIVER_MODULE(omap4_prcm, simplebus, omap4_prcm_driver,
+ omap4_prcm_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
+MODULE_VERSION(omap4_prcm, 1);
diff --git a/sys/arm/ti/omap4/omap4_reg.h b/sys/arm/ti/omap4/omap4_reg.h
new file mode 100644
index 000000000000..50cacae6ea9c
--- /dev/null
+++ b/sys/arm/ti/omap4/omap4_reg.h
@@ -0,0 +1,542 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2011
+ * Ben Gray <ben.r.gray@gmail.com>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Texas Instruments - OMAP44xx series processors
+ *
+ * Reference:
+ * OMAP44xx Applications Processor
+ * Technical Reference Manual
+ * (omap44xx_techref.pdf)
+ *
+ *
+ * Note:
+ * The devices are mapped into address above 0xD000_0000 as the kernel space
+ * memory is at 0xC000_0000 and above. The first 256MB after this is reserved
+ * for the size of the kernel, everything above that is reserved for SoC
+ * devices.
+ *
+ */
+#ifndef _OMAP44XX_REG_H_
+#define _OMAP44XX_REG_H_
+
+#ifndef _LOCORE
+#include <sys/types.h> /* for uint32_t */
+#endif
+
+/* Physical/Virtual address for SDRAM controller */
+
+#define OMAP44XX_SMS_VBASE 0x6C000000UL
+#define OMAP44XX_SMS_HWBASE 0x6C000000UL
+#define OMAP44XX_SMS_SIZE 0x01000000UL
+
+#define OMAP44XX_SDRC_VBASE 0x6D000000UL
+#define OMAP44XX_SDRC_HWBASE 0x6D000000UL
+#define OMAP44XX_SDRC_SIZE 0x01000000UL
+
+/* Physical/Virtual address for I/O space */
+
+#define OMAP44XX_L3_EMU_VBASE 0xD4000000UL
+#define OMAP44XX_L3_EMU_HWBASE 0x54000000UL
+#define OMAP44XX_L3_EMU_SIZE 0x00200000UL
+
+#define OMAP44XX_L3_EMIF1_VBASE 0xEC000000UL
+#define OMAP44XX_L3_EMIF1_HWBASE 0x4C000000UL
+#define OMAP44XX_L3_EMIF1_SIZE 0x01000000UL
+
+#define OMAP44XX_L3_EMIF2_VBASE 0xED000000UL
+#define OMAP44XX_L3_EMIF2_HWBASE 0x4D000000UL
+#define OMAP44XX_L3_EMIF2_SIZE 0x01000000UL
+
+#define OMAP44XX_L4_CORE_VBASE 0xEA000000UL
+#define OMAP44XX_L4_CORE_HWBASE 0x4A000000UL
+#define OMAP44XX_L4_CORE_SIZE 0x01000000UL
+
+#define OMAP44XX_L4_WAKEUP_VBASE 0xEA300000UL
+#define OMAP44XX_L4_WAKEUP_HWBASE 0x4A300000UL
+#define OMAP44XX_L4_WAKEUP_SIZE 0x00040000UL
+
+#define OMAP44XX_L4_PERIPH_VBASE 0xE8000000UL
+#define OMAP44XX_L4_PERIPH_HWBASE 0x48000000UL
+#define OMAP44XX_L4_PERIPH_SIZE 0x01000000UL
+
+#define OMAP44XX_L4_ABE_VBASE 0xE9000000UL
+#define OMAP44XX_L4_ABE_HWBASE 0x49000000UL
+#define OMAP44XX_L4_ABE_SIZE 0x00100000UL
+
+/* Physical/Virtual address for MPU Subsystem space */
+
+#define OMAP44XX_MPU_SUBSYS_VBASE (OMAP44XX_L4_PERIPH_VBASE + 0x00240000UL)
+#define OMAP44XX_MPU_SUBSYS_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + 0x00240000UL)
+#define OMAP44XX_MPU_SUBSYS_SIZE 0x00004000UL
+
+/*
+ * MPU Subsystem addresss offsets
+ */
+#define OMAP44XX_SCU_OFFSET 0x00000000UL
+#define OMAP44XX_GIC_CPU_OFFSET 0x00000100UL
+#define OMAP44XX_GBL_TIMER_OFFSET 0x00000200UL
+#define OMAP44XX_PRV_TIMER_OFFSET 0x00000600UL
+#define OMAP44XX_GIC_DIST_OFFSET 0x00001000UL
+#define OMAP44XX_PL310_OFFSET 0x00002000UL
+#define OMAP44XX_CORTEXA9_SOCKET_PRCM_OFFSET 0x00003000UL
+#define OMAP44XX_CORTEXA9_PRM_OFFSET 0x00003200UL
+#define OMAP44XX_CORTEXA9_CPU0_OFFSET 0x00003400UL
+#define OMAP44XX_CORTEXA9_CPU1_OFFSET 0x00003800UL
+
+#define OMAP44XX_SCU_HWBASE (OMAP44XX_MPU_SUBSYS_HWBASE + OMAP44XX_SCU_OFFSET)
+#define OMAP44XX_SCU_VBASE (OMAP44XX_MPU_SUBSYS_VBASE + OMAP44XX_SCU_OFFSET)
+#define OMAP44XX_SCU_SIZE 0x00000080UL
+#define OMAP44XX_GIC_CPU_HWBASE (OMAP44XX_MPU_SUBSYS_HWBASE + OMAP44XX_GIC_CPU_OFFSET)
+#define OMAP44XX_GIC_CPU_VBASE (OMAP44XX_MPU_SUBSYS_VBASE + OMAP44XX_GIC_CPU_OFFSET)
+#define OMAP44XX_GIC_CPU_SIZE 0x00000100UL
+#define OMAP44XX_GBL_TIMER_HWBASE (OMAP44XX_MPU_SUBSYS_HWBASE + OMAP44XX_GBL_TIMER_OFFSET)
+#define OMAP44XX_GBL_TIMER_VBASE (OMAP44XX_MPU_SUBSYS_VBASE + OMAP44XX_GBL_TIMER_OFFSET)
+#define OMAP44XX_GBL_TIMER_SIZE 0x00000100UL
+#define OMAP44XX_PRV_TIMER_HWBASE (OMAP44XX_MPU_SUBSYS_HWBASE + OMAP44XX_PRV_TIMER_OFFSET)
+#define OMAP44XX_PRV_TIMER_VBASE (OMAP44XX_MPU_SUBSYS_VBASE + OMAP44XX_PRV_TIMER_OFFSET)
+#define OMAP44XX_PRV_TIMER_SIZE 0x00000100UL
+#define OMAP44XX_GIC_DIST_HWBASE (OMAP44XX_MPU_SUBSYS_HWBASE + OMAP44XX_GIC_DIST_OFFSET)
+#define OMAP44XX_GIC_DIST_VBASE (OMAP44XX_MPU_SUBSYS_VBASE + OMAP44XX_GIC_DIST_OFFSET)
+#define OMAP44XX_GIC_DIST_SIZE 0x00000100UL
+#define OMAP44XX_PL310_HWBASE (OMAP44XX_MPU_SUBSYS_HWBASE + OMAP44XX_PL310_OFFSET)
+#define OMAP44XX_PL310_VBASE (OMAP44XX_MPU_SUBSYS_VBASE + OMAP44XX_PL310_OFFSET)
+#define OMAP44XX_PL310_SIZE 0x00001000UL
+
+/*
+ * L4-CORE Physical/Virtual addresss offsets
+ */
+#define OMAP44XX_SCM_OFFSET 0x00002000UL
+#define OMAP44XX_CM_OFFSET 0x00004000UL
+#define OMAP44XX_SDMA_OFFSET 0x00056000UL
+#define OMAP44XX_USB_TLL_OFFSET 0x00062000UL
+#define OMAP44XX_USB_UHH_OFFSET 0x00064000UL
+#define OMAP44XX_USB_OHCI_OFFSET 0x00064800UL
+#define OMAP44XX_USB_EHCI_OFFSET 0x00064C00UL
+#define OMAP44XX_MCBSP1_OFFSET 0x00074000UL
+#define OMAP44XX_MCBSP5_OFFSET 0x00096000UL
+#define OMAP44XX_SCM_PADCONF_OFFSET 0x00100000UL
+
+/*
+ * L4-WAKEUP Physical/Virtual addresss offsets
+ */
+#define OMAP44XX_PRM_OFFSET 0x00006000UL
+#define OMAP44XX_SCRM_OFFSET 0x0000A000UL
+#define OMAP44XX_GPIO1_OFFSET 0x00010000UL
+#define OMAP44XX_GPTIMER1_OFFSET 0x00018000UL
+
+/*
+ * L4-PERIPH Physical/Virtual addresss offsets
+ */
+#define OMAP44XX_UART3_OFFSET 0x00020000UL
+#define OMAP44XX_GPTIMER2_OFFSET 0x00032000UL
+#define OMAP44XX_GPTIMER3_OFFSET 0x00034000UL
+#define OMAP44XX_GPTIMER4_OFFSET 0x00036000UL
+#define OMAP44XX_GPTIMER9_OFFSET 0x0003E000UL
+#define OMAP44XX_GPIO2_OFFSET 0x00055000UL
+#define OMAP44XX_GPIO3_OFFSET 0x00057000UL
+#define OMAP44XX_GPIO4_OFFSET 0x00059000UL
+#define OMAP44XX_GPIO5_OFFSET 0x0005B000UL
+#define OMAP44XX_GPIO6_OFFSET 0x0005D000UL
+#define OMAP44XX_I2C3_OFFSET 0x00060000UL
+#define OMAP44XX_UART1_OFFSET 0x0006A000UL
+#define OMAP44XX_UART2_OFFSET 0x0006C000UL
+#define OMAP44XX_UART4_OFFSET 0x0006E000UL
+#define OMAP44XX_I2C1_OFFSET 0x00070000UL
+#define OMAP44XX_I2C2_OFFSET 0x00072000UL
+#define OMAP44XX_SLIMBUS2_OFFSET 0x00076000UL
+#define OMAP44XX_ELM_OFFSET 0x00078000UL
+#define OMAP44XX_GPTIMER10_OFFSET 0x00086000UL
+#define OMAP44XX_GPTIMER11_OFFSET 0x00088000UL
+#define OMAP44XX_MCBSP4_OFFSET 0x00096000UL
+#define OMAP44XX_MCSPI1_OFFSET 0x00098000UL
+#define OMAP44XX_MCSPI2_OFFSET 0x0009A000UL
+#define OMAP44XX_MMCHS1_OFFSET 0x0009C000UL
+#define OMAP44XX_MMCSD3_OFFSET 0x000AD000UL
+#define OMAP44XX_MMCHS2_OFFSET 0x000B4000UL
+#define OMAP44XX_MMCSD4_OFFSET 0x000D1000UL
+#define OMAP44XX_MMCSD5_OFFSET 0x000D5000UL
+#define OMAP44XX_I2C4_OFFSET 0x00350000UL
+
+/* The following are registers defined as part of the ARM MPCORE system,
+ * they are not SoC components rather registers that control the MPCORE core.
+ */
+// #define OMAP44XX_SCU_OFFSET 0x48240000 /* Snoop control unit */
+// #define OMAP44XX_GIC_PROC_OFFSET 0x48240100 /* Interrupt controller unit */
+// #define OMAP44XX_MPU_TIMER_OFFSET 0x48240600
+// #define OMAP44XX_GIC_INTR_OFFSET 0x48241000
+// #define OMAP44XX_PL310_OFFSET 0x48242000 /* L2 Cache controller */
+
+/*
+ * L4-ABE Physical/Virtual addresss offsets
+ */
+#define OMAP44XX_GPTIMER5_OFFSET 0x00038000UL
+#define OMAP44XX_GPTIMER6_OFFSET 0x0003A000UL
+#define OMAP44XX_GPTIMER7_OFFSET 0x0003C000UL
+#define OMAP44XX_GPTIMER8_OFFSET 0x0003E000UL
+
+/*
+ * System Control Module
+ */
+#define OMAP44XX_SCM_HWBASE (OMAP44XX_L4_CORE_HWBASE + OMAP44XX_SCM_OFFSET)
+#define OMAP44XX_SCM_VBASE (OMAP44XX_L4_CORE_VBASE + OMAP44XX_SCM_OFFSET)
+#define OMAP44XX_SCM_SIZE 0x00001000UL
+
+/*
+ *
+ */
+#define OMAP44XX_CM_HWBASE (OMAP44XX_L4_CORE_HWBASE + OMAP44XX_CM_OFFSET)
+#define OMAP44XX_CM_VBASE (OMAP44XX_L4_CORE_VBASE + OMAP44XX_CM_OFFSET)
+#define OMAP44XX_CM_SIZE 0x00001500UL
+
+/*
+ *
+ */
+#define OMAP44XX_PRM_HWBASE (OMAP44XX_L4_WAKEUP_HWBASE + OMAP44XX_PRM_OFFSET)
+#define OMAP44XX_PRM_VBASE (OMAP44XX_L4_WAKEUP_VBASE + OMAP44XX_PRM_OFFSET)
+#define OMAP44XX_PRM_SIZE 0x00001600UL
+
+/*
+ *
+ */
+#define OMAP44XX_SCRM_HWBASE (OMAP44XX_L4_WAKEUP_HWBASE + OMAP44XX_SCRM_OFFSET)
+#define OMAP44XX_SCRM_VBASE (OMAP44XX_L4_WAKEUP_VBASE + OMAP44XX_SCRM_OFFSET)
+#define OMAP44XX_SCRM_SIZE 0x00000800UL
+
+/*
+ * Uarts
+ */
+#define OMAP44XX_UART1_HWBASE (OMAP44XX_L4_CORE_HWBASE + OMAP44XX_UART1_OFFSET)
+#define OMAP44XX_UART1_VBASE (OMAP44XX_L4_CORE_VBASE + OMAP44XX_UART1_OFFSET)
+#define OMAP44XX_UART1_SIZE 0x00001000UL
+#define OMAP44XX_UART2_HWBASE (OMAP44XX_L4_CORE_HWBASE + OMAP44XX_UART2_OFFSET)
+#define OMAP44XX_UART2_VBASE (OMAP44XX_L4_CORE_VBASE + OMAP44XX_UART2_OFFSET)
+#define OMAP44XX_UART2_SIZE 0x00001000UL
+#define OMAP44XX_UART3_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_UART3_OFFSET)
+#define OMAP44XX_UART3_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_UART3_OFFSET)
+#define OMAP44XX_UART3_SIZE 0x00001000UL
+#define OMAP44XX_UART4_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_UART4_OFFSET)
+#define OMAP44XX_UART4_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_UART4_OFFSET)
+#define OMAP44XX_UART4_SIZE 0x00001000UL
+
+/*
+ * I2C Modules
+ */
+#define OMAP44XX_I2C1_HWBASE (OMAP44XX_L4_CORE_HWBASE + OMAP44XX_I2C1_OFFSET)
+#define OMAP44XX_I2C1_VBASE (OMAP44XX_L4_CORE_VBASE + OMAP44XX_I2C1_OFFSET)
+#define OMAP44XX_I2C1_SIZE 0x00000080UL
+#define OMAP44XX_I2C2_HWBASE (OMAP44XX_L4_CORE_HWBASE + OMAP44XX_I2C2_OFFSET)
+#define OMAP44XX_I2C2_VBASE (OMAP44XX_L4_CORE_VBASE + OMAP44XX_I2C2_OFFSET)
+#define OMAP44XX_I2C2_SIZE 0x00000080UL
+#define OMAP44XX_I2C3_HWBASE (OMAP44XX_L4_CORE_HWBASE + OMAP44XX_I2C3_OFFSET)
+#define OMAP44XX_I2C3_VBASE (OMAP44XX_L4_CORE_VBASE + OMAP44XX_I2C3_OFFSET)
+#define OMAP44XX_I2C3_SIZE 0x00000080UL
+
+/*
+ * McBSP Modules
+ */
+#define OMAP44XX_MCBSP1_HWBASE (OMAP44XX_L4_CORE_HWBASE + OMAP44XX_MCBSP1_OFFSET)
+#define OMAP44XX_MCBSP1_VBASE (OMAP44XX_L4_CORE_VBASE + OMAP44XX_MCBSP1_OFFSET)
+#define OMAP44XX_MCBSP1_SIZE 0x00001000UL
+#define OMAP44XX_MCBSP2_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_MCBSP2_OFFSET)
+#define OMAP44XX_MCBSP2_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_MCBSP2_OFFSET)
+#define OMAP44XX_MCBSP2_SIZE 0x00001000UL
+#define OMAP44XX_MCBSP3_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_MCBSP3_OFFSET)
+#define OMAP44XX_MCBSP3_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_MCBSP3_OFFSET)
+#define OMAP44XX_MCBSP3_SIZE 0x00001000UL
+#define OMAP44XX_MCBSP4_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_MCBSP4_OFFSET)
+#define OMAP44XX_MCBSP4_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_MCBSP4_OFFSET)
+#define OMAP44XX_MCBSP4_SIZE 0x00001000UL
+#define OMAP44XX_MCBSP5_HWBASE (OMAP44XX_L4_CORE_HWBASE + OMAP44XX_MCBSP5_OFFSET)
+#define OMAP44XX_MCBSP5_VBASE (OMAP44XX_L4_CORE_VBASE + OMAP44XX_MCBSP5_OFFSET)
+#define OMAP44XX_MCBSP5_SIZE 0x00001000UL
+
+/*
+ * USB TTL Module
+ */
+#define OMAP44XX_USB_TLL_HWBASE (OMAP44XX_L4_CORE_HWBASE + OMAP44XX_USB_TLL_OFFSET)
+#define OMAP44XX_USB_TLL_VBASE (OMAP44XX_L4_CORE_VBASE + OMAP44XX_USB_TLL_OFFSET)
+#define OMAP44XX_USB_TLL_SIZE 0x00001000UL
+
+/*
+ * USB Host Module
+ */
+#define OMAP44XX_USB_UHH_HWBASE (OMAP44XX_L4_CORE_HWBASE + OMAP44XX_USB_UHH_OFFSET)
+#define OMAP44XX_USB_UHH_VBASE (OMAP44XX_L4_CORE_VBASE + OMAP44XX_USB_UHH_OFFSET)
+#define OMAP44XX_USB_UHH_SIZE 0x00000700UL
+
+/*
+ * USB OHCI Module
+ */
+#define OMAP44XX_USB_OHCI_HWBASE (OMAP44XX_L4_CORE_HWBASE + OMAP44XX_USB_OHCI_OFFSET)
+#define OMAP44XX_USB_OHCI_VBASE (OMAP44XX_L4_CORE_VBASE + OMAP44XX_USB_OHCI_OFFSET)
+#define OMAP44XX_USB_OHCI_SIZE 0x00000400UL
+
+/*
+ * USB EHCI Module
+ */
+#define OMAP44XX_USB_EHCI_HWBASE (OMAP44XX_L4_CORE_HWBASE + OMAP44XX_USB_EHCI_OFFSET)
+#define OMAP44XX_USB_EHCI_VBASE (OMAP44XX_L4_CORE_VBASE + OMAP44XX_USB_EHCI_OFFSET)
+#define OMAP44XX_USB_EHCI_SIZE 0x0000400UL
+
+/*
+ * SDMA Offset
+ * PA 0x4805 6000
+ */
+
+#define OMAP44XX_SDMA_HWBASE (OMAP44XX_L4_CORE_HWBASE + OMAP44XX_SDMA_OFFSET)
+#define OMAP44XX_SDMA_VBASE (OMAP44XX_L4_CORE_VBASE + OMAP44XX_SDMA_OFFSET)
+#define OMAP44XX_SDMA_SIZE 0x00001000UL
+
+/*
+ * Interrupt Controller Unit.
+ *
+ * Refer to the omap4_intr.c file for interrupt controller (GIC)
+ * implementation.
+ *
+ * Note:
+ * - 16 Interprocessor interrupts (IPI): ID[15:0]
+ * - 2 private Timer/Watchdog interrupts: ID[30:29]
+ * - 2 legacy nFIQ & nIRQ: one per CPU, bypasses the interrupt distributor
+ * logic and directly drives interrupt requests into CPU if used in
+ * legacy mode (else treated like other interrupts lines with ID28
+ * and ID31 respectively)
+ * - 128 hardware interrupts: ID[159:32] (rising-edge or high-level sensitive).
+ */
+#define OMAP44XX_HARDIRQ(x) (32 + (x))
+
+#define OMAP44XX_IRQ_L2CACHE OMAP44XX_HARDIRQ(0) /* L2 cache controller interrupt */
+#define OMAP44XX_IRQ_CTI_0 OMAP44XX_HARDIRQ(1) /* Cross-trigger module 0 (CTI0) interrupt */
+#define OMAP44XX_IRQ_CTI_1 OMAP44XX_HARDIRQ(2) /* Cross-trigger module 1 (CTI1) interrupt */
+#define OMAP44XX_IRQ_RESERVED3 OMAP44XX_HARDIRQ(3) /* RESERVED */
+#define OMAP44XX_IRQ_ELM OMAP44XX_HARDIRQ(4) /* Error location process completion */
+#define OMAP44XX_IRQ_RESERVED5 OMAP44XX_HARDIRQ(5) /* RESERVED */
+#define OMAP44XX_IRQ_RESERVED6 OMAP44XX_HARDIRQ(6) /* RESERVED */
+#define OMAP44XX_IRQ_SYS_NIRQ OMAP44XX_HARDIRQ(7) /* External source (active low) */
+#define OMAP44XX_IRQ_RESERVED8 OMAP44XX_HARDIRQ(8) /* RESERVED */
+#define OMAP44XX_IRQ_L3_DBG OMAP44XX_HARDIRQ(9) /* L3 interconnect debug error */
+#define OMAP44XX_IRQ_L3_APP OMAP44XX_HARDIRQ(10) /* L3 interconnect application error */
+#define OMAP44XX_IRQ_PRCM_MPU OMAP44XX_HARDIRQ(11) /* PRCM module IRQ */
+#define OMAP44XX_IRQ_SDMA0 OMAP44XX_HARDIRQ(12) /* System DMA request 0(3) */
+#define OMAP44XX_IRQ_SDMA1 OMAP44XX_HARDIRQ(13) /* System DMA request 1(3) */
+#define OMAP44XX_IRQ_SDMA2 OMAP44XX_HARDIRQ(14) /* System DMA request 2 */
+#define OMAP44XX_IRQ_SDMA3 OMAP44XX_HARDIRQ(15) /* System DMA request 3 */
+#define OMAP44XX_IRQ_MCBSP4 OMAP44XX_HARDIRQ(16) /* McBSP module 4 IRQ */
+#define OMAP44XX_IRQ_MCBSP1 OMAP44XX_HARDIRQ(17) /* McBSP module 1 IRQ */
+#define OMAP44XX_IRQ_SR1 OMAP44XX_HARDIRQ(18) /* SmartReflexâ„¢ 1 */
+#define OMAP44XX_IRQ_SR2 OMAP44XX_HARDIRQ(19) /* SmartReflexâ„¢ 2 */
+#define OMAP44XX_IRQ_GPMC OMAP44XX_HARDIRQ(20) /* General-purpose memory controller module */
+#define OMAP44XX_IRQ_SGX OMAP44XX_HARDIRQ(21) /* 2D/3D graphics module */
+#define OMAP44XX_IRQ_MCBSP2 OMAP44XX_HARDIRQ(22) /* McBSP module 2 */
+#define OMAP44XX_IRQ_MCBSP3 OMAP44XX_HARDIRQ(23) /* McBSP module 3 */
+#define OMAP44XX_IRQ_ISS5 OMAP44XX_HARDIRQ(24) /* Imaging subsystem interrupt 5 */
+#define OMAP44XX_IRQ_DSS OMAP44XX_HARDIRQ(25) /* Display subsystem module(3) */
+#define OMAP44XX_IRQ_MAIL_U0 OMAP44XX_HARDIRQ(26) /* Mailbox user 0 request */
+#define OMAP44XX_IRQ_C2C_SSCM OMAP44XX_HARDIRQ(27) /* C2C status interrupt */
+#define OMAP44XX_IRQ_DSP_MMU OMAP44XX_HARDIRQ(28) /* DSP MMU */
+#define OMAP44XX_IRQ_GPIO1_MPU OMAP44XX_HARDIRQ(29) /* GPIO module 1(3) */
+#define OMAP44XX_IRQ_GPIO2_MPU OMAP44XX_HARDIRQ(30) /* GPIO module 2(3) */
+#define OMAP44XX_IRQ_GPIO3_MPU OMAP44XX_HARDIRQ(31) /* GPIO module 3(3) */
+#define OMAP44XX_IRQ_GPIO4_MPU OMAP44XX_HARDIRQ(32) /* GPIO module 4(3) */
+#define OMAP44XX_IRQ_GPIO5_MPU OMAP44XX_HARDIRQ(33) /* GPIO module 5(3) */
+#define OMAP44XX_IRQ_GPIO6_MPU OMAP44XX_HARDIRQ(34) /* GPIO module 6(3) */
+#define OMAP44XX_IRQ_RESERVED35 OMAP44XX_HARDIRQ(35) /* RESERVED */
+#define OMAP44XX_IRQ_WDT3 OMAP44XX_HARDIRQ(36) /* Watchdog timer module 3 overflow */
+#define OMAP44XX_IRQ_GPT1 OMAP44XX_HARDIRQ(37) /* General-purpose timer module 1 */
+#define OMAP44XX_IRQ_GPT2 OMAP44XX_HARDIRQ(38) /* General-purpose timer module 2 */
+#define OMAP44XX_IRQ_GPT3 OMAP44XX_HARDIRQ(39) /* General-purpose timer module 3 */
+#define OMAP44XX_IRQ_GPT4 OMAP44XX_HARDIRQ(40) /* General-purpose timer module 4 */
+#define OMAP44XX_IRQ_GPT5 OMAP44XX_HARDIRQ(41) /* General-purpose timer module 5 */
+#define OMAP44XX_IRQ_GPT6 OMAP44XX_HARDIRQ(42) /* General-purpose timer module 6 */
+#define OMAP44XX_IRQ_GPT7 OMAP44XX_HARDIRQ(43) /* General-purpose timer module 7 */
+#define OMAP44XX_IRQ_GPT8 OMAP44XX_HARDIRQ(44) /* General-purpose timer module 8 */
+#define OMAP44XX_IRQ_GPT9 OMAP44XX_HARDIRQ(45) /* General-purpose timer module 9 */
+#define OMAP44XX_IRQ_GPT10 OMAP44XX_HARDIRQ(46) /* General-purpose timer module 10 */
+#define OMAP44XX_IRQ_GPT11 OMAP44XX_HARDIRQ(47) /* General-purpose timer module 11 */
+#define OMAP44XX_IRQ_MCSPI4 OMAP44XX_HARDIRQ(48) /* McSPI module 4 */
+#define OMAP44XX_IRQ_RESERVED49 OMAP44XX_HARDIRQ(49) /* RESERVED */
+#define OMAP44XX_IRQ_RESERVED50 OMAP44XX_HARDIRQ(50) /* RESERVED */
+#define OMAP44XX_IRQ_RESERVED51 OMAP44XX_HARDIRQ(51) /* RESERVED */
+#define OMAP44XX_IRQ_RESERVED52 OMAP44XX_HARDIRQ(52) /* RESERVED */
+#define OMAP44XX_IRQ_DSS_DSI1 OMAP44XX_HARDIRQ(53) /* Display Subsystem DSI1 interrupt */
+#define OMAP44XX_IRQ_RESERVED54 OMAP44XX_HARDIRQ(54) /* RESERVED */
+#define OMAP44XX_IRQ_RESERVED55 OMAP44XX_HARDIRQ(55) /* RESERVED */
+#define OMAP44XX_IRQ_I2C1 OMAP44XX_HARDIRQ(56) /* I2C module 1 */
+#define OMAP44XX_IRQ_I2C2 OMAP44XX_HARDIRQ(57) /* I2C module 2 */
+#define OMAP44XX_IRQ_HDQ OMAP44XX_HARDIRQ(58) /* HDQ / One-wire */
+#define OMAP44XX_IRQ_MMC5 OMAP44XX_HARDIRQ(59) /* MMC5 interrupt */
+#define OMAP44XX_IRQ_RESERVED60 OMAP44XX_HARDIRQ(60) /* RESERVED */
+#define OMAP44XX_IRQ_I2C3 OMAP44XX_HARDIRQ(61) /* I2C module 3 */
+#define OMAP44XX_IRQ_I2C4 OMAP44XX_HARDIRQ(62) /* I2C module 4 */
+#define OMAP44XX_IRQ_RESERVED63 OMAP44XX_HARDIRQ(63) /* RESERVED */
+#define OMAP44XX_IRQ_RESERVED64 OMAP44XX_HARDIRQ(64) /* RESERVED */
+#define OMAP44XX_IRQ_MCSPI1 OMAP44XX_HARDIRQ(65) /* McSPI module 1 */
+#define OMAP44XX_IRQ_MCSPI2 OMAP44XX_HARDIRQ(66) /* McSPI module 2 */
+#define OMAP44XX_IRQ_HSI_P1 OMAP44XX_HARDIRQ(67) /* HSI Port 1 interrupt */
+#define OMAP44XX_IRQ_HSI_P2 OMAP44XX_HARDIRQ(68) /* HSI Port 2 interrupt */
+#define OMAP44XX_IRQ_FDIF_3 OMAP44XX_HARDIRQ(69) /* Face detect interrupt 3 */
+#define OMAP44XX_IRQ_UART4 OMAP44XX_HARDIRQ(70) /* UART module 4 interrupt */
+#define OMAP44XX_IRQ_HSI_DMA OMAP44XX_HARDIRQ(71) /* HSI DMA engine MPU request */
+#define OMAP44XX_IRQ_UART1 OMAP44XX_HARDIRQ(72) /* UART module 1 */
+#define OMAP44XX_IRQ_UART2 OMAP44XX_HARDIRQ(73) /* UART module 2 */
+#define OMAP44XX_IRQ_UART3 OMAP44XX_HARDIRQ(74) /* UART module 3 (also infrared)(3) */
+#define OMAP44XX_IRQ_PBIAS OMAP44XX_HARDIRQ(75) /* Merged interrupt for PBIASlite1 and 2 */
+#define OMAP44XX_IRQ_OHCI OMAP44XX_HARDIRQ(76) /* OHCI controller HSUSB MP Host Interrupt */
+#define OMAP44XX_IRQ_EHCI OMAP44XX_HARDIRQ(77) /* EHCI controller HSUSB MP Host Interrupt */
+#define OMAP44XX_IRQ_TLL OMAP44XX_HARDIRQ(78) /* HSUSB MP TLL Interrupt */
+#define OMAP44XX_IRQ_RESERVED79 OMAP44XX_HARDIRQ(79) /* RESERVED */
+#define OMAP44XX_IRQ_WDT2 OMAP44XX_HARDIRQ(80) /* WDTIMER2 interrupt */
+#define OMAP44XX_IRQ_RESERVED81 OMAP44XX_HARDIRQ(81) /* RESERVED */
+#define OMAP44XX_IRQ_RESERVED82 OMAP44XX_HARDIRQ(82) /* RESERVED */
+#define OMAP44XX_IRQ_MMC1 OMAP44XX_HARDIRQ(83) /* MMC/SD module 1 */
+#define OMAP44XX_IRQ_DSS_DSI2 OMAP44XX_HARDIRQ(84) /* Display subsystem DSI2 interrupt */
+#define OMAP44XX_IRQ_RESERVED85 OMAP44XX_HARDIRQ(85) /* Reserved */
+#define OMAP44XX_IRQ_MMC2 OMAP44XX_HARDIRQ(86) /* MMC/SD module 2 */
+#define OMAP44XX_IRQ_MPU_ICR OMAP44XX_HARDIRQ(87) /* MPU ICR */
+#define OMAP44XX_IRQ_C2C_GPI OMAP44XX_HARDIRQ(88) /* C2C GPI interrupt */
+#define OMAP44XX_IRQ_FSUSB OMAP44XX_HARDIRQ(89) /* FS-USB - host controller Interrupt */
+#define OMAP44XX_IRQ_FSUSB_SMI OMAP44XX_HARDIRQ(90) /* FS-USB - host controller SMI Interrupt */
+#define OMAP44XX_IRQ_MCSPI3 OMAP44XX_HARDIRQ(91) /* McSPI module 3 */
+#define OMAP44XX_IRQ_HSUSB_OTG OMAP44XX_HARDIRQ(92) /* High-Speed USB OTG controller */
+#define OMAP44XX_IRQ_HSUSB_OTG_DMA OMAP44XX_HARDIRQ(93) /* High-Speed USB OTG DMA controller */
+#define OMAP44XX_IRQ_MMC3 OMAP44XX_HARDIRQ(94) /* MMC/SD module 3 */
+#define OMAP44XX_IRQ_RESERVED95 OMAP44XX_HARDIRQ(95) /* RESERVED */
+#define OMAP44XX_IRQ_MMC4 OMAP44XX_HARDIRQ(96) /* MMC4 interrupt */
+#define OMAP44XX_IRQ_SLIMBUS1 OMAP44XX_HARDIRQ(97) /* SLIMBUS1 interrupt */
+#define OMAP44XX_IRQ_SLIMBUS2 OMAP44XX_HARDIRQ(98) /* SLIMBUS2 interrupt */
+#define OMAP44XX_IRQ_ABE OMAP44XX_HARDIRQ(99) /* Audio back-end interrupt */
+#define OMAP44XX_IRQ_CORTEXM3_MMU OMAP44XX_HARDIRQ(100) /* Cortex-M3 MMU interrupt */
+#define OMAP44XX_IRQ_DSS_HDMI OMAP44XX_HARDIRQ(101) /* Display subsystem HDMI interrupt */
+#define OMAP44XX_IRQ_SR_IVA OMAP44XX_HARDIRQ(102) /* SmartReflex IVA interrupt */
+#define OMAP44XX_IRQ_IVAHD1 OMAP44XX_HARDIRQ(103) /* Sync interrupt from iCONT2 (vDMA) */
+#define OMAP44XX_IRQ_IVAHD2 OMAP44XX_HARDIRQ(104) /* Sync interrupt from iCONT1 */
+#define OMAP44XX_IRQ_RESERVED105 OMAP44XX_HARDIRQ(105) /* RESERVED */
+#define OMAP44XX_IRQ_RESERVED106 OMAP44XX_HARDIRQ(106) /* RESERVED */
+#define OMAP44XX_IRQ_IVAHD_MAILBOX0 OMAP44XX_HARDIRQ(107) /* IVAHD mailbox interrupt */
+#define OMAP44XX_IRQ_RESERVED108 OMAP44XX_HARDIRQ(108) /* RESERVED */
+#define OMAP44XX_IRQ_MCASP1 OMAP44XX_HARDIRQ(109) /* McASP1 transmit interrupt */
+#define OMAP44XX_IRQ_EMIF1 OMAP44XX_HARDIRQ(110) /* EMIF1 interrupt */
+#define OMAP44XX_IRQ_EMIF2 OMAP44XX_HARDIRQ(111) /* EMIF2 interrupt */
+#define OMAP44XX_IRQ_MCPDM OMAP44XX_HARDIRQ(112) /* MCPDM interrupt */
+#define OMAP44XX_IRQ_DMM OMAP44XX_HARDIRQ(113) /* DMM interrupt */
+#define OMAP44XX_IRQ_DMIC OMAP44XX_HARDIRQ(114) /* DMIC interrupt */
+#define OMAP44XX_IRQ_RESERVED115 OMAP44XX_HARDIRQ(115) /* RESERVED */
+#define OMAP44XX_IRQ_RESERVED116 OMAP44XX_HARDIRQ(116) /* RESERVED */
+#define OMAP44XX_IRQ_RESERVED117 OMAP44XX_HARDIRQ(117) /* RESERVED */
+#define OMAP44XX_IRQ_RESERVED118 OMAP44XX_HARDIRQ(118) /* RESERVED */
+#define OMAP44XX_IRQ_SYS_NIRQ2 OMAP44XX_HARDIRQ(119) /* External source 2 (active low) */
+#define OMAP44XX_IRQ_KBD OMAP44XX_HARDIRQ(120) /* Keyboard controller interrupt */
+#define OMAP44XX_IRQ_RESERVED121 OMAP44XX_HARDIRQ(121) /* RESERVED */
+#define OMAP44XX_IRQ_RESERVED122 OMAP44XX_HARDIRQ(122) /* RESERVED */
+#define OMAP44XX_IRQ_RESERVED123 OMAP44XX_HARDIRQ(123) /* RESERVED */
+#define OMAP44XX_IRQ_RESERVED124 OMAP44XX_HARDIRQ(124) /* RESERVED */
+#define OMAP44XX_IRQ_RESERVED125 OMAP44XX_HARDIRQ(125) /* RESERVED */
+#define OMAP44XX_IRQ_RESERVED126 OMAP44XX_HARDIRQ(126) /* RESERVED */
+#define OMAP44XX_IRQ_RESERVED127 OMAP44XX_HARDIRQ(127) /* RESERVED */
+
+/*
+ * General Purpose Timers
+ */
+#define OMAP44XX_GPTIMER1_VBASE (OMAP44XX_L4_WAKEUP_VBASE + OMAP44XX_GPTIMER1_OFFSET)
+#define OMAP44XX_GPTIMER1_HWBASE (OMAP44XX_L4_WAKEUP_HWBASE + OMAP44XX_GPTIMER1_OFFSET)
+#define OMAP44XX_GPTIMER2_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_GPTIMER2_OFFSET)
+#define OMAP44XX_GPTIMER2_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_GPTIMER2_OFFSET)
+#define OMAP44XX_GPTIMER3_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_GPTIMER3_OFFSET)
+#define OMAP44XX_GPTIMER3_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_GPTIMER3_OFFSET)
+#define OMAP44XX_GPTIMER4_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_GPTIMER4_OFFSET)
+#define OMAP44XX_GPTIMER4_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_GPTIMER4_OFFSET)
+#define OMAP44XX_GPTIMER5_VBASE (OMAP44XX_L4_ABE_VBASE + OMAP44XX_GPTIMER5_OFFSET)
+#define OMAP44XX_GPTIMER5_HWBASE (OMAP44XX_L4_ABE_HWBASE + OMAP44XX_GPTIMER5_OFFSET)
+#define OMAP44XX_GPTIMER6_VBASE (OMAP44XX_L4_ABE_VBASE + OMAP44XX_GPTIMER6_OFFSET)
+#define OMAP44XX_GPTIMER6_HWBASE (OMAP44XX_L4_ABE_HWBASE + OMAP44XX_GPTIMER6_OFFSET)
+#define OMAP44XX_GPTIMER7_VBASE (OMAP44XX_L4_ABE_VBASE + OMAP44XX_GPTIMER7_OFFSET)
+#define OMAP44XX_GPTIMER7_HWBASE (OMAP44XX_L4_ABE_HWBASE + OMAP44XX_GPTIMER7_OFFSET)
+#define OMAP44XX_GPTIMER8_VBASE (OMAP44XX_L4_ABE_VBASE + OMAP44XX_GPTIMER8_OFFSET)
+#define OMAP44XX_GPTIMER8_HWBASE (OMAP44XX_L4_ABE_HWBASE + OMAP44XX_GPTIMER8_OFFSET)
+#define OMAP44XX_GPTIMER9_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_GPTIMER9_OFFSET)
+#define OMAP44XX_GPTIMER9_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_GPTIMER9_OFFSET)
+#define OMAP44XX_GPTIMER10_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_GPTIMER10_OFFSET)
+#define OMAP44XX_GPTIMER10_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_GPTIMER10_OFFSET)
+#define OMAP44XX_GPTIMER11_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_GPTIMER11_OFFSET)
+#define OMAP44XX_GPTIMER11_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_GPTIMER11_OFFSET)
+#define OMAP44XX_GPTIMER_SIZE 0x00001000UL
+
+/*
+ * GPIO - General Purpose IO
+ */
+
+/* Base addresses for the GPIO modules */
+#define OMAP44XX_GPIO1_HWBASE (OMAP44XX_L4_WAKEUP_HWBASE + OMAP44XX_GPIO1_OFFSET)
+#define OMAP44XX_GPIO1_VBASE (OMAP44XX_L4_WAKEUP_VBASE + OMAP44XX_GPIO1_OFFSET)
+#define OMAP44XX_GPIO1_SIZE 0x00001000UL
+#define OMAP44XX_GPIO2_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_GPIO2_OFFSET)
+#define OMAP44XX_GPIO2_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_GPIO2_OFFSET)
+#define OMAP44XX_GPIO2_SIZE 0x00001000UL
+#define OMAP44XX_GPIO3_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_GPIO3_OFFSET)
+#define OMAP44XX_GPIO3_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_GPIO3_OFFSET)
+#define OMAP44XX_GPIO3_SIZE 0x00001000UL
+#define OMAP44XX_GPIO4_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_GPIO4_OFFSET)
+#define OMAP44XX_GPIO4_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_GPIO4_OFFSET)
+#define OMAP44XX_GPIO4_SIZE 0x00001000UL
+#define OMAP44XX_GPIO5_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_GPIO5_OFFSET)
+#define OMAP44XX_GPIO5_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_GPIO5_OFFSET)
+#define OMAP44XX_GPIO5_SIZE 0x00001000UL
+#define OMAP44XX_GPIO6_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_GPIO6_OFFSET)
+#define OMAP44XX_GPIO6_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_GPIO6_OFFSET)
+#define OMAP44XX_GPIO6_SIZE 0x00001000UL
+
+/*
+ * MMC/SD/SDIO
+ */
+
+/* Base addresses for the MMC/SD/SDIO modules */
+#define OMAP44XX_MMCHS1_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_MMCHS1_OFFSET)
+#define OMAP44XX_MMCHS1_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_MMCHS1_OFFSET)
+#define OMAP44XX_MMCHS2_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_MMCHS2_OFFSET)
+#define OMAP44XX_MMCHS2_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_MMCHS2_OFFSET)
+#define OMAP44XX_MMCHS3_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_MMCSD3_OFFSET)
+#define OMAP44XX_MMCHS3_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_MMCSD3_OFFSET)
+#define OMAP44XX_MMCHS4_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_MMCSD4_OFFSET)
+#define OMAP44XX_MMCHS4_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_MMCSD4_OFFSET)
+#define OMAP44XX_MMCHS5_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_MMCSD5_OFFSET)
+#define OMAP44XX_MMCHS5_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_MMCSD5_OFFSET)
+#define OMAP44XX_MMCHS_SIZE 0x00001000UL
+
+/*
+ * SCM - System Control Module
+ */
+
+/* Base addresses for the SC modules */
+#define OMAP44XX_SCM_PADCONF_HWBASE (OMAP44XX_L4_CORE_HWBASE + OMAP44XX_SCM_PADCONF_OFFSET)
+#define OMAP44XX_SCM_PADCONF_VBASE (OMAP44XX_L4_CORE_VBASE + OMAP44XX_SCM_PADCONF_OFFSET)
+#define OMAP44XX_SCM_PADCONF_SIZE 0x00001000UL
+
+#endif /* _OMAP44XX_REG_H_ */
diff --git a/sys/arm/ti/omap4/omap4_scm_padconf.c b/sys/arm/ti/omap4/omap4_scm_padconf.c
new file mode 100644
index 000000000000..4ae9aa152cc9
--- /dev/null
+++ b/sys/arm/ti/omap4/omap4_scm_padconf.c
@@ -0,0 +1,304 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 2011
+ * Ben Gray <ben.r.gray@gmail.com>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BEN GRAY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+
+#include <machine/bus.h>
+
+#include <arm/ti/ti_pinmux.h>
+#include <arm/ti/omap4/omap4_scm_padconf.h>
+
+/*
+ * This file defines the pin mux configuration for the OMAP4xxx series of
+ * devices.
+ *
+ * How This is Suppose to Work
+ * ===========================
+ * - There is a top level ti_scm module (System Control Module) that is
+ * the interface for all omap drivers, which can use it to change the mux
+ * settings for individual pins. (That said, typically the pin mux settings
+ * are set to defaults by the 'hints' and then not altered by the driver).
+ *
+ * - For this to work the top level driver needs all the pin info, and hence
+ * this is where this file comes in. Here we define all the pin information
+ * that is supplied to the top level driver.
+ *
+ */
+
+#define _PINDEF(r, b, gp, gm, m0, m1, m2, m3, m4, m5, m6, m7) \
+ { .reg_off = r, \
+ .gpio_pin = gp, \
+ .gpio_mode = gm, \
+ .ballname = b, \
+ .muxmodes[0] = m0, \
+ .muxmodes[1] = m1, \
+ .muxmodes[2] = m2, \
+ .muxmodes[3] = m3, \
+ .muxmodes[4] = m4, \
+ .muxmodes[5] = m5, \
+ .muxmodes[6] = m6, \
+ .muxmodes[7] = m7, \
+ }
+
+const static struct ti_pinmux_padstate ti_padstate_devmap[] = {
+ {"output", PADCONF_PIN_OUTPUT},
+ {"input", PADCONF_PIN_INPUT},
+ {"input_pullup", PADCONF_PIN_INPUT_PULLUP},
+ {"input_pulldown", PADCONF_PIN_INPUT_PULLDOWN},
+ { .state = NULL }
+};
+
+/*
+ * Table 18-10, p. 3470
+ */
+const static struct ti_pinmux_padconf ti_padconf_devmap[] = {
+ _PINDEF(0x0000, "c12", 0, 0, "gpmc_ad0", "sdmmc2_dat0", NULL, NULL, NULL, NULL, NULL, NULL),
+ _PINDEF(0x0002, "d12", 0, 0, "gpmc_ad1", "sdmmc2_dat1", NULL, NULL, NULL, NULL, NULL, NULL),
+ _PINDEF(0x0004, "c13", 0, 0, "gpmc_ad2", "sdmmc2_dat2", NULL, NULL, NULL, NULL, NULL, NULL),
+ _PINDEF(0x0006, "d13", 0, 0, "gpmc_ad3", "sdmmc2_dat3", NULL, NULL, NULL, NULL, NULL, NULL),
+ _PINDEF(0x0008, "c15", 0, 0, "gpmc_ad4", "sdmmc2_dat4", "sdmmc2_dir_dat0", NULL, NULL, NULL, NULL, NULL),
+ _PINDEF(0x000a, "d15", 0, 0, "gpmc_ad5", "sdmmc2_dat5", "sdmmc2_dir_dat1", NULL, NULL, NULL, NULL, NULL),
+ _PINDEF(0x000c, "a16", 0, 0, "gpmc_ad6", "sdmmc2_dat6", "sdmmc2_dir_cmd", NULL, NULL, NULL, NULL, NULL),
+ _PINDEF(0x000e, "b16", 0, 0, "gpmc_ad7", "sdmmc2_dat7", "sdmmc2_clk_fdbk", NULL, NULL, NULL, NULL, NULL),
+ _PINDEF(0x0010, "c16", 32, 3, "gpmc_ad8", "kpd_row0", "c2c_data15", "gpio_32", NULL, "sdmmc1_dat0", NULL, NULL),
+ _PINDEF(0x0012, "d16", 33, 3, "gpmc_ad9", "kpd_row1", "c2c_data14", "gpio_33", NULL, "sdmmc1_dat1", NULL, NULL),
+ _PINDEF(0x0014, "c17", 34, 3, "gpmc_ad10", "kpd_row2", "c2c_data13", "gpio_34", NULL, "sdmmc1_dat2", NULL, NULL),
+ _PINDEF(0x0016, "d17", 35, 3, "gpmc_ad11", "kpd_row3", "c2c_data12", "gpio_35", NULL, "sdmmc1_dat3", NULL, NULL),
+ _PINDEF(0x0018, "c18", 36, 3, "gpmc_ad12", "kpd_col0", "c2c_data11", "gpio_36", NULL, "sdmmc1_dat4", NULL, NULL),
+ _PINDEF(0x001a, "d18", 37, 3, "gpmc_ad13", "kpd_col1", "c2c_data10", "gpio_37", NULL, "sdmmc1_dat5", NULL, NULL),
+ _PINDEF(0x001c, "c19", 38, 3, "gpmc_ad14", "kpd_col2", "c2c_data9", "gpio_38", NULL, "sdmmc1_dat6", NULL, NULL),
+ _PINDEF(0x001e, "d19", 39, 3, "gpmc_ad15", "kpd_col3", "c2c_data8", "gpio_39", NULL, "sdmmc1_dat7", NULL, NULL),
+ _PINDEF(0x0020, "b17", 40, 3, "gpmc_a16", "kpd_row4", "c2c_datain0", "gpio_40", "venc_656_data0", NULL, NULL, "safe_mode"),
+ _PINDEF(0x0022, "a18", 41, 3, "gpmc_a17", "kpd_row5", "c2c_datain1", "gpio_41", "venc_656_data1", NULL, NULL, "safe_mode"),
+ _PINDEF(0x0024, "b18", 42, 3, "gpmc_a18", "kpd_row6", "c2c_datain2", "gpio_42", "venc_656_data2", NULL, NULL, "safe_mode"),
+ _PINDEF(0x0026, "a19", 43, 3, "gpmc_a19", "kpd_row7", "c2c_datain3", "gpio_43", "venc_656_data3", NULL, NULL, "safe_mode"),
+ _PINDEF(0x0028, "b19", 44, 3, "gpmc_a20", "kpd_col4", "c2c_datain4", "gpio_44", "venc_656_data4", NULL, NULL, "safe_mode"),
+ _PINDEF(0x002a, "b20", 45, 3, "gpmc_a21", "kpd_col5", "c2c_datain5", "gpio_45", "venc_656_data5", NULL, NULL, "safe_mode"),
+ _PINDEF(0x002c, "a21", 46, 3, "gpmc_a22", "kpd_col6", "c2c_datain6", "gpio_46", "venc_656_data6", NULL, NULL, "safe_mode"),
+ _PINDEF(0x002e, "b21", 47, 3, "gpmc_a23", "kpd_col7", "c2c_datain7", "gpio_47", "venc_656_data7", NULL, NULL, "safe_mode"),
+ _PINDEF(0x0030, "c20", 48, 3, "gpmc_a24", "kpd_col8", "c2c_clkout0", "gpio_48", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0032, "d20", 49, 3, "gpmc_a25", NULL, "c2c_clkout1", "gpio_49", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0034, "b25", 50, 3, "gpmc_ncs0", NULL, NULL, "gpio_50", "sys_ndmareq0", NULL, NULL, NULL),
+ _PINDEF(0x0036, "c21", 51, 3, "gpmc_ncs1", NULL, "c2c_dataout6", "gpio_51", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0038, "d21", 52, 3, "gpmc_ncs2", "kpd_row8", "c2c_dataout7", "gpio_52", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x003a, "c22", 53, 3, "gpmc_ncs3", "gpmc_dir", "c2c_dataout4", "gpio_53", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x003c, "c25", 54, 3, "gpmc_nwp", "dsi1_te0", NULL, "gpio_54", "sys_ndmareq1", NULL, NULL, NULL),
+ _PINDEF(0x003e, "b22", 55, 3, "gpmc_clk", NULL, NULL, "gpio_55", "sys_ndmareq2", "sdmmc1_cmd", NULL, NULL),
+ _PINDEF(0x0040, "d25", 56, 3, "gpmc_nadv_ale", "dsi1_te1", NULL, "gpio_56", "sys_ndmareq3", "sdmmc1_clk", NULL, NULL),
+ _PINDEF(0x0042, "b11", 0, 0, "gpmc_noe", "sdmmc2_clk", NULL, NULL, NULL, NULL, NULL, NULL),
+ _PINDEF(0x0044, "b12", 0, 0, "gpmc_nwe", "sdmmc2_cmd", NULL, NULL, NULL, NULL, NULL, NULL),
+ _PINDEF(0x0046, "c23", 59, 3, "gpmc_nbe0_cle", "dsi2_te0", NULL, "gpio_59", NULL, NULL, NULL, NULL),
+ _PINDEF(0x0048, "d22", 60, 3, "gpmc_nbe1", NULL, "c2c_dataout5", "gpio_60", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x004a, "b26", 61, 3, "gpmc_wait0", "dsi2_te1", NULL, "gpio_61", NULL, NULL, NULL, NULL),
+ _PINDEF(0x004c, "b23", 62, 3, "gpmc_wait1", NULL, "c2c_dataout2", "gpio_62", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x004e, "d23", 100, 3, "gpmc_wait2", "usbc1_icusb_txen", "c2c_dataout3", "gpio_100", "sys_ndmareq0", NULL, NULL, "safe_mode"),
+ _PINDEF(0x0050, "a24", 101, 3, "gpmc_ncs4", "dsi1_te0", "c2c_clkin0", "gpio_101", "sys_ndmareq1", NULL, NULL, "safe_mode"),
+ _PINDEF(0x0052, "b24", 102, 3, "gpmc_ncs5", "dsi1_te1", "c2c_clkin1", "gpio_102", "sys_ndmareq2", NULL, NULL, "safe_mode"),
+ _PINDEF(0x0054, "c24", 103, 3, "gpmc_ncs6", "dsi2_te0", "c2c_dataout0", "gpio_103", "sys_ndmareq3", NULL, NULL, "safe_mode"),
+ _PINDEF(0x0056, "d24", 104, 3, "gpmc_ncs7", "dsi2_te1", "c2c_dataout1", "gpio_104", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0058, "b9", 63, 3, "hdmi_hpd", NULL, NULL, "gpio_63", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x005a, "b10", 64, 3, "hdmi_cec", NULL, NULL, "gpio_64", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x005c, "a8", 65, 3, "hdmi_ddc_scl", NULL, NULL, "gpio_65", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x005e, "b8", 66, 3, "hdmi_ddc_sda", NULL, NULL, "gpio_66", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0060, "r26", 0, 0, "csi21_dx0", NULL, NULL, "gpi_67", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0062, "r25", 0, 0, "csi21_dy0", NULL, NULL, "gpi_68", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0064, "t26", 0, 0, "csi21_dx1", NULL, NULL, "gpi_69", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0066, "t25", 0, 0, "csi21_dy1", NULL, NULL, "gpi_70", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0068, "u26", 0, 0, "csi21_dx2", NULL, NULL, "gpi_71", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x006a, "u25", 0, 0, "csi21_dy2", NULL, NULL, "gpi_72", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x006c, "v26", 0, 0, "csi21_dx3", NULL, NULL, "gpi_73", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x006e, "v25", 0, 0, "csi21_dy3", NULL, NULL, "gpi_74", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0070, "w26", 0, 0, "csi21_dx4", NULL, NULL, "gpi_75", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0072, "w25", 0, 0, "csi21_dy4", NULL, NULL, "gpi_76", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0074, "m26", 0, 0, "csi22_dx0", NULL, NULL, "gpi_77", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0076, "m25", 0, 0, "csi22_dy0", NULL, NULL, "gpi_78", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0078, "n26", 0, 0, "csi22_dx1", NULL, NULL, "gpi_79", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x007a, "n25", 0, 0, "csi22_dy1", NULL, NULL, "gpi_80", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x007c, "t27", 81, 3, "cam_shutter", NULL, NULL, "gpio_81", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x007e, "u27", 82, 3, "cam_strobe", NULL, NULL, "gpio_82", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0080, "v27", 83, 3, "cam_globalreset", NULL, NULL, "gpio_83", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0082, "ae18", 84, 3, "usbb1_ulpitll_clk", "hsi1_cawake", NULL, "gpio_84", "usbb1_ulpiphy_clk", NULL, "hw_dbg20", "safe_mode"),
+ _PINDEF(0x0084, "ag19", 85, 3, "usbb1_ulpitll_stp", "hsi1_cadata", "mcbsp4_clkr", "gpio_85", "usbb1_ulpiphy_stp", "usbb1_mm_rxdp", "hw_dbg21", "safe_mode"),
+ _PINDEF(0x0086, "af19", 86, 3, "usbb1_ulpitll_dir", "hsi1_caflag", "mcbsp4_fsr", "gpio_86", "usbb1_ulpiphy_dir", NULL, "hw_dbg22", "safe_mode"),
+ _PINDEF(0x0088, "ae19", 87, 3, "usbb1_ulpitll_nxt", "hsi1_acready", "mcbsp4_fsx", "gpio_87", "usbb1_ulpiphy_nxt", "usbb1_mm_rxdm", "hw_dbg23", "safe_mode"),
+ _PINDEF(0x008a, "af18", 88, 3, "usbb1_ulpitll_dat0", "hsi1_acwake", "mcbsp4_clkx", "gpio_88", "usbb1_ulpiphy_dat0", "usbb1_mm_txen", "hw_dbg24", "safe_mode"),
+ _PINDEF(0x008c, "ag18", 89, 3, "usbb1_ulpitll_dat1", "hsi1_acdata", "mcbsp4_dx", "gpio_89", "usbb1_ulpiphy_dat1", "usbb1_mm_txdat", "hw_dbg25", "safe_mode"),
+ _PINDEF(0x008e, "ae17", 90, 3, "usbb1_ulpitll_dat2", "hsi1_acflag", "mcbsp4_dr", "gpio_90", "usbb1_ulpiphy_dat2", "usbb1_mm_txse0", "hw_dbg26", "safe_mode"),
+ _PINDEF(0x0090, "af17", 91, 3, "usbb1_ulpitll_dat3", "hsi1_caready", NULL, "gpio_91", "usbb1_ulpiphy_dat3", "usbb1_mm_rxrcv", "hw_dbg27", "safe_mode"),
+ _PINDEF(0x0092, "ah17", 92, 3, "usbb1_ulpitll_dat4", "dmtimer8_pwm_evt", "abe_mcbsp3_dr", "gpio_92", "usbb1_ulpiphy_dat4", NULL, "hw_dbg28", "safe_mode"),
+ _PINDEF(0x0094, "ae16", 93, 3, "usbb1_ulpitll_dat5", "dmtimer9_pwm_evt", "abe_mcbsp3_dx", "gpio_93", "usbb1_ulpiphy_dat5", NULL, "hw_dbg29", "safe_mode"),
+ _PINDEF(0x0096, "af16", 94, 3, "usbb1_ulpitll_dat6", "dmtimer10_pwm_evt", "abe_mcbsp3_clkx", "gpio_94", "usbb1_ulpiphy_dat6", "abe_dmic_din3", "hw_dbg30", "safe_mode"),
+ _PINDEF(0x0098, "ag16", 95, 3, "usbb1_ulpitll_dat7", "dmtimer11_pwm_evt", "abe_mcbsp3_fsx", "gpio_95", "usbb1_ulpiphy_dat7", "abe_dmic_clk3", "hw_dbg31", "safe_mode"),
+ _PINDEF(0x009a, "af14", 96, 3, "usbb1_hsic_data", NULL, NULL, "gpio_96", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x009c, "ae14", 97, 3, "usbb1_hsic_strobe", NULL, NULL, "gpio_97", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x009e, "h2", 98, 3, "usbc1_icusb_dp", NULL, NULL, "gpio_98", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x00a0, "h3", 99, 3, "usbc1_icusb_dm", NULL, NULL, "gpio_99", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x00a2, "d2", 100, 3, "sdmmc1_clk", NULL, "dpm_emu19", "gpio_100", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x00a4, "e3", 101, 3, "sdmmc1_cmd", NULL, "uart1_rx", "gpio_101", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x00a6, "e4", 102, 3, "sdmmc1_dat0", NULL, "dpm_emu18", "gpio_102", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x00a8, "e2", 103, 3, "sdmmc1_dat1", NULL, "dpm_emu17", "gpio_103", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x00aa, "e1", 104, 3, "sdmmc1_dat2", NULL, "dpm_emu16", "gpio_104", "jtag_tms_tmsc", NULL, NULL, "safe_mode"),
+ _PINDEF(0x00ac, "f4", 105, 3, "sdmmc1_dat3", NULL, "dpm_emu15", "gpio_105", "jtag_tck", NULL, NULL, "safe_mode"),
+ _PINDEF(0x00ae, "f3", 106, 3, "sdmmc1_dat4", NULL, NULL, "gpio_106", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x00b0, "f1", 107, 3, "sdmmc1_dat5", NULL, NULL, "gpio_107", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x00b2, "g4", 108, 3, "sdmmc1_dat6", NULL, NULL, "gpio_108", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x00b4, "g3", 109, 3, "sdmmc1_dat7", NULL, NULL, "gpio_109", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x00b6, "ad27", 110, 3, "abe_mcbsp2_clkx", "mcspi2_clk", "abe_mcasp_ahclkx", "gpio_110", "usbb2_mm_rxdm", NULL, NULL, "safe_mode"),
+ _PINDEF(0x00b8, "ad26", 111, 3, "abe_mcbsp2_dr", "mcspi2_somi", "abe_mcasp_axr", "gpio_111", "usbb2_mm_rxdp", NULL, NULL, "safe_mode"),
+ _PINDEF(0x00ba, "ad25", 112, 3, "abe_mcbsp2_dx", "mcspi2_simo", "abe_mcasp_amute", "gpio_112", "usbb2_mm_rxrcv", NULL, NULL, "safe_mode"),
+ _PINDEF(0x00bc, "ac28", 113, 3, "abe_mcbsp2_fsx", "mcspi2_cs0", "abe_mcasp_afsx", "gpio_113", "usbb2_mm_txen", NULL, NULL, "safe_mode"),
+ _PINDEF(0x00be, "ac26", 114, 3, "abe_mcbsp1_clkx", "abe_slimbus1_clock", NULL, "gpio_114", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x00c0, "ac25", 115, 3, "abe_mcbsp1_dr", "abe_slimbus1_data", NULL, "gpio_115", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x00c2, "ab25", 116, 3, "abe_mcbsp1_dx", "sdmmc3_dat2", "abe_mcasp_aclkx", "gpio_116", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x00c4, "ac27", 117, 3, "abe_mcbsp1_fsx", "sdmmc3_dat3", "abe_mcasp_amutein", "gpio_117", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x00c6, "ag25", 0, 0, "abe_pdm_ul_data", "abe_mcbsp3_dr", NULL, NULL, NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x00c8, "af25", 0, 0, "abe_pdm_dl_data", "abe_mcbsp3_dx", NULL, NULL, NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x00ca, "ae25", 0, 0, "abe_pdm_frame", "abe_mcbsp3_clkx", NULL, NULL, NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x00cc, "af26", 0, 0, "abe_pdm_lb_clk", "abe_mcbsp3_fsx", NULL, NULL, NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x00ce, "ah26", 118, 3, "abe_clks", NULL, NULL, "gpio_118", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x00d0, "ae24", 119, 3, "abe_dmic_clk1", NULL, NULL, "gpio_119", "usbb2_mm_txse0", "uart4_cts", NULL, "safe_mode"),
+ _PINDEF(0x00d2, "af24", 120, 3, "abe_dmic_din1", NULL, NULL, "gpio_120", "usbb2_mm_txdat", "uart4_rts", NULL, "safe_mode"),
+ _PINDEF(0x00d4, "ag24", 121, 3, "abe_dmic_din2", "slimbus2_clock", "abe_mcasp_axr", "gpio_121", NULL, "dmtimer11_pwm_evt", NULL, "safe_mode"),
+ _PINDEF(0x00d6, "ah24", 122, 3, "abe_dmic_din3", "slimbus2_data", "abe_dmic_clk2", "gpio_122", NULL, "dmtimer9_pwm_evt", NULL, "safe_mode"),
+ _PINDEF(0x00d8, "ab26", 123, 3, "uart2_cts", "sdmmc3_clk", NULL, "gpio_123", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x00da, "ab27", 124, 3, "uart2_rts", "sdmmc3_cmd", NULL, "gpio_124", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x00dc, "aa25", 125, 3, "uart2_rx", "sdmmc3_dat0", NULL, "gpio_125", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x00de, "aa26", 126, 3, "uart2_tx", "sdmmc3_dat1", NULL, "gpio_126", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x00e0, "aa27", 127, 3, "hdq_sio", "i2c3_sccb", "i2c2_sccb", "gpio_127", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x00e2, "ae28", 0, 0, "i2c1_scl", NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PINDEF(0x00e4, "ae26", 0, 0, "i2c1_sda", NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PINDEF(0x00e6, "c26", 128, 3, "i2c2_scl", "uart1_rx", NULL, "gpio_128", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x00e8, "d26", 129, 3, "i2c2_sda", "uart1_tx", NULL, "gpio_129", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x00ea, "w27", 130, 3, "i2c3_scl", NULL, NULL, "gpio_130", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x00ec, "y27", 131, 3, "i2c3_sda", NULL, NULL, "gpio_131", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x00ee, "ag21", 132, 3, "i2c4_scl", NULL, NULL, "gpio_132", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x00f0, "ah22", 133, 3, "i2c4_sda", NULL, NULL, "gpio_133", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x00f2, "af22", 134, 3, "mcspi1_clk", NULL, NULL, "gpio_134", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x00f4, "ae22", 135, 3, "mcspi1_somi", NULL, NULL, "gpio_135", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x00f6, "ag22", 136, 3, "mcspi1_simo", NULL, NULL, "gpio_136", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x00f8, "ae23", 137, 3, "mcspi1_cs0", NULL, NULL, "gpio_137", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x00fa, "af23", 138, 3, "mcspi1_cs1", "uart1_rx", NULL, "gpio_138", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x00fc, "ag23", 139, 3, "mcspi1_cs2", "uart1_cts", "slimbus2_clock", "gpio_139", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x00fe, "ah23", 140, 3, "mcspi1_cs3", "uart1_rts", "slimbus2_data", "gpio_140", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0100, "f27", 141, 3, "uart3_cts_rctx", "uart1_tx", NULL, "gpio_141", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0102, "f28", 142, 3, "uart3_rts_sd", NULL, NULL, "gpio_142", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0104, "g27", 143, 3, "uart3_rx_irrx", "dmtimer8_pwm_evt", NULL, "gpio_143", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0106, "g28", 144, 3, "uart3_tx_irtx", "dmtimer9_pwm_evt", NULL, "gpio_144", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0108, "ae5", 145, 3, "sdmmc5_clk", "mcspi2_clk", "usbc1_icusb_dp", "gpio_145", NULL, "sdmmc2_clk", NULL, "safe_mode"),
+ _PINDEF(0x010a, "af5", 146, 3, "sdmmc5_cmd", "mcspi2_simo", "usbc1_icusb_dm", "gpio_146", NULL, "sdmmc2_cmd", NULL, "safe_mode"),
+ _PINDEF(0x010c, "ae4", 147, 3, "sdmmc5_dat0", "mcspi2_somi", "usbc1_icusb_rcv", "gpio_147", NULL, "sdmmc2_dat0", NULL, "safe_mode"),
+ _PINDEF(0x010e, "af4", 148, 3, "sdmmc5_dat1", NULL, "usbc1_icusb_txen", "gpio_148", NULL, "sdmmc2_dat1", NULL, "safe_mode"),
+ _PINDEF(0x0110, "ag3", 149, 3, "sdmmc5_dat2", "mcspi2_cs1", NULL, "gpio_149", NULL, "sdmmc2_dat2", NULL, "safe_mode"),
+ _PINDEF(0x0112, "af3", 150, 3, "sdmmc5_dat3", "mcspi2_cs0", NULL, "gpio_150", NULL, "sdmmc2_dat3", NULL, "safe_mode"),
+ _PINDEF(0x0114, "ae21", 151, 3, "mcspi4_clk", "sdmmc4_clk", "kpd_col6", "gpio_151", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0116, "af20", 152, 3, "mcspi4_simo", "sdmmc4_cmd", "kpd_col7", "gpio_152", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0118, "af21", 153, 3, "mcspi4_somi", "sdmmc4_dat0", "kpd_row6", "gpio_153", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x011a, "ae20", 154, 3, "mcspi4_cs0", "sdmmc4_dat3", "kpd_row7", "gpio_154", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x011c, "ag20", 155, 3, "uart4_rx", "sdmmc4_dat2", "kpd_row8", "gpio_155", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x011e, "ah19", 156, 3, "uart4_tx", "sdmmc4_dat1", "kpd_col8", "gpio_156", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0120, "ag12", 157, 3, "usbb2_ulpitll_clk", "usbb2_ulpiphy_clk", "sdmmc4_cmd", "gpio_157", "hsi2_cawake", NULL, NULL, "safe_mode"),
+ _PINDEF(0x0122, "af12", 158, 3, "usbb2_ulpitll_stp", "usbb2_ulpiphy_stp", "sdmmc4_clk", "gpio_158", "hsi2_cadata", "dispc2_data23", NULL, "safe_mode"),
+ _PINDEF(0x0124, "ae12", 159, 3, "usbb2_ulpitll_dir", "usbb2_ulpiphy_dir", "sdmmc4_dat0", "gpio_159", "hsi2_caflag", "dispc2_data22", NULL, "safe_mode"),
+ _PINDEF(0x0126, "ag13", 160, 3, "usbb2_ulpitll_nxt", "usbb2_ulpiphy_nxt", "sdmmc4_dat1", "gpio_160", "hsi2_acready", "dispc2_data21", NULL, "safe_mode"),
+ _PINDEF(0x0128, "ae11", 161, 3, "usbb2_ulpitll_dat0", "usbb2_ulpiphy_dat0", "sdmmc4_dat2", "gpio_161", "hsi2_acwake", "dispc2_data20", "usbb2_mm_txen", "safe_mode"),
+ _PINDEF(0x012a, "af11", 162, 3, "usbb2_ulpitll_dat1", "usbb2_ulpiphy_dat1", "sdmmc4_dat3", "gpio_162", "hsi2_acdata", "dispc2_data19", "usbb2_mm_txdat", "safe_mode"),
+ _PINDEF(0x012c, "ag11", 163, 3, "usbb2_ulpitll_dat2", "usbb2_ulpiphy_dat2", "sdmmc3_dat2", "gpio_163", "hsi2_acflag", "dispc2_data18", "usbb2_mm_txse0", "safe_mode"),
+ _PINDEF(0x012e, "ah11", 164, 3, "usbb2_ulpitll_dat3", "usbb2_ulpiphy_dat3", "sdmmc3_dat1", "gpio_164", "hsi2_caready", "dispc2_data15", "rfbi_data15", "safe_mode"),
+ _PINDEF(0x0130, "ae10", 165, 3, "usbb2_ulpitll_dat4", "usbb2_ulpiphy_dat4", "sdmmc3_dat0", "gpio_165", "mcspi3_somi", "dispc2_data14", "rfbi_data14", "safe_mode"),
+ _PINDEF(0x0132, "af10", 166, 3, "usbb2_ulpitll_dat5", "usbb2_ulpiphy_dat5", "sdmmc3_dat3", "gpio_166", "mcspi3_cs0", "dispc2_data13", "rfbi_data13", "safe_mode"),
+ _PINDEF(0x0134, "ag10", 167, 3, "usbb2_ulpitll_dat6", "usbb2_ulpiphy_dat6", "sdmmc3_cmd", "gpio_167", "mcspi3_simo", "dispc2_data12", "rfbi_data12", "safe_mode"),
+ _PINDEF(0x0136, "ae9", 168, 3, "usbb2_ulpitll_dat7", "usbb2_ulpiphy_dat7", "sdmmc3_clk", "gpio_168", "mcspi3_clk", "dispc2_data11", "rfbi_data11", "safe_mode"),
+ _PINDEF(0x0138, "af13", 169, 3, "usbb2_hsic_data", NULL, NULL, "gpio_169", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x013a, "ae13", 170, 3, "usbb2_hsic_strobe", NULL, NULL, "gpio_170", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x013c, "g26", 171, 3, "kpd_col3", "kpd_col0", NULL, "gpio_171", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x013e, "g25", 172, 3, "kpd_col4", "kpd_col1", NULL, "gpio_172", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0140, "h26", 173, 3, "kpd_col5", "kpd_col2", NULL, "gpio_173", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0142, "h25", 174, 3, "kpd_col0", "kpd_col3", NULL, "gpio_174", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0144, "j27", 0, 0, "kpd_col1", "kpd_col4", NULL, "gpio_0", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0146, "h27", 1, 3, "kpd_col2", "kpd_col5", NULL, "gpio_1", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0148, "j26", 175, 3, "kpd_row3", "kpd_row0", NULL, "gpio_175", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x014a, "j25", 176, 3, "kpd_row4", "kpd_row1", NULL, "gpio_176", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x014c, "k26", 177, 3, "kpd_row5", "kpd_row2", NULL, "gpio_177", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x014e, "k25", 178, 3, "kpd_row0", "kpd_row3", NULL, "gpio_178", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0150, "l27", 2, 3, "kpd_row1", "kpd_row4", NULL, "gpio_2", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0152, "k27", 3, 3, "kpd_row2", "kpd_row5", NULL, "gpio_3", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0154, "c3", 0, 0, "usba0_otg_ce", NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ _PINDEF(0x0156, "b5", 0, 0, "usba0_otg_dp", "uart3_rx_irrx", "uart2_rx", NULL, NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0158, "b4", 0, 0, "usba0_otg_dm", "uart3_tx_irtx", "uart2_tx", NULL, NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x015a, "aa28", 181, 3, "fref_clk1_out", NULL, NULL, "gpio_181", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x015c, "y28", 182, 3, "fref_clk2_out", NULL, NULL, "gpio_182", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x015e, "ae6", 0, 0, "sys_nirq1", NULL, NULL, NULL, NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0160, "af6", 183, 3, "sys_nirq2", NULL, NULL, "gpio_183", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0162, "f26", 184, 3, "sys_boot0", NULL, NULL, "gpio_184", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0164, "e27", 185, 3, "sys_boot1", NULL, NULL, "gpio_185", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0166, "e26", 186, 3, "sys_boot2", NULL, NULL, "gpio_186", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x0168, "e25", 187, 3, "sys_boot3", NULL, NULL, "gpio_187", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x016a, "d28", 188, 3, "sys_boot4", NULL, NULL, "gpio_188", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x016c, "d27", 189, 3, "sys_boot5", NULL, NULL, "gpio_189", NULL, NULL, NULL, "safe_mode"),
+ _PINDEF(0x016e, "m2", 11, 3, "dpm_emu0", NULL, NULL, "gpio_11", NULL, NULL, "hw_dbg0", "safe_mode"),
+ _PINDEF(0x0170, "n2", 12, 3, "dpm_emu1", NULL, NULL, "gpio_12", NULL, NULL, "hw_dbg1", "safe_mode"),
+ _PINDEF(0x0172, "p2", 13, 3, "dpm_emu2", "usba0_ulpiphy_clk", NULL, "gpio_13", NULL, "dispc2_fid", "hw_dbg2", "safe_mode"),
+ _PINDEF(0x0174, "v1", 14, 3, "dpm_emu3", "usba0_ulpiphy_stp", NULL, "gpio_14", "rfbi_data10", "dispc2_data10", "hw_dbg3", "safe_mode"),
+ _PINDEF(0x0176, "v2", 15, 3, "dpm_emu4", "usba0_ulpiphy_dir", NULL, "gpio_15", "rfbi_data9", "dispc2_data9", "hw_dbg4", "safe_mode"),
+ _PINDEF(0x0178, "w1", 16, 3, "dpm_emu5", "usba0_ulpiphy_nxt", NULL, "gpio_16", "rfbi_te_vsync0", "dispc2_data16", "hw_dbg5", "safe_mode"),
+ _PINDEF(0x017a, "w2", 17, 3, "dpm_emu6", "usba0_ulpiphy_dat0", "uart3_tx_irtx", "gpio_17", "rfbi_hsync0", "dispc2_data17", "hw_dbg6", "safe_mode"),
+ _PINDEF(0x017c, "w3", 18, 3, "dpm_emu7", "usba0_ulpiphy_dat1", "uart3_rx_irrx", "gpio_18", "rfbi_cs0", "dispc2_hsync", "hw_dbg7", "safe_mode"),
+ _PINDEF(0x017e, "w4", 19, 3, "dpm_emu8", "usba0_ulpiphy_dat2", "uart3_rts_sd", "gpio_19", "rfbi_re", "dispc2_pclk", "hw_dbg8", "safe_mode"),
+ _PINDEF(0x0180, "y2", 20, 3, "dpm_emu9", "usba0_ulpiphy_dat3", "uart3_cts_rctx", "gpio_20", "rfbi_we", "dispc2_vsync", "hw_dbg9", "safe_mode"),
+ _PINDEF(0x0182, "y3", 21, 3, "dpm_emu10", "usba0_ulpiphy_dat4", NULL, "gpio_21", "rfbi_a0", "dispc2_de", "hw_dbg10", "safe_mode"),
+ _PINDEF(0x0184, "y4", 22, 3, "dpm_emu11", "usba0_ulpiphy_dat5", NULL, "gpio_22", "rfbi_data8", "dispc2_data8", "hw_dbg11", "safe_mode"),
+ _PINDEF(0x0186, "aa1", 23, 3, "dpm_emu12", "usba0_ulpiphy_dat6", NULL, "gpio_23", "rfbi_data7", "dispc2_data7", "hw_dbg12", "safe_mode"),
+ _PINDEF(0x0188, "aa2", 24, 3, "dpm_emu13", "usba0_ulpiphy_dat7", NULL, "gpio_24", "rfbi_data6", "dispc2_data6", "hw_dbg13", "safe_mode"),
+ _PINDEF(0x018a, "aa3", 25, 3, "dpm_emu14", "sys_drm_msecure", "uart1_rx", "gpio_25", "rfbi_data5", "dispc2_data5", "hw_dbg14", "safe_mode"),
+ _PINDEF(0x018c, "aa4", 26, 3, "dpm_emu15", "sys_secure_indicator", NULL, "gpio_26", "rfbi_data4", "dispc2_data4", "hw_dbg15", "safe_mode"),
+ _PINDEF(0x018e, "ab2", 27, 3, "dpm_emu16", "dmtimer8_pwm_evt", "dsi1_te0", "gpio_27", "rfbi_data3", "dispc2_data3", "hw_dbg16", "safe_mode"),
+ _PINDEF(0x0190, "ab3", 28, 3, "dpm_emu17", "dmtimer9_pwm_evt", "dsi1_te1", "gpio_28", "rfbi_data2", "dispc2_data2", "hw_dbg17", "safe_mode"),
+ _PINDEF(0x0192, "ab4", 190, 3, "dpm_emu18", "dmtimer10_pwm_evt", "dsi2_te0", "gpio_190", "rfbi_data1", "dispc2_data1", "hw_dbg18", "safe_mode"),
+ _PINDEF(0x0194, "ac4", 191, 3, "dpm_emu19", "dmtimer11_pwm_evt", "dsi2_te1", "gpio_191", "rfbi_data0", "dispc2_data0", "hw_dbg19", "safe_mode"),
+ { .ballname = NULL },
+};
+
+const struct ti_pinmux_device omap4_pinmux_dev = {
+ .padconf_muxmode_mask = CONTROL_PADCONF_MUXMODE_MASK,
+ .padconf_sate_mask = CONTROL_PADCONF_SATE_MASK,
+ .padstate = ti_padstate_devmap,
+ .padconf = ti_padconf_devmap,
+};
diff --git a/sys/arm/ti/omap4/omap4_scm_padconf.h b/sys/arm/ti/omap4/omap4_scm_padconf.h
new file mode 100644
index 000000000000..adee0a799cd3
--- /dev/null
+++ b/sys/arm/ti/omap4/omap4_scm_padconf.h
@@ -0,0 +1,83 @@
+/*-
+ * Copyright (c) 2011
+ * Ben Gray <ben.r.gray@gmail.com>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BEN GRAY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef OMAP4_SCM_PADCONF_H
+#define OMAP4_SCM_PADCONF_H
+
+#define CONTROL_PADCONF_WAKEUP_EVENT (1UL << 15)
+#define CONTROL_PADCONF_WAKEUP_ENABLE (1UL << 14)
+#define CONTROL_PADCONF_OFF_PULL_UP (1UL << 13)
+#define CONTROL_PADCONF_OFF_PULL_ENABLE (1UL << 12)
+#define CONTROL_PADCONF_OFF_OUT_HIGH (1UL << 11)
+#define CONTROL_PADCONF_OFF_OUT_ENABLE (1UL << 10)
+#define CONTROL_PADCONF_OFF_ENABLE (1UL << 9)
+#define CONTROL_PADCONF_INPUT_ENABLE (1UL << 8)
+#define CONTROL_PADCONF_PULL_UP (1UL << 4)
+#define CONTROL_PADCONF_PULL_ENABLE (1UL << 3)
+#define CONTROL_PADCONF_MUXMODE_MASK (0x7)
+
+#define CONTROL_PADCONF_SATE_MASK ( CONTROL_PADCONF_WAKEUP_EVENT \
+ | CONTROL_PADCONF_WAKEUP_ENABLE \
+ | CONTROL_PADCONF_OFF_PULL_UP \
+ | CONTROL_PADCONF_OFF_PULL_ENABLE \
+ | CONTROL_PADCONF_OFF_OUT_HIGH \
+ | CONTROL_PADCONF_OFF_OUT_ENABLE \
+ | CONTROL_PADCONF_OFF_ENABLE \
+ | CONTROL_PADCONF_INPUT_ENABLE \
+ | CONTROL_PADCONF_PULL_UP \
+ | CONTROL_PADCONF_PULL_ENABLE )
+
+/* Active pin states */
+#define PADCONF_PIN_OUTPUT 0
+#define PADCONF_PIN_INPUT CONTROL_PADCONF_INPUT_ENABLE
+#define PADCONF_PIN_INPUT_PULLUP ( CONTROL_PADCONF_INPUT_ENABLE \
+ | CONTROL_PADCONF_PULL_ENABLE \
+ | CONTROL_PADCONF_PULL_UP)
+#define PADCONF_PIN_INPUT_PULLDOWN ( CONTROL_PADCONF_INPUT_ENABLE \
+ | CONTROL_PADCONF_PULL_ENABLE )
+
+/* Off mode states */
+#define PADCONF_PIN_OFF_NONE 0
+#define PADCONF_PIN_OFF_OUTPUT_HIGH ( CONTROL_PADCONF_OFF_ENABLE \
+ | CONTROL_PADCONF_OFF_OUT_ENABLE \
+ | CONTROL_PADCONF_OFF_OUT_HIGH)
+#define PADCONF_PIN_OFF_OUTPUT_LOW ( CONTROL_PADCONF_OFF_ENABLE \
+ | CONTROL_PADCONF_OFF_OUT_ENABLE)
+#define PADCONF_PIN_OFF_INPUT_PULLUP ( CONTROL_PADCONF_OFF_ENABLE \
+ | CONTROL_PADCONF_OFF_PULL_ENABLE \
+ | CONTROL_PADCONF_OFF_PULL_UP)
+#define PADCONF_PIN_OFF_INPUT_PULLDOWN ( CONTROL_PADCONF_OFF_ENABLE \
+ | CONTROL_PADCONF_OFF_PULL_ENABLE)
+#define PADCONF_PIN_OFF_WAKEUPENABLE CONTROL_PADCONF_WAKEUP_ENABLE
+
+extern const struct ti_pinmux_device omap4_pinmux_dev;
+
+#endif /* OMAP4_SCM_PADCONF_H */
diff --git a/sys/arm/ti/omap4/omap4_smc.h b/sys/arm/ti/omap4/omap4_smc.h
new file mode 100644
index 000000000000..cb22f10440fa
--- /dev/null
+++ b/sys/arm/ti/omap4/omap4_smc.h
@@ -0,0 +1,54 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Olivier Houchard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * $FreeBSD$
+ */
+
+#ifndef OMAP4_SMC_H_
+#define OMAP4_SMC_H_
+/* Define the various function IDs used by the OMAP4 */
+#define L2CACHE_WRITE_DEBUG_REG 0x100
+#define L2CACHE_CLEAN_INV_RANG 0x101
+#define L2CACHE_WRITE_CTRL_REG 0x102
+#define READ_AUX_CORE_REGS 0x103
+#define MODIFY_AUX_CORE_0 0x104
+#define WRITE_AUX_CORE_1 0x105
+#define READ_WKG_CTRL_REG 0x106
+#define CLEAR_WKG_CTRL_REG 0x107
+#define SET_POWER_STATUS_REG 0x108
+#define WRITE_AUXCTRL_REG 0x109
+#define LOCKDOWN_TLB 0x10a
+#define SELECT_TLB_ENTRY_FOR_WRITE 0x10b
+#define READ_TLB_VA_ENTRY 0x10c
+#define WRITE_TLB_VA_ENTRY 0x10d
+#define READ_TLB_PA_ENTRY 0x10e
+#define WRITE_TLB_PA_ENTRY 0x10f
+#define READ_TLB_ATTR_ENTRY 0x110
+#define WRITE_TLB_ATTR_ENTRY 0x111
+#define WRITE_LATENCY_CTRL_REG 0x112
+#define WRITE_PREFETCH_CTRL_REG 0x113
+#endif /* OMAP4_SMC_H_ */
diff --git a/sys/arm/ti/omap4/omap4_wugen.c b/sys/arm/ti/omap4/omap4_wugen.c
new file mode 100644
index 000000000000..539e4f905b30
--- /dev/null
+++ b/sys/arm/ti/omap4/omap4_wugen.c
@@ -0,0 +1,249 @@
+/*-
+ * Copyright (c) 2016 Svatopluk Kraus
+ * Copyright (c) 2016 Michal Meloun
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/systm.h>
+
+#include <machine/fdt.h>
+#include <machine/intr.h>
+#include <machine/resource.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "pic_if.h"
+
+static struct ofw_compat_data compat_data[] = {
+ {"ti,omap4-wugen-mpu", 1},
+ {NULL, 0}
+};
+
+struct omap4_wugen_sc {
+ device_t sc_dev;
+ struct resource *sc_mem_res;
+ device_t sc_parent;
+};
+
+static int
+omap4_wugen_activate_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ struct omap4_wugen_sc *sc = device_get_softc(dev);
+
+ return (PIC_ACTIVATE_INTR(sc->sc_parent, isrc, res, data));
+}
+
+static void
+omap4_wugen_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct omap4_wugen_sc *sc = device_get_softc(dev);
+
+ PIC_DISABLE_INTR(sc->sc_parent, isrc);
+}
+
+static void
+omap4_wugen_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct omap4_wugen_sc *sc = device_get_softc(dev);
+
+ PIC_ENABLE_INTR(sc->sc_parent, isrc);
+}
+
+static int
+omap4_wugen_map_intr(device_t dev, struct intr_map_data *data,
+ struct intr_irqsrc **isrcp)
+{
+ struct omap4_wugen_sc *sc = device_get_softc(dev);
+
+ return (PIC_MAP_INTR(sc->sc_parent, data, isrcp));
+}
+
+static int
+omap4_wugen_deactivate_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ struct omap4_wugen_sc *sc = device_get_softc(dev);
+
+ return (PIC_DEACTIVATE_INTR(sc->sc_parent, isrc, res, data));
+}
+
+static int
+omap4_wugen_setup_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ struct omap4_wugen_sc *sc = device_get_softc(dev);
+
+ return (PIC_SETUP_INTR(sc->sc_parent, isrc, res, data));
+}
+
+static int
+omap4_wugen_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ struct omap4_wugen_sc *sc = device_get_softc(dev);
+
+ return (PIC_TEARDOWN_INTR(sc->sc_parent, isrc, res, data));
+}
+
+static void
+omap4_wugen_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct omap4_wugen_sc *sc = device_get_softc(dev);
+
+ PIC_PRE_ITHREAD(sc->sc_parent, isrc);
+}
+
+static void
+omap4_wugen_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct omap4_wugen_sc *sc = device_get_softc(dev);
+
+ PIC_POST_ITHREAD(sc->sc_parent, isrc);
+}
+
+static void
+omap4_wugen_post_filter(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct omap4_wugen_sc *sc = device_get_softc(dev);
+
+ PIC_POST_FILTER(sc->sc_parent, isrc);
+}
+
+#ifdef SMP
+static int
+omap4_wugen_bind_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct omap4_wugen_sc *sc = device_get_softc(dev);
+
+ return (PIC_BIND_INTR(sc->sc_parent, isrc));
+}
+#endif
+
+static int
+omap4_wugen_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+omap4_wugen_detach(device_t dev)
+{
+ struct omap4_wugen_sc *sc;
+
+ sc = device_get_softc(dev);
+ if (sc->sc_mem_res != NULL) {
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+ sc->sc_mem_res = NULL;
+ }
+ return (0);
+}
+
+static int
+omap4_wugen_attach(device_t dev)
+{
+ struct omap4_wugen_sc *sc;
+ phandle_t node;
+ phandle_t parent_xref;
+ int rid, rv;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+ node = ofw_bus_get_node(dev);
+
+ rv = OF_getencprop(node, "interrupt-parent", &parent_xref,
+ sizeof(parent_xref));
+ if (rv <= 0) {
+ device_printf(dev, "can't read parent node property\n");
+ goto fail;
+ }
+ sc->sc_parent = OF_device_from_xref(parent_xref);
+ if (sc->sc_parent == NULL) {
+ device_printf(dev, "can't find parent controller\n");
+ goto fail;
+ }
+
+ rid = 0;
+ sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->sc_mem_res == NULL) {
+ device_printf(dev, "can't allocate resources\n");
+ return (ENXIO);
+ }
+
+ if (intr_pic_register(dev, OF_xref_from_node(node)) == NULL) {
+ device_printf(dev, "can't register PIC\n");
+ goto fail;
+ }
+ return (0);
+
+fail:
+ omap4_wugen_detach(dev);
+ return (ENXIO);
+}
+
+static device_method_t omap4_wugen_methods[] = {
+ DEVMETHOD(device_probe, omap4_wugen_probe),
+ DEVMETHOD(device_attach, omap4_wugen_attach),
+ DEVMETHOD(device_detach, omap4_wugen_detach),
+
+ /* Interrupt controller interface */
+ DEVMETHOD(pic_activate_intr, omap4_wugen_activate_intr),
+ DEVMETHOD(pic_disable_intr, omap4_wugen_disable_intr),
+ DEVMETHOD(pic_enable_intr, omap4_wugen_enable_intr),
+ DEVMETHOD(pic_map_intr, omap4_wugen_map_intr),
+ DEVMETHOD(pic_deactivate_intr, omap4_wugen_deactivate_intr),
+ DEVMETHOD(pic_setup_intr, omap4_wugen_setup_intr),
+ DEVMETHOD(pic_teardown_intr, omap4_wugen_teardown_intr),
+ DEVMETHOD(pic_pre_ithread, omap4_wugen_pre_ithread),
+ DEVMETHOD(pic_post_ithread, omap4_wugen_post_ithread),
+ DEVMETHOD(pic_post_filter, omap4_wugen_post_filter),
+#ifdef SMP
+ DEVMETHOD(pic_bind_intr, omap4_wugen_bind_intr),
+#endif
+ DEVMETHOD_END
+};
+devclass_t omap4_wugen_devclass;
+DEFINE_CLASS_0(omap4_wugen, omap4_wugen_driver, omap4_wugen_methods,
+ sizeof(struct omap4_wugen_sc));
+EARLY_DRIVER_MODULE(omap4_wugen, simplebus, omap4_wugen_driver,
+ omap4_wugen_devclass, NULL, NULL,
+ BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE + 1);
diff --git a/sys/arm/ti/omap4/pandaboard/pandaboard.c b/sys/arm/ti/omap4/pandaboard/pandaboard.c
new file mode 100644
index 000000000000..fbf4034933cc
--- /dev/null
+++ b/sys/arm/ti/omap4/pandaboard/pandaboard.c
@@ -0,0 +1,169 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 2011
+ * Ben Gray <ben.r.gray@gmail.com>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BEN GRAY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/bus.h>
+#include <machine/vmparam.h>
+#include <machine/fdt.h>
+
+#include <arm/ti/omap4/omap4_reg.h>
+#include <arm/ti/omap4/pandaboard/pandaboard.h>
+
+/* Registers in the SCRM that control the AUX clocks */
+#define SCRM_ALTCLKSRC (0x110)
+#define SCRM_AUXCLK0 (0x0310)
+#define SCRM_AUXCLK1 (0x0314)
+#define SCRM_AUXCLK2 (0x0318)
+#define SCRM_AUXCLK3 (0x031C)
+
+/* Some of the GPIO register set */
+#define GPIO1_OE (0x0134)
+#define GPIO1_CLEARDATAOUT (0x0190)
+#define GPIO1_SETDATAOUT (0x0194)
+#define GPIO2_OE (0x0134)
+#define GPIO2_CLEARDATAOUT (0x0190)
+#define GPIO2_SETDATAOUT (0x0194)
+
+/* Some of the PADCONF register set */
+#define CONTROL_WKUP_PAD0_FREF_CLK3_OUT (0x058)
+#define CONTROL_CORE_PAD1_KPD_COL2 (0x186)
+#define CONTROL_CORE_PAD0_GPMC_WAIT1 (0x08C)
+
+#define REG_WRITE32(r, x) *((volatile uint32_t*)(r)) = (uint32_t)(x)
+#define REG_READ32(r) *((volatile uint32_t*)(r))
+
+#define REG_WRITE16(r, x) *((volatile uint16_t*)(r)) = (uint16_t)(x)
+#define REG_READ16(r) *((volatile uint16_t*)(r))
+
+/**
+ * usb_hub_init - initialises and resets the external USB hub
+ *
+ * The USB hub needs to be held in reset while the power is being applied
+ * and the reference clock is enabled at 19.2MHz. The following is the
+ * layout of the USB hub taken from the Pandaboard reference manual.
+ *
+ *
+ * .-------------. .--------------. .----------------.
+ * | OMAP4430 | | USB3320C | | LAN9514 |
+ * | | | | | USB Hub / Eth |
+ * | CLK | <------ | CLKOUT | | |
+ * | STP | ------> | STP | | |
+ * | DIR | <------ | DIR | | |
+ * | NXT | <------ | NXT | | |
+ * | DAT0 | <-----> | DAT0 | | |
+ * | DAT1 | <-----> | DAT1 DP | <-----> | DP |
+ * | DAT2 | <-----> | DAT2 DM | <-----> | DM |
+ * | DAT3 | <-----> | DAT3 | | |
+ * | DAT4 | <-----> | DAT4 | | |
+ * | DAT5 | <-----> | DAT5 | +----> | N_RESET |
+ * | DAT6 | <-----> | DAT6 | | | |
+ * | DAT7 | <-----> | DAT7 | | | |
+ * | | | | | +-> | VDD33IO |
+ * | AUX_CLK3 | ------> | REFCLK | | +-> | VDD33A |
+ * | | | | | | | |
+ * | GPIO_62 | --+---> | RESET | | | | |
+ * | | | | | | | | |
+ * | | | '--------------' | | '----------------'
+ * | | | .--------------. | |
+ * | | '---->| VOLT CONVERT |--' |
+ * | | '--------------' |
+ * | | |
+ * | | .--------------. |
+ * | GPIO_1 | ------> | TPS73633 |-----'
+ * | | '--------------'
+ * '-------------'
+ *
+ *
+ * RETURNS:
+ * nothing.
+ */
+void
+pandaboard_usb_hub_init(void)
+{
+ bus_space_handle_t scrm_addr, gpio1_addr, gpio2_addr, scm_addr;
+
+ if (bus_space_map(fdtbus_bs_tag, OMAP44XX_SCRM_HWBASE,
+ OMAP44XX_SCRM_SIZE, 0, &scrm_addr) != 0)
+ panic("Couldn't map SCRM registers");
+ if (bus_space_map(fdtbus_bs_tag, OMAP44XX_GPIO1_HWBASE,
+ OMAP44XX_GPIO1_SIZE, 0, &gpio1_addr) != 0)
+ panic("Couldn't map GPIO1 registers");
+ if (bus_space_map(fdtbus_bs_tag, OMAP44XX_GPIO2_HWBASE,
+ OMAP44XX_GPIO2_SIZE, 0, &gpio2_addr) != 0)
+ panic("Couldn't map GPIO2 registers");
+ if (bus_space_map(fdtbus_bs_tag, OMAP44XX_SCM_PADCONF_HWBASE,
+ OMAP44XX_SCM_PADCONF_SIZE, 0, &scm_addr) != 0)
+ panic("Couldn't map SCM Padconf registers");
+
+ /* Need to set FREF_CLK3_OUT to 19.2 MHz and pump it out on pin GPIO_WK31.
+ * We know the SYS_CLK is 38.4Mhz and therefore to get the needed 19.2Mhz,
+ * just use a 2x divider and ensure the SYS_CLK is used as the source.
+ */
+ REG_WRITE32(scrm_addr + SCRM_AUXCLK3, (1 << 16) | /* Divider of 2 */
+ (0 << 1) | /* Use the SYS_CLK as the source */
+ (1 << 8)); /* Enable the clock */
+
+ /* Enable the clock out to the pin (GPIO_WK31).
+ * muxmode=fref_clk3_out, pullup/down=disabled, input buffer=disabled,
+ * wakeup=disabled.
+ */
+ REG_WRITE16(scm_addr + CONTROL_WKUP_PAD0_FREF_CLK3_OUT, 0x0000);
+
+ /* Disable the power to the USB hub, drive GPIO1 low */
+ REG_WRITE32(gpio1_addr + GPIO1_OE, REG_READ32(gpio1_addr +
+ GPIO1_OE) & ~(1UL << 1));
+ REG_WRITE32(gpio1_addr + GPIO1_CLEARDATAOUT, (1UL << 1));
+ REG_WRITE16(scm_addr + CONTROL_CORE_PAD1_KPD_COL2, 0x0003);
+
+ /* Reset the USB PHY and Hub using GPIO_62 */
+ REG_WRITE32(gpio2_addr + GPIO2_OE,
+ REG_READ32(gpio2_addr + GPIO2_OE) & ~(1UL << 30));
+ REG_WRITE32(gpio2_addr + GPIO2_CLEARDATAOUT, (1UL << 30));
+ REG_WRITE16(scm_addr + CONTROL_CORE_PAD0_GPMC_WAIT1, 0x0003);
+ DELAY(10);
+ REG_WRITE32(gpio2_addr + GPIO2_SETDATAOUT, (1UL << 30));
+
+ /* Enable power to the hub (GPIO_1) */
+ REG_WRITE32(gpio1_addr + GPIO1_SETDATAOUT, (1UL << 1));
+ bus_space_unmap(fdtbus_bs_tag, scrm_addr, OMAP44XX_SCRM_SIZE);
+ bus_space_unmap(fdtbus_bs_tag, gpio1_addr, OMAP44XX_GPIO1_SIZE);
+ bus_space_unmap(fdtbus_bs_tag, gpio2_addr, OMAP44XX_GPIO2_SIZE);
+ bus_space_unmap(fdtbus_bs_tag, scm_addr, OMAP44XX_SCM_PADCONF_SIZE);
+}
diff --git a/sys/arm/ti/omap4/pandaboard/pandaboard.h b/sys/arm/ti/omap4/pandaboard/pandaboard.h
new file mode 100644
index 000000000000..56f6f0bb8a5a
--- /dev/null
+++ b/sys/arm/ti/omap4/pandaboard/pandaboard.h
@@ -0,0 +1,31 @@
+/*-
+ * Copyright (c) 2016 Olivier Houchard <cognet@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _PANDABOARD_H_
+#define _PANDABOARD_H_
+void pandaboard_usb_hub_init(void);
+#endif /* _OMAP4_MP_H_ */
diff --git a/sys/arm/ti/omap4/std.omap4 b/sys/arm/ti/omap4/std.omap4
new file mode 100644
index 000000000000..b3edb8bec426
--- /dev/null
+++ b/sys/arm/ti/omap4/std.omap4
@@ -0,0 +1,8 @@
+# Omap4430 generic configuration
+#$FreeBSD$
+files "../ti/omap4/files.omap4"
+include "../ti/std.ti"
+
+cpu CPU_CORTEXA
+
+options SOC_OMAP4
diff --git a/sys/arm/ti/std.ti b/sys/arm/ti/std.ti
new file mode 100644
index 000000000000..83c144393910
--- /dev/null
+++ b/sys/arm/ti/std.ti
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+machine arm armv7
+makeoptions CONF_CFLAGS="-march=armv7a"
+
+files "../ti/files.ti"
diff --git a/sys/arm/ti/ti_adc.c b/sys/arm/ti/ti_adc.c
new file mode 100644
index 000000000000..a0091aebf417
--- /dev/null
+++ b/sys/arm/ti/ti_adc.c
@@ -0,0 +1,969 @@
+/*-
+ * Copyright 2014 Luiz Otavio O Souza <loos@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_evdev.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+#include <sys/selinfo.h>
+#include <sys/poll.h>
+#include <sys/uio.h>
+
+#include <machine/bus.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#ifdef EVDEV_SUPPORT
+#include <dev/evdev/input.h>
+#include <dev/evdev/evdev.h>
+#endif
+
+#include <arm/ti/ti_sysc.h>
+#include <arm/ti/ti_adcreg.h>
+#include <arm/ti/ti_adcvar.h>
+
+#undef DEBUG_TSC
+
+#define DEFAULT_CHARGE_DELAY 0x400
+#define STEPDLY_OPEN 0x98
+
+#define ORDER_XP 0
+#define ORDER_XN 1
+#define ORDER_YP 2
+#define ORDER_YN 3
+
+/* Define our 8 steps, one for each input channel. */
+static struct ti_adc_input ti_adc_inputs[TI_ADC_NPINS] = {
+ { .stepconfig = ADC_STEPCFG(1), .stepdelay = ADC_STEPDLY(1) },
+ { .stepconfig = ADC_STEPCFG(2), .stepdelay = ADC_STEPDLY(2) },
+ { .stepconfig = ADC_STEPCFG(3), .stepdelay = ADC_STEPDLY(3) },
+ { .stepconfig = ADC_STEPCFG(4), .stepdelay = ADC_STEPDLY(4) },
+ { .stepconfig = ADC_STEPCFG(5), .stepdelay = ADC_STEPDLY(5) },
+ { .stepconfig = ADC_STEPCFG(6), .stepdelay = ADC_STEPDLY(6) },
+ { .stepconfig = ADC_STEPCFG(7), .stepdelay = ADC_STEPDLY(7) },
+ { .stepconfig = ADC_STEPCFG(8), .stepdelay = ADC_STEPDLY(8) },
+};
+
+static int ti_adc_samples[5] = { 0, 2, 4, 8, 16 };
+
+static int ti_adc_detach(device_t dev);
+
+#ifdef EVDEV_SUPPORT
+static void
+ti_adc_ev_report(struct ti_adc_softc *sc)
+{
+
+ evdev_push_event(sc->sc_evdev, EV_ABS, ABS_X, sc->sc_x);
+ evdev_push_event(sc->sc_evdev, EV_ABS, ABS_Y, sc->sc_y);
+ evdev_push_event(sc->sc_evdev, EV_KEY, BTN_TOUCH, sc->sc_pen_down);
+ evdev_sync(sc->sc_evdev);
+}
+#endif /* EVDEV */
+
+static void
+ti_adc_enable(struct ti_adc_softc *sc)
+{
+ uint32_t reg;
+
+ TI_ADC_LOCK_ASSERT(sc);
+
+ if (sc->sc_last_state == 1)
+ return;
+
+ /* Enable the FIFO0 threshold and the end of sequence interrupt. */
+ ADC_WRITE4(sc, ADC_IRQENABLE_SET,
+ ADC_IRQ_FIFO0_THRES | ADC_IRQ_FIFO1_THRES | ADC_IRQ_END_OF_SEQ);
+
+ reg = ADC_CTRL_STEP_WP | ADC_CTRL_STEP_ID;
+ if (sc->sc_tsc_wires > 0) {
+ reg |= ADC_CTRL_TSC_ENABLE;
+ switch (sc->sc_tsc_wires) {
+ case 4:
+ reg |= ADC_CTRL_TSC_4WIRE;
+ break;
+ case 5:
+ reg |= ADC_CTRL_TSC_5WIRE;
+ break;
+ case 8:
+ reg |= ADC_CTRL_TSC_8WIRE;
+ break;
+ default:
+ break;
+ }
+ }
+ reg |= ADC_CTRL_ENABLE;
+ /* Enable the ADC. Run thru enabled steps, start the conversions. */
+ ADC_WRITE4(sc, ADC_CTRL, reg);
+
+ sc->sc_last_state = 1;
+}
+
+static void
+ti_adc_disable(struct ti_adc_softc *sc)
+{
+ int count;
+ uint32_t data;
+
+ TI_ADC_LOCK_ASSERT(sc);
+
+ if (sc->sc_last_state == 0)
+ return;
+
+ /* Disable all the enabled steps. */
+ ADC_WRITE4(sc, ADC_STEPENABLE, 0);
+
+ /* Disable the ADC. */
+ ADC_WRITE4(sc, ADC_CTRL, ADC_READ4(sc, ADC_CTRL) & ~ADC_CTRL_ENABLE);
+
+ /* Disable the FIFO0 threshold and the end of sequence interrupt. */
+ ADC_WRITE4(sc, ADC_IRQENABLE_CLR,
+ ADC_IRQ_FIFO0_THRES | ADC_IRQ_FIFO1_THRES | ADC_IRQ_END_OF_SEQ);
+
+ /* ACK any pending interrupt. */
+ ADC_WRITE4(sc, ADC_IRQSTATUS, ADC_READ4(sc, ADC_IRQSTATUS));
+
+ /* Drain the FIFO data. */
+ count = ADC_READ4(sc, ADC_FIFO0COUNT) & ADC_FIFO_COUNT_MSK;
+ while (count > 0) {
+ data = ADC_READ4(sc, ADC_FIFO0DATA);
+ count = ADC_READ4(sc, ADC_FIFO0COUNT) & ADC_FIFO_COUNT_MSK;
+ }
+
+ count = ADC_READ4(sc, ADC_FIFO1COUNT) & ADC_FIFO_COUNT_MSK;
+ while (count > 0) {
+ data = ADC_READ4(sc, ADC_FIFO1DATA);
+ count = ADC_READ4(sc, ADC_FIFO1COUNT) & ADC_FIFO_COUNT_MSK;
+ }
+
+ sc->sc_last_state = 0;
+}
+
+static int
+ti_adc_setup(struct ti_adc_softc *sc)
+{
+ int ain, i;
+ uint32_t enabled;
+
+ TI_ADC_LOCK_ASSERT(sc);
+
+ /* Check for enabled inputs. */
+ enabled = sc->sc_tsc_enabled;
+ for (i = 0; i < sc->sc_adc_nchannels; i++) {
+ ain = sc->sc_adc_channels[i];
+ if (ti_adc_inputs[ain].enable)
+ enabled |= (1U << (ain + 1));
+ }
+
+ /* Set the ADC global status. */
+ if (enabled != 0) {
+ ti_adc_enable(sc);
+ /* Update the enabled steps. */
+ if (enabled != ADC_READ4(sc, ADC_STEPENABLE))
+ ADC_WRITE4(sc, ADC_STEPENABLE, enabled);
+ } else
+ ti_adc_disable(sc);
+
+ return (0);
+}
+
+static void
+ti_adc_input_setup(struct ti_adc_softc *sc, int32_t ain)
+{
+ struct ti_adc_input *input;
+ uint32_t reg, val;
+
+ TI_ADC_LOCK_ASSERT(sc);
+
+ input = &ti_adc_inputs[ain];
+ reg = input->stepconfig;
+ val = ADC_READ4(sc, reg);
+
+ /* Set single ended operation. */
+ val &= ~ADC_STEP_DIFF_CNTRL;
+
+ /* Set the negative voltage reference. */
+ val &= ~ADC_STEP_RFM_MSK;
+
+ /* Set the positive voltage reference. */
+ val &= ~ADC_STEP_RFP_MSK;
+
+ /* Set the samples average. */
+ val &= ~ADC_STEP_AVG_MSK;
+ val |= input->samples << ADC_STEP_AVG_SHIFT;
+
+ /* Select the desired input. */
+ val &= ~ADC_STEP_INP_MSK;
+ val |= ain << ADC_STEP_INP_SHIFT;
+
+ /* Set the ADC to one-shot mode. */
+ val &= ~ADC_STEP_MODE_MSK;
+
+ ADC_WRITE4(sc, reg, val);
+}
+
+static void
+ti_adc_reset(struct ti_adc_softc *sc)
+{
+ int ain, i;
+
+ TI_ADC_LOCK_ASSERT(sc);
+
+ /* Disable all the inputs. */
+ for (i = 0; i < sc->sc_adc_nchannels; i++) {
+ ain = sc->sc_adc_channels[i];
+ ti_adc_inputs[ain].enable = 0;
+ }
+}
+
+static int
+ti_adc_clockdiv_proc(SYSCTL_HANDLER_ARGS)
+{
+ int error, reg;
+ struct ti_adc_softc *sc;
+
+ sc = (struct ti_adc_softc *)arg1;
+
+ TI_ADC_LOCK(sc);
+ reg = (int)ADC_READ4(sc, ADC_CLKDIV) + 1;
+ TI_ADC_UNLOCK(sc);
+
+ error = sysctl_handle_int(oidp, &reg, sizeof(reg), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ /*
+ * The actual written value is the prescaler setting - 1.
+ * Enforce a minimum value of 10 (i.e. 9) which limits the maximum
+ * ADC clock to ~2.4Mhz (CLK_M_OSC / 10).
+ */
+ reg--;
+ if (reg < 9)
+ reg = 9;
+ if (reg > USHRT_MAX)
+ reg = USHRT_MAX;
+
+ TI_ADC_LOCK(sc);
+ /* Disable the ADC. */
+ ti_adc_disable(sc);
+ /* Update the ADC prescaler setting. */
+ ADC_WRITE4(sc, ADC_CLKDIV, reg);
+ /* Enable the ADC again. */
+ ti_adc_setup(sc);
+ TI_ADC_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+ti_adc_enable_proc(SYSCTL_HANDLER_ARGS)
+{
+ int error;
+ int32_t enable;
+ struct ti_adc_softc *sc;
+ struct ti_adc_input *input;
+
+ input = (struct ti_adc_input *)arg1;
+ sc = input->sc;
+
+ enable = input->enable;
+ error = sysctl_handle_int(oidp, &enable, sizeof(enable),
+ req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ if (enable)
+ enable = 1;
+
+ TI_ADC_LOCK(sc);
+ /* Setup the ADC as needed. */
+ if (input->enable != enable) {
+ input->enable = enable;
+ ti_adc_setup(sc);
+ if (input->enable == 0)
+ input->value = 0;
+ }
+ TI_ADC_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+ti_adc_open_delay_proc(SYSCTL_HANDLER_ARGS)
+{
+ int error, reg;
+ struct ti_adc_softc *sc;
+ struct ti_adc_input *input;
+
+ input = (struct ti_adc_input *)arg1;
+ sc = input->sc;
+
+ TI_ADC_LOCK(sc);
+ reg = (int)ADC_READ4(sc, input->stepdelay) & ADC_STEP_OPEN_DELAY;
+ TI_ADC_UNLOCK(sc);
+
+ error = sysctl_handle_int(oidp, &reg, sizeof(reg), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ if (reg < 0)
+ reg = 0;
+
+ TI_ADC_LOCK(sc);
+ ADC_WRITE4(sc, input->stepdelay, reg & ADC_STEP_OPEN_DELAY);
+ TI_ADC_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+ti_adc_samples_avg_proc(SYSCTL_HANDLER_ARGS)
+{
+ int error, samples, i;
+ struct ti_adc_softc *sc;
+ struct ti_adc_input *input;
+
+ input = (struct ti_adc_input *)arg1;
+ sc = input->sc;
+
+ if (input->samples > nitems(ti_adc_samples))
+ input->samples = nitems(ti_adc_samples);
+ samples = ti_adc_samples[input->samples];
+
+ error = sysctl_handle_int(oidp, &samples, 0, req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ TI_ADC_LOCK(sc);
+ if (samples != ti_adc_samples[input->samples]) {
+ input->samples = 0;
+ for (i = 0; i < nitems(ti_adc_samples); i++)
+ if (samples >= ti_adc_samples[i])
+ input->samples = i;
+ ti_adc_input_setup(sc, input->input);
+ }
+ TI_ADC_UNLOCK(sc);
+
+ return (error);
+}
+
+static void
+ti_adc_read_data(struct ti_adc_softc *sc)
+{
+ int count, ain;
+ struct ti_adc_input *input;
+ uint32_t data;
+
+ TI_ADC_LOCK_ASSERT(sc);
+
+ /* Read the available data. */
+ count = ADC_READ4(sc, ADC_FIFO0COUNT) & ADC_FIFO_COUNT_MSK;
+ while (count > 0) {
+ data = ADC_READ4(sc, ADC_FIFO0DATA);
+ ain = (data & ADC_FIFO_STEP_ID_MSK) >> ADC_FIFO_STEP_ID_SHIFT;
+ input = &ti_adc_inputs[ain];
+ if (input->enable == 0)
+ input->value = 0;
+ else
+ input->value = (int32_t)(data & ADC_FIFO_DATA_MSK);
+ count = ADC_READ4(sc, ADC_FIFO0COUNT) & ADC_FIFO_COUNT_MSK;
+ }
+}
+
+static int
+cmp_values(const void *a, const void *b)
+{
+ const uint32_t *v1, *v2;
+ v1 = a;
+ v2 = b;
+ if (*v1 < *v2)
+ return -1;
+ if (*v1 > *v2)
+ return 1;
+
+ return (0);
+}
+
+static void
+ti_adc_tsc_read_data(struct ti_adc_softc *sc)
+{
+ int count;
+ uint32_t data[16];
+ uint32_t x, y;
+ int i, start, end;
+
+ TI_ADC_LOCK_ASSERT(sc);
+
+ /* Read the available data. */
+ count = ADC_READ4(sc, ADC_FIFO1COUNT) & ADC_FIFO_COUNT_MSK;
+ if (count == 0)
+ return;
+
+ i = 0;
+ while (count > 0) {
+ data[i++] = ADC_READ4(sc, ADC_FIFO1DATA) & ADC_FIFO_DATA_MSK;
+ count = ADC_READ4(sc, ADC_FIFO1COUNT) & ADC_FIFO_COUNT_MSK;
+ }
+
+ if (sc->sc_coord_readouts > 3) {
+ start = 1;
+ end = sc->sc_coord_readouts - 1;
+ qsort(data, sc->sc_coord_readouts,
+ sizeof(data[0]), &cmp_values);
+ qsort(&data[sc->sc_coord_readouts + 2],
+ sc->sc_coord_readouts,
+ sizeof(data[0]), &cmp_values);
+ }
+ else {
+ start = 0;
+ end = sc->sc_coord_readouts;
+ }
+
+ x = y = 0;
+ for (i = start; i < end; i++)
+ y += data[i];
+ y /= (end - start);
+
+ for (i = sc->sc_coord_readouts + 2 + start; i < sc->sc_coord_readouts + 2 + end; i++)
+ x += data[i];
+ x /= (end - start);
+
+#ifdef DEBUG_TSC
+ device_printf(sc->sc_dev, "touchscreen x: %d, y: %d\n", x, y);
+#endif
+
+#ifdef EVDEV_SUPPORT
+ if ((sc->sc_x != x) || (sc->sc_y != y)) {
+ sc->sc_x = x;
+ sc->sc_y = y;
+ ti_adc_ev_report(sc);
+ }
+#endif
+}
+
+static void
+ti_adc_intr_locked(struct ti_adc_softc *sc, uint32_t status)
+{
+ /* Read the available data. */
+ if (status & ADC_IRQ_FIFO0_THRES)
+ ti_adc_read_data(sc);
+}
+
+static void
+ti_adc_tsc_intr_locked(struct ti_adc_softc *sc, uint32_t status)
+{
+ /* Read the available data. */
+ if (status & ADC_IRQ_FIFO1_THRES)
+ ti_adc_tsc_read_data(sc);
+
+}
+
+static void
+ti_adc_intr(void *arg)
+{
+ struct ti_adc_softc *sc;
+ uint32_t status, rawstatus;
+
+ sc = (struct ti_adc_softc *)arg;
+
+ TI_ADC_LOCK(sc);
+
+ rawstatus = ADC_READ4(sc, ADC_IRQSTATUS_RAW);
+ status = ADC_READ4(sc, ADC_IRQSTATUS);
+
+ if (rawstatus & ADC_IRQ_HW_PEN_ASYNC) {
+ sc->sc_pen_down = 1;
+ status |= ADC_IRQ_HW_PEN_ASYNC;
+ ADC_WRITE4(sc, ADC_IRQENABLE_CLR,
+ ADC_IRQ_HW_PEN_ASYNC);
+#ifdef EVDEV_SUPPORT
+ ti_adc_ev_report(sc);
+#endif
+ }
+
+ if (rawstatus & ADC_IRQ_PEN_UP) {
+ sc->sc_pen_down = 0;
+ status |= ADC_IRQ_PEN_UP;
+#ifdef EVDEV_SUPPORT
+ ti_adc_ev_report(sc);
+#endif
+ }
+
+ if (status & ADC_IRQ_FIFO0_THRES)
+ ti_adc_intr_locked(sc, status);
+
+ if (status & ADC_IRQ_FIFO1_THRES)
+ ti_adc_tsc_intr_locked(sc, status);
+
+ if (status) {
+ /* ACK the interrupt. */
+ ADC_WRITE4(sc, ADC_IRQSTATUS, status);
+ }
+
+ /* Start the next conversion ? */
+ if (status & ADC_IRQ_END_OF_SEQ)
+ ti_adc_setup(sc);
+
+ TI_ADC_UNLOCK(sc);
+}
+
+static void
+ti_adc_sysctl_init(struct ti_adc_softc *sc)
+{
+ char pinbuf[3];
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid *tree_node, *inp_node, *inpN_node;
+ struct sysctl_oid_list *tree, *inp_tree, *inpN_tree;
+ int ain, i;
+
+ /*
+ * Add per-pin sysctl tree/handlers.
+ */
+ ctx = device_get_sysctl_ctx(sc->sc_dev);
+ tree_node = device_get_sysctl_tree(sc->sc_dev);
+ tree = SYSCTL_CHILDREN(tree_node);
+ SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "clockdiv",
+ CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_NEEDGIANT, sc, 0,
+ ti_adc_clockdiv_proc, "IU", "ADC clock prescaler");
+ inp_node = SYSCTL_ADD_NODE(ctx, tree, OID_AUTO, "ain",
+ CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "ADC inputs");
+ inp_tree = SYSCTL_CHILDREN(inp_node);
+
+ for (i = 0; i < sc->sc_adc_nchannels; i++) {
+ ain = sc->sc_adc_channels[i];
+
+ snprintf(pinbuf, sizeof(pinbuf), "%d", ain);
+ inpN_node = SYSCTL_ADD_NODE(ctx, inp_tree, OID_AUTO, pinbuf,
+ CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "ADC input");
+ inpN_tree = SYSCTL_CHILDREN(inpN_node);
+
+ SYSCTL_ADD_PROC(ctx, inpN_tree, OID_AUTO, "enable",
+ CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_NEEDGIANT,
+ &ti_adc_inputs[ain], 0,
+ ti_adc_enable_proc, "IU", "Enable ADC input");
+ SYSCTL_ADD_PROC(ctx, inpN_tree, OID_AUTO, "open_delay",
+ CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_NEEDGIANT,
+ &ti_adc_inputs[ain], 0,
+ ti_adc_open_delay_proc, "IU", "ADC open delay");
+ SYSCTL_ADD_PROC(ctx, inpN_tree, OID_AUTO, "samples_avg",
+ CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_NEEDGIANT,
+ &ti_adc_inputs[ain], 0,
+ ti_adc_samples_avg_proc, "IU", "ADC samples average");
+ SYSCTL_ADD_INT(ctx, inpN_tree, OID_AUTO, "input",
+ CTLFLAG_RD, &ti_adc_inputs[ain].value, 0,
+ "Converted raw value for the ADC input");
+ }
+}
+
+static void
+ti_adc_inputs_init(struct ti_adc_softc *sc)
+{
+ int ain, i;
+ struct ti_adc_input *input;
+
+ TI_ADC_LOCK(sc);
+ for (i = 0; i < sc->sc_adc_nchannels; i++) {
+ ain = sc->sc_adc_channels[i];
+ input = &ti_adc_inputs[ain];
+ input->sc = sc;
+ input->input = ain;
+ input->value = 0;
+ input->enable = 0;
+ input->samples = 0;
+ ti_adc_input_setup(sc, ain);
+ }
+ TI_ADC_UNLOCK(sc);
+}
+
+static void
+ti_adc_tsc_init(struct ti_adc_softc *sc)
+{
+ int i, start_step, end_step;
+ uint32_t stepconfig, val;
+
+ TI_ADC_LOCK(sc);
+
+ /* X coordinates */
+ stepconfig = ADC_STEP_FIFO1 | (4 << ADC_STEP_AVG_SHIFT) |
+ ADC_STEP_MODE_HW_ONESHOT | sc->sc_xp_bit;
+ if (sc->sc_tsc_wires == 4)
+ stepconfig |= ADC_STEP_INP(sc->sc_yp_inp) | sc->sc_xn_bit;
+ else if (sc->sc_tsc_wires == 5)
+ stepconfig |= ADC_STEP_INP(4) |
+ sc->sc_xn_bit | sc->sc_yn_bit | sc->sc_yp_bit;
+ else if (sc->sc_tsc_wires == 8)
+ stepconfig |= ADC_STEP_INP(sc->sc_yp_inp) | sc->sc_xn_bit;
+
+ start_step = ADC_STEPS - sc->sc_coord_readouts + 1;
+ end_step = start_step + sc->sc_coord_readouts - 1;
+ for (i = start_step; i <= end_step; i++) {
+ ADC_WRITE4(sc, ADC_STEPCFG(i), stepconfig);
+ ADC_WRITE4(sc, ADC_STEPDLY(i), STEPDLY_OPEN);
+ }
+
+ /* Y coordinates */
+ stepconfig = ADC_STEP_FIFO1 | (4 << ADC_STEP_AVG_SHIFT) |
+ ADC_STEP_MODE_HW_ONESHOT | sc->sc_yn_bit |
+ ADC_STEP_INM(8);
+ if (sc->sc_tsc_wires == 4)
+ stepconfig |= ADC_STEP_INP(sc->sc_xp_inp) | sc->sc_yp_bit;
+ else if (sc->sc_tsc_wires == 5)
+ stepconfig |= ADC_STEP_INP(4) |
+ sc->sc_xp_bit | sc->sc_xn_bit | sc->sc_yp_bit;
+ else if (sc->sc_tsc_wires == 8)
+ stepconfig |= ADC_STEP_INP(sc->sc_xp_inp) | sc->sc_yp_bit;
+
+ start_step = ADC_STEPS - (sc->sc_coord_readouts*2 + 2) + 1;
+ end_step = start_step + sc->sc_coord_readouts - 1;
+ for (i = start_step; i <= end_step; i++) {
+ ADC_WRITE4(sc, ADC_STEPCFG(i), stepconfig);
+ ADC_WRITE4(sc, ADC_STEPDLY(i), STEPDLY_OPEN);
+ }
+
+ /* Charge config */
+ val = ADC_READ4(sc, ADC_IDLECONFIG);
+ ADC_WRITE4(sc, ADC_TC_CHARGE_STEPCONFIG, val);
+ ADC_WRITE4(sc, ADC_TC_CHARGE_DELAY, sc->sc_charge_delay);
+
+ /* 2 steps for Z */
+ start_step = ADC_STEPS - (sc->sc_coord_readouts + 2) + 1;
+ stepconfig = ADC_STEP_FIFO1 | (4 << ADC_STEP_AVG_SHIFT) |
+ ADC_STEP_MODE_HW_ONESHOT | sc->sc_yp_bit |
+ sc->sc_xn_bit | ADC_STEP_INP(sc->sc_xp_inp) |
+ ADC_STEP_INM(8);
+ ADC_WRITE4(sc, ADC_STEPCFG(start_step), stepconfig);
+ ADC_WRITE4(sc, ADC_STEPDLY(start_step), STEPDLY_OPEN);
+ start_step++;
+ stepconfig |= ADC_STEP_INP(sc->sc_yn_inp);
+ ADC_WRITE4(sc, ADC_STEPCFG(start_step), stepconfig);
+ ADC_WRITE4(sc, ADC_STEPDLY(start_step), STEPDLY_OPEN);
+
+ ADC_WRITE4(sc, ADC_FIFO1THRESHOLD, (sc->sc_coord_readouts*2 + 2) - 1);
+
+ sc->sc_tsc_enabled = 1;
+ start_step = ADC_STEPS - (sc->sc_coord_readouts*2 + 2) + 1;
+ end_step = ADC_STEPS;
+ for (i = start_step; i <= end_step; i++) {
+ sc->sc_tsc_enabled |= (1 << i);
+ }
+
+ TI_ADC_UNLOCK(sc);
+}
+
+static void
+ti_adc_idlestep_init(struct ti_adc_softc *sc)
+{
+ uint32_t val;
+
+ val = ADC_STEP_YNN_SW | ADC_STEP_INM(8) | ADC_STEP_INP(8) | ADC_STEP_YPN_SW;
+
+ ADC_WRITE4(sc, ADC_IDLECONFIG, val);
+}
+
+static int
+ti_adc_config_wires(struct ti_adc_softc *sc, int *wire_configs, int nwire_configs)
+{
+ int i;
+ int wire, ai;
+
+ for (i = 0; i < nwire_configs; i++) {
+ wire = wire_configs[i] & 0xf;
+ ai = (wire_configs[i] >> 4) & 0xf;
+ switch (wire) {
+ case ORDER_XP:
+ sc->sc_xp_bit = ADC_STEP_XPP_SW;
+ sc->sc_xp_inp = ai;
+ break;
+ case ORDER_XN:
+ sc->sc_xn_bit = ADC_STEP_XNN_SW;
+ sc->sc_xn_inp = ai;
+ break;
+ case ORDER_YP:
+ sc->sc_yp_bit = ADC_STEP_YPP_SW;
+ sc->sc_yp_inp = ai;
+ break;
+ case ORDER_YN:
+ sc->sc_yn_bit = ADC_STEP_YNN_SW;
+ sc->sc_yn_inp = ai;
+ break;
+ default:
+ device_printf(sc->sc_dev, "Invalid wire config\n");
+ return (-1);
+ }
+ }
+ return (0);
+}
+
+static int
+ti_adc_probe(device_t dev)
+{
+
+ if (!ofw_bus_is_compatible(dev, "ti,am3359-tscadc"))
+ return (ENXIO);
+ device_set_desc(dev, "TI ADC controller");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ti_adc_attach(device_t dev)
+{
+ int err, rid, i;
+ struct ti_adc_softc *sc;
+ uint32_t rev, reg;
+ phandle_t node, child;
+ pcell_t cell;
+ int *channels;
+ int nwire_configs;
+ int *wire_configs;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+
+ node = ofw_bus_get_node(dev);
+
+ sc->sc_tsc_wires = 0;
+ sc->sc_coord_readouts = 1;
+ sc->sc_x_plate_resistance = 0;
+ sc->sc_charge_delay = DEFAULT_CHARGE_DELAY;
+ /* Read "tsc" node properties */
+ child = ofw_bus_find_child(node, "tsc");
+ if (child != 0 && OF_hasprop(child, "ti,wires")) {
+ if ((OF_getencprop(child, "ti,wires", &cell, sizeof(cell))) > 0)
+ sc->sc_tsc_wires = cell;
+ if ((OF_getencprop(child, "ti,coordinate-readouts", &cell,
+ sizeof(cell))) > 0)
+ sc->sc_coord_readouts = cell;
+ if ((OF_getencprop(child, "ti,x-plate-resistance", &cell,
+ sizeof(cell))) > 0)
+ sc->sc_x_plate_resistance = cell;
+ if ((OF_getencprop(child, "ti,charge-delay", &cell,
+ sizeof(cell))) > 0)
+ sc->sc_charge_delay = cell;
+ nwire_configs = OF_getencprop_alloc_multi(child,
+ "ti,wire-config", sizeof(*wire_configs),
+ (void **)&wire_configs);
+ if (nwire_configs != sc->sc_tsc_wires) {
+ device_printf(sc->sc_dev,
+ "invalid number of ti,wire-config: %d (should be %d)\n",
+ nwire_configs, sc->sc_tsc_wires);
+ OF_prop_free(wire_configs);
+ return (EINVAL);
+ }
+ err = ti_adc_config_wires(sc, wire_configs, nwire_configs);
+ OF_prop_free(wire_configs);
+ if (err)
+ return (EINVAL);
+ }
+
+ /* Read "adc" node properties */
+ child = ofw_bus_find_child(node, "adc");
+ if (child != 0) {
+ sc->sc_adc_nchannels = OF_getencprop_alloc_multi(child,
+ "ti,adc-channels", sizeof(*channels), (void **)&channels);
+ if (sc->sc_adc_nchannels > 0) {
+ for (i = 0; i < sc->sc_adc_nchannels; i++)
+ sc->sc_adc_channels[i] = channels[i];
+ OF_prop_free(channels);
+ }
+ }
+
+ /* Sanity check FDT data */
+ if (sc->sc_tsc_wires + sc->sc_adc_nchannels > TI_ADC_NPINS) {
+ device_printf(dev, "total number of chanels (%d) is larger than %d\n",
+ sc->sc_tsc_wires + sc->sc_adc_nchannels, TI_ADC_NPINS);
+ return (ENXIO);
+ }
+
+ rid = 0;
+ sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (!sc->sc_mem_res) {
+ device_printf(dev, "cannot allocate memory window\n");
+ return (ENXIO);
+ }
+
+ /* Activate the ADC_TSC module. */
+ err = ti_sysc_clock_enable(device_get_parent(dev));
+ if (err)
+ return (err);
+
+ rid = 0;
+ sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (!sc->sc_irq_res) {
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+ device_printf(dev, "cannot allocate interrupt\n");
+ return (ENXIO);
+ }
+
+ if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+ NULL, ti_adc_intr, sc, &sc->sc_intrhand) != 0) {
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+ device_printf(dev, "Unable to setup the irq handler.\n");
+ return (ENXIO);
+ }
+
+ /* Check the ADC revision. */
+ rev = ADC_READ4(sc, ti_sysc_get_rev_address_offset_host(device_get_parent(dev)));
+ device_printf(dev,
+ "scheme: %#x func: %#x rtl: %d rev: %d.%d custom rev: %d\n",
+ (rev & ADC_REV_SCHEME_MSK) >> ADC_REV_SCHEME_SHIFT,
+ (rev & ADC_REV_FUNC_MSK) >> ADC_REV_FUNC_SHIFT,
+ (rev & ADC_REV_RTL_MSK) >> ADC_REV_RTL_SHIFT,
+ (rev & ADC_REV_MAJOR_MSK) >> ADC_REV_MAJOR_SHIFT,
+ rev & ADC_REV_MINOR_MSK,
+ (rev & ADC_REV_CUSTOM_MSK) >> ADC_REV_CUSTOM_SHIFT);
+
+ reg = ADC_READ4(sc, ADC_CTRL);
+ ADC_WRITE4(sc, ADC_CTRL, reg | ADC_CTRL_STEP_WP | ADC_CTRL_STEP_ID);
+
+ /*
+ * Set the ADC prescaler to 2400 if touchscreen is not enabled
+ * and to 24 if it is. This sets the ADC clock to ~10Khz and
+ * ~1Mhz respectively (CLK_M_OSC / prescaler).
+ */
+ if (sc->sc_tsc_wires)
+ ADC_WRITE4(sc, ADC_CLKDIV, 24 - 1);
+ else
+ ADC_WRITE4(sc, ADC_CLKDIV, 2400 - 1);
+
+ TI_ADC_LOCK_INIT(sc);
+
+ ti_adc_idlestep_init(sc);
+ ti_adc_inputs_init(sc);
+ ti_adc_sysctl_init(sc);
+ ti_adc_tsc_init(sc);
+
+ TI_ADC_LOCK(sc);
+ ti_adc_setup(sc);
+ TI_ADC_UNLOCK(sc);
+
+#ifdef EVDEV_SUPPORT
+ if (sc->sc_tsc_wires > 0) {
+ sc->sc_evdev = evdev_alloc();
+ evdev_set_name(sc->sc_evdev, device_get_desc(dev));
+ evdev_set_phys(sc->sc_evdev, device_get_nameunit(dev));
+ evdev_set_id(sc->sc_evdev, BUS_VIRTUAL, 0, 0, 0);
+ evdev_support_prop(sc->sc_evdev, INPUT_PROP_DIRECT);
+ evdev_support_event(sc->sc_evdev, EV_SYN);
+ evdev_support_event(sc->sc_evdev, EV_ABS);
+ evdev_support_event(sc->sc_evdev, EV_KEY);
+
+ evdev_support_abs(sc->sc_evdev, ABS_X, 0,
+ ADC_MAX_VALUE, 0, 0, 0);
+ evdev_support_abs(sc->sc_evdev, ABS_Y, 0,
+ ADC_MAX_VALUE, 0, 0, 0);
+
+ evdev_support_key(sc->sc_evdev, BTN_TOUCH);
+
+ err = evdev_register(sc->sc_evdev);
+ if (err) {
+ device_printf(dev,
+ "failed to register evdev: error=%d\n", err);
+ ti_adc_detach(dev);
+ return (err);
+ }
+
+ sc->sc_pen_down = 0;
+ sc->sc_x = -1;
+ sc->sc_y = -1;
+ }
+#endif /* EVDEV */
+
+ return (0);
+}
+
+static int
+ti_adc_detach(device_t dev)
+{
+ struct ti_adc_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ /* Turn off the ADC. */
+ TI_ADC_LOCK(sc);
+ ti_adc_reset(sc);
+ ti_adc_setup(sc);
+
+#ifdef EVDEV_SUPPORT
+ evdev_free(sc->sc_evdev);
+#endif
+
+ TI_ADC_UNLOCK(sc);
+
+ TI_ADC_LOCK_DESTROY(sc);
+
+ if (sc->sc_intrhand)
+ bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand);
+ if (sc->sc_irq_res)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
+ if (sc->sc_mem_res)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+
+ return (bus_generic_detach(dev));
+}
+
+static device_method_t ti_adc_methods[] = {
+ DEVMETHOD(device_probe, ti_adc_probe),
+ DEVMETHOD(device_attach, ti_adc_attach),
+ DEVMETHOD(device_detach, ti_adc_detach),
+
+ DEVMETHOD_END
+};
+
+static driver_t ti_adc_driver = {
+ "ti_adc",
+ ti_adc_methods,
+ sizeof(struct ti_adc_softc),
+};
+
+static devclass_t ti_adc_devclass;
+
+DRIVER_MODULE(ti_adc, simplebus, ti_adc_driver, ti_adc_devclass, 0, 0);
+MODULE_VERSION(ti_adc, 1);
+MODULE_DEPEND(ti_adc, simplebus, 1, 1, 1);
+MODULE_DEPEND(ti_adc, ti_sysc, 1, 1, 1);
+#ifdef EVDEV_SUPPORT
+MODULE_DEPEND(ti_adc, evdev, 1, 1, 1);
+#endif
diff --git a/sys/arm/ti/ti_adcreg.h b/sys/arm/ti/ti_adcreg.h
new file mode 100644
index 000000000000..bc3b9729476f
--- /dev/null
+++ b/sys/arm/ti/ti_adcreg.h
@@ -0,0 +1,127 @@
+/*-
+ * Copyright 2014 Luiz Otavio O Souza <loos@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _TI_ADCREG_H_
+#define _TI_ADCREG_H_
+
+#define ADC_REVISION 0x000
+#define ADC_REV_SCHEME_MSK 0xc0000000
+#define ADC_REV_SCHEME_SHIFT 30
+#define ADC_REV_FUNC_MSK 0x0fff0000
+#define ADC_REV_FUNC_SHIFT 16
+#define ADC_REV_RTL_MSK 0x0000f800
+#define ADC_REV_RTL_SHIFT 11
+#define ADC_REV_MAJOR_MSK 0x00000700
+#define ADC_REV_MAJOR_SHIFT 8
+#define ADC_REV_CUSTOM_MSK 0x000000c0
+#define ADC_REV_CUSTOM_SHIFT 6
+#define ADC_REV_MINOR_MSK 0x0000003f
+#define ADC_SYSCFG 0x010
+#define ADC_SYSCFG_IDLE_MSK 0x000000c0
+#define ADC_SYSCFG_IDLE_SHIFT 2
+#define ADC_IRQSTATUS_RAW 0x024
+#define ADC_IRQSTATUS 0x028
+#define ADC_IRQENABLE_SET 0x02c
+#define ADC_IRQENABLE_CLR 0x030
+#define ADC_IRQ_HW_PEN_SYNC (1 << 10)
+#define ADC_IRQ_PEN_UP (1 << 9)
+#define ADC_IRQ_OUT_RANGE (1 << 8)
+#define ADC_IRQ_FIFO1_UNDR (1 << 7)
+#define ADC_IRQ_FIFO1_OVERR (1 << 6)
+#define ADC_IRQ_FIFO1_THRES (1 << 5)
+#define ADC_IRQ_FIFO0_UNDR (1 << 4)
+#define ADC_IRQ_FIFO0_OVERR (1 << 3)
+#define ADC_IRQ_FIFO0_THRES (1 << 2)
+#define ADC_IRQ_END_OF_SEQ (1 << 1)
+#define ADC_IRQ_HW_PEN_ASYNC (1 << 0)
+#define ADC_CTRL 0x040
+#define ADC_CTRL_TSC_ENABLE (1 << 7)
+#define ADC_CTRL_TSC_4WIRE (1 << 5)
+#define ADC_CTRL_TSC_5WIRE (2 << 5)
+#define ADC_CTRL_TSC_8WIRE (3 << 5)
+#define ADC_CTRL_STEP_WP (1 << 2)
+#define ADC_CTRL_STEP_ID (1 << 1)
+#define ADC_CTRL_ENABLE (1 << 0)
+#define ADC_STAT 0x044
+#define ADC_CLKDIV 0x04c
+#define ADC_STEPENABLE 0x054
+#define ADC_IDLECONFIG 0x058
+#define ADC_TC_CHARGE_STEPCONFIG 0x05C
+#define ADC_TC_CHARGE_DELAY 0x060
+#define ADC_STEPS 16
+#define ADC_STEPCFG(n) (0x064 + (8*((n)-1)))
+#define ADC_STEPDLY(n) (0x068 + (8*((n)-1)))
+#define ADC_STEP_FIFO1 (1 << 26)
+#define ADC_STEP_DIFF_CNTRL (1 << 25)
+#define ADC_STEP_RFM_MSK 0x01800000
+#define ADC_STEP_RFM_SHIFT 23
+#define ADC_STEP_RFM_VSSA 0
+#define ADC_STEP_RFM_XNUR 1
+#define ADC_STEP_RFM_YNLR 2
+#define ADC_STEP_RFM_VREFN 3
+#define ADC_STEP_INP_MSK 0x00780000
+#define ADC_STEP_INP_SHIFT 19
+#define ADC_STEP_INP(i) ((i) << ADC_STEP_INP_SHIFT)
+#define ADC_STEP_INM_MSK 0x00078000
+#define ADC_STEP_INM_SHIFT 15
+#define ADC_STEP_INM(i) ((i) << ADC_STEP_INM_SHIFT)
+#define ADC_STEP_IN_VREFN 8
+#define ADC_STEP_RFP_MSK 0x00007000
+#define ADC_STEP_RFP_SHIFT 12
+#define ADC_STEP_RFP_VDDA 0
+#define ADC_STEP_RFP_XPUL 1
+#define ADC_STEP_RFP_YPLL 2
+#define ADC_STEP_RFP_VREFP 3
+#define ADC_STEP_RFP_INTREF 4
+#define ADC_STEP_YPN_SW (1 << 10)
+#define ADC_STEP_YNN_SW (1 << 8)
+#define ADC_STEP_YPP_SW (1 << 7)
+#define ADC_STEP_XNN_SW (1 << 6)
+#define ADC_STEP_XPP_SW (1 << 5)
+#define ADC_STEP_AVG_MSK 0x0000001c
+#define ADC_STEP_AVG_SHIFT 2
+#define ADC_STEP_MODE_MSK 0x00000003
+#define ADC_STEP_MODE_ONESHOT 0x00000000
+#define ADC_STEP_MODE_CONTINUOUS 0x00000001
+#define ADC_STEP_MODE_HW_ONESHOT 0x00000002
+#define ADC_STEP_MODE_HW_CONTINUOUS 0x00000003
+#define ADC_STEP_SAMPLE_DELAY 0xff000000
+#define ADC_STEP_OPEN_DELAY 0x0003ffff
+#define ADC_FIFO0COUNT 0x0e4
+#define ADC_FIFO0THRESHOLD 0x0e8
+#define ADC_FIFO1COUNT 0x0f0
+#define ADC_FIFO1THRESHOLD 0x0f4
+#define ADC_FIFO0DATA 0x100
+#define ADC_FIFO1DATA 0x200
+#define ADC_FIFO_COUNT_MSK 0x0000007f
+#define ADC_FIFO_STEP_ID_MSK 0x000f0000
+#define ADC_FIFO_STEP_ID_SHIFT 16
+#define ADC_FIFO_DATA_MSK 0x00000fff
+#define ADC_MAX_VALUE 0xfff
+
+#endif /* _TI_ADCREG_H_ */
diff --git a/sys/arm/ti/ti_adcvar.h b/sys/arm/ti/ti_adcvar.h
new file mode 100644
index 000000000000..b9cc0f0b450b
--- /dev/null
+++ b/sys/arm/ti/ti_adcvar.h
@@ -0,0 +1,87 @@
+/*-
+ * Copyright 2014 Luiz Otavio O Souza <loos@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _TI_ADCVAR_H_
+#define _TI_ADCVAR_H_
+
+#define TI_ADC_NPINS 8
+
+#define ADC_READ4(_sc, reg) bus_read_4((_sc)->sc_mem_res, reg)
+#define ADC_WRITE4(_sc, reg, value) \
+ bus_write_4((_sc)->sc_mem_res, reg, value)
+
+struct ti_adc_softc {
+ device_t sc_dev;
+ int sc_last_state;
+ struct mtx sc_mtx;
+ struct resource *sc_mem_res;
+ struct resource *sc_irq_res;
+ void *sc_intrhand;
+ int sc_tsc_wires;
+ int sc_tsc_wire_config[TI_ADC_NPINS];
+ int sc_coord_readouts;
+ int sc_x_plate_resistance;
+ int sc_charge_delay;
+ int sc_adc_nchannels;
+ int sc_adc_channels[TI_ADC_NPINS];
+ int sc_xp_bit, sc_xp_inp;
+ int sc_xn_bit, sc_xn_inp;
+ int sc_yp_bit, sc_yp_inp;
+ int sc_yn_bit, sc_yn_inp;
+ uint32_t sc_tsc_enabled;
+ int sc_pen_down;
+#ifdef EVDEV_SUPPORT
+ int sc_x;
+ int sc_y;
+ struct evdev_dev *sc_evdev;
+#endif
+};
+
+struct ti_adc_input {
+ int32_t enable; /* input enabled */
+ int32_t samples; /* samples average */
+ int32_t input; /* input number */
+ int32_t value; /* raw converted value */
+ uint32_t stepconfig; /* step config register */
+ uint32_t stepdelay; /* step delay register */
+ struct ti_adc_softc *sc; /* pointer to adc softc */
+};
+
+#define TI_ADC_LOCK(_sc) \
+ mtx_lock(&(_sc)->sc_mtx)
+#define TI_ADC_UNLOCK(_sc) \
+ mtx_unlock(&(_sc)->sc_mtx)
+#define TI_ADC_LOCK_INIT(_sc) \
+ mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \
+ "ti_adc", MTX_DEF)
+#define TI_ADC_LOCK_DESTROY(_sc) \
+ mtx_destroy(&_sc->sc_mtx);
+#define TI_ADC_LOCK_ASSERT(_sc) \
+ mtx_assert(&(_sc)->sc_mtx, MA_OWNED)
+
+#endif /* _TI_ADCVAR_H_ */
diff --git a/sys/arm/ti/ti_cpuid.c b/sys/arm/ti/ti_cpuid.c
new file mode 100644
index 000000000000..b965f99943f9
--- /dev/null
+++ b/sys/arm/ti/ti_cpuid.c
@@ -0,0 +1,292 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2011
+ * Ben Gray <ben.r.gray@gmail.com>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/fdt.h>
+#include <machine/resource.h>
+#include <machine/intr.h>
+
+#include <dev/fdt/simplebus.h>
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/ti/tivar.h>
+#include <arm/ti/ti_cpuid.h>
+
+#include <arm/ti/omap4/omap4_reg.h>
+#include <arm/ti/am335x/am335x_reg.h>
+
+#define OMAP4_STD_FUSE_DIE_ID_0 0x2200
+#define OMAP4_ID_CODE 0x2204
+#define OMAP4_STD_FUSE_DIE_ID_1 0x2208
+#define OMAP4_STD_FUSE_DIE_ID_2 0x220C
+#define OMAP4_STD_FUSE_DIE_ID_3 0x2210
+#define OMAP4_STD_FUSE_PROD_ID_0 0x2214
+#define OMAP4_STD_FUSE_PROD_ID_1 0x2218
+
+#define OMAP3_ID_CODE 0xA204
+
+static uint32_t chip_revision = 0xffffffff;
+
+/**
+ * ti_revision - Returns the revision number of the device
+ *
+ * Simply returns an identifier for the revision of the chip we are running
+ * on.
+ *
+ * RETURNS
+ * A 32-bit identifier for the current chip
+ */
+uint32_t
+ti_revision(void)
+{
+ return chip_revision;
+}
+
+/**
+ * omap4_get_revision - determines omap4 revision
+ *
+ * Reads the registers to determine the revision of the chip we are currently
+ * running on. Stores the information in global variables.
+ *
+ *
+ */
+static void
+omap4_get_revision(void)
+{
+ uint32_t id_code;
+ uint32_t revision;
+ uint32_t hawkeye;
+ bus_space_handle_t bsh;
+
+ /* The chip revsion is read from the device identification registers and
+ * the JTAG (?) tap registers, which are located in address 0x4A00_2200 to
+ * 0x4A00_2218. This is part of the L4_CORE memory range and should have
+ * been mapped in by the machdep.c code.
+ *
+ * STD_FUSE_DIE_ID_0 0x4A00 2200
+ * ID_CODE 0x4A00 2204 (this is the only one we need)
+ * STD_FUSE_DIE_ID_1 0x4A00 2208
+ * STD_FUSE_DIE_ID_2 0x4A00 220C
+ * STD_FUSE_DIE_ID_3 0x4A00 2210
+ * STD_FUSE_PROD_ID_0 0x4A00 2214
+ * STD_FUSE_PROD_ID_1 0x4A00 2218
+ */
+ /* FIXME Should we map somewhere else? */
+ bus_space_map(fdtbus_bs_tag,OMAP44XX_L4_CORE_HWBASE, 0x4000, 0, &bsh);
+ id_code = bus_space_read_4(fdtbus_bs_tag, bsh, OMAP4_ID_CODE);
+ bus_space_unmap(fdtbus_bs_tag, bsh, 0x4000);
+
+ hawkeye = ((id_code >> 12) & 0xffff);
+ revision = ((id_code >> 28) & 0xf);
+
+ /* Apparently according to the linux code there were some ES2.0 samples that
+ * have the wrong id code and report themselves as ES1.0 silicon. So used
+ * the ARM cpuid to get the correct revision.
+ */
+ if (revision == 0) {
+ id_code = cp15_midr_get();
+ revision = (id_code & 0xf) - 1;
+ }
+
+ switch (hawkeye) {
+ case 0xB852:
+ switch (revision) {
+ case 0:
+ chip_revision = OMAP4430_REV_ES1_0;
+ break;
+ case 1:
+ chip_revision = OMAP4430_REV_ES2_1;
+ break;
+ default:
+ chip_revision = OMAP4430_REV_UNKNOWN;
+ break;
+ }
+ break;
+
+ case 0xB95C:
+ switch (revision) {
+ case 3:
+ chip_revision = OMAP4430_REV_ES2_1;
+ break;
+ case 4:
+ chip_revision = OMAP4430_REV_ES2_2;
+ break;
+ case 6:
+ chip_revision = OMAP4430_REV_ES2_3;
+ break;
+ default:
+ chip_revision = OMAP4430_REV_UNKNOWN;
+ break;
+ }
+ break;
+
+ case 0xB94E:
+ switch (revision) {
+ case 0:
+ chip_revision = OMAP4460_REV_ES1_0;
+ break;
+ case 2:
+ chip_revision = OMAP4460_REV_ES1_1;
+ break;
+ default:
+ chip_revision = OMAP4460_REV_UNKNOWN;
+ break;
+ }
+ break;
+
+ case 0xB975:
+ switch (revision) {
+ case 0:
+ chip_revision = OMAP4470_REV_ES1_0;
+ break;
+ default:
+ chip_revision = OMAP4470_REV_UNKNOWN;
+ break;
+ }
+ break;
+
+ default:
+ /* Default to the latest revision if we can't determine type */
+ chip_revision = OMAP_UNKNOWN_DEV;
+ break;
+ }
+ if (chip_revision != OMAP_UNKNOWN_DEV) {
+ printf("Texas Instruments OMAP%04x Processor, Revision ES%u.%u\n",
+ OMAP_REV_DEVICE(chip_revision), OMAP_REV_MAJOR(chip_revision),
+ OMAP_REV_MINOR(chip_revision));
+ }
+ else {
+ printf("Texas Instruments unknown OMAP chip: %04x, rev %d\n",
+ hawkeye, revision);
+ }
+}
+
+static void
+am335x_get_revision(void)
+{
+ uint32_t dev_feature;
+ char cpu_last_char;
+ bus_space_handle_t bsh;
+ int major;
+ int minor;
+
+ bus_space_map(fdtbus_bs_tag, AM335X_CONTROL_BASE, AM335X_CONTROL_SIZE, 0, &bsh);
+ chip_revision = bus_space_read_4(fdtbus_bs_tag, bsh, AM335X_CONTROL_DEVICE_ID);
+ dev_feature = bus_space_read_4(fdtbus_bs_tag, bsh, AM335X_CONTROL_DEV_FEATURE);
+ bus_space_unmap(fdtbus_bs_tag, bsh, AM335X_CONTROL_SIZE);
+
+ switch (dev_feature) {
+ case 0x00FF0382:
+ cpu_last_char='2';
+ break;
+ case 0x20FF0382:
+ cpu_last_char='4';
+ break;
+ case 0x00FF0383:
+ cpu_last_char='6';
+ break;
+ case 0x00FE0383:
+ cpu_last_char='7';
+ break;
+ case 0x20FF0383:
+ cpu_last_char='8';
+ break;
+ case 0x20FE0383:
+ cpu_last_char='9';
+ break;
+ default:
+ cpu_last_char='x';
+ }
+
+ switch(AM335X_DEVREV(chip_revision)) {
+ case 0:
+ major = 1;
+ minor = 0;
+ break;
+ case 1:
+ major = 2;
+ minor = 0;
+ break;
+ case 2:
+ major = 2;
+ minor = 1;
+ break;
+ default:
+ major = 0;
+ minor = AM335X_DEVREV(chip_revision);
+ break;
+ }
+ printf("Texas Instruments AM335%c Processor, Revision ES%u.%u\n",
+ cpu_last_char, major, minor);
+}
+
+/**
+ * ti_cpu_ident - attempts to identify the chip we are running on
+ * @dummy: ignored
+ *
+ * This function is called before any of the driver are initialised, however
+ * the basic virt to phys maps have been setup in machdep.c so we can still
+ * access the required registers, we just have to use direct register reads
+ * and writes rather than going through the bus stuff.
+ *
+ *
+ */
+static void
+ti_cpu_ident(void *dummy)
+{
+ if (!ti_soc_is_supported())
+ return;
+ switch(ti_chip()) {
+ case CHIP_OMAP_4:
+ omap4_get_revision();
+ break;
+ case CHIP_AM335X:
+ am335x_get_revision();
+ break;
+ default:
+ panic("Unknown chip type, fixme!\n");
+ }
+}
+
+SYSINIT(ti_cpu_ident, SI_SUB_CPU, SI_ORDER_SECOND, ti_cpu_ident, NULL);
diff --git a/sys/arm/ti/ti_cpuid.h b/sys/arm/ti/ti_cpuid.h
new file mode 100644
index 000000000000..fd84599ea7cd
--- /dev/null
+++ b/sys/arm/ti/ti_cpuid.h
@@ -0,0 +1,91 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2011
+ * Ben Gray <ben.r.gray@gmail.com>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _TI_CPUID_H_
+#define _TI_CPUID_H_
+
+#define OMAP_MAKEREV(d, a, b, c) \
+ (uint32_t)(((d) << 16) | (((a) & 0xf) << 8) | (((b) & 0xf) << 4) | ((c) & 0xf))
+
+#define OMAP_REV_DEVICE(x) (((x) >> 16) & 0xffff)
+#define OMAP_REV_MAJOR(x) (((x) >> 8) & 0xf)
+#define OMAP_REV_MINOR(x) (((x) >> 4) & 0xf)
+#define OMAP_REV_MINOR_MINOR(x) (((x) >> 0) & 0xf)
+
+#define OMAP3350_DEV 0x3530
+#define OMAP3350_REV_ES1_0 OMAP_MAKEREV(OMAP3350_DEV, 1, 0, 0)
+#define OMAP3530_REV_ES2_0 OMAP_MAKEREV(OMAP3350_DEV, 2, 0, 0)
+#define OMAP3530_REV_ES2_1 OMAP_MAKEREV(OMAP3350_DEV, 2, 1, 0)
+#define OMAP3530_REV_ES3_0 OMAP_MAKEREV(OMAP3350_DEV, 3, 0, 0)
+#define OMAP3530_REV_ES3_1 OMAP_MAKEREV(OMAP3350_DEV, 3, 1, 0)
+#define OMAP3530_REV_ES3_1_2 OMAP_MAKEREV(OMAP3350_DEV, 3, 1, 2)
+
+#define OMAP4430_DEV 0x4430
+#define OMAP4430_REV_ES1_0 OMAP_MAKEREV(OMAP4430_DEV, 1, 0, 0)
+#define OMAP4430_REV_ES2_0 OMAP_MAKEREV(OMAP4430_DEV, 2, 0, 0)
+#define OMAP4430_REV_ES2_1 OMAP_MAKEREV(OMAP4430_DEV, 2, 1, 0)
+#define OMAP4430_REV_ES2_2 OMAP_MAKEREV(OMAP4430_DEV, 2, 2, 0)
+#define OMAP4430_REV_ES2_3 OMAP_MAKEREV(OMAP4430_DEV, 2, 3, 0)
+#define OMAP4430_REV_UNKNOWN OMAP_MAKEREV(OMAP4430_DEV, 9, 9, 9)
+
+#define OMAP4460_DEV 0x4460
+#define OMAP4460_REV_ES1_0 OMAP_MAKEREV(OMAP4460_DEV, 1, 0, 0)
+#define OMAP4460_REV_ES1_1 OMAP_MAKEREV(OMAP4460_DEV, 1, 1, 0)
+#define OMAP4460_REV_UNKNOWN OMAP_MAKEREV(OMAP4460_DEV, 9, 9, 9)
+
+#define OMAP4470_DEV 0x4470
+#define OMAP4470_REV_ES1_0 OMAP_MAKEREV(OMAP4470_DEV, 1, 0, 0)
+#define OMAP4470_REV_UNKNOWN OMAP_MAKEREV(OMAP4470_DEV, 9, 9, 9)
+
+#define OMAP_UNKNOWN_DEV OMAP_MAKEREV(0x9999, 9, 9, 9)
+
+#define AM335X_DEVREV(x) ((x) >> 28)
+
+#define CHIP_OMAP_4 0
+#define CHIP_AM335X 1
+
+extern int _ti_chip;
+
+static __inline int ti_chip(void)
+{
+ KASSERT(_ti_chip != -1, ("Can't determine TI Chip"));
+ return _ti_chip;
+}
+
+uint32_t ti_revision(void);
+
+static __inline bool ti_soc_is_supported(void)
+{
+
+ return (_ti_chip != -1);
+}
+
+#endif /* _TI_CPUID_H_ */
diff --git a/sys/arm/ti/ti_edma3.c b/sys/arm/ti/ti_edma3.c
new file mode 100644
index 000000000000..436f983baa5b
--- /dev/null
+++ b/sys/arm/ti/ti_edma3.c
@@ -0,0 +1,424 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/
+ * Copyright (c) 2012 Damjan Marion <dmarion@Freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of authors nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/endian.h>
+#include <sys/mbuf.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <sys/sockio.h>
+#include <sys/bus.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <machine/resource.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/ti/ti_scm.h>
+#include <arm/ti/ti_sysc.h>
+
+#include <arm/ti/ti_edma3.h>
+
+#define TI_EDMA3_NUM_TCS 3
+#define TI_EDMA3_NUM_IRQS 3
+#define TI_EDMA3_NUM_DMA_CHS 64
+#define TI_EDMA3_NUM_QDMA_CHS 8
+
+#define TI_EDMA3CC_PID 0x000
+#define TI_EDMA3CC_DCHMAP(p) (0x100 + ((p)*4))
+#define TI_EDMA3CC_DMAQNUM(n) (0x240 + ((n)*4))
+#define TI_EDMA3CC_QDMAQNUM 0x260
+#define TI_EDMA3CC_EMCR 0x308
+#define TI_EDMA3CC_EMCRH 0x30C
+#define TI_EDMA3CC_QEMCR 0x314
+#define TI_EDMA3CC_CCERR 0x318
+#define TI_EDMA3CC_CCERRCLR 0x31C
+#define TI_EDMA3CC_DRAE(p) (0x340 + ((p)*8))
+#define TI_EDMA3CC_DRAEH(p) (0x344 + ((p)*8))
+#define TI_EDMA3CC_QRAE(p) (0x380 + ((p)*4))
+#define TI_EDMA3CC_S_ESR(p) (0x2010 + ((p)*0x200))
+#define TI_EDMA3CC_S_ESRH(p) (0x2014 + ((p)*0x200))
+#define TI_EDMA3CC_S_SECR(p) (0x2040 + ((p)*0x200))
+#define TI_EDMA3CC_S_SECRH(p) (0x2044 + ((p)*0x200))
+#define TI_EDMA3CC_S_EESR(p) (0x2030 + ((p)*0x200))
+#define TI_EDMA3CC_S_EESRH(p) (0x2034 + ((p)*0x200))
+#define TI_EDMA3CC_S_IESR(p) (0x2060 + ((p)*0x200))
+#define TI_EDMA3CC_S_IESRH(p) (0x2064 + ((p)*0x200))
+#define TI_EDMA3CC_S_IPR(p) (0x2068 + ((p)*0x200))
+#define TI_EDMA3CC_S_IPRH(p) (0x206C + ((p)*0x200))
+#define TI_EDMA3CC_S_QEESR(p) (0x208C + ((p)*0x200))
+
+#define TI_EDMA3CC_PARAM_OFFSET 0x4000
+#define TI_EDMA3CC_OPT(p) (TI_EDMA3CC_PARAM_OFFSET + 0x0 + ((p)*0x20))
+
+#define TI_EDMA3CC_DMAQNUM_SET(c,q) ((0x7 & (q)) << (((c) % 8) * 4))
+#define TI_EDMA3CC_DMAQNUM_CLR(c) (~(0x7 << (((c) % 8) * 4)))
+#define TI_EDMA3CC_QDMAQNUM_SET(c,q) ((0x7 & (q)) << ((c) * 4))
+#define TI_EDMA3CC_QDMAQNUM_CLR(c) (~(0x7 << ((c) * 4)))
+
+#define TI_EDMA3CC_OPT_TCC_CLR (~(0x3F000))
+#define TI_EDMA3CC_OPT_TCC_SET(p) (((0x3F000 >> 12) & (p)) << 12)
+
+struct ti_edma3_softc {
+ device_t sc_dev;
+ /*
+ * We use one-element array in case if we need to add
+ * mem resources for transfer control windows
+ */
+ struct resource * mem_res[1];
+ struct resource * irq_res[TI_EDMA3_NUM_IRQS];
+ void *ih_cookie[TI_EDMA3_NUM_IRQS];
+};
+
+static struct ti_edma3_softc *ti_edma3_sc = NULL;
+
+static struct resource_spec ti_edma3_mem_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { -1, 0, 0 }
+};
+static struct resource_spec ti_edma3_irq_spec[] = {
+ { SYS_RES_IRQ, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 1, RF_ACTIVE },
+ { SYS_RES_IRQ, 2, RF_ACTIVE },
+ { -1, 0, 0 }
+};
+
+/* Read/Write macros */
+#define ti_edma3_cc_rd_4(reg) bus_read_4(ti_edma3_sc->mem_res[0], reg)
+#define ti_edma3_cc_wr_4(reg, val) bus_write_4(ti_edma3_sc->mem_res[0], reg, val)
+
+static void ti_edma3_intr_comp(void *arg);
+static void ti_edma3_intr_mperr(void *arg);
+static void ti_edma3_intr_err(void *arg);
+
+static struct {
+ driver_intr_t *handler;
+ char * description;
+} ti_edma3_intrs[TI_EDMA3_NUM_IRQS] = {
+ { ti_edma3_intr_comp, "EDMA Completion Interrupt" },
+ { ti_edma3_intr_mperr, "EDMA Memory Protection Error Interrupt" },
+ { ti_edma3_intr_err, "EDMA Error Interrupt" },
+};
+
+static int
+ti_edma3_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "ti,edma3"))
+ return (ENXIO);
+
+ device_set_desc(dev, "TI EDMA Controller");
+ return (0);
+}
+
+static int
+ti_edma3_attach(device_t dev)
+{
+ struct ti_edma3_softc *sc = device_get_softc(dev);
+ uint32_t reg;
+ int err;
+ int i;
+
+ if (ti_edma3_sc)
+ return (ENXIO);
+
+ ti_edma3_sc = sc;
+ sc->sc_dev = dev;
+
+ /* Request the memory resources */
+ err = bus_alloc_resources(dev, ti_edma3_mem_spec, sc->mem_res);
+ if (err) {
+ device_printf(dev, "Error: could not allocate mem resources\n");
+ return (ENXIO);
+ }
+
+ /* Request the IRQ resources */
+ err = bus_alloc_resources(dev, ti_edma3_irq_spec, sc->irq_res);
+ if (err) {
+ device_printf(dev, "Error: could not allocate irq resources\n");
+ return (ENXIO);
+ }
+
+ /* FIXME: Require DTS from Linux kernel 5.7 */
+ /* FIXME: OK to enable clkctrl here? */
+ /* Enable Channel Controller */
+ ti_sysc_clock_enable(device_get_parent(dev));
+
+ reg = ti_edma3_cc_rd_4(TI_EDMA3CC_PID);
+
+ device_printf(dev, "EDMA revision %08x\n", reg);
+
+ /* Attach interrupt handlers */
+ for (i = 0; i < TI_EDMA3_NUM_IRQS; ++i) {
+ err = bus_setup_intr(dev, sc->irq_res[i], INTR_TYPE_MISC |
+ INTR_MPSAFE, NULL, *ti_edma3_intrs[i].handler,
+ sc, &sc->ih_cookie[i]);
+ if (err) {
+ device_printf(dev, "could not setup %s\n",
+ ti_edma3_intrs[i].description);
+ return (err);
+ }
+ }
+
+ return (0);
+}
+
+static device_method_t ti_edma3_methods[] = {
+ DEVMETHOD(device_probe, ti_edma3_probe),
+ DEVMETHOD(device_attach, ti_edma3_attach),
+ {0, 0},
+};
+
+static driver_t ti_edma3_driver = {
+ "ti_edma3",
+ ti_edma3_methods,
+ sizeof(struct ti_edma3_softc),
+};
+static devclass_t ti_edma3_devclass;
+
+DRIVER_MODULE(ti_edma3, simplebus, ti_edma3_driver, ti_edma3_devclass, 0, 0);
+MODULE_DEPEND(ti_edma3, ti_sysc, 1, 1, 1);
+
+static void
+ti_edma3_intr_comp(void *arg)
+{
+ printf("%s: unimplemented\n", __func__);
+}
+
+static void
+ti_edma3_intr_mperr(void *arg)
+{
+ printf("%s: unimplemented\n", __func__);
+}
+
+static void
+ti_edma3_intr_err(void *arg)
+{
+ printf("%s: unimplemented\n", __func__);
+}
+
+void
+ti_edma3_init(unsigned int eqn)
+{
+ uint32_t reg;
+ int i;
+
+ /* Clear Event Missed Regs */
+ ti_edma3_cc_wr_4(TI_EDMA3CC_EMCR, 0xFFFFFFFF);
+ ti_edma3_cc_wr_4(TI_EDMA3CC_EMCRH, 0xFFFFFFFF);
+ ti_edma3_cc_wr_4(TI_EDMA3CC_QEMCR, 0xFFFFFFFF);
+
+ /* Clear Error Reg */
+ ti_edma3_cc_wr_4(TI_EDMA3CC_CCERRCLR, 0xFFFFFFFF);
+
+ /* Enable DMA channels 0-63 */
+ ti_edma3_cc_wr_4(TI_EDMA3CC_DRAE(0), 0xFFFFFFFF);
+ ti_edma3_cc_wr_4(TI_EDMA3CC_DRAEH(0), 0xFFFFFFFF);
+
+ for (i = 0; i < 64; i++) {
+ ti_edma3_cc_wr_4(TI_EDMA3CC_DCHMAP(i), i<<5);
+ }
+
+ /* Initialize the DMA Queue Number Registers */
+ for (i = 0; i < TI_EDMA3_NUM_DMA_CHS; i++) {
+ reg = ti_edma3_cc_rd_4(TI_EDMA3CC_DMAQNUM(i>>3));
+ reg &= TI_EDMA3CC_DMAQNUM_CLR(i);
+ reg |= TI_EDMA3CC_DMAQNUM_SET(i, eqn);
+ ti_edma3_cc_wr_4(TI_EDMA3CC_DMAQNUM(i>>3), reg);
+ }
+
+ /* Enable the QDMA Region access for all channels */
+ ti_edma3_cc_wr_4(TI_EDMA3CC_QRAE(0), (1 << TI_EDMA3_NUM_QDMA_CHS) - 1);
+
+ /*Initialize QDMA Queue Number Registers */
+ for (i = 0; i < TI_EDMA3_NUM_QDMA_CHS; i++) {
+ reg = ti_edma3_cc_rd_4(TI_EDMA3CC_QDMAQNUM);
+ reg &= TI_EDMA3CC_QDMAQNUM_CLR(i);
+ reg |= TI_EDMA3CC_QDMAQNUM_SET(i, eqn);
+ ti_edma3_cc_wr_4(TI_EDMA3CC_QDMAQNUM, reg);
+ }
+}
+
+#ifdef notyet
+int
+ti_edma3_enable_event_intr(unsigned int ch)
+{
+ uint32_t reg;
+
+ if (ch >= TI_EDMA3_NUM_DMA_CHS)
+ return (EINVAL);
+
+ if (ch < 32) {
+ ti_edma3_cc_wr_4(TI_EDMA3CC_S_IESR(0), 1 << ch);
+ } else {
+ ti_edma3_cc_wr_4(TI_EDMA3CC_S_IESRH(0), 1 << (ch - 32));
+ }
+ return 0;
+}
+#endif
+
+int
+ti_edma3_request_dma_ch(unsigned int ch, unsigned int tccn, unsigned int eqn)
+{
+ uint32_t reg;
+
+ if (ch >= TI_EDMA3_NUM_DMA_CHS)
+ return (EINVAL);
+
+ /* Enable the DMA channel in the DRAE/DRAEH registers */
+ if (ch < 32) {
+ reg = ti_edma3_cc_rd_4(TI_EDMA3CC_DRAE(0));
+ reg |= (0x01 << ch);
+ ti_edma3_cc_wr_4(TI_EDMA3CC_DRAE(0), reg);
+ } else {
+ reg = ti_edma3_cc_rd_4(TI_EDMA3CC_DRAEH(0));
+ reg |= (0x01 << (ch - 32));
+ ti_edma3_cc_wr_4(TI_EDMA3CC_DRAEH(0), reg);
+ }
+
+ /* Associate DMA Channel to Event Queue */
+ reg = ti_edma3_cc_rd_4(TI_EDMA3CC_DMAQNUM(ch >> 3));
+ reg &= TI_EDMA3CC_DMAQNUM_CLR(ch);
+ reg |= TI_EDMA3CC_DMAQNUM_SET((ch), eqn);
+ ti_edma3_cc_wr_4(TI_EDMA3CC_DMAQNUM(ch >> 3), reg);
+
+ /* Set TCC in corresponding PaRAM Entry */
+ reg = ti_edma3_cc_rd_4(TI_EDMA3CC_OPT(ch));
+ reg &= TI_EDMA3CC_OPT_TCC_CLR;
+ reg |= TI_EDMA3CC_OPT_TCC_SET(ch);
+ ti_edma3_cc_wr_4(TI_EDMA3CC_OPT(ch), reg);
+
+ return 0;
+}
+
+int
+ti_edma3_request_qdma_ch(unsigned int ch, unsigned int tccn, unsigned int eqn)
+{
+ uint32_t reg;
+
+ if (ch >= TI_EDMA3_NUM_DMA_CHS)
+ return (EINVAL);
+
+ /* Enable the QDMA channel in the QRAE registers */
+ reg = ti_edma3_cc_rd_4(TI_EDMA3CC_QRAE(0));
+ reg |= (0x01 << ch);
+ ti_edma3_cc_wr_4(TI_EDMA3CC_QRAE(0), reg);
+
+ /* Associate QDMA Channel to Event Queue */
+ reg = ti_edma3_cc_rd_4(TI_EDMA3CC_QDMAQNUM);
+ reg |= TI_EDMA3CC_QDMAQNUM_SET(ch, eqn);
+ ti_edma3_cc_wr_4(TI_EDMA3CC_QDMAQNUM, reg);
+
+ /* Set TCC in corresponding PaRAM Entry */
+ reg = ti_edma3_cc_rd_4(TI_EDMA3CC_OPT(ch));
+ reg &= TI_EDMA3CC_OPT_TCC_CLR;
+ reg |= TI_EDMA3CC_OPT_TCC_SET(ch);
+ ti_edma3_cc_wr_4(TI_EDMA3CC_OPT(ch), reg);
+
+ return 0;
+}
+
+int
+ti_edma3_enable_transfer_manual(unsigned int ch)
+{
+ if (ch >= TI_EDMA3_NUM_DMA_CHS)
+ return (EINVAL);
+
+ /* set corresponding bit in ESR/ESRH to set a event */
+ if (ch < 32) {
+ ti_edma3_cc_wr_4(TI_EDMA3CC_S_ESR(0), 1 << ch);
+ } else {
+ ti_edma3_cc_wr_4(TI_EDMA3CC_S_ESRH(0), 1 << (ch - 32));
+ }
+
+ return 0;
+}
+
+int
+ti_edma3_enable_transfer_qdma(unsigned int ch)
+{
+ if (ch >= TI_EDMA3_NUM_QDMA_CHS)
+ return (EINVAL);
+
+ /* set corresponding bit in QEESR to enable QDMA event */
+ ti_edma3_cc_wr_4(TI_EDMA3CC_S_QEESR(0), (1 << ch));
+
+ return 0;
+}
+
+int
+ti_edma3_enable_transfer_event(unsigned int ch)
+{
+ if (ch >= TI_EDMA3_NUM_DMA_CHS)
+ return (EINVAL);
+
+ /* Clear SECR(H) & EMCR(H) to clean any previous NULL request
+ * and set corresponding bit in EESR to enable DMA event */
+ if(ch < 32) {
+ ti_edma3_cc_wr_4(TI_EDMA3CC_S_SECR(0), (1 << ch));
+ ti_edma3_cc_wr_4(TI_EDMA3CC_EMCR, (1 << ch));
+ ti_edma3_cc_wr_4(TI_EDMA3CC_S_EESR(0), (1 << ch));
+ } else {
+ ti_edma3_cc_wr_4(TI_EDMA3CC_S_SECRH(0), 1 << (ch - 32));
+ ti_edma3_cc_wr_4(TI_EDMA3CC_EMCRH, 1 << (ch - 32));
+ ti_edma3_cc_wr_4(TI_EDMA3CC_S_EESRH(0), 1 << (ch - 32));
+ }
+
+ return 0;
+}
+
+void
+ti_edma3_param_write(unsigned int ch, struct ti_edma3cc_param_set *prs)
+{
+ bus_write_region_4(ti_edma3_sc->mem_res[0], TI_EDMA3CC_OPT(ch),
+ (uint32_t *) prs, 8);
+}
+
+void
+ti_edma3_param_read(unsigned int ch, struct ti_edma3cc_param_set *prs)
+{
+ bus_read_region_4(ti_edma3_sc->mem_res[0], TI_EDMA3CC_OPT(ch),
+ (uint32_t *) prs, 8);
+}
diff --git a/sys/arm/ti/ti_edma3.h b/sys/arm/ti/ti_edma3.h
new file mode 100644
index 000000000000..501b70a643e8
--- /dev/null
+++ b/sys/arm/ti/ti_edma3.h
@@ -0,0 +1,83 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Damjan Marion <dmarion@Freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _TI_EDMA3_H_
+#define _TI_EDMA3_H_
+
+/* Direct Mapped EDMA3 Events */
+#define TI_EDMA3_EVENT_SDTXEVT1 2
+#define TI_EDMA3_EVENT_SDRXEVT1 3
+#define TI_EDMA3_EVENT_SDTXEVT0 24
+#define TI_EDMA3_EVENT_SDRXEVT0 25
+
+struct ti_edma3cc_param_set {
+ struct {
+ uint32_t sam:1; /* Source address mode */
+ uint32_t dam:1; /* Destination address mode */
+ uint32_t syncdim:1; /* Transfer synchronization dimension */
+ uint32_t static_set:1; /* Static Set */
+ uint32_t :4;
+ uint32_t fwid:3; /* FIFO Width */
+ uint32_t tccmode:1; /* Transfer complete code mode */
+ uint32_t tcc:6; /* Transfer complete code */
+ uint32_t :2;
+ uint32_t tcinten:1; /* Transfer complete interrupt enable */
+ uint32_t itcinten:1; /* Intermediate xfer completion intr. ena */
+ uint32_t tcchen:1; /* Transfer complete chaining enable */
+ uint32_t itcchen:1; /* Intermediate xfer completion chaining ena */
+ uint32_t privid:4; /* Privilege identification */
+ uint32_t :3;
+ uint32_t priv:1; /* Privilege level */
+ } opt;
+ uint32_t src; /* Channel Source Address */
+ uint16_t acnt; /* Count for 1st Dimension */
+ uint16_t bcnt; /* Count for 2nd Dimension */
+ uint32_t dst; /* Channel Destination Address */
+ int16_t srcbidx; /* Source B Index */
+ int16_t dstbidx; /* Destination B Index */
+ uint16_t link; /* Link Address */
+ uint16_t bcntrld; /* BCNT Reload */
+ int16_t srccidx; /* Source C Index */
+ int16_t dstcidx; /* Destination C Index */
+ uint16_t ccnt; /* Count for 3rd Dimension */
+ uint16_t reserved; /* Reserved */
+};
+
+void ti_edma3_init(unsigned int eqn);
+int ti_edma3_request_dma_ch(unsigned int ch, unsigned int tccn, unsigned int eqn);
+int ti_edma3_request_qdma_ch(unsigned int ch, unsigned int tccn, unsigned int eqn);
+int ti_edma3_enable_transfer_manual(unsigned int ch);
+int ti_edma3_enable_transfer_qdma(unsigned int ch);
+int ti_edma3_enable_transfer_event(unsigned int ch);
+
+void ti_edma3_param_write(unsigned int ch, struct ti_edma3cc_param_set *prs);
+void ti_edma3_param_read(unsigned int ch, struct ti_edma3cc_param_set *prs);
+
+#endif /* _TI_EDMA3_H_ */
diff --git a/sys/arm/ti/ti_gpio.c b/sys/arm/ti/ti_gpio.c
new file mode 100644
index 000000000000..0c0330b3190c
--- /dev/null
+++ b/sys/arm/ti/ti_gpio.c
@@ -0,0 +1,1125 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2011 Ben Gray <ben.r.gray@gmail.com>.
+ * Copyright (c) 2014 Luiz Otavio O Souza <loos@FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/**
+ * Beware that the OMAP4 datasheet(s) lists GPIO banks 1-6, whereas the code
+ * here uses 0-5.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/proc.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/gpio.h>
+#include <sys/interrupt.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+#include <machine/resource.h>
+
+#include <arm/ti/ti_cpuid.h>
+#include <arm/ti/ti_gpio.h>
+#include <arm/ti/ti_scm.h>
+#include <arm/ti/ti_sysc.h>
+
+#include <dev/gpio/gpiobusvar.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "gpio_if.h"
+#include "ti_gpio_if.h"
+#include "pic_if.h"
+
+#if !defined(SOC_OMAP4) && !defined(SOC_TI_AM335X)
+#error "Unknown SoC"
+#endif
+
+/* Register definitions */
+#define TI_GPIO_REVISION 0x0000
+#define TI_GPIO_SYSCONFIG 0x0010
+#define TI_GPIO_IRQSTATUS_RAW_0 0x0024
+#define TI_GPIO_IRQSTATUS_RAW_1 0x0028
+#define TI_GPIO_IRQSTATUS_0 0x002C /* writing a 0 has no effect */
+#define TI_GPIO_IRQSTATUS_1 0x0030 /* writing a 0 has no effect */
+#define TI_GPIO_IRQSTATUS_SET_0 0x0034 /* writing a 0 has no effect */
+#define TI_GPIO_IRQSTATUS_SET_1 0x0038 /* writing a 0 has no effect */
+#define TI_GPIO_IRQSTATUS_CLR_0 0x003C /* writing a 0 has no effect */
+#define TI_GPIO_IRQSTATUS_CLR_1 0x0040 /* writing a 0 has no effect */
+#define TI_GPIO_IRQWAKEN_0 0x0044
+#define TI_GPIO_IRQWAKEN_1 0x0048
+#define TI_GPIO_SYSSTATUS 0x0114
+#define TI_GPIO_IRQSTATUS1 0x0118
+#define TI_GPIO_IRQENABLE1 0x011C
+#define TI_GPIO_WAKEUPENABLE 0x0120
+#define TI_GPIO_IRQSTATUS2 0x0128
+#define TI_GPIO_IRQENABLE2 0x012C
+#define TI_GPIO_CTRL 0x0130
+#define TI_GPIO_OE 0x0134
+#define TI_GPIO_DATAIN 0x0138
+#define TI_GPIO_DATAOUT 0x013C
+#define TI_GPIO_LEVELDETECT0 0x0140 /* RW register */
+#define TI_GPIO_LEVELDETECT1 0x0144 /* RW register */
+#define TI_GPIO_RISINGDETECT 0x0148 /* RW register */
+#define TI_GPIO_FALLINGDETECT 0x014C /* RW register */
+#define TI_GPIO_DEBOUNCENABLE 0x0150
+#define TI_GPIO_DEBOUNCINGTIME 0x0154
+#define TI_GPIO_CLEARWKUPENA 0x0180
+#define TI_GPIO_SETWKUENA 0x0184
+#define TI_GPIO_CLEARDATAOUT 0x0190
+#define TI_GPIO_SETDATAOUT 0x0194
+
+/* Other SoC Specific definitions */
+#define OMAP4_FIRST_GPIO_BANK 1
+#define OMAP4_INTR_PER_BANK 1
+#define OMAP4_GPIO_REV 0x50600801
+#define AM335X_FIRST_GPIO_BANK 0
+#define AM335X_INTR_PER_BANK 2
+#define AM335X_GPIO_REV 0x50600801
+#define PINS_PER_BANK 32
+#define TI_GPIO_MASK(p) (1U << ((p) % PINS_PER_BANK))
+
+#define OMAP4_GPIO1_REV 0x00000
+#define OMAP4_GPIO2_REV 0x55000
+#define OMAP4_GPIO3_REV 0x57000
+#define OMAP4_GPIO4_REV 0x59000
+#define OMAP4_GPIO5_REV 0x5b000
+#define OMAP4_GPIO6_REV 0x5d000
+
+#define AM335X_GPIO0_REV 0x07000
+#define AM335X_GPIO1_REV 0x4C000
+#define AM335X_GPIO2_REV 0xAC000
+#define AM335X_GPIO3_REV 0xAE000
+
+static int ti_gpio_intr(void *arg);
+static int ti_gpio_detach(device_t);
+
+static int ti_gpio_pic_attach(struct ti_gpio_softc *sc);
+static int ti_gpio_pic_detach(struct ti_gpio_softc *sc);
+
+static uint32_t
+ti_gpio_rev(void)
+{
+ switch(ti_chip()) {
+#ifdef SOC_OMAP4
+ case CHIP_OMAP_4:
+ return (OMAP4_GPIO_REV);
+#endif
+#ifdef SOC_TI_AM335X
+ case CHIP_AM335X:
+ return (AM335X_GPIO_REV);
+#endif
+ }
+ return (0);
+}
+
+/**
+ * Macros for driver mutex locking
+ */
+#define TI_GPIO_LOCK(_sc) mtx_lock_spin(&(_sc)->sc_mtx)
+#define TI_GPIO_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->sc_mtx)
+#define TI_GPIO_LOCK_INIT(_sc) \
+ mtx_init(&_sc->sc_mtx, device_get_nameunit((_sc)->sc_dev), \
+ "ti_gpio", MTX_SPIN)
+#define TI_GPIO_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx)
+#define TI_GPIO_ASSERT_LOCKED(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED)
+#define TI_GPIO_ASSERT_UNLOCKED(_sc) mtx_assert(&(_sc)->sc_mtx, MA_NOTOWNED)
+
+/**
+ * ti_gpio_read_4 - reads a 32-bit value from one of the GPIO registers
+ * @sc: GPIO device context
+ * @bank: The bank to read from
+ * @off: The offset of a register from the GPIO register address range
+ *
+ *
+ * RETURNS:
+ * 32-bit value read from the register.
+ */
+static inline uint32_t
+ti_gpio_read_4(struct ti_gpio_softc *sc, bus_size_t off)
+{
+ return (bus_read_4(sc->sc_mem_res, off));
+}
+
+/**
+ * ti_gpio_write_4 - writes a 32-bit value to one of the GPIO registers
+ * @sc: GPIO device context
+ * @bank: The bank to write to
+ * @off: The offset of a register from the GPIO register address range
+ * @val: The value to write into the register
+ *
+ * RETURNS:
+ * nothing
+ */
+static inline void
+ti_gpio_write_4(struct ti_gpio_softc *sc, bus_size_t off,
+ uint32_t val)
+{
+ bus_write_4(sc->sc_mem_res, off, val);
+}
+
+static inline void
+ti_gpio_intr_clr(struct ti_gpio_softc *sc, uint32_t mask)
+{
+
+ /* We clear both set of registers. */
+ ti_gpio_write_4(sc, TI_GPIO_IRQSTATUS_CLR_0, mask);
+ ti_gpio_write_4(sc, TI_GPIO_IRQSTATUS_CLR_1, mask);
+}
+
+static inline void
+ti_gpio_intr_set(struct ti_gpio_softc *sc, uint32_t mask)
+{
+
+ /*
+ * On OMAP4 we unmask only the MPU interrupt and on AM335x we
+ * also activate only the first interrupt.
+ */
+ ti_gpio_write_4(sc, TI_GPIO_IRQSTATUS_SET_0, mask);
+}
+
+static inline void
+ti_gpio_intr_ack(struct ti_gpio_softc *sc, uint32_t mask)
+{
+
+ /*
+ * Acknowledge the interrupt on both registers even if we use only
+ * the first one.
+ */
+ ti_gpio_write_4(sc, TI_GPIO_IRQSTATUS_0, mask);
+ ti_gpio_write_4(sc, TI_GPIO_IRQSTATUS_1, mask);
+}
+
+static inline uint32_t
+ti_gpio_intr_status(struct ti_gpio_softc *sc)
+{
+ uint32_t reg;
+
+ /* Get the status from both registers. */
+ reg = ti_gpio_read_4(sc, TI_GPIO_IRQSTATUS_0);
+ reg |= ti_gpio_read_4(sc, TI_GPIO_IRQSTATUS_1);
+
+ return (reg);
+}
+
+static device_t
+ti_gpio_get_bus(device_t dev)
+{
+ struct ti_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ return (sc->sc_busdev);
+}
+
+/**
+ * ti_gpio_pin_max - Returns the maximum number of GPIO pins
+ * @dev: gpio device handle
+ * @maxpin: pointer to a value that upon return will contain the maximum number
+ * of pins in the device.
+ *
+ *
+ * LOCKING:
+ * No locking required, returns static data.
+ *
+ * RETURNS:
+ * Returns 0 on success otherwise an error code
+ */
+static int
+ti_gpio_pin_max(device_t dev, int *maxpin)
+{
+
+ *maxpin = PINS_PER_BANK - 1;
+
+ return (0);
+}
+
+static int
+ti_gpio_valid_pin(struct ti_gpio_softc *sc, int pin)
+{
+
+ if (pin >= sc->sc_maxpin || sc->sc_mem_res == NULL)
+ return (EINVAL);
+
+ return (0);
+}
+
+/**
+ * ti_gpio_pin_getcaps - Gets the capabilities of a given pin
+ * @dev: gpio device handle
+ * @pin: the number of the pin
+ * @caps: pointer to a value that upon return will contain the capabilities
+ *
+ * Currently all pins have the same capability, notably:
+ * - GPIO_PIN_INPUT
+ * - GPIO_PIN_OUTPUT
+ * - GPIO_PIN_PULLUP
+ * - GPIO_PIN_PULLDOWN
+ * - GPIO_INTR_LEVEL_LOW
+ * - GPIO_INTR_LEVEL_HIGH
+ * - GPIO_INTR_EDGE_RISING
+ * - GPIO_INTR_EDGE_FALLING
+ * - GPIO_INTR_EDGE_BOTH
+ *
+ * LOCKING:
+ * No locking required, returns static data.
+ *
+ * RETURNS:
+ * Returns 0 on success otherwise an error code
+ */
+static int
+ti_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
+{
+ struct ti_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (ti_gpio_valid_pin(sc, pin) != 0)
+ return (EINVAL);
+
+ *caps = (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_PULLUP |
+ GPIO_PIN_PULLDOWN | GPIO_INTR_LEVEL_LOW | GPIO_INTR_LEVEL_HIGH |
+ GPIO_INTR_EDGE_RISING | GPIO_INTR_EDGE_FALLING |
+ GPIO_INTR_EDGE_BOTH);
+
+ return (0);
+}
+
+/**
+ * ti_gpio_pin_getflags - Gets the current flags of a given pin
+ * @dev: gpio device handle
+ * @pin: the number of the pin
+ * @flags: upon return will contain the current flags of the pin
+ *
+ * Reads the current flags of a given pin, here we actually read the H/W
+ * registers to determine the flags, rather than storing the value in the
+ * setflags call.
+ *
+ * LOCKING:
+ * Internally locks the context
+ *
+ * RETURNS:
+ * Returns 0 on success otherwise an error code
+ */
+static int
+ti_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
+{
+ struct ti_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (ti_gpio_valid_pin(sc, pin) != 0)
+ return (EINVAL);
+
+ /* Get the current pin state */
+ TI_GPIO_LOCK(sc);
+ TI_GPIO_GET_FLAGS(dev, pin, flags);
+ TI_GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+/**
+ * ti_gpio_pin_getname - Gets the name of a given pin
+ * @dev: gpio device handle
+ * @pin: the number of the pin
+ * @name: buffer to put the name in
+ *
+ * The driver simply calls the pins gpio_n, where 'n' is obviously the number
+ * of the pin.
+ *
+ * LOCKING:
+ * No locking required, returns static data.
+ *
+ * RETURNS:
+ * Returns 0 on success otherwise an error code
+ */
+static int
+ti_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
+{
+ struct ti_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (ti_gpio_valid_pin(sc, pin) != 0)
+ return (EINVAL);
+
+ /* Set a very simple name */
+ snprintf(name, GPIOMAXNAME, "gpio_%u", pin);
+ name[GPIOMAXNAME - 1] = '\0';
+
+ return (0);
+}
+
+/**
+ * ti_gpio_pin_setflags - Sets the flags for a given pin
+ * @dev: gpio device handle
+ * @pin: the number of the pin
+ * @flags: the flags to set
+ *
+ * The flags of the pin correspond to things like input/output mode, pull-ups,
+ * pull-downs, etc. This driver doesn't support all flags, only the following:
+ * - GPIO_PIN_INPUT
+ * - GPIO_PIN_OUTPUT
+ * - GPIO_PIN_PULLUP
+ * - GPIO_PIN_PULLDOWN
+ *
+ * LOCKING:
+ * Internally locks the context
+ *
+ * RETURNS:
+ * Returns 0 on success otherwise an error code
+ */
+static int
+ti_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
+{
+ struct ti_gpio_softc *sc;
+ uint32_t oe;
+
+ sc = device_get_softc(dev);
+ if (ti_gpio_valid_pin(sc, pin) != 0)
+ return (EINVAL);
+
+ /* Set the GPIO mode and state */
+ TI_GPIO_LOCK(sc);
+ if (TI_GPIO_SET_FLAGS(dev, pin, flags) != 0) {
+ TI_GPIO_UNLOCK(sc);
+ return (EINVAL);
+ }
+
+ /* If configuring as an output set the "output enable" bit */
+ oe = ti_gpio_read_4(sc, TI_GPIO_OE);
+ if (flags & GPIO_PIN_INPUT)
+ oe |= TI_GPIO_MASK(pin);
+ else
+ oe &= ~TI_GPIO_MASK(pin);
+ ti_gpio_write_4(sc, TI_GPIO_OE, oe);
+ TI_GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+/**
+ * ti_gpio_pin_set - Sets the current level on a GPIO pin
+ * @dev: gpio device handle
+ * @pin: the number of the pin
+ * @value: non-zero value will drive the pin high, otherwise the pin is
+ * driven low.
+ *
+ *
+ * LOCKING:
+ * Internally locks the context
+ *
+ * RETURNS:
+ * Returns 0 on success otherwise a error code
+ */
+static int
+ti_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
+{
+ struct ti_gpio_softc *sc;
+ uint32_t reg;
+
+ sc = device_get_softc(dev);
+ if (ti_gpio_valid_pin(sc, pin) != 0)
+ return (EINVAL);
+
+ TI_GPIO_LOCK(sc);
+ if (value == GPIO_PIN_LOW)
+ reg = TI_GPIO_CLEARDATAOUT;
+ else
+ reg = TI_GPIO_SETDATAOUT;
+ ti_gpio_write_4(sc, reg, TI_GPIO_MASK(pin));
+ TI_GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+/**
+ * ti_gpio_pin_get - Gets the current level on a GPIO pin
+ * @dev: gpio device handle
+ * @pin: the number of the pin
+ * @value: pointer to a value that upond return will contain the pin value
+ *
+ * The pin must be configured as an input pin beforehand, otherwise this
+ * function will fail.
+ *
+ * LOCKING:
+ * Internally locks the context
+ *
+ * RETURNS:
+ * Returns 0 on success otherwise a error code
+ */
+static int
+ti_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *value)
+{
+ struct ti_gpio_softc *sc;
+ uint32_t oe, reg, val;
+
+ sc = device_get_softc(dev);
+ if (ti_gpio_valid_pin(sc, pin) != 0)
+ return (EINVAL);
+
+ /*
+ * Return data from output latch when set as output and from the
+ * input register otherwise.
+ */
+ TI_GPIO_LOCK(sc);
+ oe = ti_gpio_read_4(sc, TI_GPIO_OE);
+ if (oe & TI_GPIO_MASK(pin))
+ reg = TI_GPIO_DATAIN;
+ else
+ reg = TI_GPIO_DATAOUT;
+ val = ti_gpio_read_4(sc, reg);
+ *value = (val & TI_GPIO_MASK(pin)) ? 1 : 0;
+ TI_GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+/**
+ * ti_gpio_pin_toggle - Toggles a given GPIO pin
+ * @dev: gpio device handle
+ * @pin: the number of the pin
+ *
+ *
+ * LOCKING:
+ * Internally locks the context
+ *
+ * RETURNS:
+ * Returns 0 on success otherwise a error code
+ */
+static int
+ti_gpio_pin_toggle(device_t dev, uint32_t pin)
+{
+ struct ti_gpio_softc *sc;
+ uint32_t reg, val;
+
+ sc = device_get_softc(dev);
+ if (ti_gpio_valid_pin(sc, pin) != 0)
+ return (EINVAL);
+
+ /* Toggle the pin */
+ TI_GPIO_LOCK(sc);
+ val = ti_gpio_read_4(sc, TI_GPIO_DATAOUT);
+ if (val & TI_GPIO_MASK(pin))
+ reg = TI_GPIO_CLEARDATAOUT;
+ else
+ reg = TI_GPIO_SETDATAOUT;
+ ti_gpio_write_4(sc, reg, TI_GPIO_MASK(pin));
+ TI_GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+ti_gpio_bank_init(device_t dev)
+{
+ int pin, err;
+ struct ti_gpio_softc *sc;
+ uint32_t flags, reg_oe, reg_set, rev;
+ uint64_t rev_address;
+
+ sc = device_get_softc(dev);
+
+ /* Enable the interface and functional clocks for the module. */
+ rev_address = ti_sysc_get_rev_address(device_get_parent(dev));
+ /* AM335x
+ * sc->sc_bank used in am335x/am335x_gpio.c and omap4/omap4_gpio.c */
+ switch(ti_chip()) {
+#ifdef SOC_OMAP4
+ case CHIP_OMAP_4:
+ switch (rev_address) {
+ case OMAP4_GPIO1_REV:
+ sc->sc_bank = 0;
+ break;
+ case OMAP4_GPIO2_REV:
+ sc->sc_bank = 1;
+ break;
+ case OMAP4_GPIO3_REV:
+ sc->sc_bank = 2;
+ break;
+ case OMAP4_GPIO4_REV:
+ sc->sc_bank = 3;
+ break;
+ case OMAP4_GPIO5_REV:
+ sc->sc_bank = 4;
+ break;
+ case OMAP4_GPIO6_REV:
+ sc->sc_bank = 5;
+ break;
+ }
+#endif
+#ifdef SOC_TI_AM335X
+ case CHIP_AM335X:
+ switch (rev_address) {
+ case AM335X_GPIO0_REV:
+ sc->sc_bank = 0;
+ break;
+ case AM335X_GPIO1_REV:
+ sc->sc_bank = 1;
+ break;
+ case AM335X_GPIO2_REV:
+ sc->sc_bank = 2;
+ break;
+ case AM335X_GPIO3_REV:
+ sc->sc_bank = 3;
+ break;
+ }
+#endif
+ }
+ err = ti_sysc_clock_enable(device_get_parent(dev));
+ if (err) {
+ device_printf(dev, "Failed to enable clock\n");
+ return (EINVAL);
+ }
+
+ /*
+ * Read the revision number of the module. TI don't publish the
+ * actual revision numbers, so instead the values have been
+ * determined by experimentation.
+ */
+ rev = ti_gpio_read_4(sc,
+ ti_sysc_get_rev_address_offset_host(device_get_parent(dev)));
+
+ /* Check the revision. */
+ if (rev != ti_gpio_rev()) {
+ device_printf(dev, "Warning: could not determine the revision "
+ "of GPIO module (revision:0x%08x)\n", rev);
+ return (EINVAL);
+ }
+
+ /* Disable interrupts for all pins. */
+ ti_gpio_intr_clr(sc, 0xffffffff);
+
+ /* Init OE register based on pads configuration. */
+ reg_oe = 0xffffffff;
+ reg_set = 0;
+ for (pin = 0; pin < PINS_PER_BANK; pin++) {
+ TI_GPIO_GET_FLAGS(dev, pin, &flags);
+ if (flags & GPIO_PIN_OUTPUT) {
+ reg_oe &= ~(1UL << pin);
+ if (flags & GPIO_PIN_PULLUP)
+ reg_set |= (1UL << pin);
+ }
+ }
+ ti_gpio_write_4(sc, TI_GPIO_OE, reg_oe);
+ if (reg_set)
+ ti_gpio_write_4(sc, TI_GPIO_SETDATAOUT, reg_set);
+
+ return (0);
+}
+
+/**
+ * ti_gpio_attach - attach function for the driver
+ * @dev: gpio device handle
+ *
+ * Allocates and sets up the driver context for all GPIO banks. This function
+ * expects the memory ranges and IRQs to already be allocated to the driver.
+ *
+ * LOCKING:
+ * None
+ *
+ * RETURNS:
+ * Always returns 0
+ */
+static int
+ti_gpio_attach(device_t dev)
+{
+ struct ti_gpio_softc *sc;
+ int err;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+ TI_GPIO_LOCK_INIT(sc);
+ ti_gpio_pin_max(dev, &sc->sc_maxpin);
+ sc->sc_maxpin++;
+
+ sc->sc_mem_rid = 0;
+ sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &sc->sc_mem_rid, RF_ACTIVE);
+ if (!sc->sc_mem_res) {
+ device_printf(dev, "Error: could not allocate mem resources\n");
+ ti_gpio_detach(dev);
+ return (ENXIO);
+ }
+
+ sc->sc_irq_rid = 0;
+ sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
+ &sc->sc_irq_rid, RF_ACTIVE);
+ if (!sc->sc_irq_res) {
+ device_printf(dev, "Error: could not allocate irq resources\n");
+ ti_gpio_detach(dev);
+ return (ENXIO);
+ }
+
+ /*
+ * Register our interrupt filter for each of the IRQ resources.
+ */
+ if (bus_setup_intr(dev, sc->sc_irq_res,
+ INTR_TYPE_MISC | INTR_MPSAFE, ti_gpio_intr, NULL, sc,
+ &sc->sc_irq_hdl) != 0) {
+ device_printf(dev,
+ "WARNING: unable to register interrupt filter\n");
+ ti_gpio_detach(dev);
+ return (ENXIO);
+ }
+
+ if (ti_gpio_pic_attach(sc) != 0) {
+ device_printf(dev, "WARNING: unable to attach PIC\n");
+ ti_gpio_detach(dev);
+ return (ENXIO);
+ }
+
+ /* We need to go through each block and ensure the clocks are running and
+ * the module is enabled. It might be better to do this only when the
+ * pins are configured which would result in less power used if the GPIO
+ * pins weren't used ...
+ */
+ if (sc->sc_mem_res != NULL) {
+ /* Initialize the GPIO module. */
+ err = ti_gpio_bank_init(dev);
+ if (err != 0) {
+ ti_gpio_detach(dev);
+ return (err);
+ }
+ }
+
+ sc->sc_busdev = gpiobus_attach_bus(dev);
+ if (sc->sc_busdev == NULL) {
+ ti_gpio_detach(dev);
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+/**
+ * ti_gpio_detach - detach function for the driver
+ * @dev: scm device handle
+ *
+ * Allocates and sets up the driver context, this simply entails creating a
+ * bus mappings for the SCM register set.
+ *
+ * LOCKING:
+ * None
+ *
+ * RETURNS:
+ * Always returns 0
+ */
+static int
+ti_gpio_detach(device_t dev)
+{
+ struct ti_gpio_softc *sc = device_get_softc(dev);
+
+ KASSERT(mtx_initialized(&sc->sc_mtx), ("gpio mutex not initialized"));
+
+ /* Disable all interrupts */
+ if (sc->sc_mem_res != NULL)
+ ti_gpio_intr_clr(sc, 0xffffffff);
+ if (sc->sc_busdev != NULL)
+ gpiobus_detach_bus(dev);
+ if (sc->sc_isrcs != NULL)
+ ti_gpio_pic_detach(sc);
+ /* Release the memory and IRQ resources. */
+ if (sc->sc_irq_hdl) {
+ bus_teardown_intr(dev, sc->sc_irq_res,
+ sc->sc_irq_hdl);
+ }
+ if (sc->sc_irq_res)
+ bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid,
+ sc->sc_irq_res);
+ if (sc->sc_mem_res)
+ bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid,
+ sc->sc_mem_res);
+ TI_GPIO_LOCK_DESTROY(sc);
+
+ return (0);
+}
+
+static inline void
+ti_gpio_rwreg_modify(struct ti_gpio_softc *sc, uint32_t reg, uint32_t mask,
+ bool set_bits)
+{
+ uint32_t value;
+
+ value = ti_gpio_read_4(sc, reg);
+ ti_gpio_write_4(sc, reg, set_bits ? value | mask : value & ~mask);
+}
+
+static inline void
+ti_gpio_isrc_mask(struct ti_gpio_softc *sc, struct ti_gpio_irqsrc *tgi)
+{
+
+ /* Writing a 0 has no effect. */
+ ti_gpio_intr_clr(sc, tgi->tgi_mask);
+}
+
+static inline void
+ti_gpio_isrc_unmask(struct ti_gpio_softc *sc, struct ti_gpio_irqsrc *tgi)
+{
+
+ /* Writing a 0 has no effect. */
+ ti_gpio_intr_set(sc, tgi->tgi_mask);
+}
+
+static inline void
+ti_gpio_isrc_eoi(struct ti_gpio_softc *sc, struct ti_gpio_irqsrc *tgi)
+{
+
+ /* Writing a 0 has no effect. */
+ ti_gpio_intr_ack(sc, tgi->tgi_mask);
+}
+
+static inline bool
+ti_gpio_isrc_is_level(struct ti_gpio_irqsrc *tgi)
+{
+
+ return (tgi->tgi_mode == GPIO_INTR_LEVEL_LOW ||
+ tgi->tgi_mode == GPIO_INTR_LEVEL_HIGH);
+}
+
+static int
+ti_gpio_intr(void *arg)
+{
+ u_int irq;
+ uint32_t reg;
+ struct ti_gpio_softc *sc;
+ struct trapframe *tf;
+ struct ti_gpio_irqsrc *tgi;
+
+ sc = (struct ti_gpio_softc *)arg;
+ tf = curthread->td_intr_frame;
+
+ reg = ti_gpio_intr_status(sc);
+ for (irq = 0; irq < sc->sc_maxpin; irq++) {
+ tgi = &sc->sc_isrcs[irq];
+ if ((reg & tgi->tgi_mask) == 0)
+ continue;
+ if (!ti_gpio_isrc_is_level(tgi))
+ ti_gpio_isrc_eoi(sc, tgi);
+ if (intr_isrc_dispatch(&tgi->tgi_isrc, tf) != 0) {
+ ti_gpio_isrc_mask(sc, tgi);
+ if (ti_gpio_isrc_is_level(tgi))
+ ti_gpio_isrc_eoi(sc, tgi);
+ device_printf(sc->sc_dev, "Stray irq %u disabled\n",
+ irq);
+ }
+ }
+ return (FILTER_HANDLED);
+}
+
+static int
+ti_gpio_pic_attach(struct ti_gpio_softc *sc)
+{
+ int error;
+ uint32_t irq;
+ const char *name;
+
+ sc->sc_isrcs = malloc(sizeof(*sc->sc_isrcs) * sc->sc_maxpin, M_DEVBUF,
+ M_WAITOK | M_ZERO);
+
+ name = device_get_nameunit(sc->sc_dev);
+ for (irq = 0; irq < sc->sc_maxpin; irq++) {
+ sc->sc_isrcs[irq].tgi_irq = irq;
+ sc->sc_isrcs[irq].tgi_mask = TI_GPIO_MASK(irq);
+ sc->sc_isrcs[irq].tgi_mode = GPIO_INTR_CONFORM;
+
+ error = intr_isrc_register(&sc->sc_isrcs[irq].tgi_isrc,
+ sc->sc_dev, 0, "%s,%u", name, irq);
+ if (error != 0)
+ return (error); /* XXX deregister ISRCs */
+ }
+ if (intr_pic_register(sc->sc_dev,
+ OF_xref_from_node(ofw_bus_get_node(sc->sc_dev))) == NULL)
+ return (ENXIO);
+
+ return (0);
+}
+
+static int
+ti_gpio_pic_detach(struct ti_gpio_softc *sc)
+{
+
+ /*
+ * There has not been established any procedure yet
+ * how to detach PIC from living system correctly.
+ */
+ device_printf(sc->sc_dev, "%s: not implemented yet\n", __func__);
+ return (EBUSY);
+}
+
+static void
+ti_gpio_pic_config_intr(struct ti_gpio_softc *sc, struct ti_gpio_irqsrc *tgi,
+ uint32_t mode)
+{
+
+ TI_GPIO_LOCK(sc);
+ ti_gpio_rwreg_modify(sc, TI_GPIO_RISINGDETECT, tgi->tgi_mask,
+ mode == GPIO_INTR_EDGE_RISING || mode == GPIO_INTR_EDGE_BOTH);
+ ti_gpio_rwreg_modify(sc, TI_GPIO_FALLINGDETECT, tgi->tgi_mask,
+ mode == GPIO_INTR_EDGE_FALLING || mode == GPIO_INTR_EDGE_BOTH);
+ ti_gpio_rwreg_modify(sc, TI_GPIO_LEVELDETECT1, tgi->tgi_mask,
+ mode == GPIO_INTR_LEVEL_HIGH);
+ ti_gpio_rwreg_modify(sc, TI_GPIO_LEVELDETECT0, tgi->tgi_mask,
+ mode == GPIO_INTR_LEVEL_LOW);
+ tgi->tgi_mode = mode;
+ TI_GPIO_UNLOCK(sc);
+}
+
+static void
+ti_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct ti_gpio_softc *sc = device_get_softc(dev);
+ struct ti_gpio_irqsrc *tgi = (struct ti_gpio_irqsrc *)isrc;
+
+ ti_gpio_isrc_mask(sc, tgi);
+}
+
+static void
+ti_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct ti_gpio_softc *sc = device_get_softc(dev);
+ struct ti_gpio_irqsrc *tgi = (struct ti_gpio_irqsrc *)isrc;
+
+ arm_irq_memory_barrier(tgi->tgi_irq);
+ ti_gpio_isrc_unmask(sc, tgi);
+}
+
+static int
+ti_gpio_pic_map_fdt(struct ti_gpio_softc *sc, struct intr_map_data_fdt *daf,
+ u_int *irqp, uint32_t *modep)
+{
+ uint32_t mode;
+
+ /*
+ * The first cell is the interrupt number.
+ * The second cell is used to specify flags:
+ * bits[3:0] trigger type and level flags:
+ * 1 = low-to-high edge triggered.
+ * 2 = high-to-low edge triggered.
+ * 4 = active high level-sensitive.
+ * 8 = active low level-sensitive.
+ */
+ if (daf->ncells != 2 || daf->cells[0] >= sc->sc_maxpin)
+ return (EINVAL);
+
+ /* Only reasonable modes are supported. */
+ if (daf->cells[1] == 1)
+ mode = GPIO_INTR_EDGE_RISING;
+ else if (daf->cells[1] == 2)
+ mode = GPIO_INTR_EDGE_FALLING;
+ else if (daf->cells[1] == 3)
+ mode = GPIO_INTR_EDGE_BOTH;
+ else if (daf->cells[1] == 4)
+ mode = GPIO_INTR_LEVEL_HIGH;
+ else if (daf->cells[1] == 8)
+ mode = GPIO_INTR_LEVEL_LOW;
+ else
+ return (EINVAL);
+
+ *irqp = daf->cells[0];
+ if (modep != NULL)
+ *modep = mode;
+ return (0);
+}
+
+static int
+ti_gpio_pic_map_gpio(struct ti_gpio_softc *sc, struct intr_map_data_gpio *dag,
+ u_int *irqp, uint32_t *modep)
+{
+ uint32_t mode;
+
+ if (dag->gpio_pin_num >= sc->sc_maxpin)
+ return (EINVAL);
+
+ mode = dag->gpio_intr_mode;
+ if (mode != GPIO_INTR_LEVEL_LOW && mode != GPIO_INTR_LEVEL_HIGH &&
+ mode != GPIO_INTR_EDGE_RISING && mode != GPIO_INTR_EDGE_FALLING &&
+ mode != GPIO_INTR_EDGE_BOTH)
+ return (EINVAL);
+
+ *irqp = dag->gpio_pin_num;
+ if (modep != NULL)
+ *modep = mode;
+ return (0);
+}
+
+static int
+ti_gpio_pic_map(struct ti_gpio_softc *sc, struct intr_map_data *data,
+ u_int *irqp, uint32_t *modep)
+{
+
+ switch (data->type) {
+ case INTR_MAP_DATA_FDT:
+ return (ti_gpio_pic_map_fdt(sc,
+ (struct intr_map_data_fdt *)data, irqp, modep));
+ case INTR_MAP_DATA_GPIO:
+ return (ti_gpio_pic_map_gpio(sc,
+ (struct intr_map_data_gpio *)data, irqp, modep));
+ default:
+ return (ENOTSUP);
+ }
+}
+
+static int
+ti_gpio_pic_map_intr(device_t dev, struct intr_map_data *data,
+ struct intr_irqsrc **isrcp)
+{
+ int error;
+ u_int irq;
+ struct ti_gpio_softc *sc = device_get_softc(dev);
+
+ error = ti_gpio_pic_map(sc, data, &irq, NULL);
+ if (error == 0)
+ *isrcp = &sc->sc_isrcs[irq].tgi_isrc;
+ return (error);
+}
+
+static void
+ti_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct ti_gpio_softc *sc = device_get_softc(dev);
+ struct ti_gpio_irqsrc *tgi = (struct ti_gpio_irqsrc *)isrc;
+
+ if (ti_gpio_isrc_is_level(tgi))
+ ti_gpio_isrc_eoi(sc, tgi);
+}
+
+static void
+ti_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+
+ ti_gpio_pic_enable_intr(dev, isrc);
+}
+
+static void
+ti_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct ti_gpio_softc *sc = device_get_softc(dev);
+ struct ti_gpio_irqsrc *tgi = (struct ti_gpio_irqsrc *)isrc;
+
+ ti_gpio_isrc_mask(sc, tgi);
+ if (ti_gpio_isrc_is_level(tgi))
+ ti_gpio_isrc_eoi(sc, tgi);
+}
+
+static int
+ti_gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ u_int irq;
+ uint32_t mode;
+ struct ti_gpio_softc *sc;
+ struct ti_gpio_irqsrc *tgi;
+
+ if (data == NULL)
+ return (ENOTSUP);
+
+ sc = device_get_softc(dev);
+ tgi = (struct ti_gpio_irqsrc *)isrc;
+
+ /* Get and check config for an interrupt. */
+ if (ti_gpio_pic_map(sc, data, &irq, &mode) != 0 || tgi->tgi_irq != irq)
+ return (EINVAL);
+
+ /*
+ * If this is a setup for another handler,
+ * only check that its configuration match.
+ */
+ if (isrc->isrc_handlers != 0)
+ return (tgi->tgi_mode == mode ? 0 : EINVAL);
+
+ ti_gpio_pic_config_intr(sc, tgi, mode);
+ return (0);
+}
+
+static int
+ti_gpio_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ struct ti_gpio_softc *sc = device_get_softc(dev);
+ struct ti_gpio_irqsrc *tgi = (struct ti_gpio_irqsrc *)isrc;
+
+ if (isrc->isrc_handlers == 0)
+ ti_gpio_pic_config_intr(sc, tgi, GPIO_INTR_CONFORM);
+ return (0);
+}
+
+static phandle_t
+ti_gpio_get_node(device_t bus, device_t dev)
+{
+
+ /* We only have one child, the GPIO bus, which needs our own node. */
+ return (ofw_bus_get_node(bus));
+}
+
+static device_method_t ti_gpio_methods[] = {
+ DEVMETHOD(device_attach, ti_gpio_attach),
+ DEVMETHOD(device_detach, ti_gpio_detach),
+
+ /* GPIO protocol */
+ DEVMETHOD(gpio_get_bus, ti_gpio_get_bus),
+ DEVMETHOD(gpio_pin_max, ti_gpio_pin_max),
+ DEVMETHOD(gpio_pin_getname, ti_gpio_pin_getname),
+ DEVMETHOD(gpio_pin_getflags, ti_gpio_pin_getflags),
+ DEVMETHOD(gpio_pin_getcaps, ti_gpio_pin_getcaps),
+ DEVMETHOD(gpio_pin_setflags, ti_gpio_pin_setflags),
+ DEVMETHOD(gpio_pin_get, ti_gpio_pin_get),
+ DEVMETHOD(gpio_pin_set, ti_gpio_pin_set),
+ DEVMETHOD(gpio_pin_toggle, ti_gpio_pin_toggle),
+
+ /* Interrupt controller interface */
+ DEVMETHOD(pic_disable_intr, ti_gpio_pic_disable_intr),
+ DEVMETHOD(pic_enable_intr, ti_gpio_pic_enable_intr),
+ DEVMETHOD(pic_map_intr, ti_gpio_pic_map_intr),
+ DEVMETHOD(pic_setup_intr, ti_gpio_pic_setup_intr),
+ DEVMETHOD(pic_teardown_intr, ti_gpio_pic_teardown_intr),
+ DEVMETHOD(pic_post_filter, ti_gpio_pic_post_filter),
+ DEVMETHOD(pic_post_ithread, ti_gpio_pic_post_ithread),
+ DEVMETHOD(pic_pre_ithread, ti_gpio_pic_pre_ithread),
+
+ /* ofw_bus interface */
+ DEVMETHOD(ofw_bus_get_node, ti_gpio_get_node),
+
+ {0, 0},
+};
+
+driver_t ti_gpio_driver = {
+ "gpio",
+ ti_gpio_methods,
+ sizeof(struct ti_gpio_softc),
+};
diff --git a/sys/arm/ti/ti_gpio.h b/sys/arm/ti/ti_gpio.h
new file mode 100644
index 000000000000..3ffc8996c8d3
--- /dev/null
+++ b/sys/arm/ti/ti_gpio.h
@@ -0,0 +1,70 @@
+/*-
+ * Copyright (c) 2011
+ * Ben Gray <ben.r.gray@gmail.com>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef TI_GPIO_H
+#define TI_GPIO_H
+
+/* The maximum number of banks for any SoC */
+#define MAX_GPIO_BANKS 6
+
+/*
+ * Maximum GPIOS possible, max of *_MAX_GPIO_BANKS * *_INTR_PER_BANK.
+ * These are defined in ti_gpio.c
+ */
+#define MAX_GPIO_INTRS 8
+
+struct ti_gpio_irqsrc {
+ struct intr_irqsrc tgi_isrc;
+ u_int tgi_irq;
+ uint32_t tgi_mask;
+ uint32_t tgi_mode;
+};
+
+/**
+ * Structure that stores the driver context.
+ *
+ * This structure is allocated during driver attach.
+ */
+struct ti_gpio_softc {
+ device_t sc_dev;
+ device_t sc_busdev;
+ int sc_bank;
+ int sc_maxpin;
+ struct mtx sc_mtx;
+
+ int sc_mem_rid;
+ struct resource *sc_mem_res;
+ int sc_irq_rid;
+ struct resource *sc_irq_res;
+ struct ti_gpio_irqsrc *sc_isrcs;
+ /* The handle for the register IRQ handlers. */
+ void *sc_irq_hdl;
+};
+
+#endif /* TI_GPIO_H */
diff --git a/sys/arm/ti/ti_gpio_if.m b/sys/arm/ti/ti_gpio_if.m
new file mode 100644
index 000000000000..fe29323aef0e
--- /dev/null
+++ b/sys/arm/ti/ti_gpio_if.m
@@ -0,0 +1,49 @@
+#-
+# Copyright (c) 2014 Andrew Turner <andrew@FreeBSD.org>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+
+#include <sys/bus.h>
+
+INTERFACE ti_gpio;
+
+/**
+ * Sets the Ti SoCs gpio flags
+ */
+METHOD int set_flags {
+ device_t dev;
+ uint32_t gpio;
+ uint32_t flags;
+};
+
+/**
+ * Gets the Ti SoCs gpio flags
+ */
+METHOD int get_flags {
+ device_t dev;
+ uint32_t gpio;
+ uint32_t *flags;
+};
diff --git a/sys/arm/ti/ti_i2c.c b/sys/arm/ti/ti_i2c.c
new file mode 100644
index 000000000000..c9b27436547c
--- /dev/null
+++ b/sys/arm/ti/ti_i2c.c
@@ -0,0 +1,977 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2011 Ben Gray <ben.r.gray@gmail.com>.
+ * Copyright (c) 2014 Luiz Otavio O Souza <loos@freebsd.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/**
+ * Driver for the I2C module on the TI SoC.
+ *
+ * This driver is heavily based on the TWI driver for the AT91 (at91_twi.c).
+ *
+ * CAUTION: The I2Ci registers are limited to 16 bit and 8 bit data accesses,
+ * 32 bit data access is not allowed and can corrupt register content.
+ *
+ * This driver currently doesn't use DMA for the transfer, although I hope to
+ * incorporate that sometime in the future. The idea being that for transaction
+ * larger than a certain size the DMA engine is used, for anything less the
+ * normal interrupt/fifo driven option is used.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+#include <machine/bus.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/ti/ti_cpuid.h>
+#include <arm/ti/ti_sysc.h>
+#include <arm/ti/ti_i2c.h>
+
+#include <dev/iicbus/iiconf.h>
+#include <dev/iicbus/iicbus.h>
+
+#include "iicbus_if.h"
+
+/**
+ * I2C device driver context, a pointer to this is stored in the device
+ * driver structure.
+ */
+struct ti_i2c_softc
+{
+ device_t sc_dev;
+ struct resource* sc_irq_res;
+ struct resource* sc_mem_res;
+ device_t sc_iicbus;
+
+ void* sc_irq_h;
+
+ struct mtx sc_mtx;
+
+ struct iic_msg* sc_buffer;
+ int sc_bus_inuse;
+ int sc_buffer_pos;
+ int sc_error;
+ int sc_fifo_trsh;
+ int sc_timeout;
+
+ uint16_t sc_con_reg;
+ uint16_t sc_rev;
+};
+
+struct ti_i2c_clock_config
+{
+ u_int frequency; /* Bus frequency in Hz */
+ uint8_t psc; /* Fast/Standard mode prescale divider */
+ uint8_t scll; /* Fast/Standard mode SCL low time */
+ uint8_t sclh; /* Fast/Standard mode SCL high time */
+ uint8_t hsscll; /* High Speed mode SCL low time */
+ uint8_t hssclh; /* High Speed mode SCL high time */
+};
+
+#if defined(SOC_OMAP4)
+/*
+ * OMAP4 i2c bus clock is 96MHz / ((psc + 1) * (scll + 7 + sclh + 5)).
+ * The prescaler values for 100KHz and 400KHz modes come from the table in the
+ * OMAP4 TRM. The table doesn't list 1MHz; these values should give that speed.
+ */
+static struct ti_i2c_clock_config ti_omap4_i2c_clock_configs[] = {
+ { 100000, 23, 13, 15, 0, 0},
+ { 400000, 9, 5, 7, 0, 0},
+ { 1000000, 3, 5, 7, 0, 0},
+/* { 3200000, 1, 113, 115, 7, 10}, - HS mode */
+ { 0 /* Table terminator */ }
+};
+#endif
+
+#if defined(SOC_TI_AM335X)
+/*
+ * AM335x i2c bus clock is 48MHZ / ((psc + 1) * (scll + 7 + sclh + 5))
+ * In all cases we prescale the clock to 24MHz as recommended in the manual.
+ */
+static struct ti_i2c_clock_config ti_am335x_i2c_clock_configs[] = {
+ { 100000, 1, 111, 117, 0, 0},
+ { 400000, 1, 23, 25, 0, 0},
+ { 1000000, 1, 5, 7, 0, 0},
+ { 0 /* Table terminator */ }
+};
+#endif
+
+/**
+ * Locking macros used throughout the driver
+ */
+#define TI_I2C_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define TI_I2C_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#define TI_I2C_LOCK_INIT(_sc) \
+ mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \
+ "ti_i2c", MTX_DEF)
+#define TI_I2C_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx)
+#define TI_I2C_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED)
+#define TI_I2C_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED)
+
+#ifdef DEBUG
+#define ti_i2c_dbg(_sc, fmt, args...) \
+ device_printf((_sc)->sc_dev, fmt, ##args)
+#else
+#define ti_i2c_dbg(_sc, fmt, args...)
+#endif
+
+/**
+ * ti_i2c_read_2 - reads a 16-bit value from one of the I2C registers
+ * @sc: I2C device context
+ * @off: the byte offset within the register bank to read from.
+ *
+ *
+ * LOCKING:
+ * No locking required
+ *
+ * RETURNS:
+ * 16-bit value read from the register.
+ */
+static inline uint16_t
+ti_i2c_read_2(struct ti_i2c_softc *sc, bus_size_t off)
+{
+
+ return (bus_read_2(sc->sc_mem_res, off));
+}
+
+/**
+ * ti_i2c_write_2 - writes a 16-bit value to one of the I2C registers
+ * @sc: I2C device context
+ * @off: the byte offset within the register bank to read from.
+ * @val: the value to write into the register
+ *
+ * LOCKING:
+ * No locking required
+ *
+ * RETURNS:
+ * 16-bit value read from the register.
+ */
+static inline void
+ti_i2c_write_2(struct ti_i2c_softc *sc, bus_size_t off, uint16_t val)
+{
+
+ bus_write_2(sc->sc_mem_res, off, val);
+}
+
+static int
+ti_i2c_transfer_intr(struct ti_i2c_softc* sc, uint16_t status)
+{
+ int amount, done, i;
+
+ done = 0;
+ amount = 0;
+ /* Check for the error conditions. */
+ if (status & I2C_STAT_NACK) {
+ /* No ACK from slave. */
+ ti_i2c_dbg(sc, "NACK\n");
+ ti_i2c_write_2(sc, I2C_REG_STATUS, I2C_STAT_NACK);
+ sc->sc_error = ENXIO;
+ } else if (status & I2C_STAT_AL) {
+ /* Arbitration lost. */
+ ti_i2c_dbg(sc, "Arbitration lost\n");
+ ti_i2c_write_2(sc, I2C_REG_STATUS, I2C_STAT_AL);
+ sc->sc_error = ENXIO;
+ }
+
+ /* Check if we have finished. */
+ if (status & I2C_STAT_ARDY) {
+ /* Register access ready - transaction complete basically. */
+ ti_i2c_dbg(sc, "ARDY transaction complete\n");
+ if (sc->sc_error != 0 && sc->sc_buffer->flags & IIC_M_NOSTOP) {
+ ti_i2c_write_2(sc, I2C_REG_CON,
+ sc->sc_con_reg | I2C_CON_STP);
+ }
+ ti_i2c_write_2(sc, I2C_REG_STATUS,
+ I2C_STAT_ARDY | I2C_STAT_RDR | I2C_STAT_RRDY |
+ I2C_STAT_XDR | I2C_STAT_XRDY);
+ return (1);
+ }
+
+ if (sc->sc_buffer->flags & IIC_M_RD) {
+ /* Read some data. */
+ if (status & I2C_STAT_RDR) {
+ /*
+ * Receive draining interrupt - last data received.
+ * The set FIFO threshold won't be reached to trigger
+ * RRDY.
+ */
+ ti_i2c_dbg(sc, "Receive draining interrupt\n");
+
+ /*
+ * Drain the FIFO. Read the pending data in the FIFO.
+ */
+ amount = sc->sc_buffer->len - sc->sc_buffer_pos;
+ } else if (status & I2C_STAT_RRDY) {
+ /*
+ * Receive data ready interrupt - FIFO has reached the
+ * set threshold.
+ */
+ ti_i2c_dbg(sc, "Receive data ready interrupt\n");
+
+ amount = min(sc->sc_fifo_trsh,
+ sc->sc_buffer->len - sc->sc_buffer_pos);
+ }
+
+ /* Read the bytes from the fifo. */
+ for (i = 0; i < amount; i++)
+ sc->sc_buffer->buf[sc->sc_buffer_pos++] =
+ (uint8_t)(ti_i2c_read_2(sc, I2C_REG_DATA) & 0xff);
+
+ if (status & I2C_STAT_RDR)
+ ti_i2c_write_2(sc, I2C_REG_STATUS, I2C_STAT_RDR);
+ if (status & I2C_STAT_RRDY)
+ ti_i2c_write_2(sc, I2C_REG_STATUS, I2C_STAT_RRDY);
+
+ } else {
+ /* Write some data. */
+ if (status & I2C_STAT_XDR) {
+ /*
+ * Transmit draining interrupt - FIFO level is below
+ * the set threshold and the amount of data still to
+ * be transferred won't reach the set FIFO threshold.
+ */
+ ti_i2c_dbg(sc, "Transmit draining interrupt\n");
+
+ /*
+ * Drain the TX data. Write the pending data in the
+ * FIFO.
+ */
+ amount = sc->sc_buffer->len - sc->sc_buffer_pos;
+ } else if (status & I2C_STAT_XRDY) {
+ /*
+ * Transmit data ready interrupt - the FIFO level
+ * is below the set threshold.
+ */
+ ti_i2c_dbg(sc, "Transmit data ready interrupt\n");
+
+ amount = min(sc->sc_fifo_trsh,
+ sc->sc_buffer->len - sc->sc_buffer_pos);
+ }
+
+ /* Write the bytes from the fifo. */
+ for (i = 0; i < amount; i++)
+ ti_i2c_write_2(sc, I2C_REG_DATA,
+ sc->sc_buffer->buf[sc->sc_buffer_pos++]);
+
+ if (status & I2C_STAT_XDR)
+ ti_i2c_write_2(sc, I2C_REG_STATUS, I2C_STAT_XDR);
+ if (status & I2C_STAT_XRDY)
+ ti_i2c_write_2(sc, I2C_REG_STATUS, I2C_STAT_XRDY);
+ }
+
+ return (done);
+}
+
+/**
+ * ti_i2c_intr - interrupt handler for the I2C module
+ * @dev: i2c device handle
+ *
+ *
+ *
+ * LOCKING:
+ * Called from timer context
+ *
+ * RETURNS:
+ * EH_HANDLED or EH_NOT_HANDLED
+ */
+static void
+ti_i2c_intr(void *arg)
+{
+ int done;
+ struct ti_i2c_softc *sc;
+ uint16_t events, status;
+
+ sc = (struct ti_i2c_softc *)arg;
+
+ TI_I2C_LOCK(sc);
+
+ status = ti_i2c_read_2(sc, I2C_REG_STATUS);
+ if (status == 0) {
+ TI_I2C_UNLOCK(sc);
+ return;
+ }
+
+ /* Save enabled interrupts. */
+ events = ti_i2c_read_2(sc, I2C_REG_IRQENABLE_SET);
+
+ /* We only care about enabled interrupts. */
+ status &= events;
+
+ done = 0;
+
+ if (sc->sc_buffer != NULL)
+ done = ti_i2c_transfer_intr(sc, status);
+ else {
+ ti_i2c_dbg(sc, "Transfer interrupt without buffer\n");
+ sc->sc_error = EINVAL;
+ done = 1;
+ }
+
+ if (done)
+ /* Wakeup the process that started the transaction. */
+ wakeup(sc);
+
+ TI_I2C_UNLOCK(sc);
+}
+
+/**
+ * ti_i2c_transfer - called to perform the transfer
+ * @dev: i2c device handle
+ * @msgs: the messages to send/receive
+ * @nmsgs: the number of messages in the msgs array
+ *
+ *
+ * LOCKING:
+ * Internally locked
+ *
+ * RETURNS:
+ * 0 on function succeeded
+ * EINVAL if invalid message is passed as an arg
+ */
+static int
+ti_i2c_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
+{
+ int err, i, repstart, timeout;
+ struct ti_i2c_softc *sc;
+ uint16_t reg;
+
+ sc = device_get_softc(dev);
+ TI_I2C_LOCK(sc);
+
+ /* If the controller is busy wait until it is available. */
+ while (sc->sc_bus_inuse == 1)
+ mtx_sleep(sc, &sc->sc_mtx, 0, "i2cbuswait", 0);
+
+ /* Now we have control over the I2C controller. */
+ sc->sc_bus_inuse = 1;
+
+ err = 0;
+ repstart = 0;
+ for (i = 0; i < nmsgs; i++) {
+ sc->sc_buffer = &msgs[i];
+ sc->sc_buffer_pos = 0;
+ sc->sc_error = 0;
+
+ /* Zero byte transfers aren't allowed. */
+ if (sc->sc_buffer == NULL || sc->sc_buffer->buf == NULL ||
+ sc->sc_buffer->len == 0) {
+ err = EINVAL;
+ break;
+ }
+
+ /* Check if the i2c bus is free. */
+ if (repstart == 0) {
+ /*
+ * On repeated start we send the START condition while
+ * the bus _is_ busy.
+ */
+ timeout = 0;
+ while (ti_i2c_read_2(sc, I2C_REG_STATUS_RAW) & I2C_STAT_BB) {
+ if (timeout++ > 100) {
+ err = EBUSY;
+ goto out;
+ }
+ DELAY(1000);
+ }
+ timeout = 0;
+ } else
+ repstart = 0;
+
+ if (sc->sc_buffer->flags & IIC_M_NOSTOP)
+ repstart = 1;
+
+ /* Set the slave address. */
+ ti_i2c_write_2(sc, I2C_REG_SA, msgs[i].slave >> 1);
+
+ /* Write the data length. */
+ ti_i2c_write_2(sc, I2C_REG_CNT, sc->sc_buffer->len);
+
+ /* Clear the RX and the TX FIFO. */
+ reg = ti_i2c_read_2(sc, I2C_REG_BUF);
+ reg |= I2C_BUF_RXFIFO_CLR | I2C_BUF_TXFIFO_CLR;
+ ti_i2c_write_2(sc, I2C_REG_BUF, reg);
+
+ reg = sc->sc_con_reg | I2C_CON_STT;
+ if (repstart == 0)
+ reg |= I2C_CON_STP;
+ if ((sc->sc_buffer->flags & IIC_M_RD) == 0)
+ reg |= I2C_CON_TRX;
+ ti_i2c_write_2(sc, I2C_REG_CON, reg);
+
+ /* Wait for an event. */
+ err = mtx_sleep(sc, &sc->sc_mtx, 0, "i2ciowait", sc->sc_timeout);
+ if (err == 0)
+ err = sc->sc_error;
+
+ if (err)
+ break;
+ }
+
+out:
+ if (timeout == 0) {
+ while (ti_i2c_read_2(sc, I2C_REG_STATUS_RAW) & I2C_STAT_BB) {
+ if (timeout++ > 100)
+ break;
+ DELAY(1000);
+ }
+ }
+ /* Put the controller in master mode again. */
+ if ((ti_i2c_read_2(sc, I2C_REG_CON) & I2C_CON_MST) == 0)
+ ti_i2c_write_2(sc, I2C_REG_CON, sc->sc_con_reg);
+
+ sc->sc_buffer = NULL;
+ sc->sc_bus_inuse = 0;
+
+ /* Wake up the processes that are waiting for the bus. */
+ wakeup(sc);
+
+ TI_I2C_UNLOCK(sc);
+
+ return (err);
+}
+
+static int
+ti_i2c_reset(struct ti_i2c_softc *sc, u_char speed)
+{
+ int timeout;
+ struct ti_i2c_clock_config *clkcfg;
+ u_int busfreq;
+ uint16_t fifo_trsh, reg, scll, sclh;
+
+ switch (ti_chip()) {
+#ifdef SOC_OMAP4
+ case CHIP_OMAP_4:
+ clkcfg = ti_omap4_i2c_clock_configs;
+ break;
+#endif
+#ifdef SOC_TI_AM335X
+ case CHIP_AM335X:
+ clkcfg = ti_am335x_i2c_clock_configs;
+ break;
+#endif
+ default:
+ panic("Unknown TI SoC, unable to reset the i2c");
+ }
+
+ /*
+ * If we haven't attached the bus yet, just init at the default slow
+ * speed. This lets us get the hardware initialized enough to attach
+ * the bus which is where the real speed configuration is handled. After
+ * the bus is attached, get the configured speed from it. Search the
+ * configuration table for the best speed we can do that doesn't exceed
+ * the requested speed.
+ */
+ if (sc->sc_iicbus == NULL)
+ busfreq = 100000;
+ else
+ busfreq = IICBUS_GET_FREQUENCY(sc->sc_iicbus, speed);
+ for (;;) {
+ if (clkcfg[1].frequency == 0 || clkcfg[1].frequency > busfreq)
+ break;
+ clkcfg++;
+ }
+
+ /*
+ * 23.1.4.3 - HS I2C Software Reset
+ * From OMAP4 TRM at page 4068.
+ *
+ * 1. Ensure that the module is disabled.
+ */
+ sc->sc_con_reg = 0;
+ ti_i2c_write_2(sc, I2C_REG_CON, sc->sc_con_reg);
+
+ /* 2. Issue a softreset to the controller. */
+ bus_write_2(sc->sc_mem_res, I2C_REG_SYSC, I2C_REG_SYSC_SRST);
+
+ /*
+ * 3. Enable the module.
+ * The I2Ci.I2C_SYSS[0] RDONE bit is asserted only after the module
+ * is enabled by setting the I2Ci.I2C_CON[15] I2C_EN bit to 1.
+ */
+ ti_i2c_write_2(sc, I2C_REG_CON, I2C_CON_I2C_EN);
+
+ /* 4. Wait for the software reset to complete. */
+ timeout = 0;
+ while ((ti_i2c_read_2(sc, I2C_REG_SYSS) & I2C_SYSS_RDONE) == 0) {
+ if (timeout++ > 100)
+ return (EBUSY);
+ DELAY(100);
+ }
+
+ /*
+ * Disable the I2C controller once again, now that the reset has
+ * finished.
+ */
+ ti_i2c_write_2(sc, I2C_REG_CON, sc->sc_con_reg);
+
+ /*
+ * The following sequence is taken from the OMAP4 TRM at page 4077.
+ *
+ * 1. Enable the functional and interface clocks (see Section
+ * 23.1.5.1.1.1.1). Done at ti_i2c_activate().
+ *
+ * 2. Program the prescaler to obtain an approximately 12MHz internal
+ * sampling clock (I2Ci_INTERNAL_CLK) by programming the
+ * corresponding value in the I2Ci.I2C_PSC[3:0] PSC field.
+ * This value depends on the frequency of the functional clock
+ * (I2Ci_FCLK). Because this frequency is 96MHz, the
+ * I2Ci.I2C_PSC[7:0] PSC field value is 0x7.
+ */
+ ti_i2c_write_2(sc, I2C_REG_PSC, clkcfg->psc);
+
+ /*
+ * 3. Program the I2Ci.I2C_SCLL[7:0] SCLL and I2Ci.I2C_SCLH[7:0] SCLH
+ * bit fields to obtain a bit rate of 100 Kbps, 400 Kbps or 1Mbps.
+ * These values depend on the internal sampling clock frequency
+ * (see Table 23-8).
+ */
+ scll = clkcfg->scll & I2C_SCLL_MASK;
+ sclh = clkcfg->sclh & I2C_SCLH_MASK;
+
+ /*
+ * 4. (Optional) Program the I2Ci.I2C_SCLL[15:8] HSSCLL and
+ * I2Ci.I2C_SCLH[15:8] HSSCLH fields to obtain a bit rate of
+ * 400K bps or 3.4M bps (for the second phase of HS mode). These
+ * values depend on the internal sampling clock frequency (see
+ * Table 23-8).
+ *
+ * 5. (Optional) If a bit rate of 3.4M bps is used and the bus line
+ * capacitance exceeds 45 pF, (see Section 18.4.8, PAD Functional
+ * Multiplexing and Configuration).
+ */
+ switch (ti_chip()) {
+#ifdef SOC_OMAP4
+ case CHIP_OMAP_4:
+ if ((clkcfg->hsscll + clkcfg->hssclh) > 0) {
+ scll |= clkcfg->hsscll << I2C_HSSCLL_SHIFT;
+ sclh |= clkcfg->hssclh << I2C_HSSCLH_SHIFT;
+ sc->sc_con_reg |= I2C_CON_OPMODE_HS;
+ }
+ break;
+#endif
+ }
+
+ /* Write the selected bit rate. */
+ ti_i2c_write_2(sc, I2C_REG_SCLL, scll);
+ ti_i2c_write_2(sc, I2C_REG_SCLH, sclh);
+
+ /*
+ * 6. Configure the Own Address of the I2C controller by storing it in
+ * the I2Ci.I2C_OA0 register. Up to four Own Addresses can be
+ * programmed in the I2Ci.I2C_OAi registers (where i = 0, 1, 2, 3)
+ * for each I2C controller.
+ *
+ * Note: For a 10-bit address, set the corresponding expand Own Address
+ * bit in the I2Ci.I2C_CON register.
+ *
+ * Driver currently always in single master mode so ignore this step.
+ */
+
+ /*
+ * 7. Set the TX threshold (in transmitter mode) and the RX threshold
+ * (in receiver mode) by setting the I2Ci.I2C_BUF[5:0]XTRSH field to
+ * (TX threshold - 1) and the I2Ci.I2C_BUF[13:8]RTRSH field to (RX
+ * threshold - 1), where the TX and RX thresholds are greater than
+ * or equal to 1.
+ *
+ * The threshold is set to 5 for now.
+ */
+ fifo_trsh = (sc->sc_fifo_trsh - 1) & I2C_BUF_TRSH_MASK;
+ reg = fifo_trsh | (fifo_trsh << I2C_BUF_RXTRSH_SHIFT);
+ ti_i2c_write_2(sc, I2C_REG_BUF, reg);
+
+ /*
+ * 8. Take the I2C controller out of reset by setting the
+ * I2Ci.I2C_CON[15] I2C_EN bit to 1.
+ *
+ * 23.1.5.1.1.1.2 - Initialize the I2C Controller
+ *
+ * To initialize the I2C controller, perform the following steps:
+ *
+ * 1. Configure the I2Ci.I2C_CON register:
+ * . For master or slave mode, set the I2Ci.I2C_CON[10] MST bit
+ * (0: slave, 1: master).
+ * . For transmitter or receiver mode, set the I2Ci.I2C_CON[9] TRX
+ * bit (0: receiver, 1: transmitter).
+ */
+
+ /* Enable the I2C controller in master mode. */
+ sc->sc_con_reg |= I2C_CON_I2C_EN | I2C_CON_MST;
+ ti_i2c_write_2(sc, I2C_REG_CON, sc->sc_con_reg);
+
+ /*
+ * 2. If using an interrupt to transmit/receive data, set the
+ * corresponding bit in the I2Ci.I2C_IE register (the I2Ci.I2C_IE[4]
+ * XRDY_IE bit for the transmit interrupt, the I2Ci.I2C_IE[3] RRDY
+ * bit for the receive interrupt).
+ */
+
+ /* Set the interrupts we want to be notified. */
+ reg = I2C_IE_XDR | /* Transmit draining interrupt. */
+ I2C_IE_XRDY | /* Transmit Data Ready interrupt. */
+ I2C_IE_RDR | /* Receive draining interrupt. */
+ I2C_IE_RRDY | /* Receive Data Ready interrupt. */
+ I2C_IE_ARDY | /* Register Access Ready interrupt. */
+ I2C_IE_NACK | /* No Acknowledgment interrupt. */
+ I2C_IE_AL; /* Arbitration lost interrupt. */
+
+ /* Enable the interrupts. */
+ ti_i2c_write_2(sc, I2C_REG_IRQENABLE_SET, reg);
+
+ /*
+ * 3. If using DMA to receive/transmit data, set to 1 the corresponding
+ * bit in the I2Ci.I2C_BUF register (the I2Ci.I2C_BUF[15] RDMA_EN
+ * bit for the receive DMA channel, the I2Ci.I2C_BUF[7] XDMA_EN bit
+ * for the transmit DMA channel).
+ *
+ * Not using DMA for now, so ignore this.
+ */
+
+ return (0);
+}
+
+static int
+ti_i2c_iicbus_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
+{
+ struct ti_i2c_softc *sc;
+ int err;
+
+ sc = device_get_softc(dev);
+ TI_I2C_LOCK(sc);
+ err = ti_i2c_reset(sc, speed);
+ TI_I2C_UNLOCK(sc);
+ if (err)
+ return (err);
+
+ return (IIC_ENOADDR);
+}
+
+static int
+ti_i2c_activate(device_t dev)
+{
+ int err;
+ struct ti_i2c_softc *sc;
+
+ sc = (struct ti_i2c_softc*)device_get_softc(dev);
+
+ /*
+ * 1. Enable the functional and interface clocks (see Section
+ * 23.1.5.1.1.1.1).
+ */
+ err = ti_sysc_clock_enable(device_get_parent(dev));
+ if (err)
+ return (err);
+
+ return (ti_i2c_reset(sc, IIC_UNKNOWN));
+}
+
+/**
+ * ti_i2c_deactivate - deactivates the controller and releases resources
+ * @dev: i2c device handle
+ *
+ *
+ *
+ * LOCKING:
+ * Assumed called in an atomic context.
+ *
+ * RETURNS:
+ * nothing
+ */
+static void
+ti_i2c_deactivate(device_t dev)
+{
+ struct ti_i2c_softc *sc = device_get_softc(dev);
+
+ /* Disable the controller - cancel all transactions. */
+ ti_i2c_write_2(sc, I2C_REG_IRQENABLE_CLR, 0xffff);
+ ti_i2c_write_2(sc, I2C_REG_STATUS, 0xffff);
+ ti_i2c_write_2(sc, I2C_REG_CON, 0);
+
+ /* Release the interrupt handler. */
+ if (sc->sc_irq_h != NULL) {
+ bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_irq_h);
+ sc->sc_irq_h = NULL;
+ }
+
+ /* Unmap the I2C controller registers. */
+ if (sc->sc_mem_res != NULL) {
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+ sc->sc_mem_res = NULL;
+ }
+
+ /* Release the IRQ resource. */
+ if (sc->sc_irq_res != NULL) {
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
+ sc->sc_irq_res = NULL;
+ }
+
+ /* Finally disable the functional and interface clocks. */
+ ti_sysc_clock_disable(device_get_parent(dev));
+}
+
+static int
+ti_i2c_sysctl_clk(SYSCTL_HANDLER_ARGS)
+{
+ int clk, psc, sclh, scll;
+ struct ti_i2c_softc *sc;
+
+ sc = arg1;
+
+ TI_I2C_LOCK(sc);
+ /* Get the system prescaler value. */
+ psc = (int)ti_i2c_read_2(sc, I2C_REG_PSC) + 1;
+
+ /* Get the bitrate. */
+ scll = (int)ti_i2c_read_2(sc, I2C_REG_SCLL) & I2C_SCLL_MASK;
+ sclh = (int)ti_i2c_read_2(sc, I2C_REG_SCLH) & I2C_SCLH_MASK;
+
+ clk = I2C_CLK / psc / (scll + 7 + sclh + 5);
+ TI_I2C_UNLOCK(sc);
+
+ return (sysctl_handle_int(oidp, &clk, 0, req));
+}
+
+static int
+ti_i2c_sysctl_timeout(SYSCTL_HANDLER_ARGS)
+{
+ struct ti_i2c_softc *sc;
+ unsigned int val;
+ int err;
+
+ sc = arg1;
+
+ /*
+ * MTX_DEF lock can't be held while doing uimove in
+ * sysctl_handle_int
+ */
+ TI_I2C_LOCK(sc);
+ val = sc->sc_timeout;
+ TI_I2C_UNLOCK(sc);
+
+ err = sysctl_handle_int(oidp, &val, 0, req);
+ /* Write request? */
+ if ((err == 0) && (req->newptr != NULL)) {
+ TI_I2C_LOCK(sc);
+ sc->sc_timeout = val;
+ TI_I2C_UNLOCK(sc);
+ }
+
+ return (err);
+}
+
+static int
+ti_i2c_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+ if (!ofw_bus_is_compatible(dev, "ti,omap4-i2c"))
+ return (ENXIO);
+ device_set_desc(dev, "TI I2C Controller");
+
+ return (0);
+}
+
+static int
+ti_i2c_attach(device_t dev)
+{
+ int err, rid;
+ struct ti_i2c_softc *sc;
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid_list *tree;
+ uint16_t fifosz;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+
+ /* Get the memory resource for the register mapping. */
+ rid = 0;
+ sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->sc_mem_res == NULL) {
+ device_printf(dev, "Cannot map registers.\n");
+ return (ENXIO);
+ }
+
+ /* Allocate our IRQ resource. */
+ rid = 0;
+ sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE | RF_SHAREABLE);
+ if (sc->sc_irq_res == NULL) {
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+ device_printf(dev, "Cannot allocate interrupt.\n");
+ return (ENXIO);
+ }
+
+ TI_I2C_LOCK_INIT(sc);
+
+ /* First of all, we _must_ activate the H/W. */
+ err = ti_i2c_activate(dev);
+ if (err) {
+ device_printf(dev, "ti_i2c_activate failed\n");
+ goto out;
+ }
+
+ /* Read the version number of the I2C module */
+ sc->sc_rev = ti_i2c_read_2(sc, I2C_REG_REVNB_HI) & 0xff;
+
+ /* Get the fifo size. */
+ fifosz = ti_i2c_read_2(sc, I2C_REG_BUFSTAT);
+ fifosz >>= I2C_BUFSTAT_FIFODEPTH_SHIFT;
+ fifosz &= I2C_BUFSTAT_FIFODEPTH_MASK;
+
+ device_printf(dev, "I2C revision %d.%d FIFO size: %d bytes\n",
+ sc->sc_rev >> 4, sc->sc_rev & 0xf, 8 << fifosz);
+
+ /* Set the FIFO threshold to 5 for now. */
+ sc->sc_fifo_trsh = 5;
+
+ /* Set I2C bus timeout */
+ sc->sc_timeout = 5*hz;
+
+ ctx = device_get_sysctl_ctx(dev);
+ tree = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
+ SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "i2c_clock",
+ CTLFLAG_RD | CTLTYPE_UINT | CTLFLAG_MPSAFE, sc, 0,
+ ti_i2c_sysctl_clk, "IU", "I2C bus clock");
+
+ SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "i2c_timeout",
+ CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_MPSAFE, sc, 0,
+ ti_i2c_sysctl_timeout, "IU", "I2C bus timeout (in ticks)");
+
+ /* Activate the interrupt. */
+ err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+ NULL, ti_i2c_intr, sc, &sc->sc_irq_h);
+ if (err)
+ goto out;
+
+ /* Attach the iicbus. */
+ if ((sc->sc_iicbus = device_add_child(dev, "iicbus", -1)) == NULL) {
+ device_printf(dev, "could not allocate iicbus instance\n");
+ err = ENXIO;
+ goto out;
+ }
+
+ /* Probe and attach the iicbus when interrupts are available. */
+ err = bus_delayed_attach_children(dev);
+
+out:
+ if (err) {
+ ti_i2c_deactivate(dev);
+ TI_I2C_LOCK_DESTROY(sc);
+ }
+
+ return (err);
+}
+
+static int
+ti_i2c_detach(device_t dev)
+{
+ struct ti_i2c_softc *sc;
+ int rv;
+
+ sc = device_get_softc(dev);
+
+ if ((rv = bus_generic_detach(dev)) != 0) {
+ device_printf(dev, "cannot detach child devices\n");
+ return (rv);
+ }
+
+ if (sc->sc_iicbus &&
+ (rv = device_delete_child(dev, sc->sc_iicbus)) != 0)
+ return (rv);
+
+ ti_i2c_deactivate(dev);
+ TI_I2C_LOCK_DESTROY(sc);
+
+ return (0);
+}
+
+static phandle_t
+ti_i2c_get_node(device_t bus, device_t dev)
+{
+
+ /* Share controller node with iibus device. */
+ return (ofw_bus_get_node(bus));
+}
+
+static device_method_t ti_i2c_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ti_i2c_probe),
+ DEVMETHOD(device_attach, ti_i2c_attach),
+ DEVMETHOD(device_detach, ti_i2c_detach),
+
+ /* Bus interface */
+ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
+ DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
+ DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource),
+ DEVMETHOD(bus_release_resource, bus_generic_release_resource),
+ DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
+ DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource),
+ DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource),
+ DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource),
+
+ /* OFW methods */
+ DEVMETHOD(ofw_bus_get_node, ti_i2c_get_node),
+
+ /* iicbus interface */
+ DEVMETHOD(iicbus_callback, iicbus_null_callback),
+ DEVMETHOD(iicbus_reset, ti_i2c_iicbus_reset),
+ DEVMETHOD(iicbus_transfer, ti_i2c_transfer),
+
+ DEVMETHOD_END
+};
+
+static driver_t ti_i2c_driver = {
+ "iichb",
+ ti_i2c_methods,
+ sizeof(struct ti_i2c_softc),
+};
+
+static devclass_t ti_i2c_devclass;
+
+DRIVER_MODULE(ti_iic, simplebus, ti_i2c_driver, ti_i2c_devclass, 0, 0);
+DRIVER_MODULE(iicbus, ti_iic, iicbus_driver, iicbus_devclass, 0, 0);
+
+MODULE_DEPEND(ti_iic, ti_sysc, 1, 1, 1);
+MODULE_DEPEND(ti_iic, iicbus, 1, 1, 1);
diff --git a/sys/arm/ti/ti_i2c.h b/sys/arm/ti/ti_i2c.h
new file mode 100644
index 000000000000..835fa58d6951
--- /dev/null
+++ b/sys/arm/ti/ti_i2c.h
@@ -0,0 +1,133 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2011
+ * Ben Gray <ben.r.gray@gmail.com>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _TI_I2C_H_
+#define _TI_I2C_H_
+
+/**
+ * Header file for the OMAP I2C driver.
+ *
+ * Simply contains register bit flags.
+ */
+
+/*
+ * OMAP4 I2C Registers, Summary 1
+ */
+#define I2C_REG_IE 0x84
+#define I2C_IE_XDR (1UL << 14) /* Transmit draining interrupt */
+#define I2C_IE_RDR (1UL << 13) /* Receive draining interrupt */
+#define I2C_IE_AAS (1UL << 9) /* Addressed as Slave interrupt */
+#define I2C_IE_BF (1UL << 8) /* Bus Free interrupt */
+#define I2C_IE_AERR (1UL << 7) /* Access Error interrupt */
+#define I2C_IE_STC (1UL << 6) /* Start Condition interrupt */
+#define I2C_IE_GC (1UL << 5) /* General Call interrupt */
+#define I2C_IE_XRDY (1UL << 4) /* Transmit Data Ready interrupt */
+#define I2C_IE_RRDY (1UL << 3) /* Receive Data Ready interrupt */
+#define I2C_IE_ARDY (1UL << 2) /* Register Access Ready interrupt */
+#define I2C_IE_NACK (1UL << 1) /* No Acknowledgment interrupt */
+#define I2C_IE_AL (1UL << 0) /* Arbitration Lost interrupt */
+#define I2C_REG_STAT 0x88
+#define I2C_STAT_XDR (1UL << 14)
+#define I2C_STAT_RDR (1UL << 13)
+#define I2C_STAT_BB (1UL << 12)
+#define I2C_STAT_ROVR (1UL << 11)
+#define I2C_STAT_XUDF (1UL << 10)
+#define I2C_STAT_AAS (1UL << 9)
+#define I2C_STAT_BF (1UL << 8)
+#define I2C_STAT_AERR (1UL << 7)
+#define I2C_STAT_STC (1UL << 6)
+#define I2C_STAT_GC (1UL << 5)
+#define I2C_STAT_XRDY (1UL << 4)
+#define I2C_STAT_RRDY (1UL << 3)
+#define I2C_STAT_ARDY (1UL << 2)
+#define I2C_STAT_NACK (1UL << 1)
+#define I2C_STAT_AL (1UL << 0)
+#define I2C_REG_SYSS 0x90
+#define I2C_SYSS_RDONE (1UL << 0)
+#define I2C_REG_BUF 0x94
+#define I2C_BUF_RXFIFO_CLR (1UL << 14)
+#define I2C_BUF_TXFIFO_CLR (1UL << 6)
+#define I2C_BUF_RXTRSH_SHIFT 8
+#define I2C_BUF_TRSH_MASK 0x3f
+#define I2C_REG_CNT 0x98
+#define I2C_REG_DATA 0x9c
+#define I2C_REG_CON 0xa4
+#define I2C_CON_I2C_EN (1UL << 15)
+#define I2C_CON_OPMODE_STD (0UL << 12)
+#define I2C_CON_OPMODE_HS (1UL << 12)
+#define I2C_CON_OPMODE_SCCB (2UL << 12)
+#define I2C_CON_OPMODE_MASK (3UL << 13)
+#define I2C_CON_I2C_STB (1UL << 11)
+#define I2C_CON_MST (1UL << 10)
+#define I2C_CON_TRX (1UL << 9)
+#define I2C_CON_XSA (1UL << 8)
+#define I2C_CON_XOA0 (1UL << 7)
+#define I2C_CON_XOA1 (1UL << 6)
+#define I2C_CON_XOA2 (1UL << 5)
+#define I2C_CON_XOA3 (1UL << 4)
+#define I2C_CON_STP (1UL << 1)
+#define I2C_CON_STT (1UL << 0)
+#define I2C_REG_OA0 0xa8
+#define I2C_REG_SA 0xac
+#define I2C_REG_PSC 0xb0
+#define I2C_PSC_MASK 0xff
+#define I2C_REG_SCLL 0xb4
+#define I2C_SCLL_MASK 0xff
+#define I2C_HSSCLL_SHIFT 8
+#define I2C_REG_SCLH 0xb8
+#define I2C_SCLH_MASK 0xff
+#define I2C_HSSCLH_SHIFT 8
+#define I2C_REG_SYSTEST 0xbc
+#define I2C_REG_BUFSTAT 0xc0
+#define I2C_BUFSTAT_FIFODEPTH_MASK 0x3
+#define I2C_BUFSTAT_FIFODEPTH_SHIFT 14
+#define I2C_REG_OA1 0xc4
+#define I2C_REG_OA2 0xc8
+#define I2C_REG_OA3 0xcc
+#define I2C_REG_ACTOA 0xd0
+#define I2C_REG_SBLOCK 0xd4
+
+/*
+ * OMAP4 I2C Registers, Summary 2
+ */
+#define I2C_REG_REVNB_LO 0x00
+#define I2C_REG_REVNB_HI 0x04
+#define I2C_REG_SYSC 0x10
+#define I2C_REG_SYSC_SRST (1UL << 1)
+#define I2C_REG_STATUS_RAW 0x24
+#define I2C_REG_STATUS 0x28
+#define I2C_REG_IRQENABLE_SET 0x2C
+#define I2C_REG_IRQENABLE_CLR 0x30
+
+#define I2C_CLK 96000000UL /* 96MHz */
+#define I2C_ICLK 12000000UL /* 12MHz */
+
+#endif /* _TI_I2C_H_ */
diff --git a/sys/arm/ti/ti_machdep.c b/sys/arm/ti/ti_machdep.c
new file mode 100644
index 000000000000..33d24cda1fa6
--- /dev/null
+++ b/sys/arm/ti/ti_machdep.c
@@ -0,0 +1,166 @@
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 1994-1998 Mark Brinicombe.
+ * Copyright (c) 1994 Brini.
+ * All rights reserved.
+ *
+ * This code is derived from software written for Brini by Mark Brinicombe
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Brini.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: FreeBSD: //depot/projects/arm/src/sys/arm/at91/kb920x_machdep.c, rev 45
+ */
+
+#include "opt_platform.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/devmap.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/bus.h>
+#include <machine/machdep.h>
+#include <machine/platformvar.h>
+
+#include <arm/ti/omap4/omap4_machdep.h>
+#include <arm/ti/omap4/omap4_reg.h>
+#include <arm/ti/ti_cpuid.h>
+
+#include "platform_if.h"
+
+#if defined(SOC_OMAP4)
+#include "platform_pl310_if.h"
+
+static platform_attach_t omap4_attach;
+static platform_devmap_init_t ti_omap4_devmap_init;
+#endif
+#if defined(SOC_TI_AM335X)
+static platform_attach_t ti_am335x_attach;
+static platform_devmap_init_t ti_am335x_devmap_init;
+#endif
+static platform_cpu_reset_t ti_plat_cpu_reset;
+
+void (*ti_cpu_reset)(void) = NULL;
+
+int _ti_chip = -1;
+
+#if defined(SOC_OMAP4)
+static int
+omap4_attach(platform_t plat)
+{
+ _ti_chip = CHIP_OMAP_4;
+ return (0);
+}
+#endif
+
+#if defined(SOC_TI_AM335X)
+static int
+ti_am335x_attach(platform_t plat)
+{
+ _ti_chip = CHIP_AM335X;
+ return (0);
+}
+#endif
+
+/*
+ * Construct static devmap entries to map out the most frequently used
+ * peripherals using 1mb section mappings.
+ */
+#if defined(SOC_OMAP4)
+static int
+ti_omap4_devmap_init(platform_t plat)
+{
+ devmap_add_entry(0x48000000, 0x01000000); /*16mb L4_PER devices */
+ devmap_add_entry(0x4A000000, 0x01000000); /*16mb L4_CFG devices */
+ return (0);
+}
+#endif
+
+#if defined(SOC_TI_AM335X)
+static int
+ti_am335x_devmap_init(platform_t plat)
+{
+
+ devmap_add_entry(0x44C00000, 0x00400000); /* 4mb L4_WKUP devices*/
+ devmap_add_entry(0x47400000, 0x00100000); /* 1mb USB */
+ devmap_add_entry(0x47800000, 0x00100000); /* 1mb mmchs2 */
+ devmap_add_entry(0x48000000, 0x01000000); /*16mb L4_PER devices */
+ devmap_add_entry(0x49000000, 0x00100000); /* 1mb edma3 */
+ devmap_add_entry(0x49800000, 0x00300000); /* 3mb edma3 */
+ devmap_add_entry(0x4A000000, 0x01000000); /*16mb L4_FAST devices*/
+ return (0);
+}
+#endif
+
+static void
+ti_plat_cpu_reset(platform_t plat)
+{
+ if (ti_cpu_reset)
+ (*ti_cpu_reset)();
+ else
+ printf("no cpu_reset implementation\n");
+}
+
+#if defined(SOC_OMAP4)
+static platform_method_t omap4_methods[] = {
+ PLATFORMMETHOD(platform_attach, omap4_attach),
+ PLATFORMMETHOD(platform_devmap_init, ti_omap4_devmap_init),
+ PLATFORMMETHOD(platform_cpu_reset, ti_plat_cpu_reset),
+
+#ifdef SMP
+ PLATFORMMETHOD(platform_mp_start_ap, omap4_mp_start_ap),
+ PLATFORMMETHOD(platform_mp_setmaxid, omap4_mp_setmaxid),
+#endif
+
+ PLATFORMMETHOD(platform_pl310_init, omap4_pl310_init),
+ PLATFORMMETHOD(platform_pl310_write_ctrl, omap4_pl310_write_ctrl),
+ PLATFORMMETHOD(platform_pl310_write_debug, omap4_pl310_write_debug),
+
+ PLATFORMMETHOD_END,
+};
+FDT_PLATFORM_DEF(omap4, "omap4", 0, "ti,omap4430", 200);
+#endif
+
+#if defined(SOC_TI_AM335X)
+static platform_method_t am335x_methods[] = {
+ PLATFORMMETHOD(platform_attach, ti_am335x_attach),
+ PLATFORMMETHOD(platform_devmap_init, ti_am335x_devmap_init),
+ PLATFORMMETHOD(platform_cpu_reset, ti_plat_cpu_reset),
+
+ PLATFORMMETHOD_END,
+};
+
+FDT_PLATFORM_DEF(am335x, "am335x", 0, "ti,am33xx", 200);
+#endif
diff --git a/sys/arm/ti/ti_mbox.c b/sys/arm/ti/ti_mbox.c
new file mode 100644
index 000000000000..f77f2d9eafbf
--- /dev/null
+++ b/sys/arm/ti/ti_mbox.c
@@ -0,0 +1,268 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Rui Paulo <rpaulo@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/frame.h>
+#include <machine/intr.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+
+#include <arm/ti/ti_mbox.h>
+#include <arm/ti/ti_sysc.h>
+
+#include "mbox_if.h"
+
+#ifdef DEBUG
+#define DPRINTF(fmt, ...) do { \
+ printf("%s: ", __func__); \
+ printf(fmt, __VA_ARGS__); \
+} while (0)
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
+static device_probe_t ti_mbox_probe;
+static device_attach_t ti_mbox_attach;
+static device_detach_t ti_mbox_detach;
+static void ti_mbox_intr(void *);
+static int ti_mbox_read(device_t, int, uint32_t *);
+static int ti_mbox_write(device_t, int, uint32_t);
+
+struct ti_mbox_softc {
+ struct mtx sc_mtx;
+ struct resource *sc_mem_res;
+ struct resource *sc_irq_res;
+ void *sc_intr;
+ bus_space_tag_t sc_bt;
+ bus_space_handle_t sc_bh;
+};
+
+#define TI_MBOX_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
+#define TI_MBOX_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
+
+static device_method_t ti_mbox_methods[] = {
+ DEVMETHOD(device_probe, ti_mbox_probe),
+ DEVMETHOD(device_attach, ti_mbox_attach),
+ DEVMETHOD(device_detach, ti_mbox_detach),
+
+ DEVMETHOD(mbox_read, ti_mbox_read),
+ DEVMETHOD(mbox_write, ti_mbox_write),
+
+ DEVMETHOD_END
+};
+
+static driver_t ti_mbox_driver = {
+ "ti_mbox",
+ ti_mbox_methods,
+ sizeof(struct ti_mbox_softc)
+};
+
+static devclass_t ti_mbox_devclass;
+
+DRIVER_MODULE(ti_mbox, simplebus, ti_mbox_driver, ti_mbox_devclass, 0, 0);
+MODULE_DEPEND(ti_mbox, ti_sysc, 1, 1, 1);
+
+static __inline uint32_t
+ti_mbox_reg_read(struct ti_mbox_softc *sc, uint16_t reg)
+{
+ return (bus_space_read_4(sc->sc_bt, sc->sc_bh, reg));
+}
+
+static __inline void
+ti_mbox_reg_write(struct ti_mbox_softc *sc, uint16_t reg, uint32_t val)
+{
+ bus_space_write_4(sc->sc_bt, sc->sc_bh, reg, val);
+}
+
+static int
+ti_mbox_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_is_compatible(dev, "ti,omap4-mailbox")) {
+ device_set_desc(dev, "TI System Mailbox");
+ return (BUS_PROBE_DEFAULT);
+ }
+
+ return (ENXIO);
+}
+
+static int
+ti_mbox_attach(device_t dev)
+{
+ struct ti_mbox_softc *sc;
+ int rid, delay, chan;
+ uint32_t rev, sysconfig;
+
+ if (ti_sysc_clock_enable(device_get_parent(dev)) != 0) {
+ device_printf(dev, "could not enable MBOX clock\n");
+ return (ENXIO);
+ }
+
+ sc = device_get_softc(dev);
+ rid = 0;
+ mtx_init(&sc->sc_mtx, "TI mbox", NULL, MTX_DEF);
+ sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->sc_mem_res == NULL) {
+ device_printf(dev, "could not allocate memory resource\n");
+ return (ENXIO);
+ }
+ sc->sc_bt = rman_get_bustag(sc->sc_mem_res);
+ sc->sc_bh = rman_get_bushandle(sc->sc_mem_res);
+ rid = 0;
+ sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (sc->sc_irq_res == NULL) {
+ device_printf(dev, "could not allocate interrupt resource\n");
+ ti_mbox_detach(dev);
+ return (ENXIO);
+ }
+ if (bus_setup_intr(dev, sc->sc_irq_res, INTR_MPSAFE | INTR_TYPE_MISC,
+ NULL, ti_mbox_intr, sc, &sc->sc_intr) != 0) {
+ device_printf(dev, "unable to setup the interrupt handler\n");
+ ti_mbox_detach(dev);
+ return (ENXIO);
+ }
+ /*
+ * Reset the controller.
+ */
+ sysconfig = ti_mbox_reg_read(sc, TI_MBOX_SYSCONFIG);
+ DPRINTF("initial sysconfig %d\n", sysconfig);
+ sysconfig |= TI_MBOX_SYSCONFIG_SOFTRST;
+ delay = 100;
+ while (ti_mbox_reg_read(sc, TI_MBOX_SYSCONFIG) &
+ TI_MBOX_SYSCONFIG_SOFTRST) {
+ delay--;
+ DELAY(10);
+ }
+ if (delay == 0) {
+ device_printf(dev, "controller reset failed\n");
+ ti_mbox_detach(dev);
+ return (ENXIO);
+ }
+ /*
+ * Enable smart idle mode.
+ */
+ ti_mbox_reg_write(sc, TI_MBOX_SYSCONFIG,
+ ti_mbox_reg_read(sc, TI_MBOX_SYSCONFIG) | TI_MBOX_SYSCONFIG_SMARTIDLE);
+ rev = ti_mbox_reg_read(sc,
+ ti_sysc_get_rev_address_offset_host(device_get_parent(dev)));
+ DPRINTF("rev %d\n", rev);
+ device_printf(dev, "revision %d.%d\n", (rev >> 8) & 0x4, rev & 0x40);
+ /*
+ * Enable message interrupts.
+ */
+ for (chan = 0; chan < 8; chan++)
+ ti_mbox_reg_write(sc, TI_MBOX_IRQENABLE_SET(chan), 1);
+
+ return (0);
+}
+
+static int
+ti_mbox_detach(device_t dev)
+{
+ struct ti_mbox_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (sc->sc_intr)
+ bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr);
+ if (sc->sc_irq_res)
+ bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->sc_irq_res),
+ sc->sc_irq_res);
+ if (sc->sc_mem_res)
+ bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->sc_mem_res),
+ sc->sc_mem_res);
+
+ return (0);
+}
+
+static void
+ti_mbox_intr(void *arg)
+{
+ struct ti_mbox_softc *sc;
+
+ sc = arg;
+ DPRINTF("interrupt %p", sc);
+}
+
+static int
+ti_mbox_read(device_t dev, int chan, uint32_t *data)
+{
+ struct ti_mbox_softc *sc;
+
+ if (chan < 0 || chan > 7)
+ return (EINVAL);
+ sc = device_get_softc(dev);
+
+ return (ti_mbox_reg_read(sc, TI_MBOX_MESSAGE(chan)));
+}
+
+static int
+ti_mbox_write(device_t dev, int chan, uint32_t data)
+{
+ int limit = 500;
+ struct ti_mbox_softc *sc;
+
+ if (chan < 0 || chan > 7)
+ return (EINVAL);
+ sc = device_get_softc(dev);
+ TI_MBOX_LOCK(sc);
+ /* XXX implement interrupt method */
+ while (ti_mbox_reg_read(sc, TI_MBOX_FIFOSTATUS(chan)) == 1 &&
+ limit--) {
+ DELAY(10);
+ }
+ if (limit == 0) {
+ device_printf(dev, "FIFOSTAUS%d stuck\n", chan);
+ TI_MBOX_UNLOCK(sc);
+ return (EAGAIN);
+ }
+ ti_mbox_reg_write(sc, TI_MBOX_MESSAGE(chan), data);
+
+ return (0);
+}
diff --git a/sys/arm/ti/ti_mbox.h b/sys/arm/ti/ti_mbox.h
new file mode 100644
index 000000000000..31f82a3ce7b1
--- /dev/null
+++ b/sys/arm/ti/ti_mbox.h
@@ -0,0 +1,46 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Rui Paulo <rpaulo@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _TI_MBOX_H_
+#define _TI_MBOX_H_
+
+#define TI_MBOX_REVISION 0x00
+#define TI_MBOX_SYSCONFIG 0x10
+#define TI_MBOX_SYSCONFIG_SOFTRST 0x01
+#define TI_MBOX_SYSCONFIG_SMARTIDLE (0x02 << 2)
+#define TI_MBOX_MESSAGE(n) (0x40 + (n) * 0x4)
+#define TI_MBOX_FIFOSTATUS(n) (0x80 + (n) * 0x4)
+#define TI_MBOX_MSGSTATUS(n) (0xc0 + (n) * 0x4)
+#define TI_MBOX_IRQSTATUS_RAW(n) (0x100 + (n) * 0x10)
+#define TI_MBOX_IRQSTATUS_CLR(n) (0x104 + (n) * 0x10)
+#define TI_MBOX_IRQENABLE_SET(n) (0x108 + (n) * 0x10)
+#define TI_MBOX_IRQENABLE_CLR(n) (0x10c + (n) * 0x10)
+
+#endif /* _TI_MBOX_H_ */
diff --git a/sys/arm/ti/ti_omap4_cm.c b/sys/arm/ti/ti_omap4_cm.c
new file mode 100644
index 000000000000..795bd2b75206
--- /dev/null
+++ b/sys/arm/ti/ti_omap4_cm.c
@@ -0,0 +1,150 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2019 Emmanuel Vadot <manu@FreeBSD.org>
+ * Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Based on sys/arm/ti/ti_sysc.c
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/fbio.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/resource.h>
+#include <machine/bus.h>
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_kern.h>
+#include <vm/pmap.h>
+
+#include <dev/fdt/simplebus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/ti/ti_omap4_cm.h>
+
+static struct ofw_compat_data compat_data[] = {
+ { "ti,omap4-cm", 1 },
+ { NULL, 0 }
+};
+
+struct ti_omap4_cm_softc {
+ struct simplebus_softc sc;
+ device_t dev;
+};
+
+uint64_t
+ti_omap4_cm_get_simplebus_base_host(device_t dev) {
+ struct ti_omap4_cm_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (sc->sc.nranges == 0)
+ return (0);
+
+ return (sc->sc.ranges[0].host);
+}
+
+static int ti_omap4_cm_probe(device_t dev);
+static int ti_omap4_cm_attach(device_t dev);
+static int ti_omap4_cm_detach(device_t dev);
+
+static int
+ti_omap4_cm_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "TI OMAP4-CM");
+ if (!bootverbose)
+ device_quiet(dev);
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ti_omap4_cm_attach(device_t dev)
+{
+ struct ti_omap4_cm_softc *sc;
+ device_t cdev;
+ phandle_t node, child;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ node = ofw_bus_get_node(dev);
+
+ simplebus_init(dev, node);
+ if (simplebus_fill_ranges(node, &sc->sc) < 0) {
+ device_printf(dev, "could not get ranges\n");
+ return (ENXIO);
+ }
+
+ bus_generic_probe(sc->dev);
+
+ for (child = OF_child(node); child > 0; child = OF_peer(child)) {
+ cdev = simplebus_add_device(dev, child, 0, NULL, -1, NULL);
+ if (cdev != NULL)
+ device_probe_and_attach(cdev);
+ }
+
+ return (bus_generic_attach(dev));
+}
+
+static int
+ti_omap4_cm_detach(device_t dev)
+{
+ return (EBUSY);
+}
+
+static device_method_t ti_omap4_cm_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ti_omap4_cm_probe),
+ DEVMETHOD(device_attach, ti_omap4_cm_attach),
+ DEVMETHOD(device_detach, ti_omap4_cm_detach),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(ti_omap4_cm, ti_omap4_cm_driver, ti_omap4_cm_methods,
+ sizeof(struct ti_omap4_cm_softc), simplebus_driver);
+
+static devclass_t ti_omap4_cm_devclass;
+
+EARLY_DRIVER_MODULE(ti_omap4_cm, simplebus, ti_omap4_cm_driver,
+ti_omap4_cm_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_FIRST);
+
+EARLY_DRIVER_MODULE(ti_omap4_cm, ofwbus, ti_omap4_cm_driver,
+ti_omap4_cm_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_FIRST);
diff --git a/sys/arm/ti/ti_omap4_cm.h b/sys/arm/ti/ti_omap4_cm.h
new file mode 100644
index 000000000000..4da56520cc97
--- /dev/null
+++ b/sys/arm/ti/ti_omap4_cm.h
@@ -0,0 +1,34 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef __TI_OMAP4_CM__
+#define __TI_OMAP4_CM__
+
+uint64_t ti_omap4_cm_get_simplebus_base_host(device_t dev);
+
+#endif /* __TI_OMAP4_CM__ */
diff --git a/sys/arm/ti/ti_pinmux.c b/sys/arm/ti/ti_pinmux.c
new file mode 100644
index 000000000000..58d3b93c46b5
--- /dev/null
+++ b/sys/arm/ti/ti_pinmux.c
@@ -0,0 +1,460 @@
+/*
+ * Copyright (c) 2010
+ * Ben Gray <ben.r.gray@gmail.com>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Ben Gray.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BEN GRAY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Exposes pinmux module to pinctrl-compatible interface
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/fdt/fdt_pinctrl.h>
+
+#include <arm/ti/omap4/omap4_scm_padconf.h>
+#include <arm/ti/am335x/am335x_scm_padconf.h>
+#include <arm/ti/ti_cpuid.h>
+#include "ti_pinmux.h"
+
+struct pincfg {
+ uint32_t reg;
+ uint32_t conf;
+};
+
+static struct resource_spec ti_pinmux_res_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Control memory window */
+ { -1, 0 }
+};
+
+static struct ti_pinmux_softc *ti_pinmux_sc;
+
+#define ti_pinmux_read_2(sc, reg) \
+ bus_space_read_2((sc)->sc_bst, (sc)->sc_bsh, (reg))
+#define ti_pinmux_write_2(sc, reg, val) \
+ bus_space_write_2((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
+#define ti_pinmux_read_4(sc, reg) \
+ bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
+#define ti_pinmux_write_4(sc, reg, val) \
+ bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
+
+/**
+ * ti_padconf_devmap - Array of pins, should be defined one per SoC
+ *
+ * This array is typically defined in one of the targeted *_scm_pinumx.c
+ * files and is specific to the given SoC platform. Each entry in the array
+ * corresponds to an individual pin.
+ */
+static const struct ti_pinmux_device *ti_pinmux_dev;
+
+/**
+ * ti_pinmux_padconf_from_name - searches the list of pads and returns entry
+ * with matching ball name.
+ * @ballname: the name of the ball
+ *
+ * RETURNS:
+ * A pointer to the matching padconf or NULL if the ball wasn't found.
+ */
+static const struct ti_pinmux_padconf*
+ti_pinmux_padconf_from_name(const char *ballname)
+{
+ const struct ti_pinmux_padconf *padconf;
+
+ padconf = ti_pinmux_dev->padconf;
+ while (padconf->ballname != NULL) {
+ if (strcmp(ballname, padconf->ballname) == 0)
+ return(padconf);
+ padconf++;
+ }
+
+ return (NULL);
+}
+
+/**
+ * ti_pinmux_padconf_set_internal - sets the muxmode and state for a pad/pin
+ * @padconf: pointer to the pad structure
+ * @muxmode: the name of the mode to use for the pin, i.e. "uart1_rx"
+ * @state: the state to put the pad/pin in, i.e. PADCONF_PIN_???
+ *
+ *
+ * LOCKING:
+ * Internally locks it's own context.
+ *
+ * RETURNS:
+ * 0 on success.
+ * EINVAL if pin requested is outside valid range or already in use.
+ */
+static int
+ti_pinmux_padconf_set_internal(struct ti_pinmux_softc *sc,
+ const struct ti_pinmux_padconf *padconf,
+ const char *muxmode, unsigned int state)
+{
+ unsigned int mode;
+ uint16_t reg_val;
+
+ /* populate the new value for the PADCONF register */
+ reg_val = (uint16_t)(state & ti_pinmux_dev->padconf_sate_mask);
+
+ /* find the new mode requested */
+ for (mode = 0; mode < 8; mode++) {
+ if ((padconf->muxmodes[mode] != NULL) &&
+ (strcmp(padconf->muxmodes[mode], muxmode) == 0)) {
+ break;
+ }
+ }
+
+ /* couldn't find the mux mode */
+ if (mode >= 8) {
+ printf("Invalid mode \"%s\"\n", muxmode);
+ return (EINVAL);
+ }
+
+ /* set the mux mode */
+ reg_val |= (uint16_t)(mode & ti_pinmux_dev->padconf_muxmode_mask);
+
+ if (bootverbose)
+ device_printf(sc->sc_dev, "setting internal %x for %s\n",
+ reg_val, muxmode);
+ /* write the register value (16-bit writes) */
+ ti_pinmux_write_2(sc, padconf->reg_off, reg_val);
+
+ return (0);
+}
+
+/**
+ * ti_pinmux_padconf_set - sets the muxmode and state for a pad/pin
+ * @padname: the name of the pad, i.e. "c12"
+ * @muxmode: the name of the mode to use for the pin, i.e. "uart1_rx"
+ * @state: the state to put the pad/pin in, i.e. PADCONF_PIN_???
+ *
+ *
+ * LOCKING:
+ * Internally locks it's own context.
+ *
+ * RETURNS:
+ * 0 on success.
+ * EINVAL if pin requested is outside valid range or already in use.
+ */
+int
+ti_pinmux_padconf_set(const char *padname, const char *muxmode, unsigned int state)
+{
+ const struct ti_pinmux_padconf *padconf;
+
+ if (!ti_pinmux_sc)
+ return (ENXIO);
+
+ /* find the pin in the devmap */
+ padconf = ti_pinmux_padconf_from_name(padname);
+ if (padconf == NULL)
+ return (EINVAL);
+
+ return (ti_pinmux_padconf_set_internal(ti_pinmux_sc, padconf, muxmode, state));
+}
+
+/**
+ * ti_pinmux_padconf_get - gets the muxmode and state for a pad/pin
+ * @padname: the name of the pad, i.e. "c12"
+ * @muxmode: upon return will contain the name of the muxmode of the pin
+ * @state: upon return will contain the state of the pad/pin
+ *
+ *
+ * LOCKING:
+ * Internally locks it's own context.
+ *
+ * RETURNS:
+ * 0 on success.
+ * EINVAL if pin requested is outside valid range or already in use.
+ */
+int
+ti_pinmux_padconf_get(const char *padname, const char **muxmode,
+ unsigned int *state)
+{
+ const struct ti_pinmux_padconf *padconf;
+ uint16_t reg_val;
+
+ if (!ti_pinmux_sc)
+ return (ENXIO);
+
+ /* find the pin in the devmap */
+ padconf = ti_pinmux_padconf_from_name(padname);
+ if (padconf == NULL)
+ return (EINVAL);
+
+ /* read the register value (16-bit reads) */
+ reg_val = ti_pinmux_read_2(ti_pinmux_sc, padconf->reg_off);
+
+ /* save the state */
+ if (state)
+ *state = (reg_val & ti_pinmux_dev->padconf_sate_mask);
+
+ /* save the mode */
+ if (muxmode)
+ *muxmode = padconf->muxmodes[(reg_val & ti_pinmux_dev->padconf_muxmode_mask)];
+
+ return (0);
+}
+
+/**
+ * ti_pinmux_padconf_set_gpiomode - converts a pad to GPIO mode.
+ * @gpio: the GPIO pin number (0-195)
+ * @state: the state to put the pad/pin in, i.e. PADCONF_PIN_???
+ *
+ *
+ *
+ * LOCKING:
+ * Internally locks it's own context.
+ *
+ * RETURNS:
+ * 0 on success.
+ * EINVAL if pin requested is outside valid range or already in use.
+ */
+int
+ti_pinmux_padconf_set_gpiomode(uint32_t gpio, unsigned int state)
+{
+ const struct ti_pinmux_padconf *padconf;
+ uint16_t reg_val;
+
+ if (!ti_pinmux_sc)
+ return (ENXIO);
+
+ /* find the gpio pin in the padconf array */
+ padconf = ti_pinmux_dev->padconf;
+ while (padconf->ballname != NULL) {
+ if (padconf->gpio_pin == gpio)
+ break;
+ padconf++;
+ }
+ if (padconf->ballname == NULL)
+ return (EINVAL);
+
+ /* populate the new value for the PADCONF register */
+ reg_val = (uint16_t)(state & ti_pinmux_dev->padconf_sate_mask);
+
+ /* set the mux mode */
+ reg_val |= (uint16_t)(padconf->gpio_mode & ti_pinmux_dev->padconf_muxmode_mask);
+
+ /* write the register value (16-bit writes) */
+ ti_pinmux_write_2(ti_pinmux_sc, padconf->reg_off, reg_val);
+
+ return (0);
+}
+
+/**
+ * ti_pinmux_padconf_get_gpiomode - gets the current GPIO mode of the pin
+ * @gpio: the GPIO pin number (0-195)
+ * @state: upon return will contain the state
+ *
+ *
+ *
+ * LOCKING:
+ * Internally locks it's own context.
+ *
+ * RETURNS:
+ * 0 on success.
+ * EINVAL if pin requested is outside valid range or not configured as GPIO.
+ */
+int
+ti_pinmux_padconf_get_gpiomode(uint32_t gpio, unsigned int *state)
+{
+ const struct ti_pinmux_padconf *padconf;
+ uint16_t reg_val;
+
+ if (!ti_pinmux_sc)
+ return (ENXIO);
+
+ /* find the gpio pin in the padconf array */
+ padconf = ti_pinmux_dev->padconf;
+ while (padconf->ballname != NULL) {
+ if (padconf->gpio_pin == gpio)
+ break;
+ padconf++;
+ }
+ if (padconf->ballname == NULL)
+ return (EINVAL);
+
+ /* read the current register settings */
+ reg_val = ti_pinmux_read_2(ti_pinmux_sc, padconf->reg_off);
+
+ /* check to make sure the pins is configured as GPIO in the first state */
+ if ((reg_val & ti_pinmux_dev->padconf_muxmode_mask) != padconf->gpio_mode)
+ return (EINVAL);
+
+ /* read and store the reset of the state, i.e. pull-up, pull-down, etc */
+ if (state)
+ *state = (reg_val & ti_pinmux_dev->padconf_sate_mask);
+
+ return (0);
+}
+
+static int
+ti_pinmux_configure_pins(device_t dev, phandle_t cfgxref)
+{
+ struct pincfg *cfgtuples, *cfg;
+ phandle_t cfgnode;
+ int i, ntuples;
+ static struct ti_pinmux_softc *sc;
+
+ sc = device_get_softc(dev);
+ cfgnode = OF_node_from_xref(cfgxref);
+ ntuples = OF_getencprop_alloc_multi(cfgnode, "pinctrl-single,pins",
+ sizeof(*cfgtuples), (void **)&cfgtuples);
+
+ if (ntuples < 0)
+ return (ENOENT);
+
+ if (ntuples == 0)
+ return (0); /* Empty property is not an error. */
+
+ for (i = 0, cfg = cfgtuples; i < ntuples; i++, cfg++) {
+ if (bootverbose) {
+ char name[32];
+ OF_getprop(cfgnode, "name", &name, sizeof(name));
+ printf("%16s: muxreg 0x%04x muxval 0x%02x\n",
+ name, cfg->reg, cfg->conf);
+ }
+
+ /* write the register value (16-bit writes) */
+ ti_pinmux_write_2(sc, cfg->reg, cfg->conf);
+ }
+
+ OF_prop_free(cfgtuples);
+
+ return (0);
+}
+
+/*
+ * Device part of OMAP SCM driver
+ */
+
+static int
+ti_pinmux_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "pinctrl-single"))
+ return (ENXIO);
+
+ if (ti_pinmux_sc) {
+ printf("%s: multiple pinctrl modules in device tree data, ignoring\n",
+ __func__);
+ return (EEXIST);
+ }
+ switch (ti_chip()) {
+#ifdef SOC_OMAP4
+ case CHIP_OMAP_4:
+ ti_pinmux_dev = &omap4_pinmux_dev;
+ break;
+#endif
+#ifdef SOC_TI_AM335X
+ case CHIP_AM335X:
+ ti_pinmux_dev = &ti_am335x_pinmux_dev;
+ break;
+#endif
+ default:
+ printf("Unknown CPU in pinmux\n");
+ return (ENXIO);
+ }
+
+ device_set_desc(dev, "TI Pinmux Module");
+ return (BUS_PROBE_DEFAULT);
+}
+
+/**
+ * ti_pinmux_attach - attaches the pinmux to the simplebus
+ * @dev: new device
+ *
+ * RETURNS
+ * Zero on success or ENXIO if an error occuried.
+ */
+static int
+ti_pinmux_attach(device_t dev)
+{
+ struct ti_pinmux_softc *sc = device_get_softc(dev);
+
+#if 0
+ if (ti_pinmux_sc)
+ return (ENXIO);
+#endif
+
+ sc->sc_dev = dev;
+
+ if (bus_alloc_resources(dev, ti_pinmux_res_spec, sc->sc_res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ sc->sc_bst = rman_get_bustag(sc->sc_res[0]);
+ sc->sc_bsh = rman_get_bushandle(sc->sc_res[0]);
+
+ if (ti_pinmux_sc == NULL)
+ ti_pinmux_sc = sc;
+
+ fdt_pinctrl_register(dev, "pinctrl-single,pins");
+ fdt_pinctrl_configure_tree(dev);
+
+ return (0);
+}
+
+static device_method_t ti_pinmux_methods[] = {
+ DEVMETHOD(device_probe, ti_pinmux_probe),
+ DEVMETHOD(device_attach, ti_pinmux_attach),
+
+ /* fdt_pinctrl interface */
+ DEVMETHOD(fdt_pinctrl_configure, ti_pinmux_configure_pins),
+ { 0, 0 }
+};
+
+static driver_t ti_pinmux_driver = {
+ "ti_pinmux",
+ ti_pinmux_methods,
+ sizeof(struct ti_pinmux_softc),
+};
+
+static devclass_t ti_pinmux_devclass;
+
+DRIVER_MODULE(ti_pinmux, simplebus, ti_pinmux_driver, ti_pinmux_devclass, 0, 0);
+MODULE_VERSION(ti_pinmux, 1);
+MODULE_DEPEND(ti_pinmux, ti_scm, 1, 1, 1);
diff --git a/sys/arm/ti/ti_pinmux.h b/sys/arm/ti/ti_pinmux.h
new file mode 100644
index 000000000000..2f31699e4371
--- /dev/null
+++ b/sys/arm/ti/ti_pinmux.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2010
+ * Ben Gray <ben.r.gray@gmail.com>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Ben Gray.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BEN GRAY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/**
+ * Functions to configure the PIN multiplexing on the chip.
+ *
+ * This is different from the GPIO module in that it is used to configure the
+ * pins between modules not just GPIO input output.
+ *
+ */
+#ifndef _TI_PINMUX_H_
+#define _TI_PINMUX_H_
+
+struct ti_pinmux_padconf {
+ uint16_t reg_off;
+ uint16_t gpio_pin;
+ uint16_t gpio_mode;
+ const char *ballname;
+ const char *muxmodes[8];
+};
+
+struct ti_pinmux_padstate {
+ const char *state;
+ uint16_t reg;
+};
+
+struct ti_pinmux_device {
+ uint16_t padconf_muxmode_mask;
+ uint16_t padconf_sate_mask;
+ const struct ti_pinmux_padstate *padstate;
+ const struct ti_pinmux_padconf *padconf;
+};
+
+struct ti_pinmux_softc {
+ device_t sc_dev;
+ struct resource * sc_res[4];
+ bus_space_tag_t sc_bst;
+ bus_space_handle_t sc_bsh;
+};
+
+int ti_pinmux_padconf_set(const char *padname, const char *muxmode,
+ unsigned int state);
+int ti_pinmux_padconf_get(const char *padname, const char **muxmode,
+ unsigned int *state);
+int ti_pinmux_padconf_set_gpiomode(uint32_t gpio, unsigned int state);
+int ti_pinmux_padconf_get_gpiomode(uint32_t gpio, unsigned int *state);
+
+#endif /* _TI_SCM_H_ */
diff --git a/sys/arm/ti/ti_prcm.c b/sys/arm/ti/ti_prcm.c
new file mode 100644
index 000000000000..2664b8f71ec4
--- /dev/null
+++ b/sys/arm/ti/ti_prcm.c
@@ -0,0 +1,344 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Damjan Marion <dmarion@Freebsd.org>
+ * All rights reserved.
+ *
+ * Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/* Based on sys/arm/ti/am335x/am335x_prcm.c */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <arm/ti/ti_cpuid.h>
+#include <arm/ti/ti_prcm.h>
+#include <arm/ti/tivar.h>
+
+#include <dev/fdt/simplebus.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "clkdev_if.h"
+
+#if 0
+#define DPRINTF(dev, msg...) device_printf(dev, msg)
+#else
+#define DPRINTF(dev, msg...)
+#endif
+
+struct ti_prcm_softc {
+ struct simplebus_softc sc_simplebus;
+ device_t dev;
+ struct resource * mem_res;
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+ int attach_done;
+ struct mtx mtx;
+};
+
+static struct ti_prcm_softc *ti_prcm_sc = NULL;
+static void omap4_prcm_reset(void);
+static void am335x_prcm_reset(void);
+
+#define TI_AM3_PRCM 18
+#define TI_AM4_PRCM 17
+#define TI_OMAP2_PRCM 16
+#define TI_OMAP3_PRM 15
+#define TI_OMAP3_CM 14
+#define TI_OMAP4_CM1 13
+#define TI_OMAP4_PRM 12
+#define TI_OMAP4_CM2 11
+#define TI_OMAP4_SCRM 10
+#define TI_OMAP5_PRM 9
+#define TI_OMAP5_CM_CORE_AON 8
+#define TI_OMAP5_SCRM 7
+#define TI_OMAP5_CM_CORE 6
+#define TI_DRA7_PRM 5
+#define TI_DRA7_CM_CORE_AON 4
+#define TI_DRA7_CM_CORE 3
+#define TI_DM814_PRCM 2
+#define TI_DM816_PRCM 1
+#define TI_PRCM_END 0
+
+static struct ofw_compat_data compat_data[] = {
+ { "ti,am3-prcm", TI_AM3_PRCM },
+ { "ti,am4-prcm", TI_AM4_PRCM },
+ { "ti,omap2-prcm", TI_OMAP2_PRCM },
+ { "ti,omap3-prm", TI_OMAP3_PRM },
+ { "ti,omap3-cm", TI_OMAP3_CM },
+ { "ti,omap4-cm1", TI_OMAP4_CM1 },
+ { "ti,omap4-prm", TI_OMAP4_PRM },
+ { "ti,omap4-cm2", TI_OMAP4_CM2 },
+ { "ti,omap4-scrm", TI_OMAP4_SCRM },
+ { "ti,omap5-prm", TI_OMAP5_PRM },
+ { "ti,omap5-cm-core-aon", TI_OMAP5_CM_CORE_AON },
+ { "ti,omap5-scrm", TI_OMAP5_SCRM },
+ { "ti,omap5-cm-core", TI_OMAP5_CM_CORE },
+ { "ti,dra7-prm", TI_DRA7_PRM },
+ { "ti,dra7-cm-core-aon", TI_DRA7_CM_CORE_AON },
+ { "ti,dra7-cm-core", TI_DRA7_CM_CORE },
+ { "ti,dm814-prcm", TI_DM814_PRCM },
+ { "ti,dm816-prcm", TI_DM816_PRCM },
+ { NULL, TI_PRCM_END}
+};
+
+static int
+ti_prcm_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) {
+ return (ENXIO);
+ }
+
+ device_set_desc(dev, "TI Power and Clock Management");
+ return(BUS_PROBE_DEFAULT);
+}
+
+static int
+ti_prcm_attach(device_t dev)
+{
+ struct ti_prcm_softc *sc;
+ phandle_t node, child;
+ int rid;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ node = ofw_bus_get_node(sc->dev);
+ simplebus_init(sc->dev, node);
+
+ if (simplebus_fill_ranges(node, &sc->sc_simplebus) < 0) {
+ device_printf(sc->dev, "could not get ranges\n");
+ return (ENXIO);
+ }
+ if (sc->sc_simplebus.nranges == 0) {
+ device_printf(sc->dev, "nranges == 0\n");
+ return (ENXIO);
+ }
+
+ sc->mem_res = bus_alloc_resource(sc->dev, SYS_RES_MEMORY, &rid,
+ sc->sc_simplebus.ranges[0].host,
+ (sc->sc_simplebus.ranges[0].host +
+ sc->sc_simplebus.ranges[0].size - 1),
+ sc->sc_simplebus.ranges[0].size,
+ RF_ACTIVE | RF_SHAREABLE);
+
+ if (sc->mem_res == NULL) {
+ return (ENXIO);
+ }
+
+ sc->bst = rman_get_bustag(sc->mem_res);
+ sc->bsh = rman_get_bushandle(sc->mem_res);
+
+ mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ /* Fixme: for xxx_prcm_reset functions.
+ * Get rid of global variables?
+ */
+ ti_prcm_sc = sc;
+
+ switch(ti_chip()) {
+#ifdef SOC_OMAP4
+ case CHIP_OMAP_4:
+ ti_cpu_reset = omap4_prcm_reset;
+ break;
+#endif
+#ifdef SOC_TI_AM335X
+ case CHIP_AM335X:
+ ti_cpu_reset = am335x_prcm_reset;
+ break;
+#endif
+ }
+
+ bus_generic_probe(sc->dev);
+ for (child = OF_child(node); child != 0; child = OF_peer(child)) {
+ simplebus_add_device(dev, child, 0, NULL, -1, NULL);
+ }
+
+ return (bus_generic_attach(sc->dev));
+}
+
+int
+ti_prcm_write_4(device_t dev, bus_addr_t addr, uint32_t val)
+{
+ struct ti_prcm_softc *sc;
+
+ sc = device_get_softc(dev);
+ DPRINTF(sc->dev, "offset=%lx write %x\n", addr, val);
+ bus_space_write_4(sc->bst, sc->bsh, addr, val);
+ return (0);
+}
+int
+ti_prcm_read_4(device_t dev, bus_addr_t addr, uint32_t *val)
+{
+ struct ti_prcm_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ *val = bus_space_read_4(sc->bst, sc->bsh, addr);
+ DPRINTF(sc->dev, "offset=%lx Read %x\n", addr, *val);
+ return (0);
+}
+
+int
+ti_prcm_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set)
+{
+ struct ti_prcm_softc *sc;
+ uint32_t reg;
+
+ sc = device_get_softc(dev);
+
+ reg = bus_space_read_4(sc->bst, sc->bsh, addr);
+ reg &= ~clr;
+ reg |= set;
+ bus_space_write_4(sc->bst, sc->bsh, addr, reg);
+ DPRINTF(sc->dev, "offset=%lx reg: %x (clr %x set %x)\n", addr, reg, clr, set);
+
+ return (0);
+}
+
+void
+ti_prcm_device_lock(device_t dev)
+{
+ struct ti_prcm_softc *sc;
+
+ sc = device_get_softc(dev);
+ mtx_lock(&sc->mtx);
+}
+
+void
+ti_prcm_device_unlock(device_t dev)
+{
+ struct ti_prcm_softc *sc;
+
+ sc = device_get_softc(dev);
+ mtx_unlock(&sc->mtx);
+}
+
+static device_method_t ti_prcm_methods[] = {
+ DEVMETHOD(device_probe, ti_prcm_probe),
+ DEVMETHOD(device_attach, ti_prcm_attach),
+
+ /* clkdev interface */
+ DEVMETHOD(clkdev_write_4, ti_prcm_write_4),
+ DEVMETHOD(clkdev_read_4, ti_prcm_read_4),
+ DEVMETHOD(clkdev_modify_4, ti_prcm_modify_4),
+ DEVMETHOD(clkdev_device_lock, ti_prcm_device_lock),
+ DEVMETHOD(clkdev_device_unlock, ti_prcm_device_unlock),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(ti_prcm, ti_prcm_driver, ti_prcm_methods,
+ sizeof(struct ti_prcm_softc), simplebus_driver);
+
+static devclass_t ti_prcm_devclass;
+
+EARLY_DRIVER_MODULE(ti_prcm, ofwbus, ti_prcm_driver,
+ ti_prcm_devclass, 0, 0, BUS_PASS_BUS);
+EARLY_DRIVER_MODULE(ti_prcm, simplebus, ti_prcm_driver,
+ ti_prcm_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
+MODULE_VERSION(ti_prcm, 1);
+MODULE_DEPEND(ti_prcm, ti_scm, 1, 1, 1);
+
+/* From sys/arm/ti/am335x/am335x_prcm.c
+ * Copyright (c) 2012 Damjan Marion <dmarion@Freebsd.org>
+ */
+#define PRM_DEVICE_OFFSET 0xF00
+#define AM335x_PRM_RSTCTRL (PRM_DEVICE_OFFSET + 0x00)
+
+static void
+am335x_prcm_reset(void)
+{
+ ti_prcm_write_4(ti_prcm_sc->dev, AM335x_PRM_RSTCTRL, (1<<1));
+}
+
+/* FIXME: Is this correct - or should the license part be ontop? */
+
+/* From sys/arm/ti/omap4/omap4_prcm_clks.c */
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 2011
+ * Ben Gray <ben.r.gray@gmail.com>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BEN GRAY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#define PRM_RSTCTRL 0x1b00
+#define PRM_RSTCTRL_RESET 0x2
+
+static void
+omap4_prcm_reset(void)
+{
+ uint32_t reg;
+
+ ti_prcm_read_4(ti_prcm_sc->dev, PRM_RSTCTRL, &reg);
+ reg = reg | PRM_RSTCTRL_RESET;
+ ti_prcm_write_4(ti_prcm_sc->dev, PRM_RSTCTRL, reg);
+ ti_prcm_read_4(ti_prcm_sc->dev, PRM_RSTCTRL, &reg);
+}
diff --git a/sys/arm/ti/ti_prcm.h b/sys/arm/ti/ti_prcm.h
new file mode 100644
index 000000000000..98f8abc15dd7
--- /dev/null
+++ b/sys/arm/ti/ti_prcm.h
@@ -0,0 +1,39 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __TI_PRCM_H__
+#define __TI_PRCM_H__
+
+int ti_prcm_write_4(device_t dev, bus_addr_t addr, uint32_t val);
+int ti_prcm_read_4(device_t dev, bus_addr_t addr, uint32_t *val);
+int ti_prcm_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set);
+void ti_prcm_device_lock(device_t dev);
+void ti_prcm_device_unlock(device_t dev);
+
+#endif
diff --git a/sys/arm/ti/ti_prm.c b/sys/arm/ti/ti_prm.c
new file mode 100644
index 000000000000..4a57fbb8b972
--- /dev/null
+++ b/sys/arm/ti/ti_prm.c
@@ -0,0 +1,210 @@
+/*-
+ * Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Power management - simple driver to handle reset and give access to
+ * memory space region for other drivers through prcm driver.
+ * Documentation/devicetree/binding/arm/omap/prm-inst.txt
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+
+#include <dev/fdt/simplebus.h>
+
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/ti/ti_prcm.h>
+#include <arm/ti/ti_prm.h>
+
+#if 0
+#define DPRINTF(dev, msg...) device_printf(dev, msg)
+#else
+#define DPRINTF(dev, msg...)
+#endif
+
+/* relative to prcm address range */
+#define TI_PRM_PER_RSTCTRL 0xC00
+
+struct ti_prm_softc {
+ device_t dev;
+ uint8_t type;
+ bool has_reset;
+};
+
+/* Device */
+#define TI_OMAP_PRM_INST 10
+
+#define TI_AM3_PRM_INST 5
+#define TI_AM4_PRM_INST 4
+#define TI_OMAP4_PRM_INST 3
+#define TI_OMAP5_PRM_INST 2
+#define TI_DRA7_PRM_INST 1
+#define TI_END 0
+
+static struct ofw_compat_data compat_data[] = {
+ { "ti,am3-prm-inst", TI_AM3_PRM_INST },
+ { "ti,am4-prm-inst", TI_AM4_PRM_INST },
+ { "ti,omap4-prm-inst", TI_OMAP4_PRM_INST },
+ { "ti,omap5-prm-inst", TI_OMAP5_PRM_INST },
+ { "ti,dra7-prm-inst", TI_DRA7_PRM_INST },
+ { NULL, TI_END }
+};
+
+static struct ofw_compat_data required_data[] = {
+ { "ti,omap-prm-inst", TI_OMAP_PRM_INST },
+ { NULL, TI_END }
+};
+
+/* device interface */
+static int
+ti_prm_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, required_data)->ocd_data == 0)
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "TI OMAP Power Management");
+ return(BUS_PROBE_DEFAULT);
+}
+
+static int
+ti_prm_attach(device_t dev)
+{
+ struct ti_prm_softc *sc;
+ phandle_t node;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+
+ node = ofw_bus_get_node(sc->dev);
+
+ if (OF_hasprop(node, "#reset-cells")) {
+ sc->has_reset = true;
+ } else
+ sc->has_reset = false;
+
+ /* Make device visible for other drivers */
+ OF_device_register_xref(OF_xref_from_node(node), sc->dev);
+
+ return (0);
+}
+
+static int
+ti_prm_detach(device_t dev) {
+ return (EBUSY);
+}
+
+int
+ti_prm_reset(device_t dev)
+{
+ struct ti_prm_softc *sc;
+ int err;
+
+ sc = device_get_softc(dev);
+ if (sc->has_reset == false)
+ return 1;
+
+ err = ti_prm_modify_4(dev, TI_PRM_PER_RSTCTRL, 0x2, 0x00);
+ return (err);
+}
+
+int
+ti_prm_write_4(device_t dev, bus_addr_t addr, uint32_t val)
+{
+ struct ti_prm_softc *sc;
+ device_t parent;
+
+ parent = device_get_parent(dev);
+ sc = device_get_softc(dev);
+ DPRINTF(sc->dev, "offset=%lx write %x\n", addr, val);
+ ti_prcm_device_lock(parent);
+ ti_prcm_write_4(parent, addr, val);
+ ti_prcm_device_unlock(parent);
+ return (0);
+}
+
+int
+ti_prm_read_4(device_t dev, bus_addr_t addr, uint32_t *val)
+{
+ struct ti_prm_softc *sc;
+ device_t parent;
+
+ parent = device_get_parent(dev);
+ sc = device_get_softc(dev);
+
+ ti_prcm_device_lock(parent);
+ ti_prcm_read_4(parent, addr, val);
+ ti_prcm_device_unlock(parent);
+ DPRINTF(sc->dev, "offset=%lx Read %x\n", addr, *val);
+ return (0);
+}
+
+int
+ti_prm_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set)
+{
+ struct ti_prm_softc *sc;
+ device_t parent;
+
+ parent = device_get_parent(dev);
+ sc = device_get_softc(dev);
+
+ ti_prcm_device_lock(parent);
+ ti_prcm_modify_4(parent, addr, clr, set);
+ ti_prcm_device_unlock(parent);
+ DPRINTF(sc->dev, "offset=%lx (clr %x set %x)\n", addr, clr, set);
+
+ return (0);
+}
+
+static device_method_t ti_prm_methods[] = {
+ DEVMETHOD(device_probe, ti_prm_probe),
+ DEVMETHOD(device_attach, ti_prm_attach),
+ DEVMETHOD(device_detach, ti_prm_detach),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(ti_prm, ti_prm_driver, ti_prm_methods,
+ sizeof(struct ti_prm_softc), simplebus_driver);
+
+static devclass_t ti_prm_devclass;
+
+EARLY_DRIVER_MODULE(ti_prm, simplebus, ti_prm_driver,
+ ti_prm_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
+MODULE_VERSION(ti_prm, 1);
+MODULE_DEPEND(ti_prm, ti_sysc, 1, 1, 1);
diff --git a/sys/arm/ti/ti_prm.h b/sys/arm/ti/ti_prm.h
new file mode 100644
index 000000000000..bc3e991088f0
--- /dev/null
+++ b/sys/arm/ti/ti_prm.h
@@ -0,0 +1,38 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef __TI_PRM__
+#define __TI_PRM__
+
+int ti_prm_reset(device_t dev);
+
+int ti_prm_write_4(device_t dev, bus_addr_t addr, uint32_t val);
+int ti_prm_read_4(device_t dev, bus_addr_t addr, uint32_t *val);
+int ti_prm_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set);
+
+#endif /* __TI_PRM__ */
diff --git a/sys/arm/ti/ti_pruss.c b/sys/arm/ti/ti_pruss.c
new file mode 100644
index 000000000000..a8dc15ab80b0
--- /dev/null
+++ b/sys/arm/ti/ti_pruss.c
@@ -0,0 +1,854 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Rui Paulo <rpaulo@FreeBSD.org>
+ * Copyright (c) 2017 Manuel Stuehn
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/poll.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/fcntl.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <sys/event.h>
+#include <sys/selinfo.h>
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/frame.h>
+#include <machine/intr.h>
+#include <machine/atomic.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/extres/clk/clk.h>
+
+#include <arm/ti/ti_sysc.h>
+#include <arm/ti/ti_pruss.h>
+#include <arm/ti/ti_prm.h>
+
+#ifdef DEBUG
+#define DPRINTF(fmt, ...) do { \
+ printf("%s: ", __func__); \
+ printf(fmt, __VA_ARGS__); \
+} while (0)
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
+static d_open_t ti_pruss_irq_open;
+static d_read_t ti_pruss_irq_read;
+static d_poll_t ti_pruss_irq_poll;
+
+static device_probe_t ti_pruss_probe;
+static device_attach_t ti_pruss_attach;
+static device_detach_t ti_pruss_detach;
+static void ti_pruss_intr(void *);
+static d_open_t ti_pruss_open;
+static d_mmap_t ti_pruss_mmap;
+static void ti_pruss_irq_kqread_detach(struct knote *);
+static int ti_pruss_irq_kqevent(struct knote *, long);
+static d_kqfilter_t ti_pruss_irq_kqfilter;
+static void ti_pruss_privdtor(void *data);
+
+#define TI_PRUSS_PRU_IRQS 2
+#define TI_PRUSS_HOST_IRQS 8
+#define TI_PRUSS_IRQS (TI_PRUSS_HOST_IRQS+TI_PRUSS_PRU_IRQS)
+#define TI_PRUSS_EVENTS 64
+#define NOT_SET_STR "NONE"
+#define TI_TS_ARRAY 16
+
+struct ctl
+{
+ size_t cnt;
+ size_t idx;
+};
+
+struct ts_ring_buf
+{
+ struct ctl ctl;
+ uint64_t ts[TI_TS_ARRAY];
+};
+
+struct ti_pruss_irqsc
+{
+ struct mtx sc_mtx;
+ struct cdev *sc_pdev;
+ struct selinfo sc_selinfo;
+ int8_t channel;
+ int8_t last;
+ int8_t event;
+ bool enable;
+ struct ts_ring_buf tstamps;
+};
+
+static struct cdevsw ti_pruss_cdevirq = {
+ .d_version = D_VERSION,
+ .d_name = "ti_pruss_irq",
+ .d_open = ti_pruss_irq_open,
+ .d_read = ti_pruss_irq_read,
+ .d_poll = ti_pruss_irq_poll,
+ .d_kqfilter = ti_pruss_irq_kqfilter,
+};
+
+struct ti_pruss_softc {
+ struct mtx sc_mtx;
+ struct resource *sc_mem_res;
+ struct resource *sc_irq_res[TI_PRUSS_HOST_IRQS];
+ void *sc_intr[TI_PRUSS_HOST_IRQS];
+ struct ti_pruss_irqsc sc_irq_devs[TI_PRUSS_IRQS];
+ bus_space_tag_t sc_bt;
+ bus_space_handle_t sc_bh;
+ struct cdev *sc_pdev;
+ struct selinfo sc_selinfo;
+ bool sc_glob_irqen;
+};
+
+static struct cdevsw ti_pruss_cdevsw = {
+ .d_version = D_VERSION,
+ .d_name = "ti_pruss",
+ .d_open = ti_pruss_open,
+ .d_mmap = ti_pruss_mmap,
+};
+
+static device_method_t ti_pruss_methods[] = {
+ DEVMETHOD(device_probe, ti_pruss_probe),
+ DEVMETHOD(device_attach, ti_pruss_attach),
+ DEVMETHOD(device_detach, ti_pruss_detach),
+
+ DEVMETHOD_END
+};
+
+static driver_t ti_pruss_driver = {
+ "ti_pruss",
+ ti_pruss_methods,
+ sizeof(struct ti_pruss_softc)
+};
+
+static devclass_t ti_pruss_devclass;
+
+DRIVER_MODULE(ti_pruss, simplebus, ti_pruss_driver, ti_pruss_devclass, 0, 0);
+MODULE_DEPEND(ti_pruss, ti_sysc, 1, 1, 1);
+MODULE_DEPEND(ti_pruss, ti_prm, 1, 1, 1);
+
+static struct resource_spec ti_pruss_irq_spec[] = {
+ { SYS_RES_IRQ, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 1, RF_ACTIVE },
+ { SYS_RES_IRQ, 2, RF_ACTIVE },
+ { SYS_RES_IRQ, 3, RF_ACTIVE },
+ { SYS_RES_IRQ, 4, RF_ACTIVE },
+ { SYS_RES_IRQ, 5, RF_ACTIVE },
+ { SYS_RES_IRQ, 6, RF_ACTIVE },
+ { SYS_RES_IRQ, 7, RF_ACTIVE },
+ { -1, 0, 0 }
+};
+CTASSERT(TI_PRUSS_HOST_IRQS == nitems(ti_pruss_irq_spec) - 1);
+
+static int
+ti_pruss_irq_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
+{
+ struct ctl* irqs;
+ struct ti_pruss_irqsc *sc;
+ sc = dev->si_drv1;
+
+ irqs = malloc(sizeof(struct ctl), M_DEVBUF, M_WAITOK);
+ if (!irqs)
+ return (ENOMEM);
+
+ irqs->cnt = sc->tstamps.ctl.cnt;
+ irqs->idx = sc->tstamps.ctl.idx;
+
+ return devfs_set_cdevpriv(irqs, ti_pruss_privdtor);
+}
+
+static void
+ti_pruss_privdtor(void *data)
+{
+ free(data, M_DEVBUF);
+}
+
+static int
+ti_pruss_irq_poll(struct cdev *dev, int events, struct thread *td)
+{
+ struct ctl* irqs;
+ struct ti_pruss_irqsc *sc;
+ sc = dev->si_drv1;
+
+ devfs_get_cdevpriv((void**)&irqs);
+
+ if (events & (POLLIN | POLLRDNORM)) {
+ if (sc->tstamps.ctl.cnt != irqs->cnt)
+ return events & (POLLIN | POLLRDNORM);
+ else
+ selrecord(td, &sc->sc_selinfo);
+ }
+ return 0;
+}
+
+static int
+ti_pruss_irq_read(struct cdev *cdev, struct uio *uio, int ioflag)
+{
+ const size_t ts_len = sizeof(uint64_t);
+ struct ti_pruss_irqsc* irq;
+ struct ctl* priv;
+ int error = 0;
+ size_t idx;
+ ssize_t level;
+
+ irq = cdev->si_drv1;
+
+ if (uio->uio_resid < ts_len)
+ return (EINVAL);
+
+ error = devfs_get_cdevpriv((void**)&priv);
+ if (error)
+ return (error);
+
+ mtx_lock(&irq->sc_mtx);
+
+ if (irq->tstamps.ctl.cnt - priv->cnt > TI_TS_ARRAY)
+ {
+ priv->cnt = irq->tstamps.ctl.cnt;
+ priv->idx = irq->tstamps.ctl.idx;
+ mtx_unlock(&irq->sc_mtx);
+ return (ENXIO);
+ }
+
+ do {
+ idx = priv->idx;
+ level = irq->tstamps.ctl.idx - idx;
+ if (level < 0)
+ level += TI_TS_ARRAY;
+
+ if (level == 0) {
+ if (ioflag & O_NONBLOCK) {
+ mtx_unlock(&irq->sc_mtx);
+ return (EWOULDBLOCK);
+ }
+
+ error = msleep(irq, &irq->sc_mtx, PCATCH | PDROP,
+ "pruirq", 0);
+ if (error)
+ return error;
+
+ mtx_lock(&irq->sc_mtx);
+ }
+ }while(level == 0);
+
+ mtx_unlock(&irq->sc_mtx);
+
+ error = uiomove(&irq->tstamps.ts[idx], ts_len, uio);
+
+ if (++idx == TI_TS_ARRAY)
+ idx = 0;
+ priv->idx = idx;
+
+ atomic_add_32(&priv->cnt, 1);
+
+ return (error);
+}
+
+static struct ti_pruss_irq_arg {
+ int irq;
+ struct ti_pruss_softc *sc;
+} ti_pruss_irq_args[TI_PRUSS_IRQS];
+
+static __inline uint32_t
+ti_pruss_reg_read(struct ti_pruss_softc *sc, uint32_t reg)
+{
+ return (bus_space_read_4(sc->sc_bt, sc->sc_bh, reg));
+}
+
+static __inline void
+ti_pruss_reg_write(struct ti_pruss_softc *sc, uint32_t reg, uint32_t val)
+{
+ bus_space_write_4(sc->sc_bt, sc->sc_bh, reg, val);
+}
+
+static __inline void
+ti_pruss_interrupts_clear(struct ti_pruss_softc *sc)
+{
+ /* disable global interrupt */
+ ti_pruss_reg_write(sc, PRUSS_INTC_GER, 0 );
+
+ /* clear all events */
+ ti_pruss_reg_write(sc, PRUSS_INTC_SECR0, 0xFFFFFFFF);
+ ti_pruss_reg_write(sc, PRUSS_INTC_SECR1, 0xFFFFFFFF);
+
+ /* disable all host interrupts */
+ ti_pruss_reg_write(sc, PRUSS_INTC_HIER, 0);
+}
+
+static __inline int
+ti_pruss_interrupts_enable(struct ti_pruss_softc *sc, int8_t irq, bool enable)
+{
+ if (enable && ((sc->sc_irq_devs[irq].channel == -1) ||
+ (sc->sc_irq_devs[irq].event== -1)))
+ {
+ device_printf( sc->sc_pdev->si_drv1,
+ "Interrupt chain not fully configured, not possible to enable\n" );
+ return (EINVAL);
+ }
+
+ sc->sc_irq_devs[irq].enable = enable;
+
+ if (sc->sc_irq_devs[irq].sc_pdev) {
+ destroy_dev(sc->sc_irq_devs[irq].sc_pdev);
+ sc->sc_irq_devs[irq].sc_pdev = NULL;
+ }
+
+ if (enable) {
+ sc->sc_irq_devs[irq].sc_pdev = make_dev(&ti_pruss_cdevirq, 0, UID_ROOT, GID_WHEEL,
+ 0600, "pruss%d.irq%d", device_get_unit(sc->sc_pdev->si_drv1), irq);
+ sc->sc_irq_devs[irq].sc_pdev->si_drv1 = &sc->sc_irq_devs[irq];
+
+ sc->sc_irq_devs[irq].tstamps.ctl.idx = 0;
+ }
+
+ uint32_t reg = enable ? PRUSS_INTC_HIEISR : PRUSS_INTC_HIDISR;
+ ti_pruss_reg_write(sc, reg, sc->sc_irq_devs[irq].channel);
+
+ reg = enable ? PRUSS_INTC_EISR : PRUSS_INTC_EICR;
+ ti_pruss_reg_write(sc, reg, sc->sc_irq_devs[irq].event );
+
+ return (0);
+}
+
+static __inline void
+ti_pruss_map_write(struct ti_pruss_softc *sc, uint32_t basereg, uint8_t index, uint8_t content)
+{
+ const size_t regadr = basereg + index & ~0x03;
+ const size_t bitpos = (index & 0x03) * 8;
+ uint32_t rmw = ti_pruss_reg_read(sc, regadr);
+ rmw = (rmw & ~( 0xF << bitpos)) | ( (content & 0xF) << bitpos);
+ ti_pruss_reg_write(sc, regadr, rmw);
+}
+
+static int
+ti_pruss_event_map( SYSCTL_HANDLER_ARGS )
+{
+ struct ti_pruss_softc *sc;
+ const int8_t irq = arg2;
+ int err;
+ char event[sizeof(NOT_SET_STR)];
+
+ sc = arg1;
+
+ if(sc->sc_irq_devs[irq].event == -1)
+ bcopy(NOT_SET_STR, event, sizeof(event));
+ else
+ snprintf(event, sizeof(event), "%d", sc->sc_irq_devs[irq].event);
+
+ err = sysctl_handle_string(oidp, event, sizeof(event), req);
+ if(err != 0)
+ return (err);
+
+ if (req->newptr) { // write event
+ if (strcmp(NOT_SET_STR, event) == 0) {
+ ti_pruss_interrupts_enable(sc, irq, false);
+ sc->sc_irq_devs[irq].event = -1;
+ } else {
+ if (sc->sc_irq_devs[irq].channel == -1) {
+ device_printf( sc->sc_pdev->si_drv1,
+ "corresponding channel not configured\n");
+ return (ENXIO);
+ }
+
+ const int8_t channelnr = sc->sc_irq_devs[irq].channel;
+ const int8_t eventnr = strtol( event, NULL, 10 ); // TODO: check if strol is valid
+ if (eventnr > TI_PRUSS_EVENTS || eventnr < 0) {
+ device_printf( sc->sc_pdev->si_drv1,
+ "Event number %d not valid (0 - %d)",
+ channelnr, TI_PRUSS_EVENTS -1);
+ return (EINVAL);
+ }
+
+ sc->sc_irq_devs[irq].channel = channelnr;
+ sc->sc_irq_devs[irq].event = eventnr;
+
+ // event[nr] <= channel
+ ti_pruss_map_write(sc, PRUSS_INTC_CMR_BASE,
+ eventnr, channelnr);
+ }
+ }
+ return (err);
+}
+
+static int
+ti_pruss_channel_map(SYSCTL_HANDLER_ARGS)
+{
+ struct ti_pruss_softc *sc;
+ int err;
+ char channel[sizeof(NOT_SET_STR)];
+ const int8_t irq = arg2;
+
+ sc = arg1;
+
+ if (sc->sc_irq_devs[irq].channel == -1)
+ bcopy(NOT_SET_STR, channel, sizeof(channel));
+ else
+ snprintf(channel, sizeof(channel), "%d", sc->sc_irq_devs[irq].channel);
+
+ err = sysctl_handle_string(oidp, channel, sizeof(channel), req);
+ if (err != 0)
+ return (err);
+
+ if (req->newptr) { // write event
+ if (strcmp(NOT_SET_STR, channel) == 0) {
+ ti_pruss_interrupts_enable(sc, irq, false);
+ ti_pruss_reg_write(sc, PRUSS_INTC_HIDISR,
+ sc->sc_irq_devs[irq].channel);
+ sc->sc_irq_devs[irq].channel = -1;
+ } else {
+ const int8_t channelnr = strtol(channel, NULL, 10); // TODO: check if strol is valid
+ if (channelnr > TI_PRUSS_IRQS || channelnr < 0)
+ {
+ device_printf(sc->sc_pdev->si_drv1,
+ "Channel number %d not valid (0 - %d)",
+ channelnr, TI_PRUSS_IRQS-1);
+ return (EINVAL);
+ }
+
+ sc->sc_irq_devs[irq].channel = channelnr;
+ sc->sc_irq_devs[irq].last = -1;
+
+ // channel[nr] <= irqnr
+ ti_pruss_map_write(sc, PRUSS_INTC_HMR_BASE,
+ irq, channelnr);
+ }
+ }
+
+ return (err);
+}
+
+static int
+ti_pruss_interrupt_enable(SYSCTL_HANDLER_ARGS)
+{
+ struct ti_pruss_softc *sc;
+ int err;
+ bool irqenable;
+ const int8_t irq = arg2;
+
+ sc = arg1;
+ irqenable = sc->sc_irq_devs[arg2].enable;
+
+ err = sysctl_handle_bool(oidp, &irqenable, arg2, req);
+ if (err != 0)
+ return (err);
+
+ if (req->newptr) // write enable
+ return ti_pruss_interrupts_enable(sc, irq, irqenable);
+
+ return (err);
+}
+
+static int
+ti_pruss_global_interrupt_enable(SYSCTL_HANDLER_ARGS)
+{
+ struct ti_pruss_softc *sc;
+ int err;
+ bool glob_irqen;
+
+ sc = arg1;
+ glob_irqen = sc->sc_glob_irqen;
+
+ err = sysctl_handle_bool(oidp, &glob_irqen, arg2, req);
+ if (err != 0)
+ return (err);
+
+ if (req->newptr) {
+ sc->sc_glob_irqen = glob_irqen;
+ ti_pruss_reg_write(sc, PRUSS_INTC_GER, glob_irqen);
+ }
+
+ return (err);
+}
+static int
+ti_pruss_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_is_compatible(dev, "ti,pruss-v1") ||
+ ofw_bus_is_compatible(dev, "ti,pruss-v2")) {
+ device_set_desc(dev, "TI Programmable Realtime Unit Subsystem");
+ return (BUS_PROBE_DEFAULT);
+ }
+
+ return (ENXIO);
+}
+
+static int
+ti_pruss_attach(device_t dev)
+{
+ struct ti_pruss_softc *sc;
+ int rid, i, err, ncells;
+ uint32_t reg;
+ phandle_t node;
+ clk_t l3_gclk, pruss_ocp_gclk;
+ phandle_t ti_prm_ref, *cells;
+ device_t ti_prm_dev;
+
+ rid = 0;
+ sc = device_get_softc(dev);
+ node = ofw_bus_get_node(device_get_parent(dev));
+ if (node <= 0) {
+ device_printf(dev, "Cant get ofw node\n");
+ return (ENXIO);
+ }
+
+ /*
+ * Follow activate pattern from sys/arm/ti/am335x/am335x_prcm.c
+ * by Damjan Marion
+ */
+
+ /* Set MODULEMODE to ENABLE(2) */
+ /* Wait for MODULEMODE to become ENABLE(2) */
+ if (ti_sysc_clock_enable(device_get_parent(dev)) != 0) {
+ device_printf(dev, "Could not enable PRUSS clock\n");
+ return (ENXIO);
+ }
+
+ /* Set CLKTRCTRL to SW_WKUP(2) */
+ /* Wait for the 200 MHz OCP clock to become active */
+ /* Wait for the 200 MHz IEP clock to become active */
+ /* Wait for the 192 MHz UART clock to become active */
+ /*
+ * At the moment there is no reference to CM_PER_PRU_ICSS_CLKSTCTRL@140
+ * in the devicetree. The register reset state are SW_WKUP(2) as default
+ * so at the moment ignore setting this register.
+ */
+
+ /* Select L3F as OCP clock */
+ /* Get the clock and set the parent */
+ err = clk_get_by_name(dev, "l3_gclk", &l3_gclk);
+ if (err) {
+ device_printf(dev, "Cant get l3_gclk err %d\n", err);
+ return (ENXIO);
+ }
+
+ err = clk_get_by_name(dev, "pruss_ocp_gclk@530", &pruss_ocp_gclk);
+ if (err) {
+ device_printf(dev, "Cant get pruss_ocp_gclk@530 err %d\n", err);
+ return (ENXIO);
+ }
+
+ err = clk_set_parent_by_clk(pruss_ocp_gclk, l3_gclk);
+ if (err) {
+ device_printf(dev,
+ "Cant set pruss_ocp_gclk parent to l3_gclk err %d\n", err);
+ return (ENXIO);
+ }
+
+ /* Clear the RESET bit */
+ /* Find the ti_prm */
+ /* #reset-cells should not been used in this way but... */
+ err = ofw_bus_parse_xref_list_alloc(node, "resets", "#reset-cells", 0,
+ &ti_prm_ref, &ncells, &cells);
+ OF_prop_free(cells);
+ if (err) {
+ device_printf(dev,
+ "Cant fetch \"resets\" reference %x\n", err);
+ return (ENXIO);
+ }
+
+ ti_prm_dev = OF_device_from_xref(ti_prm_ref);
+ if (ti_prm_dev == NULL) {
+ device_printf(dev, "Cant get device from \"resets\"\n");
+ return (ENXIO);
+ }
+
+ err = ti_prm_reset(ti_prm_dev);
+ if (err) {
+ device_printf(dev, "ti_prm_reset failed %d\n", err);
+ return (ENXIO);
+ }
+ /* End of clock activation */
+
+ mtx_init(&sc->sc_mtx, "TI PRUSS", NULL, MTX_DEF);
+ sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->sc_mem_res == NULL) {
+ device_printf(dev, "could not allocate memory resource\n");
+ return (ENXIO);
+ }
+
+ struct sysctl_ctx_list *clist = device_get_sysctl_ctx(dev);
+ if (!clist)
+ return (EINVAL);
+
+ struct sysctl_oid *poid;
+ poid = device_get_sysctl_tree( dev );
+ if (!poid)
+ return (EINVAL);
+
+ sc->sc_glob_irqen = false;
+ struct sysctl_oid *irq_root = SYSCTL_ADD_NODE(clist, SYSCTL_CHILDREN(poid),
+ OID_AUTO, "irq", CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
+ "PRUSS Host Interrupts");
+ SYSCTL_ADD_PROC(clist, SYSCTL_CHILDREN(poid), OID_AUTO,
+ "global_interrupt_enable",
+ CTLFLAG_RW | CTLTYPE_U8 | CTLFLAG_NEEDGIANT,
+ sc, 0, ti_pruss_global_interrupt_enable,
+ "CU", "Global interrupt enable");
+
+ sc->sc_bt = rman_get_bustag(sc->sc_mem_res);
+ sc->sc_bh = rman_get_bushandle(sc->sc_mem_res);
+ if (bus_alloc_resources(dev, ti_pruss_irq_spec, sc->sc_irq_res) != 0) {
+ device_printf(dev, "could not allocate interrupt resource\n");
+ ti_pruss_detach(dev);
+ return (ENXIO);
+ }
+
+ ti_pruss_interrupts_clear(sc);
+
+ for (i = 0; i < TI_PRUSS_IRQS; i++) {
+ char name[8];
+ snprintf(name, sizeof(name), "%d", i);
+
+ struct sysctl_oid *irq_nodes = SYSCTL_ADD_NODE(clist, SYSCTL_CHILDREN(irq_root),
+ OID_AUTO, name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
+ "PRUSS Interrupts");
+ SYSCTL_ADD_PROC(clist, SYSCTL_CHILDREN(irq_nodes), OID_AUTO,
+ "channel", CTLFLAG_RW | CTLTYPE_STRING | CTLFLAG_NEEDGIANT,
+ sc, i, ti_pruss_channel_map,
+ "A", "Channel attached to this irq");
+ SYSCTL_ADD_PROC(clist, SYSCTL_CHILDREN(irq_nodes), OID_AUTO,
+ "event", CTLFLAG_RW | CTLTYPE_STRING | CTLFLAG_NEEDGIANT,
+ sc, i, ti_pruss_event_map,
+ "A", "Event attached to this irq");
+ SYSCTL_ADD_PROC(clist, SYSCTL_CHILDREN(irq_nodes), OID_AUTO,
+ "enable", CTLFLAG_RW | CTLTYPE_U8 | CTLFLAG_NEEDGIANT,
+ sc, i, ti_pruss_interrupt_enable,
+ "CU", "Enable/Disable interrupt");
+
+ sc->sc_irq_devs[i].event = -1;
+ sc->sc_irq_devs[i].channel = -1;
+ sc->sc_irq_devs[i].tstamps.ctl.idx = 0;
+
+ if (i < TI_PRUSS_HOST_IRQS) {
+ ti_pruss_irq_args[i].irq = i;
+ ti_pruss_irq_args[i].sc = sc;
+ if (bus_setup_intr(dev, sc->sc_irq_res[i],
+ INTR_MPSAFE | INTR_TYPE_MISC,
+ NULL, ti_pruss_intr, &ti_pruss_irq_args[i],
+ &sc->sc_intr[i]) != 0) {
+ device_printf(dev,
+ "unable to setup the interrupt handler\n");
+ ti_pruss_detach(dev);
+
+ return (ENXIO);
+ }
+ mtx_init(&sc->sc_irq_devs[i].sc_mtx, "TI PRUSS IRQ", NULL, MTX_DEF);
+ knlist_init_mtx(&sc->sc_irq_devs[i].sc_selinfo.si_note, &sc->sc_irq_devs[i].sc_mtx);
+ }
+ }
+
+ reg = ti_pruss_reg_read(sc,
+ ti_sysc_get_sysc_address_offset_host(device_get_parent(dev)));
+
+ if (ti_pruss_reg_read(sc, PRUSS_AM33XX_INTC) == PRUSS_AM33XX_REV)
+ device_printf(dev, "AM33xx PRU-ICSS\n");
+
+ sc->sc_pdev = make_dev(&ti_pruss_cdevsw, 0, UID_ROOT, GID_WHEEL,
+ 0600, "pruss%d", device_get_unit(dev));
+ sc->sc_pdev->si_drv1 = dev;
+
+ /* Acc. to datasheet always write 1 to polarity registers */
+ ti_pruss_reg_write(sc, PRUSS_INTC_SIPR0, 0xFFFFFFFF);
+ ti_pruss_reg_write(sc, PRUSS_INTC_SIPR1, 0xFFFFFFFF);
+
+ /* Acc. to datasheet always write 0 to event type registers */
+ ti_pruss_reg_write(sc, PRUSS_INTC_SITR0, 0);
+ ti_pruss_reg_write(sc, PRUSS_INTC_SITR1, 0);
+
+ return (0);
+}
+
+static int
+ti_pruss_detach(device_t dev)
+{
+ struct ti_pruss_softc *sc = device_get_softc(dev);
+
+ ti_pruss_interrupts_clear(sc);
+
+ for (int i = 0; i < TI_PRUSS_HOST_IRQS; i++) {
+ ti_pruss_interrupts_enable( sc, i, false );
+
+ if (sc->sc_intr[i])
+ bus_teardown_intr(dev, sc->sc_irq_res[i], sc->sc_intr[i]);
+ if (sc->sc_irq_res[i])
+ bus_release_resource(dev, SYS_RES_IRQ,
+ rman_get_rid(sc->sc_irq_res[i]),
+ sc->sc_irq_res[i]);
+ knlist_clear(&sc->sc_irq_devs[i].sc_selinfo.si_note, 0);
+ mtx_lock(&sc->sc_irq_devs[i].sc_mtx);
+ if (!knlist_empty(&sc->sc_irq_devs[i].sc_selinfo.si_note))
+ printf("IRQ %d KQueue not empty!\n", i );
+ mtx_unlock(&sc->sc_irq_devs[i].sc_mtx);
+ knlist_destroy(&sc->sc_irq_devs[i].sc_selinfo.si_note);
+ mtx_destroy(&sc->sc_irq_devs[i].sc_mtx);
+ }
+
+ mtx_destroy(&sc->sc_mtx);
+ if (sc->sc_mem_res)
+ bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->sc_mem_res),
+ sc->sc_mem_res);
+ if (sc->sc_pdev)
+ destroy_dev(sc->sc_pdev);
+
+ return (0);
+}
+
+static void
+ti_pruss_intr(void *arg)
+{
+ int val;
+ struct ti_pruss_irq_arg *iap = arg;
+ struct ti_pruss_softc *sc = iap->sc;
+ /*
+ * Interrupts pr1_host_intr[0:7] are mapped to
+ * Host-2 to Host-9 of PRU-ICSS IRQ-controller.
+ */
+ const int pru_int = iap->irq + TI_PRUSS_PRU_IRQS;
+ const int pru_int_mask = (1 << pru_int);
+ const int pru_channel = sc->sc_irq_devs[pru_int].channel;
+ const int pru_event = sc->sc_irq_devs[pru_channel].event;
+
+ val = ti_pruss_reg_read(sc, PRUSS_INTC_HIER);
+ if (!(val & pru_int_mask))
+ return;
+
+ ti_pruss_reg_write(sc, PRUSS_INTC_HIDISR, pru_int);
+ ti_pruss_reg_write(sc, PRUSS_INTC_SICR, pru_event);
+ ti_pruss_reg_write(sc, PRUSS_INTC_HIEISR, pru_int);
+
+ struct ti_pruss_irqsc* irq = &sc->sc_irq_devs[pru_channel];
+ size_t wr = irq->tstamps.ctl.idx;
+
+ struct timespec ts;
+ nanouptime(&ts);
+ irq->tstamps.ts[wr] = ts.tv_sec * 1000000000 + ts.tv_nsec;
+
+ if (++wr == TI_TS_ARRAY)
+ wr = 0;
+ atomic_add_32(&irq->tstamps.ctl.cnt, 1);
+
+ irq->tstamps.ctl.idx = wr;
+
+ KNOTE_UNLOCKED(&irq->sc_selinfo.si_note, pru_int);
+ wakeup(irq);
+ selwakeup(&irq->sc_selinfo);
+}
+
+static int
+ti_pruss_open(struct cdev *cdev __unused, int oflags __unused,
+ int devtype __unused, struct thread *td __unused)
+{
+ return (0);
+}
+
+static int
+ti_pruss_mmap(struct cdev *cdev, vm_ooffset_t offset, vm_paddr_t *paddr,
+ int nprot, vm_memattr_t *memattr)
+{
+ device_t dev = cdev->si_drv1;
+ struct ti_pruss_softc *sc = device_get_softc(dev);
+
+ if (offset >= rman_get_size(sc->sc_mem_res))
+ return (ENOSPC);
+ *paddr = rman_get_start(sc->sc_mem_res) + offset;
+ *memattr = VM_MEMATTR_UNCACHEABLE;
+
+ return (0);
+}
+
+static struct filterops ti_pruss_kq_read = {
+ .f_isfd = 1,
+ .f_detach = ti_pruss_irq_kqread_detach,
+ .f_event = ti_pruss_irq_kqevent,
+};
+
+static void
+ti_pruss_irq_kqread_detach(struct knote *kn)
+{
+ struct ti_pruss_irqsc *sc = kn->kn_hook;
+
+ knlist_remove(&sc->sc_selinfo.si_note, kn, 0);
+}
+
+static int
+ti_pruss_irq_kqevent(struct knote *kn, long hint)
+{
+ struct ti_pruss_irqsc* irq_sc;
+ int notify;
+
+ irq_sc = kn->kn_hook;
+
+ if (hint > 0)
+ kn->kn_data = hint - 2;
+
+ if (hint > 0 || irq_sc->last > 0)
+ notify = 1;
+ else
+ notify = 0;
+
+ irq_sc->last = hint;
+
+ return (notify);
+}
+
+static int
+ti_pruss_irq_kqfilter(struct cdev *cdev, struct knote *kn)
+{
+ struct ti_pruss_irqsc *sc = cdev->si_drv1;
+
+ switch (kn->kn_filter) {
+ case EVFILT_READ:
+ kn->kn_hook = sc;
+ kn->kn_fop = &ti_pruss_kq_read;
+ knlist_add(&sc->sc_selinfo.si_note, kn, 0);
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ return (0);
+}
diff --git a/sys/arm/ti/ti_pruss.h b/sys/arm/ti/ti_pruss.h
new file mode 100644
index 000000000000..855ac2d69790
--- /dev/null
+++ b/sys/arm/ti/ti_pruss.h
@@ -0,0 +1,57 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Rui Paulo <rpaulo@FreeBSD.org>
+ * Copyright (c) 2017 Manuel Stuehn
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _TI_PRUSS_H_
+#define _TI_PRUSS_H_
+
+#define PRUSS_AM18XX_INTC 0x04000
+#define PRUSS_AM18XX_REV 0x4e825900
+#define PRUSS_AM33XX_REV 0x4e82A900
+#define PRUSS_AM33XX_INTC 0x20000
+
+#define PRUSS_INTC_GER (PRUSS_AM33XX_INTC + 0x0010)
+#define PRUSS_INTC_SISR (PRUSS_AM33XX_INTC + 0x0020)
+#define PRUSS_INTC_SICR (PRUSS_AM33XX_INTC + 0x0024)
+#define PRUSS_INTC_EISR (PRUSS_AM33XX_INTC + 0x0028)
+#define PRUSS_INTC_EICR (PRUSS_AM33XX_INTC + 0x002C)
+#define PRUSS_INTC_HIEISR (PRUSS_AM33XX_INTC + 0x0034)
+#define PRUSS_INTC_HIDISR (PRUSS_AM33XX_INTC + 0x0038)
+#define PRUSS_INTC_SECR0 (PRUSS_AM33XX_INTC + 0x0280)
+#define PRUSS_INTC_SECR1 (PRUSS_AM33XX_INTC + 0x0284)
+#define PRUSS_INTC_CMR_BASE (PRUSS_AM33XX_INTC + 0x0400)
+#define PRUSS_INTC_HMR_BASE (PRUSS_AM33XX_INTC + 0x0800)
+#define PRUSS_INTC_HIPIR_BASE (PRUSS_AM33XX_INTC + 0x0900)
+#define PRUSS_INTC_SIPR0 (PRUSS_AM33XX_INTC + 0x0D00)
+#define PRUSS_INTC_SIPR1 (PRUSS_AM33XX_INTC + 0x0D04)
+#define PRUSS_INTC_SITR0 (PRUSS_AM33XX_INTC + 0x0D80)
+#define PRUSS_INTC_SITR1 (PRUSS_AM33XX_INTC + 0x0D84)
+#define PRUSS_INTC_HIER (PRUSS_AM33XX_INTC + 0x1500)
+
+#endif /* _TI_PRUSS_H_ */
diff --git a/sys/arm/ti/ti_scm.c b/sys/arm/ti/ti_scm.c
new file mode 100644
index 000000000000..9d3da5e92b3b
--- /dev/null
+++ b/sys/arm/ti/ti_scm.c
@@ -0,0 +1,160 @@
+/*-
+ * Copyright (c) 2019 Emmanuel Vadot <manu@FreeBSD.org>
+ *
+ * Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/* Based on sys/arm/ti/ti_sysc.c */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/fbio.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/resource.h>
+#include <machine/bus.h>
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_kern.h>
+#include <vm/pmap.h>
+
+#include <dev/fdt/simplebus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#define TI_AM3_SCM 14
+#define TI_AM4_SCM 13
+#define TI_DM814_SCRM 12
+#define TI_DM816_SCRM 11
+#define TI_OMAP2_SCM 10
+#define TI_OMAP3_SCM 9
+#define TI_OMAP4_SCM_CORE 8
+#define TI_OMAP4_SCM_PADCONF_CORE 7
+#define TI_OMAP4_SCM_WKUP 6
+#define TI_OMAP4_SCM_PADCONF_WKUP 5
+#define TI_OMAP5_SCM_CORE 4
+#define TI_OMAP5_SCM_PADCONF_CORE 3
+#define TI_OMAP5_SCM_WKUP_PAD_CONF 2
+#define TI_DRA7_SCM_CORE 1
+#define TI_SCM_END 0
+
+static struct ofw_compat_data compat_data[] = {
+ { "ti,am3-scm", TI_AM3_SCM },
+ { "ti,am4-scm", TI_AM4_SCM },
+ { "ti,dm814-scrm", TI_DM814_SCRM },
+ { "ti,dm816-scrm", TI_DM816_SCRM },
+ { "ti,omap2-scm", TI_OMAP2_SCM },
+ { "ti,omap3-scm", TI_OMAP3_SCM },
+ { "ti,omap4-scm-core", TI_OMAP4_SCM_CORE },
+ { "ti,omap4-scm-padconf-core", TI_OMAP4_SCM_PADCONF_CORE },
+ { "ti,omap4-scm-wkup", TI_OMAP4_SCM_WKUP },
+ { "ti,omap4-scm-padconf-wkup", TI_OMAP4_SCM_PADCONF_WKUP },
+ { "ti,omap5-scm-core", TI_OMAP5_SCM_CORE },
+ { "ti,omap5-scm-padconf-core", TI_OMAP5_SCM_PADCONF_CORE },
+ { "ti,omap5-scm-wkup-pad-conf", TI_OMAP5_SCM_WKUP_PAD_CONF },
+ { "ti,dra7-scm-core", TI_DRA7_SCM_CORE },
+ { NULL, TI_SCM_END }
+};
+
+struct ti_scm_softc {
+ struct simplebus_softc sc;
+ device_t dev;
+};
+
+static int ti_scm_probe(device_t dev);
+static int ti_scm_attach(device_t dev);
+static int ti_scm_detach(device_t dev);
+
+static int
+ti_scm_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "TI OMAP Control Module");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ti_scm_attach(device_t dev)
+{
+ struct ti_scm_softc *sc;
+ device_t cdev;
+ phandle_t node, child;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ node = ofw_bus_get_node(dev);
+
+ simplebus_init(dev, node);
+ if (simplebus_fill_ranges(node, &sc->sc) < 0) {
+ device_printf(dev, "could not get ranges\n");
+ return (ENXIO);
+ }
+
+ for (child = OF_child(node); child > 0; child = OF_peer(child)) {
+ cdev = simplebus_add_device(dev, child, 0, NULL, -1, NULL);
+ if (cdev != NULL)
+ device_probe_and_attach(cdev);
+ }
+
+ return (bus_generic_attach(dev));
+}
+
+static int
+ti_scm_detach(device_t dev)
+{
+ return (EBUSY);
+}
+
+static device_method_t ti_scm_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ti_scm_probe),
+ DEVMETHOD(device_attach, ti_scm_attach),
+ DEVMETHOD(device_detach, ti_scm_detach),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(ti_scm, ti_scm_driver, ti_scm_methods,
+ sizeof(struct ti_scm_softc), simplebus_driver);
+
+static devclass_t ti_scm_devclass;
+
+EARLY_DRIVER_MODULE(ti_scm, simplebus, ti_scm_driver,
+ ti_scm_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_FIRST);
+MODULE_VERSION(ti_scm, 1);
+MODULE_DEPEND(ti_scm, ti_sysc, 1, 1, 1);
diff --git a/sys/arm/ti/ti_scm.h b/sys/arm/ti/ti_scm.h
new file mode 100644
index 000000000000..e59901bc97ee
--- /dev/null
+++ b/sys/arm/ti/ti_scm.h
@@ -0,0 +1,57 @@
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 2010
+ * Ben Gray <ben.r.gray@gmail.com>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Ben Gray.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BEN GRAY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/**
+ * Functions to configure the PIN multiplexing on the chip.
+ *
+ * This is different from the GPIO module in that it is used to configure the
+ * pins between modules not just GPIO input output.
+ *
+ */
+#ifndef _TI_SCM_H_
+#define _TI_SCM_H_
+
+struct ti_scm_softc {
+ device_t sc_dev;
+ struct resource * sc_res[4];
+ bus_space_tag_t sc_bst;
+ bus_space_handle_t sc_bsh;
+};
+
+int ti_scm_reg_read_4(uint32_t reg, uint32_t *val);
+int ti_scm_reg_write_4(uint32_t reg, uint32_t val);
+
+#endif /* _TI_SCM_H_ */
diff --git a/sys/arm/ti/ti_scm_syscon.c b/sys/arm/ti/ti_scm_syscon.c
new file mode 100644
index 000000000000..d3d3f2e3a7e2
--- /dev/null
+++ b/sys/arm/ti/ti_scm_syscon.c
@@ -0,0 +1,298 @@
+/*-
+ * Copyright (c) 2019 Emmanuel Vadot <manu@FreeBSD.org>
+ *
+ * Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+/* Based on sys/arm/ti/ti_sysc.c */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/fdt/simplebus.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "syscon_if.h"
+#include <dev/extres/syscon/syscon.h>
+#include "clkdev_if.h"
+
+#include <arm/ti/ti_cpuid.h>
+
+#if 0
+#define DPRINTF(dev, msg...) device_printf(dev, msg)
+#else
+#define DPRINTF(dev, msg...)
+#endif
+
+MALLOC_DECLARE(M_SYSCON);
+
+struct ti_scm_syscon_softc {
+ struct simplebus_softc sc_simplebus;
+ device_t dev;
+ struct syscon * syscon;
+ struct resource * res[1];
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+ struct mtx mtx;
+};
+
+static struct resource_spec ti_scm_syscon_res_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE | RF_SHAREABLE },
+ { -1, 0 }
+};
+
+/* Device */
+static struct ofw_compat_data compat_data[] = {
+ { "syscon", 1 },
+ { NULL, 0 }
+};
+
+/* --- dev/extres/syscon syscon_method_t interface --- */
+static int
+ti_scm_syscon_write_4(struct syscon *syscon, bus_size_t offset, uint32_t val)
+{
+ struct ti_scm_syscon_softc *sc;
+
+ sc = device_get_softc(syscon->pdev);
+ DPRINTF(sc->dev, "offset=%lx write %x\n", offset, val);
+ mtx_lock(&sc->mtx);
+ bus_space_write_4(sc->bst, sc->bsh, offset, val);
+ mtx_unlock(&sc->mtx);
+ return (0);
+}
+
+static uint32_t
+ti_scm_syscon_read_4(struct syscon *syscon, bus_size_t offset)
+{
+ struct ti_scm_syscon_softc *sc;
+ uint32_t val;
+
+ sc = device_get_softc(syscon->pdev);
+
+ mtx_lock(&sc->mtx);
+ val = bus_space_read_4(sc->bst, sc->bsh, offset);
+ mtx_unlock(&sc->mtx);
+ DPRINTF(sc->dev, "offset=%lx Read %x\n", offset, val);
+ return (val);
+}
+static int
+ti_scm_syscon_modify_4(struct syscon *syscon, bus_size_t offset, uint32_t clr, uint32_t set)
+{
+ struct ti_scm_syscon_softc *sc;
+ uint32_t reg;
+
+ sc = device_get_softc(syscon->pdev);
+
+ mtx_lock(&sc->mtx);
+ reg = bus_space_read_4(sc->bst, sc->bsh, offset);
+ reg &= ~clr;
+ reg |= set;
+ bus_space_write_4(sc->bst, sc->bsh, offset, reg);
+ mtx_unlock(&sc->mtx);
+ DPRINTF(sc->dev, "offset=%lx reg: %x (clr %x set %x)\n", offset, reg, clr, set);
+
+ return (0);
+}
+
+static syscon_method_t ti_scm_syscon_reg_methods[] = {
+ SYSCONMETHOD(syscon_read_4, ti_scm_syscon_read_4),
+ SYSCONMETHOD(syscon_write_4, ti_scm_syscon_write_4),
+ SYSCONMETHOD(syscon_modify_4, ti_scm_syscon_modify_4),
+
+ SYSCONMETHOD_END
+};
+
+DEFINE_CLASS_1(ti_scm_syscon_reg, ti_scm_syscon_reg_class, ti_scm_syscon_reg_methods,
+ 0, syscon_class);
+
+/* device interface */
+static int
+ti_scm_syscon_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ti_soc_is_supported())
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "TI OMAP Control Module Syscon");
+ return(BUS_PROBE_DEFAULT);
+}
+
+static int
+ti_scm_syscon_attach(device_t dev)
+{
+ struct ti_scm_syscon_softc *sc;
+ phandle_t node, child;
+ int err;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ if (bus_alloc_resources(dev, ti_scm_syscon_res_spec, sc->res)) {
+ device_printf(sc->dev, "Cant allocate resources\n");
+ return (ENXIO);
+ }
+
+ sc->dev = dev;
+ sc->bst = rman_get_bustag(sc->res[0]);
+ sc->bsh = rman_get_bushandle(sc->res[0]);
+
+ mtx_init(&sc->mtx, device_get_nameunit(sc->dev), NULL, MTX_DEF);
+ node = ofw_bus_get_node(sc->dev);
+
+ /* dev/extres/syscon interface */
+ sc->syscon = syscon_create_ofw_node(dev, &ti_scm_syscon_reg_class, node);
+ if (sc->syscon == NULL) {
+ device_printf(dev, "Failed to create/register syscon\n");
+ return (ENXIO);
+ }
+
+ simplebus_init(sc->dev, node);
+
+ err = bus_generic_probe(sc->dev);
+ for (child = OF_child(node); child != 0; child = OF_peer(child)) {
+ simplebus_add_device(sc->dev, child, 0, NULL, -1, NULL);
+ }
+
+ return (bus_generic_attach(sc->dev));
+}
+
+/* syscon interface */
+static int
+ti_scm_syscon_get_handle(device_t dev, struct syscon **syscon)
+{
+ struct ti_scm_syscon_softc *sc;
+
+ sc = device_get_softc(dev);
+ *syscon = sc->syscon;
+ if (*syscon == NULL)
+ return (ENODEV);
+ return (0);
+}
+
+/* clkdev interface */
+static int
+ti_scm_syscon_clk_write_4(device_t dev, bus_addr_t addr, uint32_t val)
+{
+ struct ti_scm_syscon_softc *sc;
+
+ sc = device_get_softc(dev);
+ DPRINTF(sc->dev, "offset=%lx write %x\n", addr, val);
+ bus_space_write_4(sc->bst, sc->bsh, addr, val);
+ return (0);
+}
+
+static int
+ti_scm_syscon_clk_read_4(device_t dev, bus_addr_t addr, uint32_t *val)
+{
+ struct ti_scm_syscon_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ *val = bus_space_read_4(sc->bst, sc->bsh, addr);
+ DPRINTF(sc->dev, "offset=%lx Read %x\n", addr, *val);
+ return (0);
+}
+
+static int
+ti_scm_syscon_clk_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set)
+{
+ struct ti_scm_syscon_softc *sc;
+ uint32_t reg;
+
+ sc = device_get_softc(dev);
+
+ reg = bus_space_read_4(sc->bst, sc->bsh, addr);
+ reg &= ~clr;
+ reg |= set;
+ bus_space_write_4(sc->bst, sc->bsh, addr, reg);
+ DPRINTF(sc->dev, "offset=%lx reg: %x (clr %x set %x)\n", addr, reg, clr, set);
+
+ return (0);
+}
+
+static void
+ti_scm_syscon_clk_device_lock(device_t dev)
+{
+ struct ti_scm_syscon_softc *sc;
+
+ sc = device_get_softc(dev);
+ mtx_lock(&sc->mtx);
+}
+
+static void
+ti_scm_syscon_clk_device_unlock(device_t dev)
+{
+ struct ti_scm_syscon_softc *sc;
+ sc = device_get_softc(dev);
+ mtx_unlock(&sc->mtx);
+}
+
+static device_method_t ti_scm_syscon_methods[] = {
+ DEVMETHOD(device_probe, ti_scm_syscon_probe),
+ DEVMETHOD(device_attach, ti_scm_syscon_attach),
+
+ /* syscon interface */
+ DEVMETHOD(syscon_get_handle, ti_scm_syscon_get_handle),
+
+ /* clkdev interface */
+ DEVMETHOD(clkdev_write_4, ti_scm_syscon_clk_write_4),
+ DEVMETHOD(clkdev_read_4, ti_scm_syscon_clk_read_4),
+ DEVMETHOD(clkdev_modify_4, ti_scm_syscon_clk_modify_4),
+ DEVMETHOD(clkdev_device_lock, ti_scm_syscon_clk_device_lock),
+ DEVMETHOD(clkdev_device_unlock, ti_scm_syscon_clk_device_unlock),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(ti_scm_syscon, ti_scm_syscon_driver, ti_scm_syscon_methods,
+ sizeof(struct ti_scm_syscon_softc), simplebus_driver);
+
+static devclass_t ti_scm_syscon_devclass;
+
+EARLY_DRIVER_MODULE(ti_scm_syscon, simplebus, ti_scm_syscon_driver,
+ ti_scm_syscon_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
+MODULE_VERSION(ti_scm_syscon, 1);
+MODULE_DEPEND(ti_scm_syscon, ti_scm, 1, 1, 1);
diff --git a/sys/arm/ti/ti_sdhci.c b/sys/arm/ti/ti_sdhci.c
new file mode 100644
index 000000000000..8554a562ad84
--- /dev/null
+++ b/sys/arm/ti/ti_sdhci.c
@@ -0,0 +1,770 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Ian Lepore <ian@freebsd.org>
+ * Copyright (c) 2011 Ben Gray <ben.r.gray@gmail.com>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/gpio.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+#include <sys/taskqueue.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <arm/ti/ti_cpuid.h>
+#include <arm/ti/ti_sysc.h>
+#include "gpio_if.h"
+
+#include <dev/extres/clk/clk.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/mmc/bridge.h>
+#include <dev/mmc/mmcreg.h>
+#include <dev/mmc/mmcbrvar.h>
+
+#include <dev/sdhci/sdhci.h>
+#include <dev/sdhci/sdhci_fdt_gpio.h>
+#include "sdhci_if.h"
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/intr.h>
+
+#include "opt_mmccam.h"
+
+struct ti_sdhci_softc {
+ device_t dev;
+ struct sdhci_fdt_gpio * gpio;
+ struct resource * mem_res;
+ struct resource * irq_res;
+ void * intr_cookie;
+ struct sdhci_slot slot;
+ uint32_t mmchs_reg_off;
+ uint32_t sdhci_reg_off;
+ uint64_t baseclk_hz;
+ uint32_t cmd_and_mode;
+ uint32_t sdhci_clkdiv;
+ boolean_t disable_highspeed;
+ boolean_t force_card_present;
+ boolean_t disable_readonly;
+};
+
+/*
+ * Table of supported FDT compat strings.
+ *
+ * Note that "ti,mmchs" is our own invention, and should be phased out in favor
+ * of the documented names.
+ *
+ * Note that vendor Beaglebone dtsi files use "ti,omap3-hsmmc" for the am335x.
+ */
+static struct ofw_compat_data compat_data[] = {
+ {"ti,am335-sdhci", 1},
+ {"ti,omap3-hsmmc", 1},
+ {"ti,omap4-hsmmc", 1},
+ {"ti,mmchs", 1},
+ {NULL, 0},
+};
+
+/*
+ * The MMCHS hardware has a few control and status registers at the beginning of
+ * the device's memory map, followed by the standard sdhci register block.
+ * Different SoCs have the register blocks at different offsets from the
+ * beginning of the device. Define some constants to map out the registers we
+ * access, and the various per-SoC offsets. The SDHCI_REG_OFFSET is how far
+ * beyond the MMCHS block the SDHCI block is found; it's the same on all SoCs.
+ */
+#define OMAP3_MMCHS_REG_OFFSET 0x000
+#define OMAP4_MMCHS_REG_OFFSET 0x100
+#define AM335X_MMCHS_REG_OFFSET 0x100
+#define SDHCI_REG_OFFSET 0x100
+
+#define MMCHS_SYSCONFIG 0x010
+#define MMCHS_SYSCONFIG_RESET (1 << 1)
+#define MMCHS_SYSSTATUS 0x014
+#define MMCHS_SYSSTATUS_RESETDONE (1 << 0)
+#define MMCHS_CON 0x02C
+#define MMCHS_CON_DW8 (1 << 5)
+#define MMCHS_CON_DVAL_8_4MS (3 << 9)
+#define MMCHS_CON_OD (1 << 0)
+#define MMCHS_SYSCTL 0x12C
+#define MMCHS_SYSCTL_CLKD_MASK 0x3FF
+#define MMCHS_SYSCTL_CLKD_SHIFT 6
+#define MMCHS_SD_CAPA 0x140
+#define MMCHS_SD_CAPA_VS18 (1 << 26)
+#define MMCHS_SD_CAPA_VS30 (1 << 25)
+#define MMCHS_SD_CAPA_VS33 (1 << 24)
+
+/* Forward declarations, CAM-relataed */
+// static void ti_sdhci_cam_poll(struct cam_sim *);
+// static void ti_sdhci_cam_action(struct cam_sim *, union ccb *);
+// static int ti_sdhci_cam_settran_settings(struct ti_sdhci_softc *sc, union ccb *);
+
+static inline uint32_t
+ti_mmchs_read_4(struct ti_sdhci_softc *sc, bus_size_t off)
+{
+
+ return (bus_read_4(sc->mem_res, off + sc->mmchs_reg_off));
+}
+
+static inline void
+ti_mmchs_write_4(struct ti_sdhci_softc *sc, bus_size_t off, uint32_t val)
+{
+
+ bus_write_4(sc->mem_res, off + sc->mmchs_reg_off, val);
+}
+
+static inline uint32_t
+RD4(struct ti_sdhci_softc *sc, bus_size_t off)
+{
+
+ return (bus_read_4(sc->mem_res, off + sc->sdhci_reg_off));
+}
+
+static inline void
+WR4(struct ti_sdhci_softc *sc, bus_size_t off, uint32_t val)
+{
+
+ bus_write_4(sc->mem_res, off + sc->sdhci_reg_off, val);
+}
+
+static uint8_t
+ti_sdhci_read_1(device_t dev, struct sdhci_slot *slot, bus_size_t off)
+{
+ struct ti_sdhci_softc *sc = device_get_softc(dev);
+
+ return ((RD4(sc, off & ~3) >> (off & 3) * 8) & 0xff);
+}
+
+static uint16_t
+ti_sdhci_read_2(device_t dev, struct sdhci_slot *slot, bus_size_t off)
+{
+ struct ti_sdhci_softc *sc = device_get_softc(dev);
+ uint32_t clkdiv, val32;
+
+ /*
+ * The MMCHS hardware has a non-standard interpretation of the sdclock
+ * divisor bits. It uses the same bit positions as SDHCI 3.0 (15..6)
+ * but doesn't split them into low:high fields. Instead they're a
+ * single number in the range 0..1023 and the number is exactly the
+ * clock divisor (with 0 and 1 both meaning divide by 1). The SDHCI
+ * driver code expects a v2.0 or v3.0 divisor. The shifting and masking
+ * here extracts the MMCHS representation from the hardware word, cleans
+ * those bits out, applies the 2N adjustment, and plugs the result into
+ * the bit positions for the 2.0 or 3.0 divisor in the returned register
+ * value. The ti_sdhci_write_2() routine performs the opposite
+ * transformation when the SDHCI driver writes to the register.
+ */
+ if (off == SDHCI_CLOCK_CONTROL) {
+ val32 = RD4(sc, SDHCI_CLOCK_CONTROL);
+ clkdiv = ((val32 >> MMCHS_SYSCTL_CLKD_SHIFT) &
+ MMCHS_SYSCTL_CLKD_MASK) / 2;
+ val32 &= ~(MMCHS_SYSCTL_CLKD_MASK << MMCHS_SYSCTL_CLKD_SHIFT);
+ val32 |= (clkdiv & SDHCI_DIVIDER_MASK) << SDHCI_DIVIDER_SHIFT;
+ if (slot->version >= SDHCI_SPEC_300)
+ val32 |= ((clkdiv >> SDHCI_DIVIDER_MASK_LEN) &
+ SDHCI_DIVIDER_HI_MASK) << SDHCI_DIVIDER_HI_SHIFT;
+ return (val32 & 0xffff);
+ }
+
+ /*
+ * Standard 32-bit handling of command and transfer mode.
+ */
+ if (off == SDHCI_TRANSFER_MODE) {
+ return (sc->cmd_and_mode >> 16);
+ } else if (off == SDHCI_COMMAND_FLAGS) {
+ return (sc->cmd_and_mode & 0x0000ffff);
+ }
+
+ return ((RD4(sc, off & ~3) >> (off & 3) * 8) & 0xffff);
+}
+
+static uint32_t
+ti_sdhci_read_4(device_t dev, struct sdhci_slot *slot, bus_size_t off)
+{
+ struct ti_sdhci_softc *sc = device_get_softc(dev);
+ uint32_t val32;
+
+ val32 = RD4(sc, off);
+
+ /*
+ * If we need to disallow highspeed mode due to the OMAP4 erratum, strip
+ * that flag from the returned capabilities.
+ */
+ if (off == SDHCI_CAPABILITIES && sc->disable_highspeed)
+ val32 &= ~SDHCI_CAN_DO_HISPD;
+
+ /*
+ * Force the card-present state if necessary.
+ */
+ if (off == SDHCI_PRESENT_STATE && sc->force_card_present)
+ val32 |= SDHCI_CARD_PRESENT;
+
+ return (val32);
+}
+
+static void
+ti_sdhci_read_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off,
+ uint32_t *data, bus_size_t count)
+{
+ struct ti_sdhci_softc *sc = device_get_softc(dev);
+
+ bus_read_multi_4(sc->mem_res, off + sc->sdhci_reg_off, data, count);
+}
+
+static void
+ti_sdhci_write_1(device_t dev, struct sdhci_slot *slot, bus_size_t off,
+ uint8_t val)
+{
+ struct ti_sdhci_softc *sc = device_get_softc(dev);
+ uint32_t val32;
+
+#ifdef MMCCAM
+ uint32_t newval32;
+ if (off == SDHCI_HOST_CONTROL) {
+ val32 = ti_mmchs_read_4(sc, MMCHS_CON);
+ newval32 = val32;
+ if (val & SDHCI_CTRL_8BITBUS) {
+ device_printf(dev, "Custom-enabling 8-bit bus\n");
+ newval32 |= MMCHS_CON_DW8;
+ } else {
+ device_printf(dev, "Custom-disabling 8-bit bus\n");
+ newval32 &= ~MMCHS_CON_DW8;
+ }
+ if (newval32 != val32)
+ ti_mmchs_write_4(sc, MMCHS_CON, newval32);
+ }
+#endif
+ val32 = RD4(sc, off & ~3);
+ val32 &= ~(0xff << (off & 3) * 8);
+ val32 |= (val << (off & 3) * 8);
+
+ WR4(sc, off & ~3, val32);
+}
+
+static void
+ti_sdhci_write_2(device_t dev, struct sdhci_slot *slot, bus_size_t off,
+ uint16_t val)
+{
+ struct ti_sdhci_softc *sc = device_get_softc(dev);
+ uint32_t clkdiv, val32;
+
+ /*
+ * Translate between the hardware and SDHCI 2.0 or 3.0 representations
+ * of the clock divisor. See the comments in ti_sdhci_read_2() for
+ * details.
+ */
+ if (off == SDHCI_CLOCK_CONTROL) {
+ clkdiv = (val >> SDHCI_DIVIDER_SHIFT) & SDHCI_DIVIDER_MASK;
+ if (slot->version >= SDHCI_SPEC_300)
+ clkdiv |= ((val >> SDHCI_DIVIDER_HI_SHIFT) &
+ SDHCI_DIVIDER_HI_MASK) << SDHCI_DIVIDER_MASK_LEN;
+ clkdiv *= 2;
+ if (clkdiv > MMCHS_SYSCTL_CLKD_MASK)
+ clkdiv = MMCHS_SYSCTL_CLKD_MASK;
+ val32 = RD4(sc, SDHCI_CLOCK_CONTROL);
+ val32 &= 0xffff0000;
+ val32 |= val & ~(MMCHS_SYSCTL_CLKD_MASK <<
+ MMCHS_SYSCTL_CLKD_SHIFT);
+ val32 |= clkdiv << MMCHS_SYSCTL_CLKD_SHIFT;
+ WR4(sc, SDHCI_CLOCK_CONTROL, val32);
+ return;
+ }
+
+ /*
+ * Standard 32-bit handling of command and transfer mode.
+ */
+ if (off == SDHCI_TRANSFER_MODE) {
+ sc->cmd_and_mode = (sc->cmd_and_mode & 0xffff0000) |
+ ((uint32_t)val & 0x0000ffff);
+ return;
+ } else if (off == SDHCI_COMMAND_FLAGS) {
+ sc->cmd_and_mode = (sc->cmd_and_mode & 0x0000ffff) |
+ ((uint32_t)val << 16);
+ WR4(sc, SDHCI_TRANSFER_MODE, sc->cmd_and_mode);
+ return;
+ }
+
+ val32 = RD4(sc, off & ~3);
+ val32 &= ~(0xffff << (off & 3) * 8);
+ val32 |= ((val & 0xffff) << (off & 3) * 8);
+ WR4(sc, off & ~3, val32);
+}
+
+static void
+ti_sdhci_write_4(device_t dev, struct sdhci_slot *slot, bus_size_t off,
+ uint32_t val)
+{
+ struct ti_sdhci_softc *sc = device_get_softc(dev);
+
+ WR4(sc, off, val);
+}
+
+static void
+ti_sdhci_write_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off,
+ uint32_t *data, bus_size_t count)
+{
+ struct ti_sdhci_softc *sc = device_get_softc(dev);
+
+ bus_write_multi_4(sc->mem_res, off + sc->sdhci_reg_off, data, count);
+}
+
+static void
+ti_sdhci_intr(void *arg)
+{
+ struct ti_sdhci_softc *sc = arg;
+
+ sdhci_generic_intr(&sc->slot);
+}
+
+static int
+ti_sdhci_update_ios(device_t brdev, device_t reqdev)
+{
+ struct ti_sdhci_softc *sc = device_get_softc(brdev);
+ struct sdhci_slot *slot;
+ struct mmc_ios *ios;
+ uint32_t val32, newval32;
+
+ slot = device_get_ivars(reqdev);
+ ios = &slot->host.ios;
+
+ /*
+ * There is an 8-bit-bus bit in the MMCHS control register which, when
+ * set, overrides the 1 vs 4 bit setting in the standard SDHCI
+ * registers. Set that bit first according to whether an 8-bit bus is
+ * requested, then let the standard driver handle everything else.
+ */
+ val32 = ti_mmchs_read_4(sc, MMCHS_CON);
+ newval32 = val32;
+
+ if (ios->bus_width == bus_width_8)
+ newval32 |= MMCHS_CON_DW8;
+ else
+ newval32 &= ~MMCHS_CON_DW8;
+
+ if (ios->bus_mode == opendrain)
+ newval32 |= MMCHS_CON_OD;
+ else /* if (ios->bus_mode == pushpull) */
+ newval32 &= ~MMCHS_CON_OD;
+
+ if (newval32 != val32)
+ ti_mmchs_write_4(sc, MMCHS_CON, newval32);
+
+ return (sdhci_generic_update_ios(brdev, reqdev));
+}
+
+static int
+ti_sdhci_get_ro(device_t brdev, device_t reqdev)
+{
+ struct ti_sdhci_softc *sc = device_get_softc(brdev);
+
+ if (sc->disable_readonly)
+ return (0);
+
+ return (sdhci_fdt_gpio_get_readonly(sc->gpio));
+}
+
+static bool
+ti_sdhci_get_card_present(device_t dev, struct sdhci_slot *slot)
+{
+ struct ti_sdhci_softc *sc = device_get_softc(dev);
+
+ return (sdhci_fdt_gpio_get_present(sc->gpio));
+}
+
+static int
+ti_sdhci_detach(device_t dev)
+{
+
+ /* sdhci_fdt_gpio_teardown(sc->gpio); */
+
+ return (EBUSY);
+}
+
+static int
+ti_sdhci_hw_init(device_t dev)
+{
+ struct ti_sdhci_softc *sc = device_get_softc(dev);
+ uint32_t regval;
+ unsigned long timeout;
+ clk_t mmc_clk;
+ int err;
+
+ /* Enable the controller and interface/functional clocks */
+ if (ti_sysc_clock_enable(device_get_parent(dev)) != 0) {
+ device_printf(dev, "Error: failed to enable MMC clock\n");
+ return (ENXIO);
+ }
+
+ /* FIXME: Devicetree dosent have any reference to mmc_clk */
+ err = clk_get_by_name(dev, "mmc_clk", &mmc_clk);
+ if (err) {
+ device_printf(dev, "Can not find mmc_clk\n");
+ return (ENXIO);
+ }
+ err = clk_get_freq(mmc_clk, &sc->baseclk_hz);
+ if (err) {
+ device_printf(dev, "Cant get mmc_clk frequency\n");
+ /* AM335x TRM 8.1.6.8 table 8-24 96MHz @ OPP100 */
+ sc->baseclk_hz = 96000000;
+ }
+
+ /* Issue a softreset to the controller */
+ ti_mmchs_write_4(sc, MMCHS_SYSCONFIG, MMCHS_SYSCONFIG_RESET);
+ timeout = 1000;
+ while (!(ti_mmchs_read_4(sc, MMCHS_SYSSTATUS) &
+ MMCHS_SYSSTATUS_RESETDONE)) {
+ if (--timeout == 0) {
+ device_printf(dev,
+ "Error: Controller reset operation timed out\n");
+ break;
+ }
+ DELAY(100);
+ }
+
+ /*
+ * Reset the command and data state machines and also other aspects of
+ * the controller such as bus clock and power.
+ *
+ * If we read the software reset register too fast after writing it we
+ * can get back a zero that means the reset hasn't started yet rather
+ * than that the reset is complete. Per TI recommendations, work around
+ * it by reading until we see the reset bit asserted, then read until
+ * it's clear. We also set the SDHCI_QUIRK_WAITFOR_RESET_ASSERTED quirk
+ * so that the main sdhci driver uses this same logic in its resets.
+ */
+ ti_sdhci_write_1(dev, NULL, SDHCI_SOFTWARE_RESET, SDHCI_RESET_ALL);
+ timeout = 10000;
+ while ((ti_sdhci_read_1(dev, NULL, SDHCI_SOFTWARE_RESET) &
+ SDHCI_RESET_ALL) != SDHCI_RESET_ALL) {
+ if (--timeout == 0) {
+ break;
+ }
+ DELAY(1);
+ }
+ timeout = 10000;
+ while ((ti_sdhci_read_1(dev, NULL, SDHCI_SOFTWARE_RESET) &
+ SDHCI_RESET_ALL)) {
+ if (--timeout == 0) {
+ device_printf(dev,
+ "Error: Software reset operation timed out\n");
+ break;
+ }
+ DELAY(100);
+ }
+
+ /*
+ * The attach() routine has examined fdt data and set flags in
+ * slot.host.caps to reflect what voltages we can handle. Set those
+ * values in the CAPA register. Empirical testing shows that the
+ * values in this register can be overwritten at any time, but the
+ * manual says that these values should only be set once, "before
+ * initialization" whatever that means, and that they survive a reset.
+ */
+ regval = ti_mmchs_read_4(sc, MMCHS_SD_CAPA);
+ if (sc->slot.host.caps & MMC_OCR_LOW_VOLTAGE)
+ regval |= MMCHS_SD_CAPA_VS18;
+ if (sc->slot.host.caps & (MMC_OCR_290_300 | MMC_OCR_300_310))
+ regval |= MMCHS_SD_CAPA_VS30;
+ ti_mmchs_write_4(sc, MMCHS_SD_CAPA, regval);
+
+ /* Set initial host configuration (1-bit, std speed, pwr off). */
+ ti_sdhci_write_1(dev, NULL, SDHCI_HOST_CONTROL, 0);
+ ti_sdhci_write_1(dev, NULL, SDHCI_POWER_CONTROL, 0);
+
+ /* Set the initial controller configuration. */
+ ti_mmchs_write_4(sc, MMCHS_CON, MMCHS_CON_DVAL_8_4MS);
+
+ return (0);
+}
+
+static int
+ti_sdhci_attach(device_t dev)
+{
+ struct ti_sdhci_softc *sc = device_get_softc(dev);
+ int rid, err;
+ pcell_t prop;
+ phandle_t node;
+
+ sc->dev = dev;
+
+ /*
+ * Get the MMCHS device id from FDT. Use rev address to identify the unit.
+ */
+ node = ofw_bus_get_node(dev);
+
+ /*
+ * The hardware can inherently do dual-voltage (1p8v, 3p0v) on the first
+ * device, and only 1p8v on other devices unless an external transceiver
+ * is used. The only way we could know about a transceiver is fdt data.
+ * Note that we have to do this before calling ti_sdhci_hw_init() so
+ * that it can set the right values in the CAPA register.
+ */
+ sc->slot.host.caps |= MMC_OCR_LOW_VOLTAGE;
+
+ if (OF_hasprop(node, "ti,dual-volt")) {
+ sc->slot.host.caps |= MMC_OCR_290_300 | MMC_OCR_300_310;
+ }
+
+ /*
+ * Set the offset from the device's memory start to the MMCHS registers.
+ * Also for OMAP4 disable high speed mode due to erratum ID i626.
+ */
+ switch (ti_chip()) {
+#ifdef SOC_OMAP4
+ case CHIP_OMAP_4:
+ sc->mmchs_reg_off = OMAP4_MMCHS_REG_OFFSET;
+ sc->disable_highspeed = true;
+ break;
+#endif
+#ifdef SOC_TI_AM335X
+ case CHIP_AM335X:
+ sc->mmchs_reg_off = AM335X_MMCHS_REG_OFFSET;
+ break;
+#endif
+ default:
+ panic("Unknown OMAP device\n");
+ }
+
+ /*
+ * The standard SDHCI registers are at a fixed offset (the same on all
+ * SoCs) beyond the MMCHS registers.
+ */
+ sc->sdhci_reg_off = sc->mmchs_reg_off + SDHCI_REG_OFFSET;
+
+ /* Resource setup. */
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (!sc->mem_res) {
+ device_printf(dev, "cannot allocate memory window\n");
+ err = ENXIO;
+ goto fail;
+ }
+
+ rid = 0;
+ sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (!sc->irq_res) {
+ device_printf(dev, "cannot allocate interrupt\n");
+ err = ENXIO;
+ goto fail;
+ }
+
+ if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ NULL, ti_sdhci_intr, sc, &sc->intr_cookie)) {
+ device_printf(dev, "cannot setup interrupt handler\n");
+ err = ENXIO;
+ goto fail;
+ }
+
+ /*
+ * Set up handling of card-detect and write-protect gpio lines.
+ *
+ * If there is no write protect info in the fdt data, fall back to the
+ * historical practice of assuming that the card is writable. This
+ * works around bad fdt data from the upstream source. The alternative
+ * would be to trust the sdhci controller's PRESENT_STATE register WP
+ * bit, but it may say write protect is in effect when it's not if the
+ * pinmux setup doesn't route the WP signal into the sdchi block.
+ */
+ sc->gpio = sdhci_fdt_gpio_setup(sc->dev, &sc->slot);
+
+ if (!OF_hasprop(node, "wp-gpios") && !OF_hasprop(node, "wp-disable"))
+ sc->disable_readonly = true;
+
+ /* Initialise the MMCHS hardware. */
+ err = ti_sdhci_hw_init(dev);
+ if (err != 0) {
+ /* err should already contain ENXIO from ti_sdhci_hw_init() */
+ goto fail;
+ }
+
+ /*
+ * The capabilities register can only express base clock frequencies in
+ * the range of 0-63MHz for a v2.0 controller. Since our clock runs
+ * faster than that, the hardware sets the frequency to zero in the
+ * register. When the register contains zero, the sdhci driver expects
+ * slot.max_clk to already have the right value in it.
+ */
+ sc->slot.max_clk = sc->baseclk_hz;
+
+ /*
+ * The MMCHS timeout counter is based on the output sdclock. Tell the
+ * sdhci driver to recalculate the timeout clock whenever the output
+ * sdclock frequency changes.
+ */
+ sc->slot.quirks |= SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK;
+
+ /*
+ * The MMCHS hardware shifts the 136-bit response data (in violation of
+ * the spec), so tell the sdhci driver not to do the same in software.
+ */
+ sc->slot.quirks |= SDHCI_QUIRK_DONT_SHIFT_RESPONSE;
+
+ /*
+ * Reset bits are broken, have to wait to see the bits asserted
+ * before waiting to see them de-asserted.
+ */
+ sc->slot.quirks |= SDHCI_QUIRK_WAITFOR_RESET_ASSERTED;
+
+ /*
+ * The controller waits for busy responses.
+ */
+ sc->slot.quirks |= SDHCI_QUIRK_WAIT_WHILE_BUSY;
+
+ /*
+ * DMA is not really broken, I just haven't implemented it yet.
+ */
+ sc->slot.quirks |= SDHCI_QUIRK_BROKEN_DMA;
+
+ /*
+ * Set up the hardware and go. Note that this sets many of the
+ * slot.host.* fields, so we have to do this before overriding any of
+ * those values based on fdt data, below.
+ */
+ sdhci_init_slot(dev, &sc->slot, 0);
+
+ /*
+ * The SDHCI controller doesn't realize it, but we can support 8-bit
+ * even though we're not a v3.0 controller. If there's an fdt bus-width
+ * property, honor it.
+ */
+ if (OF_getencprop(node, "bus-width", &prop, sizeof(prop)) > 0) {
+ sc->slot.host.caps &= ~(MMC_CAP_4_BIT_DATA |
+ MMC_CAP_8_BIT_DATA);
+ switch (prop) {
+ case 8:
+ sc->slot.host.caps |= MMC_CAP_8_BIT_DATA;
+ /* FALLTHROUGH */
+ case 4:
+ sc->slot.host.caps |= MMC_CAP_4_BIT_DATA;
+ break;
+ case 1:
+ break;
+ default:
+ device_printf(dev, "Bad bus-width value %u\n", prop);
+ break;
+ }
+ }
+
+ /*
+ * If the slot is flagged with the non-removable property, set our flag
+ * to always force the SDHCI_CARD_PRESENT bit on.
+ */
+ node = ofw_bus_get_node(dev);
+ if (OF_hasprop(node, "non-removable"))
+ sc->force_card_present = true;
+
+ bus_generic_probe(dev);
+ bus_generic_attach(dev);
+
+ sdhci_start_slot(&sc->slot);
+ return (0);
+
+fail:
+ if (sc->intr_cookie)
+ bus_teardown_intr(dev, sc->irq_res, sc->intr_cookie);
+ if (sc->irq_res)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+ if (sc->mem_res)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+
+ return (err);
+}
+
+static int
+ti_sdhci_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) {
+ device_set_desc(dev, "TI MMCHS (SDHCI 2.0)");
+ return (BUS_PROBE_DEFAULT);
+ }
+
+ return (ENXIO);
+}
+
+static device_method_t ti_sdhci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ti_sdhci_probe),
+ DEVMETHOD(device_attach, ti_sdhci_attach),
+ DEVMETHOD(device_detach, ti_sdhci_detach),
+
+ /* Bus interface */
+ DEVMETHOD(bus_read_ivar, sdhci_generic_read_ivar),
+ DEVMETHOD(bus_write_ivar, sdhci_generic_write_ivar),
+
+ /* MMC bridge interface */
+ DEVMETHOD(mmcbr_update_ios, ti_sdhci_update_ios),
+ DEVMETHOD(mmcbr_request, sdhci_generic_request),
+ DEVMETHOD(mmcbr_get_ro, ti_sdhci_get_ro),
+ DEVMETHOD(mmcbr_acquire_host, sdhci_generic_acquire_host),
+ DEVMETHOD(mmcbr_release_host, sdhci_generic_release_host),
+
+ /* SDHCI registers accessors */
+ DEVMETHOD(sdhci_read_1, ti_sdhci_read_1),
+ DEVMETHOD(sdhci_read_2, ti_sdhci_read_2),
+ DEVMETHOD(sdhci_read_4, ti_sdhci_read_4),
+ DEVMETHOD(sdhci_read_multi_4, ti_sdhci_read_multi_4),
+ DEVMETHOD(sdhci_write_1, ti_sdhci_write_1),
+ DEVMETHOD(sdhci_write_2, ti_sdhci_write_2),
+ DEVMETHOD(sdhci_write_4, ti_sdhci_write_4),
+ DEVMETHOD(sdhci_write_multi_4, ti_sdhci_write_multi_4),
+ DEVMETHOD(sdhci_get_card_present, ti_sdhci_get_card_present),
+
+ DEVMETHOD_END
+};
+
+static devclass_t ti_sdhci_devclass;
+
+static driver_t ti_sdhci_driver = {
+ "sdhci_ti",
+ ti_sdhci_methods,
+ sizeof(struct ti_sdhci_softc),
+};
+
+DRIVER_MODULE(sdhci_ti, simplebus, ti_sdhci_driver, ti_sdhci_devclass, NULL,
+ NULL);
+MODULE_DEPEND(sdhci_ti, ti_sysc, 1, 1, 1);
+SDHCI_DEPEND(sdhci_ti);
+
+#ifndef MMCCAM
+MMC_DECLARE_BRIDGE(sdhci_ti);
+#endif
diff --git a/sys/arm/ti/ti_sdma.c b/sys/arm/ti/ti_sdma.c
new file mode 100644
index 000000000000..9da33d47df82
--- /dev/null
+++ b/sys/arm/ti/ti_sdma.c
@@ -0,0 +1,1248 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2011
+ * Ben Gray <ben.r.gray@gmail.com>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/interrupt.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/queue.h>
+#include <sys/taskqueue.h>
+#include <sys/timetc.h>
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/ti/ti_cpuid.h>
+#include <arm/ti/ti_sysc.h>
+#include <arm/ti/ti_sdma.h>
+#include <arm/ti/ti_sdmareg.h>
+
+/**
+ * Kernel functions for using the DMA controller
+ *
+ *
+ * DMA TRANSFERS:
+ * A DMA transfer block consists of a number of frames (FN). Each frame
+ * consists of a number of elements, and each element can have a size of 8, 16,
+ * or 32 bits.
+ *
+ * OMAP44xx and newer chips support linked list (aka scatter gather) transfers,
+ * where a linked list of source/destination pairs can be placed in memory
+ * for the H/W to process. Earlier chips only allowed you to chain multiple
+ * channels together. However currently this linked list feature is not
+ * supported by the driver.
+ *
+ */
+
+/**
+ * Data structure per DMA channel.
+ *
+ *
+ */
+struct ti_sdma_channel {
+ /*
+ * The configuration registers for the given channel, these are modified
+ * by the set functions and only written to the actual registers when a
+ * transaction is started.
+ */
+ uint32_t reg_csdp;
+ uint32_t reg_ccr;
+ uint32_t reg_cicr;
+
+ /* Set when one of the configuration registers above change */
+ uint32_t need_reg_write;
+
+ /* Callback function used when an interrupt is tripped on the given channel */
+ void (*callback)(unsigned int ch, uint32_t ch_status, void *data);
+
+ /* Callback data passed in the callback ... duh */
+ void* callback_data;
+
+};
+
+/**
+ * DMA driver context, allocated and stored globally, this driver is not
+ * intetned to ever be unloaded (see ti_sdma_sc).
+ *
+ */
+struct ti_sdma_softc {
+ device_t sc_dev;
+ struct resource* sc_irq_res;
+ struct resource* sc_mem_res;
+
+ /*
+ * I guess in theory we should have a mutex per DMA channel for register
+ * modifications. But since we know we are never going to be run on a SMP
+ * system, we can use just the single lock for all channels.
+ */
+ struct mtx sc_mtx;
+
+ /* Stores the H/W revision read from the registers */
+ uint32_t sc_hw_rev;
+
+ /*
+ * Bits in the sc_active_channels data field indicate if the channel has
+ * been activated.
+ */
+ uint32_t sc_active_channels;
+
+ struct ti_sdma_channel sc_channel[NUM_DMA_CHANNELS];
+
+};
+
+static struct ti_sdma_softc *ti_sdma_sc = NULL;
+
+/**
+ * Macros for driver mutex locking
+ */
+#define TI_SDMA_LOCK(_sc) mtx_lock_spin(&(_sc)->sc_mtx)
+#define TI_SDMA_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->sc_mtx)
+#define TI_SDMA_LOCK_INIT(_sc) \
+ mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \
+ "ti_sdma", MTX_SPIN)
+#define TI_SDMA_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx);
+#define TI_SDMA_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED);
+#define TI_SDMA_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
+
+/**
+ * Function prototypes
+ *
+ */
+static void ti_sdma_intr(void *);
+
+/**
+ * ti_sdma_read_4 - reads a 32-bit value from one of the DMA registers
+ * @sc: DMA device context
+ * @off: The offset of a register from the DMA register address range
+ *
+ *
+ * RETURNS:
+ * 32-bit value read from the register.
+ */
+static inline uint32_t
+ti_sdma_read_4(struct ti_sdma_softc *sc, bus_size_t off)
+{
+ return bus_read_4(sc->sc_mem_res, off);
+}
+
+/**
+ * ti_sdma_write_4 - writes a 32-bit value to one of the DMA registers
+ * @sc: DMA device context
+ * @off: The offset of a register from the DMA register address range
+ *
+ *
+ * RETURNS:
+ * 32-bit value read from the register.
+ */
+static inline void
+ti_sdma_write_4(struct ti_sdma_softc *sc, bus_size_t off, uint32_t val)
+{
+ bus_write_4(sc->sc_mem_res, off, val);
+}
+
+/**
+ * ti_sdma_is_omap3_rev - returns true if H/W is from OMAP3 series
+ * @sc: DMA device context
+ *
+ */
+static inline int
+ti_sdma_is_omap3_rev(struct ti_sdma_softc *sc)
+{
+ return (sc->sc_hw_rev == DMA4_OMAP3_REV);
+}
+
+/**
+ * ti_sdma_is_omap4_rev - returns true if H/W is from OMAP4 series
+ * @sc: DMA device context
+ *
+ */
+static inline int
+ti_sdma_is_omap4_rev(struct ti_sdma_softc *sc)
+{
+ return (sc->sc_hw_rev == DMA4_OMAP4_REV);
+}
+
+/**
+ * ti_sdma_intr - interrupt handler for all 4 DMA IRQs
+ * @arg: ignored
+ *
+ * Called when any of the four DMA IRQs are triggered.
+ *
+ * LOCKING:
+ * DMA registers protected by internal mutex
+ *
+ * RETURNS:
+ * nothing
+ */
+static void
+ti_sdma_intr(void *arg)
+{
+ struct ti_sdma_softc *sc = ti_sdma_sc;
+ uint32_t intr;
+ uint32_t csr;
+ unsigned int ch, j;
+ struct ti_sdma_channel* channel;
+
+ TI_SDMA_LOCK(sc);
+
+ for (j = 0; j < NUM_DMA_IRQS; j++) {
+ /* Get the flag interrupts (enabled) */
+ intr = ti_sdma_read_4(sc, DMA4_IRQSTATUS_L(j));
+ intr &= ti_sdma_read_4(sc, DMA4_IRQENABLE_L(j));
+ if (intr == 0x00000000)
+ continue;
+
+ /* Loop through checking the status bits */
+ for (ch = 0; ch < NUM_DMA_CHANNELS; ch++) {
+ if (intr & (1 << ch)) {
+ channel = &sc->sc_channel[ch];
+
+ /* Read the CSR regsiter and verify we don't have a spurious IRQ */
+ csr = ti_sdma_read_4(sc, DMA4_CSR(ch));
+ if (csr == 0) {
+ device_printf(sc->sc_dev, "Spurious DMA IRQ for channel "
+ "%d\n", ch);
+ continue;
+ }
+
+ /* Sanity check this channel is active */
+ if ((sc->sc_active_channels & (1 << ch)) == 0) {
+ device_printf(sc->sc_dev, "IRQ %d for a non-activated "
+ "channel %d\n", j, ch);
+ continue;
+ }
+
+ /* Check the status error codes */
+ if (csr & DMA4_CSR_DROP)
+ device_printf(sc->sc_dev, "Synchronization event drop "
+ "occurred during the transfer on channel %u\n",
+ ch);
+ if (csr & DMA4_CSR_SECURE_ERR)
+ device_printf(sc->sc_dev, "Secure transaction error event "
+ "on channel %u\n", ch);
+ if (csr & DMA4_CSR_MISALIGNED_ADRS_ERR)
+ device_printf(sc->sc_dev, "Misaligned address error event "
+ "on channel %u\n", ch);
+ if (csr & DMA4_CSR_TRANS_ERR) {
+ device_printf(sc->sc_dev, "Transaction error event on "
+ "channel %u\n", ch);
+ /*
+ * Apparently according to linux code, there is an errata
+ * that says the channel is not disabled upon this error.
+ * They explicitly disable the channel here .. since I
+ * haven't seen the errata, I'm going to ignore for now.
+ */
+ }
+
+ /* Clear the status flags for the IRQ */
+ ti_sdma_write_4(sc, DMA4_CSR(ch), DMA4_CSR_CLEAR_MASK);
+ ti_sdma_write_4(sc, DMA4_IRQSTATUS_L(j), (1 << ch));
+
+ /* Call the callback for the given channel */
+ if (channel->callback)
+ channel->callback(ch, csr, channel->callback_data);
+ }
+ }
+ }
+
+ TI_SDMA_UNLOCK(sc);
+
+ return;
+}
+
+/**
+ * ti_sdma_activate_channel - activates a DMA channel
+ * @ch: upon return contains the channel allocated
+ * @callback: a callback function to associate with the channel
+ * @data: optional data supplied when the callback is called
+ *
+ * Simply activates a channel be enabling and writing default values to the
+ * channel's register set. It doesn't start a transaction, just populates the
+ * internal data structures and sets defaults.
+ *
+ * Note this function doesn't enable interrupts, for that you need to call
+ * ti_sdma_enable_channel_irq(). If not using IRQ to detect the end of the
+ * transfer, you can use ti_sdma_status_poll() to detect a change in the
+ * status.
+ *
+ * A channel must be activated before any of the other DMA functions can be
+ * called on it.
+ *
+ * LOCKING:
+ * DMA registers protected by internal mutex
+ *
+ * RETURNS:
+ * 0 on success, otherwise an error code
+ */
+int
+ti_sdma_activate_channel(unsigned int *ch,
+ void (*callback)(unsigned int ch, uint32_t status, void *data),
+ void *data)
+{
+ struct ti_sdma_softc *sc = ti_sdma_sc;
+ struct ti_sdma_channel *channel = NULL;
+ uint32_t addr;
+ unsigned int i;
+
+ /* Sanity check */
+ if (sc == NULL)
+ return (ENOMEM);
+
+ if (ch == NULL)
+ return (EINVAL);
+
+ TI_SDMA_LOCK(sc);
+
+ /* Check to see if all channels are in use */
+ if (sc->sc_active_channels == 0xffffffff) {
+ TI_SDMA_UNLOCK(sc);
+ return (ENOMEM);
+ }
+
+ /* Find the first non-active channel */
+ for (i = 0; i < NUM_DMA_CHANNELS; i++) {
+ if (!(sc->sc_active_channels & (0x1 << i))) {
+ sc->sc_active_channels |= (0x1 << i);
+ *ch = i;
+ break;
+ }
+ }
+
+ /* Get the channel struct and populate the fields */
+ channel = &sc->sc_channel[*ch];
+
+ channel->callback = callback;
+ channel->callback_data = data;
+
+ channel->need_reg_write = 1;
+
+ /* Set the default configuration for the DMA channel */
+ channel->reg_csdp = DMA4_CSDP_DATA_TYPE(0x2)
+ | DMA4_CSDP_SRC_BURST_MODE(0)
+ | DMA4_CSDP_DST_BURST_MODE(0)
+ | DMA4_CSDP_SRC_ENDIANISM(0)
+ | DMA4_CSDP_DST_ENDIANISM(0)
+ | DMA4_CSDP_WRITE_MODE(0)
+ | DMA4_CSDP_SRC_PACKED(0)
+ | DMA4_CSDP_DST_PACKED(0);
+
+ channel->reg_ccr = DMA4_CCR_DST_ADDRESS_MODE(1)
+ | DMA4_CCR_SRC_ADDRESS_MODE(1)
+ | DMA4_CCR_READ_PRIORITY(0)
+ | DMA4_CCR_WRITE_PRIORITY(0)
+ | DMA4_CCR_SYNC_TRIGGER(0)
+ | DMA4_CCR_FRAME_SYNC(0)
+ | DMA4_CCR_BLOCK_SYNC(0);
+
+ channel->reg_cicr = DMA4_CICR_TRANS_ERR_IE
+ | DMA4_CICR_SECURE_ERR_IE
+ | DMA4_CICR_SUPERVISOR_ERR_IE
+ | DMA4_CICR_MISALIGNED_ADRS_ERR_IE;
+
+ /* Clear all the channel registers, this should abort any transaction */
+ for (addr = DMA4_CCR(*ch); addr <= DMA4_COLOR(*ch); addr += 4)
+ ti_sdma_write_4(sc, addr, 0x00000000);
+
+ TI_SDMA_UNLOCK(sc);
+
+ return 0;
+}
+
+/**
+ * ti_sdma_deactivate_channel - deactivates a channel
+ * @ch: the channel to deactivate
+ *
+ *
+ *
+ * LOCKING:
+ * DMA registers protected by internal mutex
+ *
+ * RETURNS:
+ * EH_HANDLED or EH_NOT_HANDLED
+ */
+int
+ti_sdma_deactivate_channel(unsigned int ch)
+{
+ struct ti_sdma_softc *sc = ti_sdma_sc;
+ unsigned int j;
+ unsigned int addr;
+
+ /* Sanity check */
+ if (sc == NULL)
+ return (ENOMEM);
+
+ TI_SDMA_LOCK(sc);
+
+ /* First check if the channel is currently active */
+ if ((sc->sc_active_channels & (1 << ch)) == 0) {
+ TI_SDMA_UNLOCK(sc);
+ return (EBUSY);
+ }
+
+ /* Mark the channel as inactive */
+ sc->sc_active_channels &= ~(1 << ch);
+
+ /* Disable all DMA interrupts for the channel. */
+ ti_sdma_write_4(sc, DMA4_CICR(ch), 0);
+
+ /* Make sure the DMA transfer is stopped. */
+ ti_sdma_write_4(sc, DMA4_CCR(ch), 0);
+
+ /* Clear the CSR register and IRQ status register */
+ ti_sdma_write_4(sc, DMA4_CSR(ch), DMA4_CSR_CLEAR_MASK);
+ for (j = 0; j < NUM_DMA_IRQS; j++) {
+ ti_sdma_write_4(sc, DMA4_IRQSTATUS_L(j), (1 << ch));
+ }
+
+ /* Clear all the channel registers, this should abort any transaction */
+ for (addr = DMA4_CCR(ch); addr <= DMA4_COLOR(ch); addr += 4)
+ ti_sdma_write_4(sc, addr, 0x00000000);
+
+ TI_SDMA_UNLOCK(sc);
+
+ return 0;
+}
+
+/**
+ * ti_sdma_disable_channel_irq - disables IRQ's on the given channel
+ * @ch: the channel to disable IRQ's on
+ *
+ * Disable interrupt generation for the given channel.
+ *
+ * LOCKING:
+ * DMA registers protected by internal mutex
+ *
+ * RETURNS:
+ * EH_HANDLED or EH_NOT_HANDLED
+ */
+int
+ti_sdma_disable_channel_irq(unsigned int ch)
+{
+ struct ti_sdma_softc *sc = ti_sdma_sc;
+ uint32_t irq_enable;
+ unsigned int j;
+
+ /* Sanity check */
+ if (sc == NULL)
+ return (ENOMEM);
+
+ TI_SDMA_LOCK(sc);
+
+ if ((sc->sc_active_channels & (1 << ch)) == 0) {
+ TI_SDMA_UNLOCK(sc);
+ return (EINVAL);
+ }
+
+ /* Disable all the individual error conditions */
+ sc->sc_channel[ch].reg_cicr = 0x0000;
+ ti_sdma_write_4(sc, DMA4_CICR(ch), 0x0000);
+
+ /* Disable the channel interrupt enable */
+ for (j = 0; j < NUM_DMA_IRQS; j++) {
+ irq_enable = ti_sdma_read_4(sc, DMA4_IRQENABLE_L(j));
+ irq_enable &= ~(1 << ch);
+
+ ti_sdma_write_4(sc, DMA4_IRQENABLE_L(j), irq_enable);
+ }
+
+ /* Indicate the registers need to be rewritten on the next transaction */
+ sc->sc_channel[ch].need_reg_write = 1;
+
+ TI_SDMA_UNLOCK(sc);
+
+ return (0);
+}
+
+/**
+ * ti_sdma_disable_channel_irq - enables IRQ's on the given channel
+ * @ch: the channel to enable IRQ's on
+ * @flags: bitmask of interrupt types to enable
+ *
+ * Flags can be a bitmask of the following options:
+ * DMA_IRQ_FLAG_DROP
+ * DMA_IRQ_FLAG_HALF_FRAME_COMPL
+ * DMA_IRQ_FLAG_FRAME_COMPL
+ * DMA_IRQ_FLAG_START_LAST_FRAME
+ * DMA_IRQ_FLAG_BLOCK_COMPL
+ * DMA_IRQ_FLAG_ENDOF_PKT
+ * DMA_IRQ_FLAG_DRAIN
+ *
+ *
+ * LOCKING:
+ * DMA registers protected by internal mutex
+ *
+ * RETURNS:
+ * EH_HANDLED or EH_NOT_HANDLED
+ */
+int
+ti_sdma_enable_channel_irq(unsigned int ch, uint32_t flags)
+{
+ struct ti_sdma_softc *sc = ti_sdma_sc;
+ uint32_t irq_enable;
+
+ /* Sanity check */
+ if (sc == NULL)
+ return (ENOMEM);
+
+ TI_SDMA_LOCK(sc);
+
+ if ((sc->sc_active_channels & (1 << ch)) == 0) {
+ TI_SDMA_UNLOCK(sc);
+ return (EINVAL);
+ }
+
+ /* Always enable the error interrupts if we have interrupts enabled */
+ flags |= DMA4_CICR_TRANS_ERR_IE | DMA4_CICR_SECURE_ERR_IE |
+ DMA4_CICR_SUPERVISOR_ERR_IE | DMA4_CICR_MISALIGNED_ADRS_ERR_IE;
+
+ sc->sc_channel[ch].reg_cicr = flags;
+
+ /* Write the values to the register */
+ ti_sdma_write_4(sc, DMA4_CICR(ch), flags);
+
+ /* Enable the channel interrupt enable */
+ irq_enable = ti_sdma_read_4(sc, DMA4_IRQENABLE_L(0));
+ irq_enable |= (1 << ch);
+
+ ti_sdma_write_4(sc, DMA4_IRQENABLE_L(0), irq_enable);
+
+ /* Indicate the registers need to be rewritten on the next transaction */
+ sc->sc_channel[ch].need_reg_write = 1;
+
+ TI_SDMA_UNLOCK(sc);
+
+ return (0);
+}
+
+/**
+ * ti_sdma_get_channel_status - returns the status of a given channel
+ * @ch: the channel number to get the status of
+ * @status: upon return will contain the status bitmask, see below for possible
+ * values.
+ *
+ * DMA_STATUS_DROP
+ * DMA_STATUS_HALF
+ * DMA_STATUS_FRAME
+ * DMA_STATUS_LAST
+ * DMA_STATUS_BLOCK
+ * DMA_STATUS_SYNC
+ * DMA_STATUS_PKT
+ * DMA_STATUS_TRANS_ERR
+ * DMA_STATUS_SECURE_ERR
+ * DMA_STATUS_SUPERVISOR_ERR
+ * DMA_STATUS_MISALIGNED_ADRS_ERR
+ * DMA_STATUS_DRAIN_END
+ *
+ *
+ * LOCKING:
+ * DMA registers protected by internal mutex
+ *
+ * RETURNS:
+ * EH_HANDLED or EH_NOT_HANDLED
+ */
+int
+ti_sdma_get_channel_status(unsigned int ch, uint32_t *status)
+{
+ struct ti_sdma_softc *sc = ti_sdma_sc;
+ uint32_t csr;
+
+ /* Sanity check */
+ if (sc == NULL)
+ return (ENOMEM);
+
+ TI_SDMA_LOCK(sc);
+
+ if ((sc->sc_active_channels & (1 << ch)) == 0) {
+ TI_SDMA_UNLOCK(sc);
+ return (EINVAL);
+ }
+
+ TI_SDMA_UNLOCK(sc);
+
+ csr = ti_sdma_read_4(sc, DMA4_CSR(ch));
+
+ if (status != NULL)
+ *status = csr;
+
+ return (0);
+}
+
+/**
+ * ti_sdma_start_xfer - starts a DMA transfer
+ * @ch: the channel number to set the endianness of
+ * @src_paddr: the source phsyical address
+ * @dst_paddr: the destination phsyical address
+ * @frmcnt: the number of frames per block
+ * @elmcnt: the number of elements in a frame, an element is either an 8, 16
+ * or 32-bit value as defined by ti_sdma_set_xfer_burst()
+ *
+ *
+ * LOCKING:
+ * DMA registers protected by internal mutex
+ *
+ * RETURNS:
+ * EH_HANDLED or EH_NOT_HANDLED
+ */
+int
+ti_sdma_start_xfer(unsigned int ch, unsigned int src_paddr,
+ unsigned long dst_paddr,
+ unsigned int frmcnt, unsigned int elmcnt)
+{
+ struct ti_sdma_softc *sc = ti_sdma_sc;
+ struct ti_sdma_channel *channel;
+ uint32_t ccr;
+
+ /* Sanity check */
+ if (sc == NULL)
+ return (ENOMEM);
+
+ TI_SDMA_LOCK(sc);
+
+ if ((sc->sc_active_channels & (1 << ch)) == 0) {
+ TI_SDMA_UNLOCK(sc);
+ return (EINVAL);
+ }
+
+ channel = &sc->sc_channel[ch];
+
+ /* a) Write the CSDP register */
+ ti_sdma_write_4(sc, DMA4_CSDP(ch),
+ channel->reg_csdp | DMA4_CSDP_WRITE_MODE(1));
+
+ /* b) Set the number of element per frame CEN[23:0] */
+ ti_sdma_write_4(sc, DMA4_CEN(ch), elmcnt);
+
+ /* c) Set the number of frame per block CFN[15:0] */
+ ti_sdma_write_4(sc, DMA4_CFN(ch), frmcnt);
+
+ /* d) Set the Source/dest start address index CSSA[31:0]/CDSA[31:0] */
+ ti_sdma_write_4(sc, DMA4_CSSA(ch), src_paddr);
+ ti_sdma_write_4(sc, DMA4_CDSA(ch), dst_paddr);
+
+ /* e) Write the CCR register */
+ ti_sdma_write_4(sc, DMA4_CCR(ch), channel->reg_ccr);
+
+ /* f) - Set the source element index increment CSEI[15:0] */
+ ti_sdma_write_4(sc, DMA4_CSE(ch), 0x0001);
+
+ /* - Set the source frame index increment CSFI[15:0] */
+ ti_sdma_write_4(sc, DMA4_CSF(ch), 0x0001);
+
+ /* - Set the destination element index increment CDEI[15:0]*/
+ ti_sdma_write_4(sc, DMA4_CDE(ch), 0x0001);
+
+ /* - Set the destination frame index increment CDFI[31:0] */
+ ti_sdma_write_4(sc, DMA4_CDF(ch), 0x0001);
+
+ /* Clear the status register */
+ ti_sdma_write_4(sc, DMA4_CSR(ch), 0x1FFE);
+
+ /* Write the start-bit and away we go */
+ ccr = ti_sdma_read_4(sc, DMA4_CCR(ch));
+ ccr |= (1 << 7);
+ ti_sdma_write_4(sc, DMA4_CCR(ch), ccr);
+
+ /* Clear the reg write flag */
+ channel->need_reg_write = 0;
+
+ TI_SDMA_UNLOCK(sc);
+
+ return (0);
+}
+
+/**
+ * ti_sdma_start_xfer_packet - starts a packet DMA transfer
+ * @ch: the channel number to use for the transfer
+ * @src_paddr: the source physical address
+ * @dst_paddr: the destination physical address
+ * @frmcnt: the number of frames to transfer
+ * @elmcnt: the number of elements in a frame, an element is either an 8, 16
+ * or 32-bit value as defined by ti_sdma_set_xfer_burst()
+ * @pktsize: the number of elements in each transfer packet
+ *
+ * The @frmcnt and @elmcnt define the overall number of bytes to transfer,
+ * typically @frmcnt is 1 and @elmcnt contains the total number of elements.
+ * @pktsize is the size of each individual packet, there might be multiple
+ * packets per transfer. i.e. for the following with element size of 32-bits
+ *
+ * frmcnt = 1, elmcnt = 512, pktsize = 128
+ *
+ * Total transfer bytes = 1 * 512 = 512 elements or 2048 bytes
+ * Packets transferred = 128 / 512 = 4
+ *
+ *
+ * LOCKING:
+ * DMA registers protected by internal mutex
+ *
+ * RETURNS:
+ * EH_HANDLED or EH_NOT_HANDLED
+ */
+int
+ti_sdma_start_xfer_packet(unsigned int ch, unsigned int src_paddr,
+ unsigned long dst_paddr, unsigned int frmcnt,
+ unsigned int elmcnt, unsigned int pktsize)
+{
+ struct ti_sdma_softc *sc = ti_sdma_sc;
+ struct ti_sdma_channel *channel;
+ uint32_t ccr;
+
+ /* Sanity check */
+ if (sc == NULL)
+ return (ENOMEM);
+
+ TI_SDMA_LOCK(sc);
+
+ if ((sc->sc_active_channels & (1 << ch)) == 0) {
+ TI_SDMA_UNLOCK(sc);
+ return (EINVAL);
+ }
+
+ channel = &sc->sc_channel[ch];
+
+ /* a) Write the CSDP register */
+ if (channel->need_reg_write)
+ ti_sdma_write_4(sc, DMA4_CSDP(ch),
+ channel->reg_csdp | DMA4_CSDP_WRITE_MODE(1));
+
+ /* b) Set the number of elements to transfer CEN[23:0] */
+ ti_sdma_write_4(sc, DMA4_CEN(ch), elmcnt);
+
+ /* c) Set the number of frames to transfer CFN[15:0] */
+ ti_sdma_write_4(sc, DMA4_CFN(ch), frmcnt);
+
+ /* d) Set the Source/dest start address index CSSA[31:0]/CDSA[31:0] */
+ ti_sdma_write_4(sc, DMA4_CSSA(ch), src_paddr);
+ ti_sdma_write_4(sc, DMA4_CDSA(ch), dst_paddr);
+
+ /* e) Write the CCR register */
+ ti_sdma_write_4(sc, DMA4_CCR(ch),
+ channel->reg_ccr | DMA4_CCR_PACKET_TRANS);
+
+ /* f) - Set the source element index increment CSEI[15:0] */
+ ti_sdma_write_4(sc, DMA4_CSE(ch), 0x0001);
+
+ /* - Set the packet size, this is dependent on the sync source */
+ if (channel->reg_ccr & DMA4_CCR_SEL_SRC_DST_SYNC(1))
+ ti_sdma_write_4(sc, DMA4_CSF(ch), pktsize);
+ else
+ ti_sdma_write_4(sc, DMA4_CDF(ch), pktsize);
+
+ /* - Set the destination frame index increment CDFI[31:0] */
+ ti_sdma_write_4(sc, DMA4_CDE(ch), 0x0001);
+
+ /* Clear the status register */
+ ti_sdma_write_4(sc, DMA4_CSR(ch), 0x1FFE);
+
+ /* Write the start-bit and away we go */
+ ccr = ti_sdma_read_4(sc, DMA4_CCR(ch));
+ ccr |= (1 << 7);
+ ti_sdma_write_4(sc, DMA4_CCR(ch), ccr);
+
+ /* Clear the reg write flag */
+ channel->need_reg_write = 0;
+
+ TI_SDMA_UNLOCK(sc);
+
+ return (0);
+}
+
+/**
+ * ti_sdma_stop_xfer - stops any currently active transfers
+ * @ch: the channel number to set the endianness of
+ *
+ * This function call is effectively a NOP if no transaction is in progress.
+ *
+ * LOCKING:
+ * DMA registers protected by internal mutex
+ *
+ * RETURNS:
+ * EH_HANDLED or EH_NOT_HANDLED
+ */
+int
+ti_sdma_stop_xfer(unsigned int ch)
+{
+ struct ti_sdma_softc *sc = ti_sdma_sc;
+ unsigned int j;
+
+ /* Sanity check */
+ if (sc == NULL)
+ return (ENOMEM);
+
+ TI_SDMA_LOCK(sc);
+
+ if ((sc->sc_active_channels & (1 << ch)) == 0) {
+ TI_SDMA_UNLOCK(sc);
+ return (EINVAL);
+ }
+
+ /* Disable all DMA interrupts for the channel. */
+ ti_sdma_write_4(sc, DMA4_CICR(ch), 0);
+
+ /* Make sure the DMA transfer is stopped. */
+ ti_sdma_write_4(sc, DMA4_CCR(ch), 0);
+
+ /* Clear the CSR register and IRQ status register */
+ ti_sdma_write_4(sc, DMA4_CSR(ch), DMA4_CSR_CLEAR_MASK);
+ for (j = 0; j < NUM_DMA_IRQS; j++) {
+ ti_sdma_write_4(sc, DMA4_IRQSTATUS_L(j), (1 << ch));
+ }
+
+ /* Configuration registers need to be re-written on the next xfer */
+ sc->sc_channel[ch].need_reg_write = 1;
+
+ TI_SDMA_UNLOCK(sc);
+
+ return (0);
+}
+
+/**
+ * ti_sdma_set_xfer_endianess - sets the endianness of subsequent transfers
+ * @ch: the channel number to set the endianness of
+ * @src: the source endianness (either DMA_ENDIAN_LITTLE or DMA_ENDIAN_BIG)
+ * @dst: the destination endianness (either DMA_ENDIAN_LITTLE or DMA_ENDIAN_BIG)
+ *
+ *
+ * LOCKING:
+ * DMA registers protected by internal mutex
+ *
+ * RETURNS:
+ * EH_HANDLED or EH_NOT_HANDLED
+ */
+int
+ti_sdma_set_xfer_endianess(unsigned int ch, unsigned int src, unsigned int dst)
+{
+ struct ti_sdma_softc *sc = ti_sdma_sc;
+
+ /* Sanity check */
+ if (sc == NULL)
+ return (ENOMEM);
+
+ TI_SDMA_LOCK(sc);
+
+ if ((sc->sc_active_channels & (1 << ch)) == 0) {
+ TI_SDMA_UNLOCK(sc);
+ return (EINVAL);
+ }
+
+ sc->sc_channel[ch].reg_csdp &= ~DMA4_CSDP_SRC_ENDIANISM(1);
+ sc->sc_channel[ch].reg_csdp |= DMA4_CSDP_SRC_ENDIANISM(src);
+
+ sc->sc_channel[ch].reg_csdp &= ~DMA4_CSDP_DST_ENDIANISM(1);
+ sc->sc_channel[ch].reg_csdp |= DMA4_CSDP_DST_ENDIANISM(dst);
+
+ sc->sc_channel[ch].need_reg_write = 1;
+
+ TI_SDMA_UNLOCK(sc);
+
+ return 0;
+}
+
+/**
+ * ti_sdma_set_xfer_burst - sets the source and destination element size
+ * @ch: the channel number to set the burst settings of
+ * @src: the source endianness (either DMA_BURST_NONE, DMA_BURST_16, DMA_BURST_32
+ * or DMA_BURST_64)
+ * @dst: the destination endianness (either DMA_BURST_NONE, DMA_BURST_16,
+ * DMA_BURST_32 or DMA_BURST_64)
+ *
+ * This function sets the size of the elements for all subsequent transfers.
+ *
+ * LOCKING:
+ * DMA registers protected by internal mutex
+ *
+ * RETURNS:
+ * EH_HANDLED or EH_NOT_HANDLED
+ */
+int
+ti_sdma_set_xfer_burst(unsigned int ch, unsigned int src, unsigned int dst)
+{
+ struct ti_sdma_softc *sc = ti_sdma_sc;
+
+ /* Sanity check */
+ if (sc == NULL)
+ return (ENOMEM);
+
+ TI_SDMA_LOCK(sc);
+
+ if ((sc->sc_active_channels & (1 << ch)) == 0) {
+ TI_SDMA_UNLOCK(sc);
+ return (EINVAL);
+ }
+
+ sc->sc_channel[ch].reg_csdp &= ~DMA4_CSDP_SRC_BURST_MODE(0x3);
+ sc->sc_channel[ch].reg_csdp |= DMA4_CSDP_SRC_BURST_MODE(src);
+
+ sc->sc_channel[ch].reg_csdp &= ~DMA4_CSDP_DST_BURST_MODE(0x3);
+ sc->sc_channel[ch].reg_csdp |= DMA4_CSDP_DST_BURST_MODE(dst);
+
+ sc->sc_channel[ch].need_reg_write = 1;
+
+ TI_SDMA_UNLOCK(sc);
+
+ return 0;
+}
+
+/**
+ * ti_sdma_set_xfer_data_type - driver attach function
+ * @ch: the channel number to set the endianness of
+ * @type: the xfer data type (either DMA_DATA_8BITS_SCALAR, DMA_DATA_16BITS_SCALAR
+ * or DMA_DATA_32BITS_SCALAR)
+ *
+ *
+ * LOCKING:
+ * DMA registers protected by internal mutex
+ *
+ * RETURNS:
+ * EH_HANDLED or EH_NOT_HANDLED
+ */
+int
+ti_sdma_set_xfer_data_type(unsigned int ch, unsigned int type)
+{
+ struct ti_sdma_softc *sc = ti_sdma_sc;
+
+ /* Sanity check */
+ if (sc == NULL)
+ return (ENOMEM);
+
+ TI_SDMA_LOCK(sc);
+
+ if ((sc->sc_active_channels & (1 << ch)) == 0) {
+ TI_SDMA_UNLOCK(sc);
+ return (EINVAL);
+ }
+
+ sc->sc_channel[ch].reg_csdp &= ~DMA4_CSDP_DATA_TYPE(0x3);
+ sc->sc_channel[ch].reg_csdp |= DMA4_CSDP_DATA_TYPE(type);
+
+ sc->sc_channel[ch].need_reg_write = 1;
+
+ TI_SDMA_UNLOCK(sc);
+
+ return 0;
+}
+
+/**
+ * ti_sdma_set_callback - driver attach function
+ * @dev: dma device handle
+ *
+ *
+ *
+ * LOCKING:
+ * DMA registers protected by internal mutex
+ *
+ * RETURNS:
+ * EH_HANDLED or EH_NOT_HANDLED
+ */
+int
+ti_sdma_set_callback(unsigned int ch,
+ void (*callback)(unsigned int ch, uint32_t status, void *data),
+ void *data)
+{
+ struct ti_sdma_softc *sc = ti_sdma_sc;
+
+ /* Sanity check */
+ if (sc == NULL)
+ return (ENOMEM);
+
+ TI_SDMA_LOCK(sc);
+
+ if ((sc->sc_active_channels & (1 << ch)) == 0) {
+ TI_SDMA_UNLOCK(sc);
+ return (EINVAL);
+ }
+
+ sc->sc_channel[ch].callback = callback;
+ sc->sc_channel[ch].callback_data = data;
+
+ sc->sc_channel[ch].need_reg_write = 1;
+
+ TI_SDMA_UNLOCK(sc);
+
+ return 0;
+}
+
+/**
+ * ti_sdma_sync_params - sets channel sync settings
+ * @ch: the channel number to set the sync on
+ * @trigger: the number of the sync trigger, this depends on what other H/W
+ * module is triggering/receiving the DMA transactions
+ * @mode: flags describing the sync mode to use, it may have one or more of
+ * the following bits set; TI_SDMA_SYNC_FRAME,
+ * TI_SDMA_SYNC_BLOCK, TI_SDMA_SYNC_TRIG_ON_SRC.
+ *
+ *
+ *
+ * LOCKING:
+ * DMA registers protected by internal mutex
+ *
+ * RETURNS:
+ * EH_HANDLED or EH_NOT_HANDLED
+ */
+int
+ti_sdma_sync_params(unsigned int ch, unsigned int trigger, unsigned int mode)
+{
+ struct ti_sdma_softc *sc = ti_sdma_sc;
+ uint32_t ccr;
+
+ /* Sanity check */
+ if (sc == NULL)
+ return (ENOMEM);
+
+ TI_SDMA_LOCK(sc);
+
+ if ((sc->sc_active_channels & (1 << ch)) == 0) {
+ TI_SDMA_UNLOCK(sc);
+ return (EINVAL);
+ }
+
+ ccr = sc->sc_channel[ch].reg_ccr;
+
+ ccr &= ~DMA4_CCR_SYNC_TRIGGER(0x7F);
+ ccr |= DMA4_CCR_SYNC_TRIGGER(trigger + 1);
+
+ if (mode & TI_SDMA_SYNC_FRAME)
+ ccr |= DMA4_CCR_FRAME_SYNC(1);
+ else
+ ccr &= ~DMA4_CCR_FRAME_SYNC(1);
+
+ if (mode & TI_SDMA_SYNC_BLOCK)
+ ccr |= DMA4_CCR_BLOCK_SYNC(1);
+ else
+ ccr &= ~DMA4_CCR_BLOCK_SYNC(1);
+
+ if (mode & TI_SDMA_SYNC_TRIG_ON_SRC)
+ ccr |= DMA4_CCR_SEL_SRC_DST_SYNC(1);
+ else
+ ccr &= ~DMA4_CCR_SEL_SRC_DST_SYNC(1);
+
+ sc->sc_channel[ch].reg_ccr = ccr;
+
+ sc->sc_channel[ch].need_reg_write = 1;
+
+ TI_SDMA_UNLOCK(sc);
+
+ return 0;
+}
+
+/**
+ * ti_sdma_set_addr_mode - driver attach function
+ * @ch: the channel number to set the endianness of
+ * @rd_mode: the xfer source addressing mode (either DMA_ADDR_CONSTANT,
+ * DMA_ADDR_POST_INCREMENT, DMA_ADDR_SINGLE_INDEX or
+ * DMA_ADDR_DOUBLE_INDEX)
+ * @wr_mode: the xfer destination addressing mode (either DMA_ADDR_CONSTANT,
+ * DMA_ADDR_POST_INCREMENT, DMA_ADDR_SINGLE_INDEX or
+ * DMA_ADDR_DOUBLE_INDEX)
+ *
+ *
+ * LOCKING:
+ * DMA registers protected by internal mutex
+ *
+ * RETURNS:
+ * EH_HANDLED or EH_NOT_HANDLED
+ */
+int
+ti_sdma_set_addr_mode(unsigned int ch, unsigned int src_mode,
+ unsigned int dst_mode)
+{
+ struct ti_sdma_softc *sc = ti_sdma_sc;
+ uint32_t ccr;
+
+ /* Sanity check */
+ if (sc == NULL)
+ return (ENOMEM);
+
+ TI_SDMA_LOCK(sc);
+
+ if ((sc->sc_active_channels & (1 << ch)) == 0) {
+ TI_SDMA_UNLOCK(sc);
+ return (EINVAL);
+ }
+
+ ccr = sc->sc_channel[ch].reg_ccr;
+
+ ccr &= ~DMA4_CCR_SRC_ADDRESS_MODE(0x3);
+ ccr |= DMA4_CCR_SRC_ADDRESS_MODE(src_mode);
+
+ ccr &= ~DMA4_CCR_DST_ADDRESS_MODE(0x3);
+ ccr |= DMA4_CCR_DST_ADDRESS_MODE(dst_mode);
+
+ sc->sc_channel[ch].reg_ccr = ccr;
+
+ sc->sc_channel[ch].need_reg_write = 1;
+
+ TI_SDMA_UNLOCK(sc);
+
+ return 0;
+}
+
+/**
+ * ti_sdma_probe - driver probe function
+ * @dev: dma device handle
+ *
+ *
+ *
+ * RETURNS:
+ * Always returns 0.
+ */
+static int
+ti_sdma_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "ti,omap4430-sdma"))
+ return (ENXIO);
+
+ device_set_desc(dev, "TI sDMA Controller");
+ return (0);
+}
+
+/**
+ * ti_sdma_attach - driver attach function
+ * @dev: dma device handle
+ *
+ * Initialises memory mapping/pointers to the DMA register set and requests
+ * IRQs. This is effectively the setup function for the driver.
+ *
+ * RETURNS:
+ * 0 on success or a negative error code failure.
+ */
+static int
+ti_sdma_attach(device_t dev)
+{
+ struct ti_sdma_softc *sc = device_get_softc(dev);
+ unsigned int timeout;
+ unsigned int i;
+ int rid;
+ void *ihl;
+ int err;
+
+ /* Setup the basics */
+ sc->sc_dev = dev;
+
+ /* No channels active at the moment */
+ sc->sc_active_channels = 0x00000000;
+
+ /* Mutex to protect the shared data structures */
+ TI_SDMA_LOCK_INIT(sc);
+
+ /* Get the memory resource for the register mapping */
+ rid = 0;
+ sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
+ if (sc->sc_mem_res == NULL)
+ panic("%s: Cannot map registers", device_get_name(dev));
+
+ /* Enable the interface and functional clocks */
+ ti_sysc_clock_enable(device_get_parent(dev));
+
+ /* Read the sDMA revision register and sanity check it's known */
+ sc->sc_hw_rev = ti_sdma_read_4(sc,
+ ti_sysc_get_rev_address_offset_host(device_get_parent(dev)));
+ device_printf(dev, "sDMA revision %08x\n", sc->sc_hw_rev);
+
+ if (!ti_sdma_is_omap4_rev(sc) && !ti_sdma_is_omap3_rev(sc)) {
+ device_printf(sc->sc_dev, "error - unknown sDMA H/W revision\n");
+ return (EINVAL);
+ }
+
+ /* Disable all interrupts */
+ for (i = 0; i < NUM_DMA_IRQS; i++) {
+ ti_sdma_write_4(sc, DMA4_IRQENABLE_L(i), 0x00000000);
+ }
+
+ /* Soft-reset is only supported on pre-OMAP44xx devices */
+ if (ti_sdma_is_omap3_rev(sc)) {
+ /* Soft-reset */
+ ti_sdma_write_4(sc, DMA4_OCP_SYSCONFIG, 0x0002);
+
+ /* Set the timeout to 100ms*/
+ timeout = (hz < 10) ? 1 : ((100 * hz) / 1000);
+
+ /* Wait for DMA reset to complete */
+ while ((ti_sdma_read_4(sc, DMA4_SYSSTATUS) & 0x1) == 0x0) {
+ /* Sleep for a tick */
+ pause("DMARESET", 1);
+
+ if (timeout-- == 0) {
+ device_printf(sc->sc_dev, "sDMA reset operation timed out\n");
+ return (EINVAL);
+ }
+ }
+ }
+
+ /*
+ * Install interrupt handlers for the for possible interrupts. Any channel
+ * can trip one of the four IRQs
+ */
+ rid = 0;
+ sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE | RF_SHAREABLE);
+ if (sc->sc_irq_res == NULL)
+ panic("Unable to setup the dma irq handler.\n");
+
+ err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+ NULL, ti_sdma_intr, NULL, &ihl);
+ if (err)
+ panic("%s: Cannot register IRQ", device_get_name(dev));
+
+ /* Store the DMA structure globally ... this driver should never be unloaded */
+ ti_sdma_sc = sc;
+
+ return (0);
+}
+
+static device_method_t ti_sdma_methods[] = {
+ DEVMETHOD(device_probe, ti_sdma_probe),
+ DEVMETHOD(device_attach, ti_sdma_attach),
+ {0, 0},
+};
+
+static driver_t ti_sdma_driver = {
+ "ti_sdma",
+ ti_sdma_methods,
+ sizeof(struct ti_sdma_softc),
+};
+static devclass_t ti_sdma_devclass;
+
+DRIVER_MODULE(ti_sdma, simplebus, ti_sdma_driver, ti_sdma_devclass, 0, 0);
+MODULE_DEPEND(ti_sdma, ti_sysc, 1, 1, 1);
diff --git a/sys/arm/ti/ti_sdma.h b/sys/arm/ti/ti_sdma.h
new file mode 100644
index 000000000000..5b96766b2fb1
--- /dev/null
+++ b/sys/arm/ti/ti_sdma.h
@@ -0,0 +1,113 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2011
+ * Ben Gray <ben.r.gray@gmail.com>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/**
+ * sDMA device driver interface for the TI SoC
+ *
+ * See the ti_sdma.c file for implementation details.
+ *
+ * Reference:
+ * OMAP35x Applications Processor
+ * Technical Reference Manual
+ * (omap35xx_techref.pdf)
+ */
+#ifndef _TI_DMA_H_
+#define _TI_DMA_H_
+
+#define TI_SDMA_ENDIAN_BIG 0x1
+#define TI_SDMA_ENDIAN_LITTLE 0x0
+
+#define TI_SDMA_BURST_NONE 0x0
+#define TI_SDMA_BURST_16 0x1
+#define TI_SDMA_BURST_32 0x2
+#define TI_SDMA_BURST_64 0x3
+
+#define TI_SDMA_DATA_8BITS_SCALAR 0x0
+#define TI_SDMA_DATA_16BITS_SCALAR 0x1
+#define TI_SDMA_DATA_32BITS_SCALAR 0x2
+
+#define TI_SDMA_ADDR_CONSTANT 0x0
+#define TI_SDMA_ADDR_POST_INCREMENT 0x1
+#define TI_SDMA_ADDR_SINGLE_INDEX 0x2
+#define TI_SDMA_ADDR_DOUBLE_INDEX 0x3
+
+/**
+ * Status flags for the DMA callback
+ *
+ */
+#define TI_SDMA_STATUS_DROP (1UL << 1)
+#define TI_SDMA_STATUS_HALF (1UL << 2)
+#define TI_SDMA_STATUS_FRAME (1UL << 3)
+#define TI_SDMA_STATUS_LAST (1UL << 4)
+#define TI_SDMA_STATUS_BLOCK (1UL << 5)
+#define TI_SDMA_STATUS_SYNC (1UL << 6)
+#define TI_SDMA_STATUS_PKT (1UL << 7)
+#define TI_SDMA_STATUS_TRANS_ERR (1UL << 8)
+#define TI_SDMA_STATUS_SECURE_ERR (1UL << 9)
+#define TI_SDMA_STATUS_SUPERVISOR_ERR (1UL << 10)
+#define TI_SDMA_STATUS_MISALIGNED_ADRS_ERR (1UL << 11)
+#define TI_SDMA_STATUS_DRAIN_END (1UL << 12)
+
+#define TI_SDMA_SYNC_FRAME (1UL << 0)
+#define TI_SDMA_SYNC_BLOCK (1UL << 1)
+#define TI_SDMA_SYNC_PACKET (TI_SDMA_SYNC_FRAME | TI_SDMA_SYNC_BLOCK)
+#define TI_SDMA_SYNC_TRIG_ON_SRC (1UL << 8)
+#define TI_SDMA_SYNC_TRIG_ON_DST (1UL << 9)
+
+#define TI_SDMA_IRQ_FLAG_DROP (1UL << 1)
+#define TI_SDMA_IRQ_FLAG_HALF_FRAME_COMPL (1UL << 2)
+#define TI_SDMA_IRQ_FLAG_FRAME_COMPL (1UL << 3)
+#define TI_SDMA_IRQ_FLAG_START_LAST_FRAME (1UL << 4)
+#define TI_SDMA_IRQ_FLAG_BLOCK_COMPL (1UL << 5)
+#define TI_SDMA_IRQ_FLAG_ENDOF_PKT (1UL << 7)
+#define TI_SDMA_IRQ_FLAG_DRAIN (1UL << 12)
+
+int ti_sdma_activate_channel(unsigned int *ch,
+ void (*callback)(unsigned int ch, uint32_t status, void *data), void *data);
+int ti_sdma_deactivate_channel(unsigned int ch);
+int ti_sdma_start_xfer(unsigned int ch, unsigned int src_paddr,
+ unsigned long dst_paddr, unsigned int frmcnt, unsigned int elmcnt);
+int ti_sdma_start_xfer_packet(unsigned int ch, unsigned int src_paddr,
+ unsigned long dst_paddr, unsigned int frmcnt, unsigned int elmcnt,
+ unsigned int pktsize);
+int ti_sdma_stop_xfer(unsigned int ch);
+int ti_sdma_enable_channel_irq(unsigned int ch, uint32_t flags);
+int ti_sdma_disable_channel_irq(unsigned int ch);
+int ti_sdma_get_channel_status(unsigned int ch, uint32_t *status);
+int ti_sdma_set_xfer_endianess(unsigned int ch, unsigned int src, unsigned int dst);
+int ti_sdma_set_xfer_burst(unsigned int ch, unsigned int src, unsigned int dst);
+int ti_sdma_set_xfer_data_type(unsigned int ch, unsigned int type);
+int ti_sdma_set_callback(unsigned int ch,
+ void (*callback)(unsigned int ch, uint32_t status, void *data), void *data);
+int ti_sdma_sync_params(unsigned int ch, unsigned int trigger, unsigned int mode);
+int ti_sdma_set_addr_mode(unsigned int ch, unsigned int src_mode, unsigned int dst_mode);
+
+#endif /* _TI_SDMA_H_ */
diff --git a/sys/arm/ti/ti_sdmareg.h b/sys/arm/ti/ti_sdmareg.h
new file mode 100644
index 000000000000..8fa8d7ba02d4
--- /dev/null
+++ b/sys/arm/ti/ti_sdmareg.h
@@ -0,0 +1,135 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2011
+ * Ben Gray <ben.r.gray@gmail.com>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef __TI_SDMAREG_H__
+#define __TI_SDMAREG_H__
+
+/**
+ * The number of DMA channels possible on the controller.
+ */
+#define NUM_DMA_CHANNELS 32
+#define NUM_DMA_IRQS 4
+
+/**
+ * Register offsets
+ */
+#define DMA4_REVISION 0x0000
+#define DMA4_IRQSTATUS_L(j) (0x0008 + ((j) * 0x4))
+#define DMA4_IRQENABLE_L(j) (0x0018 + ((j) * 0x4))
+#define DMA4_SYSSTATUS 0x0028
+#define DMA4_OCP_SYSCONFIG 0x002C
+#define DMA4_CAPS_0 0x0064
+#define DMA4_CAPS_2 0x006C
+#define DMA4_CAPS_3 0x0070
+#define DMA4_CAPS_4 0x0074
+#define DMA4_GCR 0x0078
+#define DMA4_CCR(i) (0x0080 + ((i) * 0x60))
+#define DMA4_CLNK_CTRL(i) (0x0084 + ((i) * 0x60))
+#define DMA4_CICR(i) (0x0088 + ((i) * 0x60))
+#define DMA4_CSR(i) (0x008C + ((i) * 0x60))
+#define DMA4_CSDP(i) (0x0090 + ((i) * 0x60))
+#define DMA4_CEN(i) (0x0094 + ((i) * 0x60))
+#define DMA4_CFN(i) (0x0098 + ((i) * 0x60))
+#define DMA4_CSSA(i) (0x009C + ((i) * 0x60))
+#define DMA4_CDSA(i) (0x00A0 + ((i) * 0x60))
+#define DMA4_CSE(i) (0x00A4 + ((i) * 0x60))
+#define DMA4_CSF(i) (0x00A8 + ((i) * 0x60))
+#define DMA4_CDE(i) (0x00AC + ((i) * 0x60))
+#define DMA4_CDF(i) (0x00B0 + ((i) * 0x60))
+#define DMA4_CSAC(i) (0x00B4 + ((i) * 0x60))
+#define DMA4_CDAC(i) (0x00B8 + ((i) * 0x60))
+#define DMA4_CCEN(i) (0x00BC + ((i) * 0x60))
+#define DMA4_CCFN(i) (0x00C0 + ((i) * 0x60))
+#define DMA4_COLOR(i) (0x00C4 + ((i) * 0x60))
+
+/* The following register are only defined on OMAP44xx (and newer?) */
+#define DMA4_CDP(i) (0x00D0 + ((i) * 0x60))
+#define DMA4_CNDP(i) (0x00D4 + ((i) * 0x60))
+#define DMA4_CCDN(i) (0x00D8 + ((i) * 0x60))
+
+/**
+ * Various register field settings
+ */
+#define DMA4_CSDP_DATA_TYPE(x) (((x) & 0x3) << 0)
+#define DMA4_CSDP_SRC_BURST_MODE(x) (((x) & 0x3) << 7)
+#define DMA4_CSDP_DST_BURST_MODE(x) (((x) & 0x3) << 14)
+#define DMA4_CSDP_SRC_ENDIANISM(x) (((x) & 0x1) << 21)
+#define DMA4_CSDP_DST_ENDIANISM(x) (((x) & 0x1) << 19)
+#define DMA4_CSDP_WRITE_MODE(x) (((x) & 0x3) << 16)
+#define DMA4_CSDP_SRC_PACKED(x) (((x) & 0x1) << 6)
+#define DMA4_CSDP_DST_PACKED(x) (((x) & 0x1) << 13)
+
+#define DMA4_CCR_DST_ADDRESS_MODE(x) (((x) & 0x3) << 14)
+#define DMA4_CCR_SRC_ADDRESS_MODE(x) (((x) & 0x3) << 12)
+#define DMA4_CCR_READ_PRIORITY(x) (((x) & 0x1) << 6)
+#define DMA4_CCR_WRITE_PRIORITY(x) (((x) & 0x1) << 26)
+#define DMA4_CCR_SYNC_TRIGGER(x) ((((x) & 0x60) << 14) \
+ | ((x) & 0x1f))
+#define DMA4_CCR_FRAME_SYNC(x) (((x) & 0x1) << 5)
+#define DMA4_CCR_BLOCK_SYNC(x) (((x) & 0x1) << 18)
+#define DMA4_CCR_SEL_SRC_DST_SYNC(x) (((x) & 0x1) << 24)
+
+#define DMA4_CCR_PACKET_TRANS (DMA4_CCR_FRAME_SYNC(1) | \
+ DMA4_CCR_BLOCK_SYNC(1) )
+
+#define DMA4_CSR_DROP (1UL << 1)
+#define DMA4_CSR_HALF (1UL << 2)
+#define DMA4_CSR_FRAME (1UL << 3)
+#define DMA4_CSR_LAST (1UL << 4)
+#define DMA4_CSR_BLOCK (1UL << 5)
+#define DMA4_CSR_SYNC (1UL << 6)
+#define DMA4_CSR_PKT (1UL << 7)
+#define DMA4_CSR_TRANS_ERR (1UL << 8)
+#define DMA4_CSR_SECURE_ERR (1UL << 9)
+#define DMA4_CSR_SUPERVISOR_ERR (1UL << 10)
+#define DMA4_CSR_MISALIGNED_ADRS_ERR (1UL << 11)
+#define DMA4_CSR_DRAIN_END (1UL << 12)
+#define DMA4_CSR_CLEAR_MASK (0xffe)
+
+#define DMA4_CICR_DROP_IE (1UL << 1)
+#define DMA4_CICR_HALF_IE (1UL << 2)
+#define DMA4_CICR_FRAME_IE (1UL << 3)
+#define DMA4_CICR_LAST_IE (1UL << 4)
+#define DMA4_CICR_BLOCK_IE (1UL << 5)
+#define DMA4_CICR_PKT_IE (1UL << 7)
+#define DMA4_CICR_TRANS_ERR_IE (1UL << 8)
+#define DMA4_CICR_SECURE_ERR_IE (1UL << 9)
+#define DMA4_CICR_SUPERVISOR_ERR_IE (1UL << 10)
+#define DMA4_CICR_MISALIGNED_ADRS_ERR_IE (1UL << 11)
+#define DMA4_CICR_DRAIN_IE (1UL << 12)
+
+/**
+ * The following H/W revision values were found be experimentation, TI don't
+ * publish the revision numbers. The TRM says "TI internal Data".
+ */
+#define DMA4_OMAP3_REV 0x00000040
+#define DMA4_OMAP4_REV 0x00010900
+
+#endif /* __TI_SDMAREG_H__ */
diff --git a/sys/arm/ti/ti_smc.S b/sys/arm/ti/ti_smc.S
new file mode 100644
index 000000000000..287f0a7711f7
--- /dev/null
+++ b/sys/arm/ti/ti_smc.S
@@ -0,0 +1,41 @@
+/*-
+ * Copyright (c) 2012 Olivier Houchard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <machine/asm.h>
+__FBSDID("$FreeBSD$");
+
+#include <machine/armreg.h>
+
+ .cpu cortex-a8
+ .arch_extension sec
+
+/* Issue a smc #0 call */
+/* r0 and r1 contains the eventual arguments, r2 contains the function ID */
+ENTRY(ti_smc0)
+ stmfd sp!, {r4-r12, lr}
+ mov r12, r2 /* the rom expects the function ID in r12 */
+ dsb
+ smc #0
+ ldmfd sp!, {r4-r12, pc}
+
diff --git a/sys/arm/ti/ti_smc.h b/sys/arm/ti/ti_smc.h
new file mode 100644
index 000000000000..8db0333fc2fb
--- /dev/null
+++ b/sys/arm/ti/ti_smc.h
@@ -0,0 +1,34 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Olivier Houchard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * $FreeBSD$
+ */
+
+#ifndef TI_SMC_H_
+#define TI_SMC_H_
+uint32_t ti_smc0(uint32_t r0, uint32_t r1, uint32_t function_id);
+#endif /* TI_SMC_H_ */
diff --git a/sys/arm/ti/ti_spi.c b/sys/arm/ti/ti_spi.c
new file mode 100644
index 000000000000..19b80605b9b6
--- /dev/null
+++ b/sys/arm/ti/ti_spi.c
@@ -0,0 +1,582 @@
+/*-
+ * Copyright (c) 2016 Rubicon Communications, LLC (Netgate)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/intr.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/spibus/spi.h>
+#include <dev/spibus/spibusvar.h>
+
+#include <arm/ti/ti_sysc.h>
+#include <arm/ti/ti_spireg.h>
+#include <arm/ti/ti_spivar.h>
+
+#include "spibus_if.h"
+
+static void ti_spi_intr(void *);
+static int ti_spi_detach(device_t);
+
+#undef TI_SPI_DEBUG
+#ifdef TI_SPI_DEBUG
+#define IRQSTATUSBITS \
+ "\020\1TX0_EMPTY\2TX0_UNDERFLOW\3RX0_FULL\4RX0_OVERFLOW" \
+ "\5TX1_EMPTY\6TX1_UNDERFLOW\7RX1_FULL\11TX2_EMPTY" \
+ "\12TX1_UNDERFLOW\13RX2_FULL\15TX3_EMPTY\16TX3_UNDERFLOW" \
+ "\17RX3_FULL\22EOW"
+#define CONFBITS \
+ "\020\1PHA\2POL\7EPOL\17DMAW\20DMAR\21DPE0\22DPE1\23IS" \
+ "\24TURBO\25FORCE\30SBE\31SBPOL\34FFEW\35FFER\36CLKG"
+#define STATBITS \
+ "\020\1RXS\2TXS\3EOT\4TXFFE\5TXFFF\6RXFFE\7RXFFFF"
+#define MODULCTRLBITS \
+ "\020\1SINGLE\2NOSPIEN\3SLAVE\4SYST\10MOA\11FDAA"
+#define CTRLBITS \
+ "\020\1ENABLED"
+
+static void
+ti_spi_printr(device_t dev)
+{
+ int clk, conf, ctrl, div, i, j, wl;
+ struct ti_spi_softc *sc;
+ uint32_t reg;
+
+ sc = device_get_softc(dev);
+ reg = TI_SPI_READ(sc, MCSPI_SYSCONFIG);
+ device_printf(dev, "SYSCONFIG: %#x\n", reg);
+ reg = TI_SPI_READ(sc, MCSPI_SYSSTATUS);
+ device_printf(dev, "SYSSTATUS: %#x\n", reg);
+ reg = TI_SPI_READ(sc, MCSPI_IRQSTATUS);
+ device_printf(dev, "IRQSTATUS: 0x%b\n", reg, IRQSTATUSBITS);
+ reg = TI_SPI_READ(sc, MCSPI_IRQENABLE);
+ device_printf(dev, "IRQENABLE: 0x%b\n", reg, IRQSTATUSBITS);
+ reg = TI_SPI_READ(sc, MCSPI_MODULCTRL);
+ device_printf(dev, "MODULCTRL: 0x%b\n", reg, MODULCTRLBITS);
+ for (i = 0; i < sc->sc_numcs; i++) {
+ ctrl = TI_SPI_READ(sc, MCSPI_CTRL_CH(i));
+ conf = TI_SPI_READ(sc, MCSPI_CONF_CH(i));
+ device_printf(dev, "CH%dCONF: 0x%b\n", i, conf, CONFBITS);
+ if (conf & MCSPI_CONF_CLKG) {
+ div = (conf >> MCSPI_CONF_CLK_SHIFT) & MCSPI_CONF_CLK_MSK;
+ div |= ((ctrl >> MCSPI_CTRL_EXTCLK_SHIFT) & MCSPI_CTRL_EXTCLK_MSK) << 4;
+ } else {
+ div = 1;
+ j = (conf >> MCSPI_CONF_CLK_SHIFT) & MCSPI_CONF_CLK_MSK;
+ while (j-- > 0)
+ div <<= 1;
+ }
+ clk = TI_SPI_GCLK / div;
+ wl = ((conf >> MCSPI_CONF_WL_SHIFT) & MCSPI_CONF_WL_MSK) + 1;
+ device_printf(dev, "wordlen: %-2d clock: %d\n", wl, clk);
+ reg = TI_SPI_READ(sc, MCSPI_STAT_CH(i));
+ device_printf(dev, "CH%dSTAT: 0x%b\n", i, reg, STATBITS);
+ device_printf(dev, "CH%dCTRL: 0x%b\n", i, ctrl, CTRLBITS);
+ }
+ reg = TI_SPI_READ(sc, MCSPI_XFERLEVEL);
+ device_printf(dev, "XFERLEVEL: %#x\n", reg);
+}
+#endif
+
+static void
+ti_spi_set_clock(struct ti_spi_softc *sc, int ch, int freq)
+{
+ uint32_t clkdiv, conf, div, extclk, reg;
+
+ clkdiv = TI_SPI_GCLK / freq;
+ if (clkdiv > MCSPI_EXTCLK_MSK) {
+ extclk = 0;
+ clkdiv = 0;
+ div = 1;
+ while (TI_SPI_GCLK / div > freq && clkdiv <= 0xf) {
+ clkdiv++;
+ div <<= 1;
+ }
+ conf = clkdiv << MCSPI_CONF_CLK_SHIFT;
+ } else {
+ extclk = clkdiv >> 4;
+ clkdiv &= MCSPI_CONF_CLK_MSK;
+ conf = MCSPI_CONF_CLKG | clkdiv << MCSPI_CONF_CLK_SHIFT;
+ }
+
+ reg = TI_SPI_READ(sc, MCSPI_CTRL_CH(ch));
+ reg &= ~(MCSPI_CTRL_EXTCLK_MSK << MCSPI_CTRL_EXTCLK_SHIFT);
+ reg |= extclk << MCSPI_CTRL_EXTCLK_SHIFT;
+ TI_SPI_WRITE(sc, MCSPI_CTRL_CH(ch), reg);
+
+ reg = TI_SPI_READ(sc, MCSPI_CONF_CH(ch));
+ reg &= ~(MCSPI_CONF_CLKG | MCSPI_CONF_CLK_MSK << MCSPI_CONF_CLK_SHIFT);
+ TI_SPI_WRITE(sc, MCSPI_CONF_CH(ch), reg | conf);
+}
+
+static int
+ti_spi_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+ if (!ofw_bus_is_compatible(dev, "ti,omap4-mcspi"))
+ return (ENXIO);
+
+ device_set_desc(dev, "TI McSPI controller");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ti_spi_attach(device_t dev)
+{
+ int err, i, rid, timeout;
+ struct ti_spi_softc *sc;
+ uint32_t rev;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+
+ /* Activate the McSPI module. */
+ err = ti_sysc_clock_enable(device_get_parent(dev));
+ if (err) {
+ device_printf(dev, "Error: failed to activate source clock\n");
+ return (err);
+ }
+
+ /* Get the number of available channels. */
+ if ((OF_getencprop(ofw_bus_get_node(dev), "ti,spi-num-cs",
+ &sc->sc_numcs, sizeof(sc->sc_numcs))) <= 0) {
+ sc->sc_numcs = 2;
+ }
+
+ rid = 0;
+ sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (!sc->sc_mem_res) {
+ device_printf(dev, "cannot allocate memory window\n");
+ return (ENXIO);
+ }
+
+ sc->sc_bst = rman_get_bustag(sc->sc_mem_res);
+ sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res);
+
+ rid = 0;
+ sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (!sc->sc_irq_res) {
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+ device_printf(dev, "cannot allocate interrupt\n");
+ return (ENXIO);
+ }
+
+ /* Hook up our interrupt handler. */
+ if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+ NULL, ti_spi_intr, sc, &sc->sc_intrhand)) {
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+ device_printf(dev, "cannot setup the interrupt handler\n");
+ return (ENXIO);
+ }
+
+ mtx_init(&sc->sc_mtx, "ti_spi", NULL, MTX_DEF);
+
+ /* Issue a softreset to the controller */
+ TI_SPI_WRITE(sc, MCSPI_SYSCONFIG, MCSPI_SYSCONFIG_SOFTRESET);
+ timeout = 1000;
+ while (!(TI_SPI_READ(sc, MCSPI_SYSSTATUS) &
+ MCSPI_SYSSTATUS_RESETDONE)) {
+ if (--timeout == 0) {
+ device_printf(dev,
+ "Error: Controller reset operation timed out\n");
+ ti_spi_detach(dev);
+ return (ENXIO);
+ }
+ DELAY(100);
+ }
+
+ /* Print the McSPI module revision. */
+ rev = TI_SPI_READ(sc,
+ ti_sysc_get_rev_address_offset_host(device_get_parent(dev)));
+ device_printf(dev,
+ "scheme: %#x func: %#x rtl: %d rev: %d.%d custom rev: %d\n",
+ (rev >> MCSPI_REVISION_SCHEME_SHIFT) & MCSPI_REVISION_SCHEME_MSK,
+ (rev >> MCSPI_REVISION_FUNC_SHIFT) & MCSPI_REVISION_FUNC_MSK,
+ (rev >> MCSPI_REVISION_RTL_SHIFT) & MCSPI_REVISION_RTL_MSK,
+ (rev >> MCSPI_REVISION_MAJOR_SHIFT) & MCSPI_REVISION_MAJOR_MSK,
+ (rev >> MCSPI_REVISION_MINOR_SHIFT) & MCSPI_REVISION_MINOR_MSK,
+ (rev >> MCSPI_REVISION_CUSTOM_SHIFT) & MCSPI_REVISION_CUSTOM_MSK);
+
+ /* Set Master mode, single channel. */
+ TI_SPI_WRITE(sc, MCSPI_MODULCTRL, MCSPI_MODULCTRL_SINGLE);
+
+ /* Clear pending interrupts and disable interrupts. */
+ TI_SPI_WRITE(sc, MCSPI_IRQENABLE, 0x0);
+ TI_SPI_WRITE(sc, MCSPI_IRQSTATUS, 0xffff);
+
+ for (i = 0; i < sc->sc_numcs; i++) {
+ /*
+ * Default to SPI mode 0, CS active low, 8 bits word length and
+ * 500kHz clock.
+ */
+ TI_SPI_WRITE(sc, MCSPI_CONF_CH(i),
+ MCSPI_CONF_DPE0 | MCSPI_CONF_EPOL |
+ (8 - 1) << MCSPI_CONF_WL_SHIFT);
+ /* Set initial clock - 500kHz. */
+ ti_spi_set_clock(sc, i, 500000);
+ }
+
+#ifdef TI_SPI_DEBUG
+ ti_spi_printr(dev);
+#endif
+
+ device_add_child(dev, "spibus", -1);
+
+ return (bus_generic_attach(dev));
+}
+
+static int
+ti_spi_detach(device_t dev)
+{
+ struct ti_spi_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ /* Clear pending interrupts and disable interrupts. */
+ TI_SPI_WRITE(sc, MCSPI_IRQENABLE, 0);
+ TI_SPI_WRITE(sc, MCSPI_IRQSTATUS, 0xffff);
+
+ /* Reset controller. */
+ TI_SPI_WRITE(sc, MCSPI_SYSCONFIG, MCSPI_SYSCONFIG_SOFTRESET);
+
+ bus_generic_detach(dev);
+
+ mtx_destroy(&sc->sc_mtx);
+ if (sc->sc_intrhand)
+ bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand);
+ if (sc->sc_irq_res)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
+ if (sc->sc_mem_res)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+
+ return (0);
+}
+
+static int
+ti_spi_fill_fifo(struct ti_spi_softc *sc)
+{
+ int bytes, timeout;
+ struct spi_command *cmd;
+ uint32_t written;
+ uint8_t *data;
+
+ cmd = sc->sc_cmd;
+ bytes = min(sc->sc_len - sc->sc_written, sc->sc_fifolvl);
+ while (bytes-- > 0) {
+ data = (uint8_t *)cmd->tx_cmd;
+ written = sc->sc_written++;
+ if (written >= cmd->tx_cmd_sz) {
+ data = (uint8_t *)cmd->tx_data;
+ written -= cmd->tx_cmd_sz;
+ }
+ if (sc->sc_fifolvl == 1) {
+ /* FIFO disabled. */
+ timeout = 1000;
+ while (--timeout > 0 && (TI_SPI_READ(sc,
+ MCSPI_STAT_CH(sc->sc_cs)) & MCSPI_STAT_TXS) == 0) {
+ DELAY(100);
+ }
+ if (timeout == 0)
+ return (-1);
+ }
+ TI_SPI_WRITE(sc, MCSPI_TX_CH(sc->sc_cs), data[written]);
+ }
+
+ return (0);
+}
+
+static int
+ti_spi_drain_fifo(struct ti_spi_softc *sc)
+{
+ int bytes, timeout;
+ struct spi_command *cmd;
+ uint32_t read;
+ uint8_t *data;
+
+ cmd = sc->sc_cmd;
+ bytes = min(sc->sc_len - sc->sc_read, sc->sc_fifolvl);
+ while (bytes-- > 0) {
+ data = (uint8_t *)cmd->rx_cmd;
+ read = sc->sc_read++;
+ if (read >= cmd->rx_cmd_sz) {
+ data = (uint8_t *)cmd->rx_data;
+ read -= cmd->rx_cmd_sz;
+ }
+ if (sc->sc_fifolvl == 1) {
+ /* FIFO disabled. */
+ timeout = 1000;
+ while (--timeout > 0 && (TI_SPI_READ(sc,
+ MCSPI_STAT_CH(sc->sc_cs)) & MCSPI_STAT_RXS) == 0) {
+ DELAY(100);
+ }
+ if (timeout == 0)
+ return (-1);
+ }
+ data[read] = TI_SPI_READ(sc, MCSPI_RX_CH(sc->sc_cs));
+ }
+
+ return (0);
+}
+
+static void
+ti_spi_intr(void *arg)
+{
+ int eow;
+ struct ti_spi_softc *sc;
+ uint32_t status;
+
+ eow = 0;
+ sc = (struct ti_spi_softc *)arg;
+ TI_SPI_LOCK(sc);
+ status = TI_SPI_READ(sc, MCSPI_IRQSTATUS);
+
+ /*
+ * No new TX_empty or RX_full event will be asserted while the CPU has
+ * not performed the number of writes or reads defined by
+ * MCSPI_XFERLEVEL[AEL] and MCSPI_XFERLEVEL[AFL]. It is responsibility
+ * of CPU perform the right number of writes and reads.
+ */
+ if (status & MCSPI_IRQ_TX0_EMPTY)
+ ti_spi_fill_fifo(sc);
+ if (status & MCSPI_IRQ_RX0_FULL)
+ ti_spi_drain_fifo(sc);
+
+ if (status & MCSPI_IRQ_EOW)
+ eow = 1;
+
+ /* Clear interrupt status. */
+ TI_SPI_WRITE(sc, MCSPI_IRQSTATUS, status);
+
+ /* Check for end of transfer. */
+ if (sc->sc_written == sc->sc_len && sc->sc_read == sc->sc_len) {
+ sc->sc_flags |= TI_SPI_DONE;
+ wakeup(sc->sc_dev);
+ }
+
+ TI_SPI_UNLOCK(sc);
+}
+
+static int
+ti_spi_pio_transfer(struct ti_spi_softc *sc)
+{
+
+ while (sc->sc_len - sc->sc_written > 0) {
+ if (ti_spi_fill_fifo(sc) == -1)
+ return (EIO);
+ if (ti_spi_drain_fifo(sc) == -1)
+ return (EIO);
+ }
+
+ return (0);
+}
+
+static int
+ti_spi_gcd(int a, int b)
+{
+ int m;
+
+ while ((m = a % b) != 0) {
+ a = b;
+ b = m;
+ }
+
+ return (b);
+}
+
+static int
+ti_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
+{
+ int err;
+ struct ti_spi_softc *sc;
+ uint32_t clockhz, cs, mode, reg;
+
+ sc = device_get_softc(dev);
+
+ KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz,
+ ("TX/RX command sizes should be equal"));
+ KASSERT(cmd->tx_data_sz == cmd->rx_data_sz,
+ ("TX/RX data sizes should be equal"));
+
+ /* Get the proper chip select for this child. */
+ spibus_get_cs(child, &cs);
+ spibus_get_clock(child, &clockhz);
+ spibus_get_mode(child, &mode);
+
+ cs &= ~SPIBUS_CS_HIGH;
+
+ if (cs > sc->sc_numcs) {
+ device_printf(dev, "Invalid chip select %d requested by %s\n",
+ cs, device_get_nameunit(child));
+ return (EINVAL);
+ }
+
+ if (mode > 3)
+ {
+ device_printf(dev, "Invalid mode %d requested by %s\n", mode,
+ device_get_nameunit(child));
+ return (EINVAL);
+ }
+
+ TI_SPI_LOCK(sc);
+
+ /* If the controller is in use wait until it is available. */
+ while (sc->sc_flags & TI_SPI_BUSY)
+ mtx_sleep(dev, &sc->sc_mtx, 0, "ti_spi", 0);
+
+ /* Now we have control over SPI controller. */
+ sc->sc_flags = TI_SPI_BUSY;
+
+ /* Save the SPI command data. */
+ sc->sc_cs = cs;
+ sc->sc_cmd = cmd;
+ sc->sc_read = 0;
+ sc->sc_written = 0;
+ sc->sc_len = cmd->tx_cmd_sz + cmd->tx_data_sz;
+ sc->sc_fifolvl = ti_spi_gcd(sc->sc_len, TI_SPI_FIFOSZ);
+ if (sc->sc_fifolvl < 2 || sc->sc_len > 0xffff)
+ sc->sc_fifolvl = 1; /* FIFO disabled. */
+ /* Disable FIFO for now. */
+ sc->sc_fifolvl = 1;
+
+ /* Set the bus frequency. */
+ ti_spi_set_clock(sc, sc->sc_cs, clockhz);
+
+ /* Disable the FIFO. */
+ TI_SPI_WRITE(sc, MCSPI_XFERLEVEL, 0);
+
+ /* 8 bits word, d0 miso, d1 mosi, mode 0 and CS active low. */
+ reg = TI_SPI_READ(sc, MCSPI_CONF_CH(sc->sc_cs));
+ reg &= ~(MCSPI_CONF_FFER | MCSPI_CONF_FFEW | MCSPI_CONF_SBPOL |
+ MCSPI_CONF_SBE | MCSPI_CONF_TURBO | MCSPI_CONF_IS |
+ MCSPI_CONF_DPE1 | MCSPI_CONF_DPE0 | MCSPI_CONF_DMAR |
+ MCSPI_CONF_DMAW | MCSPI_CONF_EPOL);
+ reg |= MCSPI_CONF_DPE0 | MCSPI_CONF_EPOL | MCSPI_CONF_WL8BITS;
+ reg |= mode; /* POL and PHA are the low bits, we can just OR-in mode */
+ TI_SPI_WRITE(sc, MCSPI_CONF_CH(sc->sc_cs), reg);
+
+#if 0
+ /* Enable channel interrupts. */
+ reg = TI_SPI_READ(sc, MCSPI_IRQENABLE);
+ reg |= 0xf;
+ TI_SPI_WRITE(sc, MCSPI_IRQENABLE, reg);
+#endif
+
+ /* Start the transfer. */
+ reg = TI_SPI_READ(sc, MCSPI_CTRL_CH(sc->sc_cs));
+ TI_SPI_WRITE(sc, MCSPI_CTRL_CH(sc->sc_cs), reg | MCSPI_CTRL_ENABLE);
+
+ /* Force CS on. */
+ reg = TI_SPI_READ(sc, MCSPI_CONF_CH(sc->sc_cs));
+ TI_SPI_WRITE(sc, MCSPI_CONF_CH(sc->sc_cs), reg |= MCSPI_CONF_FORCE);
+
+ err = 0;
+ if (sc->sc_fifolvl == 1)
+ err = ti_spi_pio_transfer(sc);
+
+ /* Force CS off. */
+ reg = TI_SPI_READ(sc, MCSPI_CONF_CH(sc->sc_cs));
+ reg &= ~MCSPI_CONF_FORCE;
+ TI_SPI_WRITE(sc, MCSPI_CONF_CH(sc->sc_cs), reg);
+
+ /* Disable IRQs. */
+ reg = TI_SPI_READ(sc, MCSPI_IRQENABLE);
+ reg &= ~0xf;
+ TI_SPI_WRITE(sc, MCSPI_IRQENABLE, reg);
+ TI_SPI_WRITE(sc, MCSPI_IRQSTATUS, 0xf);
+
+ /* Disable the SPI channel. */
+ reg = TI_SPI_READ(sc, MCSPI_CTRL_CH(sc->sc_cs));
+ reg &= ~MCSPI_CTRL_ENABLE;
+ TI_SPI_WRITE(sc, MCSPI_CTRL_CH(sc->sc_cs), reg);
+
+ /* Disable FIFO. */
+ reg = TI_SPI_READ(sc, MCSPI_CONF_CH(sc->sc_cs));
+ reg &= ~(MCSPI_CONF_FFER | MCSPI_CONF_FFEW);
+ TI_SPI_WRITE(sc, MCSPI_CONF_CH(sc->sc_cs), reg);
+
+ /* Release the controller and wakeup the next thread waiting for it. */
+ sc->sc_flags = 0;
+ wakeup_one(dev);
+ TI_SPI_UNLOCK(sc);
+
+ return (err);
+}
+
+static phandle_t
+ti_spi_get_node(device_t bus, device_t dev)
+{
+
+ /* Share controller node with spibus. */
+ return (ofw_bus_get_node(bus));
+}
+
+static device_method_t ti_spi_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ti_spi_probe),
+ DEVMETHOD(device_attach, ti_spi_attach),
+ DEVMETHOD(device_detach, ti_spi_detach),
+
+ /* SPI interface */
+ DEVMETHOD(spibus_transfer, ti_spi_transfer),
+
+ /* ofw_bus interface */
+ DEVMETHOD(ofw_bus_get_node, ti_spi_get_node),
+
+ DEVMETHOD_END
+};
+
+static devclass_t ti_spi_devclass;
+
+static driver_t ti_spi_driver = {
+ "spi",
+ ti_spi_methods,
+ sizeof(struct ti_spi_softc),
+};
+
+DRIVER_MODULE(ti_spi, simplebus, ti_spi_driver, ti_spi_devclass, 0, 0);
+MODULE_DEPEND(ti_spi, ti_sysc, 1, 1, 1);
diff --git a/sys/arm/ti/ti_spireg.h b/sys/arm/ti/ti_spireg.h
new file mode 100644
index 000000000000..f31f55e426eb
--- /dev/null
+++ b/sys/arm/ti/ti_spireg.h
@@ -0,0 +1,97 @@
+/*-
+ * Copyright (c) 2016 Rubicon Communications, LLC (Netgate)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _TI_SPIREG_H_
+#define _TI_SPIREG_H_
+
+#define TI_SPI_GCLK 48000000U
+#define TI_SPI_FIFOSZ 32
+#define MCSPI_REVISION 0x0
+#define MCSPI_REVISION_SCHEME_SHIFT 30
+#define MCSPI_REVISION_SCHEME_MSK 0x3
+#define MCSPI_REVISION_FUNC_SHIFT 16
+#define MCSPI_REVISION_FUNC_MSK 0xfff
+#define MCSPI_REVISION_RTL_SHIFT 11
+#define MCSPI_REVISION_RTL_MSK 0x1f
+#define MCSPI_REVISION_MAJOR_SHIFT 8
+#define MCSPI_REVISION_MAJOR_MSK 0x7
+#define MCSPI_REVISION_CUSTOM_SHIFT 6
+#define MCSPI_REVISION_CUSTOM_MSK 0x3
+#define MCSPI_REVISION_MINOR_SHIFT 0
+#define MCSPI_REVISION_MINOR_MSK 0x3f
+#define MCSPI_SYSCONFIG 0x110
+#define MCSPI_SYSCONFIG_SOFTRESET (1 << 1)
+#define MCSPI_SYSSTATUS 0x114
+#define MCSPI_SYSSTATUS_RESETDONE (1 << 0)
+#define MCSPI_MODULCTRL 0x128
+#define MCSPI_MODULCTRL_SLAVE (1 << 2)
+#define MCSPI_MODULCTRL_SINGLE (1 << 0)
+#define MCSPI_IRQSTATUS 0x118
+#define MCSPI_IRQENABLE 0x11c
+#define MCSPI_IRQ_EOW (1 << 17)
+#define MCSPI_IRQ_RX0_OVERFLOW (1 << 3)
+#define MCSPI_IRQ_RX0_FULL (1 << 2)
+#define MCSPI_IRQ_TX0_UNDERFLOW (1 << 1)
+#define MCSPI_IRQ_TX0_EMPTY (1 << 0)
+#define MCSPI_CONF_CH(_c) (0x12c + 0x14 * (_c))
+#define MCSPI_CONF_CLKG (1 << 29)
+#define MCSPI_CONF_FFER (1 << 28)
+#define MCSPI_CONF_FFEW (1 << 27)
+#define MCSPI_CONF_SBPOL (1 << 24)
+#define MCSPI_CONF_SBE (1 << 23)
+#define MCSPI_CONF_FORCE (1 << 20)
+#define MCSPI_CONF_TURBO (1 << 19)
+#define MCSPI_CONF_IS (1 << 18)
+#define MCSPI_CONF_DPE1 (1 << 17)
+#define MCSPI_CONF_DPE0 (1 << 16)
+#define MCSPI_CONF_DMAR (1 << 15)
+#define MCSPI_CONF_DMAW (1 << 14)
+#define MCSPI_CONF_WL_MSK 0x1f
+#define MCSPI_CONF_WL_SHIFT 7
+#define MCSPI_CONF_WL8BITS (7 << MCSPI_CONF_WL_SHIFT)
+#define MCSPI_CONF_EPOL (1 << 6)
+#define MCSPI_CONF_CLK_MSK 0xf
+#define MCSPI_CONF_CLK_SHIFT 2
+#define MCSPI_CONF_POL (1 << 1)
+#define MCSPI_CONF_PHA (1 << 0)
+#define MCSPI_STAT_CH(_c) (0x130 + 0x14 * (_c))
+#define MCSPI_STAT_TXFFF (1 << 4)
+#define MCSPI_STAT_TXS (1 << 1)
+#define MCSPI_STAT_RXS (1 << 0)
+#define MCSPI_CTRL_CH(_c) (0x134 + 0x14 * (_c))
+#define MCSPI_EXTCLK_MSK 0xfff
+#define MCSPI_CTRL_EXTCLK_MSK 0xff
+#define MCSPI_CTRL_EXTCLK_SHIFT 8
+#define MCSPI_CTRL_ENABLE (1 << 0)
+#define MCSPI_TX_CH(_c) (0x138 + 0x14 * (_c))
+#define MCSPI_RX_CH(_c) (0x13c + 0x14 * (_c))
+#define MCSPI_XFERLEVEL 0x17c
+#define MCSPI_XFERLEVEL_AFL(_a) (((_a) >> 8) & 0xff)
+#define MCSPI_XFERLEVEL_AEL(_a) (((_a) >> 0) & 0xff)
+
+#endif /* _TI_SPIREG_H_ */
diff --git a/sys/arm/ti/ti_spivar.h b/sys/arm/ti/ti_spivar.h
new file mode 100644
index 000000000000..89731f323691
--- /dev/null
+++ b/sys/arm/ti/ti_spivar.h
@@ -0,0 +1,71 @@
+/*-
+ * Copyright (c) 2016 Rubicon Communications, LLC (Netgate)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _TI_SPIVAR_H_
+#define _TI_SPIVAR_H_
+
+struct ti_spi_softc {
+ bus_space_tag_t sc_bst;
+ bus_space_handle_t sc_bsh;
+ device_t sc_dev;
+ int sc_numcs;
+ struct mtx sc_mtx;
+ struct resource *sc_mem_res;
+ struct resource *sc_irq_res;
+ struct {
+ int cs;
+ int fifolvl;
+ struct spi_command *cmd;
+ uint32_t len;
+ uint32_t read;
+ uint32_t written;
+ } xfer;
+ uint32_t sc_flags;
+ void *sc_intrhand;
+#define sc_cs xfer.cs
+#define sc_fifolvl xfer.fifolvl
+#define sc_cmd xfer.cmd
+#define sc_len xfer.len
+#define sc_read xfer.read
+#define sc_written xfer.written
+};
+
+#define TI_SPI_BUSY 0x1
+#define TI_SPI_DONE 0x2
+
+#define TI_SPI_WRITE(_sc, _off, _val) \
+ bus_space_write_4((_sc)->sc_bst, (_sc)->sc_bsh, (_off), (_val))
+#define TI_SPI_READ(_sc, _off) \
+ bus_space_read_4((_sc)->sc_bst, (_sc)->sc_bsh, (_off))
+
+#define TI_SPI_LOCK(_sc) \
+ mtx_lock(&(_sc)->sc_mtx)
+#define TI_SPI_UNLOCK(_sc) \
+ mtx_unlock(&(_sc)->sc_mtx)
+
+#endif /* _TI_SPIVAR_H_ */
diff --git a/sys/arm/ti/ti_sysc.c b/sys/arm/ti/ti_sysc.c
new file mode 100644
index 000000000000..b16158aa5d83
--- /dev/null
+++ b/sys/arm/ti/ti_sysc.c
@@ -0,0 +1,622 @@
+/*-
+ * Copyright (c) 2019 Emmanuel Vadot <manu@FreeBSD.org>
+ *
+ * Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/fbio.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/queue.h>
+#include <sys/rman.h>
+#include <sys/resource.h>
+#include <machine/bus.h>
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_kern.h>
+#include <vm/pmap.h>
+
+#include <dev/fdt/simplebus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/extres/clk/clk.h>
+
+#include <arm/ti/ti_sysc.h>
+#include <arm/ti/clk/clock_common.h>
+
+#define DEBUG_SYSC 0
+
+#if DEBUG_SYSC
+#define DPRINTF(dev, msg...) device_printf(dev, msg)
+#else
+#define DPRINTF(dev, msg...)
+#endif
+
+/* Documentation/devicetree/bindings/bus/ti-sysc.txt
+ *
+ * Documentation/devicetree/clock/clock-bindings.txt
+ * Defines phandle + optional pair
+ * Documentation/devicetree/clock/ti-clkctl.txt
+ */
+
+static int ti_sysc_probe(device_t dev);
+static int ti_sysc_attach(device_t dev);
+static int ti_sysc_detach(device_t dev);
+
+#define TI_SYSC_DRA7_MCAN 15
+#define TI_SYSC_USB_HOST_FS 14
+#define TI_SYSC_DRA7_MCASP 13
+#define TI_SYSC_MCASP 12
+#define TI_SYSC_OMAP_AES 11
+#define TI_SYSC_OMAP3_SHAM 10
+#define TI_SYSC_OMAP4_SR 9
+#define TI_SYSC_OMAP3630_SR 8
+#define TI_SYSC_OMAP3430_SR 7
+#define TI_SYSC_OMAP4_TIMER 6
+#define TI_SYSC_OMAP2_TIMER 5
+/* Above needs special workarounds */
+#define TI_SYSC_OMAP4_SIMPLE 4
+#define TI_SYSC_OMAP4 3
+#define TI_SYSC_OMAP2 2
+#define TI_SYSC 1
+#define TI_SYSC_END 0
+
+static struct ofw_compat_data compat_data[] = {
+ { "ti,sysc-dra7-mcan", TI_SYSC_DRA7_MCAN },
+ { "ti,sysc-usb-host-fs", TI_SYSC_USB_HOST_FS },
+ { "ti,sysc-dra7-mcasp", TI_SYSC_DRA7_MCASP },
+ { "ti,sysc-mcasp", TI_SYSC_MCASP },
+ { "ti,sysc-omap-aes", TI_SYSC_OMAP_AES },
+ { "ti,sysc-omap3-sham", TI_SYSC_OMAP3_SHAM },
+ { "ti,sysc-omap4-sr", TI_SYSC_OMAP4_SR },
+ { "ti,sysc-omap3630-sr", TI_SYSC_OMAP3630_SR },
+ { "ti,sysc-omap3430-sr", TI_SYSC_OMAP3430_SR },
+ { "ti,sysc-omap4-timer", TI_SYSC_OMAP4_TIMER },
+ { "ti,sysc-omap2-timer", TI_SYSC_OMAP2_TIMER },
+ /* Above needs special workarounds */
+ { "ti,sysc-omap4-simple", TI_SYSC_OMAP4_SIMPLE },
+ { "ti,sysc-omap4", TI_SYSC_OMAP4 },
+ { "ti,sysc-omap2", TI_SYSC_OMAP2 },
+ { "ti,sysc", TI_SYSC },
+ { NULL, TI_SYSC_END }
+};
+
+/* reg-names can be "rev", "sysc" and "syss" */
+static const char * reg_names[] = { "rev", "sysc", "syss" };
+#define REG_REV 0
+#define REG_SYSC 1
+#define REG_SYSS 2
+#define REG_MAX 3
+
+/* master idle / slave idle mode defined in 8.1.3.2.1 / 8.1.3.2.2 */
+#include <dt-bindings/bus/ti-sysc.h>
+#define SYSC_IDLE_MAX 4
+
+struct sysc_reg {
+ uint64_t address;
+ uint64_t size;
+};
+
+struct clk_list {
+ TAILQ_ENTRY(clk_list) next;
+ clk_t clk;
+};
+
+struct ti_sysc_softc {
+ struct simplebus_softc sc;
+ bool attach_done;
+
+ device_t dev;
+ int device_type;
+
+ struct sysc_reg reg[REG_MAX];
+ /* Offset from host base address */
+ uint64_t offset_reg[REG_MAX];
+
+ uint32_t ti_sysc_mask;
+ int32_t ti_sysc_midle[SYSC_IDLE_MAX];
+ int32_t ti_sysc_sidle[SYSC_IDLE_MAX];
+ uint32_t ti_sysc_delay_us;
+ uint32_t ti_syss_mask;
+
+ int num_clocks;
+ TAILQ_HEAD(, clk_list) clk_list;
+
+ /* deprecated ti_hwmods */
+ bool ti_no_reset_on_init;
+ bool ti_no_idle_on_init;
+ bool ti_no_idle;
+};
+
+/*
+ * All sysc seems to have a reg["rev"] register.
+ * Lets use that for identification of which module the driver are connected to.
+ */
+uint64_t
+ti_sysc_get_rev_address(device_t dev) {
+ struct ti_sysc_softc *sc = device_get_softc(dev);
+
+ return (sc->reg[REG_REV].address);
+}
+
+uint64_t
+ti_sysc_get_rev_address_offset_host(device_t dev) {
+ struct ti_sysc_softc *sc = device_get_softc(dev);
+
+ return (sc->offset_reg[REG_REV]);
+}
+
+uint64_t
+ti_sysc_get_sysc_address(device_t dev) {
+ struct ti_sysc_softc *sc = device_get_softc(dev);
+
+ return (sc->reg[REG_SYSC].address);
+}
+
+uint64_t
+ti_sysc_get_sysc_address_offset_host(device_t dev) {
+ struct ti_sysc_softc *sc = device_get_softc(dev);
+
+ return (sc->offset_reg[REG_SYSC]);
+}
+
+uint64_t
+ti_sysc_get_syss_address(device_t dev) {
+ struct ti_sysc_softc *sc = device_get_softc(dev);
+
+ return (sc->reg[REG_SYSS].address);
+}
+
+uint64_t
+ti_sysc_get_syss_address_offset_host(device_t dev) {
+ struct ti_sysc_softc *sc = device_get_softc(dev);
+
+ return (sc->offset_reg[REG_SYSS]);
+}
+
+/*
+ * Due no memory region is assigned the sysc driver the children needs to
+ * handle the practical read/writes to the registers.
+ * Check if sysc has reset bit.
+ */
+uint32_t
+ti_sysc_get_soft_reset_bit(device_t dev) {
+ struct ti_sysc_softc *sc = device_get_softc(dev);
+ switch (sc->device_type) {
+ case TI_SYSC_OMAP4_TIMER:
+ case TI_SYSC_OMAP4_SIMPLE:
+ case TI_SYSC_OMAP4:
+ if (sc->ti_sysc_mask & SYSC_OMAP4_SOFTRESET) {
+ return (SYSC_OMAP4_SOFTRESET);
+ }
+ break;
+
+ case TI_SYSC_OMAP2_TIMER:
+ case TI_SYSC_OMAP2:
+ case TI_SYSC:
+ if (sc->ti_sysc_mask & SYSC_OMAP2_SOFTRESET) {
+ return (SYSC_OMAP2_SOFTRESET);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+int
+ti_sysc_clock_enable(device_t dev) {
+ struct clk_list *clkp, *clkp_tmp;
+ struct ti_sysc_softc *sc = device_get_softc(dev);
+ int err;
+
+ TAILQ_FOREACH_SAFE(clkp, &sc->clk_list, next, clkp_tmp) {
+ err = clk_enable(clkp->clk);
+
+ if (err) {
+ DPRINTF(sc->dev, "clk_enable %s failed %d\n",
+ clk_get_name(clkp->clk), err);
+ break;
+ }
+ }
+ return (err);
+}
+
+int
+ti_sysc_clock_disable(device_t dev) {
+ struct clk_list *clkp, *clkp_tmp;
+ struct ti_sysc_softc *sc = device_get_softc(dev);
+ int err = 0;
+
+ TAILQ_FOREACH_SAFE(clkp, &sc->clk_list, next, clkp_tmp) {
+ err = clk_disable(clkp->clk);
+
+ if (err) {
+ DPRINTF(sc->dev, "clk_enable %s failed %d\n",
+ clk_get_name(clkp->clk), err);
+ break;
+ }
+ }
+ return (err);
+}
+
+static int
+parse_regfields(struct ti_sysc_softc *sc) {
+ phandle_t node;
+ uint32_t parent_address_cells;
+ uint32_t parent_size_cells;
+ cell_t *reg;
+ ssize_t nreg;
+ int err, k, reg_i, prop_idx;
+ uint32_t idx;
+
+ node = ofw_bus_get_node(sc->dev);
+
+ /* Get parents address and size properties */
+ err = OF_searchencprop(OF_parent(node), "#address-cells",
+ &parent_address_cells, sizeof(parent_address_cells));
+ if (err == -1)
+ return (ENXIO);
+ if (!(parent_address_cells == 1 || parent_address_cells == 2)) {
+ DPRINTF(sc->dev, "Expect parent #address-cells=[1||2]\n");
+ return (ENXIO);
+ }
+
+ err = OF_searchencprop(OF_parent(node), "#size-cells",
+ &parent_size_cells, sizeof(parent_size_cells));
+ if (err == -1)
+ return (ENXIO);
+
+ if (!(parent_size_cells == 1 || parent_size_cells == 2)) {
+ DPRINTF(sc->dev, "Expect parent #size-cells = [1||2]\n");
+ return (ENXIO);
+ }
+
+ /* Grab the content of reg properties */
+ nreg = OF_getproplen(node, "reg");
+ reg = malloc(nreg, M_DEVBUF, M_WAITOK);
+ OF_getencprop(node, "reg", reg, nreg);
+
+ /* Make sure address & size are 0 */
+ for (idx = 0; idx < REG_MAX; idx++) {
+ sc->reg[idx].address = 0;
+ sc->reg[idx].size = 0;
+ }
+
+ /* Loop through reg-names and figure out which reg-name corresponds to
+ * index populate the values into the reg array.
+ */
+ for (idx = 0, reg_i = 0; idx < REG_MAX && reg_i < nreg; idx++) {
+ err = ofw_bus_find_string_index(node, "reg-names",
+ reg_names[idx], &prop_idx);
+ if (err != 0)
+ continue;
+
+ for (k = 0; k < parent_address_cells; k++) {
+ sc->reg[prop_idx].address <<= 32;
+ sc->reg[prop_idx].address |= reg[reg_i++];
+ }
+
+ for (k = 0; k < parent_size_cells; k++) {
+ sc->reg[prop_idx].size <<= 32;
+ sc->reg[prop_idx].size |= reg[reg_i++];
+ }
+
+ if (sc->sc.nranges == 0)
+ sc->offset_reg[prop_idx] = sc->reg[prop_idx].address;
+ else
+ sc->offset_reg[prop_idx] = sc->reg[prop_idx].address -
+ sc->sc.ranges[REG_REV].host;
+
+ DPRINTF(sc->dev, "reg[%s] adress %#jx size %#jx\n",
+ reg_names[idx],
+ sc->reg[prop_idx].address,
+ sc->reg[prop_idx].size);
+ }
+ free(reg, M_DEVBUF);
+ return (0);
+}
+
+static void
+parse_idle(struct ti_sysc_softc *sc, const char *name, uint32_t *idle) {
+ phandle_t node;
+ cell_t value[SYSC_IDLE_MAX];
+ int len, no, i;
+
+ node = ofw_bus_get_node(sc->dev);
+
+ if (!OF_hasprop(node, name)) {
+ return;
+ }
+
+ len = OF_getproplen(node, name);
+ no = len / sizeof(cell_t);
+ if (no >= SYSC_IDLE_MAX) {
+ DPRINTF(sc->dev, "Limit %s\n", name);
+ no = SYSC_IDLE_MAX-1;
+ len = no * sizeof(cell_t);
+ }
+
+ OF_getencprop(node, name, value, len);
+ for (i = 0; i < no; i++) {
+ idle[i] = value[i];
+#if DEBUG_SYSC
+ DPRINTF(sc->dev, "%s[%d] = %d ",
+ name, i, value[i]);
+ switch(value[i]) {
+ case SYSC_IDLE_FORCE:
+ DPRINTF(sc->dev, "SYSC_IDLE_FORCE\n");
+ break;
+ case SYSC_IDLE_NO:
+ DPRINTF(sc->dev, "SYSC_IDLE_NO\n");
+ break;
+ case SYSC_IDLE_SMART:
+ DPRINTF(sc->dev, "SYSC_IDLE_SMART\n");
+ break;
+ case SYSC_IDLE_SMART_WKUP:
+ DPRINTF(sc->dev, "SYSC_IDLE_SMART_WKUP\n");
+ break;
+ }
+#endif
+ }
+ for ( ; i < SYSC_IDLE_MAX; i++)
+ idle[i] = -1;
+}
+
+static int
+ti_sysc_attach_clocks(struct ti_sysc_softc *sc) {
+ clk_t *clk;
+ struct clk_list *clkp;
+ int index, err;
+ phandle_t cnode;
+
+ clk = malloc(sc->num_clocks*sizeof(clk_t), M_DEVBUF, M_WAITOK | M_ZERO);
+
+ cnode = ofw_bus_get_node(sc->dev);
+
+ /* Check if all clocks can be found */
+ for (index = 0; index < sc->num_clocks; index++) {
+ err = clk_get_by_ofw_index(sc->dev, 0, index, &clk[index]);
+
+ if (err != 0) {
+ free(clk, M_DEVBUF);
+ return (1);
+ }
+ }
+
+ /* All clocks are found, add to list */
+ for (index = 0; index < sc->num_clocks; index++) {
+ clkp = malloc(sizeof(*clkp), M_DEVBUF, M_WAITOK | M_ZERO);
+ clkp->clk = clk[index];
+ TAILQ_INSERT_TAIL(&sc->clk_list, clkp, next);
+ }
+
+ /* Release the clk array */
+ free(clk, M_DEVBUF);
+ return (0);
+}
+
+static int
+ti_sysc_simplebus_attach_child(device_t dev) {
+ device_t cdev;
+ phandle_t node, child;
+ struct ti_sysc_softc *sc = device_get_softc(dev);
+
+ node = ofw_bus_get_node(sc->dev);
+
+ for (child = OF_child(node); child > 0; child = OF_peer(child)) {
+ cdev = simplebus_add_device(sc->dev, child, 0, NULL, -1, NULL);
+ if (cdev != NULL)
+ device_probe_and_attach(cdev);
+ }
+ return (0);
+}
+
+/* Device interface */
+static int
+ti_sysc_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "TI SYSC Interconnect");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ti_sysc_attach(device_t dev)
+{
+ struct ti_sysc_softc *sc;
+ phandle_t node;
+ int err;
+ cell_t value;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ sc->device_type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+
+ node = ofw_bus_get_node(sc->dev);
+ /* ranges - use simplebus */
+ simplebus_init(sc->dev, node);
+ if (simplebus_fill_ranges(node, &sc->sc) < 0) {
+ DPRINTF(sc->dev, "could not get ranges\n");
+ return (ENXIO);
+ }
+
+ if (sc->sc.nranges == 0) {
+ DPRINTF(sc->dev, "nranges == 0\n");
+ return (ENXIO);
+ }
+
+ /* Required field reg & reg-names - assume at least "rev" exists */
+ err = parse_regfields(sc);
+ if (err) {
+ DPRINTF(sc->dev, "parse_regfields failed %d\n", err);
+ return (ENXIO);
+ }
+
+ /* Optional */
+ if (OF_hasprop(node, "ti,sysc-mask")) {
+ OF_getencprop(node, "ti,sysc-mask", &value, sizeof(cell_t));
+ sc->ti_sysc_mask = value;
+ }
+ if (OF_hasprop(node, "ti,syss-mask")) {
+ OF_getencprop(node, "ti,syss-mask", &value, sizeof(cell_t));
+ sc->ti_syss_mask = value;
+ }
+ if (OF_hasprop(node, "ti,sysc-delay-us")) {
+ OF_getencprop(node, "ti,sysc-delay-us", &value, sizeof(cell_t));
+ sc->ti_sysc_delay_us = value;
+ }
+
+ DPRINTF(sc->dev, "sysc_mask %x syss_mask %x delay_us %x\n",
+ sc->ti_sysc_mask, sc->ti_syss_mask, sc->ti_sysc_delay_us);
+
+ parse_idle(sc, "ti,sysc-midle", sc->ti_sysc_midle);
+ parse_idle(sc, "ti,sysc-sidle", sc->ti_sysc_sidle);
+
+ if (OF_hasprop(node, "ti,no-reset-on-init"))
+ sc->ti_no_reset_on_init = true;
+ else
+ sc->ti_no_reset_on_init = false;
+
+ if (OF_hasprop(node, "ti,no-idle-on-init"))
+ sc->ti_no_idle_on_init = true;
+ else
+ sc->ti_no_idle_on_init = false;
+
+ if (OF_hasprop(node, "ti,no-idle"))
+ sc->ti_no_idle = true;
+ else
+ sc->ti_no_idle = false;
+
+ DPRINTF(sc->dev,
+ "no-reset-on-init %d, no-idle-on-init %d, no-idle %d\n",
+ sc->ti_no_reset_on_init,
+ sc->ti_no_idle_on_init,
+ sc->ti_no_idle);
+
+ if (OF_hasprop(node, "clocks")) {
+ struct clock_cell_info cell_info;
+ read_clock_cells(sc->dev, &cell_info);
+ free(cell_info.clock_cells, M_DEVBUF);
+ free(cell_info.clock_cells_ncells, M_DEVBUF);
+
+ sc->num_clocks = cell_info.num_real_clocks;
+ TAILQ_INIT(&sc->clk_list);
+
+ err = ti_sysc_attach_clocks(sc);
+ if (err) {
+ DPRINTF(sc->dev, "Failed to attach clocks\n");
+ return (bus_generic_attach(sc->dev));
+ }
+ }
+
+ err = ti_sysc_simplebus_attach_child(sc->dev);
+ if (err) {
+ DPRINTF(sc->dev, "ti_sysc_simplebus_attach_child %d\n",
+ err);
+ return (err);
+ }
+
+ sc->attach_done = true;
+
+ return (bus_generic_attach(sc->dev));
+}
+
+static int
+ti_sysc_detach(device_t dev)
+{
+ return (EBUSY);
+}
+
+/* Bus interface */
+static void
+ti_sysc_new_pass(device_t dev)
+{
+ struct ti_sysc_softc *sc;
+ int err;
+ phandle_t node;
+
+ sc = device_get_softc(dev);
+
+ if (sc->attach_done) {
+ bus_generic_new_pass(sc->dev);
+ return;
+ }
+
+ node = ofw_bus_get_node(sc->dev);
+ if (OF_hasprop(node, "clocks")) {
+ err = ti_sysc_attach_clocks(sc);
+ if (err) {
+ DPRINTF(sc->dev, "Failed to attach clocks\n");
+ return;
+ }
+ }
+
+ err = ti_sysc_simplebus_attach_child(sc->dev);
+ if (err) {
+ DPRINTF(sc->dev,
+ "ti_sysc_simplebus_attach_child failed %d\n", err);
+ return;
+ }
+ sc->attach_done = true;
+
+ bus_generic_attach(sc->dev);
+}
+
+static device_method_t ti_sysc_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ti_sysc_probe),
+ DEVMETHOD(device_attach, ti_sysc_attach),
+ DEVMETHOD(device_detach, ti_sysc_detach),
+
+ /* Bus interface */
+ DEVMETHOD(bus_new_pass, ti_sysc_new_pass),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(ti_sysc, ti_sysc_driver, ti_sysc_methods,
+ sizeof(struct ti_sysc_softc), simplebus_driver);
+
+static devclass_t ti_sysc_devclass;
+
+EARLY_DRIVER_MODULE(ti_sysc, simplebus, ti_sysc_driver,
+ ti_sysc_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_FIRST);
diff --git a/sys/arm/ti/ti_sysc.h b/sys/arm/ti/ti_sysc.h
new file mode 100644
index 000000000000..b74222f05772
--- /dev/null
+++ b/sys/arm/ti/ti_sysc.h
@@ -0,0 +1,43 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef __TI_SYSC__
+#define __TI_SYSC__
+
+uint64_t ti_sysc_get_rev_address(device_t dev);
+uint64_t ti_sysc_get_rev_address_offset_host(device_t dev);
+uint64_t ti_sysc_get_sysc_address(device_t dev);
+uint64_t ti_sysc_get_sysc_address_offset_host(device_t dev);
+uint64_t ti_sysc_get_syss_address(device_t dev);
+uint64_t ti_sysc_get_syss_address_offset_host(device_t dev);
+int ti_sysc_clock_enable(device_t dev);
+int ti_sysc_clock_disable(device_t dev);
+
+uint32_t ti_sysc_get_soft_reset_bit(device_t dev);
+
+#endif /* __TI_SYSC__ */
diff --git a/sys/arm/ti/ti_wdt.c b/sys/arm/ti/ti_wdt.c
new file mode 100644
index 000000000000..29ae41eac531
--- /dev/null
+++ b/sys/arm/ti/ti_wdt.c
@@ -0,0 +1,276 @@
+/*-
+ * Copyright (c) 2014 Rui Paulo <rpaulo@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/eventhandler.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/event.h>
+#include <sys/selinfo.h>
+#include <sys/watchdog.h>
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/frame.h>
+#include <machine/intr.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+
+#include <arm/ti/ti_wdt.h>
+
+#ifdef DEBUG
+#define DPRINTF(fmt, ...) do { \
+ printf("%s: ", __func__); \
+ printf(fmt, __VA_ARGS__); \
+} while (0)
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
+static device_probe_t ti_wdt_probe;
+static device_attach_t ti_wdt_attach;
+static device_detach_t ti_wdt_detach;
+static void ti_wdt_intr(void *);
+static void ti_wdt_event(void *, unsigned int, int *);
+
+struct ti_wdt_softc {
+ struct resource *sc_mem_res;
+ struct resource *sc_irq_res;
+ void *sc_intr;
+ bus_space_tag_t sc_bt;
+ bus_space_handle_t sc_bh;
+ eventhandler_tag sc_ev_tag;
+};
+
+static device_method_t ti_wdt_methods[] = {
+ DEVMETHOD(device_probe, ti_wdt_probe),
+ DEVMETHOD(device_attach, ti_wdt_attach),
+ DEVMETHOD(device_detach, ti_wdt_detach),
+
+ DEVMETHOD_END
+};
+
+static driver_t ti_wdt_driver = {
+ "ti_wdt",
+ ti_wdt_methods,
+ sizeof(struct ti_wdt_softc)
+};
+
+static devclass_t ti_wdt_devclass;
+
+DRIVER_MODULE(ti_wdt, simplebus, ti_wdt_driver, ti_wdt_devclass, 0, 0);
+MODULE_DEPEND(ti_wdt, ti_sysc, 1, 1, 1);
+
+static __inline uint32_t
+ti_wdt_reg_read(struct ti_wdt_softc *sc, uint32_t reg)
+{
+
+ return (bus_space_read_4(sc->sc_bt, sc->sc_bh, reg));
+}
+
+static __inline void
+ti_wdt_reg_write(struct ti_wdt_softc *sc, uint32_t reg, uint32_t val)
+{
+
+ bus_space_write_4(sc->sc_bt, sc->sc_bh, reg, val);
+}
+
+/*
+ * Wait for the write to a specific synchronised register to complete.
+ */
+static __inline void
+ti_wdt_reg_wait(struct ti_wdt_softc *sc, uint32_t bit)
+{
+
+ while (ti_wdt_reg_read(sc, TI_WDT_WWPS) & bit)
+ DELAY(10);
+}
+
+static __inline void
+ti_wdt_disable(struct ti_wdt_softc *sc)
+{
+
+ DPRINTF("disabling watchdog %p\n", sc);
+ ti_wdt_reg_write(sc, TI_WDT_WSPR, 0xAAAA);
+ ti_wdt_reg_wait(sc, TI_W_PEND_WSPR);
+ ti_wdt_reg_write(sc, TI_WDT_WSPR, 0x5555);
+ ti_wdt_reg_wait(sc, TI_W_PEND_WSPR);
+}
+
+static __inline void
+ti_wdt_enable(struct ti_wdt_softc *sc)
+{
+
+ DPRINTF("enabling watchdog %p\n", sc);
+ ti_wdt_reg_write(sc, TI_WDT_WSPR, 0xBBBB);
+ ti_wdt_reg_wait(sc, TI_W_PEND_WSPR);
+ ti_wdt_reg_write(sc, TI_WDT_WSPR, 0x4444);
+ ti_wdt_reg_wait(sc, TI_W_PEND_WSPR);
+}
+
+static int
+ti_wdt_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+ if (ofw_bus_is_compatible(dev, "ti,omap3-wdt")) {
+ device_set_desc(dev, "TI Watchdog Timer");
+ return (BUS_PROBE_DEFAULT);
+ }
+
+ return (ENXIO);
+}
+
+static int
+ti_wdt_attach(device_t dev)
+{
+ struct ti_wdt_softc *sc;
+ int rid;
+
+ sc = device_get_softc(dev);
+ rid = 0;
+ sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->sc_mem_res == NULL) {
+ device_printf(dev, "could not allocate memory resource\n");
+ return (ENXIO);
+ }
+ sc->sc_bt = rman_get_bustag(sc->sc_mem_res);
+ sc->sc_bh = rman_get_bushandle(sc->sc_mem_res);
+ sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
+ if (sc->sc_irq_res == NULL) {
+ device_printf(dev, "could not allocate interrupt resource\n");
+ ti_wdt_detach(dev);
+ return (ENXIO);
+ }
+ if (bus_setup_intr(dev, sc->sc_irq_res, INTR_MPSAFE | INTR_TYPE_MISC,
+ NULL, ti_wdt_intr, sc, &sc->sc_intr) != 0) {
+ device_printf(dev,
+ "unable to setup the interrupt handler\n");
+ ti_wdt_detach(dev);
+ return (ENXIO);
+ }
+ /* Reset, enable interrupts and stop the watchdog. */
+ ti_wdt_reg_write(sc, TI_WDT_WDSC,
+ ti_wdt_reg_read(sc, TI_WDT_WDSC) | TI_WDSC_SR);
+ while (ti_wdt_reg_read(sc, TI_WDT_WDSC) & TI_WDSC_SR)
+ DELAY(10);
+ ti_wdt_reg_write(sc, TI_WDT_WIRQENSET, TI_IRQ_EN_OVF | TI_IRQ_EN_DLY);
+ ti_wdt_disable(sc);
+ if (bootverbose)
+ device_printf(dev, "revision: 0x%x\n",
+ ti_wdt_reg_read(sc, TI_WDT_WIDR));
+ sc->sc_ev_tag = EVENTHANDLER_REGISTER(watchdog_list, ti_wdt_event, sc,
+ 0);
+
+ return (0);
+}
+
+static int
+ti_wdt_detach(device_t dev)
+{
+ struct ti_wdt_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (sc->sc_ev_tag)
+ EVENTHANDLER_DEREGISTER(watchdog_list, sc->sc_ev_tag);
+ if (sc->sc_intr)
+ bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr);
+ if (sc->sc_irq_res)
+ bus_release_resource(dev, SYS_RES_IRQ,
+ rman_get_rid(sc->sc_irq_res), sc->sc_irq_res);
+ if (sc->sc_mem_res)
+ bus_release_resource(dev, SYS_RES_MEMORY,
+ rman_get_rid(sc->sc_mem_res), sc->sc_mem_res);
+
+ return (0);
+}
+
+static void
+ti_wdt_intr(void *arg)
+{
+ struct ti_wdt_softc *sc;
+
+ sc = arg;
+ DPRINTF("interrupt %p", sc);
+ ti_wdt_reg_write(sc, TI_WDT_WIRQSTAT, TI_IRQ_EV_OVF | TI_IRQ_EV_DLY);
+ /* TODO: handle interrupt */
+}
+
+static void
+ti_wdt_event(void *arg, unsigned int cmd, int *error)
+{
+ struct ti_wdt_softc *sc;
+ uint8_t s;
+ uint32_t wldr;
+ uint32_t ptv;
+
+ sc = arg;
+ ti_wdt_disable(sc);
+ if (cmd == WD_TO_NEVER) {
+ *error = 0;
+ return;
+ }
+ DPRINTF("cmd 0x%x\n", cmd);
+ cmd &= WD_INTERVAL;
+ if (cmd < WD_TO_1SEC) {
+ *error = EINVAL;
+ return;
+ }
+ s = 1 << (cmd - WD_TO_1SEC);
+ DPRINTF("seconds %u\n", s);
+ /*
+ * Leave the pre-scaler with its default values:
+ * PTV = 0 == 2**0 == 1
+ * PRE = 1 (enabled)
+ *
+ * Compute the load register value assuming a 32kHz clock.
+ * See OVF_Rate in the WDT section of the AM335x TRM.
+ */
+ ptv = 0;
+ wldr = 0xffffffff - (s * (32768 / (1 << ptv))) + 1;
+ DPRINTF("wldr 0x%x\n", wldr);
+ ti_wdt_reg_write(sc, TI_WDT_WLDR, wldr);
+ /*
+ * Trigger a timer reload.
+ */
+ ti_wdt_reg_write(sc, TI_WDT_WTGR,
+ ti_wdt_reg_read(sc, TI_WDT_WTGR) + 1);
+ ti_wdt_reg_wait(sc, TI_W_PEND_WTGR);
+ ti_wdt_enable(sc);
+ *error = 0;
+}
diff --git a/sys/arm/ti/ti_wdt.h b/sys/arm/ti/ti_wdt.h
new file mode 100644
index 000000000000..425b16164fb8
--- /dev/null
+++ b/sys/arm/ti/ti_wdt.h
@@ -0,0 +1,74 @@
+/*-
+ * Copyright (c) 2014 Rui Paulo <rpaulo@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _TI_WDT_H_
+#define _TI_WDT_H_
+
+/* TI WDT registers */
+#define TI_WDT_WIDR 0x00 /* Watchdog Identification Register */
+#define TI_WDT_WDSC 0x10 /* Watchdog System Control Register */
+#define TI_WDT_WDST 0x14 /* Watchdog Status Register */
+#define TI_WDT_WISR 0x18 /* Watchdog Interrupt Status Register */
+#define TI_WDT_WIER 0x1c /* Watchdog Interrupt Enable Register */
+#define TI_WDT_WCLR 0x24 /* Watchdog Control Register */
+#define TI_WDT_WCRR 0x28 /* Watchdog Counter Register */
+#define TI_WDT_WLDR 0x2c /* Watchdog Load Register */
+#define TI_WDT_WTGR 0x30 /* Watchdog Trigger Register */
+#define TI_WDT_WWPS 0x34 /* Watchdog Write Posting Register */
+#define TI_WDT_WDLY 0x44 /* Watchdog Delay Configuration Reg */
+#define TI_WDT_WSPR 0x48 /* Watchdog Start/Stop Register */
+#define TI_WDT_WIRQSTATRAW 0x54 /* Watchdog Raw Interrupt Status Reg. */
+#define TI_WDT_WIRQSTAT 0x58 /* Watchdog Int. Status Register */
+#define TI_WDT_WIRQENSET 0x5c /* Watchdog Int. Enable Set Register */
+#define TI_WDT_WIRQENCLR 0x60 /* Watchdog Int. Enable Clear Reg. */
+
+/* WDT_WDSC Register */
+#define TI_WDSC_SR (1 << 1) /* Soft reset */
+
+/*
+ * WDT_WWPS Register
+ *
+ * Writes to some registers require synchronisation with a different clock
+ * domain. The WDT_WWPS register is the place where this synchronisation
+ * happens.
+ */
+#define TI_W_PEND_WCLR (1 << 0)
+#define TI_W_PEND_WCRR (1 << 1)
+#define TI_W_PEND_WLDR (1 << 2)
+#define TI_W_PEND_WTGR (1 << 3)
+#define TI_W_PEND_WSPR (1 << 4)
+#define TI_W_PEND_WDLY (1 << 5)
+
+/* WDT_WIRQENSET Register */
+#define TI_IRQ_EN_OVF (1 << 0) /* Overflow interrupt */
+#define TI_IRQ_EN_DLY (1 << 1) /* Delay interrupt */
+
+/* WDT_WIRQSTAT Register */
+#define TI_IRQ_EV_OVF (1 << 0) /* Overflow event */
+#define TI_IRQ_EV_DLY (1 << 1) /* Delay event */
+
+#endif /* _TI_WDT_H_ */
diff --git a/sys/arm/ti/tivar.h b/sys/arm/ti/tivar.h
new file mode 100644
index 000000000000..ecc0a4487947
--- /dev/null
+++ b/sys/arm/ti/tivar.h
@@ -0,0 +1,43 @@
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 2010
+ * Ben Gray <ben.r.gray@gmail.com>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Ben Gray.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BEN GRAY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _TIVAR_H_
+#define _TIVAR_H_
+
+/* board-dependent reset function implementation */
+extern void (*ti_cpu_reset)(void);
+
+#endif /* _TIVAR_H_ */
diff --git a/sys/arm/ti/twl/twl.c b/sys/arm/ti/twl/twl.c
new file mode 100644
index 000000000000..e1b20cbbaa5f
--- /dev/null
+++ b/sys/arm/ti/twl/twl.c
@@ -0,0 +1,458 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2011
+ * Ben Gray <ben.r.gray@gmail.com>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Texas Instruments TWL4030/TWL5030/TWL60x0/TPS659x0 Power Management and
+ * Audio CODEC devices.
+ *
+ * This code is based on the Linux TWL multifunctional device driver, which is
+ * copyright (C) 2005-2006 Texas Instruments, Inc.
+ *
+ * These chips are typically used as support ICs for the OMAP range of embedded
+ * ARM processes/SOC from Texas Instruments. They are typically used to control
+ * on board voltages, however some variants have other features like audio
+ * codecs, USB OTG transceivers, RTC, PWM, etc.
+ *
+ * This driver acts as a bus for more specific companion devices.
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+#include <sys/mutex.h>
+#include <sys/malloc.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/intr.h>
+
+#include <dev/iicbus/iicbus.h>
+#include <dev/iicbus/iiconf.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "arm/ti/twl/twl.h"
+
+/* TWL device IDs */
+#define TWL_DEVICE_UNKNOWN 0xffff
+#define TWL_DEVICE_4030 0x4030
+#define TWL_DEVICE_6025 0x6025
+#define TWL_DEVICE_6030 0x6030
+
+/* Each TWL device typically has more than one I2C address */
+#define TWL_MAX_SUBADDRS 4
+
+/* The maxium number of bytes that can be written in one call */
+#define TWL_MAX_IIC_DATA_SIZE 63
+
+/* The TWL devices typically use 4 I2C address for the different internal
+ * register sets, plus one SmartReflex I2C address.
+ */
+#define TWL_CHIP_ID0 0x48
+#define TWL_CHIP_ID1 0x49
+#define TWL_CHIP_ID2 0x4A
+#define TWL_CHIP_ID3 0x4B
+
+#define TWL_SMARTREFLEX_CHIP_ID 0x12
+
+#define TWL_INVALID_CHIP_ID 0xff
+
+struct twl_softc {
+ device_t sc_dev;
+ struct mtx sc_mtx;
+ unsigned int sc_type;
+
+ uint8_t sc_subaddr_map[TWL_MAX_SUBADDRS];
+
+ struct intr_config_hook sc_scan_hook;
+
+ device_t sc_vreg;
+ device_t sc_clks;
+};
+
+/**
+ * Macros for driver mutex locking
+ */
+#define TWL_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define TWL_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#define TWL_LOCK_INIT(_sc) \
+ mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \
+ "twl", MTX_DEF)
+#define TWL_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx);
+#define TWL_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED);
+#define TWL_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
+
+/**
+ * twl_is_4030 - returns true if the device is TWL4030
+ * twl_is_6025 - returns true if the device is TWL6025
+ * twl_is_6030 - returns true if the device is TWL6030
+ * @sc: device soft context
+ *
+ * Returns a non-zero value if the device matches.
+ *
+ * RETURNS:
+ * Returns a non-zero value if the device matches, otherwise zero.
+ */
+int
+twl_is_4030(device_t dev)
+{
+ struct twl_softc *sc = device_get_softc(dev);
+ return (sc->sc_type == TWL_DEVICE_4030);
+}
+
+int
+twl_is_6025(device_t dev)
+{
+ struct twl_softc *sc = device_get_softc(dev);
+ return (sc->sc_type == TWL_DEVICE_6025);
+}
+
+int
+twl_is_6030(device_t dev)
+{
+ struct twl_softc *sc = device_get_softc(dev);
+ return (sc->sc_type == TWL_DEVICE_6030);
+}
+
+/**
+ * twl_read - read one or more registers from the TWL device
+ * @sc: device soft context
+ * @nsub: the sub-module to read from
+ * @reg: the register offset within the module to read
+ * @buf: buffer to store the bytes in
+ * @cnt: the number of bytes to read
+ *
+ * Reads one or more registers and stores the result in the suppled buffer.
+ *
+ * RETURNS:
+ * Zero on success or an error code on failure.
+ */
+int
+twl_read(device_t dev, uint8_t nsub, uint8_t reg, uint8_t *buf, uint16_t cnt)
+{
+ struct twl_softc *sc;
+ struct iic_msg msg[2];
+ uint8_t addr;
+ int rc;
+
+ sc = device_get_softc(dev);
+
+ TWL_LOCK(sc);
+ addr = sc->sc_subaddr_map[nsub];
+ TWL_UNLOCK(sc);
+
+ if (addr == TWL_INVALID_CHIP_ID)
+ return (EIO);
+
+ /* Set the address to read from */
+ msg[0].slave = addr;
+ msg[0].flags = IIC_M_WR | IIC_M_NOSTOP;
+ msg[0].len = 1;
+ msg[0].buf = &reg;
+ /* Read the data back */
+ msg[1].slave = addr;
+ msg[1].flags = IIC_M_RD;
+ msg[1].len = cnt;
+ msg[1].buf = buf;
+
+ rc = iicbus_transfer(dev, msg, 2);
+ if (rc != 0) {
+ device_printf(dev, "iicbus read failed (adr:0x%02x, reg:0x%02x)\n",
+ addr, reg);
+ return (EIO);
+ }
+
+ return (0);
+}
+
+/**
+ * twl_write - writes one or more registers to the TWL device
+ * @sc: device soft context
+ * @nsub: the sub-module to read from
+ * @reg: the register offset within the module to read
+ * @buf: data to write
+ * @cnt: the number of bytes to write
+ *
+ * Writes one or more registers.
+ *
+ * RETURNS:
+ * Zero on success or a negative error code on failure.
+ */
+int
+twl_write(device_t dev, uint8_t nsub, uint8_t reg, uint8_t *buf, uint16_t cnt)
+{
+ struct twl_softc *sc;
+ struct iic_msg msg;
+ uint8_t addr;
+ uint8_t tmp_buf[TWL_MAX_IIC_DATA_SIZE + 1];
+ int rc;
+
+ if (cnt > TWL_MAX_IIC_DATA_SIZE)
+ return (ENOMEM);
+
+ /* Set the register address as the first byte */
+ tmp_buf[0] = reg;
+ memcpy(&tmp_buf[1], buf, cnt);
+
+ sc = device_get_softc(dev);
+
+ TWL_LOCK(sc);
+ addr = sc->sc_subaddr_map[nsub];
+ TWL_UNLOCK(sc);
+
+ if (addr == TWL_INVALID_CHIP_ID)
+ return (EIO);
+
+ /* Setup the transfer and execute it */
+ msg.slave = addr;
+ msg.flags = IIC_M_WR;
+ msg.len = cnt + 1;
+ msg.buf = tmp_buf;
+
+ rc = iicbus_transfer(dev, &msg, 1);
+ if (rc != 0) {
+ device_printf(sc->sc_dev, "iicbus write failed (adr:0x%02x, reg:0x%02x)\n",
+ addr, reg);
+ return (EIO);
+ }
+
+ return (0);
+}
+
+/**
+ * twl_test_present - checks if a device with given address is present
+ * @sc: device soft context
+ * @addr: the address of the device to scan for
+ *
+ * Sends just the address byte and checks for an ACK. If no ACK then device
+ * is assumed to not be present.
+ *
+ * RETURNS:
+ * EIO if device is not present, otherwise 0 is returned.
+ */
+static int
+twl_test_present(struct twl_softc *sc, uint8_t addr)
+{
+ struct iic_msg msg;
+ uint8_t tmp;
+
+ /* Set the address to read from */
+ msg.slave = addr;
+ msg.flags = IIC_M_RD;
+ msg.len = 1;
+ msg.buf = &tmp;
+
+ if (iicbus_transfer(sc->sc_dev, &msg, 1) != 0)
+ return (EIO);
+
+ return (0);
+}
+
+/**
+ * twl_scan - scans the i2c bus for sub modules
+ * @dev: the twl device
+ *
+ * TWL devices don't just have one i2c slave address, rather they have up to
+ * 5 other addresses, each is for separate modules within the device. This
+ * function scans the bus for 4 possible sub-devices and stores the info
+ * internally.
+ *
+ */
+static void
+twl_scan(void *dev)
+{
+ struct twl_softc *sc;
+ unsigned i;
+ uint8_t devs[TWL_MAX_SUBADDRS];
+ uint8_t base = TWL_CHIP_ID0;
+
+ sc = device_get_softc((device_t)dev);
+
+ memset(devs, TWL_INVALID_CHIP_ID, TWL_MAX_SUBADDRS);
+
+ /* Try each of the addresses (0x48, 0x49, 0x4a & 0x4b) to determine which
+ * sub modules we have.
+ */
+ for (i = 0; i < TWL_MAX_SUBADDRS; i++) {
+ if (twl_test_present(sc, (base + i)) == 0) {
+ devs[i] = (base + i);
+ device_printf(sc->sc_dev, "Found (sub)device at 0x%02x\n", (base + i));
+ }
+ }
+
+ TWL_LOCK(sc);
+ memcpy(sc->sc_subaddr_map, devs, TWL_MAX_SUBADDRS);
+ TWL_UNLOCK(sc);
+
+ /* Finished with the interrupt hook */
+ config_intrhook_disestablish(&sc->sc_scan_hook);
+}
+
+/**
+ * twl_probe -
+ * @dev: the twl device
+ *
+ * Scans the FDT for a match for the device, possible compatible device
+ * strings are; "ti,twl6030", "ti,twl6025", "ti,twl4030".
+ *
+ * The FDT compat string also determines the type of device (it is currently
+ * not possible to dynamically determine the device type).
+ *
+ */
+static int
+twl_probe(device_t dev)
+{
+ phandle_t node;
+ const char *compat;
+ int len, l;
+ struct twl_softc *sc;
+
+ if ((compat = ofw_bus_get_compat(dev)) == NULL)
+ return (ENXIO);
+
+ if ((node = ofw_bus_get_node(dev)) == 0)
+ return (ENXIO);
+
+ /* Get total 'compatible' prop len */
+ if ((len = OF_getproplen(node, "compatible")) <= 0)
+ return (ENXIO);
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+ sc->sc_type = TWL_DEVICE_UNKNOWN;
+
+ while (len > 0) {
+ if (strncasecmp(compat, "ti,twl6030", 10) == 0)
+ sc->sc_type = TWL_DEVICE_6030;
+ else if (strncasecmp(compat, "ti,twl6025", 10) == 0)
+ sc->sc_type = TWL_DEVICE_6025;
+ else if (strncasecmp(compat, "ti,twl4030", 10) == 0)
+ sc->sc_type = TWL_DEVICE_4030;
+
+ if (sc->sc_type != TWL_DEVICE_UNKNOWN)
+ break;
+
+ /* Slide to the next sub-string. */
+ l = strlen(compat) + 1;
+ compat += l;
+ len -= l;
+ }
+
+ switch (sc->sc_type) {
+ case TWL_DEVICE_4030:
+ device_set_desc(dev, "TI TWL4030/TPS659x0 Companion IC");
+ break;
+ case TWL_DEVICE_6025:
+ device_set_desc(dev, "TI TWL6025 Companion IC");
+ break;
+ case TWL_DEVICE_6030:
+ device_set_desc(dev, "TI TWL6030 Companion IC");
+ break;
+ case TWL_DEVICE_UNKNOWN:
+ default:
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static int
+twl_attach(device_t dev)
+{
+ struct twl_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+
+ TWL_LOCK_INIT(sc);
+
+ /* We have to wait until interrupts are enabled. I2C read and write
+ * only works if the interrupts are available.
+ */
+ sc->sc_scan_hook.ich_func = twl_scan;
+ sc->sc_scan_hook.ich_arg = dev;
+
+ if (config_intrhook_establish(&sc->sc_scan_hook) != 0)
+ return (ENOMEM);
+
+ /* FIXME: should be in DTS file */
+ if ((sc->sc_vreg = device_add_child(dev, "twl_vreg", -1)) == NULL)
+ device_printf(dev, "could not allocate twl_vreg instance\n");
+ if ((sc->sc_clks = device_add_child(dev, "twl_clks", -1)) == NULL)
+ device_printf(dev, "could not allocate twl_clks instance\n");
+
+ return (bus_generic_attach(dev));
+}
+
+static int
+twl_detach(device_t dev)
+{
+ struct twl_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (sc->sc_vreg)
+ device_delete_child(dev, sc->sc_vreg);
+ if (sc->sc_clks)
+ device_delete_child(dev, sc->sc_clks);
+
+ TWL_LOCK_DESTROY(sc);
+
+ return (0);
+}
+
+static device_method_t twl_methods[] = {
+ DEVMETHOD(device_probe, twl_probe),
+ DEVMETHOD(device_attach, twl_attach),
+ DEVMETHOD(device_detach, twl_detach),
+
+ {0, 0},
+};
+
+static driver_t twl_driver = {
+ "twl",
+ twl_methods,
+ sizeof(struct twl_softc),
+};
+static devclass_t twl_devclass;
+
+DRIVER_MODULE(twl, iicbus, twl_driver, twl_devclass, 0, 0);
+MODULE_VERSION(twl, 1);
diff --git a/sys/arm/ti/twl/twl.h b/sys/arm/ti/twl/twl.h
new file mode 100644
index 000000000000..44203a765623
--- /dev/null
+++ b/sys/arm/ti/twl/twl.h
@@ -0,0 +1,41 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2011
+ * Ben Gray <ben.r.gray@gmail.com>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _TWL_H_
+#define _TWL_H_
+
+int twl_read(device_t dev, uint8_t nsub, uint8_t reg, uint8_t *buf, uint16_t cnt);
+int twl_write(device_t dev, uint8_t nsub, uint8_t reg, uint8_t *buf, uint16_t cnt);
+
+int twl_is_4030(device_t dev);
+int twl_is_6025(device_t dev);
+int twl_is_6030(device_t dev);
+
+#endif /* _TWL_H_ */
diff --git a/sys/arm/ti/twl/twl_clks.c b/sys/arm/ti/twl/twl_clks.c
new file mode 100644
index 000000000000..fee2f2226a32
--- /dev/null
+++ b/sys/arm/ti/twl/twl_clks.c
@@ -0,0 +1,651 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012
+ * Ben Gray <bgray@freebsd.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Texas Instruments TWL4030/TWL5030/TWL60x0/TPS659x0 Power Management.
+ *
+ * This driver covers the external clocks, allows for enabling &
+ * disabling their output.
+ *
+ *
+ *
+ * FLATTENED DEVICE TREE (FDT)
+ * Startup override settings can be specified in the FDT, if they are they
+ * should be under the twl parent device and take the following form:
+ *
+ * external-clocks = "name1", "state1",
+ * "name2", "state2",
+ * etc;
+ *
+ * Each override should be a pair, the first entry is the name of the clock
+ * the second is the state to set, possible strings are either "on" or "off".
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <sys/malloc.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/intr.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+
+#include "twl.h"
+#include "twl_clks.h"
+
+static int twl_clks_debug = 1;
+
+/*
+ * Power Groups bits for the 4030 and 6030 devices
+ */
+#define TWL4030_P3_GRP 0x80 /* Peripherals, power group */
+#define TWL4030_P2_GRP 0x40 /* Modem power group */
+#define TWL4030_P1_GRP 0x20 /* Application power group (FreeBSD control) */
+
+#define TWL6030_P3_GRP 0x04 /* Modem power group */
+#define TWL6030_P2_GRP 0x02 /* Connectivity power group */
+#define TWL6030_P1_GRP 0x01 /* Application power group (FreeBSD control) */
+
+/*
+ * Register offsets within a clk regulator register set
+ */
+#define TWL_CLKS_GRP 0x00 /* Regulator GRP register */
+#define TWL_CLKS_STATE 0x02 /* TWL6030 only */
+
+/**
+ * Support voltage regulators for the different IC's
+ */
+struct twl_clock {
+ const char *name;
+ uint8_t subdev;
+ uint8_t regbase;
+};
+
+static const struct twl_clock twl4030_clocks[] = {
+ { "32kclkout", 0, 0x8e },
+ { NULL, 0, 0x00 }
+};
+
+static const struct twl_clock twl6030_clocks[] = {
+ { "clk32kg", 0, 0xbc },
+ { "clk32kao", 0, 0xb9 },
+ { "clk32kaudio", 0, 0xbf },
+ { NULL, 0, 0x00 }
+};
+
+#define TWL_CLKS_MAX_NAMELEN 32
+
+struct twl_clk_entry {
+ LIST_ENTRY(twl_clk_entry) link;
+ struct sysctl_oid *oid;
+ char name[TWL_CLKS_MAX_NAMELEN];
+ uint8_t sub_dev; /* the sub-device number for the clock */
+ uint8_t reg_off; /* register base address of the clock */
+};
+
+struct twl_clks_softc {
+ device_t sc_dev; /* twl_clk device */
+ device_t sc_pdev; /* parent device (twl) */
+ struct sx sc_sx; /* internal locking */
+ struct intr_config_hook sc_init_hook;
+ LIST_HEAD(twl_clk_list, twl_clk_entry) sc_clks_list;
+};
+
+/**
+ * Macros for driver shared locking
+ */
+#define TWL_CLKS_XLOCK(_sc) sx_xlock(&(_sc)->sc_sx)
+#define TWL_CLKS_XUNLOCK(_sc) sx_xunlock(&(_sc)->sc_sx)
+#define TWL_CLKS_SLOCK(_sc) sx_slock(&(_sc)->sc_sx)
+#define TWL_CLKS_SUNLOCK(_sc) sx_sunlock(&(_sc)->sc_sx)
+#define TWL_CLKS_LOCK_INIT(_sc) sx_init(&(_sc)->sc_sx, "twl_clks")
+#define TWL_CLKS_LOCK_DESTROY(_sc) sx_destroy(&(_sc)->sc_sx);
+
+#define TWL_CLKS_ASSERT_LOCKED(_sc) sx_assert(&(_sc)->sc_sx, SA_LOCKED);
+
+#define TWL_CLKS_LOCK_UPGRADE(_sc) \
+ do { \
+ while (!sx_try_upgrade(&(_sc)->sc_sx)) \
+ pause("twl_clks_ex", (hz / 100)); \
+ } while(0)
+#define TWL_CLKS_LOCK_DOWNGRADE(_sc) sx_downgrade(&(_sc)->sc_sx);
+
+/**
+ * twl_clks_read_1 - read single register from the TWL device
+ * twl_clks_write_1 - writes a single register in the TWL device
+ * @sc: device context
+ * @clk: the clock device we're reading from / writing to
+ * @off: offset within the clock's register set
+ * @val: the value to write or a pointer to a variable to store the result
+ *
+ * RETURNS:
+ * Zero on success or an error code on failure.
+ */
+static inline int
+twl_clks_read_1(struct twl_clks_softc *sc, struct twl_clk_entry *clk,
+ uint8_t off, uint8_t *val)
+{
+ return (twl_read(sc->sc_pdev, clk->sub_dev, clk->reg_off + off, val, 1));
+}
+
+static inline int
+twl_clks_write_1(struct twl_clks_softc *sc, struct twl_clk_entry *clk,
+ uint8_t off, uint8_t val)
+{
+ return (twl_write(sc->sc_pdev, clk->sub_dev, clk->reg_off + off, &val, 1));
+}
+
+/**
+ * twl_clks_is_enabled - determines if a clock is enabled
+ * @dev: TWL CLK device
+ * @name: the name of the clock
+ * @enabled: upon return will contain the 'enabled' state
+ *
+ * LOCKING:
+ * Internally the function takes and releases the TWL lock.
+ *
+ * RETURNS:
+ * Zero on success or a negative error code on failure.
+ */
+int
+twl_clks_is_enabled(device_t dev, const char *name, int *enabled)
+{
+ struct twl_clks_softc *sc = device_get_softc(dev);
+ struct twl_clk_entry *clk;
+ int found = 0;
+ int err;
+ uint8_t grp, state;
+
+ TWL_CLKS_SLOCK(sc);
+
+ LIST_FOREACH(clk, &sc->sc_clks_list, link) {
+ if (strcmp(clk->name, name) == 0) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ TWL_CLKS_SUNLOCK(sc);
+ return (EINVAL);
+ }
+
+ if (twl_is_4030(sc->sc_pdev)) {
+ err = twl_clks_read_1(sc, clk, TWL_CLKS_GRP, &grp);
+ if (!err)
+ *enabled = (grp & TWL4030_P1_GRP);
+
+ } else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev)) {
+ TWL_CLKS_LOCK_UPGRADE(sc);
+
+ /* Check the clock is in the application group */
+ if (twl_is_6030(sc->sc_pdev)) {
+ err = twl_clks_read_1(sc, clk, TWL_CLKS_GRP, &grp);
+ if (err) {
+ TWL_CLKS_LOCK_DOWNGRADE(sc);
+ goto done;
+ }
+
+ if (!(grp & TWL6030_P1_GRP)) {
+ TWL_CLKS_LOCK_DOWNGRADE(sc);
+ *enabled = 0; /* disabled */
+ goto done;
+ }
+ }
+
+ /* Read the application mode state and verify it's ON */
+ err = twl_clks_read_1(sc, clk, TWL_CLKS_STATE, &state);
+ if (!err)
+ *enabled = ((state & 0x0C) == 0x04);
+
+ TWL_CLKS_LOCK_DOWNGRADE(sc);
+
+ } else {
+ err = EINVAL;
+ }
+
+done:
+ TWL_CLKS_SUNLOCK(sc);
+ return (err);
+}
+
+/**
+ * twl_clks_set_state - enables/disables a clock output
+ * @sc: device context
+ * @clk: the clock entry to enable/disable
+ * @enable: non-zero the clock is enabled, zero the clock is disabled
+ *
+ * LOCKING:
+ * The TWL CLK lock must be held before this function is called.
+ *
+ * RETURNS:
+ * Zero on success or an error code on failure.
+ */
+static int
+twl_clks_set_state(struct twl_clks_softc *sc, struct twl_clk_entry *clk,
+ int enable)
+{
+ int xlocked;
+ int err;
+ uint8_t grp;
+
+ TWL_CLKS_ASSERT_LOCKED(sc);
+
+ /* Upgrade the lock to exclusive because about to perform read-mod-write */
+ xlocked = sx_xlocked(&sc->sc_sx);
+ if (!xlocked)
+ TWL_CLKS_LOCK_UPGRADE(sc);
+
+ err = twl_clks_read_1(sc, clk, TWL_CLKS_GRP, &grp);
+ if (err)
+ goto done;
+
+ if (twl_is_4030(sc->sc_pdev)) {
+ /* On the TWL4030 we just need to ensure the clock is in the right
+ * power domain, don't need to turn on explicitly like TWL6030.
+ */
+ if (enable)
+ grp |= TWL4030_P1_GRP;
+ else
+ grp &= ~(TWL4030_P1_GRP | TWL4030_P2_GRP | TWL4030_P3_GRP);
+
+ err = twl_clks_write_1(sc, clk, TWL_CLKS_GRP, grp);
+
+ } else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev)) {
+ /* Make sure the clock belongs to at least the APP power group */
+ if (twl_is_6030(sc->sc_pdev) && !(grp & TWL6030_P1_GRP)) {
+ grp |= TWL6030_P1_GRP;
+ err = twl_clks_write_1(sc, clk, TWL_CLKS_GRP, grp);
+ if (err)
+ goto done;
+ }
+
+ /* On TWL6030 we need to make sure we disable power for all groups */
+ if (twl_is_6030(sc->sc_pdev))
+ grp = TWL6030_P1_GRP | TWL6030_P2_GRP | TWL6030_P3_GRP;
+ else
+ grp = 0x00;
+
+ /* Set the state of the clock */
+ if (enable)
+ err = twl_clks_write_1(sc, clk, TWL_CLKS_STATE, (grp << 5) | 0x01);
+ else
+ err = twl_clks_write_1(sc, clk, TWL_CLKS_STATE, (grp << 5));
+
+ } else {
+
+ err = EINVAL;
+ }
+
+done:
+ if (!xlocked)
+ TWL_CLKS_LOCK_DOWNGRADE(sc);
+
+ if ((twl_clks_debug > 1) && !err)
+ device_printf(sc->sc_dev, "%s : %sabled\n", clk->name,
+ enable ? "en" : "dis");
+
+ return (err);
+}
+
+/**
+ * twl_clks_disable - disables a clock output
+ * @dev: TWL clk device
+* @name: the name of the clock
+ *
+ * LOCKING:
+ * Internally the function takes and releases the TWL lock.
+ *
+ * RETURNS:
+* Zero on success or an error code on failure.
+ */
+int
+twl_clks_disable(device_t dev, const char *name)
+{
+ struct twl_clks_softc *sc = device_get_softc(dev);
+ struct twl_clk_entry *clk;
+ int err = EINVAL;
+
+ TWL_CLKS_SLOCK(sc);
+
+ LIST_FOREACH(clk, &sc->sc_clks_list, link) {
+ if (strcmp(clk->name, name) == 0) {
+ err = twl_clks_set_state(sc, clk, 0);
+ break;
+ }
+ }
+
+ TWL_CLKS_SUNLOCK(sc);
+ return (err);
+}
+
+/**
+ * twl_clks_enable - enables a clock output
+ * @dev: TWL clk device
+ * @name: the name of the clock
+ *
+ * LOCKING:
+ * Internally the function takes and releases the TWL CLKS lock.
+ *
+ * RETURNS:
+ * Zero on success or an error code on failure.
+ */
+int
+twl_clks_enable(device_t dev, const char *name)
+{
+ struct twl_clks_softc *sc = device_get_softc(dev);
+ struct twl_clk_entry *clk;
+ int err = EINVAL;
+
+ TWL_CLKS_SLOCK(sc);
+
+ LIST_FOREACH(clk, &sc->sc_clks_list, link) {
+ if (strcmp(clk->name, name) == 0) {
+ err = twl_clks_set_state(sc, clk, 1);
+ break;
+ }
+ }
+
+ TWL_CLKS_SUNLOCK(sc);
+ return (err);
+}
+
+/**
+ * twl_clks_sysctl_clock - reads the state of the clock
+ * @SYSCTL_HANDLER_ARGS: arguments for the callback
+ *
+ * Returns the clock status; disabled is zero and enabled is non-zero.
+ *
+ * LOCKING:
+ * It's expected the TWL lock is held while this function is called.
+ *
+ * RETURNS:
+ * EIO if device is not present, otherwise 0 is returned.
+ */
+static int
+twl_clks_sysctl_clock(SYSCTL_HANDLER_ARGS)
+{
+ struct twl_clks_softc *sc = (struct twl_clks_softc*)arg1;
+ int err;
+ int enabled = 0;
+
+ if ((err = twl_clks_is_enabled(sc->sc_dev, oidp->oid_name, &enabled)) != 0)
+ return err;
+
+ return sysctl_handle_int(oidp, &enabled, 0, req);
+}
+
+/**
+ * twl_clks_add_clock - adds single clock sysctls for the device
+ * @sc: device soft context
+ * @name: the name of the regulator
+ * @nsub: the number of the subdevice
+ * @regbase: the base address of the clocks registers
+ *
+ * Adds a single clock to the device and also a sysctl interface for
+ * querying it's status.
+ *
+ * LOCKING:
+ * It's expected the exclusive lock is held while this function is called.
+ *
+ * RETURNS:
+ * Pointer to the new clock entry on success, otherwise NULL on failure.
+ */
+static struct twl_clk_entry*
+twl_clks_add_clock(struct twl_clks_softc *sc, const char *name,
+ uint8_t nsub, uint8_t regbase)
+{
+ struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
+ struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
+ struct twl_clk_entry *new;
+
+ TWL_CLKS_ASSERT_LOCKED(sc);
+
+ new = malloc(sizeof(struct twl_clk_entry), M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (new == NULL)
+ return (NULL);
+
+ strncpy(new->name, name, TWL_CLKS_MAX_NAMELEN);
+ new->name[TWL_CLKS_MAX_NAMELEN - 1] = '\0';
+
+ new->sub_dev = nsub;
+ new->reg_off = regbase;
+
+ /* Add a sysctl entry for the clock */
+ new->oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, name,
+ CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, 0,
+ twl_clks_sysctl_clock, "I", "external clock");
+
+ /* Finally add the regulator to list of supported regulators */
+ LIST_INSERT_HEAD(&sc->sc_clks_list, new, link);
+
+ return (new);
+}
+
+/**
+ * twl_clks_add_clocks - populates the internal list of clocks
+ * @sc: device soft context
+ * @chip: the name of the chip used in the hints
+ * @clks the list of clocks supported by the device
+ *
+ * Loops over the list of clocks and adds them to the device context. Also
+ * scans the FDT to determine if there are any clocks that should be
+ * enabled/disabled automatically.
+ *
+ * LOCKING:
+ * Internally takes the exclusive lock while adding the clocks to the
+ * device context.
+ *
+ * RETURNS:
+ * Always returns 0.
+ */
+static int
+twl_clks_add_clocks(struct twl_clks_softc *sc, const struct twl_clock *clks)
+{
+ int err;
+ const struct twl_clock *walker;
+ struct twl_clk_entry *entry;
+ phandle_t child;
+ char rnames[256];
+ char *name, *state;
+ int len = 0, prop_len;
+ int enable;
+
+ TWL_CLKS_XLOCK(sc);
+
+ /* Add the regulators from the list */
+ walker = &clks[0];
+ while (walker->name != NULL) {
+ /* Add the regulator to the list */
+ entry = twl_clks_add_clock(sc, walker->name, walker->subdev,
+ walker->regbase);
+ if (entry == NULL)
+ continue;
+
+ walker++;
+ }
+
+ /* Check for any FDT settings that need to be applied */
+ child = ofw_bus_get_node(sc->sc_pdev);
+ if (child) {
+ prop_len = OF_getprop(child, "external-clocks", rnames, sizeof(rnames));
+ while (len < prop_len) {
+ name = rnames + len;
+ len += strlen(name) + 1;
+ if ((len >= prop_len) || (name[0] == '\0'))
+ break;
+
+ state = rnames + len;
+ len += strlen(state) + 1;
+ if (state[0] == '\0')
+ break;
+
+ enable = !strncmp(state, "on", 2);
+
+ LIST_FOREACH(entry, &sc->sc_clks_list, link) {
+ if (strcmp(entry->name, name) == 0) {
+ twl_clks_set_state(sc, entry, enable);
+ break;
+ }
+ }
+ }
+ }
+
+ TWL_CLKS_XUNLOCK(sc);
+
+ if (twl_clks_debug) {
+ LIST_FOREACH(entry, &sc->sc_clks_list, link) {
+ err = twl_clks_is_enabled(sc->sc_dev, entry->name, &enable);
+ if (!err)
+ device_printf(sc->sc_dev, "%s : %s\n", entry->name,
+ enable ? "on" : "off");
+ }
+ }
+
+ return (0);
+}
+
+/**
+ * twl_clks_init - initialises the list of clocks
+ * @dev: the twl_clks device
+ *
+ * This function is called as an intrhook once interrupts have been enabled,
+ * this is done so that the driver has the option to enable/disable a clock
+ * based on settings providied in the FDT.
+ *
+ * LOCKING:
+ * May takes the exclusive lock in the function.
+ */
+static void
+twl_clks_init(void *dev)
+{
+ struct twl_clks_softc *sc;
+
+ sc = device_get_softc((device_t)dev);
+
+ if (twl_is_4030(sc->sc_pdev))
+ twl_clks_add_clocks(sc, twl4030_clocks);
+ else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev))
+ twl_clks_add_clocks(sc, twl6030_clocks);
+
+ config_intrhook_disestablish(&sc->sc_init_hook);
+}
+
+static int
+twl_clks_probe(device_t dev)
+{
+ if (twl_is_4030(device_get_parent(dev)))
+ device_set_desc(dev, "TI TWL4030 PMIC External Clocks");
+ else if (twl_is_6025(device_get_parent(dev)) ||
+ twl_is_6030(device_get_parent(dev)))
+ device_set_desc(dev, "TI TWL6025/TWL6030 PMIC External Clocks");
+ else
+ return (ENXIO);
+
+ return (0);
+}
+
+static int
+twl_clks_attach(device_t dev)
+{
+ struct twl_clks_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+ sc->sc_pdev = device_get_parent(dev);
+
+ TWL_CLKS_LOCK_INIT(sc);
+
+ LIST_INIT(&sc->sc_clks_list);
+
+ sc->sc_init_hook.ich_func = twl_clks_init;
+ sc->sc_init_hook.ich_arg = dev;
+
+ if (config_intrhook_establish(&sc->sc_init_hook) != 0)
+ return (ENOMEM);
+
+ return (0);
+}
+
+static int
+twl_clks_detach(device_t dev)
+{
+ struct twl_clks_softc *sc;
+ struct twl_clk_entry *clk;
+ struct twl_clk_entry *tmp;
+
+ sc = device_get_softc(dev);
+
+ TWL_CLKS_XLOCK(sc);
+
+ LIST_FOREACH_SAFE(clk, &sc->sc_clks_list, link, tmp) {
+ LIST_REMOVE(clk, link);
+ sysctl_remove_oid(clk->oid, 1, 0);
+ free(clk, M_DEVBUF);
+ }
+
+ TWL_CLKS_XUNLOCK(sc);
+
+ TWL_CLKS_LOCK_DESTROY(sc);
+
+ return (0);
+}
+
+static device_method_t twl_clks_methods[] = {
+ DEVMETHOD(device_probe, twl_clks_probe),
+ DEVMETHOD(device_attach, twl_clks_attach),
+ DEVMETHOD(device_detach, twl_clks_detach),
+
+ {0, 0},
+};
+
+static driver_t twl_clks_driver = {
+ "twl_clks",
+ twl_clks_methods,
+ sizeof(struct twl_clks_softc),
+};
+
+static devclass_t twl_clks_devclass;
+
+DRIVER_MODULE(twl_clks, twl, twl_clks_driver, twl_clks_devclass, 0, 0);
+MODULE_VERSION(twl_clks, 1);
diff --git a/sys/arm/ti/twl/twl_clks.h b/sys/arm/ti/twl/twl_clks.h
new file mode 100644
index 000000000000..85aeb4368e4f
--- /dev/null
+++ b/sys/arm/ti/twl/twl_clks.h
@@ -0,0 +1,38 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012
+ * Ben Gray <bgray@freebsd.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _TWL_CLKS_H_
+#define _TWL_CLKS_H_
+
+int twl_clks_enable(device_t dev, const char *name);
+int twl_clks_disable(device_t dev, const char *name);
+int twl_clks_is_enabled(device_t dev, const char *name, int *enabled);
+
+#endif /* _TWL_CLKS_H_ */
diff --git a/sys/arm/ti/twl/twl_vreg.c b/sys/arm/ti/twl/twl_vreg.c
new file mode 100644
index 000000000000..6d78f053624b
--- /dev/null
+++ b/sys/arm/ti/twl/twl_vreg.c
@@ -0,0 +1,1030 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2011
+ * Ben Gray <ben.r.gray@gmail.com>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Texas Instruments TWL4030/TWL5030/TWL60x0/TPS659x0 Power Management.
+ *
+ * This driver covers the voltages regulators (LDO), allows for enabling &
+ * disabling the voltage output and adjusting the voltage level.
+ *
+ * Voltage regulators can belong to different power groups, in this driver we
+ * put the regulators under our control in the "Application power group".
+ *
+ *
+ * FLATTENED DEVICE TREE (FDT)
+ * Startup override settings can be specified in the FDT, if they are they
+ * should be under the twl parent device and take the following form:
+ *
+ * voltage-regulators = "name1", "millivolts1",
+ * "name2", "millivolts2";
+ *
+ * Each override should be a pair, the first entry is the name of the regulator
+ * the second is the voltage (in millivolts) to set for the given regulator.
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <sys/malloc.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/intr.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+
+#include "twl.h"
+#include "twl_vreg.h"
+
+static int twl_vreg_debug = 1;
+
+/*
+ * Power Groups bits for the 4030 and 6030 devices
+ */
+#define TWL4030_P3_GRP 0x80 /* Peripherals, power group */
+#define TWL4030_P2_GRP 0x40 /* Modem power group */
+#define TWL4030_P1_GRP 0x20 /* Application power group (FreeBSD control) */
+
+#define TWL6030_P3_GRP 0x04 /* Modem power group */
+#define TWL6030_P2_GRP 0x02 /* Connectivity power group */
+#define TWL6030_P1_GRP 0x01 /* Application power group (FreeBSD control) */
+
+/*
+ * Register offsets within a LDO regulator register set
+ */
+#define TWL_VREG_GRP 0x00 /* Regulator GRP register */
+#define TWL_VREG_STATE 0x02
+#define TWL_VREG_VSEL 0x03 /* Voltage select register */
+
+#define UNDF 0xFFFF
+
+static const uint16_t twl6030_voltages[] = {
+ 0000, 1000, 1100, 1200, 1300, 1400, 1500, 1600,
+ 1700, 1800, 1900, 2000, 2100, 2200, 2300, 2400,
+ 2500, 2600, 2700, 2800, 2900, 3000, 3100, 3200,
+ 3300, UNDF, UNDF, UNDF, UNDF, UNDF, UNDF, 2750
+};
+
+static const uint16_t twl4030_vaux1_voltages[] = {
+ 1500, 1800, 2500, 2800, 3000, 3000, 3000, 3000
+};
+static const uint16_t twl4030_vaux2_voltages[] = {
+ 1700, 1700, 1900, 1300, 1500, 1800, 2000, 2500,
+ 2100, 2800, 2200, 2300, 2400, 2400, 2400, 2400
+};
+static const uint16_t twl4030_vaux3_voltages[] = {
+ 1500, 1800, 2500, 2800, 3000, 3000, 3000, 3000
+};
+static const uint16_t twl4030_vaux4_voltages[] = {
+ 700, 1000, 1200, 1300, 1500, 1800, 1850, 2500,
+ 2600, 2800, 2850, 3000, 3150, 3150, 3150, 3150
+};
+static const uint16_t twl4030_vmmc1_voltages[] = {
+ 1850, 2850, 3000, 3150
+};
+static const uint16_t twl4030_vmmc2_voltages[] = {
+ 1000, 1000, 1200, 1300, 1500, 1800, 1850, 2500,
+ 2600, 2800, 2850, 3000, 3150, 3150, 3150, 3150
+};
+static const uint16_t twl4030_vpll1_voltages[] = {
+ 1000, 1200, 1300, 1800, 2800, 3000, 3000, 3000
+};
+static const uint16_t twl4030_vpll2_voltages[] = {
+ 700, 1000, 1200, 1300, 1500, 1800, 1850, 2500,
+ 2600, 2800, 2850, 3000, 3150, 3150, 3150, 3150
+};
+static const uint16_t twl4030_vsim_voltages[] = {
+ 1000, 1200, 1300, 1800, 2800, 3000, 3000, 3000
+};
+static const uint16_t twl4030_vdac_voltages[] = {
+ 1200, 1300, 1800, 1800
+};
+#if 0 /* vdd1, vdd2, vdio, not currently used. */
+static const uint16_t twl4030_vdd1_voltages[] = {
+ 800, 1450
+};
+static const uint16_t twl4030_vdd2_voltages[] = {
+ 800, 1450, 1500
+};
+static const uint16_t twl4030_vio_voltages[] = {
+ 1800, 1850
+};
+#endif
+static const uint16_t twl4030_vintana2_voltages[] = {
+ 2500, 2750
+};
+
+/**
+ * Support voltage regulators for the different IC's
+ */
+struct twl_regulator {
+ const char *name;
+ uint8_t subdev;
+ uint8_t regbase;
+
+ uint16_t fixedvoltage;
+
+ const uint16_t *voltages;
+ uint32_t num_voltages;
+};
+
+#define TWL_REGULATOR_ADJUSTABLE(name, subdev, reg, voltages) \
+ { name, subdev, reg, 0, voltages, (sizeof(voltages)/sizeof(voltages[0])) }
+#define TWL_REGULATOR_FIXED(name, subdev, reg, voltage) \
+ { name, subdev, reg, voltage, NULL, 0 }
+
+static const struct twl_regulator twl4030_regulators[] = {
+ TWL_REGULATOR_ADJUSTABLE("vaux1", 0, 0x17, twl4030_vaux1_voltages),
+ TWL_REGULATOR_ADJUSTABLE("vaux2", 0, 0x1B, twl4030_vaux2_voltages),
+ TWL_REGULATOR_ADJUSTABLE("vaux3", 0, 0x1F, twl4030_vaux3_voltages),
+ TWL_REGULATOR_ADJUSTABLE("vaux4", 0, 0x23, twl4030_vaux4_voltages),
+ TWL_REGULATOR_ADJUSTABLE("vmmc1", 0, 0x27, twl4030_vmmc1_voltages),
+ TWL_REGULATOR_ADJUSTABLE("vmmc2", 0, 0x2B, twl4030_vmmc2_voltages),
+ TWL_REGULATOR_ADJUSTABLE("vpll1", 0, 0x2F, twl4030_vpll1_voltages),
+ TWL_REGULATOR_ADJUSTABLE("vpll2", 0, 0x33, twl4030_vpll2_voltages),
+ TWL_REGULATOR_ADJUSTABLE("vsim", 0, 0x37, twl4030_vsim_voltages),
+ TWL_REGULATOR_ADJUSTABLE("vdac", 0, 0x3B, twl4030_vdac_voltages),
+ TWL_REGULATOR_ADJUSTABLE("vintana2", 0, 0x43, twl4030_vintana2_voltages),
+ TWL_REGULATOR_FIXED("vintana1", 0, 0x3F, 1500),
+ TWL_REGULATOR_FIXED("vintdig", 0, 0x47, 1500),
+ TWL_REGULATOR_FIXED("vusb1v5", 0, 0x71, 1500),
+ TWL_REGULATOR_FIXED("vusb1v8", 0, 0x74, 1800),
+ TWL_REGULATOR_FIXED("vusb3v1", 0, 0x77, 3100),
+ { NULL, 0, 0x00, 0, NULL, 0 }
+};
+
+static const struct twl_regulator twl6030_regulators[] = {
+ TWL_REGULATOR_ADJUSTABLE("vaux1", 0, 0x84, twl6030_voltages),
+ TWL_REGULATOR_ADJUSTABLE("vaux2", 0, 0x89, twl6030_voltages),
+ TWL_REGULATOR_ADJUSTABLE("vaux3", 0, 0x8C, twl6030_voltages),
+ TWL_REGULATOR_ADJUSTABLE("vmmc", 0, 0x98, twl6030_voltages),
+ TWL_REGULATOR_ADJUSTABLE("vpp", 0, 0x9C, twl6030_voltages),
+ TWL_REGULATOR_ADJUSTABLE("vusim", 0, 0xA4, twl6030_voltages),
+ TWL_REGULATOR_FIXED("vmem", 0, 0x64, 1800),
+ TWL_REGULATOR_FIXED("vusb", 0, 0xA0, 3300),
+ TWL_REGULATOR_FIXED("v1v8", 0, 0x46, 1800),
+ TWL_REGULATOR_FIXED("v2v1", 0, 0x4C, 2100),
+ TWL_REGULATOR_FIXED("v1v29", 0, 0x40, 1290),
+ TWL_REGULATOR_FIXED("vcxio", 0, 0x90, 1800),
+ TWL_REGULATOR_FIXED("vdac", 0, 0x94, 1800),
+ TWL_REGULATOR_FIXED("vana", 0, 0x80, 2100),
+ { NULL, 0, 0x00, 0, NULL, 0 }
+};
+
+#define TWL_VREG_MAX_NAMELEN 32
+
+struct twl_regulator_entry {
+ LIST_ENTRY(twl_regulator_entry) entries;
+ char name[TWL_VREG_MAX_NAMELEN];
+ struct sysctl_oid *oid;
+ uint8_t sub_dev; /* TWL sub-device group */
+ uint8_t reg_off; /* base register offset for the LDO */
+ uint16_t fixed_voltage; /* the (milli)voltage if LDO is fixed */
+ const uint16_t *supp_voltages; /* pointer to an array of possible voltages */
+ uint32_t num_supp_voltages; /* the number of supplied voltages */
+};
+
+struct twl_vreg_softc {
+ device_t sc_dev;
+ device_t sc_pdev;
+ struct sx sc_sx;
+
+ struct intr_config_hook sc_init_hook;
+ LIST_HEAD(twl_regulator_list, twl_regulator_entry) sc_vreg_list;
+};
+
+#define TWL_VREG_XLOCK(_sc) sx_xlock(&(_sc)->sc_sx)
+#define TWL_VREG_XUNLOCK(_sc) sx_xunlock(&(_sc)->sc_sx)
+#define TWL_VREG_SLOCK(_sc) sx_slock(&(_sc)->sc_sx)
+#define TWL_VREG_SUNLOCK(_sc) sx_sunlock(&(_sc)->sc_sx)
+#define TWL_VREG_LOCK_INIT(_sc) sx_init(&(_sc)->sc_sx, "twl_vreg")
+#define TWL_VREG_LOCK_DESTROY(_sc) sx_destroy(&(_sc)->sc_sx);
+
+#define TWL_VREG_ASSERT_LOCKED(_sc) sx_assert(&(_sc)->sc_sx, SA_LOCKED);
+
+#define TWL_VREG_LOCK_UPGRADE(_sc) \
+ do { \
+ while (!sx_try_upgrade(&(_sc)->sc_sx)) \
+ pause("twl_vreg_ex", (hz / 100)); \
+ } while(0)
+#define TWL_VREG_LOCK_DOWNGRADE(_sc) sx_downgrade(&(_sc)->sc_sx);
+
+/**
+ * twl_vreg_read_1 - read single register from the TWL device
+ * twl_vreg_write_1 - write a single register in the TWL device
+ * @sc: device context
+ * @clk: the clock device we're reading from / writing to
+ * @off: offset within the clock's register set
+ * @val: the value to write or a pointer to a variable to store the result
+ *
+ * RETURNS:
+ * Zero on success or an error code on failure.
+ */
+static inline int
+twl_vreg_read_1(struct twl_vreg_softc *sc, struct twl_regulator_entry *regulator,
+ uint8_t off, uint8_t *val)
+{
+ return (twl_read(sc->sc_pdev, regulator->sub_dev,
+ regulator->reg_off + off, val, 1));
+}
+
+static inline int
+twl_vreg_write_1(struct twl_vreg_softc *sc, struct twl_regulator_entry *regulator,
+ uint8_t off, uint8_t val)
+{
+ return (twl_write(sc->sc_pdev, regulator->sub_dev,
+ regulator->reg_off + off, &val, 1));
+}
+
+/**
+ * twl_millivolt_to_vsel - gets the vsel bit value to write into the register
+ * for a desired voltage and regulator
+ * @sc: the device soft context
+ * @regulator: pointer to the regulator device
+ * @millivolts: the millivolts to find the bit value for
+ * @vsel: upon return will contain the corresponding register value
+ *
+ * Accepts a (milli)voltage value and tries to find the closest match to the
+ * actual supported voltages for the given regulator. If a match is found
+ * within 100mv of the target, @vsel is written with the match and 0 is
+ * returned. If no voltage match is found the function returns an non-zero
+ * value.
+ *
+ * RETURNS:
+ * Zero on success or an error code on failure.
+ */
+static int
+twl_vreg_millivolt_to_vsel(struct twl_vreg_softc *sc,
+ struct twl_regulator_entry *regulator, int millivolts, uint8_t *vsel)
+{
+ int delta, smallest_delta;
+ unsigned i, closest_idx;
+
+ TWL_VREG_ASSERT_LOCKED(sc);
+
+ if (regulator->supp_voltages == NULL)
+ return (EINVAL);
+
+ /* Loop over the support voltages and try and find the closest match */
+ closest_idx = 0;
+ smallest_delta = 0x7fffffff;
+ for (i = 0; i < regulator->num_supp_voltages; i++) {
+ /* Ignore undefined values */
+ if (regulator->supp_voltages[i] == UNDF)
+ continue;
+
+ /* Calculate the difference */
+ delta = millivolts - (int)regulator->supp_voltages[i];
+ if (abs(delta) < smallest_delta) {
+ smallest_delta = abs(delta);
+ closest_idx = i;
+ }
+ }
+
+ /* Check we got a voltage that was within 100mv of the actual target, this
+ * is just a value I picked out of thin air.
+ */
+ if ((smallest_delta > 100) && (closest_idx < 0x100))
+ return (EINVAL);
+
+ *vsel = closest_idx;
+ return (0);
+}
+
+/**
+ * twl_vreg_is_regulator_enabled - returns the enabled status of the regulator
+ * @sc: the device soft context
+ * @regulator: pointer to the regulator device
+ * @enabled: stores the enabled status, zero disabled, non-zero enabled
+ *
+ * LOCKING:
+ * On entry expects the TWL VREG lock to be held. Will upgrade the lock to
+ * exclusive if not already but, if so, it will be downgraded again before
+ * returning.
+ *
+ * RETURNS:
+ * Zero on success or an error code on failure.
+ */
+static int
+twl_vreg_is_regulator_enabled(struct twl_vreg_softc *sc,
+ struct twl_regulator_entry *regulator, int *enabled)
+{
+ int err;
+ uint8_t grp;
+ uint8_t state;
+ int xlocked;
+
+ if (enabled == NULL)
+ return (EINVAL);
+
+ TWL_VREG_ASSERT_LOCKED(sc);
+
+ xlocked = sx_xlocked(&sc->sc_sx);
+ if (!xlocked)
+ TWL_VREG_LOCK_UPGRADE(sc);
+
+ /* The status reading is different for the different devices */
+ if (twl_is_4030(sc->sc_pdev)) {
+ err = twl_vreg_read_1(sc, regulator, TWL_VREG_GRP, &state);
+ if (err)
+ goto done;
+
+ *enabled = (state & TWL4030_P1_GRP);
+
+ } else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev)) {
+ /* Check the regulator is in the application group */
+ if (twl_is_6030(sc->sc_pdev)) {
+ err = twl_vreg_read_1(sc, regulator, TWL_VREG_GRP, &grp);
+ if (err)
+ goto done;
+
+ if (!(grp & TWL6030_P1_GRP)) {
+ *enabled = 0; /* disabled */
+ goto done;
+ }
+ }
+
+ /* Read the application mode state and verify it's ON */
+ err = twl_vreg_read_1(sc, regulator, TWL_VREG_STATE, &state);
+ if (err)
+ goto done;
+
+ *enabled = ((state & 0x0C) == 0x04);
+
+ } else {
+ err = EINVAL;
+ }
+
+done:
+ if (!xlocked)
+ TWL_VREG_LOCK_DOWNGRADE(sc);
+
+ return (err);
+}
+
+/**
+ * twl_vreg_disable_regulator - disables a voltage regulator
+ * @sc: the device soft context
+ * @regulator: pointer to the regulator device
+ *
+ * Disables the regulator which will stop the output drivers.
+ *
+ * LOCKING:
+ * On entry expects the TWL VREG lock to be held. Will upgrade the lock to
+ * exclusive if not already but, if so, it will be downgraded again before
+ * returning.
+ *
+ * RETURNS:
+ * Zero on success or a positive error code on failure.
+ */
+static int
+twl_vreg_disable_regulator(struct twl_vreg_softc *sc,
+ struct twl_regulator_entry *regulator)
+{
+ int err = 0;
+ uint8_t grp;
+ int xlocked;
+
+ TWL_VREG_ASSERT_LOCKED(sc);
+
+ xlocked = sx_xlocked(&sc->sc_sx);
+ if (!xlocked)
+ TWL_VREG_LOCK_UPGRADE(sc);
+
+ if (twl_is_4030(sc->sc_pdev)) {
+ /* Read the regulator CFG_GRP register */
+ err = twl_vreg_read_1(sc, regulator, TWL_VREG_GRP, &grp);
+ if (err)
+ goto done;
+
+ /* On the TWL4030 we just need to remove the regulator from all the
+ * power groups.
+ */
+ grp &= ~(TWL4030_P1_GRP | TWL4030_P2_GRP | TWL4030_P3_GRP);
+ err = twl_vreg_write_1(sc, regulator, TWL_VREG_GRP, grp);
+
+ } else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev)) {
+ /* On TWL6030 we need to make sure we disable power for all groups */
+ if (twl_is_6030(sc->sc_pdev))
+ grp = TWL6030_P1_GRP | TWL6030_P2_GRP | TWL6030_P3_GRP;
+ else
+ grp = 0x00;
+
+ /* Write the resource state to "OFF" */
+ err = twl_vreg_write_1(sc, regulator, TWL_VREG_STATE, (grp << 5));
+ }
+
+done:
+ if (!xlocked)
+ TWL_VREG_LOCK_DOWNGRADE(sc);
+
+ return (err);
+}
+
+/**
+ * twl_vreg_enable_regulator - enables the voltage regulator
+ * @sc: the device soft context
+ * @regulator: pointer to the regulator device
+ *
+ * Enables the regulator which will enable the voltage out at the currently
+ * set voltage. Set the voltage before calling this function to avoid
+ * driving the voltage too high/low by mistake.
+ *
+ * LOCKING:
+ * On entry expects the TWL VREG lock to be held. Will upgrade the lock to
+ * exclusive if not already but, if so, it will be downgraded again before
+ * returning.
+ *
+ * RETURNS:
+ * Zero on success or a positive error code on failure.
+ */
+static int
+twl_vreg_enable_regulator(struct twl_vreg_softc *sc,
+ struct twl_regulator_entry *regulator)
+{
+ int err;
+ uint8_t grp;
+ int xlocked;
+
+ TWL_VREG_ASSERT_LOCKED(sc);
+
+ xlocked = sx_xlocked(&sc->sc_sx);
+ if (!xlocked)
+ TWL_VREG_LOCK_UPGRADE(sc);
+
+ err = twl_vreg_read_1(sc, regulator, TWL_VREG_GRP, &grp);
+ if (err)
+ goto done;
+
+ /* Enable the regulator by ensuring it's in the application power group
+ * and is in the "on" state.
+ */
+ if (twl_is_4030(sc->sc_pdev)) {
+ /* On the TWL4030 we just need to ensure the regulator is in the right
+ * power domain, don't need to turn on explicitly like TWL6030.
+ */
+ grp |= TWL4030_P1_GRP;
+ err = twl_vreg_write_1(sc, regulator, TWL_VREG_GRP, grp);
+
+ } else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev)) {
+ if (twl_is_6030(sc->sc_pdev) && !(grp & TWL6030_P1_GRP)) {
+ grp |= TWL6030_P1_GRP;
+ err = twl_vreg_write_1(sc, regulator, TWL_VREG_GRP, grp);
+ if (err)
+ goto done;
+ }
+
+ /* Write the resource state to "ON" */
+ err = twl_vreg_write_1(sc, regulator, TWL_VREG_STATE, (grp << 5) | 0x01);
+ }
+
+done:
+ if (!xlocked)
+ TWL_VREG_LOCK_DOWNGRADE(sc);
+
+ return (err);
+}
+
+/**
+ * twl_vreg_write_regulator_voltage - sets the voltage level on a regulator
+ * @sc: the device soft context
+ * @regulator: pointer to the regulator structure
+ * @millivolts: the voltage to set
+ *
+ * Sets the voltage output on a given regulator, if the regulator is not
+ * enabled, it will be enabled.
+ *
+ * LOCKING:
+ * On entry expects the TWL VREG lock to be held, may upgrade the lock to
+ * exclusive but if so it will be downgraded once again before returning.
+ *
+ * RETURNS:
+ * Zero on success or an error code on failure.
+ */
+static int
+twl_vreg_write_regulator_voltage(struct twl_vreg_softc *sc,
+ struct twl_regulator_entry *regulator, int millivolts)
+{
+ int err;
+ uint8_t vsel;
+ int xlocked;
+
+ TWL_VREG_ASSERT_LOCKED(sc);
+
+ /* If millivolts is zero then we simply disable the output */
+ if (millivolts == 0)
+ return (twl_vreg_disable_regulator(sc, regulator));
+
+ /* If the regulator has a fixed voltage then check the setting matches
+ * and simply enable.
+ */
+ if (regulator->supp_voltages == NULL || regulator->num_supp_voltages == 0) {
+ if (millivolts != regulator->fixed_voltage)
+ return (EINVAL);
+
+ return (twl_vreg_enable_regulator(sc, regulator));
+ }
+
+ /* Get the VSEL value for the given voltage */
+ err = twl_vreg_millivolt_to_vsel(sc, regulator, millivolts, &vsel);
+ if (err)
+ return (err);
+
+ /* Need to upgrade because writing the voltage and enabling should be atomic */
+ xlocked = sx_xlocked(&sc->sc_sx);
+ if (!xlocked)
+ TWL_VREG_LOCK_UPGRADE(sc);
+
+ /* Set voltage and enable (atomically) */
+ err = twl_vreg_write_1(sc, regulator, TWL_VREG_VSEL, (vsel & 0x1f));
+ if (!err) {
+ err = twl_vreg_enable_regulator(sc, regulator);
+ }
+
+ if (!xlocked)
+ TWL_VREG_LOCK_DOWNGRADE(sc);
+
+ if ((twl_vreg_debug > 1) && !err)
+ device_printf(sc->sc_dev, "%s : setting voltage to %dmV (vsel: 0x%x)\n",
+ regulator->name, millivolts, vsel);
+
+ return (err);
+}
+
+/**
+ * twl_vreg_read_regulator_voltage - reads the voltage on a given regulator
+ * @sc: the device soft context
+ * @regulator: pointer to the regulator structure
+ * @millivolts: upon return will contain the voltage on the regulator
+ *
+ * LOCKING:
+ * On entry expects the TWL VREG lock to be held. It will upgrade the lock to
+ * exclusive if not already, but if so, it will be downgraded again before
+ * returning.
+ *
+ * RETURNS:
+ * Zero on success, or otherwise an error code.
+ */
+static int
+twl_vreg_read_regulator_voltage(struct twl_vreg_softc *sc,
+ struct twl_regulator_entry *regulator, int *millivolts)
+{
+ int err;
+ int en = 0;
+ int xlocked;
+ uint8_t vsel;
+
+ TWL_VREG_ASSERT_LOCKED(sc);
+
+ /* Need to upgrade the lock because checking enabled state and voltage
+ * should be atomic.
+ */
+ xlocked = sx_xlocked(&sc->sc_sx);
+ if (!xlocked)
+ TWL_VREG_LOCK_UPGRADE(sc);
+
+ /* Check if the regulator is currently enabled */
+ err = twl_vreg_is_regulator_enabled(sc, regulator, &en);
+ if (err)
+ goto done;
+
+ *millivolts = 0;
+ if (!en)
+ goto done;
+
+ /* Not all voltages are adjustable */
+ if (regulator->supp_voltages == NULL || !regulator->num_supp_voltages) {
+ *millivolts = regulator->fixed_voltage;
+ goto done;
+ }
+
+ /* For variable voltages read the voltage register */
+ err = twl_vreg_read_1(sc, regulator, TWL_VREG_VSEL, &vsel);
+ if (err)
+ goto done;
+
+ vsel &= (regulator->num_supp_voltages - 1);
+ if (regulator->supp_voltages[vsel] == UNDF) {
+ err = EINVAL;
+ goto done;
+ }
+
+ *millivolts = regulator->supp_voltages[vsel];
+
+done:
+ if (!xlocked)
+ TWL_VREG_LOCK_DOWNGRADE(sc);
+
+ if ((twl_vreg_debug > 1) && !err)
+ device_printf(sc->sc_dev, "%s : reading voltage is %dmV (vsel: 0x%x)\n",
+ regulator->name, *millivolts, vsel);
+
+ return (err);
+}
+
+/**
+ * twl_vreg_get_voltage - public interface to read the voltage on a regulator
+ * @dev: TWL VREG device
+ * @name: the name of the regulator to read the voltage of
+ * @millivolts: pointer to an integer that upon return will contain the mV
+ *
+ * If the regulator is disabled the function will set the @millivolts to zero.
+ *
+ * LOCKING:
+ * Internally the function takes and releases the TWL VREG lock.
+ *
+ * RETURNS:
+ * Zero on success or a negative error code on failure.
+ */
+int
+twl_vreg_get_voltage(device_t dev, const char *name, int *millivolts)
+{
+ struct twl_vreg_softc *sc;
+ struct twl_regulator_entry *regulator;
+ int err = EINVAL;
+
+ if (millivolts == NULL)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+
+ TWL_VREG_SLOCK(sc);
+
+ LIST_FOREACH(regulator, &sc->sc_vreg_list, entries) {
+ if (strcmp(regulator->name, name) == 0) {
+ err = twl_vreg_read_regulator_voltage(sc, regulator, millivolts);
+ break;
+ }
+ }
+
+ TWL_VREG_SUNLOCK(sc);
+
+ return (err);
+}
+
+/**
+ * twl_vreg_set_voltage - public interface to write the voltage on a regulator
+ * @dev: TWL VREG device
+ * @name: the name of the regulator to read the voltage of
+ * @millivolts: the voltage to set in millivolts
+ *
+ * Sets the output voltage on a given regulator. If the regulator is a fixed
+ * voltage reg then the @millivolts value should match the fixed voltage. If
+ * a variable regulator then the @millivolt value must fit within the max/min
+ * range of the given regulator.
+ *
+ * LOCKING:
+ * Internally the function takes and releases the TWL VREG lock.
+ *
+ * RETURNS:
+ * Zero on success or a negative error code on failure.
+ */
+int
+twl_vreg_set_voltage(device_t dev, const char *name, int millivolts)
+{
+ struct twl_vreg_softc *sc;
+ struct twl_regulator_entry *regulator;
+ int err = EINVAL;
+
+ sc = device_get_softc(dev);
+
+ TWL_VREG_SLOCK(sc);
+
+ LIST_FOREACH(regulator, &sc->sc_vreg_list, entries) {
+ if (strcmp(regulator->name, name) == 0) {
+ err = twl_vreg_write_regulator_voltage(sc, regulator, millivolts);
+ break;
+ }
+ }
+
+ TWL_VREG_SUNLOCK(sc);
+
+ return (err);
+}
+
+/**
+ * twl_sysctl_voltage - reads or writes the voltage for a regulator
+ * @SYSCTL_HANDLER_ARGS: arguments for the callback
+ *
+ * Callback for the sysctl entry for the regulator, simply used to return
+ * the voltage on a particular regulator.
+ *
+ * LOCKING:
+ * Takes the TWL_VREG shared lock internally.
+ *
+ * RETURNS:
+ * Zero on success or an error code on failure.
+ */
+static int
+twl_vreg_sysctl_voltage(SYSCTL_HANDLER_ARGS)
+{
+ struct twl_vreg_softc *sc = (struct twl_vreg_softc*)arg1;
+ struct twl_regulator_entry *regulator;
+ int voltage;
+ int found = 0;
+
+ TWL_VREG_SLOCK(sc);
+
+ /* Find the regulator with the matching name */
+ LIST_FOREACH(regulator, &sc->sc_vreg_list, entries) {
+ if (strcmp(regulator->name, oidp->oid_name) == 0) {
+ found = 1;
+ break;
+ }
+ }
+
+ /* Sanity check that we found the regulator */
+ if (!found) {
+ TWL_VREG_SUNLOCK(sc);
+ return (EINVAL);
+ }
+
+ twl_vreg_read_regulator_voltage(sc, regulator, &voltage);
+
+ TWL_VREG_SUNLOCK(sc);
+
+ return sysctl_handle_int(oidp, &voltage, 0, req);
+}
+
+/**
+ * twl_add_regulator - adds single voltage regulator sysctls for the device
+ * @sc: device soft context
+ * @name: the name of the regulator
+ * @nsub: the number of the subdevice
+ * @regbase: the base address of the voltage regulator registers
+ * @fixed_voltage: if a fixed voltage regulator this defines it's voltage
+ * @voltages: if a variable voltage regulator, an array of possible voltages
+ * @num_voltages: the number of entries @voltages
+ *
+ * Adds a voltage regulator to the device and also a sysctl interface for the
+ * regulator.
+ *
+ * LOCKING:
+ * The TWL_VEG exclusive lock must be held while this function is called.
+ *
+ * RETURNS:
+ * Pointer to the new regulator entry on success, otherwise on failure NULL.
+ */
+static struct twl_regulator_entry*
+twl_vreg_add_regulator(struct twl_vreg_softc *sc, const char *name,
+ uint8_t nsub, uint8_t regbase, uint16_t fixed_voltage,
+ const uint16_t *voltages, uint32_t num_voltages)
+{
+ struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
+ struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
+ struct twl_regulator_entry *new;
+
+ new = malloc(sizeof(struct twl_regulator_entry), M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (new == NULL)
+ return (NULL);
+
+ strncpy(new->name, name, TWL_VREG_MAX_NAMELEN);
+ new->name[TWL_VREG_MAX_NAMELEN - 1] = '\0';
+
+ new->sub_dev = nsub;
+ new->reg_off = regbase;
+
+ new->fixed_voltage = fixed_voltage;
+
+ new->supp_voltages = voltages;
+ new->num_supp_voltages = num_voltages;
+
+ /* Add a sysctl entry for the voltage */
+ new->oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, name,
+ CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, 0,
+ twl_vreg_sysctl_voltage, "I", "voltage regulator");
+
+ /* Finally add the regulator to list of supported regulators */
+ LIST_INSERT_HEAD(&sc->sc_vreg_list, new, entries);
+
+ return (new);
+}
+
+/**
+ * twl_vreg_add_regulators - adds any voltage regulators to the device
+ * @sc: device soft context
+ * @chip: the name of the chip used in the hints
+ * @regulators: the list of possible voltage regulators
+ *
+ * Loops over the list of regulators and matches up with the FDT values,
+ * adjusting the actual voltage based on the supplied values.
+ *
+ * LOCKING:
+ * The TWL_VEG exclusive lock must be held while this function is called.
+ *
+ * RETURNS:
+ * Always returns 0.
+ */
+static int
+twl_vreg_add_regulators(struct twl_vreg_softc *sc,
+ const struct twl_regulator *regulators)
+{
+ int err;
+ int millivolts;
+ const struct twl_regulator *walker;
+ struct twl_regulator_entry *entry;
+ phandle_t child;
+ char rnames[256];
+ char *name, *voltage;
+ int len = 0, prop_len;
+
+ /* Add the regulators from the list */
+ walker = &regulators[0];
+ while (walker->name != NULL) {
+ /* Add the regulator to the list */
+ entry = twl_vreg_add_regulator(sc, walker->name, walker->subdev,
+ walker->regbase, walker->fixedvoltage,
+ walker->voltages, walker->num_voltages);
+ if (entry == NULL)
+ continue;
+
+ walker++;
+ }
+
+ /* Check if the FDT is telling us to set any voltages */
+ child = ofw_bus_get_node(sc->sc_pdev);
+ if (child) {
+ prop_len = OF_getprop(child, "voltage-regulators", rnames, sizeof(rnames));
+ while (len < prop_len) {
+ name = rnames + len;
+ len += strlen(name) + 1;
+ if ((len >= prop_len) || (name[0] == '\0'))
+ break;
+
+ voltage = rnames + len;
+ len += strlen(voltage) + 1;
+ if (voltage[0] == '\0')
+ break;
+
+ millivolts = strtoul(voltage, NULL, 0);
+
+ LIST_FOREACH(entry, &sc->sc_vreg_list, entries) {
+ if (strcmp(entry->name, name) == 0) {
+ twl_vreg_write_regulator_voltage(sc, entry, millivolts);
+ break;
+ }
+ }
+ }
+ }
+
+ if (twl_vreg_debug) {
+ LIST_FOREACH(entry, &sc->sc_vreg_list, entries) {
+ err = twl_vreg_read_regulator_voltage(sc, entry, &millivolts);
+ if (!err)
+ device_printf(sc->sc_dev, "%s : %d mV\n", entry->name, millivolts);
+ }
+ }
+
+ return (0);
+}
+
+/**
+ * twl_vreg_init - initialises the list of regulators
+ * @dev: the twl_vreg device
+ *
+ * This function is called as an intrhook once interrupts have been enabled,
+ * this is done so that the driver has the option to enable/disable or set
+ * the voltage level based on settings providied in the FDT.
+ *
+ * LOCKING:
+ * Takes the exclusive lock in the function.
+ */
+static void
+twl_vreg_init(void *dev)
+{
+ struct twl_vreg_softc *sc;
+
+ sc = device_get_softc((device_t)dev);
+
+ TWL_VREG_XLOCK(sc);
+
+ if (twl_is_4030(sc->sc_pdev))
+ twl_vreg_add_regulators(sc, twl4030_regulators);
+ else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev))
+ twl_vreg_add_regulators(sc, twl6030_regulators);
+
+ TWL_VREG_XUNLOCK(sc);
+
+ config_intrhook_disestablish(&sc->sc_init_hook);
+}
+
+static int
+twl_vreg_probe(device_t dev)
+{
+ if (twl_is_4030(device_get_parent(dev)))
+ device_set_desc(dev, "TI TWL4030 PMIC Voltage Regulators");
+ else if (twl_is_6025(device_get_parent(dev)) ||
+ twl_is_6030(device_get_parent(dev)))
+ device_set_desc(dev, "TI TWL6025/TWL6030 PMIC Voltage Regulators");
+ else
+ return (ENXIO);
+
+ return (0);
+}
+
+static int
+twl_vreg_attach(device_t dev)
+{
+ struct twl_vreg_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+ sc->sc_pdev = device_get_parent(dev);
+
+ TWL_VREG_LOCK_INIT(sc);
+
+ LIST_INIT(&sc->sc_vreg_list);
+
+ /* We have to wait until interrupts are enabled. I2C read and write
+ * only works if the interrupts are available.
+ */
+ sc->sc_init_hook.ich_func = twl_vreg_init;
+ sc->sc_init_hook.ich_arg = dev;
+
+ if (config_intrhook_establish(&sc->sc_init_hook) != 0)
+ return (ENOMEM);
+
+ return (0);
+}
+
+static int
+twl_vreg_detach(device_t dev)
+{
+ struct twl_vreg_softc *sc;
+ struct twl_regulator_entry *regulator;
+ struct twl_regulator_entry *tmp;
+
+ sc = device_get_softc(dev);
+
+ /* Take the lock and free all the added regulators */
+ TWL_VREG_XLOCK(sc);
+
+ LIST_FOREACH_SAFE(regulator, &sc->sc_vreg_list, entries, tmp) {
+ LIST_REMOVE(regulator, entries);
+ sysctl_remove_oid(regulator->oid, 1, 0);
+ free(regulator, M_DEVBUF);
+ }
+
+ TWL_VREG_XUNLOCK(sc);
+
+ TWL_VREG_LOCK_DESTROY(sc);
+
+ return (0);
+}
+
+static device_method_t twl_vreg_methods[] = {
+ DEVMETHOD(device_probe, twl_vreg_probe),
+ DEVMETHOD(device_attach, twl_vreg_attach),
+ DEVMETHOD(device_detach, twl_vreg_detach),
+
+ {0, 0},
+};
+
+static driver_t twl_vreg_driver = {
+ "twl_vreg",
+ twl_vreg_methods,
+ sizeof(struct twl_vreg_softc),
+};
+
+static devclass_t twl_vreg_devclass;
+
+DRIVER_MODULE(twl_vreg, twl, twl_vreg_driver, twl_vreg_devclass, 0, 0);
+MODULE_VERSION(twl_vreg, 1);
diff --git a/sys/arm/ti/twl/twl_vreg.h b/sys/arm/ti/twl/twl_vreg.h
new file mode 100644
index 000000000000..ee7165fce920
--- /dev/null
+++ b/sys/arm/ti/twl/twl_vreg.h
@@ -0,0 +1,37 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2011
+ * Ben Gray <ben.r.gray@gmail.com>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _TWL_VREG_H_
+#define _TWL_VREG_H_
+
+int twl_vreg_get_voltage(device_t dev, const char *name, int *millivolts);
+int twl_vreg_set_voltage(device_t dev, const char *name, int millivolts);
+
+#endif /* _TWL_VREG_H_ */
diff --git a/sys/arm/ti/usb/omap_ehci.c b/sys/arm/ti/usb/omap_ehci.c
new file mode 100644
index 000000000000..8b1bb700bf43
--- /dev/null
+++ b/sys/arm/ti/usb/omap_ehci.c
@@ -0,0 +1,468 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2011
+ * Ben Gray <ben.r.gray@gmail.com>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/rman.h>
+#include <sys/module.h>
+#include <sys/proc.h>
+#include <sys/condvar.h>
+
+#include <dev/fdt/simplebus.h>
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/controller/ehci.h>
+#include <dev/usb/controller/ehcireg.h>
+
+#include <machine/bus.h>
+
+#include <arm/ti/usb/omap_usb.h>
+
+#include <arm/ti/omap4/pandaboard/pandaboard.h>
+
+/* EHCI */
+#define OMAP_USBHOST_HCCAPBASE 0x0000
+#define OMAP_USBHOST_HCSPARAMS 0x0004
+#define OMAP_USBHOST_HCCPARAMS 0x0008
+#define OMAP_USBHOST_USBCMD 0x0010
+#define OMAP_USBHOST_USBSTS 0x0014
+#define OMAP_USBHOST_USBINTR 0x0018
+#define OMAP_USBHOST_FRINDEX 0x001C
+#define OMAP_USBHOST_CTRLDSSEGMENT 0x0020
+#define OMAP_USBHOST_PERIODICLISTBASE 0x0024
+#define OMAP_USBHOST_ASYNCLISTADDR 0x0028
+#define OMAP_USBHOST_CONFIGFLAG 0x0050
+#define OMAP_USBHOST_PORTSC(i) (0x0054 + (0x04 * (i)))
+#define OMAP_USBHOST_INSNREG00 0x0090
+#define OMAP_USBHOST_INSNREG01 0x0094
+#define OMAP_USBHOST_INSNREG02 0x0098
+#define OMAP_USBHOST_INSNREG03 0x009C
+#define OMAP_USBHOST_INSNREG04 0x00A0
+#define OMAP_USBHOST_INSNREG05_UTMI 0x00A4
+#define OMAP_USBHOST_INSNREG05_ULPI 0x00A4
+#define OMAP_USBHOST_INSNREG06 0x00A8
+#define OMAP_USBHOST_INSNREG07 0x00AC
+#define OMAP_USBHOST_INSNREG08 0x00B0
+
+#define OMAP_USBHOST_INSNREG04_DISABLE_UNSUSPEND (1 << 5)
+
+#define OMAP_USBHOST_INSNREG05_ULPI_CONTROL_SHIFT 31
+#define OMAP_USBHOST_INSNREG05_ULPI_PORTSEL_SHIFT 24
+#define OMAP_USBHOST_INSNREG05_ULPI_OPSEL_SHIFT 22
+#define OMAP_USBHOST_INSNREG05_ULPI_REGADD_SHIFT 16
+#define OMAP_USBHOST_INSNREG05_ULPI_EXTREGADD_SHIFT 8
+#define OMAP_USBHOST_INSNREG05_ULPI_WRDATA_SHIFT 0
+
+#define ULPI_FUNC_CTRL_RESET (1 << 5)
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Macros for Set and Clear
+ * See ULPI 1.1 specification to find the registers with Set and Clear offsets
+ */
+#define ULPI_SET(a) (a + 1)
+#define ULPI_CLR(a) (a + 2)
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Register Map
+ */
+#define ULPI_VENDOR_ID_LOW 0x00
+#define ULPI_VENDOR_ID_HIGH 0x01
+#define ULPI_PRODUCT_ID_LOW 0x02
+#define ULPI_PRODUCT_ID_HIGH 0x03
+#define ULPI_FUNC_CTRL 0x04
+#define ULPI_IFC_CTRL 0x07
+#define ULPI_OTG_CTRL 0x0a
+#define ULPI_USB_INT_EN_RISE 0x0d
+#define ULPI_USB_INT_EN_FALL 0x10
+#define ULPI_USB_INT_STS 0x13
+#define ULPI_USB_INT_LATCH 0x14
+#define ULPI_DEBUG 0x15
+#define ULPI_SCRATCH 0x16
+
+#define OMAP_EHCI_HC_DEVSTR "TI OMAP USB 2.0 controller"
+
+struct omap_ehci_softc {
+ ehci_softc_t base; /* storage for EHCI code */
+ device_t sc_dev;
+};
+
+static device_attach_t omap_ehci_attach;
+static device_detach_t omap_ehci_detach;
+
+/**
+ * omap_ehci_read_4 - read a 32-bit value from the EHCI registers
+ * omap_ehci_write_4 - write a 32-bit value from the EHCI registers
+ * @sc: omap ehci device context
+ * @off: byte offset within the register set to read from
+ * @val: the value to write into the register
+ *
+ *
+ * LOCKING:
+ * None
+ *
+ * RETURNS:
+ * nothing in case of write function, if read function returns the value read.
+ */
+static inline uint32_t
+omap_ehci_read_4(struct omap_ehci_softc *sc, bus_size_t off)
+{
+ return (bus_read_4(sc->base.sc_io_res, off));
+}
+
+static inline void
+omap_ehci_write_4(struct omap_ehci_softc *sc, bus_size_t off, uint32_t val)
+{
+ bus_write_4(sc->base.sc_io_res, off, val);
+}
+
+/**
+ * omap_ehci_soft_phy_reset - resets the phy using the reset command
+ * @isc: omap ehci device context
+ * @port: port to send the reset over
+ *
+ *
+ * LOCKING:
+ * none
+ *
+ * RETURNS:
+ * nothing
+ */
+static void
+omap_ehci_soft_phy_reset(struct omap_ehci_softc *isc, unsigned int port)
+{
+ unsigned long timeout = (hz < 10) ? 1 : ((100 * hz) / 1000);
+ uint32_t reg;
+
+ reg = ULPI_FUNC_CTRL_RESET
+ /* FUNCTION_CTRL_SET register */
+ | (ULPI_SET(ULPI_FUNC_CTRL) << OMAP_USBHOST_INSNREG05_ULPI_REGADD_SHIFT)
+ /* Write */
+ | (2 << OMAP_USBHOST_INSNREG05_ULPI_OPSEL_SHIFT)
+ /* PORTn */
+ | ((port + 1) << OMAP_USBHOST_INSNREG05_ULPI_PORTSEL_SHIFT)
+ /* start ULPI access*/
+ | (1 << OMAP_USBHOST_INSNREG05_ULPI_CONTROL_SHIFT);
+
+ omap_ehci_write_4(isc, OMAP_USBHOST_INSNREG05_ULPI, reg);
+
+ /* Wait for ULPI access completion */
+ while ((omap_ehci_read_4(isc, OMAP_USBHOST_INSNREG05_ULPI)
+ & (1 << OMAP_USBHOST_INSNREG05_ULPI_CONTROL_SHIFT))) {
+ /* Sleep for a tick */
+ pause("USBPHY_RESET", 1);
+
+ if (timeout-- == 0) {
+ device_printf(isc->sc_dev, "PHY reset operation timed out\n");
+ break;
+ }
+ }
+}
+
+/**
+ * omap_ehci_init - initialises the USB host EHCI controller
+ * @isc: omap ehci device context
+ *
+ * This initialisation routine is quite heavily based on the work done by the
+ * OMAP Linux team (for which I thank them very much). The init sequence is
+ * almost identical, diverging only for the FreeBSD specifics.
+ *
+ * LOCKING:
+ * none
+ *
+ * RETURNS:
+ * 0 on success, a negative error code on failure.
+ */
+static int
+omap_ehci_init(struct omap_ehci_softc *isc)
+{
+ uint32_t reg = 0;
+ int i;
+ device_t uhh_dev;
+
+ uhh_dev = device_get_parent(isc->sc_dev);
+ device_printf(isc->sc_dev, "Starting TI EHCI USB Controller\n");
+
+ /* Set the interrupt threshold control, it controls the maximum rate at
+ * which the host controller issues interrupts. We set it to 1 microframe
+ * at startup - the default is 8 mircoframes (equates to 1ms).
+ */
+ reg = omap_ehci_read_4(isc, OMAP_USBHOST_USBCMD);
+ reg &= 0xff00ffff;
+ reg |= (1 << 16);
+ omap_ehci_write_4(isc, OMAP_USBHOST_USBCMD, reg);
+
+ /* Soft reset the PHY using PHY reset command over ULPI */
+ for (i = 0; i < OMAP_HS_USB_PORTS; i++) {
+ if (omap_usb_port_mode(uhh_dev, i) == EHCI_HCD_OMAP_MODE_PHY)
+ omap_ehci_soft_phy_reset(isc, i);
+ }
+
+ return(0);
+}
+
+/**
+ * omap_ehci_probe - starts the given command
+ * @dev:
+ *
+ * Effectively boilerplate EHCI resume code.
+ *
+ * LOCKING:
+ * Caller should be holding the OMAP3_MMC lock.
+ *
+ * RETURNS:
+ * EH_HANDLED or EH_NOT_HANDLED
+ */
+static int
+omap_ehci_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "ti,ehci-omap"))
+ return (ENXIO);
+
+ device_set_desc(dev, OMAP_EHCI_HC_DEVSTR);
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+/**
+ * omap_ehci_attach - driver entry point, sets up the ECHI controller/driver
+ * @dev: the new device handle
+ *
+ * Sets up bus spaces, interrupt handles, etc for the EHCI controller. It also
+ * parses the resource hints and calls omap_ehci_init() to initialise the
+ * H/W.
+ *
+ * LOCKING:
+ * none
+ *
+ * RETURNS:
+ * 0 on success or a positive error code on failure.
+ */
+static int
+omap_ehci_attach(device_t dev)
+{
+ struct omap_ehci_softc *isc = device_get_softc(dev);
+ ehci_softc_t *sc = &isc->base;
+#ifdef SOC_OMAP4
+ phandle_t root;
+#endif
+ int err;
+ int rid;
+
+#ifdef SOC_OMAP4
+ /*
+ * If we're running a Pandaboard, run Pandaboard-specific
+ * init code.
+ */
+ root = OF_finddevice("/");
+ if (ofw_bus_node_is_compatible(root, "ti,omap4-panda"))
+ pandaboard_usb_hub_init();
+#endif
+
+ /* initialise some bus fields */
+ sc->sc_bus.parent = dev;
+ sc->sc_bus.devices = sc->sc_devices;
+ sc->sc_bus.devices_max = EHCI_MAX_DEVICES;
+ sc->sc_bus.dma_bits = 32;
+
+ sprintf(sc->sc_vendor, "Texas Instruments");
+
+ /* save the device */
+ isc->sc_dev = dev;
+
+ /* get all DMA memory */
+ if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(dev),
+ &ehci_iterate_hw_softc)) {
+ return (ENOMEM);
+ }
+
+ /* Allocate resource for the EHCI register set */
+ rid = 0;
+ sc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
+ if (!sc->sc_io_res) {
+ device_printf(dev, "Error: Could not map EHCI memory\n");
+ goto error;
+ }
+ /* Request an interrupt resource */
+ rid = 0;
+ sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
+ if (sc->sc_irq_res == NULL) {
+ device_printf(dev, "Error: could not allocate irq\n");
+ goto error;
+ }
+
+ /* Add this device as a child of the USBus device */
+ sc->sc_bus.bdev = device_add_child(dev, "usbus", -1);
+ if (!sc->sc_bus.bdev) {
+ device_printf(dev, "Error: could not add USB device\n");
+ goto error;
+ }
+
+ device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
+ device_set_desc(sc->sc_bus.bdev, OMAP_EHCI_HC_DEVSTR);
+
+ /* Initialise the ECHI registers */
+ err = omap_ehci_init(isc);
+ if (err) {
+ device_printf(dev, "Error: could not setup OMAP EHCI, %d\n", err);
+ goto error;
+ }
+
+ /* Set the tag and size of the register set in the EHCI context */
+ sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res);
+ sc->sc_io_tag = rman_get_bustag(sc->sc_io_res);
+ sc->sc_io_size = rman_get_size(sc->sc_io_res);
+
+ /* Setup the interrupt */
+ err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl);
+ if (err) {
+ device_printf(dev, "Error: could not setup irq, %d\n", err);
+ sc->sc_intr_hdl = NULL;
+ goto error;
+ }
+
+ /* Finally we are ready to kick off the ECHI host controller */
+ err = ehci_init(sc);
+ if (err == 0) {
+ err = device_probe_and_attach(sc->sc_bus.bdev);
+ }
+ if (err) {
+ device_printf(dev, "Error: USB init failed err=%d\n", err);
+ goto error;
+ }
+
+ return (0);
+
+error:
+ omap_ehci_detach(dev);
+ return (ENXIO);
+}
+
+/**
+ * omap_ehci_detach - detach the device and cleanup the driver
+ * @dev: device handle
+ *
+ * Clean-up routine where everything initialised in omap_ehci_attach is
+ * freed and cleaned up. This function calls omap_ehci_fini() to shutdown
+ * the on-chip module.
+ *
+ * LOCKING:
+ * none
+ *
+ * RETURNS:
+ * Always returns 0 (success).
+ */
+static int
+omap_ehci_detach(device_t dev)
+{
+ struct omap_ehci_softc *isc = device_get_softc(dev);
+ ehci_softc_t *sc = &isc->base;
+ int err;
+
+ /* during module unload there are lots of children leftover */
+ device_delete_children(dev);
+
+ /*
+ * disable interrupts that might have been switched on in ehci_init
+ */
+ if (sc->sc_io_res) {
+ EWRITE4(sc, EHCI_USBINTR, 0);
+ }
+
+ if (sc->sc_irq_res && sc->sc_intr_hdl) {
+ /*
+ * only call ehci_detach() after ehci_init()
+ */
+ ehci_detach(sc);
+
+ err = bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr_hdl);
+ if (err)
+ device_printf(dev, "Error: could not tear down irq, %d\n", err);
+ sc->sc_intr_hdl = NULL;
+ }
+
+ /* Free the resources stored in the base EHCI handler */
+ if (sc->sc_irq_res) {
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
+ sc->sc_irq_res = NULL;
+ }
+ if (sc->sc_io_res) {
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_io_res);
+ sc->sc_io_res = NULL;
+ }
+
+ return (0);
+}
+
+static device_method_t ehci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, omap_ehci_probe),
+ DEVMETHOD(device_attach, omap_ehci_attach),
+ DEVMETHOD(device_detach, omap_ehci_detach),
+
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ {0, 0}
+};
+
+static driver_t ehci_driver = {
+ "ehci",
+ ehci_methods,
+ sizeof(struct omap_ehci_softc),
+};
+
+static devclass_t ehci_devclass;
+
+DRIVER_MODULE(omap_ehci, omap_uhh, ehci_driver, ehci_devclass, 0, 0);
diff --git a/sys/arm/ti/usb/omap_host.c b/sys/arm/ti/usb/omap_host.c
new file mode 100644
index 000000000000..c73ec1319042
--- /dev/null
+++ b/sys/arm/ti/usb/omap_host.c
@@ -0,0 +1,467 @@
+/*-
+ * Copyright (c) 2015 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * Copyright (c) 2011 Ben Gray <ben.r.gray@gmail.com>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/rman.h>
+#include <sys/module.h>
+
+#include <dev/fdt/simplebus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+
+#include <arm/ti/ti_sysc.h>
+#include <arm/ti/usb/omap_usb.h>
+
+/*
+ * USB Host Module
+ */
+
+/* UHH */
+#define OMAP_USBHOST_UHH_REVISION 0x0000
+#define OMAP_USBHOST_UHH_SYSCONFIG 0x0010
+#define OMAP_USBHOST_UHH_SYSSTATUS 0x0014
+#define OMAP_USBHOST_UHH_HOSTCONFIG 0x0040
+#define OMAP_USBHOST_UHH_DEBUG_CSR 0x0044
+
+/* UHH Register Set */
+#define UHH_SYSCONFIG_MIDLEMODE_MASK (3UL << 12)
+#define UHH_SYSCONFIG_MIDLEMODE_SMARTSTANDBY (2UL << 12)
+#define UHH_SYSCONFIG_MIDLEMODE_NOSTANDBY (1UL << 12)
+#define UHH_SYSCONFIG_MIDLEMODE_FORCESTANDBY (0UL << 12)
+#define UHH_SYSCONFIG_CLOCKACTIVITY (1UL << 8)
+#define UHH_SYSCONFIG_SIDLEMODE_MASK (3UL << 3)
+#define UHH_SYSCONFIG_SIDLEMODE_SMARTIDLE (2UL << 3)
+#define UHH_SYSCONFIG_SIDLEMODE_NOIDLE (1UL << 3)
+#define UHH_SYSCONFIG_SIDLEMODE_FORCEIDLE (0UL << 3)
+#define UHH_SYSCONFIG_ENAWAKEUP (1UL << 2)
+#define UHH_SYSCONFIG_SOFTRESET (1UL << 1)
+#define UHH_SYSCONFIG_AUTOIDLE (1UL << 0)
+
+#define UHH_HOSTCONFIG_APP_START_CLK (1UL << 31)
+#define UHH_HOSTCONFIG_P3_CONNECT_STATUS (1UL << 10)
+#define UHH_HOSTCONFIG_P2_CONNECT_STATUS (1UL << 9)
+#define UHH_HOSTCONFIG_P1_CONNECT_STATUS (1UL << 8)
+#define UHH_HOSTCONFIG_ENA_INCR_ALIGN (1UL << 5)
+#define UHH_HOSTCONFIG_ENA_INCR16 (1UL << 4)
+#define UHH_HOSTCONFIG_ENA_INCR8 (1UL << 3)
+#define UHH_HOSTCONFIG_ENA_INCR4 (1UL << 2)
+#define UHH_HOSTCONFIG_AUTOPPD_ON_OVERCUR_EN (1UL << 1)
+#define UHH_HOSTCONFIG_P1_ULPI_BYPASS (1UL << 0)
+
+/* The following are on rev2 (OMAP44xx) of the EHCI only */
+#define UHH_SYSCONFIG_IDLEMODE_MASK (3UL << 2)
+#define UHH_SYSCONFIG_IDLEMODE_NOIDLE (1UL << 2)
+#define UHH_SYSCONFIG_STANDBYMODE_MASK (3UL << 4)
+#define UHH_SYSCONFIG_STANDBYMODE_NOSTDBY (1UL << 4)
+
+#define UHH_HOSTCONFIG_P1_MODE_MASK (3UL << 16)
+#define UHH_HOSTCONFIG_P1_MODE_ULPI_PHY (0UL << 16)
+#define UHH_HOSTCONFIG_P1_MODE_UTMI_PHY (1UL << 16)
+#define UHH_HOSTCONFIG_P1_MODE_HSIC (3UL << 16)
+#define UHH_HOSTCONFIG_P2_MODE_MASK (3UL << 18)
+#define UHH_HOSTCONFIG_P2_MODE_ULPI_PHY (0UL << 18)
+#define UHH_HOSTCONFIG_P2_MODE_UTMI_PHY (1UL << 18)
+#define UHH_HOSTCONFIG_P2_MODE_HSIC (3UL << 18)
+
+/*
+ * Values of UHH_REVISION - Note: these are not given in the TRM but taken
+ * from the linux OMAP EHCI driver (thanks guys). It has been verified on
+ * a Panda and Beagle board.
+ */
+#define OMAP_UHH_REV1 0x00000010 /* OMAP3 */
+#define OMAP_UHH_REV2 0x50700100 /* OMAP4 */
+
+struct omap_uhh_softc {
+ struct simplebus_softc simplebus_sc;
+ device_t sc_dev;
+
+ /* UHH register set */
+ struct resource* uhh_mem_res;
+
+ /* The revision of the HS USB HOST read from UHH_REVISION */
+ uint32_t uhh_rev;
+
+ /* The following details are provided by conf hints */
+ int port_mode[3];
+};
+
+static device_attach_t omap_uhh_attach;
+static device_detach_t omap_uhh_detach;
+
+static inline uint32_t
+omap_uhh_read_4(struct omap_uhh_softc *sc, bus_size_t off)
+{
+ return bus_read_4(sc->uhh_mem_res, off);
+}
+
+static inline void
+omap_uhh_write_4(struct omap_uhh_softc *sc, bus_size_t off, uint32_t val)
+{
+ bus_write_4(sc->uhh_mem_res, off, val);
+}
+
+static int
+omap_uhh_init(struct omap_uhh_softc *isc)
+{
+ uint8_t tll_ch_mask;
+ uint32_t reg;
+ int i;
+
+ /* Enable Clocks for high speed USBHOST */
+ ti_sysc_clock_enable(device_get_parent(isc->sc_dev));
+
+ /* Read the UHH revision */
+ isc->uhh_rev = omap_uhh_read_4(isc, OMAP_USBHOST_UHH_REVISION);
+ device_printf(isc->sc_dev, "UHH revision 0x%08x\n", isc->uhh_rev);
+
+ /* FIXME */
+#if 0
+ if (isc->uhh_rev == OMAP_UHH_REV2) {
+ /* For OMAP44xx devices you have to enable the per-port clocks:
+ * PHY_MODE - External ULPI clock
+ * TTL_MODE - Internal UTMI clock
+ * HSIC_MODE - Internal 480Mhz and 60Mhz clocks
+ */
+ switch(isc->port_mode[0]) {
+ case EHCI_HCD_OMAP_MODE_UNKNOWN:
+ break;
+ case EHCI_HCD_OMAP_MODE_PHY:
+ if (ti_prcm_clk_set_source(USBP1_PHY_CLK, EXT_CLK))
+ device_printf(isc->sc_dev,
+ "failed to set clock source for port 0\n");
+ if (ti_prcm_clk_enable(USBP1_PHY_CLK))
+ device_printf(isc->sc_dev,
+ "failed to set clock USBP1_PHY_CLK source for port 0\n");
+ break;
+ case EHCI_HCD_OMAP_MODE_TLL:
+ if (ti_prcm_clk_enable(USBP1_UTMI_CLK))
+ device_printf(isc->sc_dev,
+ "failed to set clock USBP1_PHY_CLK source for port 0\n");
+ break;
+ case EHCI_HCD_OMAP_MODE_HSIC:
+ if (ti_prcm_clk_enable(USBP1_HSIC_CLK))
+ device_printf(isc->sc_dev,
+ "failed to set clock USBP1_PHY_CLK source for port 0\n");
+ break;
+ default:
+ device_printf(isc->sc_dev, "unknown port mode %d for port 0\n", isc->port_mode[0]);
+ }
+ switch(isc->port_mode[1]) {
+ case EHCI_HCD_OMAP_MODE_UNKNOWN:
+ break;
+ case EHCI_HCD_OMAP_MODE_PHY:
+ if (ti_prcm_clk_set_source(USBP2_PHY_CLK, EXT_CLK))
+ device_printf(isc->sc_dev,
+ "failed to set clock source for port 0\n");
+ if (ti_prcm_clk_enable(USBP2_PHY_CLK))
+ device_printf(isc->sc_dev,
+ "failed to set clock USBP2_PHY_CLK source for port 1\n");
+ break;
+ case EHCI_HCD_OMAP_MODE_TLL:
+ if (ti_prcm_clk_enable(USBP2_UTMI_CLK))
+ device_printf(isc->sc_dev,
+ "failed to set clock USBP2_UTMI_CLK source for port 1\n");
+ break;
+ case EHCI_HCD_OMAP_MODE_HSIC:
+ if (ti_prcm_clk_enable(USBP2_HSIC_CLK))
+ device_printf(isc->sc_dev,
+ "failed to set clock USBP2_HSIC_CLK source for port 1\n");
+ break;
+ default:
+ device_printf(isc->sc_dev, "unknown port mode %d for port 1\n", isc->port_mode[1]);
+ }
+ }
+#endif
+
+ /* Put UHH in SmartIdle/SmartStandby mode */
+ reg = omap_uhh_read_4(isc, OMAP_USBHOST_UHH_SYSCONFIG);
+ if (isc->uhh_rev == OMAP_UHH_REV1) {
+ reg &= ~(UHH_SYSCONFIG_SIDLEMODE_MASK |
+ UHH_SYSCONFIG_MIDLEMODE_MASK);
+ reg |= (UHH_SYSCONFIG_ENAWAKEUP |
+ UHH_SYSCONFIG_AUTOIDLE |
+ UHH_SYSCONFIG_CLOCKACTIVITY |
+ UHH_SYSCONFIG_SIDLEMODE_SMARTIDLE |
+ UHH_SYSCONFIG_MIDLEMODE_SMARTSTANDBY);
+ } else if (isc->uhh_rev == OMAP_UHH_REV2) {
+ reg &= ~UHH_SYSCONFIG_IDLEMODE_MASK;
+ reg |= UHH_SYSCONFIG_IDLEMODE_NOIDLE;
+ reg &= ~UHH_SYSCONFIG_STANDBYMODE_MASK;
+ reg |= UHH_SYSCONFIG_STANDBYMODE_NOSTDBY;
+ }
+ omap_uhh_write_4(isc, OMAP_USBHOST_UHH_SYSCONFIG, reg);
+ device_printf(isc->sc_dev, "OMAP_UHH_SYSCONFIG: 0x%08x\n", reg);
+
+ reg = omap_uhh_read_4(isc, OMAP_USBHOST_UHH_HOSTCONFIG);
+
+ /* Setup ULPI bypass and burst configurations */
+ reg |= (UHH_HOSTCONFIG_ENA_INCR4 |
+ UHH_HOSTCONFIG_ENA_INCR8 |
+ UHH_HOSTCONFIG_ENA_INCR16);
+ reg &= ~UHH_HOSTCONFIG_ENA_INCR_ALIGN;
+
+ if (isc->uhh_rev == OMAP_UHH_REV1) {
+ if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_UNKNOWN)
+ reg &= ~UHH_HOSTCONFIG_P1_CONNECT_STATUS;
+ if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_UNKNOWN)
+ reg &= ~UHH_HOSTCONFIG_P2_CONNECT_STATUS;
+ if (isc->port_mode[2] == EHCI_HCD_OMAP_MODE_UNKNOWN)
+ reg &= ~UHH_HOSTCONFIG_P3_CONNECT_STATUS;
+
+ /* Bypass the TLL module for PHY mode operation */
+ if ((isc->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) ||
+ (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) ||
+ (isc->port_mode[2] == EHCI_HCD_OMAP_MODE_PHY))
+ reg &= ~UHH_HOSTCONFIG_P1_ULPI_BYPASS;
+ else
+ reg |= UHH_HOSTCONFIG_P1_ULPI_BYPASS;
+
+ } else if (isc->uhh_rev == OMAP_UHH_REV2) {
+ reg |= UHH_HOSTCONFIG_APP_START_CLK;
+
+ /* Clear port mode fields for PHY mode*/
+ reg &= ~UHH_HOSTCONFIG_P1_MODE_MASK;
+ reg &= ~UHH_HOSTCONFIG_P2_MODE_MASK;
+
+ if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL)
+ reg |= UHH_HOSTCONFIG_P1_MODE_UTMI_PHY;
+ else if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_HSIC)
+ reg |= UHH_HOSTCONFIG_P1_MODE_HSIC;
+
+ if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL)
+ reg |= UHH_HOSTCONFIG_P2_MODE_UTMI_PHY;
+ else if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_HSIC)
+ reg |= UHH_HOSTCONFIG_P2_MODE_HSIC;
+ }
+
+ omap_uhh_write_4(isc, OMAP_USBHOST_UHH_HOSTCONFIG, reg);
+ device_printf(isc->sc_dev, "UHH setup done, uhh_hostconfig=0x%08x\n", reg);
+
+ /* I found the code and comments in the Linux EHCI driver - thanks guys :)
+ *
+ * "An undocumented "feature" in the OMAP3 EHCI controller, causes suspended
+ * ports to be taken out of suspend when the USBCMD.Run/Stop bit is cleared
+ * (for example when we do omap_uhh_bus_suspend). This breaks suspend-resume if
+ * the root-hub is allowed to suspend. Writing 1 to this undocumented
+ * register bit disables this feature and restores normal behavior."
+ */
+#if 0
+ omap_uhh_write_4(isc, OMAP_USBHOST_INSNREG04,
+ OMAP_USBHOST_INSNREG04_DISABLE_UNSUSPEND);
+#endif
+ tll_ch_mask = 0;
+ for (i = 0; i < OMAP_HS_USB_PORTS; i++) {
+ if (isc->port_mode[i] == EHCI_HCD_OMAP_MODE_TLL)
+ tll_ch_mask |= (1 << i);
+ }
+ if (tll_ch_mask)
+ omap_tll_utmi_enable(tll_ch_mask);
+
+ return(0);
+}
+
+/**
+ * omap_uhh_fini - shutdown the EHCI controller
+ * @isc: omap ehci device context
+ *
+ *
+ *
+ * LOCKING:
+ * none
+ *
+ * RETURNS:
+ * 0 on success, a negative error code on failure.
+ */
+static void
+omap_uhh_fini(struct omap_uhh_softc *isc)
+{
+ unsigned long timeout;
+
+ device_printf(isc->sc_dev, "Stopping TI EHCI USB Controller\n");
+
+ /* Set the timeout */
+ if (hz < 10)
+ timeout = 1;
+ else
+ timeout = (100 * hz) / 1000;
+
+ /* Reset the UHH, OHCI and EHCI modules */
+ omap_uhh_write_4(isc, OMAP_USBHOST_UHH_SYSCONFIG, 0x0002);
+ while ((omap_uhh_read_4(isc, OMAP_USBHOST_UHH_SYSSTATUS) & 0x07) == 0x00) {
+ /* Sleep for a tick */
+ pause("USBRESET", 1);
+
+ if (timeout-- == 0) {
+ device_printf(isc->sc_dev, "operation timed out\n");
+ break;
+ }
+ }
+
+ /* Disable functional and interface clocks for the TLL and HOST modules */
+ ti_sysc_clock_disable(device_get_parent(isc->sc_dev));
+
+ device_printf(isc->sc_dev, "Clock to USB host has been disabled\n");
+}
+
+int
+omap_usb_port_mode(device_t dev, int port)
+{
+ struct omap_uhh_softc *isc;
+
+ isc = device_get_softc(dev);
+ if ((port < 0) || (port >= OMAP_HS_USB_PORTS))
+ return (-1);
+
+ return isc->port_mode[port];
+}
+
+static int
+omap_uhh_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "ti,usbhs-host"))
+ return (ENXIO);
+
+ device_set_desc(dev, "TI OMAP USB 2.0 Host module");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+omap_uhh_attach(device_t dev)
+{
+ struct omap_uhh_softc *isc = device_get_softc(dev);
+ int err;
+ int rid;
+ int i;
+ phandle_t node;
+ char propname[16];
+ char *mode;
+
+ /* save the device */
+ isc->sc_dev = dev;
+
+ /* Allocate resource for the UHH register set */
+ rid = 0;
+ isc->uhh_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
+ if (!isc->uhh_mem_res) {
+ device_printf(dev, "Error: Could not map UHH memory\n");
+ goto error;
+ }
+
+ node = ofw_bus_get_node(dev);
+
+ if (node == -1)
+ goto error;
+
+ /* Get port modes from FDT */
+ for (i = 0; i < OMAP_HS_USB_PORTS; i++) {
+ isc->port_mode[i] = EHCI_HCD_OMAP_MODE_UNKNOWN;
+ snprintf(propname, sizeof(propname),
+ "port%d-mode", i+1);
+
+ if (OF_getprop_alloc(node, propname, (void**)&mode) <= 0)
+ continue;
+ if (strcmp(mode, "ehci-phy") == 0)
+ isc->port_mode[i] = EHCI_HCD_OMAP_MODE_PHY;
+ else if (strcmp(mode, "ehci-tll") == 0)
+ isc->port_mode[i] = EHCI_HCD_OMAP_MODE_TLL;
+ else if (strcmp(mode, "ehci-hsic") == 0)
+ isc->port_mode[i] = EHCI_HCD_OMAP_MODE_HSIC;
+ }
+
+ /* Initialise the ECHI registers */
+ err = omap_uhh_init(isc);
+ if (err) {
+ device_printf(dev, "Error: could not setup OMAP EHCI, %d\n", err);
+ goto error;
+ }
+
+ simplebus_init(dev, node);
+
+ /*
+ * Allow devices to identify.
+ */
+ bus_generic_probe(dev);
+
+ /*
+ * Now walk the OFW tree and attach top-level devices.
+ */
+ for (node = OF_child(node); node > 0; node = OF_peer(node))
+ simplebus_add_device(dev, node, 0, NULL, -1, NULL);
+ return (bus_generic_attach(dev));
+
+error:
+ omap_uhh_detach(dev);
+ return (ENXIO);
+}
+
+static int
+omap_uhh_detach(device_t dev)
+{
+ struct omap_uhh_softc *isc = device_get_softc(dev);
+
+ /* during module unload there are lots of children leftover */
+ device_delete_children(dev);
+
+ if (isc->uhh_mem_res) {
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, isc->uhh_mem_res);
+ isc->uhh_mem_res = NULL;
+ }
+
+ omap_uhh_fini(isc);
+
+ return (0);
+}
+
+static device_method_t omap_uhh_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, omap_uhh_probe),
+ DEVMETHOD(device_attach, omap_uhh_attach),
+ DEVMETHOD(device_detach, omap_uhh_detach),
+
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(omap_uhh, omap_uhh_driver, omap_uhh_methods,
+ sizeof(struct omap_uhh_softc), simplebus_driver);
+static devclass_t omap_uhh_devclass;
+DRIVER_MODULE(omap_uhh, simplebus, omap_uhh_driver, omap_uhh_devclass, 0, 0);
diff --git a/sys/arm/ti/usb/omap_tll.c b/sys/arm/ti/usb/omap_tll.c
new file mode 100644
index 000000000000..6737b23b2d07
--- /dev/null
+++ b/sys/arm/ti/usb/omap_tll.c
@@ -0,0 +1,361 @@
+/*-
+ * Copyright (c) 2011
+ * Ben Gray <ben.r.gray@gmail.com>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/rman.h>
+#include <sys/module.h>
+
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+
+#include <arm/ti/ti_sysc.h>
+#include <arm/ti/usb/omap_usb.h>
+
+/*
+ * USB TLL Module
+ */
+#define OMAP_USBTLL_REVISION 0x0000
+#define OMAP_USBTLL_SYSCONFIG 0x0010
+#define OMAP_USBTLL_SYSSTATUS 0x0014
+#define OMAP_USBTLL_IRQSTATUS 0x0018
+#define OMAP_USBTLL_IRQENABLE 0x001C
+#define OMAP_USBTLL_TLL_SHARED_CONF 0x0030
+#define OMAP_USBTLL_TLL_CHANNEL_CONF(i) (0x0040 + (0x04 * (i)))
+#define OMAP_USBTLL_SAR_CNTX(i) (0x0400 + (0x04 * (i)))
+#define OMAP_USBTLL_ULPI_VENDOR_ID_LO(i) (0x0800 + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_VENDOR_ID_HI(i) (0x0801 + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_PRODUCT_ID_LO(i) (0x0802 + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_PRODUCT_ID_HI(i) (0x0803 + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_FUNCTION_CTRL(i) (0x0804 + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_FUNCTION_CTRL_SET(i) (0x0805 + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_FUNCTION_CTRL_CLR(i) (0x0806 + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_INTERFACE_CTRL(i) (0x0807 + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_INTERFACE_CTRL_SET(i) (0x0808 + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_INTERFACE_CTRL_CLR(i) (0x0809 + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_OTG_CTRL(i) (0x080A + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_OTG_CTRL_SET(i) (0x080B + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_OTG_CTRL_CLR(i) (0x080C + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_USB_INT_EN_RISE(i) (0x080D + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_USB_INT_EN_RISE_SET(i) (0x080E + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_USB_INT_EN_RISE_CLR(i) (0x080F + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_USB_INT_EN_FALL(i) (0x0810 + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_USB_INT_EN_FALL_SET(i) (0x0811 + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_USB_INT_EN_FALL_CLR(i) (0x0812 + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_USB_INT_STATUS(i) (0x0813 + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_USB_INT_LATCH(i) (0x0814 + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_DEBUG(i) (0x0815 + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_SCRATCH_REGISTER(i) (0x0816 + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_SCRATCH_REGISTER_SET(i) (0x0817 + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_SCRATCH_REGISTER_CLR(i) (0x0818 + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_EXTENDED_SET_ACCESS(i) (0x082F + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_UTMI_VCONTROL_EN(i) (0x0830 + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_UTMI_VCONTROL_EN_SET(i) (0x0831 + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_UTMI_VCONTROL_EN_CLR(i) (0x0832 + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_UTMI_VCONTROL_STATUS(i) (0x0833 + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_UTMI_VCONTROL_LATCH(i) (0x0834 + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_UTMI_VSTATUS(i) (0x0835 + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_UTMI_VSTATUS_SET(i) (0x0836 + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_UTMI_VSTATUS_CLR(i) (0x0837 + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_USB_INT_LATCH_NOCLR(i) (0x0838 + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_VENDOR_INT_EN(i) (0x083B + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_VENDOR_INT_EN_SET(i) (0x083C + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_VENDOR_INT_EN_CLR(i) (0x083D + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_VENDOR_INT_STATUS(i) (0x083E + (0x100 * (i)))
+#define OMAP_USBTLL_ULPI_VENDOR_INT_LATCH(i) (0x083F + (0x100 * (i)))
+
+/* TLL Register Set */
+#define TLL_SYSCONFIG_CACTIVITY (1UL << 8)
+#define TLL_SYSCONFIG_SIDLE_SMART_IDLE (2UL << 3)
+#define TLL_SYSCONFIG_SIDLE_NO_IDLE (1UL << 3)
+#define TLL_SYSCONFIG_SIDLE_FORCED_IDLE (0UL << 3)
+#define TLL_SYSCONFIG_ENAWAKEUP (1UL << 2)
+#define TLL_SYSCONFIG_SOFTRESET (1UL << 1)
+#define TLL_SYSCONFIG_AUTOIDLE (1UL << 0)
+
+#define TLL_SYSSTATUS_RESETDONE (1UL << 0)
+
+#define TLL_SHARED_CONF_USB_90D_DDR_EN (1UL << 6)
+#define TLL_SHARED_CONF_USB_180D_SDR_EN (1UL << 5)
+#define TLL_SHARED_CONF_USB_DIVRATIO_MASK (7UL << 2)
+#define TLL_SHARED_CONF_USB_DIVRATIO_128 (7UL << 2)
+#define TLL_SHARED_CONF_USB_DIVRATIO_64 (6UL << 2)
+#define TLL_SHARED_CONF_USB_DIVRATIO_32 (5UL << 2)
+#define TLL_SHARED_CONF_USB_DIVRATIO_16 (4UL << 2)
+#define TLL_SHARED_CONF_USB_DIVRATIO_8 (3UL << 2)
+#define TLL_SHARED_CONF_USB_DIVRATIO_4 (2UL << 2)
+#define TLL_SHARED_CONF_USB_DIVRATIO_2 (1UL << 2)
+#define TLL_SHARED_CONF_USB_DIVRATIO_1 (0UL << 2)
+#define TLL_SHARED_CONF_FCLK_REQ (1UL << 1)
+#define TLL_SHARED_CONF_FCLK_IS_ON (1UL << 0)
+
+#define TLL_CHANNEL_CONF_DRVVBUS (1UL << 16)
+#define TLL_CHANNEL_CONF_CHRGVBUS (1UL << 15)
+#define TLL_CHANNEL_CONF_ULPINOBITSTUFF (1UL << 11)
+#define TLL_CHANNEL_CONF_ULPIAUTOIDLE (1UL << 10)
+#define TLL_CHANNEL_CONF_UTMIAUTOIDLE (1UL << 9)
+#define TLL_CHANNEL_CONF_ULPIDDRMODE (1UL << 8)
+#define TLL_CHANNEL_CONF_ULPIOUTCLKMODE (1UL << 7)
+#define TLL_CHANNEL_CONF_TLLFULLSPEED (1UL << 6)
+#define TLL_CHANNEL_CONF_TLLCONNECT (1UL << 5)
+#define TLL_CHANNEL_CONF_TLLATTACH (1UL << 4)
+#define TLL_CHANNEL_CONF_UTMIISADEV (1UL << 3)
+#define TLL_CHANNEL_CONF_CHANEN (1UL << 0)
+
+struct omap_tll_softc {
+ device_t sc_dev;
+
+ /* TLL register set */
+ struct resource* tll_mem_res;
+ int tll_mem_rid;
+};
+
+static struct omap_tll_softc *omap_tll_sc;
+
+static int omap_tll_attach(device_t dev);
+static int omap_tll_detach(device_t dev);
+
+static inline uint32_t
+omap_tll_read_4(struct omap_tll_softc *sc, bus_size_t off)
+{
+ return bus_read_4(sc->tll_mem_res, off);
+}
+
+static inline void
+omap_tll_write_4(struct omap_tll_softc *sc, bus_size_t off, uint32_t val)
+{
+ bus_write_4(sc->tll_mem_res, off, val);
+}
+
+void
+omap_tll_utmi_enable(unsigned int en_mask)
+{
+ struct omap_tll_softc *sc;
+ unsigned int i;
+ uint32_t reg;
+
+ sc = omap_tll_sc;
+ if (sc == NULL)
+ return;
+
+ /* There are 3 TLL channels, one per USB controller so set them all up the
+ * same, SDR mode, bit stuffing and no autoidle.
+ */
+ for (i=0; i<3; i++) {
+ reg = omap_tll_read_4(sc, OMAP_USBTLL_TLL_CHANNEL_CONF(i));
+
+ reg &= ~(TLL_CHANNEL_CONF_UTMIAUTOIDLE
+ | TLL_CHANNEL_CONF_ULPINOBITSTUFF
+ | TLL_CHANNEL_CONF_ULPIDDRMODE);
+
+ omap_tll_write_4(sc, OMAP_USBTLL_TLL_CHANNEL_CONF(i), reg);
+ }
+
+ /* Program the common TLL register */
+ reg = omap_tll_read_4(sc, OMAP_USBTLL_TLL_SHARED_CONF);
+
+ reg &= ~( TLL_SHARED_CONF_USB_90D_DDR_EN
+ | TLL_SHARED_CONF_USB_DIVRATIO_MASK);
+ reg |= ( TLL_SHARED_CONF_FCLK_IS_ON
+ | TLL_SHARED_CONF_USB_DIVRATIO_2
+ | TLL_SHARED_CONF_USB_180D_SDR_EN);
+
+ omap_tll_write_4(sc, OMAP_USBTLL_TLL_SHARED_CONF, reg);
+
+ /* Enable channels now */
+ for (i = 0; i < 3; i++) {
+ reg = omap_tll_read_4(sc, OMAP_USBTLL_TLL_CHANNEL_CONF(i));
+
+ /* Enable only the reg that is needed */
+ if ((en_mask & (1 << i)) == 0)
+ continue;
+
+ reg |= TLL_CHANNEL_CONF_CHANEN;
+ omap_tll_write_4(sc, OMAP_USBTLL_TLL_CHANNEL_CONF(i), reg);
+ }
+}
+
+static int
+omap_tll_init(struct omap_tll_softc *sc)
+{
+ unsigned long timeout;
+ int ret = 0;
+
+ /* Enable the USB TLL */
+ ti_sysc_clock_enable(device_get_parent(sc->sc_dev));
+
+ /* Perform TLL soft reset, and wait until reset is complete */
+ omap_tll_write_4(sc, OMAP_USBTLL_SYSCONFIG, TLL_SYSCONFIG_SOFTRESET);
+
+ /* Set the timeout to 100ms*/
+ timeout = (hz < 10) ? 1 : ((100 * hz) / 1000);
+
+ /* Wait for TLL reset to complete */
+ while ((omap_tll_read_4(sc, OMAP_USBTLL_SYSSTATUS) &
+ TLL_SYSSTATUS_RESETDONE) == 0x00) {
+ /* Sleep for a tick */
+ pause("USBRESET", 1);
+
+ if (timeout-- == 0) {
+ device_printf(sc->sc_dev, "TLL reset operation timed out\n");
+ ret = EINVAL;
+ goto err_sys_status;
+ }
+ }
+
+ /* CLOCKACTIVITY = 1 : OCP-derived internal clocks ON during idle
+ * SIDLEMODE = 2 : Smart-idle mode. Sidleack asserted after Idlereq
+ * assertion when no more activity on the USB.
+ * ENAWAKEUP = 1 : Wakeup generation enabled
+ */
+ omap_tll_write_4(sc, OMAP_USBTLL_SYSCONFIG, TLL_SYSCONFIG_ENAWAKEUP |
+ TLL_SYSCONFIG_AUTOIDLE |
+ TLL_SYSCONFIG_SIDLE_SMART_IDLE |
+ TLL_SYSCONFIG_CACTIVITY);
+
+ return(0);
+
+err_sys_status:
+ /* Disable the TLL clocks */
+ ti_sysc_clock_disable(device_get_parent(sc->sc_dev));
+
+ return(ret);
+}
+
+static void
+omap_tll_disable(struct omap_tll_softc *sc)
+{
+ unsigned long timeout;
+
+ timeout = (hz < 10) ? 1 : ((100 * hz) / 1000);
+
+ /* Reset the TLL module */
+ omap_tll_write_4(sc, OMAP_USBTLL_SYSCONFIG, 0x0002);
+ while ((omap_tll_read_4(sc, OMAP_USBTLL_SYSSTATUS) & (0x01)) == 0x00) {
+ /* Sleep for a tick */
+ pause("USBRESET", 1);
+
+ if (timeout-- == 0) {
+ device_printf(sc->sc_dev, "operation timed out\n");
+ break;
+ }
+ }
+
+ /* Disable functional and interface clocks for the TLL and HOST modules */
+ ti_sysc_clock_disable(device_get_parent(sc->sc_dev));
+}
+
+static int
+omap_tll_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "ti,usbhs-tll"))
+ return (ENXIO);
+
+ device_set_desc(dev, "TI OMAP USB 2.0 TLL module");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+omap_tll_attach(device_t dev)
+{
+ struct omap_tll_softc *sc;
+
+ sc = device_get_softc(dev);
+ /* save the device */
+ sc->sc_dev = dev;
+
+ /* Allocate resource for the TLL register set */
+ sc->tll_mem_rid = 0;
+ sc->tll_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &sc->tll_mem_rid, RF_ACTIVE);
+ if (!sc->tll_mem_res) {
+ device_printf(dev, "Error: Could not map TLL memory\n");
+ goto error;
+ }
+
+ omap_tll_init(sc);
+
+ omap_tll_sc = sc;
+
+ return (0);
+
+error:
+ omap_tll_detach(dev);
+ return (ENXIO);
+}
+
+static int
+omap_tll_detach(device_t dev)
+{
+ struct omap_tll_softc *sc;
+
+ sc = device_get_softc(dev);
+ omap_tll_disable(sc);
+
+ /* Release the other register set memory maps */
+ if (sc->tll_mem_res) {
+ bus_release_resource(dev, SYS_RES_MEMORY,
+ sc->tll_mem_rid, sc->tll_mem_res);
+ sc->tll_mem_res = NULL;
+ }
+
+ omap_tll_sc = NULL;
+
+ return (0);
+}
+
+static device_method_t omap_tll_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, omap_tll_probe),
+ DEVMETHOD(device_attach, omap_tll_attach),
+ DEVMETHOD(device_detach, omap_tll_detach),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ {0, 0}
+};
+
+static driver_t omap_tll_driver = {
+ "omap_tll",
+ omap_tll_methods,
+ sizeof(struct omap_tll_softc),
+};
+
+static devclass_t omap_tll_devclass;
+
+DRIVER_MODULE(omap_tll, simplebus, omap_tll_driver, omap_tll_devclass, 0, 0);
diff --git a/sys/arm/ti/usb/omap_usb.h b/sys/arm/ti/usb/omap_usb.h
new file mode 100644
index 000000000000..246828d31f9d
--- /dev/null
+++ b/sys/arm/ti/usb/omap_usb.h
@@ -0,0 +1,50 @@
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 2010
+ * Ben Gray <ben.r.gray@gmail.com>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Ben Gray.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BEN GRAY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _OMAP_USB_H_
+#define _OMAP_USB_H_
+
+#define OMAP_HS_USB_PORTS 3
+
+#define EHCI_HCD_OMAP_MODE_UNKNOWN 0
+#define EHCI_HCD_OMAP_MODE_PHY 1
+#define EHCI_HCD_OMAP_MODE_TLL 2
+#define EHCI_HCD_OMAP_MODE_HSIC 3
+
+void omap_tll_utmi_enable(unsigned int en_mask);
+int omap_usb_port_mode(device_t dev, int port);
+
+#endif /* _OMAP_USB_H_ */
diff --git a/sys/arm/versatile/files.versatile b/sys/arm/versatile/files.versatile
new file mode 100644
index 000000000000..7e772aba4b0e
--- /dev/null
+++ b/sys/arm/versatile/files.versatile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+arm/versatile/pl050.c optional sc
+arm/versatile/sp804.c standard
+arm/versatile/versatile_machdep.c standard
+arm/versatile/versatile_clcd.c optional sc
+arm/versatile/versatile_pci.c optional pci
+arm/versatile/versatile_scm.c standard
+arm/versatile/versatile_sic.c standard
diff --git a/sys/arm/versatile/pl050.c b/sys/arm/versatile/pl050.c
new file mode 100644
index 000000000000..cccb4a947c6e
--- /dev/null
+++ b/sys/arm/versatile/pl050.c
@@ -0,0 +1,742 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * All rights reserved.
+ *
+ * Based on dev/usb/input/ukbd.c
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/proc.h>
+#include <sys/sched.h>
+#include <sys/kdb.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <sys/ioccom.h>
+#include <sys/filio.h>
+#include <sys/kbio.h>
+
+#include <dev/kbd/kbdreg.h>
+
+#include <machine/bus.h>
+
+#include <dev/kbd/kbdtables.h>
+
+#define KMI_LOCK() mtx_lock(&Giant)
+#define KMI_UNLOCK() mtx_unlock(&Giant)
+
+#ifdef INVARIANTS
+/*
+ * Assert that the lock is held in all contexts
+ * where the code can be executed.
+ */
+#define KMI_LOCK_ASSERT() mtx_assert(&Giant, MA_OWNED)
+/*
+ * Assert that the lock is held in the contexts
+ * where it really has to be so.
+ */
+#define KMI_CTX_LOCK_ASSERT() \
+ do { \
+ if (!kdb_active && !KERNEL_PANICKED()) \
+ mtx_assert(&Giant, MA_OWNED); \
+ } while (0)
+#else
+#define KMI_LOCK_ASSERT() (void)0
+#define KMI_CTX_LOCK_ASSERT() (void)0
+#endif
+
+#define KMICR 0x00
+#define KMICR_TYPE_NONPS2 (1 << 5)
+#define KMICR_RXINTREN (1 << 4)
+#define KMICR_TXINTREN (1 << 3)
+#define KMICR_EN (1 << 2)
+#define KMICR_FKMID (1 << 1)
+#define KMICR_FKMIC (1 << 0)
+#define KMISTAT 0x04
+#define KMISTAT_TXEMPTY (1 << 6)
+#define KMISTAT_TXBUSY (1 << 5)
+#define KMISTAT_RXFULL (1 << 4)
+#define KMISTAT_RXBUSY (1 << 3)
+#define KMISTAT_RXPARITY (1 << 2)
+#define KMISTAT_KMIC (1 << 1)
+#define KMISTAT_KMID (1 << 0)
+#define KMIDATA 0x08
+#define KMICLKDIV 0x0C
+#define KMIIR 0x10
+#define KMIIR_TXINTR (1 << 1)
+#define KMIIR_RXINTR (1 << 0)
+
+#define KMI_DRIVER_NAME "kmi"
+#define KMI_NFKEY (sizeof(fkey_tab)/sizeof(fkey_tab[0])) /* units */
+
+#define SET_SCANCODE_SET 0xf0
+
+struct kmi_softc {
+ device_t sc_dev;
+ keyboard_t sc_kbd;
+ keymap_t sc_keymap;
+ accentmap_t sc_accmap;
+ fkeytab_t sc_fkeymap[KMI_NFKEY];
+
+ struct resource* sc_mem_res;
+ struct resource* sc_irq_res;
+ void* sc_intr_hl;
+
+ int sc_mode; /* input mode (K_XLATE,K_RAW,K_CODE) */
+ int sc_state; /* shift/lock key state */
+ int sc_accents; /* accent key index (> 0) */
+ uint32_t sc_flags; /* flags */
+#define KMI_FLAG_COMPOSE 0x00000001
+#define KMI_FLAG_POLLING 0x00000002
+
+ struct thread *sc_poll_thread;
+};
+
+/* Read/Write macros for Timer used as timecounter */
+#define pl050_kmi_read_4(sc, reg) \
+ bus_read_4((sc)->sc_mem_res, (reg))
+
+#define pl050_kmi_write_4(sc, reg, val) \
+ bus_write_4((sc)->sc_mem_res, (reg), (val))
+
+/* prototypes */
+static void kmi_set_leds(struct kmi_softc *, uint8_t);
+static int kmi_set_typematic(keyboard_t *, int);
+static uint32_t kmi_read_char(keyboard_t *, int);
+static void kmi_clear_state(keyboard_t *);
+static int kmi_ioctl(keyboard_t *, u_long, caddr_t);
+static int kmi_enable(keyboard_t *);
+static int kmi_disable(keyboard_t *);
+
+static int kmi_attached = 0;
+
+/* early keyboard probe, not supported */
+static int
+kmi_configure(int flags)
+{
+ return (0);
+}
+
+/* detect a keyboard, not used */
+static int
+kmi_probe(int unit, void *arg, int flags)
+{
+ return (ENXIO);
+}
+
+/* reset and initialize the device, not used */
+static int
+kmi_init(int unit, keyboard_t **kbdp, void *arg, int flags)
+{
+ return (ENXIO);
+}
+
+/* test the interface to the device, not used */
+static int
+kmi_test_if(keyboard_t *kbd)
+{
+ return (0);
+}
+
+/* finish using this keyboard, not used */
+static int
+kmi_term(keyboard_t *kbd)
+{
+ return (ENXIO);
+}
+
+/* keyboard interrupt routine, not used */
+static int
+kmi_intr(keyboard_t *kbd, void *arg)
+{
+
+ return (0);
+}
+
+/* lock the access to the keyboard, not used */
+static int
+kmi_lock(keyboard_t *kbd, int lock)
+{
+ return (1);
+}
+
+/*
+ * Enable the access to the device; until this function is called,
+ * the client cannot read from the keyboard.
+ */
+static int
+kmi_enable(keyboard_t *kbd)
+{
+
+ KMI_LOCK();
+ KBD_ACTIVATE(kbd);
+ KMI_UNLOCK();
+
+ return (0);
+}
+
+/* disallow the access to the device */
+static int
+kmi_disable(keyboard_t *kbd)
+{
+
+ KMI_LOCK();
+ KBD_DEACTIVATE(kbd);
+ KMI_UNLOCK();
+
+ return (0);
+}
+
+/* check if data is waiting */
+static int
+kmi_check(keyboard_t *kbd)
+{
+ struct kmi_softc *sc = kbd->kb_data;
+ uint32_t reg;
+
+ KMI_CTX_LOCK_ASSERT();
+
+ if (!KBD_IS_ACTIVE(kbd))
+ return (0);
+
+ reg = pl050_kmi_read_4(sc, KMIIR);
+ return (reg & KMIIR_RXINTR);
+}
+
+/* check if char is waiting */
+static int
+kmi_check_char_locked(keyboard_t *kbd)
+{
+ KMI_CTX_LOCK_ASSERT();
+
+ if (!KBD_IS_ACTIVE(kbd))
+ return (0);
+
+ return (kmi_check(kbd));
+}
+
+static int
+kmi_check_char(keyboard_t *kbd)
+{
+ int result;
+
+ KMI_LOCK();
+ result = kmi_check_char_locked(kbd);
+ KMI_UNLOCK();
+
+ return (result);
+}
+
+/* read one byte from the keyboard if it's allowed */
+/* Currently unused. */
+static int
+kmi_read(keyboard_t *kbd, int wait)
+{
+ KMI_CTX_LOCK_ASSERT();
+
+ if (!KBD_IS_ACTIVE(kbd))
+ return (-1);
+
+ ++(kbd->kb_count);
+ printf("Implement ME: %s\n", __func__);
+ return (0);
+}
+
+/* read char from the keyboard */
+static uint32_t
+kmi_read_char_locked(keyboard_t *kbd, int wait)
+{
+ struct kmi_softc *sc = kbd->kb_data;
+ uint32_t reg, data;
+
+ KMI_CTX_LOCK_ASSERT();
+
+ if (!KBD_IS_ACTIVE(kbd))
+ return (NOKEY);
+
+ reg = pl050_kmi_read_4(sc, KMIIR);
+ if (reg & KMIIR_RXINTR) {
+ data = pl050_kmi_read_4(sc, KMIDATA);
+ return (data);
+ }
+
+ ++kbd->kb_count;
+ return (NOKEY);
+}
+
+/* Currently wait is always false. */
+static uint32_t
+kmi_read_char(keyboard_t *kbd, int wait)
+{
+ uint32_t keycode;
+
+ KMI_LOCK();
+ keycode = kmi_read_char_locked(kbd, wait);
+ KMI_UNLOCK();
+
+ return (keycode);
+}
+
+/* some useful control functions */
+static int
+kmi_ioctl_locked(keyboard_t *kbd, u_long cmd, caddr_t arg)
+{
+ struct kmi_softc *sc = kbd->kb_data;
+ int i;
+#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
+ defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
+ int ival;
+
+#endif
+
+ KMI_LOCK_ASSERT();
+
+ switch (cmd) {
+ case KDGKBMODE: /* get keyboard mode */
+ *(int *)arg = sc->sc_mode;
+ break;
+#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
+ defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
+ case _IO('K', 7):
+ ival = IOCPARM_IVAL(arg);
+ arg = (caddr_t)&ival;
+ /* FALLTHROUGH */
+#endif
+ case KDSKBMODE: /* set keyboard mode */
+ switch (*(int *)arg) {
+ case K_XLATE:
+ if (sc->sc_mode != K_XLATE) {
+ /* make lock key state and LED state match */
+ sc->sc_state &= ~LOCK_MASK;
+ sc->sc_state |= KBD_LED_VAL(kbd);
+ }
+ /* FALLTHROUGH */
+ case K_RAW:
+ case K_CODE:
+ if (sc->sc_mode != *(int *)arg) {
+ if ((sc->sc_flags & KMI_FLAG_POLLING) == 0)
+ kmi_clear_state(kbd);
+ sc->sc_mode = *(int *)arg;
+ }
+ break;
+ default:
+ return (EINVAL);
+ }
+ break;
+
+ case KDGETLED: /* get keyboard LED */
+ *(int *)arg = KBD_LED_VAL(kbd);
+ break;
+#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
+ defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
+ case _IO('K', 66):
+ ival = IOCPARM_IVAL(arg);
+ arg = (caddr_t)&ival;
+ /* FALLTHROUGH */
+#endif
+ case KDSETLED: /* set keyboard LED */
+ /* NOTE: lock key state in "sc_state" won't be changed */
+ if (*(int *)arg & ~LOCK_MASK)
+ return (EINVAL);
+
+ i = *(int *)arg;
+
+ /* replace CAPS LED with ALTGR LED for ALTGR keyboards */
+ if (sc->sc_mode == K_XLATE &&
+ kbd->kb_keymap->n_keys > ALTGR_OFFSET) {
+ if (i & ALKED)
+ i |= CLKED;
+ else
+ i &= ~CLKED;
+ }
+ if (KBD_HAS_DEVICE(kbd))
+ kmi_set_leds(sc, i);
+
+ KBD_LED_VAL(kbd) = *(int *)arg;
+ break;
+ case KDGKBSTATE: /* get lock key state */
+ *(int *)arg = sc->sc_state & LOCK_MASK;
+ break;
+#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
+ defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
+ case _IO('K', 20):
+ ival = IOCPARM_IVAL(arg);
+ arg = (caddr_t)&ival;
+ /* FALLTHROUGH */
+#endif
+ case KDSKBSTATE: /* set lock key state */
+ if (*(int *)arg & ~LOCK_MASK) {
+ return (EINVAL);
+ }
+ sc->sc_state &= ~LOCK_MASK;
+ sc->sc_state |= *(int *)arg;
+
+ /* set LEDs and quit */
+ return (kmi_ioctl(kbd, KDSETLED, arg));
+
+ case KDSETREPEAT: /* set keyboard repeat rate (new
+ * interface) */
+ if (!KBD_HAS_DEVICE(kbd)) {
+ return (0);
+ }
+ if (((int *)arg)[1] < 0) {
+ return (EINVAL);
+ }
+ if (((int *)arg)[0] < 0) {
+ return (EINVAL);
+ }
+ if (((int *)arg)[0] < 200) /* fastest possible value */
+ kbd->kb_delay1 = 200;
+ else
+ kbd->kb_delay1 = ((int *)arg)[0];
+ kbd->kb_delay2 = ((int *)arg)[1];
+ return (0);
+
+#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
+ defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
+ case _IO('K', 67):
+ ival = IOCPARM_IVAL(arg);
+ arg = (caddr_t)&ival;
+ /* FALLTHROUGH */
+#endif
+ case KDSETRAD: /* set keyboard repeat rate (old
+ * interface) */
+ return (kmi_set_typematic(kbd, *(int *)arg));
+
+ case PIO_KEYMAP: /* set keyboard translation table */
+ case OPIO_KEYMAP: /* set keyboard translation table
+ * (compat) */
+ case PIO_KEYMAPENT: /* set keyboard translation table
+ * entry */
+ case PIO_DEADKEYMAP: /* set accent key translation table */
+ sc->sc_accents = 0;
+ /* FALLTHROUGH */
+ default:
+ return (genkbd_commonioctl(kbd, cmd, arg));
+ }
+
+ return (0);
+}
+
+static int
+kmi_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg)
+{
+ int result;
+
+ /*
+ * XXX KDGKBSTATE, KDSKBSTATE and KDSETLED can be called from any
+ * context where printf(9) can be called, which among other things
+ * includes interrupt filters and threads with any kinds of locks
+ * already held. For this reason it would be dangerous to acquire
+ * the Giant here unconditionally. On the other hand we have to
+ * have it to handle the ioctl.
+ * So we make our best effort to auto-detect whether we can grab
+ * the Giant or not. Blame syscons(4) for this.
+ */
+ switch (cmd) {
+ case KDGKBSTATE:
+ case KDSKBSTATE:
+ case KDSETLED:
+ if (!mtx_owned(&Giant) && !SCHEDULER_STOPPED())
+ return (EDEADLK); /* best I could come up with */
+ /* FALLTHROUGH */
+ default:
+ KMI_LOCK();
+ result = kmi_ioctl_locked(kbd, cmd, arg);
+ KMI_UNLOCK();
+ return (result);
+ }
+}
+
+/* clear the internal state of the keyboard */
+static void
+kmi_clear_state(keyboard_t *kbd)
+{
+ struct kmi_softc *sc = kbd->kb_data;
+
+ KMI_CTX_LOCK_ASSERT();
+
+ sc->sc_flags &= ~(KMI_FLAG_COMPOSE | KMI_FLAG_POLLING);
+ sc->sc_state &= LOCK_MASK; /* preserve locking key state */
+ sc->sc_accents = 0;
+}
+
+/* save the internal state, not used */
+static int
+kmi_get_state(keyboard_t *kbd, void *buf, size_t len)
+{
+ return (len == 0) ? 1 : -1;
+}
+
+/* set the internal state, not used */
+static int
+kmi_set_state(keyboard_t *kbd, void *buf, size_t len)
+{
+ return (EINVAL);
+}
+
+static int
+kmi_poll(keyboard_t *kbd, int on)
+{
+ struct kmi_softc *sc = kbd->kb_data;
+
+ KMI_LOCK();
+ if (on) {
+ sc->sc_flags |= KMI_FLAG_POLLING;
+ sc->sc_poll_thread = curthread;
+ } else {
+ sc->sc_flags &= ~KMI_FLAG_POLLING;
+ }
+ KMI_UNLOCK();
+
+ return (0);
+}
+
+/* local functions */
+
+static void
+kmi_set_leds(struct kmi_softc *sc, uint8_t leds)
+{
+
+ KMI_LOCK_ASSERT();
+
+ /* start transfer, if not already started */
+ printf("Implement me: %s\n", __func__);
+}
+
+static int
+kmi_set_typematic(keyboard_t *kbd, int code)
+{
+ static const int delays[] = {250, 500, 750, 1000};
+ static const int rates[] = {34, 38, 42, 46, 50, 55, 59, 63,
+ 68, 76, 84, 92, 100, 110, 118, 126,
+ 136, 152, 168, 184, 200, 220, 236, 252,
+ 272, 304, 336, 368, 400, 440, 472, 504};
+
+ if (code & ~0x7f) {
+ return (EINVAL);
+ }
+ kbd->kb_delay1 = delays[(code >> 5) & 3];
+ kbd->kb_delay2 = rates[code & 0x1f];
+ return (0);
+}
+
+static keyboard_switch_t kmisw = {
+ .probe = &kmi_probe,
+ .init = &kmi_init,
+ .term = &kmi_term,
+ .intr = &kmi_intr,
+ .test_if = &kmi_test_if,
+ .enable = &kmi_enable,
+ .disable = &kmi_disable,
+ .read = &kmi_read,
+ .check = &kmi_check,
+ .read_char = &kmi_read_char,
+ .check_char = &kmi_check_char,
+ .ioctl = &kmi_ioctl,
+ .lock = &kmi_lock,
+ .clear_state = &kmi_clear_state,
+ .get_state = &kmi_get_state,
+ .set_state = &kmi_set_state,
+ .poll = &kmi_poll,
+};
+
+KEYBOARD_DRIVER(kmi, kmisw, kmi_configure);
+
+static void
+pl050_kmi_intr(void *arg)
+{
+ struct kmi_softc *sc = arg;
+ uint32_t c;
+
+ KMI_CTX_LOCK_ASSERT();
+
+ if ((sc->sc_flags & KMI_FLAG_POLLING) != 0)
+ return;
+
+ if (KBD_IS_ACTIVE(&sc->sc_kbd) &&
+ KBD_IS_BUSY(&sc->sc_kbd)) {
+ /* let the callback function process the input */
+ (sc->sc_kbd.kb_callback.kc_func) (&sc->sc_kbd, KBDIO_KEYINPUT,
+ sc->sc_kbd.kb_callback.kc_arg);
+ } else {
+ /* read and discard the input, no one is waiting for it */
+ do {
+ c = kmi_read_char_locked(&sc->sc_kbd, 0);
+ } while (c != NOKEY);
+ }
+
+}
+
+static int
+pl050_kmi_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ /*
+ * PL050 is plain PS2 port that pushes bytes to/from computer
+ * VersatilePB has two such ports and QEMU simulates keyboard
+ * connected to port #0 and mouse connected to port #1. This
+ * information can't be obtained from device tree so we just
+ * hardcode this knowledge here. We attach keyboard driver to
+ * port #0 and ignore port #1
+ */
+ if (kmi_attached)
+ return (ENXIO);
+
+ if (ofw_bus_is_compatible(dev, "arm,pl050")) {
+ device_set_desc(dev, "PL050 Keyboard/Mouse Interface");
+ return (BUS_PROBE_DEFAULT);
+ }
+
+ return (ENXIO);
+}
+
+static int
+pl050_kmi_attach(device_t dev)
+{
+ struct kmi_softc *sc = device_get_softc(dev);
+ keyboard_t *kbd;
+ int rid;
+ int i;
+ uint32_t ack;
+
+ sc->sc_dev = dev;
+ kbd = &sc->sc_kbd;
+ rid = 0;
+
+ sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
+ if (sc->sc_mem_res == NULL) {
+ device_printf(dev, "could not allocate memory resource\n");
+ return (ENXIO);
+ }
+
+ /* Request the IRQ resources */
+ sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
+ if (sc->sc_irq_res == NULL) {
+ device_printf(dev, "Error: could not allocate irq resources\n");
+ return (ENXIO);
+ }
+
+ /* Setup and enable the timer */
+ if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_CLK,
+ NULL, pl050_kmi_intr, sc,
+ &sc->sc_intr_hl) != 0) {
+ bus_release_resource(dev, SYS_RES_IRQ, rid,
+ sc->sc_irq_res);
+ device_printf(dev, "Unable to setup the clock irq handler.\n");
+ return (ENXIO);
+ }
+
+ /* TODO: clock & divisor */
+
+ pl050_kmi_write_4(sc, KMICR, KMICR_EN);
+
+ pl050_kmi_write_4(sc, KMIDATA, SET_SCANCODE_SET);
+ /* read out ACK */
+ ack = pl050_kmi_read_4(sc, KMIDATA);
+ /* Set Scan Code set 1 (XT) */
+ pl050_kmi_write_4(sc, KMIDATA, 1);
+ /* read out ACK */
+ ack = pl050_kmi_read_4(sc, KMIDATA);
+
+ pl050_kmi_write_4(sc, KMICR, KMICR_EN | KMICR_RXINTREN);
+
+ kbd_init_struct(kbd, KMI_DRIVER_NAME, KB_OTHER,
+ device_get_unit(dev), 0, 0, 0);
+ kbd->kb_data = (void *)sc;
+
+ sc->sc_keymap = key_map;
+ sc->sc_accmap = accent_map;
+ for (i = 0; i < KMI_NFKEY; i++) {
+ sc->sc_fkeymap[i] = fkey_tab[i];
+ }
+
+ kbd_set_maps(kbd, &sc->sc_keymap, &sc->sc_accmap,
+ sc->sc_fkeymap, KMI_NFKEY);
+
+ KBD_FOUND_DEVICE(kbd);
+ kmi_clear_state(kbd);
+ KBD_PROBE_DONE(kbd);
+
+ KBD_INIT_DONE(kbd);
+
+ if (kbd_register(kbd) < 0) {
+ goto detach;
+ }
+ KBD_CONFIG_DONE(kbd);
+
+#ifdef KBD_INSTALL_CDEV
+ if (kbd_attach(kbd)) {
+ goto detach;
+ }
+#endif
+
+ if (bootverbose) {
+ kbdd_diag(kbd, bootverbose);
+ }
+ kmi_attached = 1;
+ return (0);
+
+detach:
+ return (ENXIO);
+
+}
+
+static device_method_t pl050_kmi_methods[] = {
+ DEVMETHOD(device_probe, pl050_kmi_probe),
+ DEVMETHOD(device_attach, pl050_kmi_attach),
+ { 0, 0 }
+};
+
+static driver_t pl050_kmi_driver = {
+ "kmi",
+ pl050_kmi_methods,
+ sizeof(struct kmi_softc),
+};
+
+static devclass_t pl050_kmi_devclass;
+
+DRIVER_MODULE(pl050_kmi, simplebus, pl050_kmi_driver, pl050_kmi_devclass, 0, 0);
diff --git a/sys/arm/versatile/sp804.c b/sys/arm/versatile/sp804.c
new file mode 100644
index 000000000000..e45541b0abe5
--- /dev/null
+++ b/sys/arm/versatile/sp804.c
@@ -0,0 +1,343 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * Copyright (c) 2012 Damjan Marion <dmarion@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <machine/machdep.h> /* For arm_set_delay */
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+
+#define SP804_TIMER1_LOAD 0x00
+#define SP804_TIMER1_VALUE 0x04
+#define SP804_TIMER1_CONTROL 0x08
+#define TIMER_CONTROL_EN (1 << 7)
+#define TIMER_CONTROL_FREERUN (0 << 6)
+#define TIMER_CONTROL_PERIODIC (1 << 6)
+#define TIMER_CONTROL_INTREN (1 << 5)
+#define TIMER_CONTROL_DIV1 (0 << 2)
+#define TIMER_CONTROL_DIV16 (1 << 2)
+#define TIMER_CONTROL_DIV256 (2 << 2)
+#define TIMER_CONTROL_32BIT (1 << 1)
+#define TIMER_CONTROL_ONESHOT (1 << 0)
+#define SP804_TIMER1_INTCLR 0x0C
+#define SP804_TIMER1_RIS 0x10
+#define SP804_TIMER1_MIS 0x14
+#define SP804_TIMER1_BGLOAD 0x18
+#define SP804_TIMER2_LOAD 0x20
+#define SP804_TIMER2_VALUE 0x24
+#define SP804_TIMER2_CONTROL 0x28
+#define SP804_TIMER2_INTCLR 0x2C
+#define SP804_TIMER2_RIS 0x30
+#define SP804_TIMER2_MIS 0x34
+#define SP804_TIMER2_BGLOAD 0x38
+
+#define SP804_PERIPH_ID0 0xFE0
+#define SP804_PERIPH_ID1 0xFE4
+#define SP804_PERIPH_ID2 0xFE8
+#define SP804_PERIPH_ID3 0xFEC
+#define SP804_PRIMECELL_ID0 0xFF0
+#define SP804_PRIMECELL_ID1 0xFF4
+#define SP804_PRIMECELL_ID2 0xFF8
+#define SP804_PRIMECELL_ID3 0xFFC
+
+#define DEFAULT_FREQUENCY 1000000
+/*
+ * QEMU seems to have problem with full frequency
+ */
+#define DEFAULT_DIVISOR 16
+#define DEFAULT_CONTROL_DIV TIMER_CONTROL_DIV16
+
+struct sp804_timer_softc {
+ struct resource* mem_res;
+ struct resource* irq_res;
+ void* intr_hl;
+ uint32_t sysclk_freq;
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+ struct timecounter tc;
+ bool et_enabled;
+ struct eventtimer et;
+ int timer_initialized;
+};
+
+/* Read/Write macros for Timer used as timecounter */
+#define sp804_timer_tc_read_4(reg) \
+ bus_space_read_4(sc->bst, sc->bsh, reg)
+
+#define sp804_timer_tc_write_4(reg, val) \
+ bus_space_write_4(sc->bst, sc->bsh, reg, val)
+
+static unsigned sp804_timer_tc_get_timecount(struct timecounter *);
+static void sp804_timer_delay(int, void *);
+
+static unsigned
+sp804_timer_tc_get_timecount(struct timecounter *tc)
+{
+ struct sp804_timer_softc *sc = tc->tc_priv;
+ return 0xffffffff - sp804_timer_tc_read_4(SP804_TIMER1_VALUE);
+}
+
+static int
+sp804_timer_start(struct eventtimer *et, sbintime_t first, sbintime_t period)
+{
+ struct sp804_timer_softc *sc = et->et_priv;
+ uint32_t count, reg;
+
+ if (first != 0) {
+ sc->et_enabled = 1;
+
+ count = ((uint32_t)et->et_frequency * first) >> 32;
+
+ sp804_timer_tc_write_4(SP804_TIMER2_LOAD, count);
+ reg = TIMER_CONTROL_32BIT | TIMER_CONTROL_INTREN |
+ TIMER_CONTROL_PERIODIC | DEFAULT_CONTROL_DIV |
+ TIMER_CONTROL_EN;
+ sp804_timer_tc_write_4(SP804_TIMER2_CONTROL, reg);
+
+ return (0);
+ }
+
+ if (period != 0) {
+ panic("period");
+ }
+
+ return (EINVAL);
+}
+
+static int
+sp804_timer_stop(struct eventtimer *et)
+{
+ struct sp804_timer_softc *sc = et->et_priv;
+ uint32_t reg;
+
+ sc->et_enabled = 0;
+ reg = sp804_timer_tc_read_4(SP804_TIMER2_CONTROL);
+ reg &= ~(TIMER_CONTROL_EN);
+ sp804_timer_tc_write_4(SP804_TIMER2_CONTROL, reg);
+
+ return (0);
+}
+
+static int
+sp804_timer_intr(void *arg)
+{
+ struct sp804_timer_softc *sc = arg;
+ static uint32_t prev = 0;
+ uint32_t x = 0;
+
+ x = sp804_timer_tc_read_4(SP804_TIMER1_VALUE);
+
+ prev =x ;
+ sp804_timer_tc_write_4(SP804_TIMER2_INTCLR, 1);
+ if (sc->et_enabled) {
+ if (sc->et.et_active) {
+ sc->et.et_event_cb(&sc->et, sc->et.et_arg);
+ }
+ }
+
+ return (FILTER_HANDLED);
+}
+
+static int
+sp804_timer_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_is_compatible(dev, "arm,sp804")) {
+ device_set_desc(dev, "SP804 System Timer");
+ return (BUS_PROBE_DEFAULT);
+ }
+
+ return (ENXIO);
+}
+
+static int
+sp804_timer_attach(device_t dev)
+{
+ struct sp804_timer_softc *sc = device_get_softc(dev);
+ int rid = 0;
+ int i;
+ uint32_t id, reg;
+ phandle_t node;
+ pcell_t clock;
+
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "could not allocate memory resource\n");
+ return (ENXIO);
+ }
+
+ sc->bst = rman_get_bustag(sc->mem_res);
+ sc->bsh = rman_get_bushandle(sc->mem_res);
+
+ /* Request the IRQ resources */
+ sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
+ if (sc->irq_res == NULL) {
+ device_printf(dev, "Error: could not allocate irq resources\n");
+ return (ENXIO);
+ }
+
+ sc->sysclk_freq = DEFAULT_FREQUENCY;
+ /* Get the base clock frequency */
+ node = ofw_bus_get_node(dev);
+ if ((OF_getencprop(node, "clock-frequency", &clock, sizeof(clock))) > 0) {
+ sc->sysclk_freq = clock;
+ }
+
+ /* Setup and enable the timer */
+ if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_CLK,
+ sp804_timer_intr, NULL, sc,
+ &sc->intr_hl) != 0) {
+ bus_release_resource(dev, SYS_RES_IRQ, rid,
+ sc->irq_res);
+ device_printf(dev, "Unable to setup the clock irq handler.\n");
+ return (ENXIO);
+ }
+
+ sp804_timer_tc_write_4(SP804_TIMER1_CONTROL, 0);
+ sp804_timer_tc_write_4(SP804_TIMER2_CONTROL, 0);
+
+ /*
+ * Timer 1, timecounter
+ */
+ sc->tc.tc_frequency = sc->sysclk_freq;
+ sc->tc.tc_name = "SP804-1";
+ sc->tc.tc_get_timecount = sp804_timer_tc_get_timecount;
+ sc->tc.tc_poll_pps = NULL;
+ sc->tc.tc_counter_mask = ~0u;
+ sc->tc.tc_quality = 1000;
+ sc->tc.tc_priv = sc;
+
+ sp804_timer_tc_write_4(SP804_TIMER1_VALUE, 0xffffffff);
+ sp804_timer_tc_write_4(SP804_TIMER1_LOAD, 0xffffffff);
+ reg = TIMER_CONTROL_PERIODIC | TIMER_CONTROL_32BIT;
+ sp804_timer_tc_write_4(SP804_TIMER1_CONTROL, reg);
+ reg |= TIMER_CONTROL_EN;
+ sp804_timer_tc_write_4(SP804_TIMER1_CONTROL, reg);
+ tc_init(&sc->tc);
+
+ /*
+ * Timer 2, event timer
+ */
+ sc->et_enabled = 0;
+ sc->et.et_name = "SP804-2";
+ sc->et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT;
+ sc->et.et_quality = 1000;
+ sc->et.et_frequency = sc->sysclk_freq / DEFAULT_DIVISOR;
+ sc->et.et_min_period = (0x00000002LLU << 32) / sc->et.et_frequency;
+ sc->et.et_max_period = (0xfffffffeLLU << 32) / sc->et.et_frequency;
+ sc->et.et_start = sp804_timer_start;
+ sc->et.et_stop = sp804_timer_stop;
+ sc->et.et_priv = sc;
+ et_register(&sc->et);
+
+ id = 0;
+ for (i = 3; i >= 0; i--) {
+ id = (id << 8) |
+ (sp804_timer_tc_read_4(SP804_PERIPH_ID0 + i*4) & 0xff);
+ }
+
+ device_printf(dev, "peripheral ID: %08x\n", id);
+
+ id = 0;
+ for (i = 3; i >= 0; i--) {
+ id = (id << 8) |
+ (sp804_timer_tc_read_4(SP804_PRIMECELL_ID0 + i*4) & 0xff);
+ }
+
+ arm_set_delay(sp804_timer_delay, sc);
+
+ device_printf(dev, "PrimeCell ID: %08x\n", id);
+
+ sc->timer_initialized = 1;
+
+ return (0);
+}
+
+static device_method_t sp804_timer_methods[] = {
+ DEVMETHOD(device_probe, sp804_timer_probe),
+ DEVMETHOD(device_attach, sp804_timer_attach),
+ { 0, 0 }
+};
+
+static driver_t sp804_timer_driver = {
+ "timer",
+ sp804_timer_methods,
+ sizeof(struct sp804_timer_softc),
+};
+
+static devclass_t sp804_timer_devclass;
+
+DRIVER_MODULE(sp804_timer, simplebus, sp804_timer_driver, sp804_timer_devclass, 0, 0);
+
+static void
+sp804_timer_delay(int usec, void *arg)
+{
+ struct sp804_timer_softc *sc = arg;
+ int32_t counts;
+ uint32_t first, last;
+
+ /* Get the number of times to count */
+ counts = usec * ((sc->tc.tc_frequency / 1000000) + 1);
+
+ first = sp804_timer_tc_get_timecount(&sc->tc);
+
+ while (counts > 0) {
+ last = sp804_timer_tc_get_timecount(&sc->tc);
+ if (last == first)
+ continue;
+ if (last > first) {
+ counts -= (int32_t)(last - first);
+ } else {
+ counts -= (int32_t)((0xFFFFFFFF - first) + last);
+ }
+ first = last;
+ }
+}
diff --git a/sys/arm/versatile/versatile_clcd.c b/sys/arm/versatile/versatile_clcd.c
new file mode 100644
index 000000000000..81a0dbcc5256
--- /dev/null
+++ b/sys/arm/versatile/versatile_clcd.c
@@ -0,0 +1,921 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012-2017 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/fbio.h>
+#include <sys/consio.h>
+#include <sys/kdb.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/fb/fbreg.h>
+#include <dev/syscons/syscons.h>
+
+#include <arm/versatile/versatile_scm.h>
+
+#include <machine/bus.h>
+
+#define PL110_VENDOR_ARM926PXP 1
+
+#define CLCD_MODE_RGB888 0x0
+#define CLCD_MODE_RGB555 0x01
+#define CLCD_MODE_RBG565 0x02
+#define CLCD_MODE_RGB565 0x03
+
+#define CLCDC_TIMING0 0x00
+#define CLCDC_TIMING1 0x04
+#define CLCDC_TIMING2 0x08
+#define CLCDC_TIMING3 0x0C
+#define CLCDC_TIMING3 0x0C
+#define CLCDC_UPBASE 0x10
+#define CLCDC_LPBASE 0x14
+#ifdef PL110_VENDOR_ARM926PXP
+#define CLCDC_CONTROL 0x18
+#define CLCDC_IMSC 0x1C
+#else
+#define CLCDC_IMSC 0x18
+#define CLCDC_CONTROL 0x1C
+#endif
+#define CONTROL_WATERMARK (1 << 16)
+#define CONTROL_VCOMP_VS (0 << 12)
+#define CONTROL_VCOMP_BP (1 << 12)
+#define CONTROL_VCOMP_SAV (2 << 12)
+#define CONTROL_VCOMP_FP (3 << 12)
+#define CONTROL_PWR (1 << 11)
+#define CONTROL_BEPO (1 << 10)
+#define CONTROL_BEBO (1 << 9)
+#define CONTROL_BGR (1 << 8)
+#define CONTROL_DUAL (1 << 7)
+#define CONTROL_MONO8 (1 << 6)
+#define CONTROL_TFT (1 << 5)
+#define CONTROL_BW (1 << 4)
+#define CONTROL_BPP1 (0x00 << 1)
+#define CONTROL_BPP2 (0x01 << 1)
+#define CONTROL_BPP4 (0x02 << 1)
+#define CONTROL_BPP8 (0x03 << 1)
+#define CONTROL_BPP16 (0x04 << 1)
+#define CONTROL_BPP24 (0x05 << 1)
+#define CONTROL_EN (1 << 0)
+#define CLCDC_RIS 0x20
+#define CLCDC_MIS 0x24
+#define INTR_MBERR (1 << 4)
+#define INTR_VCOMP (1 << 3)
+#define INTR_LNB (1 << 2)
+#define INTR_FUF (1 << 1)
+#define CLCDC_ICR 0x28
+
+#ifdef DEBUG
+#define dprintf(fmt, args...) do { printf("%s(): ", __func__); \
+ printf(fmt,##args); } while (0)
+#else
+#define dprintf(fmt, args...)
+#endif
+
+#define versatile_clcdc_read_4(sc, reg) \
+ bus_read_4((sc)->mem_res, (reg))
+#define versatile_clcdc_write_4(sc, reg, val) \
+ bus_write_4((sc)->mem_res, (reg), (val))
+
+struct versatile_clcdc_softc {
+ struct resource* mem_res;
+
+ struct mtx mtx;
+
+ int width;
+ int height;
+ int mode;
+
+ bus_dma_tag_t dma_tag;
+ bus_dmamap_t dma_map;
+ bus_addr_t fb_phys;
+ uint8_t *fb_base;
+
+};
+
+struct video_adapter_softc {
+ /* Videoadpater part */
+ video_adapter_t va;
+ int console;
+
+ intptr_t fb_addr;
+ unsigned int fb_size;
+
+ unsigned int height;
+ unsigned int width;
+ unsigned int depth;
+ unsigned int stride;
+
+ unsigned int xmargin;
+ unsigned int ymargin;
+
+ unsigned char *font;
+ int initialized;
+};
+
+struct argb {
+ uint8_t a;
+ uint8_t r;
+ uint8_t g;
+ uint8_t b;
+};
+
+static struct argb versatilefb_palette[16] = {
+ {0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0xaa},
+ {0x00, 0x00, 0xaa, 0x00},
+ {0x00, 0x00, 0xaa, 0xaa},
+ {0x00, 0xaa, 0x00, 0x00},
+ {0x00, 0xaa, 0x00, 0xaa},
+ {0x00, 0xaa, 0x55, 0x00},
+ {0x00, 0xaa, 0xaa, 0xaa},
+ {0x00, 0x55, 0x55, 0x55},
+ {0x00, 0x55, 0x55, 0xff},
+ {0x00, 0x55, 0xff, 0x55},
+ {0x00, 0x55, 0xff, 0xff},
+ {0x00, 0xff, 0x55, 0x55},
+ {0x00, 0xff, 0x55, 0xff},
+ {0x00, 0xff, 0xff, 0x55},
+ {0x00, 0xff, 0xff, 0xff}
+};
+
+/* mouse pointer from dev/syscons/scgfbrndr.c */
+static u_char mouse_pointer[16] = {
+ 0x00, 0x40, 0x60, 0x70, 0x78, 0x7c, 0x7e, 0x68,
+ 0x0c, 0x0c, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00
+};
+
+#define FB_WIDTH 640
+#define FB_HEIGHT 480
+#define FB_DEPTH 16
+
+#define VERSATILE_FONT_HEIGHT 16
+
+static struct video_adapter_softc va_softc;
+
+static int versatilefb_configure(int);
+static void versatilefb_update_margins(video_adapter_t *adp);
+
+static void
+versatile_fb_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err)
+{
+ bus_addr_t *addr;
+
+ if (err)
+ return;
+
+ addr = (bus_addr_t*)arg;
+ *addr = segs[0].ds_addr;
+}
+
+static int
+versatile_clcdc_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_is_compatible(dev, "arm,pl110")) {
+ device_set_desc(dev, "PL110 CLCD controller");
+ return (BUS_PROBE_DEFAULT);
+ }
+
+ return (ENXIO);
+}
+
+static int
+versatile_clcdc_attach(device_t dev)
+{
+ struct versatile_clcdc_softc *sc = device_get_softc(dev);
+ struct video_adapter_softc *va_sc = &va_softc;
+ int err, rid;
+ uint32_t reg;
+ int clcdid;
+ int dma_size;
+
+ /* Request memory resources */
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "could not allocate memory resources\n");
+ return (ENXIO);
+ }
+
+ err = versatile_scm_reg_read_4(SCM_CLCD, &reg);
+ if (err) {
+ device_printf(dev, "failed to read SCM register\n");
+ goto fail;
+ }
+ clcdid = (reg >> SCM_CLCD_CLCDID_SHIFT) & SCM_CLCD_CLCDID_MASK;
+ switch (clcdid) {
+ case 31:
+ device_printf(dev, "QEMU VGA 640x480\n");
+ sc->width = 640;
+ sc->height = 480;
+ break;
+ default:
+ device_printf(dev, "Unsupported: %d\n", clcdid);
+ goto fail;
+ }
+
+ reg &= ~SCM_CLCD_LCD_MODE_MASK;
+ reg |= CLCD_MODE_RGB565;
+ sc->mode = CLCD_MODE_RGB565;
+ versatile_scm_reg_write_4(SCM_CLCD, reg);
+ dma_size = sc->width*sc->height*2;
+
+ /*
+ * Power on LCD
+ */
+ reg |= SCM_CLCD_PWR3V5VSWITCH | SCM_CLCD_NLCDIOON;
+ versatile_scm_reg_write_4(SCM_CLCD, reg);
+
+ /*
+ * XXX: hardcoded timing for VGA. For other modes/panels
+ * we need to keep table of timing register values
+ */
+ /*
+ * XXX: set SYS_OSC1
+ */
+ versatile_clcdc_write_4(sc, CLCDC_TIMING0, 0x3F1F3F9C);
+ versatile_clcdc_write_4(sc, CLCDC_TIMING1, 0x090B61DF);
+ versatile_clcdc_write_4(sc, CLCDC_TIMING2, 0x067F1800);
+ /* XXX: timing 3? */
+
+ /*
+ * Now allocate framebuffer memory
+ */
+ err = bus_dma_tag_create(
+ bus_get_dma_tag(dev),
+ 4, 0, /* alignment, boundary */
+ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ dma_size, 1, /* maxsize, nsegments */
+ dma_size, 0, /* maxsegsize, flags */
+ NULL, NULL, /* lockfunc, lockarg */
+ &sc->dma_tag);
+
+ err = bus_dmamem_alloc(sc->dma_tag, (void **)&sc->fb_base,
+ 0, &sc->dma_map);
+ if (err) {
+ device_printf(dev, "cannot allocate framebuffer\n");
+ goto fail;
+ }
+
+ err = bus_dmamap_load(sc->dma_tag, sc->dma_map, sc->fb_base,
+ dma_size, versatile_fb_dmamap_cb, &sc->fb_phys, BUS_DMA_NOWAIT);
+
+ if (err) {
+ device_printf(dev, "cannot load DMA map\n");
+ goto fail;
+ }
+
+ /* Make sure it's blank */
+ memset(sc->fb_base, 0x00, dma_size);
+
+ versatile_clcdc_write_4(sc, CLCDC_UPBASE, sc->fb_phys);
+
+ err = (sc_attach_unit(device_get_unit(dev),
+ device_get_flags(dev) | SC_AUTODETECT_KBD));
+
+ if (err) {
+ device_printf(dev, "failed to attach syscons\n");
+ goto fail;
+ }
+
+ /*
+ * XXX: hardcoded for VGA
+ */
+ reg = CONTROL_VCOMP_BP | CONTROL_TFT | CONTROL_BGR | CONTROL_EN;
+ reg |= CONTROL_BPP16;
+ versatile_clcdc_write_4(sc, CLCDC_CONTROL, reg);
+ DELAY(20);
+ reg |= CONTROL_PWR;
+ versatile_clcdc_write_4(sc, CLCDC_CONTROL, reg);
+
+ va_sc->fb_addr = (vm_offset_t)sc->fb_base;
+ va_sc->fb_size = dma_size;
+ va_sc->width = sc->width;
+ va_sc->height = sc->height;
+ va_sc->depth = 16;
+ va_sc->stride = sc->width * 2;
+ versatilefb_update_margins(&va_sc->va);
+
+ return (0);
+
+fail:
+ if (sc->fb_base)
+ bus_dmamem_free(sc->dma_tag, sc->fb_base, sc->dma_map);
+ if (sc->dma_tag)
+ bus_dma_tag_destroy(sc->dma_tag);
+ return (err);
+}
+
+static device_method_t versatile_clcdc_methods[] = {
+ DEVMETHOD(device_probe, versatile_clcdc_probe),
+ DEVMETHOD(device_attach, versatile_clcdc_attach),
+
+ DEVMETHOD_END
+};
+
+static driver_t versatile_clcdc_driver = {
+ "clcdc",
+ versatile_clcdc_methods,
+ sizeof(struct versatile_clcdc_softc),
+};
+
+static devclass_t versatile_clcdc_devclass;
+
+DRIVER_MODULE(versatile_clcdc, simplebus, versatile_clcdc_driver, versatile_clcdc_devclass, 0, 0);
+
+/*
+ * Video driver routines and glue.
+ */
+static vi_probe_t versatilefb_probe;
+static vi_init_t versatilefb_init;
+static vi_get_info_t versatilefb_get_info;
+static vi_query_mode_t versatilefb_query_mode;
+static vi_set_mode_t versatilefb_set_mode;
+static vi_save_font_t versatilefb_save_font;
+static vi_load_font_t versatilefb_load_font;
+static vi_show_font_t versatilefb_show_font;
+static vi_save_palette_t versatilefb_save_palette;
+static vi_load_palette_t versatilefb_load_palette;
+static vi_set_border_t versatilefb_set_border;
+static vi_save_state_t versatilefb_save_state;
+static vi_load_state_t versatilefb_load_state;
+static vi_set_win_org_t versatilefb_set_win_org;
+static vi_read_hw_cursor_t versatilefb_read_hw_cursor;
+static vi_set_hw_cursor_t versatilefb_set_hw_cursor;
+static vi_set_hw_cursor_shape_t versatilefb_set_hw_cursor_shape;
+static vi_blank_display_t versatilefb_blank_display;
+static vi_mmap_t versatilefb_mmap;
+static vi_ioctl_t versatilefb_ioctl;
+static vi_clear_t versatilefb_clear;
+static vi_fill_rect_t versatilefb_fill_rect;
+static vi_bitblt_t versatilefb_bitblt;
+static vi_diag_t versatilefb_diag;
+static vi_save_cursor_palette_t versatilefb_save_cursor_palette;
+static vi_load_cursor_palette_t versatilefb_load_cursor_palette;
+static vi_copy_t versatilefb_copy;
+static vi_putp_t versatilefb_putp;
+static vi_putc_t versatilefb_putc;
+static vi_puts_t versatilefb_puts;
+static vi_putm_t versatilefb_putm;
+
+static video_switch_t versatilefbvidsw = {
+ .probe = versatilefb_probe,
+ .init = versatilefb_init,
+ .get_info = versatilefb_get_info,
+ .query_mode = versatilefb_query_mode,
+ .set_mode = versatilefb_set_mode,
+ .save_font = versatilefb_save_font,
+ .load_font = versatilefb_load_font,
+ .show_font = versatilefb_show_font,
+ .save_palette = versatilefb_save_palette,
+ .load_palette = versatilefb_load_palette,
+ .set_border = versatilefb_set_border,
+ .save_state = versatilefb_save_state,
+ .load_state = versatilefb_load_state,
+ .set_win_org = versatilefb_set_win_org,
+ .read_hw_cursor = versatilefb_read_hw_cursor,
+ .set_hw_cursor = versatilefb_set_hw_cursor,
+ .set_hw_cursor_shape = versatilefb_set_hw_cursor_shape,
+ .blank_display = versatilefb_blank_display,
+ .mmap = versatilefb_mmap,
+ .ioctl = versatilefb_ioctl,
+ .clear = versatilefb_clear,
+ .fill_rect = versatilefb_fill_rect,
+ .bitblt = versatilefb_bitblt,
+ .diag = versatilefb_diag,
+ .save_cursor_palette = versatilefb_save_cursor_palette,
+ .load_cursor_palette = versatilefb_load_cursor_palette,
+ .copy = versatilefb_copy,
+ .putp = versatilefb_putp,
+ .putc = versatilefb_putc,
+ .puts = versatilefb_puts,
+ .putm = versatilefb_putm,
+};
+
+VIDEO_DRIVER(versatilefb, versatilefbvidsw, versatilefb_configure);
+
+static vr_init_t clcdr_init;
+static vr_clear_t clcdr_clear;
+static vr_draw_border_t clcdr_draw_border;
+static vr_draw_t clcdr_draw;
+static vr_set_cursor_t clcdr_set_cursor;
+static vr_draw_cursor_t clcdr_draw_cursor;
+static vr_blink_cursor_t clcdr_blink_cursor;
+static vr_set_mouse_t clcdr_set_mouse;
+static vr_draw_mouse_t clcdr_draw_mouse;
+
+/*
+ * We use our own renderer; this is because we must emulate a hardware
+ * cursor.
+ */
+static sc_rndr_sw_t clcdrend = {
+ clcdr_init,
+ clcdr_clear,
+ clcdr_draw_border,
+ clcdr_draw,
+ clcdr_set_cursor,
+ clcdr_draw_cursor,
+ clcdr_blink_cursor,
+ clcdr_set_mouse,
+ clcdr_draw_mouse
+};
+
+RENDERER(versatilefb, 0, clcdrend, gfb_set);
+RENDERER_MODULE(versatilefb, gfb_set);
+
+static void
+clcdr_init(scr_stat* scp)
+{
+}
+
+static void
+clcdr_clear(scr_stat* scp, int c, int attr)
+{
+}
+
+static void
+clcdr_draw_border(scr_stat* scp, int color)
+{
+}
+
+static void
+clcdr_draw(scr_stat* scp, int from, int count, int flip)
+{
+ video_adapter_t* adp = scp->sc->adp;
+ int i, c, a;
+
+ if (!flip) {
+ /* Normal printing */
+ vidd_puts(adp, from, (uint16_t*)sc_vtb_pointer(&scp->vtb, from), count);
+ } else {
+ /* This is for selections and such: invert the color attribute */
+ for (i = count; i-- > 0; ++from) {
+ c = sc_vtb_getc(&scp->vtb, from);
+ a = sc_vtb_geta(&scp->vtb, from) >> 8;
+ vidd_putc(adp, from, c, (a >> 4) | ((a & 0xf) << 4));
+ }
+ }
+}
+
+static void
+clcdr_set_cursor(scr_stat* scp, int base, int height, int blink)
+{
+}
+
+static void
+clcdr_draw_cursor(scr_stat* scp, int off, int blink, int on, int flip)
+{
+ video_adapter_t* adp = scp->sc->adp;
+ struct video_adapter_softc *sc;
+ int row, col;
+ uint8_t *addr;
+ int i,j;
+
+ sc = (struct video_adapter_softc *)adp;
+
+ if (scp->curs_attr.height <= 0)
+ return;
+
+ if (sc->fb_addr == 0)
+ return;
+
+ if (off >= adp->va_info.vi_width * adp->va_info.vi_height)
+ return;
+
+ /* calculate the coordinates in the video buffer */
+ row = (off / adp->va_info.vi_width) * adp->va_info.vi_cheight;
+ col = (off % adp->va_info.vi_width) * adp->va_info.vi_cwidth;
+
+ addr = (uint8_t *)sc->fb_addr
+ + (row + sc->ymargin)*(sc->stride)
+ + (sc->depth/8) * (col + sc->xmargin);
+
+ /* our cursor consists of simply inverting the char under it */
+ for (i = 0; i < adp->va_info.vi_cheight; i++) {
+ for (j = 0; j < adp->va_info.vi_cwidth; j++) {
+ addr[2*j] ^= 0xff;
+ addr[2*j + 1] ^= 0xff;
+ }
+
+ addr += sc->stride;
+ }
+}
+
+static void
+clcdr_blink_cursor(scr_stat* scp, int at, int flip)
+{
+}
+
+static void
+clcdr_set_mouse(scr_stat* scp)
+{
+}
+
+static void
+clcdr_draw_mouse(scr_stat* scp, int x, int y, int on)
+{
+ vidd_putm(scp->sc->adp, x, y, mouse_pointer, 0xffffffff, 16, 8);
+
+}
+
+static uint16_t versatilefb_static_window[ROW*COL];
+extern u_char dflt_font_16[];
+
+/*
+ * Update videoadapter settings after changing resolution
+ */
+static void
+versatilefb_update_margins(video_adapter_t *adp)
+{
+ struct video_adapter_softc *sc;
+ video_info_t *vi;
+
+ sc = (struct video_adapter_softc *)adp;
+ vi = &adp->va_info;
+
+ sc->xmargin = (sc->width - (vi->vi_width * vi->vi_cwidth)) / 2;
+ sc->ymargin = (sc->height - (vi->vi_height * vi->vi_cheight))/2;
+}
+
+static int
+versatilefb_configure(int flags)
+{
+ struct video_adapter_softc *va_sc;
+
+ va_sc = &va_softc;
+
+ if (va_sc->initialized)
+ return (0);
+
+ va_sc->width = FB_WIDTH;
+ va_sc->height = FB_HEIGHT;
+ va_sc->depth = FB_DEPTH;
+
+ versatilefb_init(0, &va_sc->va, 0);
+
+ va_sc->initialized = 1;
+
+ return (0);
+}
+
+static int
+versatilefb_probe(int unit, video_adapter_t **adp, void *arg, int flags)
+{
+
+ return (0);
+}
+
+static int
+versatilefb_init(int unit, video_adapter_t *adp, int flags)
+{
+ struct video_adapter_softc *sc;
+ video_info_t *vi;
+
+ sc = (struct video_adapter_softc *)adp;
+ vi = &adp->va_info;
+
+ vid_init_struct(adp, "versatilefb", -1, unit);
+
+ sc->font = dflt_font_16;
+ vi->vi_cheight = VERSATILE_FONT_HEIGHT;
+ vi->vi_cwidth = 8;
+
+ vi->vi_width = sc->width/8;
+ vi->vi_height = sc->height/vi->vi_cheight;
+
+ /*
+ * Clamp width/height to syscons maximums
+ */
+ if (vi->vi_width > COL)
+ vi->vi_width = COL;
+ if (vi->vi_height > ROW)
+ vi->vi_height = ROW;
+
+ sc->xmargin = (sc->width - (vi->vi_width * vi->vi_cwidth)) / 2;
+ sc->ymargin = (sc->height - (vi->vi_height * vi->vi_cheight))/2;
+
+ adp->va_window = (vm_offset_t) versatilefb_static_window;
+ adp->va_flags |= V_ADP_FONT /* | V_ADP_COLOR | V_ADP_MODECHANGE */;
+
+ vid_register(&sc->va);
+
+ return (0);
+}
+
+static int
+versatilefb_get_info(video_adapter_t *adp, int mode, video_info_t *info)
+{
+ bcopy(&adp->va_info, info, sizeof(*info));
+ return (0);
+}
+
+static int
+versatilefb_query_mode(video_adapter_t *adp, video_info_t *info)
+{
+ return (0);
+}
+
+static int
+versatilefb_set_mode(video_adapter_t *adp, int mode)
+{
+ return (0);
+}
+
+static int
+versatilefb_save_font(video_adapter_t *adp, int page, int size, int width,
+ u_char *data, int c, int count)
+{
+ return (0);
+}
+
+static int
+versatilefb_load_font(video_adapter_t *adp, int page, int size, int width,
+ u_char *data, int c, int count)
+{
+ struct video_adapter_softc *sc = (struct video_adapter_softc *)adp;
+
+ sc->font = data;
+
+ return (0);
+}
+
+static int
+versatilefb_show_font(video_adapter_t *adp, int page)
+{
+ return (0);
+}
+
+static int
+versatilefb_save_palette(video_adapter_t *adp, u_char *palette)
+{
+ return (0);
+}
+
+static int
+versatilefb_load_palette(video_adapter_t *adp, u_char *palette)
+{
+ return (0);
+}
+
+static int
+versatilefb_set_border(video_adapter_t *adp, int border)
+{
+ return (versatilefb_blank_display(adp, border));
+}
+
+static int
+versatilefb_save_state(video_adapter_t *adp, void *p, size_t size)
+{
+ return (0);
+}
+
+static int
+versatilefb_load_state(video_adapter_t *adp, void *p)
+{
+ return (0);
+}
+
+static int
+versatilefb_set_win_org(video_adapter_t *adp, off_t offset)
+{
+ return (0);
+}
+
+static int
+versatilefb_read_hw_cursor(video_adapter_t *adp, int *col, int *row)
+{
+ *col = *row = 0;
+
+ return (0);
+}
+
+static int
+versatilefb_set_hw_cursor(video_adapter_t *adp, int col, int row)
+{
+
+ return (0);
+}
+
+static int
+versatilefb_set_hw_cursor_shape(video_adapter_t *adp, int base, int height,
+ int celsize, int blink)
+{
+ return (0);
+}
+
+static int
+versatilefb_blank_display(video_adapter_t *adp, int mode)
+{
+
+ struct video_adapter_softc *sc;
+
+ sc = (struct video_adapter_softc *)adp;
+ if (sc && sc->fb_addr)
+ memset((void*)sc->fb_addr, 0, sc->fb_size);
+
+ return (0);
+}
+
+static int
+versatilefb_mmap(video_adapter_t *adp, vm_ooffset_t offset, vm_paddr_t *paddr,
+ int prot, vm_memattr_t *memattr)
+{
+ struct video_adapter_softc *sc;
+
+ sc = (struct video_adapter_softc *)adp;
+
+ /*
+ * This might be a legacy VGA mem request: if so, just point it at the
+ * framebuffer, since it shouldn't be touched
+ */
+ if (offset < sc->stride*sc->height) {
+ *paddr = sc->fb_addr + offset;
+ return (0);
+ }
+
+ return (EINVAL);
+}
+
+static int
+versatilefb_ioctl(video_adapter_t *adp, u_long cmd, caddr_t data)
+{
+
+ return (0);
+}
+
+static int
+versatilefb_clear(video_adapter_t *adp)
+{
+
+ return (versatilefb_blank_display(adp, 0));
+}
+
+static int
+versatilefb_fill_rect(video_adapter_t *adp, int val, int x, int y, int cx, int cy)
+{
+
+ return (0);
+}
+
+static int
+versatilefb_bitblt(video_adapter_t *adp, ...)
+{
+
+ return (0);
+}
+
+static int
+versatilefb_diag(video_adapter_t *adp, int level)
+{
+
+ return (0);
+}
+
+static int
+versatilefb_save_cursor_palette(video_adapter_t *adp, u_char *palette)
+{
+
+ return (0);
+}
+
+static int
+versatilefb_load_cursor_palette(video_adapter_t *adp, u_char *palette)
+{
+
+ return (0);
+}
+
+static int
+versatilefb_copy(video_adapter_t *adp, vm_offset_t src, vm_offset_t dst, int n)
+{
+
+ return (0);
+}
+
+static int
+versatilefb_putp(video_adapter_t *adp, vm_offset_t off, uint32_t p, uint32_t a,
+ int size, int bpp, int bit_ltor, int byte_ltor)
+{
+
+ return (0);
+}
+
+static int
+versatilefb_putc(video_adapter_t *adp, vm_offset_t off, uint8_t c, uint8_t a)
+{
+ struct video_adapter_softc *sc;
+ int row;
+ int col;
+ int i, j, k;
+ uint8_t *addr;
+ u_char *p;
+ uint8_t fg, bg, color;
+ uint16_t rgb;
+
+ sc = (struct video_adapter_softc *)adp;
+
+ if (sc->fb_addr == 0)
+ return (0);
+
+ if (off >= adp->va_info.vi_width * adp->va_info.vi_height)
+ return (0);
+
+ row = (off / adp->va_info.vi_width) * adp->va_info.vi_cheight;
+ col = (off % adp->va_info.vi_width) * adp->va_info.vi_cwidth;
+ p = sc->font + c*VERSATILE_FONT_HEIGHT;
+ addr = (uint8_t *)sc->fb_addr
+ + (row + sc->ymargin)*(sc->stride)
+ + (sc->depth/8) * (col + sc->xmargin);
+
+ fg = a & 0xf ;
+ bg = (a >> 4) & 0xf;
+
+ for (i = 0; i < VERSATILE_FONT_HEIGHT; i++) {
+ for (j = 0, k = 7; j < 8; j++, k--) {
+ if ((p[i] & (1 << k)) == 0)
+ color = bg;
+ else
+ color = fg;
+
+ switch (sc->depth) {
+ case 16:
+ rgb = (versatilefb_palette[color].r >> 3) << 11;
+ rgb |= (versatilefb_palette[color].g >> 2) << 5;
+ rgb |= (versatilefb_palette[color].b >> 3);
+ addr[2*j] = rgb & 0xff;
+ addr[2*j + 1] = (rgb >> 8) & 0xff;
+ default:
+ /* Not supported yet */
+ break;
+ }
+ }
+
+ addr += (sc->stride);
+ }
+
+ return (0);
+}
+
+static int
+versatilefb_puts(video_adapter_t *adp, vm_offset_t off, u_int16_t *s, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ versatilefb_putc(adp, off + i, s[i] & 0xff, (s[i] & 0xff00) >> 8);
+
+ return (0);
+}
+
+static int
+versatilefb_putm(video_adapter_t *adp, int x, int y, uint8_t *pixel_image,
+ uint32_t pixel_mask, int size, int width)
+{
+
+ return (0);
+}
diff --git a/sys/arm/versatile/versatile_machdep.c b/sys/arm/versatile/versatile_machdep.c
new file mode 100644
index 000000000000..87f52ceab12b
--- /dev/null
+++ b/sys/arm/versatile/versatile_machdep.c
@@ -0,0 +1,104 @@
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 2012 Oleksandr Tymoshenko.
+ * All rights reserved.
+ *
+ * This code is derived from software written for Brini by Mark Brinicombe
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Brini.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "opt_ddb.h"
+#include "opt_platform.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/devmap.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/bus.h>
+#include <machine/machdep.h>
+#include <machine/platform.h>
+#include <machine/platformvar.h>
+
+#include "platform_if.h"
+
+/* Start of address space used for bootstrap map */
+#define DEVMAP_BOOTSTRAP_MAP_START 0xE0000000
+
+static vm_offset_t
+versatile_lastaddr(platform_t plat)
+{
+
+ return (DEVMAP_BOOTSTRAP_MAP_START);
+}
+
+#define FDT_DEVMAP_MAX (2) /* FIXME */
+static struct devmap_entry fdt_devmap[FDT_DEVMAP_MAX] = {
+ { 0, 0, 0, },
+ { 0, 0, 0, }
+};
+
+/*
+ * Construct devmap table with DT-derived config data.
+ */
+static int
+versatile_devmap_init(platform_t plat)
+{
+ int i = 0;
+ fdt_devmap[i].pd_va = 0xf0100000;
+ fdt_devmap[i].pd_pa = 0x10100000;
+ fdt_devmap[i].pd_size = 0x01000000; /* 1 MB */
+
+ devmap_register_table(&fdt_devmap[0]);
+ return (0);
+}
+
+static void
+versatile_cpu_reset(platform_t plat)
+{
+ printf("cpu_reset\n");
+ while (1);
+}
+
+static platform_method_t versatile_methods[] = {
+ PLATFORMMETHOD(platform_lastaddr, versatile_lastaddr),
+ PLATFORMMETHOD(platform_devmap_init, versatile_devmap_init),
+ PLATFORMMETHOD(platform_cpu_reset, versatile_cpu_reset),
+
+ PLATFORMMETHOD_END,
+};
+FDT_PLATFORM_DEF(versatile, "versatile", 0, "arm,versatile-pb", 1);
diff --git a/sys/arm/versatile/versatile_pci.c b/sys/arm/versatile/versatile_pci.c
new file mode 100644
index 000000000000..73e047180063
--- /dev/null
+++ b/sys/arm/versatile/versatile_pci.c
@@ -0,0 +1,548 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012-2017 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/watchdog.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+
+#include <dev/pci/pcib_private.h>
+#include "pcib_if.h"
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/ofw_pci.h>
+
+#include <arm/versatile/versatile_scm.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+
+#define MEM_CORE 0
+#define MEM_BASE 1
+#define MEM_CONF_BASE 2
+#define MEM_REGIONS 3
+
+#define PCI_CORE_IMAP0 0x00
+#define PCI_CORE_IMAP1 0x04
+#define PCI_CORE_IMAP2 0x08
+#define PCI_CORE_SELFID 0x0C
+#define PCI_CORE_SMAP0 0x10
+#define PCI_CORE_SMAP1 0x14
+#define PCI_CORE_SMAP2 0x18
+
+#define VERSATILE_PCI_DEV 0x030010ee
+#define VERSATILE_PCI_CLASS 0x0b400000
+
+#define PCI_IO_WINDOW 0x44000000
+#define PCI_IO_SIZE 0x0c000000
+#define PCI_NPREFETCH_WINDOW 0x50000000
+#define PCI_NPREFETCH_SIZE 0x10000000
+#define PCI_PREFETCH_WINDOW 0x60000000
+#define PCI_PREFETCH_SIZE 0x10000000
+
+#define VERSATILE_PCI_IRQ_START 27
+#define VERSATILE_PCI_IRQ_END 30
+
+#ifdef DEBUG
+#define dprintf(fmt, args...) do { printf("%s(): ", __func__); \
+ printf(fmt,##args); } while (0)
+#else
+#define dprintf(fmt, args...)
+#endif
+
+#define versatile_pci_core_read_4(reg) \
+ bus_read_4(sc->mem_res[MEM_CORE], (reg))
+#define versatile_pci_core_write_4(reg, val) \
+ bus_write_4(sc->mem_res[MEM_CORE], (reg), (val))
+
+#define versatile_pci_read_4(reg) \
+ bus_read_4(sc->mem_res[MEM_BASE], (reg))
+#define versatile_pci_write_4(reg, val) \
+ bus_write_4(sc->mem_res[MEM_BASE], (reg), (val))
+
+#define versatile_pci_conf_read_4(reg) \
+ bus_read_4(sc->mem_res[MEM_CONF_BASE], (reg))
+#define versatile_pci_conf_write_4(reg, val) \
+ bus_write_4(sc->mem_res[MEM_CONF_BASE], (reg), (val))
+#define versatile_pci_conf_write_2(reg, val) \
+ bus_write_2(sc->mem_res[MEM_CONF_BASE], (reg), (val))
+#define versatile_pci_conf_write_1(reg, val) \
+ bus_write_1(sc->mem_res[MEM_CONF_BASE], (reg), (val))
+
+struct versatile_pci_softc {
+ struct resource* mem_res[MEM_REGIONS];
+ struct resource* irq_res;
+ void* intr_hl;
+
+ int pcib_slot;
+
+ /* Bus part */
+ int busno;
+ struct rman io_rman;
+ struct rman irq_rman;
+ struct rman mem_rman;
+
+ struct mtx mtx;
+ struct ofw_bus_iinfo pci_iinfo;
+};
+
+static struct resource_spec versatile_pci_mem_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_MEMORY, 1, RF_ACTIVE },
+ { SYS_RES_MEMORY, 2, RF_ACTIVE },
+ { -1, 0, 0 }
+};
+
+static int
+versatile_pci_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_is_compatible(dev, "arm,versatile-pci")) {
+ device_set_desc(dev, "Versatile PCI controller");
+ return (BUS_PROBE_DEFAULT);
+ }
+
+ return (ENXIO);
+}
+
+static int
+versatile_pci_attach(device_t dev)
+{
+ struct versatile_pci_softc *sc = device_get_softc(dev);
+ int err;
+ int slot;
+ uint32_t vendordev_id, class_id;
+ uint32_t val;
+ phandle_t node;
+
+ node = ofw_bus_get_node(dev);
+
+ /* Request memory resources */
+ err = bus_alloc_resources(dev, versatile_pci_mem_spec,
+ sc->mem_res);
+ if (err) {
+ device_printf(dev, "Error: could not allocate memory resources\n");
+ return (ENXIO);
+ }
+
+ /*
+ * Setup memory windows
+ */
+ versatile_pci_core_write_4(PCI_CORE_IMAP0, (PCI_IO_WINDOW >> 28));
+ versatile_pci_core_write_4(PCI_CORE_IMAP1, (PCI_NPREFETCH_WINDOW >> 28));
+ versatile_pci_core_write_4(PCI_CORE_IMAP2, (PCI_PREFETCH_WINDOW >> 28));
+
+ /*
+ * XXX: this is SDRAM offset >> 28
+ * Unused as of QEMU 1.5
+ */
+ versatile_pci_core_write_4(PCI_CORE_SMAP0, (PCI_IO_WINDOW >> 28));
+ versatile_pci_core_write_4(PCI_CORE_SMAP1, (PCI_NPREFETCH_WINDOW >> 28));
+ versatile_pci_core_write_4(PCI_CORE_SMAP2, (PCI_NPREFETCH_WINDOW >> 28));
+
+ versatile_scm_reg_write_4(SCM_PCICTL, 1);
+
+ for (slot = 0; slot <= PCI_SLOTMAX; slot++) {
+ vendordev_id = versatile_pci_read_4((slot << 11) + PCIR_DEVVENDOR);
+ class_id = versatile_pci_read_4((slot << 11) + PCIR_REVID);
+ if ((vendordev_id == VERSATILE_PCI_DEV) &&
+ (class_id == VERSATILE_PCI_CLASS))
+ break;
+ }
+
+ if (slot == (PCI_SLOTMAX + 1)) {
+ bus_release_resources(dev, versatile_pci_mem_spec,
+ sc->mem_res);
+ device_printf(dev, "Versatile PCI core not found\n");
+ return (ENXIO);
+ }
+
+ sc->pcib_slot = slot;
+ device_printf(dev, "PCI core at slot #%d\n", slot);
+
+ versatile_pci_core_write_4(PCI_CORE_SELFID, slot);
+ val = versatile_pci_conf_read_4((slot << 11) + PCIR_COMMAND);
+ val |= (PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN | PCIM_CMD_MWRICEN);
+ versatile_pci_conf_write_4((slot << 11) + PCIR_COMMAND, val);
+
+ /* Again SDRAM start >> 28 */
+ versatile_pci_write_4((slot << 11) + PCIR_BAR(0), 0);
+ versatile_pci_write_4((slot << 11) + PCIR_BAR(1), 0);
+ versatile_pci_write_4((slot << 11) + PCIR_BAR(2), 0);
+
+ /* Prepare resource managers */
+ sc->mem_rman.rm_type = RMAN_ARRAY;
+ sc->mem_rman.rm_descr = "versatile PCI memory window";
+ if (rman_init(&sc->mem_rman) != 0 ||
+ rman_manage_region(&sc->mem_rman, PCI_NPREFETCH_WINDOW,
+ PCI_NPREFETCH_WINDOW + PCI_NPREFETCH_SIZE - 1) != 0) {
+ panic("versatile_pci_attach: failed to set up memory rman");
+ }
+
+ bootverbose = 1;
+ sc->io_rman.rm_type = RMAN_ARRAY;
+ sc->io_rman.rm_descr = "versatile PCI IO window";
+ if (rman_init(&sc->io_rman) != 0 ||
+ rman_manage_region(&sc->io_rman, PCI_IO_WINDOW,
+ PCI_IO_WINDOW + PCI_IO_SIZE - 1) != 0) {
+ panic("versatile_pci_attach: failed to set up I/O rman");
+ }
+
+ sc->irq_rman.rm_type = RMAN_ARRAY;
+ sc->irq_rman.rm_descr = "versatile PCI IRQs";
+ if (rman_init(&sc->irq_rman) != 0 ||
+ rman_manage_region(&sc->irq_rman, VERSATILE_PCI_IRQ_START,
+ VERSATILE_PCI_IRQ_END) != 0) {
+ panic("versatile_pci_attach: failed to set up IRQ rman");
+ }
+
+ mtx_init(&sc->mtx, device_get_nameunit(dev), "versatilepci",
+ MTX_SPIN);
+
+ val = versatile_pci_conf_read_4((12 << 11) + PCIR_COMMAND);
+
+ for (slot = 0; slot <= PCI_SLOTMAX; slot++) {
+ vendordev_id = versatile_pci_read_4((slot << 11) + PCIR_DEVVENDOR);
+ class_id = versatile_pci_read_4((slot << 11) + PCIR_REVID);
+
+ if (slot == sc->pcib_slot)
+ continue;
+
+ if ((vendordev_id == 0xffffffff) &&
+ (class_id == 0xffffffff))
+ continue;
+
+ val = versatile_pci_conf_read_4((slot << 11) + PCIR_COMMAND);
+ val |= PCIM_CMD_MEMEN | PCIM_CMD_PORTEN;
+ versatile_pci_conf_write_4((slot << 11) + PCIR_COMMAND, val);
+ }
+
+ ofw_bus_setup_iinfo(node, &sc->pci_iinfo, sizeof(cell_t));
+
+ device_add_child(dev, "pci", -1);
+ return (bus_generic_attach(dev));
+}
+
+static int
+versatile_pci_read_ivar(device_t dev, device_t child, int which,
+ uintptr_t *result)
+{
+ struct versatile_pci_softc *sc = device_get_softc(dev);
+
+ switch (which) {
+ case PCIB_IVAR_DOMAIN:
+ *result = 0;
+ return (0);
+ case PCIB_IVAR_BUS:
+ *result = sc->busno;
+ return (0);
+ }
+
+ return (ENOENT);
+}
+
+static int
+versatile_pci_write_ivar(device_t dev, device_t child, int which,
+ uintptr_t result)
+{
+ struct versatile_pci_softc * sc = device_get_softc(dev);
+
+ switch (which) {
+ case PCIB_IVAR_BUS:
+ sc->busno = result;
+ return (0);
+ }
+
+ return (ENOENT);
+}
+
+static struct resource *
+versatile_pci_alloc_resource(device_t bus, device_t child, int type, int *rid,
+ rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
+{
+
+ struct versatile_pci_softc *sc = device_get_softc(bus);
+ struct resource *rv;
+ struct rman *rm;
+
+ dprintf("Alloc resources %d, %08lx..%08lx, %ld\n", type, start, end, count);
+
+ switch (type) {
+ case SYS_RES_IOPORT:
+ rm = &sc->io_rman;
+ break;
+ case SYS_RES_IRQ:
+ rm = NULL;
+ break;
+ case SYS_RES_MEMORY:
+ rm = &sc->mem_rman;
+ break;
+ default:
+ return (NULL);
+ }
+
+ if (rm == NULL)
+ return (BUS_ALLOC_RESOURCE(device_get_parent(bus),
+ child, type, rid, start, end, count, flags));
+
+ rv = rman_reserve_resource(rm, start, end, count, flags, child);
+ if (rv == NULL)
+ return (NULL);
+
+ rman_set_rid(rv, *rid);
+
+ if (flags & RF_ACTIVE) {
+ if (bus_activate_resource(child, type, *rid, rv)) {
+ rman_release_resource(rv);
+ return (NULL);
+ }
+ }
+ return (rv);
+}
+
+static int
+versatile_pci_activate_resource(device_t bus, device_t child, int type, int rid,
+ struct resource *r)
+{
+ vm_offset_t vaddr;
+ int res;
+
+ switch(type) {
+ case SYS_RES_MEMORY:
+ case SYS_RES_IOPORT:
+ vaddr = (vm_offset_t)pmap_mapdev(rman_get_start(r),
+ rman_get_size(r));
+ rman_set_bushandle(r, vaddr);
+ rman_set_bustag(r, fdtbus_bs_tag);
+ res = rman_activate_resource(r);
+ break;
+ case SYS_RES_IRQ:
+ res = (BUS_ACTIVATE_RESOURCE(device_get_parent(bus),
+ child, type, rid, r));
+ break;
+ default:
+ res = ENXIO;
+ break;
+ }
+
+ return (res);
+}
+
+static int
+versatile_pci_setup_intr(device_t bus, device_t child, struct resource *ires,
+ int flags, driver_filter_t *filt, driver_intr_t *handler,
+ void *arg, void **cookiep)
+{
+
+ return BUS_SETUP_INTR(device_get_parent(bus), bus, ires, flags,
+ filt, handler, arg, cookiep);
+}
+
+static int
+versatile_pci_teardown_intr(device_t dev, device_t child, struct resource *ires,
+ void *cookie)
+{
+
+ return BUS_TEARDOWN_INTR(device_get_parent(dev), dev, ires, cookie);
+}
+
+static int
+versatile_pci_maxslots(device_t dev)
+{
+
+ return (PCI_SLOTMAX);
+}
+
+static int
+versatile_pci_route_interrupt(device_t bus, device_t dev, int pin)
+{
+ struct versatile_pci_softc *sc;
+ struct ofw_pci_register reg;
+ uint32_t pintr, mintr[4];
+ phandle_t iparent;
+ int intrcells;
+
+ sc = device_get_softc(bus);
+ pintr = pin;
+
+ bzero(&reg, sizeof(reg));
+ reg.phys_hi = (pci_get_bus(dev) << OFW_PCI_PHYS_HI_BUSSHIFT) |
+ (pci_get_slot(dev) << OFW_PCI_PHYS_HI_DEVICESHIFT) |
+ (pci_get_function(dev) << OFW_PCI_PHYS_HI_FUNCTIONSHIFT);
+
+ intrcells = ofw_bus_lookup_imap(ofw_bus_get_node(dev),
+ &sc->pci_iinfo, &reg, sizeof(reg), &pintr, sizeof(pintr),
+ mintr, sizeof(mintr), &iparent);
+ if (intrcells) {
+ pintr = ofw_bus_map_intr(dev, iparent, intrcells, mintr);
+ return (pintr);
+ }
+
+ device_printf(bus, "could not route pin %d for device %d.%d\n",
+ pin, pci_get_slot(dev), pci_get_function(dev));
+ return (PCI_INVALID_IRQ);
+}
+
+static uint32_t
+versatile_pci_read_config(device_t dev, u_int bus, u_int slot, u_int func,
+ u_int reg, int bytes)
+{
+ struct versatile_pci_softc *sc = device_get_softc(dev);
+ uint32_t data;
+ uint32_t shift, mask;
+ uint32_t addr;
+
+ if (sc->pcib_slot == slot) {
+ switch (bytes) {
+ case 4:
+ return (0xffffffff);
+ break;
+ case 2:
+ return (0xffff);
+ break;
+ case 1:
+ return (0xff);
+ break;
+ }
+ }
+
+ addr = (bus << 16) | (slot << 11) | (func << 8) | (reg & ~3);
+
+ /* register access is 32-bit aligned */
+ shift = (reg & 3) * 8;
+
+ /* Create a mask based on the width, post-shift */
+ if (bytes == 2)
+ mask = 0xffff;
+ else if (bytes == 1)
+ mask = 0xff;
+ else
+ mask = 0xffffffff;
+
+ dprintf("%s: tag (%x, %x, %x) reg %d(%d)\n", __func__, bus, slot,
+ func, reg, bytes);
+
+ mtx_lock_spin(&sc->mtx);
+ data = versatile_pci_conf_read_4(addr);
+ mtx_unlock_spin(&sc->mtx);
+
+ /* get request bytes from 32-bit word */
+ data = (data >> shift) & mask;
+
+ dprintf("%s: read 0x%x\n", __func__, data);
+
+ return (data);
+}
+
+static void
+versatile_pci_write_config(device_t dev, u_int bus, u_int slot, u_int func,
+ u_int reg, uint32_t data, int bytes)
+{
+
+ struct versatile_pci_softc *sc = device_get_softc(dev);
+ uint32_t addr;
+
+ dprintf("%s: tag (%x, %x, %x) reg %d(%d)\n", __func__, bus, slot,
+ func, reg, bytes);
+
+ if (sc->pcib_slot == slot)
+ return;
+
+ addr = (bus << 16) | (slot << 11) | (func << 8) | reg;
+ mtx_lock_spin(&sc->mtx);
+ switch (bytes) {
+ case 4:
+ versatile_pci_conf_write_4(addr, data);
+ break;
+ case 2:
+ versatile_pci_conf_write_2(addr, data);
+ break;
+ case 1:
+ versatile_pci_conf_write_1(addr, data);
+ break;
+ }
+ mtx_unlock_spin(&sc->mtx);
+}
+
+static device_method_t versatile_pci_methods[] = {
+ DEVMETHOD(device_probe, versatile_pci_probe),
+ DEVMETHOD(device_attach, versatile_pci_attach),
+
+ /* Bus interface */
+ DEVMETHOD(bus_read_ivar, versatile_pci_read_ivar),
+ DEVMETHOD(bus_write_ivar, versatile_pci_write_ivar),
+ DEVMETHOD(bus_alloc_resource, versatile_pci_alloc_resource),
+ DEVMETHOD(bus_release_resource, bus_generic_release_resource),
+ DEVMETHOD(bus_activate_resource, versatile_pci_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
+ DEVMETHOD(bus_setup_intr, versatile_pci_setup_intr),
+ DEVMETHOD(bus_teardown_intr, versatile_pci_teardown_intr),
+
+ /* pcib interface */
+ DEVMETHOD(pcib_maxslots, versatile_pci_maxslots),
+ DEVMETHOD(pcib_read_config, versatile_pci_read_config),
+ DEVMETHOD(pcib_write_config, versatile_pci_write_config),
+ DEVMETHOD(pcib_route_interrupt, versatile_pci_route_interrupt),
+ DEVMETHOD(pcib_request_feature, pcib_request_feature_allow),
+
+ DEVMETHOD_END
+};
+
+static driver_t versatile_pci_driver = {
+ "pcib",
+ versatile_pci_methods,
+ sizeof(struct versatile_pci_softc),
+};
+
+static devclass_t versatile_pci_devclass;
+
+DRIVER_MODULE(versatile_pci, simplebus, versatile_pci_driver, versatile_pci_devclass, 0, 0);
diff --git a/sys/arm/versatile/versatile_scm.c b/sys/arm/versatile/versatile_scm.c
new file mode 100644
index 000000000000..835d2381075d
--- /dev/null
+++ b/sys/arm/versatile/versatile_scm.c
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2017 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Ben Gray.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BEN GRAY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * SCM - System Control Module
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "versatile_scm.h"
+
+struct versatile_scm_softc {
+ device_t sc_dev;
+ struct resource * sc_mem_res;
+};
+
+static struct versatile_scm_softc *versatile_scm_sc;
+
+#define versatile_scm_read_4(sc, reg) \
+ bus_read_4((sc)->sc_mem_res, (reg))
+#define versatile_scm_write_4(sc, reg, val) \
+ bus_write_4((sc)->sc_mem_res, (reg), (val))
+
+static int
+versatile_scm_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "syscon"))
+ return (ENXIO);
+
+ if (versatile_scm_sc) {
+ return (EEXIST);
+ }
+
+ device_set_desc(dev, "Versatile Control Module");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+versatile_scm_attach(device_t dev)
+{
+ struct versatile_scm_softc *sc;
+ int rid;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+
+ rid = 0;
+ sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
+
+ if (sc->sc_mem_res == NULL) {
+ device_printf(dev, "could not allocate memory resources\n");
+ return (ENXIO);
+ }
+
+ versatile_scm_sc = sc;
+
+ return (0);
+}
+
+int
+versatile_scm_reg_read_4(uint32_t reg, uint32_t *val)
+{
+ if (!versatile_scm_sc)
+ return (ENXIO);
+
+ *val = versatile_scm_read_4(versatile_scm_sc, reg);
+ return (0);
+}
+
+int
+versatile_scm_reg_write_4(uint32_t reg, uint32_t val)
+{
+ if (!versatile_scm_sc)
+ return (ENXIO);
+
+ versatile_scm_write_4(versatile_scm_sc, reg, val);
+ return (0);
+}
+
+static device_method_t versatile_scm_methods[] = {
+ DEVMETHOD(device_probe, versatile_scm_probe),
+ DEVMETHOD(device_attach, versatile_scm_attach),
+
+ DEVMETHOD_END
+};
+
+static driver_t versatile_scm_driver = {
+ "scm",
+ versatile_scm_methods,
+ sizeof(struct versatile_scm_softc),
+};
+
+static devclass_t versatile_scm_devclass;
+
+EARLY_DRIVER_MODULE(versatile_scm, simplebus, versatile_scm_driver, versatile_scm_devclass, 0, 0,
+ BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
diff --git a/sys/arm/versatile/versatile_scm.h b/sys/arm/versatile/versatile_scm.h
new file mode 100644
index 000000000000..a59ad0664528
--- /dev/null
+++ b/sys/arm/versatile/versatile_scm.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2017 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Ben Gray.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BEN GRAY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _VERSATILE_SCM_H_
+#define _VERSATILE_SCM_H_
+
+#define SCM_PCICTL 0x44
+#define SCM_CLCD 0x50
+#define SCM_CLCD_CLCDID_SHIFT 0x08
+#define SCM_CLCD_CLCDID_MASK 0x1f
+#define SCM_CLCD_PWR3V5VSWITCH (1 << 4)
+#define SCM_CLCD_VDDPOSSWITCH (1 << 3)
+#define SCM_CLCD_NLCDIOON (1 << 2)
+#define SCM_CLCD_LCD_MODE_MASK 0x03
+
+int versatile_scm_reg_read_4(uint32_t reg, uint32_t *val);
+int versatile_scm_reg_write_4(uint32_t reg, uint32_t val);
+
+#endif /* _VERSATILE_SCM_H_ */
diff --git a/sys/arm/versatile/versatile_sic.c b/sys/arm/versatile/versatile_sic.c
new file mode 100644
index 000000000000..f3a35ce68a26
--- /dev/null
+++ b/sys/arm/versatile/versatile_sic.c
@@ -0,0 +1,324 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012-2017 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/ktr.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/rman.h>
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "pic_if.h"
+
+#ifdef DEBUG
+#define dprintf(fmt, args...) printf(fmt, ##args)
+#else
+#define dprintf(fmt, args...)
+#endif
+
+#define SIC_STATUS 0x00
+#define SIC_RAWSTAT 0x04
+#define SIC_ENABLE 0x08
+#define SIC_ENSET 0x08
+#define SIC_ENCLR 0x0C
+#define SIC_SOFTINTSET 0x10
+#define SIC_SOFTINTCLR 0x14
+#define SIC_PICENABLE 0x20
+#define SIC_PICENSET 0x20
+#define SIC_PICENCLR 0x24
+
+#define SIC_NIRQS 32
+
+struct versatile_sic_irqsrc {
+ struct intr_irqsrc isrc;
+ u_int irq;
+};
+
+struct versatile_sic_softc {
+ device_t dev;
+ struct mtx mtx;
+ struct resource * mem_res;
+ struct resource * irq_res;
+ void *intrh;
+ struct versatile_sic_irqsrc isrcs[SIC_NIRQS];
+};
+
+#define SIC_LOCK(_sc) mtx_lock_spin(&(_sc)->mtx)
+#define SIC_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->mtx)
+
+#define SIC_READ_4(sc, reg) \
+ bus_read_4(sc->mem_res, (reg))
+#define SIC_WRITE_4(sc, reg, val) \
+ bus_write_4(sc->mem_res, (reg), (val))
+
+/*
+ * Driver stuff
+ */
+static int versatile_sic_probe(device_t);
+static int versatile_sic_attach(device_t);
+static int versatile_sic_detach(device_t);
+
+static void
+versatile_sic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct versatile_sic_softc *sc;
+ struct versatile_sic_irqsrc *src;
+
+ sc = device_get_softc(dev);
+ src = (struct versatile_sic_irqsrc *)isrc;
+
+ SIC_LOCK(sc);
+ SIC_WRITE_4(sc, SIC_ENCLR, (1 << src->irq));
+ SIC_UNLOCK(sc);
+}
+
+static void
+versatile_sic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct versatile_sic_softc *sc;
+ struct versatile_sic_irqsrc *src;
+
+ sc = device_get_softc(dev);
+ src = (struct versatile_sic_irqsrc *)isrc;
+
+ SIC_LOCK(sc);
+ SIC_WRITE_4(sc, SIC_ENSET, (1 << src->irq));
+ SIC_UNLOCK(sc);
+}
+
+static int
+versatile_sic_map_intr(device_t dev, struct intr_map_data *data,
+ struct intr_irqsrc **isrcp)
+{
+ struct intr_map_data_fdt *daf;
+ struct versatile_sic_softc *sc;
+
+ if (data->type != INTR_MAP_DATA_FDT)
+ return (ENOTSUP);
+
+ daf = (struct intr_map_data_fdt *)data;
+ if (daf->ncells != 1 || daf->cells[0] >= SIC_NIRQS)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+ *isrcp = &sc->isrcs[daf->cells[0]].isrc;
+ return (0);
+}
+
+static void
+versatile_sic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+ versatile_sic_disable_intr(dev, isrc);
+}
+
+static void
+versatile_sic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct versatile_sic_irqsrc *src;
+
+ src = (struct versatile_sic_irqsrc *)isrc;
+ arm_irq_memory_barrier(src->irq);
+ versatile_sic_enable_intr(dev, isrc);
+}
+
+static void
+versatile_sic_post_filter(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct versatile_sic_irqsrc *src;
+
+ src = (struct versatile_sic_irqsrc *)isrc;
+ arm_irq_memory_barrier(src->irq);
+}
+
+static int
+versatile_sic_setup_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+
+ return (0);
+}
+
+static int
+versatile_sic_filter(void *arg)
+{
+ struct versatile_sic_softc *sc;
+ struct intr_irqsrc *isrc;
+ uint32_t i, interrupts;
+
+ sc = arg;
+ SIC_LOCK(sc);
+ interrupts = SIC_READ_4(sc, SIC_STATUS);
+ SIC_UNLOCK(sc);
+ for (i = 0; interrupts != 0; i++, interrupts >>= 1) {
+ if ((interrupts & 0x1) == 0)
+ continue;
+ isrc = &sc->isrcs[i].isrc;
+ if (intr_isrc_dispatch(isrc, curthread->td_intr_frame) != 0) {
+ versatile_sic_disable_intr(sc->dev, isrc);
+ versatile_sic_post_filter(sc->dev, isrc);
+ device_printf(sc->dev, "Stray irq %u disabled\n", i);
+ }
+ }
+
+ return (FILTER_HANDLED);
+}
+
+static int
+versatile_sic_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "arm,versatile-sic"))
+ return (ENXIO);
+ device_set_desc(dev, "ARM Versatile SIC");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+versatile_sic_attach(device_t dev)
+{
+ struct versatile_sic_softc *sc = device_get_softc(dev);
+ int rid, error;
+ uint32_t irq;
+ const char *name;
+ struct versatile_sic_irqsrc *isrcs;
+
+ sc->dev = dev;
+ mtx_init(&sc->mtx, device_get_nameunit(dev), "sic",
+ MTX_SPIN);
+
+ /* Request memory resources */
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "Error: could not allocate memory resources\n");
+ return (ENXIO);
+ }
+
+ /* Request memory resources */
+ rid = 0;
+ sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (sc->irq_res == NULL) {
+ device_printf(dev, "could not allocate IRQ resources\n");
+ versatile_sic_detach(dev);
+ return (ENXIO);
+ }
+
+ if ((bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC,
+ versatile_sic_filter, NULL, sc, &sc->intrh))) {
+ device_printf(dev,
+ "unable to register interrupt handler\n");
+ versatile_sic_detach(dev);
+ return (ENXIO);
+ }
+
+ /* Disable all interrupts on SIC */
+ SIC_WRITE_4(sc, SIC_ENCLR, 0xffffffff);
+
+ /* PIC attachment */
+ isrcs = sc->isrcs;
+ name = device_get_nameunit(sc->dev);
+ for (irq = 0; irq < SIC_NIRQS; irq++) {
+ isrcs[irq].irq = irq;
+ error = intr_isrc_register(&isrcs[irq].isrc, sc->dev,
+ 0, "%s,%u", name, irq);
+ if (error != 0)
+ return (error);
+ }
+
+ intr_pic_register(dev, OF_xref_from_node(ofw_bus_get_node(dev)));
+
+ return (0);
+}
+
+static int
+versatile_sic_detach(device_t dev)
+{
+ struct versatile_sic_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (sc->intrh)
+ bus_teardown_intr(dev, sc->irq_res, sc->intrh);
+
+ if (sc->mem_res == NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY,
+ rman_get_rid(sc->mem_res), sc->mem_res);
+
+ if (sc->irq_res == NULL)
+ bus_release_resource(dev, SYS_RES_IRQ,
+ rman_get_rid(sc->irq_res), sc->irq_res);
+
+ mtx_destroy(&sc->mtx);
+
+ return (0);
+
+}
+
+static device_method_t versatile_sic_methods[] = {
+ DEVMETHOD(device_probe, versatile_sic_probe),
+ DEVMETHOD(device_attach, versatile_sic_attach),
+ DEVMETHOD(device_detach, versatile_sic_detach),
+
+ DEVMETHOD(pic_disable_intr, versatile_sic_disable_intr),
+ DEVMETHOD(pic_enable_intr, versatile_sic_enable_intr),
+ DEVMETHOD(pic_map_intr, versatile_sic_map_intr),
+ DEVMETHOD(pic_post_filter, versatile_sic_post_filter),
+ DEVMETHOD(pic_post_ithread, versatile_sic_post_ithread),
+ DEVMETHOD(pic_pre_ithread, versatile_sic_pre_ithread),
+ DEVMETHOD(pic_setup_intr, versatile_sic_setup_intr),
+
+ DEVMETHOD_END
+};
+
+static driver_t versatile_sic_driver = {
+ "sic",
+ versatile_sic_methods,
+ sizeof(struct versatile_sic_softc),
+};
+
+static devclass_t versatile_sic_devclass;
+
+DRIVER_MODULE(sic, simplebus, versatile_sic_driver, versatile_sic_devclass, 0, 0);
diff --git a/sys/arm/xilinx/files.zynq7 b/sys/arm/xilinx/files.zynq7
new file mode 100644
index 000000000000..c118f38c6c68
--- /dev/null
+++ b/sys/arm/xilinx/files.zynq7
@@ -0,0 +1,17 @@
+#
+# files.zynq7
+#
+# $FreeBSD$
+
+arm/xilinx/zy7_machdep.c standard
+arm/xilinx/zy7_l2cache.c standard
+arm/xilinx/zy7_slcr.c standard
+arm/xilinx/zy7_devcfg.c standard
+arm/xilinx/zy7_mp.c optional smp
+
+arm/xilinx/zy7_ehci.c optional ehci
+arm/xilinx/uart_dev_cdnc.c optional uart
+arm/xilinx/zy7_gpio.c optional gpio
+arm/xilinx/zy7_qspi.c optional zy7_qspi
+arm/xilinx/zy7_spi.c optional zy7_spi
+
diff --git a/sys/arm/xilinx/std.zynq7 b/sys/arm/xilinx/std.zynq7
new file mode 100644
index 000000000000..c3952ce3dc3b
--- /dev/null
+++ b/sys/arm/xilinx/std.zynq7
@@ -0,0 +1,10 @@
+#
+# std.zynq7 - Generic configuration for Xilinx Zynq-7000 PS.
+#
+# $FreeBSD$
+
+cpu CPU_CORTEXA
+machine arm armv7
+makeoptions CONF_CFLAGS="-march=armv7a"
+
+files "../xilinx/files.zynq7"
diff --git a/sys/arm/xilinx/uart_dev_cdnc.c b/sys/arm/xilinx/uart_dev_cdnc.c
new file mode 100644
index 000000000000..50fbfd5c616c
--- /dev/null
+++ b/sys/arm/xilinx/uart_dev_cdnc.c
@@ -0,0 +1,714 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2005 Olivier Houchard All rights reserved.
+ * Copyright (c) 2012 Thomas Skibo All rights reserved.
+ * Copyright (c) 2005 M. Warner Losh <imp@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* A driver for the Cadence AMBA UART as used by the Xilinx Zynq-7000.
+ *
+ * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual.
+ * (v1.4) November 16, 2012. Xilinx doc UG585. UART is covered in Ch. 19
+ * and register definitions are in appendix B.33.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/cons.h>
+#include <machine/bus.h>
+
+#include <dev/uart/uart.h>
+#include <dev/uart/uart_cpu.h>
+#include <dev/uart/uart_cpu_fdt.h>
+#include <dev/uart/uart_bus.h>
+
+#include "uart_if.h"
+
+#define UART_FIFO_SIZE 64
+
+#define RD4(bas, reg) \
+ bus_space_read_4((bas)->bst, (bas)->bsh, uart_regofs((bas), (reg)))
+#define WR4(bas, reg, value) \
+ bus_space_write_4((bas)->bst, (bas)->bsh, uart_regofs((bas), (reg)), \
+ (value))
+
+/* Register definitions for Cadence UART Controller.
+ */
+#define CDNC_UART_CTRL_REG 0x00 /* Control Register. */
+#define CDNC_UART_CTRL_REG_STOPBRK (1<<8)
+#define CDNC_UART_CTRL_REG_STARTBRK (1<<7)
+#define CDNC_UART_CTRL_REG_TORST (1<<6)
+#define CDNC_UART_CTRL_REG_TX_DIS (1<<5)
+#define CDNC_UART_CTRL_REG_TX_EN (1<<4)
+#define CDNC_UART_CTRL_REG_RX_DIS (1<<3)
+#define CDNC_UART_CTRL_REG_RX_EN (1<<2)
+#define CDNC_UART_CTRL_REG_TXRST (1<<1)
+#define CDNC_UART_CTRL_REG_RXRST (1<<0)
+
+#define CDNC_UART_MODE_REG 0x04 /* Mode Register. */
+#define CDNC_UART_MODE_REG_CHMOD_R_LOOP (3<<8) /* [9:8] - channel mode */
+#define CDNC_UART_MODE_REG_CHMOD_L_LOOP (2<<8)
+#define CDNC_UART_MODE_REG_CHMOD_AUTECHO (1<<8)
+#define CDNC_UART_MODE_REG_STOP2 (2<<6) /* [7:6] - stop bits */
+#define CDNC_UART_MODE_REG_PAR_NONE (4<<3) /* [5:3] - parity type */
+#define CDNC_UART_MODE_REG_PAR_MARK (3<<3)
+#define CDNC_UART_MODE_REG_PAR_SPACE (2<<3)
+#define CDNC_UART_MODE_REG_PAR_ODD (1<<3)
+#define CDNC_UART_MODE_REG_PAR_EVEN (0<<3)
+#define CDNC_UART_MODE_REG_6BIT (3<<1) /* [2:1] - character len */
+#define CDNC_UART_MODE_REG_7BIT (2<<1)
+#define CDNC_UART_MODE_REG_8BIT (0<<1)
+#define CDNC_UART_MODE_REG_CLKSEL (1<<0)
+
+#define CDNC_UART_IEN_REG 0x08 /* Interrupt registers. */
+#define CDNC_UART_IDIS_REG 0x0C
+#define CDNC_UART_IMASK_REG 0x10
+#define CDNC_UART_ISTAT_REG 0x14
+#define CDNC_UART_INT_TXOVR (1<<12)
+#define CDNC_UART_INT_TXNRLYFUL (1<<11) /* tx "nearly" full */
+#define CDNC_UART_INT_TXTRIG (1<<10)
+#define CDNC_UART_INT_DMSI (1<<9) /* delta modem status */
+#define CDNC_UART_INT_RXTMOUT (1<<8)
+#define CDNC_UART_INT_PARITY (1<<7)
+#define CDNC_UART_INT_FRAMING (1<<6)
+#define CDNC_UART_INT_RXOVR (1<<5)
+#define CDNC_UART_INT_TXFULL (1<<4)
+#define CDNC_UART_INT_TXEMPTY (1<<3)
+#define CDNC_UART_INT_RXFULL (1<<2)
+#define CDNC_UART_INT_RXEMPTY (1<<1)
+#define CDNC_UART_INT_RXTRIG (1<<0)
+#define CDNC_UART_INT_ALL 0x1FFF
+
+#define CDNC_UART_BAUDGEN_REG 0x18
+#define CDNC_UART_RX_TIMEO_REG 0x1C
+#define CDNC_UART_RX_WATER_REG 0x20
+
+#define CDNC_UART_MODEM_CTRL_REG 0x24
+#define CDNC_UART_MODEM_CTRL_REG_FCM (1<<5) /* automatic flow control */
+#define CDNC_UART_MODEM_CTRL_REG_RTS (1<<1)
+#define CDNC_UART_MODEM_CTRL_REG_DTR (1<<0)
+
+#define CDNC_UART_MODEM_STAT_REG 0x28
+#define CDNC_UART_MODEM_STAT_REG_FCMS (1<<8) /* flow control mode (rw) */
+#define CDNC_UART_MODEM_STAT_REG_DCD (1<<7)
+#define CDNC_UART_MODEM_STAT_REG_RI (1<<6)
+#define CDNC_UART_MODEM_STAT_REG_DSR (1<<5)
+#define CDNC_UART_MODEM_STAT_REG_CTS (1<<4)
+#define CDNC_UART_MODEM_STAT_REG_DDCD (1<<3) /* change in DCD (w1tc) */
+#define CDNC_UART_MODEM_STAT_REG_TERI (1<<2) /* trail edge ring (w1tc) */
+#define CDNC_UART_MODEM_STAT_REG_DDSR (1<<1) /* change in DSR (w1tc) */
+#define CDNC_UART_MODEM_STAT_REG_DCTS (1<<0) /* change in CTS (w1tc) */
+
+#define CDNC_UART_CHAN_STAT_REG 0x2C /* Channel status register. */
+#define CDNC_UART_CHAN_STAT_REG_TXNRLYFUL (1<<14) /* tx "nearly" full */
+#define CDNC_UART_CHAN_STAT_REG_TXTRIG (1<<13)
+#define CDNC_UART_CHAN_STAT_REG_FDELT (1<<12)
+#define CDNC_UART_CHAN_STAT_REG_TXACTIVE (1<<11)
+#define CDNC_UART_CHAN_STAT_REG_RXACTIVE (1<<10)
+#define CDNC_UART_CHAN_STAT_REG_TXFULL (1<<4)
+#define CDNC_UART_CHAN_STAT_REG_TXEMPTY (1<<3)
+#define CDNC_UART_CHAN_STAT_REG_RXEMPTY (1<<1)
+#define CDNC_UART_CHAN_STAT_REG_RXTRIG (1<<0)
+
+#define CDNC_UART_FIFO 0x30 /* Data FIFO (tx and rx) */
+#define CDNC_UART_BAUDDIV_REG 0x34
+#define CDNC_UART_FLOWDEL_REG 0x38
+#define CDNC_UART_TX_WATER_REG 0x44
+
+/*
+ * Low-level UART interface.
+ */
+static int cdnc_uart_probe(struct uart_bas *bas);
+static void cdnc_uart_init(struct uart_bas *bas, int, int, int, int);
+static void cdnc_uart_term(struct uart_bas *bas);
+static void cdnc_uart_putc(struct uart_bas *bas, int);
+static int cdnc_uart_rxready(struct uart_bas *bas);
+static int cdnc_uart_getc(struct uart_bas *bas, struct mtx *mtx);
+
+extern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs;
+
+static struct uart_ops cdnc_uart_ops = {
+ .probe = cdnc_uart_probe,
+ .init = cdnc_uart_init,
+ .term = cdnc_uart_term,
+ .putc = cdnc_uart_putc,
+ .rxready = cdnc_uart_rxready,
+ .getc = cdnc_uart_getc,
+};
+
+#define SIGCHG(c, i, s, d) \
+ if (c) { \
+ i |= (i & s) ? s : s | d; \
+ } else { \
+ i = (i & s) ? (i & ~s) | d : i; \
+ }
+
+static int
+cdnc_uart_probe(struct uart_bas *bas)
+{
+
+ return (0);
+}
+
+static int
+cdnc_uart_set_baud(struct uart_bas *bas, int baudrate)
+{
+ uint32_t baudgen, bauddiv;
+ uint32_t best_bauddiv, best_baudgen, best_error;
+ uint32_t baud_out, err;
+
+ best_bauddiv = 0;
+ best_baudgen = 0;
+ best_error = ~0;
+
+ /* Try all possible bauddiv values and pick best match. */
+ for (bauddiv = 4; bauddiv <= 255; bauddiv++) {
+ baudgen = (bas->rclk + (baudrate * (bauddiv + 1)) / 2) /
+ (baudrate * (bauddiv + 1));
+ if (baudgen < 1 || baudgen > 0xffff)
+ continue;
+
+ baud_out = bas->rclk / (baudgen * (bauddiv + 1));
+ err = baud_out > baudrate ?
+ baud_out - baudrate : baudrate - baud_out;
+
+ if (err < best_error) {
+ best_error = err;
+ best_bauddiv = bauddiv;
+ best_baudgen = baudgen;
+ }
+ }
+
+ if (best_bauddiv > 0) {
+ WR4(bas, CDNC_UART_BAUDDIV_REG, best_bauddiv);
+ WR4(bas, CDNC_UART_BAUDGEN_REG, best_baudgen);
+ return (0);
+ } else
+ return (-1); /* out of range */
+}
+
+static int
+cdnc_uart_set_params(struct uart_bas *bas, int baudrate, int databits,
+ int stopbits, int parity)
+{
+ uint32_t mode_reg_value = 0;
+
+ switch (databits) {
+ case 6:
+ mode_reg_value |= CDNC_UART_MODE_REG_6BIT;
+ break;
+ case 7:
+ mode_reg_value |= CDNC_UART_MODE_REG_7BIT;
+ break;
+ case 8:
+ default:
+ mode_reg_value |= CDNC_UART_MODE_REG_8BIT;
+ break;
+ }
+
+ if (stopbits == 2)
+ mode_reg_value |= CDNC_UART_MODE_REG_STOP2;
+
+ switch (parity) {
+ case UART_PARITY_MARK:
+ mode_reg_value |= CDNC_UART_MODE_REG_PAR_MARK;
+ break;
+ case UART_PARITY_SPACE:
+ mode_reg_value |= CDNC_UART_MODE_REG_PAR_SPACE;
+ break;
+ case UART_PARITY_ODD:
+ mode_reg_value |= CDNC_UART_MODE_REG_PAR_ODD;
+ break;
+ case UART_PARITY_EVEN:
+ mode_reg_value |= CDNC_UART_MODE_REG_PAR_EVEN;
+ break;
+ case UART_PARITY_NONE:
+ default:
+ mode_reg_value |= CDNC_UART_MODE_REG_PAR_NONE;
+ break;
+ }
+
+ WR4(bas, CDNC_UART_MODE_REG, mode_reg_value);
+
+ if (baudrate > 0 && cdnc_uart_set_baud(bas, baudrate) < 0)
+ return (EINVAL);
+
+ return(0);
+}
+
+static void
+cdnc_uart_hw_init(struct uart_bas *bas)
+{
+
+ /* Reset RX and TX. */
+ WR4(bas, CDNC_UART_CTRL_REG,
+ CDNC_UART_CTRL_REG_RXRST | CDNC_UART_CTRL_REG_TXRST);
+
+ /* Interrupts all off. */
+ WR4(bas, CDNC_UART_IDIS_REG, CDNC_UART_INT_ALL);
+ WR4(bas, CDNC_UART_ISTAT_REG, CDNC_UART_INT_ALL);
+
+ /* Clear delta bits. */
+ WR4(bas, CDNC_UART_MODEM_STAT_REG,
+ CDNC_UART_MODEM_STAT_REG_DDCD | CDNC_UART_MODEM_STAT_REG_TERI |
+ CDNC_UART_MODEM_STAT_REG_DDSR | CDNC_UART_MODEM_STAT_REG_DCTS);
+
+ /* RX FIFO water level, stale timeout */
+ WR4(bas, CDNC_UART_RX_WATER_REG, UART_FIFO_SIZE/2);
+ WR4(bas, CDNC_UART_RX_TIMEO_REG, 10);
+
+ /* TX FIFO water level (not used.) */
+ WR4(bas, CDNC_UART_TX_WATER_REG, UART_FIFO_SIZE/2);
+
+ /* Bring RX and TX online. */
+ WR4(bas, CDNC_UART_CTRL_REG,
+ CDNC_UART_CTRL_REG_RX_EN | CDNC_UART_CTRL_REG_TX_EN |
+ CDNC_UART_CTRL_REG_TORST | CDNC_UART_CTRL_REG_STOPBRK);
+
+ /* Set DTR and RTS. */
+ WR4(bas, CDNC_UART_MODEM_CTRL_REG, CDNC_UART_MODEM_CTRL_REG_DTR |
+ CDNC_UART_MODEM_CTRL_REG_RTS);
+}
+
+/*
+ * Initialize this device for use as a console.
+ */
+static void
+cdnc_uart_init(struct uart_bas *bas, int baudrate, int databits, int stopbits,
+ int parity)
+{
+
+ /* Initialize hardware. */
+ cdnc_uart_hw_init(bas);
+
+ /* Set baudrate, parameters. */
+ (void)cdnc_uart_set_params(bas, baudrate, databits, stopbits, parity);
+}
+
+/*
+ * Free resources now that we're no longer the console. This appears to
+ * be never called, and I'm unsure quite what to do if I am called.
+ */
+static void
+cdnc_uart_term(struct uart_bas *bas)
+{
+
+ /* XXX */
+}
+
+/*
+ * Put a character of console output (so we do it here polling rather than
+ * interrutp driven).
+ */
+static void
+cdnc_uart_putc(struct uart_bas *bas, int c)
+{
+
+ /* Wait for room. */
+ while ((RD4(bas,CDNC_UART_CHAN_STAT_REG) &
+ CDNC_UART_CHAN_STAT_REG_TXFULL) != 0)
+ ;
+
+ WR4(bas, CDNC_UART_FIFO, c);
+
+ while ((RD4(bas,CDNC_UART_CHAN_STAT_REG) &
+ CDNC_UART_CHAN_STAT_REG_TXEMPTY) == 0)
+ ;
+}
+
+/*
+ * Check for a character available.
+ */
+static int
+cdnc_uart_rxready(struct uart_bas *bas)
+{
+
+ return ((RD4(bas, CDNC_UART_CHAN_STAT_REG) &
+ CDNC_UART_CHAN_STAT_REG_RXEMPTY) == 0);
+}
+
+/*
+ * Block waiting for a character.
+ */
+static int
+cdnc_uart_getc(struct uart_bas *bas, struct mtx *mtx)
+{
+ int c;
+
+ uart_lock(mtx);
+
+ while ((RD4(bas, CDNC_UART_CHAN_STAT_REG) &
+ CDNC_UART_CHAN_STAT_REG_RXEMPTY) != 0) {
+ uart_unlock(mtx);
+ DELAY(4);
+ uart_lock(mtx);
+ }
+
+ c = RD4(bas, CDNC_UART_FIFO);
+
+ uart_unlock(mtx);
+
+ c &= 0xff;
+ return (c);
+}
+
+/*****************************************************************************/
+/*
+ * High-level UART interface.
+ */
+
+static int cdnc_uart_bus_probe(struct uart_softc *sc);
+static int cdnc_uart_bus_attach(struct uart_softc *sc);
+static int cdnc_uart_bus_flush(struct uart_softc *, int);
+static int cdnc_uart_bus_getsig(struct uart_softc *);
+static int cdnc_uart_bus_ioctl(struct uart_softc *, int, intptr_t);
+static int cdnc_uart_bus_ipend(struct uart_softc *);
+static int cdnc_uart_bus_param(struct uart_softc *, int, int, int, int);
+static int cdnc_uart_bus_receive(struct uart_softc *);
+static int cdnc_uart_bus_setsig(struct uart_softc *, int);
+static int cdnc_uart_bus_transmit(struct uart_softc *);
+static void cdnc_uart_bus_grab(struct uart_softc *);
+static void cdnc_uart_bus_ungrab(struct uart_softc *);
+
+static kobj_method_t cdnc_uart_bus_methods[] = {
+ KOBJMETHOD(uart_probe, cdnc_uart_bus_probe),
+ KOBJMETHOD(uart_attach, cdnc_uart_bus_attach),
+ KOBJMETHOD(uart_flush, cdnc_uart_bus_flush),
+ KOBJMETHOD(uart_getsig, cdnc_uart_bus_getsig),
+ KOBJMETHOD(uart_ioctl, cdnc_uart_bus_ioctl),
+ KOBJMETHOD(uart_ipend, cdnc_uart_bus_ipend),
+ KOBJMETHOD(uart_param, cdnc_uart_bus_param),
+ KOBJMETHOD(uart_receive, cdnc_uart_bus_receive),
+ KOBJMETHOD(uart_setsig, cdnc_uart_bus_setsig),
+ KOBJMETHOD(uart_transmit, cdnc_uart_bus_transmit),
+ KOBJMETHOD(uart_grab, cdnc_uart_bus_grab),
+ KOBJMETHOD(uart_ungrab, cdnc_uart_bus_ungrab),
+
+ KOBJMETHOD_END
+};
+
+int
+cdnc_uart_bus_probe(struct uart_softc *sc)
+{
+
+ sc->sc_txfifosz = UART_FIFO_SIZE;
+ sc->sc_rxfifosz = UART_FIFO_SIZE;
+ sc->sc_hwiflow = 0;
+ sc->sc_hwoflow = 0;
+
+ device_set_desc(sc->sc_dev, "Cadence UART");
+
+ return (0);
+}
+
+static int
+cdnc_uart_bus_attach(struct uart_softc *sc)
+{
+ struct uart_bas *bas = &sc->sc_bas;
+ struct uart_devinfo *di;
+
+ if (sc->sc_sysdev != NULL) {
+ di = sc->sc_sysdev;
+ (void)cdnc_uart_set_params(bas, di->baudrate, di->databits,
+ di->stopbits, di->parity);
+ } else
+ cdnc_uart_hw_init(bas);
+
+ (void)cdnc_uart_bus_getsig(sc);
+
+ /* Enable interrupts. */
+ WR4(bas, CDNC_UART_IEN_REG,
+ CDNC_UART_INT_RXTRIG | CDNC_UART_INT_RXTMOUT |
+ CDNC_UART_INT_TXOVR | CDNC_UART_INT_RXOVR |
+ CDNC_UART_INT_DMSI);
+
+ return (0);
+}
+
+static int
+cdnc_uart_bus_transmit(struct uart_softc *sc)
+{
+ int i;
+ struct uart_bas *bas = &sc->sc_bas;
+
+ uart_lock(sc->sc_hwmtx);
+
+ /* Clear sticky TXEMPTY status bit. */
+ WR4(bas, CDNC_UART_ISTAT_REG, CDNC_UART_INT_TXEMPTY);
+
+ for (i = 0; i < sc->sc_txdatasz; i++)
+ WR4(bas, CDNC_UART_FIFO, sc->sc_txbuf[i]);
+
+ /* Enable TX empty interrupt. */
+ WR4(bas, CDNC_UART_IEN_REG, CDNC_UART_INT_TXEMPTY);
+ sc->sc_txbusy = 1;
+
+ uart_unlock(sc->sc_hwmtx);
+
+ return (0);
+}
+
+static int
+cdnc_uart_bus_setsig(struct uart_softc *sc, int sig)
+{
+ struct uart_bas *bas = &sc->sc_bas;
+ uint32_t new, old, modem_ctrl;
+
+ do {
+ old = sc->sc_hwsig;
+ new = old;
+ if (sig & SER_DDTR) {
+ SIGCHG(sig & SER_DTR, new, SER_DTR, SER_DDTR);
+ }
+ if (sig & SER_DRTS) {
+ SIGCHG(sig & SER_RTS, new, SER_RTS, SER_DRTS);
+ }
+ } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
+ uart_lock(sc->sc_hwmtx);
+ modem_ctrl = RD4(bas, CDNC_UART_MODEM_CTRL_REG) &
+ ~(CDNC_UART_MODEM_CTRL_REG_DTR | CDNC_UART_MODEM_CTRL_REG_RTS);
+ if ((new & SER_DTR) != 0)
+ modem_ctrl |= CDNC_UART_MODEM_CTRL_REG_DTR;
+ if ((new & SER_RTS) != 0)
+ modem_ctrl |= CDNC_UART_MODEM_CTRL_REG_RTS;
+ WR4(bas, CDNC_UART_MODEM_CTRL_REG, modem_ctrl);
+
+ uart_unlock(sc->sc_hwmtx);
+ return (0);
+}
+
+static int
+cdnc_uart_bus_receive(struct uart_softc *sc)
+{
+ struct uart_bas *bas = &sc->sc_bas;
+ uint32_t status;
+ int c, c_status = 0;
+
+ uart_lock(sc->sc_hwmtx);
+
+ /* Check for parity or framing errors and clear the status bits. */
+ status = RD4(bas, CDNC_UART_ISTAT_REG);
+ if ((status & (CDNC_UART_INT_FRAMING | CDNC_UART_INT_PARITY)) != 0) {
+ WR4(bas, CDNC_UART_ISTAT_REG,
+ status & (CDNC_UART_INT_FRAMING | CDNC_UART_INT_PARITY));
+ if ((status & CDNC_UART_INT_PARITY) != 0)
+ c_status |= UART_STAT_PARERR;
+ if ((status & CDNC_UART_INT_FRAMING) != 0)
+ c_status |= UART_STAT_FRAMERR;
+ }
+
+ while ((RD4(bas, CDNC_UART_CHAN_STAT_REG) &
+ CDNC_UART_CHAN_STAT_REG_RXEMPTY) == 0) {
+ c = RD4(bas, CDNC_UART_FIFO) & 0xff;
+#ifdef KDB
+ /* Detect break and drop into debugger. */
+ if (c == 0 && (c_status & UART_STAT_FRAMERR) != 0 &&
+ sc->sc_sysdev != NULL &&
+ sc->sc_sysdev->type == UART_DEV_CONSOLE) {
+ kdb_break();
+ WR4(bas, CDNC_UART_ISTAT_REG, CDNC_UART_INT_FRAMING);
+ }
+#endif
+ uart_rx_put(sc, c | c_status);
+ }
+
+ uart_unlock(sc->sc_hwmtx);
+
+ return (0);
+}
+
+static int
+cdnc_uart_bus_param(struct uart_softc *sc, int baudrate, int databits,
+ int stopbits, int parity)
+{
+
+ return (cdnc_uart_set_params(&sc->sc_bas, baudrate,
+ databits, stopbits, parity));
+}
+
+static int
+cdnc_uart_bus_ipend(struct uart_softc *sc)
+{
+ int ipend = 0;
+ struct uart_bas *bas = &sc->sc_bas;
+ uint32_t istatus;
+
+ uart_lock(sc->sc_hwmtx);
+
+ istatus = RD4(bas, CDNC_UART_ISTAT_REG);
+
+ /* Clear interrupt bits. */
+ WR4(bas, CDNC_UART_ISTAT_REG, istatus &
+ (CDNC_UART_INT_RXTRIG | CDNC_UART_INT_RXTMOUT |
+ CDNC_UART_INT_TXOVR | CDNC_UART_INT_RXOVR |
+ CDNC_UART_INT_TXEMPTY | CDNC_UART_INT_DMSI));
+
+ /* Receive data. */
+ if ((istatus & (CDNC_UART_INT_RXTRIG | CDNC_UART_INT_RXTMOUT)) != 0)
+ ipend |= SER_INT_RXREADY;
+
+ /* Transmit fifo empty. */
+ if (sc->sc_txbusy && (istatus & CDNC_UART_INT_TXEMPTY) != 0) {
+ /* disable txempty interrupt. */
+ WR4(bas, CDNC_UART_IDIS_REG, CDNC_UART_INT_TXEMPTY);
+ ipend |= SER_INT_TXIDLE;
+ }
+
+ /* TX Overflow. */
+ if ((istatus & CDNC_UART_INT_TXOVR) != 0)
+ ipend |= SER_INT_OVERRUN;
+
+ /* RX Overflow. */
+ if ((istatus & CDNC_UART_INT_RXOVR) != 0)
+ ipend |= SER_INT_OVERRUN;
+
+ /* Modem signal change. */
+ if ((istatus & CDNC_UART_INT_DMSI) != 0) {
+ WR4(bas, CDNC_UART_MODEM_STAT_REG,
+ CDNC_UART_MODEM_STAT_REG_DDCD |
+ CDNC_UART_MODEM_STAT_REG_TERI |
+ CDNC_UART_MODEM_STAT_REG_DDSR |
+ CDNC_UART_MODEM_STAT_REG_DCTS);
+ ipend |= SER_INT_SIGCHG;
+ }
+
+ uart_unlock(sc->sc_hwmtx);
+ return (ipend);
+}
+
+static int
+cdnc_uart_bus_flush(struct uart_softc *sc, int what)
+{
+
+ return (0);
+}
+
+static int
+cdnc_uart_bus_getsig(struct uart_softc *sc)
+{
+ struct uart_bas *bas = &sc->sc_bas;
+ uint32_t new, old, sig;
+ uint8_t modem_status;
+
+ do {
+ old = sc->sc_hwsig;
+ sig = old;
+ uart_lock(sc->sc_hwmtx);
+ modem_status = RD4(bas, CDNC_UART_MODEM_STAT_REG);
+ uart_unlock(sc->sc_hwmtx);
+ SIGCHG(modem_status & CDNC_UART_MODEM_STAT_REG_DSR,
+ sig, SER_DSR, SER_DDSR);
+ SIGCHG(modem_status & CDNC_UART_MODEM_STAT_REG_CTS,
+ sig, SER_CTS, SER_DCTS);
+ SIGCHG(modem_status & CDNC_UART_MODEM_STAT_REG_DCD,
+ sig, SER_DCD, SER_DDCD);
+ SIGCHG(modem_status & CDNC_UART_MODEM_STAT_REG_RI,
+ sig, SER_RI, SER_DRI);
+ new = sig & ~SER_MASK_DELTA;
+ } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
+ return (sig);
+}
+
+static int
+cdnc_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
+{
+ struct uart_bas *bas = &sc->sc_bas;
+ uint32_t uart_ctrl, modem_ctrl;
+ int error = 0;
+
+ uart_lock(sc->sc_hwmtx);
+
+ switch (request) {
+ case UART_IOCTL_BREAK:
+ uart_ctrl = RD4(bas, CDNC_UART_CTRL_REG);
+ if (data) {
+ uart_ctrl |= CDNC_UART_CTRL_REG_STARTBRK;
+ uart_ctrl &= ~CDNC_UART_CTRL_REG_STOPBRK;
+ } else {
+ uart_ctrl |= CDNC_UART_CTRL_REG_STOPBRK;
+ uart_ctrl &= ~CDNC_UART_CTRL_REG_STARTBRK;
+ }
+ WR4(bas, CDNC_UART_CTRL_REG, uart_ctrl);
+ break;
+ case UART_IOCTL_IFLOW:
+ modem_ctrl = RD4(bas, CDNC_UART_MODEM_CTRL_REG);
+ if (data)
+ modem_ctrl |= CDNC_UART_MODEM_CTRL_REG_RTS;
+ else
+ modem_ctrl &= ~CDNC_UART_MODEM_CTRL_REG_RTS;
+ WR4(bas, CDNC_UART_MODEM_CTRL_REG, modem_ctrl);
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ uart_unlock(sc->sc_hwmtx);
+
+ return (error);
+}
+
+static void
+cdnc_uart_bus_grab(struct uart_softc *sc)
+{
+
+ /* Enable interrupts. */
+ WR4(&sc->sc_bas, CDNC_UART_IEN_REG,
+ CDNC_UART_INT_TXOVR | CDNC_UART_INT_RXOVR |
+ CDNC_UART_INT_DMSI);
+}
+
+static void
+cdnc_uart_bus_ungrab(struct uart_softc *sc)
+{
+
+ /* Enable interrupts. */
+ WR4(&sc->sc_bas, CDNC_UART_IEN_REG,
+ CDNC_UART_INT_RXTRIG | CDNC_UART_INT_RXTMOUT |
+ CDNC_UART_INT_TXOVR | CDNC_UART_INT_RXOVR |
+ CDNC_UART_INT_DMSI);
+}
+
+static struct uart_class uart_cdnc_class = {
+ "cdnc_uart",
+ cdnc_uart_bus_methods,
+ sizeof(struct uart_softc),
+ .uc_ops = &cdnc_uart_ops,
+ .uc_range = 8
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"cadence,uart", (uintptr_t)&uart_cdnc_class},
+ {"cdns,uart-r1p12", (uintptr_t)&uart_cdnc_class},
+ {"xlnx,xuartps", (uintptr_t)&uart_cdnc_class},
+ {NULL, (uintptr_t)NULL},
+};
+UART_FDT_CLASS_AND_DEVICE(compat_data);
diff --git a/sys/arm/xilinx/zy7_devcfg.c b/sys/arm/xilinx/zy7_devcfg.c
new file mode 100644
index 000000000000..d10cf1e31c23
--- /dev/null
+++ b/sys/arm/xilinx/zy7_devcfg.c
@@ -0,0 +1,850 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Thomas Skibo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Zynq-7000 Devcfg driver. This allows programming the PL (FPGA) section
+ * of Zynq.
+ *
+ * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual.
+ * (v1.4) November 16, 2012. Xilinx doc UG585. PL Configuration is
+ * covered in section 6.4.5.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/uio.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/stdarg.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/xilinx/zy7_slcr.h>
+
+struct zy7_devcfg_softc {
+ device_t dev;
+ struct mtx sc_mtx;
+ struct resource *mem_res;
+ struct resource *irq_res;
+ struct cdev *sc_ctl_dev;
+ void *intrhandle;
+
+ bus_dma_tag_t dma_tag;
+ bus_dmamap_t dma_map;
+
+ int is_open;
+
+ struct sysctl_ctx_list sysctl_tree;
+ struct sysctl_oid *sysctl_tree_top;
+};
+
+static struct zy7_devcfg_softc *zy7_devcfg_softc_p;
+
+#define FCLK_NUM 4
+
+struct zy7_fclk_config {
+ int source;
+ int frequency;
+ int actual_frequency;
+};
+
+static struct zy7_fclk_config fclk_configs[FCLK_NUM];
+
+#define DEVCFG_SC_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
+#define DEVCFG_SC_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
+#define DEVCFG_SC_LOCK_INIT(sc) \
+ mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->dev), \
+ "zy7_devcfg", MTX_DEF)
+#define DEVCFG_SC_LOCK_DESTROY(sc) mtx_destroy(&(sc)->sc_mtx);
+#define DEVCFG_SC_ASSERT_LOCKED(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED);
+
+#define RD4(sc, off) (bus_read_4((sc)->mem_res, (off)))
+#define WR4(sc, off, val) (bus_write_4((sc)->mem_res, (off), (val)))
+
+SYSCTL_NODE(_hw, OID_AUTO, fpga, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
+ "Xilinx Zynq-7000 PL (FPGA) section");
+
+static int zy7_devcfg_sysctl_pl_done(SYSCTL_HANDLER_ARGS);
+SYSCTL_PROC(_hw_fpga, OID_AUTO, pl_done,
+ CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, NULL, 0,
+ zy7_devcfg_sysctl_pl_done, "I",
+ "PL section config DONE signal");
+
+static int zy7_en_level_shifters = 1;
+SYSCTL_INT(_hw_fpga, OID_AUTO, en_level_shifters, CTLFLAG_RW,
+ &zy7_en_level_shifters, 0,
+ "Enable PS-PL level shifters after device config");
+
+static int zy7_ps_vers = 0;
+SYSCTL_INT(_hw, OID_AUTO, ps_vers, CTLFLAG_RD, &zy7_ps_vers, 0,
+ "Zynq-7000 PS version");
+
+static int zy7_devcfg_fclk_sysctl_level_shifters(SYSCTL_HANDLER_ARGS);
+SYSCTL_PROC(_hw_fpga, OID_AUTO, level_shifters,
+ CTLFLAG_RW | CTLTYPE_INT | CTLFLAG_NEEDGIANT, NULL, 0,
+ zy7_devcfg_fclk_sysctl_level_shifters, "I",
+ "Enable/disable level shifters");
+
+/* cdev entry points. */
+static int zy7_devcfg_open(struct cdev *, int, int, struct thread *);
+static int zy7_devcfg_write(struct cdev *, struct uio *, int);
+static int zy7_devcfg_close(struct cdev *, int, int, struct thread *);
+
+struct cdevsw zy7_devcfg_cdevsw = {
+ .d_version = D_VERSION,
+ .d_open = zy7_devcfg_open,
+ .d_write = zy7_devcfg_write,
+ .d_close = zy7_devcfg_close,
+ .d_name = "devcfg",
+};
+
+/* Devcfg block registers. */
+#define ZY7_DEVCFG_CTRL 0x0000
+#define ZY7_DEVCFG_CTRL_FORCE_RST (1<<31)
+#define ZY7_DEVCFG_CTRL_PCFG_PROG_B (1<<30)
+#define ZY7_DEVCFG_CTRL_PCFG_POR_CNT_4K (1<<29)
+#define ZY7_DEVCFG_CTRL_PCAP_PR (1<<27)
+#define ZY7_DEVCFG_CTRL_PCAP_MODE (1<<26)
+#define ZY7_DEVCFG_CTRL_QTR_PCAP_RATE_EN (1<<25)
+#define ZY7_DEVCFG_CTRL_MULTIBOOT_EN (1<<24)
+#define ZY7_DEVCFG_CTRL_JTAG_CHAIN_DIS (1<<23)
+#define ZY7_DEVCFG_CTRL_USER_MODE (1<<15)
+#define ZY7_DEVCFG_CTRL_RESVD_WR11 (3<<13) /* always write 11 */
+#define ZY7_DEVCFG_CTRL_PCFG_AES_FUSE (1<<12)
+#define ZY7_DEVCFG_CTRL_PCFG_AES_EN_MASK (7<<9) /* all 1's or 0's */
+#define ZY7_DEVCFG_CTRL_SEU_EN (1<<8)
+#define ZY7_DEVCFG_CTRL_SEC_EN (1<<7)
+#define ZY7_DEVCFG_CTRL_SPNIDEN (1<<6)
+#define ZY7_DEVCFG_CTRL_SPIDEN (1<<5)
+#define ZY7_DEVCFG_CTRL_NIDEN (1<<4)
+#define ZY7_DEVCFG_CTRL_DBGEN (1<<3)
+#define ZY7_DEVCFG_CTRL_DAP_EN_MASK (7<<0) /* all 1's to enable */
+
+#define ZY7_DEVCFG_LOCK 0x004
+#define ZY7_DEVCFG_LOCK_AES_FUSE_LOCK (1<<4)
+#define ZY7_DEVCFG_LOCK_AES_EN (1<<3)
+#define ZY7_DEVCFG_LOCK_SEU_LOCK (1<<2)
+#define ZY7_DEVCFG_LOCK_SEC_LOCK (1<<1)
+#define ZY7_DEVCFG_LOCK_DBG_LOCK (1<<0)
+
+#define ZY7_DEVCFG_CFG 0x008
+#define ZY7_DEVCFG_CFG_RFIFO_TH_MASK (3<<10)
+#define ZY7_DEVCFG_CFG_WFIFO_TH_MASK (3<<8)
+#define ZY7_DEVCFG_CFG_RCLK_EDGE (1<<7)
+#define ZY7_DEVCFG_CFG_WCLK_EDGE (1<<6)
+#define ZY7_DEVCFG_CFG_DIS_SRC_INC (1<<5)
+#define ZY7_DEVCFG_CFG_DIS_DST_INC (1<<4)
+
+#define ZY7_DEVCFG_INT_STATUS 0x00C
+#define ZY7_DEVCFG_INT_MASK 0x010
+#define ZY7_DEVCFG_INT_PSS_GTS_USR_B (1<<31)
+#define ZY7_DEVCFG_INT_PSS_FST_CFG_B (1<<30)
+#define ZY7_DEVCFG_INT_PSS_GPWRDWN_B (1<<29)
+#define ZY7_DEVCFG_INT_PSS_GTS_CFG_B (1<<28)
+#define ZY7_DEVCFG_INT_CFG_RESET_B (1<<27)
+#define ZY7_DEVCFG_INT_AXI_WTO (1<<23) /* axi write timeout */
+#define ZY7_DEVCFG_INT_AXI_WERR (1<<22) /* axi write err */
+#define ZY7_DEVCFG_INT_AXI_RTO (1<<21) /* axi read timeout */
+#define ZY7_DEVCFG_INT_AXI_RERR (1<<20) /* axi read err */
+#define ZY7_DEVCFG_INT_RX_FIFO_OV (1<<18) /* rx fifo overflow */
+#define ZY7_DEVCFG_INT_WR_FIFO_LVL (1<<17) /* wr fifo < level */
+#define ZY7_DEVCFG_INT_RD_FIFO_LVL (1<<16) /* rd fifo >= level */
+#define ZY7_DEVCFG_INT_DMA_CMD_ERR (1<<15)
+#define ZY7_DEVCFG_INT_DMA_Q_OV (1<<14)
+#define ZY7_DEVCFG_INT_DMA_DONE (1<<13)
+#define ZY7_DEVCFG_INT_DMA_PCAP_DONE (1<<12)
+#define ZY7_DEVCFG_INT_P2D_LEN_ERR (1<<11)
+#define ZY7_DEVCFG_INT_PCFG_HMAC_ERR (1<<6)
+#define ZY7_DEVCFG_INT_PCFG_SEU_ERR (1<<5)
+#define ZY7_DEVCFG_INT_PCFG_POR_B (1<<4)
+#define ZY7_DEVCFG_INT_PCFG_CFG_RST (1<<3)
+#define ZY7_DEVCFG_INT_PCFG_DONE (1<<2)
+#define ZY7_DEVCFG_INT_PCFG_INIT_PE (1<<1)
+#define ZY7_DEVCFG_INT_PCFG_INIT_NE (1<<0)
+#define ZY7_DEVCFG_INT_ERRORS 0x00f0f860
+#define ZY7_DEVCFG_INT_ALL 0xf8f7f87f
+
+#define ZY7_DEVCFG_STATUS 0x014
+#define ZY7_DEVCFG_STATUS_DMA_CMD_Q_F (1<<31) /* cmd queue full */
+#define ZY7_DEVCFG_STATUS_DMA_CMD_Q_E (1<<30) /* cmd queue empty */
+#define ZY7_DEVCFG_STATUS_DONE_COUNT_MASK (3<<28)
+#define ZY7_DEVCFG_STATUS_DONE_COUNT_SHIFT 28
+#define ZY7_DEVCFG_STATUS_RX_FIFO_LVL_MASK (0x1f<<20)
+#define ZY7_DEVCFG_STATUS_RX_FIFO_LVL_SHIFT 20
+#define ZY7_DEVCFG_STATUS_TX_FIFO_LVL_MASK (0x7f<<12)
+#define ZY7_DEVCFG_STATUS_TX_FIFO_LVL_SHIFT 12
+#define ZY7_DEVCFG_STATUS_PSS_GTS_USR_B (1<<11)
+#define ZY7_DEVCFG_STATUS_PSS_FST_CFG_B (1<<10)
+#define ZY7_DEVCFG_STATUS_PSS_GPWRDWN_B (1<<9)
+#define ZY7_DEVCFG_STATUS_PSS_GTS_CFG_B (1<<8)
+#define ZY7_DEVCFG_STATUS_ILL_APB_ACCE (1<<6)
+#define ZY7_DEVCFG_STATUS_PSS_CFG_RESET_B (1<<5)
+#define ZY7_DEVCFG_STATUS_PCFG_INIT (1<<4)
+#define ZY7_DEVCFG_STATUS_EFUSE_BBRAM_KEY_DIS (1<<3)
+#define ZY7_DEVCFG_STATUS_EFUSE_SEC_EN (1<<2)
+#define ZY7_DEVCFG_STATUS_EFUSE_JTAG_DIS (1<<1)
+
+#define ZY7_DEVCFG_DMA_SRC_ADDR 0x018
+#define ZY7_DEVCFG_DMA_DST_ADDR 0x01c
+#define ZY7_DEVCFG_DMA_ADDR_WAIT_PCAP 1
+#define ZY7_DEVCFG_DMA_ADDR_ILLEGAL 0xffffffff
+
+#define ZY7_DEVCFG_DMA_SRC_LEN 0x020 /* in 4-byte words. */
+#define ZY7_DEVCFG_DMA_SRC_LEN_MAX 0x7ffffff
+#define ZY7_DEVCFG_DMA_DST_LEN 0x024
+#define ZY7_DEVCFG_ROM_SHADOW 0x028
+#define ZY7_DEVCFG_MULTIBOOT_ADDR 0x02c
+#define ZY7_DEVCFG_SW_ID 0x030
+#define ZY7_DEVCFG_UNLOCK 0x034
+#define ZY7_DEVCFG_UNLOCK_MAGIC 0x757bdf0d
+#define ZY7_DEVCFG_MCTRL 0x080
+#define ZY7_DEVCFG_MCTRL_PS_VERS_MASK (0xf<<28)
+#define ZY7_DEVCFG_MCTRL_PS_VERS_SHIFT 28
+#define ZY7_DEVCFG_MCTRL_PCFG_POR_B (1<<8)
+#define ZY7_DEVCFG_MCTRL_INT_PCAP_LPBK (1<<4)
+#define ZY7_DEVCFG_XADCIF_CFG 0x100
+#define ZY7_DEVCFG_XADCIF_INT_STAT 0x104
+#define ZY7_DEVCFG_XADCIF_INT_MASK 0x108
+#define ZY7_DEVCFG_XADCIF_MSTS 0x10c
+#define ZY7_DEVCFG_XADCIF_CMD_FIFO 0x110
+#define ZY7_DEVCFG_XADCIF_RD_FIFO 0x114
+#define ZY7_DEVCFG_XADCIF_MCTL 0x118
+
+static int
+zy7_devcfg_fclk_sysctl_source(SYSCTL_HANDLER_ARGS)
+{
+ char buf[4];
+ struct zy7_fclk_config *cfg;
+ int unit;
+ int error;
+
+ cfg = arg1;
+ unit = arg2;
+
+ switch (cfg->source) {
+ case ZY7_PL_FCLK_SRC_IO:
+ case ZY7_PL_FCLK_SRC_IO_ALT:
+ strncpy(buf, "IO", sizeof(buf));
+ break;
+ case ZY7_PL_FCLK_SRC_DDR:
+ strncpy(buf, "DDR", sizeof(buf));
+ break;
+ case ZY7_PL_FCLK_SRC_ARM:
+ strncpy(buf, "ARM", sizeof(buf));
+ break;
+ default:
+ strncpy(buf, "???", sizeof(buf));
+ break;
+ }
+
+ error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ if (strcasecmp(buf, "io") == 0)
+ cfg->source = ZY7_PL_FCLK_SRC_IO;
+ else if (strcasecmp(buf, "ddr") == 0)
+ cfg->source = ZY7_PL_FCLK_SRC_DDR;
+ else if (strcasecmp(buf, "arm") == 0)
+ cfg->source = ZY7_PL_FCLK_SRC_ARM;
+ else
+ return (EINVAL);
+
+ zy7_pl_fclk_set_source(unit, cfg->source);
+ if (cfg->frequency > 0)
+ cfg->actual_frequency = zy7_pl_fclk_get_freq(unit);
+
+ return (0);
+}
+
+static int
+zy7_devcfg_fclk_sysctl_freq(SYSCTL_HANDLER_ARGS)
+{
+ struct zy7_fclk_config *cfg;
+ int unit;
+ int error;
+ int freq;
+ int new_actual_freq;
+
+ cfg = arg1;
+ unit = arg2;
+
+ freq = cfg->frequency;
+
+ error = sysctl_handle_int(oidp, &freq, 0, req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ if (freq > 0) {
+ new_actual_freq = zy7_pl_fclk_set_freq(unit, freq);
+ if (new_actual_freq < 0)
+ return (EINVAL);
+ if (!zy7_pl_fclk_enabled(unit))
+ zy7_pl_fclk_enable(unit);
+ }
+ else {
+ zy7_pl_fclk_disable(unit);
+ new_actual_freq = 0;
+ }
+
+ cfg->frequency = freq;
+ cfg->actual_frequency = new_actual_freq;
+
+ return (0);
+}
+
+static int
+zy7_devcfg_fclk_sysctl_level_shifters(SYSCTL_HANDLER_ARGS)
+{
+ int error, enabled;
+
+ enabled = zy7_pl_level_shifters_enabled();
+
+ error = sysctl_handle_int(oidp, &enabled, 0, req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ if (enabled)
+ zy7_pl_level_shifters_enable();
+ else
+ zy7_pl_level_shifters_disable();
+
+ return (0);
+}
+
+static int
+zy7_devcfg_init_fclk_sysctl(struct zy7_devcfg_softc *sc)
+{
+ struct sysctl_oid *fclk_node;
+ char fclk_num[4];
+ int i;
+
+ sysctl_ctx_init(&sc->sysctl_tree);
+ sc->sysctl_tree_top = SYSCTL_ADD_NODE(&sc->sysctl_tree,
+ SYSCTL_STATIC_CHILDREN(_hw_fpga), OID_AUTO, "fclk",
+ CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
+ if (sc->sysctl_tree_top == NULL) {
+ sysctl_ctx_free(&sc->sysctl_tree);
+ return (-1);
+ }
+
+ for (i = 0; i < FCLK_NUM; i++) {
+ snprintf(fclk_num, sizeof(fclk_num), "%d", i);
+ fclk_node = SYSCTL_ADD_NODE(&sc->sysctl_tree,
+ SYSCTL_CHILDREN(sc->sysctl_tree_top), OID_AUTO, fclk_num,
+ CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
+
+ SYSCTL_ADD_INT(&sc->sysctl_tree,
+ SYSCTL_CHILDREN(fclk_node), OID_AUTO,
+ "actual_freq", CTLFLAG_RD,
+ &fclk_configs[i].actual_frequency, i,
+ "Actual frequency");
+ SYSCTL_ADD_PROC(&sc->sysctl_tree,
+ SYSCTL_CHILDREN(fclk_node), OID_AUTO,
+ "freq", CTLFLAG_RW | CTLTYPE_INT | CTLFLAG_NEEDGIANT,
+ &fclk_configs[i], i,
+ zy7_devcfg_fclk_sysctl_freq,
+ "I", "Configured frequency");
+ SYSCTL_ADD_PROC(&sc->sysctl_tree,
+ SYSCTL_CHILDREN(fclk_node), OID_AUTO,
+ "source", CTLFLAG_RW | CTLTYPE_STRING | CTLFLAG_NEEDGIANT,
+ &fclk_configs[i], i,
+ zy7_devcfg_fclk_sysctl_source,
+ "A", "Clock source");
+ }
+
+ return (0);
+}
+
+/* Enable programming the PL through PCAP. */
+static void
+zy7_devcfg_init_hw(struct zy7_devcfg_softc *sc)
+{
+
+ DEVCFG_SC_ASSERT_LOCKED(sc);
+
+ /* Set devcfg control register. */
+ WR4(sc, ZY7_DEVCFG_CTRL,
+ ZY7_DEVCFG_CTRL_PCFG_PROG_B |
+ ZY7_DEVCFG_CTRL_PCAP_PR |
+ ZY7_DEVCFG_CTRL_PCAP_MODE |
+ ZY7_DEVCFG_CTRL_USER_MODE |
+ ZY7_DEVCFG_CTRL_RESVD_WR11 |
+ ZY7_DEVCFG_CTRL_SPNIDEN |
+ ZY7_DEVCFG_CTRL_SPIDEN |
+ ZY7_DEVCFG_CTRL_NIDEN |
+ ZY7_DEVCFG_CTRL_DBGEN |
+ ZY7_DEVCFG_CTRL_DAP_EN_MASK);
+
+ /* Turn off internal PCAP loopback. */
+ WR4(sc, ZY7_DEVCFG_MCTRL, RD4(sc, ZY7_DEVCFG_MCTRL) &
+ ~ZY7_DEVCFG_MCTRL_INT_PCAP_LPBK);
+}
+
+/* Clear previous configuration of the PL by asserting PROG_B. */
+static int
+zy7_devcfg_reset_pl(struct zy7_devcfg_softc *sc)
+{
+ uint32_t devcfg_ctl;
+ int tries, err;
+
+ DEVCFG_SC_ASSERT_LOCKED(sc);
+
+ devcfg_ctl = RD4(sc, ZY7_DEVCFG_CTRL);
+
+ /* Clear sticky bits and set up INIT signal positive edge interrupt. */
+ WR4(sc, ZY7_DEVCFG_INT_STATUS, ZY7_DEVCFG_INT_ALL);
+ WR4(sc, ZY7_DEVCFG_INT_MASK, ~ZY7_DEVCFG_INT_PCFG_INIT_PE);
+
+ /* Deassert PROG_B (active low). */
+ devcfg_ctl |= ZY7_DEVCFG_CTRL_PCFG_PROG_B;
+ WR4(sc, ZY7_DEVCFG_CTRL, devcfg_ctl);
+
+ /*
+ * Wait for INIT to assert. If it is already asserted, we may not get
+ * an edge interrupt so cancel it and continue.
+ */
+ if ((RD4(sc, ZY7_DEVCFG_STATUS) &
+ ZY7_DEVCFG_STATUS_PCFG_INIT) != 0) {
+ /* Already asserted. Cancel interrupt. */
+ WR4(sc, ZY7_DEVCFG_INT_MASK, ~0);
+ }
+ else {
+ /* Wait for positive edge interrupt. */
+ err = mtx_sleep(sc, &sc->sc_mtx, PCATCH, "zy7i1", hz);
+ if (err != 0)
+ return (err);
+ }
+
+ /* Reassert PROG_B (active low). */
+ devcfg_ctl &= ~ZY7_DEVCFG_CTRL_PCFG_PROG_B;
+ WR4(sc, ZY7_DEVCFG_CTRL, devcfg_ctl);
+
+ /* Wait for INIT deasserted. This happens almost instantly. */
+ tries = 0;
+ while ((RD4(sc, ZY7_DEVCFG_STATUS) &
+ ZY7_DEVCFG_STATUS_PCFG_INIT) != 0) {
+ if (++tries >= 100)
+ return (EIO);
+ DELAY(5);
+ }
+
+ /* Clear sticky bits and set up INIT positive edge interrupt. */
+ WR4(sc, ZY7_DEVCFG_INT_STATUS, ZY7_DEVCFG_INT_ALL);
+ WR4(sc, ZY7_DEVCFG_INT_MASK, ~ZY7_DEVCFG_INT_PCFG_INIT_PE);
+
+ /* Deassert PROG_B again. */
+ devcfg_ctl |= ZY7_DEVCFG_CTRL_PCFG_PROG_B;
+ WR4(sc, ZY7_DEVCFG_CTRL, devcfg_ctl);
+
+ /*
+ * Wait for INIT asserted indicating FPGA internal initialization
+ * is complete.
+ */
+ err = mtx_sleep(sc, &sc->sc_mtx, PCATCH, "zy7i2", hz);
+ if (err != 0)
+ return (err);
+
+ /* Clear sticky DONE bit in interrupt status. */
+ WR4(sc, ZY7_DEVCFG_INT_STATUS, ZY7_DEVCFG_INT_ALL);
+
+ return (0);
+}
+
+/* Callback function for bus_dmamap_load(). */
+static void
+zy7_dma_cb2(void *arg, bus_dma_segment_t *seg, int nsegs, int error)
+{
+ if (!error && nsegs == 1)
+ *(bus_addr_t *)arg = seg[0].ds_addr;
+}
+
+static int
+zy7_devcfg_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
+{
+ struct zy7_devcfg_softc *sc = dev->si_drv1;
+ int err;
+
+ DEVCFG_SC_LOCK(sc);
+ if (sc->is_open) {
+ DEVCFG_SC_UNLOCK(sc);
+ return (EBUSY);
+ }
+
+ sc->dma_map = NULL;
+ err = bus_dma_tag_create(bus_get_dma_tag(sc->dev), 4, 0,
+ BUS_SPACE_MAXADDR_32BIT,
+ BUS_SPACE_MAXADDR,
+ NULL, NULL,
+ PAGE_SIZE,
+ 1,
+ PAGE_SIZE,
+ 0,
+ busdma_lock_mutex,
+ &sc->sc_mtx,
+ &sc->dma_tag);
+ if (err) {
+ DEVCFG_SC_UNLOCK(sc);
+ return (err);
+ }
+
+ sc->is_open = 1;
+ DEVCFG_SC_UNLOCK(sc);
+ return (0);
+}
+
+static int
+zy7_devcfg_write(struct cdev *dev, struct uio *uio, int ioflag)
+{
+ struct zy7_devcfg_softc *sc = dev->si_drv1;
+ void *dma_mem;
+ bus_addr_t dma_physaddr;
+ int segsz, err;
+
+ DEVCFG_SC_LOCK(sc);
+
+ /* First write? Reset PL. */
+ if (uio->uio_offset == 0 && uio->uio_resid > 0) {
+ zy7_devcfg_init_hw(sc);
+ zy7_slcr_preload_pl();
+ err = zy7_devcfg_reset_pl(sc);
+ if (err != 0) {
+ DEVCFG_SC_UNLOCK(sc);
+ return (err);
+ }
+ }
+
+ /* Allocate dma memory and load. */
+ err = bus_dmamem_alloc(sc->dma_tag, &dma_mem, BUS_DMA_NOWAIT,
+ &sc->dma_map);
+ if (err != 0) {
+ DEVCFG_SC_UNLOCK(sc);
+ return (err);
+ }
+ err = bus_dmamap_load(sc->dma_tag, sc->dma_map, dma_mem, PAGE_SIZE,
+ zy7_dma_cb2, &dma_physaddr, 0);
+ if (err != 0) {
+ bus_dmamem_free(sc->dma_tag, dma_mem, sc->dma_map);
+ DEVCFG_SC_UNLOCK(sc);
+ return (err);
+ }
+
+ while (uio->uio_resid > 0) {
+ /* If DONE signal has been set, we shouldn't write anymore. */
+ if ((RD4(sc, ZY7_DEVCFG_INT_STATUS) &
+ ZY7_DEVCFG_INT_PCFG_DONE) != 0) {
+ err = EIO;
+ break;
+ }
+
+ /* uiomove the data from user buffer to our dma map. */
+ segsz = MIN(PAGE_SIZE, uio->uio_resid);
+ DEVCFG_SC_UNLOCK(sc);
+ err = uiomove(dma_mem, segsz, uio);
+ DEVCFG_SC_LOCK(sc);
+ if (err != 0)
+ break;
+
+ /* Flush the cache to memory. */
+ bus_dmamap_sync(sc->dma_tag, sc->dma_map,
+ BUS_DMASYNC_PREWRITE);
+
+ /* Program devcfg's DMA engine. The ordering of these
+ * register writes is critical.
+ */
+ if (uio->uio_resid > segsz)
+ WR4(sc, ZY7_DEVCFG_DMA_SRC_ADDR,
+ (uint32_t) dma_physaddr);
+ else
+ WR4(sc, ZY7_DEVCFG_DMA_SRC_ADDR,
+ (uint32_t) dma_physaddr |
+ ZY7_DEVCFG_DMA_ADDR_WAIT_PCAP);
+ WR4(sc, ZY7_DEVCFG_DMA_DST_ADDR, ZY7_DEVCFG_DMA_ADDR_ILLEGAL);
+ WR4(sc, ZY7_DEVCFG_DMA_SRC_LEN, (segsz+3)/4);
+ WR4(sc, ZY7_DEVCFG_DMA_DST_LEN, 0);
+
+ /* Now clear done bit and set up DMA done interrupt. */
+ WR4(sc, ZY7_DEVCFG_INT_STATUS, ZY7_DEVCFG_INT_ALL);
+ WR4(sc, ZY7_DEVCFG_INT_MASK, ~ZY7_DEVCFG_INT_DMA_DONE);
+
+ /* Wait for DMA done interrupt. */
+ err = mtx_sleep(sc->dma_map, &sc->sc_mtx, PCATCH,
+ "zy7dma", hz);
+ if (err != 0)
+ break;
+
+ bus_dmamap_sync(sc->dma_tag, sc->dma_map,
+ BUS_DMASYNC_POSTWRITE);
+
+ /* Check DONE signal. */
+ if ((RD4(sc, ZY7_DEVCFG_INT_STATUS) &
+ ZY7_DEVCFG_INT_PCFG_DONE) != 0)
+ zy7_slcr_postload_pl(zy7_en_level_shifters);
+ }
+
+ bus_dmamap_unload(sc->dma_tag, sc->dma_map);
+ bus_dmamem_free(sc->dma_tag, dma_mem, sc->dma_map);
+ DEVCFG_SC_UNLOCK(sc);
+ return (err);
+}
+
+static int
+zy7_devcfg_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
+{
+ struct zy7_devcfg_softc *sc = dev->si_drv1;
+
+ DEVCFG_SC_LOCK(sc);
+ sc->is_open = 0;
+ bus_dma_tag_destroy(sc->dma_tag);
+ DEVCFG_SC_UNLOCK(sc);
+
+ zy7_slcr_postload_pl(zy7_en_level_shifters);
+
+ return (0);
+}
+
+static void
+zy7_devcfg_intr(void *arg)
+{
+ struct zy7_devcfg_softc *sc = (struct zy7_devcfg_softc *)arg;
+ uint32_t istatus, imask;
+
+ DEVCFG_SC_LOCK(sc);
+
+ istatus = RD4(sc, ZY7_DEVCFG_INT_STATUS);
+ imask = ~RD4(sc, ZY7_DEVCFG_INT_MASK);
+
+ /* Turn interrupt off. */
+ WR4(sc, ZY7_DEVCFG_INT_MASK, ~0);
+
+ if ((istatus & imask) == 0) {
+ DEVCFG_SC_UNLOCK(sc);
+ return;
+ }
+
+ /* DMA done? */
+ if ((istatus & ZY7_DEVCFG_INT_DMA_DONE) != 0)
+ wakeup(sc->dma_map);
+
+ /* INIT_B positive edge? */
+ if ((istatus & ZY7_DEVCFG_INT_PCFG_INIT_PE) != 0)
+ wakeup(sc);
+
+ DEVCFG_SC_UNLOCK(sc);
+}
+
+/* zy7_devcfg_sysctl_pl_done() returns status of the PL_DONE signal.
+ */
+static int
+zy7_devcfg_sysctl_pl_done(SYSCTL_HANDLER_ARGS)
+{
+ struct zy7_devcfg_softc *sc = zy7_devcfg_softc_p;
+ int pl_done = 0;
+
+ if (sc) {
+ DEVCFG_SC_LOCK(sc);
+
+ /* PCFG_DONE bit is sticky. Clear it before checking it. */
+ WR4(sc, ZY7_DEVCFG_INT_STATUS, ZY7_DEVCFG_INT_PCFG_DONE);
+ pl_done = ((RD4(sc, ZY7_DEVCFG_INT_STATUS) &
+ ZY7_DEVCFG_INT_PCFG_DONE) != 0);
+
+ DEVCFG_SC_UNLOCK(sc);
+ }
+ return (sysctl_handle_int(oidp, &pl_done, 0, req));
+}
+
+static int
+zy7_devcfg_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "xlnx,zy7_devcfg"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Zynq devcfg block");
+ return (0);
+}
+
+static int zy7_devcfg_detach(device_t dev);
+
+static int
+zy7_devcfg_attach(device_t dev)
+{
+ struct zy7_devcfg_softc *sc = device_get_softc(dev);
+ int i;
+ int rid, err;
+
+ /* Allow only one attach. */
+ if (zy7_devcfg_softc_p != NULL)
+ return (ENXIO);
+
+ sc->dev = dev;
+
+ DEVCFG_SC_LOCK_INIT(sc);
+
+ /* Get memory resource. */
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "could not allocate memory resources.\n");
+ zy7_devcfg_detach(dev);
+ return (ENOMEM);
+ }
+
+ /* Allocate IRQ. */
+ rid = 0;
+ sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (sc->irq_res == NULL) {
+ device_printf(dev, "cannot allocate IRQ\n");
+ zy7_devcfg_detach(dev);
+ return (ENOMEM);
+ }
+
+ /* Activate the interrupt. */
+ err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+ NULL, zy7_devcfg_intr, sc, &sc->intrhandle);
+ if (err) {
+ device_printf(dev, "cannot setup IRQ\n");
+ zy7_devcfg_detach(dev);
+ return (err);
+ }
+
+ /* Create /dev/devcfg */
+ sc->sc_ctl_dev = make_dev(&zy7_devcfg_cdevsw, 0,
+ UID_ROOT, GID_WHEEL, 0600, "devcfg");
+ if (sc->sc_ctl_dev == NULL) {
+ device_printf(dev, "failed to create /dev/devcfg");
+ zy7_devcfg_detach(dev);
+ return (ENXIO);
+ }
+ sc->sc_ctl_dev->si_drv1 = sc;
+
+ zy7_devcfg_softc_p = sc;
+
+ /* Unlock devcfg registers. */
+ WR4(sc, ZY7_DEVCFG_UNLOCK, ZY7_DEVCFG_UNLOCK_MAGIC);
+
+ /* Make sure interrupts are completely disabled. */
+ WR4(sc, ZY7_DEVCFG_INT_STATUS, ZY7_DEVCFG_INT_ALL);
+ WR4(sc, ZY7_DEVCFG_INT_MASK, 0xffffffff);
+
+ /* Get PS_VERS for SYSCTL. */
+ zy7_ps_vers = (RD4(sc, ZY7_DEVCFG_MCTRL) &
+ ZY7_DEVCFG_MCTRL_PS_VERS_MASK) >>
+ ZY7_DEVCFG_MCTRL_PS_VERS_SHIFT;
+
+ for (i = 0; i < FCLK_NUM; i++) {
+ fclk_configs[i].source = zy7_pl_fclk_get_source(i);
+ fclk_configs[i].actual_frequency =
+ zy7_pl_fclk_enabled(i) ? zy7_pl_fclk_get_freq(i) : 0;
+ /* Initially assume actual frequency is the configure one */
+ fclk_configs[i].frequency = fclk_configs[i].actual_frequency;
+ }
+
+ if (zy7_devcfg_init_fclk_sysctl(sc) < 0)
+ device_printf(dev, "failed to initialized sysctl tree\n");
+
+ return (0);
+}
+
+static int
+zy7_devcfg_detach(device_t dev)
+{
+ struct zy7_devcfg_softc *sc = device_get_softc(dev);
+
+ if (sc->sysctl_tree_top != NULL) {
+ sysctl_ctx_free(&sc->sysctl_tree);
+ sc->sysctl_tree_top = NULL;
+ }
+
+ if (device_is_attached(dev))
+ bus_generic_detach(dev);
+
+ /* Get rid of /dev/devcfg0. */
+ if (sc->sc_ctl_dev != NULL)
+ destroy_dev(sc->sc_ctl_dev);
+
+ /* Teardown and release interrupt. */
+ if (sc->irq_res != NULL) {
+ if (sc->intrhandle)
+ bus_teardown_intr(dev, sc->irq_res, sc->intrhandle);
+ bus_release_resource(dev, SYS_RES_IRQ,
+ rman_get_rid(sc->irq_res), sc->irq_res);
+ }
+
+ /* Release memory resource. */
+ if (sc->mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY,
+ rman_get_rid(sc->mem_res), sc->mem_res);
+
+ zy7_devcfg_softc_p = NULL;
+
+ DEVCFG_SC_LOCK_DESTROY(sc);
+
+ return (0);
+}
+
+static device_method_t zy7_devcfg_methods[] = {
+ /* device_if */
+ DEVMETHOD(device_probe, zy7_devcfg_probe),
+ DEVMETHOD(device_attach, zy7_devcfg_attach),
+ DEVMETHOD(device_detach, zy7_devcfg_detach),
+
+ DEVMETHOD_END
+};
+
+static driver_t zy7_devcfg_driver = {
+ "zy7_devcfg",
+ zy7_devcfg_methods,
+ sizeof(struct zy7_devcfg_softc),
+};
+static devclass_t zy7_devcfg_devclass;
+
+DRIVER_MODULE(zy7_devcfg, simplebus, zy7_devcfg_driver, zy7_devcfg_devclass, \
+ 0, 0);
+MODULE_DEPEND(zy7_devcfg, zy7_slcr, 1, 1, 1);
diff --git a/sys/arm/xilinx/zy7_ehci.c b/sys/arm/xilinx/zy7_ehci.c
new file mode 100644
index 000000000000..28e4705599c7
--- /dev/null
+++ b/sys/arm/xilinx/zy7_ehci.c
@@ -0,0 +1,371 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012-2013 Thomas Skibo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * A host-controller driver for Zynq-7000's USB OTG controller.
+ *
+ * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual.
+ * (v1.4) November 16, 2012. Xilinx doc UG585. Ch. 15 covers the USB
+ * controller and register definitions are in appendix B.34.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/stdarg.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/controller/ehci.h>
+#include <dev/usb/controller/ehcireg.h>
+
+/* Register definitions. */
+#define ZY7_USB_ID 0x0000
+#define ZY7_USB_HWGENERAL 0x0004
+#define ZY7_USB_HWHOST 0x0008
+#define ZY7_USB_HWDEVICE 0x000c
+#define ZY7_USB_HWTXBUF 0x0010
+#define ZY7_USB_HWRXBUF 0x0014
+#define ZY7_USB_GPTIMER0LD 0x0080
+#define ZY7_USB_GPTIMER0CTRL 0x0084
+#define ZY7_USB_GPTIMER1LD 0x0088
+#define ZY7_USB_GPTIMER1CTRL 0x008c
+#define ZY7_USB_SBUSCFG 0x0090
+#define ZY7_USB_CAPLENGTH_HCIVERSION 0x0100
+#define ZY7_USB_HCSPARAMS 0x0104
+#define ZY7_USB_HCCPARAMS 0x0108
+#define ZY7_USB_DCIVERSION 0x0120
+#define ZY7_USB_DCCPARAMS 0x0124
+#define ZY7_USB_USBCMD 0x0140
+#define ZY7_USB_USBSTS 0x0144
+#define ZY7_USB_USBINTR 0x0148
+#define ZY7_USB_FRINDEX 0x014c
+#define ZY7_USB_PERIODICLISTBASE_DEICEADDR 0x0154
+#define ZY7_USB_ASYNCLISTADDR_ENDPOINTLISTADDR 0x0158
+#define ZY7_USB_TTCTRL 0x015c
+#define ZY7_USB_BURSTSIZE 0x0160
+#define ZY7_USB_TXFILLTUNING 0x0164
+#define ZY7_USB_TXFILLTUNING_TXFIFOTHRES_SHFT 16
+#define ZY7_USB_TXFILLTUNING_TXFIFOTHRES_MASK (0x3f<<16)
+#define ZY7_USB_TXTFILLTUNING 0x0168
+#define ZY7_USB_IC_USB 0x016c
+#define ZY7_USB_ULPI_VIEWPORT 0x0170
+#define ZY7_USB_ULPI_VIEWPORT_WU (1<<31)
+#define ZY7_USB_ULPI_VIEWPORT_RUN (1<<30)
+#define ZY7_USB_ULPI_VIEWPORT_RW (1<<29)
+#define ZY7_USB_ULPI_VIEWPORT_SS (1<<27)
+#define ZY7_USB_ULPI_VIEWPORT_PORT_MASK (7<<24)
+#define ZY7_USB_ULPI_VIEWPORT_PORT_SHIFT 24
+#define ZY7_USB_ULPI_VIEWPORT_ADDR_MASK (0xff<<16)
+#define ZY7_USB_ULPI_VIEWPORT_ADDR_SHIFT 16
+#define ZY7_USB_ULPI_VIEWPORT_DATARD_MASK (0xff<<8)
+#define ZY7_USB_ULPI_VIEWPORT_DATARD_SHIFT 8
+#define ZY7_USB_ULPI_VIEWPORT_DATAWR_MASK (0xff<<0)
+#define ZY7_USB_ULPI_VIEWPORT_DATAWR_SHIFT 0
+#define ZY7_USB_ENDPTNAK 0x0178
+#define ZY7_USB_ENDPTNAKEN 0x017c
+#define ZY7_USB_CONFIGFLAG 0x0180
+#define ZY7_USB_PORTSC(n) (0x0180+4*(n))
+#define ZY7_USB_PORTSC_PTS_MASK (3<<30)
+#define ZY7_USB_PORTSC_PTS_SHIFT 30
+#define ZY7_USB_PORTSC_PTS_UTMI (0<<30)
+#define ZY7_USB_PORTSC_PTS_ULPI (2<<30)
+#define ZY7_USB_PORTSC_PTS_SERIAL (3<<30)
+#define ZY7_USB_PORTSC_PTW (1<<28)
+#define ZY7_USB_PORTSC_PTS2 (1<<25)
+#define ZY7_USB_OTGSC 0x01a4
+#define ZY7_USB_USBMODE 0x01a8
+#define ZY7_USB_ENDPTSETUPSTAT 0x01ac
+#define ZY7_USB_ENDPTPRIME 0x01b0
+#define ZY7_USB_ENDPTFLUSH 0x01b4
+#define ZY7_USB_ENDPTSTAT 0x01b8
+#define ZY7_USB_ENDPTCOMPLETE 0x01bc
+#define ZY7_USB_ENDPTCTRL(n) (0x01c0+4*(n))
+
+#define EHCI_REG_OFFSET ZY7_USB_CAPLENGTH_HCIVERSION
+#define EHCI_REG_SIZE 0x100
+
+static void
+zy7_ehci_post_reset(struct ehci_softc *ehci_softc)
+{
+ uint32_t usbmode;
+
+ /* Force HOST mode */
+ usbmode = EOREAD4(ehci_softc, EHCI_USBMODE_NOLPM);
+ usbmode &= ~EHCI_UM_CM;
+ usbmode |= EHCI_UM_CM_HOST;
+ EOWRITE4(ehci_softc, EHCI_USBMODE_NOLPM, usbmode);
+}
+
+static int
+zy7_phy_config(device_t dev, bus_space_tag_t io_tag, bus_space_handle_t bsh)
+{
+ phandle_t node;
+ char buf[64];
+ uint32_t portsc;
+ int tries;
+
+ node = ofw_bus_get_node(dev);
+
+ if (OF_getprop(node, "phy_type", buf, sizeof(buf)) > 0) {
+ portsc = bus_space_read_4(io_tag, bsh, ZY7_USB_PORTSC(1));
+ portsc &= ~(ZY7_USB_PORTSC_PTS_MASK | ZY7_USB_PORTSC_PTW |
+ ZY7_USB_PORTSC_PTS2);
+
+ if (strcmp(buf,"ulpi") == 0)
+ portsc |= ZY7_USB_PORTSC_PTS_ULPI;
+ else if (strcmp(buf,"utmi") == 0)
+ portsc |= ZY7_USB_PORTSC_PTS_UTMI;
+ else if (strcmp(buf,"utmi-wide") == 0)
+ portsc |= (ZY7_USB_PORTSC_PTS_UTMI |
+ ZY7_USB_PORTSC_PTW);
+ else if (strcmp(buf, "serial") == 0)
+ portsc |= ZY7_USB_PORTSC_PTS_SERIAL;
+
+ bus_space_write_4(io_tag, bsh, ZY7_USB_PORTSC(1), portsc);
+ }
+
+ if (OF_getprop(node, "phy_vbus_ext", buf, sizeof(buf)) >= 0) {
+ /* Tell PHY that VBUS is supplied externally. */
+ bus_space_write_4(io_tag, bsh, ZY7_USB_ULPI_VIEWPORT,
+ ZY7_USB_ULPI_VIEWPORT_RUN |
+ ZY7_USB_ULPI_VIEWPORT_RW |
+ (0 << ZY7_USB_ULPI_VIEWPORT_PORT_SHIFT) |
+ (0x0b << ZY7_USB_ULPI_VIEWPORT_ADDR_SHIFT) |
+ (0x60 << ZY7_USB_ULPI_VIEWPORT_DATAWR_SHIFT)
+ );
+
+ tries = 100;
+ while ((bus_space_read_4(io_tag, bsh, ZY7_USB_ULPI_VIEWPORT) &
+ ZY7_USB_ULPI_VIEWPORT_RUN) != 0) {
+ if (--tries < 0)
+ return (-1);
+ DELAY(1);
+ }
+ }
+
+ return (0);
+}
+
+static int
+zy7_ehci_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "xlnx,zy7_ehci"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Zynq-7000 EHCI USB 2.0 controller");
+ return (0);
+}
+
+static int zy7_ehci_detach(device_t dev);
+
+static int
+zy7_ehci_attach(device_t dev)
+{
+ ehci_softc_t *sc = device_get_softc(dev);
+ bus_space_handle_t bsh;
+ int err, rid;
+
+ /* initialize some bus fields */
+ sc->sc_bus.parent = dev;
+ sc->sc_bus.devices = sc->sc_devices;
+ sc->sc_bus.devices_max = EHCI_MAX_DEVICES;
+ sc->sc_bus.dma_bits = 32;
+
+ /* get all DMA memory */
+ if (usb_bus_mem_alloc_all(&sc->sc_bus,
+ USB_GET_DMA_TAG(dev), &ehci_iterate_hw_softc))
+ return (ENOMEM);
+
+ /* Allocate memory. */
+ rid = 0;
+ sc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &rid, RF_ACTIVE);
+ if (sc->sc_io_res == NULL) {
+ device_printf(dev, "Can't allocate memory");
+ zy7_ehci_detach(dev);
+ return (ENOMEM);
+ }
+
+ sc->sc_io_tag = rman_get_bustag(sc->sc_io_res);
+ bsh = rman_get_bushandle(sc->sc_io_res);
+ sc->sc_io_size = EHCI_REG_SIZE;
+
+ if (bus_space_subregion(sc->sc_io_tag, bsh, EHCI_REG_OFFSET,
+ sc->sc_io_size, &sc->sc_io_hdl) != 0)
+ panic("%s: unable to subregion USB host registers",
+ device_get_name(dev));
+
+ /* Allocate IRQ. */
+ rid = 0;
+ sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (sc->sc_irq_res == NULL) {
+ device_printf(dev, "Can't allocate IRQ\n");
+ zy7_ehci_detach(dev);
+ return (ENOMEM);
+ }
+
+ /* Add USB device */
+ sc->sc_bus.bdev = device_add_child(dev, "usbus", -1);
+ if (!sc->sc_bus.bdev) {
+ device_printf(dev, "Could not add USB device\n");
+ zy7_ehci_detach(dev);
+ return (ENXIO);
+ }
+ device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
+ device_set_desc(sc->sc_bus.bdev, "Zynq-7000 ehci USB 2.0 controller");
+
+ strcpy(sc->sc_vendor, "Xilinx"); /* or IP vendor? */
+
+ /* Activate the interrupt */
+ err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ NULL, (driver_intr_t *)ehci_interrupt, sc,
+ &sc->sc_intr_hdl);
+ if (err) {
+ device_printf(dev, "Cannot setup IRQ\n");
+ zy7_ehci_detach(dev);
+ return (err);
+ }
+
+ /* Customization. */
+ sc->sc_flags |= EHCI_SCFLG_TT | EHCI_SCFLG_NORESTERM;
+ sc->sc_vendor_post_reset = zy7_ehci_post_reset;
+ sc->sc_vendor_get_port_speed = ehci_get_port_speed_portsc;
+
+ /* Modify FIFO burst threshold from 2 to 8. */
+ bus_space_write_4(sc->sc_io_tag, bsh,
+ ZY7_USB_TXFILLTUNING,
+ 8 << ZY7_USB_TXFILLTUNING_TXFIFOTHRES_SHFT);
+
+ /* Handle PHY options. */
+ if (zy7_phy_config(dev, sc->sc_io_tag, bsh) < 0) {
+ device_printf(dev, "Cannot config phy!\n");
+ zy7_ehci_detach(dev);
+ return (EIO);
+ }
+
+ /* Init ehci. */
+ err = ehci_init(sc);
+ if (!err) {
+ sc->sc_flags |= EHCI_SCFLG_DONEINIT;
+ err = device_probe_and_attach(sc->sc_bus.bdev);
+ }
+ if (err) {
+ device_printf(dev, "USB init failed err=%d\n", err);
+ zy7_ehci_detach(dev);
+ return (err);
+ }
+
+ return (0);
+}
+
+static int
+zy7_ehci_detach(device_t dev)
+{
+ ehci_softc_t *sc = device_get_softc(dev);
+
+ /* during module unload there are lots of children leftover */
+ device_delete_children(dev);
+
+ if ((sc->sc_flags & EHCI_SCFLG_DONEINIT) != 0) {
+ ehci_detach(sc);
+ sc->sc_flags &= ~EHCI_SCFLG_DONEINIT;
+ }
+
+ if (sc->sc_irq_res) {
+ if (sc->sc_intr_hdl != NULL)
+ bus_teardown_intr(dev, sc->sc_irq_res,
+ sc->sc_intr_hdl);
+ bus_release_resource(dev, SYS_RES_IRQ,
+ rman_get_rid(sc->sc_irq_res), sc->sc_irq_res);
+ }
+
+ if (sc->sc_io_res)
+ bus_release_resource(dev, SYS_RES_MEMORY,
+ rman_get_rid(sc->sc_io_res), sc->sc_io_res);
+ usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc);
+
+ return (0);
+}
+
+static device_method_t ehci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, zy7_ehci_probe),
+ DEVMETHOD(device_attach, zy7_ehci_attach),
+ DEVMETHOD(device_detach, zy7_ehci_detach),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+
+ DEVMETHOD_END
+};
+
+static driver_t ehci_driver = {
+ "ehci",
+ ehci_methods,
+ sizeof(struct ehci_softc),
+};
+static devclass_t ehci_devclass;
+
+DRIVER_MODULE(zy7_ehci, simplebus, ehci_driver, ehci_devclass, NULL, NULL);
+MODULE_DEPEND(zy7_ehci, usb, 1, 1, 1);
diff --git a/sys/arm/xilinx/zy7_gpio.c b/sys/arm/xilinx/zy7_gpio.c
new file mode 100644
index 000000000000..3e31805f19fe
--- /dev/null
+++ b/sys/arm/xilinx/zy7_gpio.c
@@ -0,0 +1,377 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Thomas Skibo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * A GPIO driver for Xilinx Zynq-7000.
+ *
+ * The GPIO peripheral on Zynq allows controlling 114 general purpose I/Os.
+ *
+ * Pins 53-0 are sent to the MIO. Any MIO pins not used by a PS peripheral are
+ * available as a GPIO pin. Pins 64-127 are sent to the PL (FPGA) section of
+ * Zynq as EMIO signals.
+ *
+ * The hardware provides a way to use IOs as interrupt sources but the
+ * gpio framework doesn't seem to have hooks for this.
+ *
+ * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual.
+ * (v1.4) November 16, 2012. Xilinx doc UG585. GPIO is covered in
+ * chater 14. Register definitions are in appendix B.19.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/gpio.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/stdarg.h>
+
+#include <dev/gpio/gpiobusvar.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "gpio_if.h"
+
+#define NUMBANKS 4
+#define MAXPIN (32*NUMBANKS)
+
+#define MIO_PIN 0 /* pins 0-53 go to MIO */
+#define NUM_MIO_PINS 54
+#define EMIO_PIN 64 /* pins 64-127 go to PL */
+#define NUM_EMIO_PINS 64
+
+#define VALID_PIN(u) (((u) >= MIO_PIN && (u) < MIO_PIN + NUM_MIO_PINS) || \
+ ((u) >= EMIO_PIN && (u) < EMIO_PIN + NUM_EMIO_PINS))
+
+#define ZGPIO_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
+#define ZGPIO_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
+#define ZGPIO_LOCK_INIT(sc) \
+ mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->dev), \
+ "gpio", MTX_DEF)
+#define ZGPIO_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx);
+
+struct zy7_gpio_softc {
+ device_t dev;
+ device_t busdev;
+ struct mtx sc_mtx;
+ struct resource *mem_res; /* Memory resource */
+};
+
+#define WR4(sc, off, val) bus_write_4((sc)->mem_res, (off), (val))
+#define RD4(sc, off) bus_read_4((sc)->mem_res, (off))
+
+/* Xilinx Zynq-7000 GPIO register definitions:
+ */
+#define ZY7_GPIO_MASK_DATA_LSW(b) (0x0000+8*(b)) /* maskable wr lo */
+#define ZY7_GPIO_MASK_DATA_MSW(b) (0x0004+8*(b)) /* maskable wr hi */
+#define ZY7_GPIO_DATA(b) (0x0040+4*(b)) /* in/out data */
+#define ZY7_GPIO_DATA_RO(b) (0x0060+4*(b)) /* input data */
+
+#define ZY7_GPIO_DIRM(b) (0x0204+0x40*(b)) /* direction mode */
+#define ZY7_GPIO_OEN(b) (0x0208+0x40*(b)) /* output enable */
+#define ZY7_GPIO_INT_MASK(b) (0x020c+0x40*(b)) /* int mask */
+#define ZY7_GPIO_INT_EN(b) (0x0210+0x40*(b)) /* int enable */
+#define ZY7_GPIO_INT_DIS(b) (0x0214+0x40*(b)) /* int disable */
+#define ZY7_GPIO_INT_STAT(b) (0x0218+0x40*(b)) /* int status */
+#define ZY7_GPIO_INT_TYPE(b) (0x021c+0x40*(b)) /* int type */
+#define ZY7_GPIO_INT_POLARITY(b) (0x0220+0x40*(b)) /* int polarity */
+#define ZY7_GPIO_INT_ANY(b) (0x0224+0x40*(b)) /* any edge */
+
+static device_t
+zy7_gpio_get_bus(device_t dev)
+{
+ struct zy7_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ return (sc->busdev);
+}
+
+static int
+zy7_gpio_pin_max(device_t dev, int *maxpin)
+{
+
+ *maxpin = MAXPIN;
+ return (0);
+}
+
+/* Get a specific pin's capabilities. */
+static int
+zy7_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
+{
+
+ if (!VALID_PIN(pin))
+ return (EINVAL);
+
+ *caps = (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_TRISTATE);
+
+ return (0);
+}
+
+/* Get a specific pin's name. */
+static int
+zy7_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
+{
+
+ if (!VALID_PIN(pin))
+ return (EINVAL);
+
+ if (pin < NUM_MIO_PINS) {
+ snprintf(name, GPIOMAXNAME, "MIO_%d", pin);
+ name[GPIOMAXNAME - 1] = '\0';
+ } else {
+ snprintf(name, GPIOMAXNAME, "EMIO_%d", pin - EMIO_PIN);
+ name[GPIOMAXNAME - 1] = '\0';
+ }
+
+ return (0);
+}
+
+/* Get a specific pin's current in/out/tri state. */
+static int
+zy7_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
+{
+ struct zy7_gpio_softc *sc = device_get_softc(dev);
+
+ if (!VALID_PIN(pin))
+ return (EINVAL);
+
+ ZGPIO_LOCK(sc);
+
+ if ((RD4(sc, ZY7_GPIO_DIRM(pin >> 5)) & (1 << (pin & 31))) != 0) {
+ /* output */
+ if ((RD4(sc, ZY7_GPIO_OEN(pin >> 5)) & (1 << (pin & 31))) == 0)
+ *flags = (GPIO_PIN_OUTPUT | GPIO_PIN_TRISTATE);
+ else
+ *flags = GPIO_PIN_OUTPUT;
+ } else
+ /* input */
+ *flags = GPIO_PIN_INPUT;
+
+ ZGPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+/* Set a specific pin's in/out/tri state. */
+static int
+zy7_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
+{
+ struct zy7_gpio_softc *sc = device_get_softc(dev);
+
+ if (!VALID_PIN(pin))
+ return (EINVAL);
+
+ ZGPIO_LOCK(sc);
+
+ if ((flags & GPIO_PIN_OUTPUT) != 0) {
+ /* Output. Set or reset OEN too. */
+ WR4(sc, ZY7_GPIO_DIRM(pin >> 5),
+ RD4(sc, ZY7_GPIO_DIRM(pin >> 5)) | (1 << (pin & 31)));
+
+ if ((flags & GPIO_PIN_TRISTATE) != 0)
+ WR4(sc, ZY7_GPIO_OEN(pin >> 5),
+ RD4(sc, ZY7_GPIO_OEN(pin >> 5)) &
+ ~(1 << (pin & 31)));
+ else
+ WR4(sc, ZY7_GPIO_OEN(pin >> 5),
+ RD4(sc, ZY7_GPIO_OEN(pin >> 5)) |
+ (1 << (pin & 31)));
+ } else {
+ /* Input. Turn off OEN. */
+ WR4(sc, ZY7_GPIO_DIRM(pin >> 5),
+ RD4(sc, ZY7_GPIO_DIRM(pin >> 5)) & ~(1 << (pin & 31)));
+ WR4(sc, ZY7_GPIO_OEN(pin >> 5),
+ RD4(sc, ZY7_GPIO_OEN(pin >> 5)) & ~(1 << (pin & 31)));
+ }
+
+ ZGPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+/* Set a specific output pin's value. */
+static int
+zy7_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
+{
+ struct zy7_gpio_softc *sc = device_get_softc(dev);
+
+ if (!VALID_PIN(pin) || value > 1)
+ return (EINVAL);
+
+ /* Fancy register tricks allow atomic set or reset. */
+ if ((pin & 16) != 0)
+ WR4(sc, ZY7_GPIO_MASK_DATA_MSW(pin >> 5),
+ (0xffff0000 ^ (0x10000 << (pin & 15))) |
+ (value << (pin & 15)));
+ else
+ WR4(sc, ZY7_GPIO_MASK_DATA_LSW(pin >> 5),
+ (0xffff0000 ^ (0x10000 << (pin & 15))) |
+ (value << (pin & 15)));
+
+ return (0);
+}
+
+/* Get a specific pin's input value. */
+static int
+zy7_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *value)
+{
+ struct zy7_gpio_softc *sc = device_get_softc(dev);
+
+ if (!VALID_PIN(pin))
+ return (EINVAL);
+
+ *value = (RD4(sc, ZY7_GPIO_DATA_RO(pin >> 5)) >> (pin & 31)) & 1;
+
+ return (0);
+}
+
+/* Toggle a pin's output value. */
+static int
+zy7_gpio_pin_toggle(device_t dev, uint32_t pin)
+{
+ struct zy7_gpio_softc *sc = device_get_softc(dev);
+
+ if (!VALID_PIN(pin))
+ return (EINVAL);
+
+ ZGPIO_LOCK(sc);
+
+ WR4(sc, ZY7_GPIO_DATA(pin >> 5),
+ RD4(sc, ZY7_GPIO_DATA(pin >> 5)) ^ (1 << (pin & 31)));
+
+ ZGPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+zy7_gpio_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "xlnx,zy7_gpio"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Zynq-7000 GPIO driver");
+ return (0);
+}
+
+static int zy7_gpio_detach(device_t dev);
+
+static int
+zy7_gpio_attach(device_t dev)
+{
+ struct zy7_gpio_softc *sc = device_get_softc(dev);
+ int rid;
+
+ sc->dev = dev;
+
+ ZGPIO_LOCK_INIT(sc);
+
+ /* Allocate memory. */
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev,
+ SYS_RES_MEMORY, &rid, RF_ACTIVE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "Can't allocate memory for device");
+ zy7_gpio_detach(dev);
+ return (ENOMEM);
+ }
+
+ sc->busdev = gpiobus_attach_bus(dev);
+ if (sc->busdev == NULL) {
+ zy7_gpio_detach(dev);
+ return (ENOMEM);
+ }
+
+ return (0);
+}
+
+static int
+zy7_gpio_detach(device_t dev)
+{
+ struct zy7_gpio_softc *sc = device_get_softc(dev);
+
+ gpiobus_detach_bus(dev);
+
+ if (sc->mem_res != NULL) {
+ /* Release memory resource. */
+ bus_release_resource(dev, SYS_RES_MEMORY,
+ rman_get_rid(sc->mem_res), sc->mem_res);
+ }
+
+ ZGPIO_LOCK_DESTROY(sc);
+
+ return (0);
+}
+
+static device_method_t zy7_gpio_methods[] = {
+ /* device_if */
+ DEVMETHOD(device_probe, zy7_gpio_probe),
+ DEVMETHOD(device_attach, zy7_gpio_attach),
+ DEVMETHOD(device_detach, zy7_gpio_detach),
+
+ /* GPIO protocol */
+ DEVMETHOD(gpio_get_bus, zy7_gpio_get_bus),
+ DEVMETHOD(gpio_pin_max, zy7_gpio_pin_max),
+ DEVMETHOD(gpio_pin_getname, zy7_gpio_pin_getname),
+ DEVMETHOD(gpio_pin_getflags, zy7_gpio_pin_getflags),
+ DEVMETHOD(gpio_pin_getcaps, zy7_gpio_pin_getcaps),
+ DEVMETHOD(gpio_pin_setflags, zy7_gpio_pin_setflags),
+ DEVMETHOD(gpio_pin_get, zy7_gpio_pin_get),
+ DEVMETHOD(gpio_pin_set, zy7_gpio_pin_set),
+ DEVMETHOD(gpio_pin_toggle, zy7_gpio_pin_toggle),
+
+ DEVMETHOD_END
+};
+
+static driver_t zy7_gpio_driver = {
+ "gpio",
+ zy7_gpio_methods,
+ sizeof(struct zy7_gpio_softc),
+};
+static devclass_t zy7_gpio_devclass;
+
+DRIVER_MODULE(zy7_gpio, simplebus, zy7_gpio_driver, zy7_gpio_devclass, \
+ NULL, NULL);
diff --git a/sys/arm/xilinx/zy7_l2cache.c b/sys/arm/xilinx/zy7_l2cache.c
new file mode 100644
index 000000000000..4711bb8d38ec
--- /dev/null
+++ b/sys/arm/xilinx/zy7_l2cache.c
@@ -0,0 +1,53 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Thomas Skibo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <machine/bus.h>
+#include <machine/pl310.h>
+#include <machine/platformvar.h>
+
+#include <arm/xilinx/zy7_machdep.h>
+
+#include "platform_pl310_if.h"
+
+void
+zynq7_pl310_init(platform_t plat, struct pl310_softc *softc)
+{
+}
diff --git a/sys/arm/xilinx/zy7_machdep.c b/sys/arm/xilinx/zy7_machdep.c
new file mode 100644
index 000000000000..ddab208e80d1
--- /dev/null
+++ b/sys/arm/xilinx/zy7_machdep.c
@@ -0,0 +1,104 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Thomas Skibo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Machine dependent code for Xilinx Zynq-7000 Soc.
+ *
+ * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual.
+ * (v1.4) November 16, 2012. Xilinx doc UG585.
+ */
+
+#include "opt_platform.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/devmap.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/bus.h>
+#include <machine/machdep.h>
+#include <machine/platform.h>
+#include <machine/platformvar.h>
+
+#include <arm/xilinx/zy7_machdep.h>
+#include <arm/xilinx/zy7_reg.h>
+
+#include "platform_if.h"
+#include "platform_pl310_if.h"
+
+void (*zynq7_cpu_reset)(void);
+
+/*
+ * Set up static device mappings. Not strictly necessary -- simplebus will
+ * dynamically establish mappings as needed -- but doing it this way gets us
+ * nice efficient 1MB section mappings.
+ */
+static int
+zynq7_devmap_init(platform_t plat)
+{
+
+ devmap_add_entry(ZYNQ7_PSIO_HWBASE, ZYNQ7_PSIO_SIZE);
+ devmap_add_entry(ZYNQ7_PSCTL_HWBASE, ZYNQ7_PSCTL_SIZE);
+
+ return (0);
+}
+
+static void
+zynq7_do_cpu_reset(platform_t plat)
+{
+ if (zynq7_cpu_reset != NULL)
+ (*zynq7_cpu_reset)();
+
+ printf("cpu_reset: no platform cpu_reset. hanging.\n");
+ for (;;)
+ ;
+}
+
+static platform_method_t zynq7_methods[] = {
+ PLATFORMMETHOD(platform_devmap_init, zynq7_devmap_init),
+ PLATFORMMETHOD(platform_cpu_reset, zynq7_do_cpu_reset),
+
+#ifdef SMP
+ PLATFORMMETHOD(platform_mp_setmaxid, zynq7_mp_setmaxid),
+ PLATFORMMETHOD(platform_mp_start_ap, zynq7_mp_start_ap),
+#endif
+
+ PLATFORMMETHOD(platform_pl310_init, zynq7_pl310_init),
+
+ PLATFORMMETHOD_END,
+};
+
+FDT_PLATFORM_DEF(zynq7, "zynq7", 0, "xlnx,zynq-7000", 200);
diff --git a/sys/arm/xilinx/zy7_machdep.h b/sys/arm/xilinx/zy7_machdep.h
new file mode 100644
index 000000000000..e45409546d26
--- /dev/null
+++ b/sys/arm/xilinx/zy7_machdep.h
@@ -0,0 +1,38 @@
+/*-
+ * Copyright (c) 2017 Andrew Turner <andrew@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _ZY7_MACHDEP_H_
+#define _ZY7_MACHDEP_H_
+
+struct pl310_softc;
+
+void zynq7_mp_setmaxid(platform_t);
+void zynq7_mp_start_ap(platform_t);
+
+void zynq7_pl310_init(platform_t, struct pl310_softc *);
+
+#endif /* _ZY7_MACHDEP_H_ */
diff --git a/sys/arm/xilinx/zy7_mp.c b/sys/arm/xilinx/zy7_mp.c
new file mode 100644
index 000000000000..98329720a427
--- /dev/null
+++ b/sys/arm/xilinx/zy7_mp.c
@@ -0,0 +1,144 @@
+/*-
+ * Copyright (c) 2013 Thomas Skibo. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "opt_platform.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/smp.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/cpu.h>
+#include <machine/smp.h>
+#include <machine/fdt.h>
+#include <machine/intr.h>
+#include <machine/platformvar.h>
+
+#include <arm/xilinx/zy7_machdep.h>
+#include <arm/xilinx/zy7_reg.h>
+#include <arm/xilinx/zy7_slcr.h>
+
+#define ZYNQ7_CPU1_ENTRY 0xfffffff0
+
+#define SCU_CONTROL_REG 0xf8f00000
+#define SCU_CONTROL_ENABLE 1
+#define SCU_CONFIG_REG 0xf8f00004
+#define SCU_CONFIG_N_CPUS_MASK 3
+
+#define SLCR_PSS_IDCODE 0xf8000530
+
+void
+zynq7_mp_setmaxid(platform_t plat)
+{
+ bus_space_handle_t slcr_handle;
+ int device_id;
+ bus_space_handle_t scu_handle;
+
+ if (mp_ncpus != 0)
+ return;
+
+ /* Map in SLCR PSS_IDCODE register. */
+ if (bus_space_map(fdtbus_bs_tag, SLCR_PSS_IDCODE, 4, 0,
+ &slcr_handle) != 0)
+ panic("%s: Could not map SLCR IDCODE reg.\n", __func__);
+
+ device_id = bus_space_read_4(fdtbus_bs_tag, slcr_handle, 0) &
+ ZY7_SLCR_PSS_IDCODE_DEVICE_MASK;
+
+ bus_space_unmap(fdtbus_bs_tag, slcr_handle, 4);
+
+ /*
+ * Zynq XC7z0xxS single core chips indicate incorrect number of CPUs in
+ * SCU configuration register.
+ */
+ if (device_id == ZY7_SLCR_PSS_IDCODE_DEVICE_7Z007S ||
+ device_id == ZY7_SLCR_PSS_IDCODE_DEVICE_7Z012S ||
+ device_id == ZY7_SLCR_PSS_IDCODE_DEVICE_7Z014S) {
+ mp_maxid = 0;
+ mp_ncpus = 1;
+ return;
+ }
+
+ /* Map in SCU config register. */
+ if (bus_space_map(fdtbus_bs_tag, SCU_CONFIG_REG, 4, 0,
+ &scu_handle) != 0)
+ panic("zynq7_mp_setmaxid: Could not map SCU config reg.\n");
+
+ mp_maxid = bus_space_read_4(fdtbus_bs_tag, scu_handle, 0) &
+ SCU_CONFIG_N_CPUS_MASK;
+ mp_ncpus = mp_maxid + 1;
+
+ bus_space_unmap(fdtbus_bs_tag, scu_handle, 4);
+}
+
+void
+zynq7_mp_start_ap(platform_t plat)
+{
+ bus_space_handle_t scu_handle;
+ bus_space_handle_t ocm_handle;
+ uint32_t scu_ctrl;
+
+ /* Map in SCU control register. */
+ if (bus_space_map(fdtbus_bs_tag, SCU_CONTROL_REG, 4,
+ 0, &scu_handle) != 0)
+ panic("%s: Could not map SCU control reg.\n", __func__);
+
+ /* Set SCU enable bit. */
+ scu_ctrl = bus_space_read_4(fdtbus_bs_tag, scu_handle, 0);
+ scu_ctrl |= SCU_CONTROL_ENABLE;
+ bus_space_write_4(fdtbus_bs_tag, scu_handle, 0, scu_ctrl);
+
+ bus_space_unmap(fdtbus_bs_tag, scu_handle, 4);
+
+ /* Map in magic location to give entry address to CPU1. */
+ if (bus_space_map(fdtbus_bs_tag, ZYNQ7_CPU1_ENTRY, 4,
+ 0, &ocm_handle) != 0)
+ panic("%s: Could not map OCM\n", __func__);
+
+ /* Write start address for CPU1. */
+ bus_space_write_4(fdtbus_bs_tag, ocm_handle, 0,
+ pmap_kextract((vm_offset_t)mpentry));
+
+ bus_space_unmap(fdtbus_bs_tag, ocm_handle, 4);
+
+ /*
+ * The SCU is enabled above but I think the second CPU doesn't
+ * turn on filtering until after the wake-up below. I think that's why
+ * things don't work if I don't put these cache ops here. Also, the
+ * magic location, 0xfffffff0, isn't in the SCU's filtering range so it
+ * needs a write-back too.
+ */
+ dcache_wbinv_poc_all();
+
+ /* Wake up CPU1. */
+ dsb();
+ sev();
+}
diff --git a/sys/arm/xilinx/zy7_qspi.c b/sys/arm/xilinx/zy7_qspi.c
new file mode 100644
index 000000000000..e94c93db1e36
--- /dev/null
+++ b/sys/arm/xilinx/zy7_qspi.c
@@ -0,0 +1,754 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2018 Thomas Skibo <thomasskibo@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * This is a driver for the Quad-SPI Flash Controller in the Xilinx
+ * Zynq-7000 SoC.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/uio.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/stdarg.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/spibus/spi.h>
+#include <dev/spibus/spibusvar.h>
+
+#include <dev/flash/mx25lreg.h>
+
+#include "spibus_if.h"
+
+static struct ofw_compat_data compat_data[] = {
+ {"xlnx,zy7_qspi", 1},
+ {"xlnx,zynq-qspi-1.0", 1},
+ {NULL, 0}
+};
+
+struct zy7_qspi_softc {
+ device_t dev;
+ device_t child;
+ struct mtx sc_mtx;
+ struct resource *mem_res;
+ struct resource *irq_res;
+ void *intrhandle;
+
+ uint32_t cfg_reg_shadow;
+ uint32_t lqspi_cfg_shadow;
+ uint32_t spi_clock;
+ uint32_t ref_clock;
+ unsigned int spi_clk_real_freq;
+ unsigned int rx_overflows;
+ unsigned int tx_underflows;
+ unsigned int interrupts;
+ unsigned int stray_ints;
+ struct spi_command *cmd;
+ int tx_bytes; /* tx_cmd_sz + tx_data_sz */
+ int tx_bytes_sent;
+ int rx_bytes; /* rx_cmd_sz + rx_data_sz */
+ int rx_bytes_rcvd;
+ int busy;
+ int is_dual;
+ int is_stacked;
+ int is_dio;
+};
+
+#define ZY7_QSPI_DEFAULT_SPI_CLOCK 50000000
+
+#define QSPI_SC_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
+#define QSPI_SC_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
+#define QSPI_SC_LOCK_INIT(sc) \
+ mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->dev), NULL, MTX_DEF)
+#define QSPI_SC_LOCK_DESTROY(sc) mtx_destroy(&(sc)->sc_mtx)
+#define QSPI_SC_ASSERT_LOCKED(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED)
+
+#define RD4(sc, off) (bus_read_4((sc)->mem_res, (off)))
+#define WR4(sc, off, val) (bus_write_4((sc)->mem_res, (off), (val)))
+
+/*
+ * QSPI device registers.
+ * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual.
+ * (v1.12.2) July 1, 2018. Xilinx doc UG585.
+ */
+#define ZY7_QSPI_CONFIG_REG 0x0000
+#define ZY7_QSPI_CONFIG_IFMODE (1U << 31)
+#define ZY7_QSPI_CONFIG_ENDIAN (1 << 26)
+#define ZY7_QSPI_CONFIG_HOLDB_DR (1 << 19)
+#define ZY7_QSPI_CONFIG_RSVD1 (1 << 17) /* must be 1 */
+#define ZY7_QSPI_CONFIG_MANSTRT (1 << 16)
+#define ZY7_QSPI_CONFIG_MANSTRTEN (1 << 15)
+#define ZY7_QSPI_CONFIG_SSFORCE (1 << 14)
+#define ZY7_QSPI_CONFIG_PCS (1 << 10)
+#define ZY7_QSPI_CONFIG_REF_CLK (1 << 8)
+#define ZY7_QSPI_CONFIG_FIFO_WIDTH_MASK (3 << 6)
+#define ZY7_QSPI_CONFIG_FIFO_WIDTH32 (3 << 6)
+#define ZY7_QSPI_CONFIG_BAUD_RATE_DIV_MASK (7 << 3)
+#define ZY7_QSPI_CONFIG_BAUD_RATE_DIV_SHIFT 3
+#define ZY7_QSPI_CONFIG_BAUD_RATE_DIV(x) ((x) << 3) /* divide by 2<<x */
+#define ZY7_QSPI_CONFIG_CLK_PH (1 << 2) /* clock phase */
+#define ZY7_QSPI_CONFIG_CLK_POL (1 << 1) /* clock polarity */
+#define ZY7_QSPI_CONFIG_MODE_SEL (1 << 0) /* master enable */
+
+#define ZY7_QSPI_INTR_STAT_REG 0x0004
+#define ZY7_QSPI_INTR_EN_REG 0x0008
+#define ZY7_QSPI_INTR_DIS_REG 0x000c
+#define ZY7_QSPI_INTR_MASK_REG 0x0010
+#define ZY7_QSPI_INTR_TX_FIFO_UNDERFLOW (1 << 6)
+#define ZY7_QSPI_INTR_RX_FIFO_FULL (1 << 5)
+#define ZY7_QSPI_INTR_RX_FIFO_NOT_EMPTY (1 << 4)
+#define ZY7_QSPI_INTR_TX_FIFO_FULL (1 << 3)
+#define ZY7_QSPI_INTR_TX_FIFO_NOT_FULL (1 << 2)
+#define ZY7_QSPI_INTR_RX_OVERFLOW (1 << 0)
+
+#define ZY7_QSPI_EN_REG 0x0014
+#define ZY7_SPI_ENABLE 1
+
+#define ZY7_QSPI_DELAY_REG 0x0018
+#define ZY7_QSPI_DELAY_NSS_MASK (0xffU << 24)
+#define ZY7_QSPI_DELAY_NSS_SHIFT 24
+#define ZY7_QSPI_DELAY_NSS(x) ((x) << 24)
+#define ZY7_QSPI_DELAY_BTWN_MASK (0xff << 16)
+#define ZY7_QSPI_DELAY_BTWN_SHIFT 16
+#define ZY7_QSPI_DELAY_BTWN(x) ((x) << 16)
+#define ZY7_QSPI_DELAY_AFTER_MASK (0xff << 8)
+#define ZY7_QSPI_DELAY_AFTER_SHIFT 8
+#define ZY7_QSPI_DELAY_AFTER(x) ((x) << 8)
+#define ZY7_QSPI_DELAY_INIT_MASK 0xff
+#define ZY7_QSPI_DELAY_INIT_SHIFT 0
+#define ZY7_QSPI_DELAY_INIT(x) (x)
+
+#define ZY7_QSPI_TXD0_REG 0x001c
+#define ZY7_QSPI_RX_DATA_REG 0x0020
+
+#define ZY7_QSPI_SLV_IDLE_CT_REG 0x0024
+#define ZY7_QSPI_SLV_IDLE_CT_MASK 0xff
+
+#define ZY7_QSPI_TX_THRESH_REG 0x0028
+#define ZY7_QSPI_RX_THRESH_REG 0x002c
+
+#define ZY7_QSPI_GPIO_REG 0x0030
+#define ZY7_QSPI_GPIO_WP_N 1
+
+#define ZY7_QSPI_LPBK_DLY_ADJ_REG 0x0038
+#define ZY7_QSPI_LPBK_DLY_ADJ_LPBK_SEL (1 << 8)
+#define ZY7_QSPI_LPBK_DLY_ADJ_LPBK_PH (1 << 7)
+#define ZY7_QSPI_LPBK_DLY_ADJ_USE_LPBK (1 << 5)
+#define ZY7_QSPI_LPBK_DLY_ADJ_DLY1_MASK (3 << 3)
+#define ZY7_QSPI_LPBK_DLY_ADJ_DLY1_SHIFT 3
+#define ZY7_QSPI_LPBK_DLY_ADJ_DLY1(x) ((x) << 3)
+#define ZY7_QSPI_LPBK_DLY_ADJ_DLY0_MASK 7
+#define ZY7_QSPI_LPBK_DLY_ADJ_DLY0_SHIFT 0
+#define ZY7_QSPI_LPBK_DLY_ADJ_DLY0(x) (x)
+
+#define ZY7_QSPI_TXD1_REG 0x0080
+#define ZY7_QSPI_TXD2_REG 0x0084
+#define ZY7_QSPI_TXD3_REG 0x0088
+
+#define ZY7_QSPI_LQSPI_CFG_REG 0x00a0
+#define ZY7_QSPI_LQSPI_CFG_LINEAR (1U << 31)
+#define ZY7_QSPI_LQSPI_CFG_TWO_MEM (1 << 30)
+#define ZY7_QSPI_LQSPI_CFG_SEP_BUS (1 << 29)
+#define ZY7_QSPI_LQSPI_CFG_U_PAGE (1 << 28)
+#define ZY7_QSPI_LQSPI_CFG_MODE_EN (1 << 25)
+#define ZY7_QSPI_LQSPI_CFG_MODE_ON (1 << 24)
+#define ZY7_QSPI_LQSPI_CFG_MODE_BITS_MASK (0xff << 16)
+#define ZY7_QSPI_LQSPI_CFG_MODE_BITS_SHIFT 16
+#define ZY7_QSPI_LQSPI_CFG_MODE_BITS(x) ((x) << 16)
+#define ZY7_QSPI_LQSPI_CFG_DUMMY_BYTES_MASK (7 << 8)
+#define ZY7_QSPI_LQSPI_CFG_DUMMY_BYTES_SHIFT 8
+#define ZY7_QSPI_LQSPI_CFG_DUMMY_BYTES(x) ((x) << 8)
+#define ZY7_QSPI_LQSPI_CFG_INST_CODE_MASK 0xff
+#define ZY7_QSPI_LQSPI_CFG_INST_CODE_SHIFT 0
+#define ZY7_QSPI_LQSPI_CFG_INST_CODE(x) (x)
+
+#define ZY7_QSPI_LQSPI_STS_REG 0x00a4
+#define ZY7_QSPI_LQSPI_STS_D_FSM_ERR (1 << 2)
+#define ZY7_QSPI_LQSPI_STS_WR_RECVD (1 << 1)
+
+#define ZY7_QSPI_MOD_ID_REG 0x00fc
+
+static int zy7_qspi_detach(device_t);
+
+/* Fill hardware fifo with command and data bytes. */
+static void
+zy7_qspi_write_fifo(struct zy7_qspi_softc *sc, int nbytes)
+{
+ int n, nvalid;
+ uint32_t data;
+
+ while (nbytes > 0) {
+ nvalid = MIN(4, nbytes);
+ data = 0xffffffff;
+
+ /*
+ * A hardware bug forces us to wait until the tx fifo is
+ * empty before writing partial words. We'll come back
+ * next tx interrupt.
+ */
+ if (nvalid < 4 && (RD4(sc, ZY7_QSPI_INTR_STAT_REG) &
+ ZY7_QSPI_INTR_TX_FIFO_NOT_FULL) == 0)
+ return;
+
+ if (sc->tx_bytes_sent < sc->cmd->tx_cmd_sz) {
+ /* Writing command. */
+ n = MIN(nvalid, sc->cmd->tx_cmd_sz -
+ sc->tx_bytes_sent);
+ memcpy(&data, (uint8_t *)sc->cmd->tx_cmd +
+ sc->tx_bytes_sent, n);
+
+ if (nvalid > n) {
+ /* Writing start of data. */
+ memcpy((uint8_t *)&data + n,
+ sc->cmd->tx_data, nvalid - n);
+ }
+ } else
+ /* Writing data. */
+ memcpy(&data, (uint8_t *)sc->cmd->tx_data +
+ (sc->tx_bytes_sent - sc->cmd->tx_cmd_sz), nvalid);
+
+ switch (nvalid) {
+ case 1:
+ WR4(sc, ZY7_QSPI_TXD1_REG, data);
+ break;
+ case 2:
+ WR4(sc, ZY7_QSPI_TXD2_REG, data);
+ break;
+ case 3:
+ WR4(sc, ZY7_QSPI_TXD3_REG, data);
+ break;
+ case 4:
+ WR4(sc, ZY7_QSPI_TXD0_REG, data);
+ break;
+ }
+
+ sc->tx_bytes_sent += nvalid;
+ nbytes -= nvalid;
+ }
+}
+
+/* Read hardware fifo data into command response and data buffers. */
+static void
+zy7_qspi_read_fifo(struct zy7_qspi_softc *sc)
+{
+ int n, nbytes;
+ uint32_t data;
+
+ do {
+ data = RD4(sc, ZY7_QSPI_RX_DATA_REG);
+ nbytes = MIN(4, sc->rx_bytes - sc->rx_bytes_rcvd);
+
+ /*
+ * Last word in non-word-multiple transfer is packed
+ * non-intuitively.
+ */
+ if (nbytes < 4)
+ data >>= 8 * (4 - nbytes);
+
+ if (sc->rx_bytes_rcvd < sc->cmd->rx_cmd_sz) {
+ /* Reading command. */
+ n = MIN(nbytes, sc->cmd->rx_cmd_sz -
+ sc->rx_bytes_rcvd);
+ memcpy((uint8_t *)sc->cmd->rx_cmd + sc->rx_bytes_rcvd,
+ &data, n);
+ sc->rx_bytes_rcvd += n;
+ nbytes -= n;
+ data >>= 8 * n;
+ }
+
+ if (nbytes > 0) {
+ /* Reading data. */
+ memcpy((uint8_t *)sc->cmd->rx_data +
+ (sc->rx_bytes_rcvd - sc->cmd->rx_cmd_sz),
+ &data, nbytes);
+ sc->rx_bytes_rcvd += nbytes;
+ }
+
+ } while (sc->rx_bytes_rcvd < sc->rx_bytes &&
+ (RD4(sc, ZY7_QSPI_INTR_STAT_REG) &
+ ZY7_QSPI_INTR_RX_FIFO_NOT_EMPTY) != 0);
+}
+
+/* End a transfer early by draining rx fifo and disabling interrupts. */
+static void
+zy7_qspi_abort_transfer(struct zy7_qspi_softc *sc)
+{
+ /* Drain receive fifo. */
+ while ((RD4(sc, ZY7_QSPI_INTR_STAT_REG) &
+ ZY7_QSPI_INTR_RX_FIFO_NOT_EMPTY) != 0)
+ (void)RD4(sc, ZY7_QSPI_RX_DATA_REG);
+
+ /* Shut down interrupts. */
+ WR4(sc, ZY7_QSPI_INTR_DIS_REG,
+ ZY7_QSPI_INTR_RX_OVERFLOW |
+ ZY7_QSPI_INTR_RX_FIFO_NOT_EMPTY |
+ ZY7_QSPI_INTR_TX_FIFO_NOT_FULL);
+}
+
+static void
+zy7_qspi_intr(void *arg)
+{
+ struct zy7_qspi_softc *sc = (struct zy7_qspi_softc *)arg;
+ uint32_t istatus;
+
+ QSPI_SC_LOCK(sc);
+
+ sc->interrupts++;
+
+ istatus = RD4(sc, ZY7_QSPI_INTR_STAT_REG);
+
+ /* Stray interrupts can happen if a transfer gets interrupted. */
+ if (!sc->busy) {
+ sc->stray_ints++;
+ QSPI_SC_UNLOCK(sc);
+ return;
+ }
+
+ if ((istatus & ZY7_QSPI_INTR_RX_OVERFLOW) != 0) {
+ device_printf(sc->dev, "rx fifo overflow!\n");
+ sc->rx_overflows++;
+
+ /* Clear status bit. */
+ WR4(sc, ZY7_QSPI_INTR_STAT_REG,
+ ZY7_QSPI_INTR_RX_OVERFLOW);
+ }
+
+ /* Empty receive fifo before any more transmit data is sent. */
+ if (sc->rx_bytes_rcvd < sc->rx_bytes &&
+ (istatus & ZY7_QSPI_INTR_RX_FIFO_NOT_EMPTY) != 0) {
+ zy7_qspi_read_fifo(sc);
+ if (sc->rx_bytes_rcvd == sc->rx_bytes)
+ /* Disable receive interrupts. */
+ WR4(sc, ZY7_QSPI_INTR_DIS_REG,
+ ZY7_QSPI_INTR_RX_FIFO_NOT_EMPTY |
+ ZY7_QSPI_INTR_RX_OVERFLOW);
+ }
+
+ /*
+ * Transmit underflows aren't really a bug because a hardware
+ * bug forces us to allow the tx fifo to go empty between full
+ * and partial fifo writes. Why bother counting?
+ */
+ if ((istatus & ZY7_QSPI_INTR_TX_FIFO_UNDERFLOW) != 0) {
+ sc->tx_underflows++;
+
+ /* Clear status bit. */
+ WR4(sc, ZY7_QSPI_INTR_STAT_REG,
+ ZY7_QSPI_INTR_TX_FIFO_UNDERFLOW);
+ }
+
+ /* Fill transmit fifo. */
+ if (sc->tx_bytes_sent < sc->tx_bytes &&
+ (istatus & ZY7_QSPI_INTR_TX_FIFO_NOT_FULL) != 0) {
+ zy7_qspi_write_fifo(sc, MIN(240, sc->tx_bytes -
+ sc->tx_bytes_sent));
+
+ if (sc->tx_bytes_sent == sc->tx_bytes) {
+ /*
+ * Disable transmit FIFO interrupt, enable receive
+ * FIFO interrupt.
+ */
+ WR4(sc, ZY7_QSPI_INTR_DIS_REG,
+ ZY7_QSPI_INTR_TX_FIFO_NOT_FULL);
+ WR4(sc, ZY7_QSPI_INTR_EN_REG,
+ ZY7_QSPI_INTR_RX_FIFO_NOT_EMPTY);
+ }
+ }
+
+ /* Finished with transfer? */
+ if (sc->tx_bytes_sent == sc->tx_bytes &&
+ sc->rx_bytes_rcvd == sc->rx_bytes) {
+ /* De-assert CS. */
+ sc->cfg_reg_shadow |= ZY7_QSPI_CONFIG_PCS;
+ WR4(sc, ZY7_QSPI_CONFIG_REG, sc->cfg_reg_shadow);
+
+ wakeup(sc->dev);
+ }
+
+ QSPI_SC_UNLOCK(sc);
+}
+
+/* Initialize hardware. */
+static int
+zy7_qspi_init_hw(struct zy7_qspi_softc *sc)
+{
+ uint32_t baud_div;
+
+ /* Configure LQSPI Config register. Disable linear mode. */
+ sc->lqspi_cfg_shadow = RD4(sc, ZY7_QSPI_LQSPI_CFG_REG);
+ sc->lqspi_cfg_shadow &= ~(ZY7_QSPI_LQSPI_CFG_LINEAR |
+ ZY7_QSPI_LQSPI_CFG_TWO_MEM |
+ ZY7_QSPI_LQSPI_CFG_SEP_BUS);
+ if (sc->is_dual) {
+ sc->lqspi_cfg_shadow |= ZY7_QSPI_LQSPI_CFG_TWO_MEM;
+ if (sc->is_stacked) {
+ sc->lqspi_cfg_shadow &=
+ ~ZY7_QSPI_LQSPI_CFG_INST_CODE_MASK;
+ sc->lqspi_cfg_shadow |=
+ ZY7_QSPI_LQSPI_CFG_INST_CODE(sc->is_dio ?
+ CMD_READ_DUAL_IO : CMD_READ_QUAD_OUTPUT);
+ } else
+ sc->lqspi_cfg_shadow |= ZY7_QSPI_LQSPI_CFG_SEP_BUS;
+ }
+ WR4(sc, ZY7_QSPI_LQSPI_CFG_REG, sc->lqspi_cfg_shadow);
+
+ /* Find best clock divider. */
+ baud_div = 0;
+ while ((sc->ref_clock >> (baud_div + 1)) > sc->spi_clock &&
+ baud_div < 8)
+ baud_div++;
+ if (baud_div >= 8) {
+ device_printf(sc->dev, "cannot configure clock divider: ref=%d"
+ " spi=%d.\n", sc->ref_clock, sc->spi_clock);
+ return (EINVAL);
+ }
+ sc->spi_clk_real_freq = sc->ref_clock >> (baud_div + 1);
+
+ /*
+ * If divider is 2 (the max speed), use internal loopback master
+ * clock for read data. (See section 12.3.1 in ref man.)
+ */
+ if (baud_div == 0)
+ WR4(sc, ZY7_QSPI_LPBK_DLY_ADJ_REG,
+ ZY7_QSPI_LPBK_DLY_ADJ_USE_LPBK |
+ ZY7_QSPI_LPBK_DLY_ADJ_DLY1(0) |
+ ZY7_QSPI_LPBK_DLY_ADJ_DLY0(0));
+ else
+ WR4(sc, ZY7_QSPI_LPBK_DLY_ADJ_REG, 0);
+
+ /* Set up configuration register. */
+ sc->cfg_reg_shadow =
+ ZY7_QSPI_CONFIG_IFMODE |
+ ZY7_QSPI_CONFIG_HOLDB_DR |
+ ZY7_QSPI_CONFIG_RSVD1 |
+ ZY7_QSPI_CONFIG_SSFORCE |
+ ZY7_QSPI_CONFIG_PCS |
+ ZY7_QSPI_CONFIG_FIFO_WIDTH32 |
+ ZY7_QSPI_CONFIG_BAUD_RATE_DIV(baud_div) |
+ ZY7_QSPI_CONFIG_MODE_SEL;
+ WR4(sc, ZY7_QSPI_CONFIG_REG, sc->cfg_reg_shadow);
+
+ /*
+ * Set thresholds. We must use 1 for tx threshold because there
+ * is no fifo empty flag and we need one to implement a bug
+ * workaround.
+ */
+ WR4(sc, ZY7_QSPI_TX_THRESH_REG, 1);
+ WR4(sc, ZY7_QSPI_RX_THRESH_REG, 1);
+
+ /* Clear and disable all interrupts. */
+ WR4(sc, ZY7_QSPI_INTR_STAT_REG, ~0);
+ WR4(sc, ZY7_QSPI_INTR_DIS_REG, ~0);
+
+ /* Enable SPI. */
+ WR4(sc, ZY7_QSPI_EN_REG, ZY7_SPI_ENABLE);
+
+ return (0);
+}
+
+static void
+zy7_qspi_add_sysctls(device_t dev)
+{
+ struct zy7_qspi_softc *sc = device_get_softc(dev);
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid_list *child;
+
+ ctx = device_get_sysctl_ctx(dev);
+ child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
+
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "spi_clk_real_freq", CTLFLAG_RD,
+ &sc->spi_clk_real_freq, 0, "SPI clock real frequency");
+
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_overflows", CTLFLAG_RD,
+ &sc->rx_overflows, 0, "RX FIFO overflow events");
+
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_underflows", CTLFLAG_RD,
+ &sc->tx_underflows, 0, "TX FIFO underflow events");
+
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "interrupts", CTLFLAG_RD,
+ &sc->interrupts, 0, "interrupt calls");
+
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "stray_ints", CTLFLAG_RD,
+ &sc->stray_ints, 0, "stray interrupts");
+}
+
+static int
+zy7_qspi_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Zynq Quad-SPI Flash Controller");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+zy7_qspi_attach(device_t dev)
+{
+ struct zy7_qspi_softc *sc;
+ int rid, err;
+ phandle_t node;
+ pcell_t cell;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ QSPI_SC_LOCK_INIT(sc);
+
+ /* Get ref-clock, spi-clock, and other properties. */
+ node = ofw_bus_get_node(dev);
+ if (OF_getprop(node, "ref-clock", &cell, sizeof(cell)) > 0)
+ sc->ref_clock = fdt32_to_cpu(cell);
+ else {
+ device_printf(dev, "must have ref-clock property\n");
+ return (ENXIO);
+ }
+ if (OF_getprop(node, "spi-clock", &cell, sizeof(cell)) > 0)
+ sc->spi_clock = fdt32_to_cpu(cell);
+ else
+ sc->spi_clock = ZY7_QSPI_DEFAULT_SPI_CLOCK;
+ if (OF_getprop(node, "is-stacked", &cell, sizeof(cell)) > 0 &&
+ fdt32_to_cpu(cell) != 0) {
+ sc->is_dual = 1;
+ sc->is_stacked = 1;
+ } else if (OF_getprop(node, "is-dual", &cell, sizeof(cell)) > 0 &&
+ fdt32_to_cpu(cell) != 0)
+ sc->is_dual = 1;
+ if (OF_getprop(node, "is-dio", &cell, sizeof(cell)) > 0 &&
+ fdt32_to_cpu(cell) != 0)
+ sc->is_dio = 1;
+
+ /* Get memory resource. */
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "could not allocate memory resources.\n");
+ zy7_qspi_detach(dev);
+ return (ENOMEM);
+ }
+
+ /* Allocate IRQ. */
+ rid = 0;
+ sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (sc->irq_res == NULL) {
+ device_printf(dev, "could not allocate IRQ resource.\n");
+ zy7_qspi_detach(dev);
+ return (ENOMEM);
+ }
+
+ /* Activate the interrupt. */
+ err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+ NULL, zy7_qspi_intr, sc, &sc->intrhandle);
+ if (err) {
+ device_printf(dev, "could not setup IRQ.\n");
+ zy7_qspi_detach(dev);
+ return (err);
+ }
+
+ /* Configure the device. */
+ err = zy7_qspi_init_hw(sc);
+ if (err) {
+ zy7_qspi_detach(dev);
+ return (err);
+ }
+
+ sc->child = device_add_child(dev, "spibus", -1);
+
+ zy7_qspi_add_sysctls(dev);
+
+ /* Attach spibus driver as a child later when interrupts work. */
+ config_intrhook_oneshot((ich_func_t)bus_generic_attach, dev);
+
+ return (0);
+}
+
+static int
+zy7_qspi_detach(device_t dev)
+{
+ struct zy7_qspi_softc *sc = device_get_softc(dev);
+
+ if (device_is_attached(dev))
+ bus_generic_detach(dev);
+
+ /* Delete child bus. */
+ if (sc->child)
+ device_delete_child(dev, sc->child);
+
+ /* Disable hardware. */
+ if (sc->mem_res != NULL) {
+ /* Disable SPI. */
+ WR4(sc, ZY7_QSPI_EN_REG, 0);
+
+ /* Clear and disable all interrupts. */
+ WR4(sc, ZY7_QSPI_INTR_STAT_REG, ~0);
+ WR4(sc, ZY7_QSPI_INTR_DIS_REG, ~0);
+ }
+
+ /* Teardown and release interrupt. */
+ if (sc->irq_res != NULL) {
+ if (sc->intrhandle)
+ bus_teardown_intr(dev, sc->irq_res, sc->intrhandle);
+ bus_release_resource(dev, SYS_RES_IRQ,
+ rman_get_rid(sc->irq_res), sc->irq_res);
+ }
+
+ /* Release memory resource. */
+ if (sc->mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY,
+ rman_get_rid(sc->mem_res), sc->mem_res);
+
+ QSPI_SC_LOCK_DESTROY(sc);
+
+ return (0);
+}
+
+static phandle_t
+zy7_qspi_get_node(device_t bus, device_t dev)
+{
+
+ return (ofw_bus_get_node(bus));
+}
+
+static int
+zy7_qspi_transfer(device_t dev, device_t child, struct spi_command *cmd)
+{
+ struct zy7_qspi_softc *sc = device_get_softc(dev);
+ int err = 0;
+
+ KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz,
+ ("TX/RX command sizes should be equal"));
+ KASSERT(cmd->tx_data_sz == cmd->rx_data_sz,
+ ("TX/RX data sizes should be equal"));
+
+ if (sc->is_dual && cmd->tx_data_sz % 2 != 0) {
+ device_printf(dev, "driver does not support odd byte data "
+ "transfers in dual mode. (sz=%d)\n", cmd->tx_data_sz);
+ return (EINVAL);
+ }
+
+ QSPI_SC_LOCK(sc);
+
+ /* Wait for controller available. */
+ while (sc->busy != 0) {
+ err = mtx_sleep(dev, &sc->sc_mtx, 0, "zqspi0", 0);
+ if (err) {
+ QSPI_SC_UNLOCK(sc);
+ return (err);
+ }
+ }
+
+ /* Start transfer. */
+ sc->busy = 1;
+ sc->cmd = cmd;
+ sc->tx_bytes = sc->cmd->tx_cmd_sz + sc->cmd->tx_data_sz;
+ sc->tx_bytes_sent = 0;
+ sc->rx_bytes = sc->cmd->rx_cmd_sz + sc->cmd->rx_data_sz;
+ sc->rx_bytes_rcvd = 0;
+
+ /* Enable interrupts. zy7_qspi_intr() will handle transfer. */
+ WR4(sc, ZY7_QSPI_INTR_EN_REG,
+ ZY7_QSPI_INTR_TX_FIFO_NOT_FULL |
+ ZY7_QSPI_INTR_RX_OVERFLOW);
+
+#ifdef SPI_XFER_U_PAGE /* XXX: future support for stacked memories. */
+ if (sc->is_stacked) {
+ if ((cmd->flags & SPI_XFER_U_PAGE) != 0)
+ sc->lqspi_cfg_shadow |= ZY7_QSPI_LQSPI_CFG_U_PAGE;
+ else
+ sc->lqspi_cfg_shadow &= ~ZY7_QSPI_LQSPI_CFG_U_PAGE;
+ WR4(sc, ZY7_QSPI_LQSPI_CFG_REG, sc->lqspi_cfg_shadow);
+ }
+#endif
+
+ /* Assert CS. */
+ sc->cfg_reg_shadow &= ~ZY7_QSPI_CONFIG_PCS;
+ WR4(sc, ZY7_QSPI_CONFIG_REG, sc->cfg_reg_shadow);
+
+ /* Wait for completion. */
+ err = mtx_sleep(dev, &sc->sc_mtx, 0, "zqspi1", hz * 2);
+ if (err)
+ zy7_qspi_abort_transfer(sc);
+
+ /* Release controller. */
+ sc->busy = 0;
+ wakeup_one(dev);
+
+ QSPI_SC_UNLOCK(sc);
+
+ return (err);
+}
+
+static device_method_t zy7_qspi_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, zy7_qspi_probe),
+ DEVMETHOD(device_attach, zy7_qspi_attach),
+ DEVMETHOD(device_detach, zy7_qspi_detach),
+
+ /* SPI interface */
+ DEVMETHOD(spibus_transfer, zy7_qspi_transfer),
+
+ /* ofw_bus interface */
+ DEVMETHOD(ofw_bus_get_node, zy7_qspi_get_node),
+
+ DEVMETHOD_END
+};
+
+static driver_t zy7_qspi_driver = {
+ "zy7_qspi",
+ zy7_qspi_methods,
+ sizeof(struct zy7_qspi_softc),
+};
+static devclass_t zy7_qspi_devclass;
+
+DRIVER_MODULE(zy7_qspi, simplebus, zy7_qspi_driver, zy7_qspi_devclass, 0, 0);
+DRIVER_MODULE(ofw_spibus, zy7_qspi, ofw_spibus_driver, ofw_spibus_devclass, 0, 0);
+SIMPLEBUS_PNP_INFO(compat_data);
+MODULE_DEPEND(zy7_qspi, ofw_spibus, 1, 1, 1);
diff --git a/sys/arm/xilinx/zy7_reg.h b/sys/arm/xilinx/zy7_reg.h
new file mode 100644
index 000000000000..855fa0df16d1
--- /dev/null
+++ b/sys/arm/xilinx/zy7_reg.h
@@ -0,0 +1,73 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012-2013 Thomas Skibo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Address regions of Zynq-7000.
+ * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual.
+ * (v1.4) November 16, 2012. Xilinx doc UG585.
+ */
+
+#ifndef _ZY7_REG_H_
+#define _ZY7_REG_H_
+
+/* PL AXI buses: General Purpose Port #0, M_AXI_GP0. */
+#define ZYNQ7_PLGP0_HWBASE 0x40000000
+#define ZYNQ7_PLGP0_SIZE 0x40000000
+
+/* PL AXI buses: General Purpose Port #1, M_AXI_GP1. */
+#define ZYNQ7_PLGP1_HWBASE 0x80000000
+#define ZYNQ7_PLGP1_SIZE 0x40000000
+
+/* I/O Peripheral registers. */
+#define ZYNQ7_PSIO_HWBASE 0xE0000000
+#define ZYNQ7_PSIO_SIZE 0x00300000
+
+/* UART0 and UART1 */
+#define ZYNQ7_UART0_HWBASE (ZYNQ7_PSIO_HWBASE)
+#define ZYNQ7_UART0_SIZE 0x1000
+
+#define ZYNQ7_UART1_HWBASE (ZYNQ7_PSIO_HWBASE+0x1000)
+#define ZYNQ7_UART1_SIZE 0x1000
+
+/* SMC Memories not mapped for now. */
+#define ZYNQ7_SMC_HWBASE 0xE1000000
+#define ZYNQ7_SMC_SIZE 0x05000000
+
+/* SLCR, PS system, and CPU private registers combined in this region. */
+#define ZYNQ7_PSCTL_HWBASE 0xF8000000
+#define ZYNQ7_PSCTL_SIZE 0x01000000
+
+#define ZYNQ7_SLCR_HWBASE (ZYNQ7_PSCTL_HWBASE)
+#define ZYNQ7_SLCR_SIZE 0x1000
+
+#define ZYNQ7_DEVCFG_HWBASE (ZYNQ7_PSCTL_HWBASE+0x7000)
+#define ZYNQ7_DEVCFG_SIZE 0x1000
+
+#endif /* _ZY7_REG_H_ */
diff --git a/sys/arm/xilinx/zy7_slcr.c b/sys/arm/xilinx/zy7_slcr.c
new file mode 100644
index 000000000000..6651252151e2
--- /dev/null
+++ b/sys/arm/xilinx/zy7_slcr.c
@@ -0,0 +1,714 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Thomas Skibo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Zynq-700 SLCR driver. Provides hooks for cpu_reset and PL control stuff.
+ * In the future, maybe MIO control, clock control, etc. could go here.
+ *
+ * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual.
+ * (v1.4) November 16, 2012. Xilinx doc UG585.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/resource.h>
+#include <sys/sysctl.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/stdarg.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/xilinx/zy7_slcr.h>
+
+struct zy7_slcr_softc {
+ device_t dev;
+ struct mtx sc_mtx;
+ struct resource *mem_res;
+};
+
+static struct zy7_slcr_softc *zy7_slcr_softc_p;
+extern void (*zynq7_cpu_reset);
+
+#define ZSLCR_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
+#define ZSLCR_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
+#define ZSLCR_LOCK_INIT(sc) \
+ mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->dev), \
+ "zy7_slcr", MTX_DEF)
+#define ZSLCR_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx);
+
+#define RD4(sc, off) (bus_read_4((sc)->mem_res, (off)))
+#define WR4(sc, off, val) (bus_write_4((sc)->mem_res, (off), (val)))
+
+#define ZYNQ_DEFAULT_PS_CLK_FREQUENCY 33333333 /* 33.3 Mhz */
+
+SYSCTL_NODE(_hw, OID_AUTO, zynq, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
+ "Xilinx Zynq-7000");
+
+static char zynq_bootmode[64];
+SYSCTL_STRING(_hw_zynq, OID_AUTO, bootmode, CTLFLAG_RD, zynq_bootmode, 0,
+ "Zynq boot mode");
+
+static char zynq_pssid[100];
+SYSCTL_STRING(_hw_zynq, OID_AUTO, pssid, CTLFLAG_RD, zynq_pssid, 0,
+ "Zynq PSS IDCODE");
+
+static uint32_t zynq_reboot_status;
+SYSCTL_INT(_hw_zynq, OID_AUTO, reboot_status, CTLFLAG_RD, &zynq_reboot_status,
+ 0, "Zynq REBOOT_STATUS register");
+
+static int ps_clk_frequency;
+SYSCTL_INT(_hw_zynq, OID_AUTO, ps_clk_frequency, CTLFLAG_RD, &ps_clk_frequency,
+ 0, "Zynq PS_CLK Frequency");
+
+static int io_pll_frequency;
+SYSCTL_INT(_hw_zynq, OID_AUTO, io_pll_frequency, CTLFLAG_RD, &io_pll_frequency,
+ 0, "Zynq IO PLL Frequency");
+
+static int arm_pll_frequency;
+SYSCTL_INT(_hw_zynq, OID_AUTO, arm_pll_frequency, CTLFLAG_RD,
+ &arm_pll_frequency, 0, "Zynq ARM PLL Frequency");
+
+static int ddr_pll_frequency;
+SYSCTL_INT(_hw_zynq, OID_AUTO, ddr_pll_frequency, CTLFLAG_RD,
+ &ddr_pll_frequency, 0, "Zynq DDR PLL Frequency");
+
+static void
+zy7_slcr_unlock(struct zy7_slcr_softc *sc)
+{
+
+ /* Unlock SLCR with magic number. */
+ WR4(sc, ZY7_SLCR_UNLOCK, ZY7_SLCR_UNLOCK_MAGIC);
+}
+
+static void
+zy7_slcr_lock(struct zy7_slcr_softc *sc)
+{
+
+ /* Lock SLCR with magic number. */
+ WR4(sc, ZY7_SLCR_LOCK, ZY7_SLCR_LOCK_MAGIC);
+}
+
+static void
+zy7_slcr_cpu_reset(void)
+{
+ struct zy7_slcr_softc *sc = zy7_slcr_softc_p;
+
+ /* Unlock SLCR registers. */
+ zy7_slcr_unlock(sc);
+
+ /* This has something to do with a work-around so the fsbl will load
+ * the bitstream after soft-reboot. It's very important.
+ */
+ WR4(sc, ZY7_SLCR_REBOOT_STAT,
+ RD4(sc, ZY7_SLCR_REBOOT_STAT) & 0xf0ffffff);
+
+ /* Soft reset */
+ WR4(sc, ZY7_SLCR_PSS_RST_CTRL, ZY7_SLCR_PSS_RST_CTRL_SOFT_RESET);
+
+ for (;;)
+ ;
+}
+
+/* Assert PL resets and disable level shifters in preparation of programming
+ * the PL (FPGA) section. Called from zy7_devcfg.c.
+ */
+void
+zy7_slcr_preload_pl(void)
+{
+ struct zy7_slcr_softc *sc = zy7_slcr_softc_p;
+
+ if (!sc)
+ return;
+
+ ZSLCR_LOCK(sc);
+
+ /* Unlock SLCR registers. */
+ zy7_slcr_unlock(sc);
+
+ /* Assert top level output resets. */
+ WR4(sc, ZY7_SLCR_FPGA_RST_CTRL, ZY7_SLCR_FPGA_RST_CTRL_RST_ALL);
+
+ /* Disable all level shifters. */
+ WR4(sc, ZY7_SLCR_LVL_SHFTR_EN, 0);
+
+ /* Lock SLCR registers. */
+ zy7_slcr_lock(sc);
+
+ ZSLCR_UNLOCK(sc);
+}
+
+/* After PL configuration, enable level shifters and deassert top-level
+ * PL resets. Called from zy7_devcfg.c. Optionally, the level shifters
+ * can be left disabled but that's rare of an FPGA application. That option
+ * is controlled by a sysctl in the devcfg driver.
+ */
+void
+zy7_slcr_postload_pl(int en_level_shifters)
+{
+ struct zy7_slcr_softc *sc = zy7_slcr_softc_p;
+
+ if (!sc)
+ return;
+
+ ZSLCR_LOCK(sc);
+
+ /* Unlock SLCR registers. */
+ zy7_slcr_unlock(sc);
+
+ if (en_level_shifters)
+ /* Enable level shifters. */
+ WR4(sc, ZY7_SLCR_LVL_SHFTR_EN, ZY7_SLCR_LVL_SHFTR_EN_ALL);
+
+ /* Deassert top level output resets. */
+ WR4(sc, ZY7_SLCR_FPGA_RST_CTRL, 0);
+
+ /* Lock SLCR registers. */
+ zy7_slcr_lock(sc);
+
+ ZSLCR_UNLOCK(sc);
+}
+
+/* Override cgem_set_refclk() in gigabit ethernet driver
+ * (sys/dev/cadence/if_cgem.c). This function is called to
+ * request a change in the gem's reference clock speed.
+ */
+int
+cgem_set_ref_clk(int unit, int frequency)
+{
+ struct zy7_slcr_softc *sc = zy7_slcr_softc_p;
+ int div0, div1;
+
+ if (!sc)
+ return (-1);
+
+ /* Find suitable divisor pairs. Round result to nearest khz
+ * to test for match.
+ */
+ for (div1 = 1; div1 <= ZY7_SLCR_GEM_CLK_CTRL_DIVISOR1_MAX; div1++) {
+ div0 = (io_pll_frequency + div1 * frequency / 2) /
+ div1 / frequency;
+ if (div0 > 0 && div0 <= ZY7_SLCR_GEM_CLK_CTRL_DIVISOR_MAX &&
+ ((io_pll_frequency / div0 / div1) + 500) / 1000 ==
+ (frequency + 500) / 1000)
+ break;
+ }
+
+ if (div1 > ZY7_SLCR_GEM_CLK_CTRL_DIVISOR1_MAX)
+ return (-1);
+
+ ZSLCR_LOCK(sc);
+
+ /* Unlock SLCR registers. */
+ zy7_slcr_unlock(sc);
+
+ /* Modify GEM reference clock. */
+ WR4(sc, unit ? ZY7_SLCR_GEM1_CLK_CTRL : ZY7_SLCR_GEM0_CLK_CTRL,
+ (div1 << ZY7_SLCR_GEM_CLK_CTRL_DIVISOR1_SHIFT) |
+ (div0 << ZY7_SLCR_GEM_CLK_CTRL_DIVISOR_SHIFT) |
+ ZY7_SLCR_GEM_CLK_CTRL_SRCSEL_IO_PLL |
+ ZY7_SLCR_GEM_CLK_CTRL_CLKACT);
+
+ /* Lock SLCR registers. */
+ zy7_slcr_lock(sc);
+
+ ZSLCR_UNLOCK(sc);
+
+ return (0);
+}
+
+/*
+ * PL clocks management function
+ */
+int
+zy7_pl_fclk_set_source(int unit, int source)
+{
+ struct zy7_slcr_softc *sc = zy7_slcr_softc_p;
+ uint32_t reg;
+
+ if (!sc)
+ return (-1);
+
+ ZSLCR_LOCK(sc);
+
+ /* Unlock SLCR registers. */
+ zy7_slcr_unlock(sc);
+
+ /* Modify FPGAx source. */
+ reg = RD4(sc, ZY7_SLCR_FPGA_CLK_CTRL(unit));
+ reg &= ~(ZY7_SLCR_FPGA_CLK_CTRL_SRCSEL_MASK);
+ reg |= (source << ZY7_SLCR_FPGA_CLK_CTRL_SRCSEL_SHIFT);
+ WR4(sc, ZY7_SLCR_FPGA_CLK_CTRL(unit), reg);
+
+ /* Lock SLCR registers. */
+ zy7_slcr_lock(sc);
+
+ ZSLCR_UNLOCK(sc);
+
+ return (0);
+}
+
+int
+zy7_pl_fclk_get_source(int unit)
+{
+ struct zy7_slcr_softc *sc = zy7_slcr_softc_p;
+ uint32_t reg;
+ int source;
+
+ if (!sc)
+ return (-1);
+
+ ZSLCR_LOCK(sc);
+
+ /* Modify GEM reference clock. */
+ reg = RD4(sc, ZY7_SLCR_FPGA_CLK_CTRL(unit));
+ source = (reg & ZY7_SLCR_FPGA_CLK_CTRL_SRCSEL_MASK) >>
+ ZY7_SLCR_FPGA_CLK_CTRL_SRCSEL_SHIFT;
+
+ /* ZY7_PL_FCLK_SRC_IO is actually b0x */
+ if ((source & 2) == 0)
+ source = ZY7_PL_FCLK_SRC_IO;
+
+ ZSLCR_UNLOCK(sc);
+
+ return (source);
+}
+
+int
+zy7_pl_fclk_set_freq(int unit, int frequency)
+{
+ struct zy7_slcr_softc *sc = zy7_slcr_softc_p;
+ int div0, div1;
+ int base_frequency;
+ uint32_t reg;
+ int source;
+
+ if (!sc)
+ return (-1);
+
+ source = zy7_pl_fclk_get_source(unit);
+ switch (source) {
+ case ZY7_PL_FCLK_SRC_IO:
+ base_frequency = io_pll_frequency;
+ break;
+
+ case ZY7_PL_FCLK_SRC_ARM:
+ base_frequency = arm_pll_frequency;
+ break;
+
+ case ZY7_PL_FCLK_SRC_DDR:
+ base_frequency = ddr_pll_frequency;
+ break;
+
+ default:
+ return (-1);
+ }
+
+ /* Find suitable divisor pairs. Round result to nearest khz
+ * to test for match.
+ */
+ for (div1 = 1; div1 <= ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR_MAX; div1++) {
+ div0 = (base_frequency + div1 * frequency / 2) /
+ div1 / frequency;
+ if (div0 > 0 && div0 <= ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR_MAX &&
+ ((base_frequency / div0 / div1) + 500) / 1000 ==
+ (frequency + 500) / 1000)
+ break;
+ }
+
+ if (div1 > ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR_MAX)
+ return (-1);
+
+ ZSLCR_LOCK(sc);
+
+ /* Unlock SLCR registers. */
+ zy7_slcr_unlock(sc);
+
+ /* Modify FPGAx reference clock. */
+ reg = RD4(sc, ZY7_SLCR_FPGA_CLK_CTRL(unit));
+ reg &= ~(ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR1_MASK |
+ ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR0_MASK);
+ reg |= (div1 << ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR1_SHIFT) |
+ (div0 << ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR0_SHIFT);
+ WR4(sc, ZY7_SLCR_FPGA_CLK_CTRL(unit), reg);
+
+ /* Lock SLCR registers. */
+ zy7_slcr_lock(sc);
+
+ ZSLCR_UNLOCK(sc);
+
+ return (base_frequency / div0 / div1);
+}
+
+int
+zy7_pl_fclk_get_freq(int unit)
+{
+ struct zy7_slcr_softc *sc = zy7_slcr_softc_p;
+ int div0, div1;
+ int base_frequency;
+ int frequency;
+ uint32_t reg;
+ int source;
+
+ if (!sc)
+ return (-1);
+
+ source = zy7_pl_fclk_get_source(unit);
+ switch (source) {
+ case ZY7_PL_FCLK_SRC_IO:
+ base_frequency = io_pll_frequency;
+ break;
+
+ case ZY7_PL_FCLK_SRC_ARM:
+ base_frequency = arm_pll_frequency;
+ break;
+
+ case ZY7_PL_FCLK_SRC_DDR:
+ base_frequency = ddr_pll_frequency;
+ break;
+
+ default:
+ return (-1);
+ }
+
+ ZSLCR_LOCK(sc);
+
+ /* Modify FPGAx reference clock. */
+ reg = RD4(sc, ZY7_SLCR_FPGA_CLK_CTRL(unit));
+ div1 = (reg & ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR1_MASK) >>
+ ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR1_SHIFT;
+ div0 = (reg & ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR0_MASK) >>
+ ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR0_SHIFT;
+
+ ZSLCR_UNLOCK(sc);
+
+ if (div0 == 0)
+ div0 = 1;
+
+ if (div1 == 0)
+ div1 = 1;
+
+ frequency = (base_frequency / div0 / div1);
+ /* Round to KHz */
+ frequency = (frequency + 500) / 1000;
+ frequency = frequency * 1000;
+
+ return (frequency);
+}
+
+int
+zy7_pl_fclk_enable(int unit)
+{
+ struct zy7_slcr_softc *sc = zy7_slcr_softc_p;
+
+ if (!sc)
+ return (-1);
+
+ ZSLCR_LOCK(sc);
+
+ /* Unlock SLCR registers. */
+ zy7_slcr_unlock(sc);
+
+ WR4(sc, ZY7_SLCR_FPGA_THR_CTRL(unit), 0);
+ WR4(sc, ZY7_SLCR_FPGA_THR_CNT(unit), 0);
+
+ /* Lock SLCR registers. */
+ zy7_slcr_lock(sc);
+
+ ZSLCR_UNLOCK(sc);
+
+ return (0);
+}
+
+int
+zy7_pl_fclk_disable(int unit)
+{
+ struct zy7_slcr_softc *sc = zy7_slcr_softc_p;
+
+ if (!sc)
+ return (-1);
+
+ ZSLCR_LOCK(sc);
+
+ /* Unlock SLCR registers. */
+ zy7_slcr_unlock(sc);
+
+ WR4(sc, ZY7_SLCR_FPGA_THR_CTRL(unit), 0);
+ WR4(sc, ZY7_SLCR_FPGA_THR_CNT(unit), 1);
+
+ /* Lock SLCR registers. */
+ zy7_slcr_lock(sc);
+
+ ZSLCR_UNLOCK(sc);
+
+ return (0);
+}
+
+int
+zy7_pl_fclk_enabled(int unit)
+{
+ struct zy7_slcr_softc *sc = zy7_slcr_softc_p;
+ uint32_t reg;
+
+ if (!sc)
+ return (-1);
+
+ ZSLCR_LOCK(sc);
+ reg = RD4(sc, ZY7_SLCR_FPGA_THR_CNT(unit));
+ ZSLCR_UNLOCK(sc);
+
+ return !(reg & 1);
+}
+
+int
+zy7_pl_level_shifters_enabled(void)
+{
+ struct zy7_slcr_softc *sc = zy7_slcr_softc_p;
+
+ uint32_t reg;
+
+ if (!sc)
+ return (-1);
+
+ ZSLCR_LOCK(sc);
+ reg = RD4(sc, ZY7_SLCR_LVL_SHFTR_EN);
+ ZSLCR_UNLOCK(sc);
+
+ return (reg == ZY7_SLCR_LVL_SHFTR_EN_ALL);
+}
+
+void
+zy7_pl_level_shifters_enable(void)
+{
+ struct zy7_slcr_softc *sc = zy7_slcr_softc_p;
+
+ if (!sc)
+ return;
+
+ ZSLCR_LOCK(sc);
+ zy7_slcr_unlock(sc);
+ WR4(sc, ZY7_SLCR_LVL_SHFTR_EN, ZY7_SLCR_LVL_SHFTR_EN_ALL);
+ zy7_slcr_lock(sc);
+ ZSLCR_UNLOCK(sc);
+}
+
+void
+zy7_pl_level_shifters_disable(void)
+{
+ struct zy7_slcr_softc *sc = zy7_slcr_softc_p;
+
+ if (!sc)
+ return;
+
+ ZSLCR_LOCK(sc);
+ zy7_slcr_unlock(sc);
+ WR4(sc, ZY7_SLCR_LVL_SHFTR_EN, 0);
+ zy7_slcr_lock(sc);
+ ZSLCR_UNLOCK(sc);
+}
+
+static int
+zy7_slcr_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "xlnx,zy7_slcr"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Zynq-7000 slcr block");
+ return (0);
+}
+
+static int
+zy7_slcr_attach(device_t dev)
+{
+ struct zy7_slcr_softc *sc = device_get_softc(dev);
+ int rid;
+ phandle_t node;
+ pcell_t cell;
+ uint32_t bootmode;
+ uint32_t pss_idcode;
+ uint32_t arm_pll_ctrl;
+ uint32_t ddr_pll_ctrl;
+ uint32_t io_pll_ctrl;
+ static char *bootdev_names[] = {
+ "JTAG", "Quad-SPI", "NOR", "(3?)",
+ "NAND", "SD Card", "(6?)", "(7?)"
+ };
+
+ /* Allow only one attach. */
+ if (zy7_slcr_softc_p != NULL)
+ return (ENXIO);
+
+ sc->dev = dev;
+
+ ZSLCR_LOCK_INIT(sc);
+
+ /* Get memory resource. */
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "could not allocate memory resources.\n");
+ return (ENOMEM);
+ }
+
+ /* Hook up cpu_reset. */
+ zy7_slcr_softc_p = sc;
+ zynq7_cpu_reset = zy7_slcr_cpu_reset;
+
+ /* Read info and set sysctls. */
+ bootmode = RD4(sc, ZY7_SLCR_BOOT_MODE);
+ snprintf(zynq_bootmode, sizeof(zynq_bootmode),
+ "0x%x: boot device: %s", bootmode,
+ bootdev_names[bootmode & ZY7_SLCR_BOOT_MODE_BOOTDEV_MASK]);
+
+ pss_idcode = RD4(sc, ZY7_SLCR_PSS_IDCODE);
+ snprintf(zynq_pssid, sizeof(zynq_pssid),
+ "0x%x: manufacturer: 0x%x device: 0x%x "
+ "family: 0x%x sub-family: 0x%x rev: 0x%x",
+ pss_idcode,
+ (pss_idcode & ZY7_SLCR_PSS_IDCODE_MNFR_ID_MASK) >>
+ ZY7_SLCR_PSS_IDCODE_MNFR_ID_SHIFT,
+ (pss_idcode & ZY7_SLCR_PSS_IDCODE_DEVICE_MASK) >>
+ ZY7_SLCR_PSS_IDCODE_DEVICE_SHIFT,
+ (pss_idcode & ZY7_SLCR_PSS_IDCODE_FAMILY_MASK) >>
+ ZY7_SLCR_PSS_IDCODE_FAMILY_SHIFT,
+ (pss_idcode & ZY7_SLCR_PSS_IDCODE_SUB_FAMILY_MASK) >>
+ ZY7_SLCR_PSS_IDCODE_SUB_FAMILY_SHIFT,
+ (pss_idcode & ZY7_SLCR_PSS_IDCODE_REVISION_MASK) >>
+ ZY7_SLCR_PSS_IDCODE_REVISION_SHIFT);
+
+ zynq_reboot_status = RD4(sc, ZY7_SLCR_REBOOT_STAT);
+
+ /* Derive PLL frequencies from PS_CLK. */
+ node = ofw_bus_get_node(dev);
+ if (OF_getencprop(node, "clock-frequency", &cell, sizeof(cell)) > 0)
+ ps_clk_frequency = cell;
+ else
+ ps_clk_frequency = ZYNQ_DEFAULT_PS_CLK_FREQUENCY;
+
+ arm_pll_ctrl = RD4(sc, ZY7_SLCR_ARM_PLL_CTRL);
+ ddr_pll_ctrl = RD4(sc, ZY7_SLCR_DDR_PLL_CTRL);
+ io_pll_ctrl = RD4(sc, ZY7_SLCR_IO_PLL_CTRL);
+
+ /* Determine ARM PLL frequency. */
+ if (((arm_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_QUAL) == 0 &&
+ (arm_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_FORCE) != 0) ||
+ ((arm_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_QUAL) != 0 &&
+ (bootmode & ZY7_SLCR_BOOT_MODE_PLL_BYPASS) != 0))
+ /* PLL is bypassed. */
+ arm_pll_frequency = ps_clk_frequency;
+ else
+ arm_pll_frequency = ps_clk_frequency *
+ ((arm_pll_ctrl & ZY7_SLCR_PLL_CTRL_FDIV_MASK) >>
+ ZY7_SLCR_PLL_CTRL_FDIV_SHIFT);
+
+ /* Determine DDR PLL frequency. */
+ if (((ddr_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_QUAL) == 0 &&
+ (ddr_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_FORCE) != 0) ||
+ ((ddr_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_QUAL) != 0 &&
+ (bootmode & ZY7_SLCR_BOOT_MODE_PLL_BYPASS) != 0))
+ /* PLL is bypassed. */
+ ddr_pll_frequency = ps_clk_frequency;
+ else
+ ddr_pll_frequency = ps_clk_frequency *
+ ((ddr_pll_ctrl & ZY7_SLCR_PLL_CTRL_FDIV_MASK) >>
+ ZY7_SLCR_PLL_CTRL_FDIV_SHIFT);
+
+ /* Determine IO PLL frequency. */
+ if (((io_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_QUAL) == 0 &&
+ (io_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_FORCE) != 0) ||
+ ((io_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_QUAL) != 0 &&
+ (bootmode & ZY7_SLCR_BOOT_MODE_PLL_BYPASS) != 0))
+ /* PLL is bypassed. */
+ io_pll_frequency = ps_clk_frequency;
+ else
+ io_pll_frequency = ps_clk_frequency *
+ ((io_pll_ctrl & ZY7_SLCR_PLL_CTRL_FDIV_MASK) >>
+ ZY7_SLCR_PLL_CTRL_FDIV_SHIFT);
+
+ /* Lock SLCR registers. */
+ zy7_slcr_lock(sc);
+
+ return (0);
+}
+
+static int
+zy7_slcr_detach(device_t dev)
+{
+ struct zy7_slcr_softc *sc = device_get_softc(dev);
+
+ bus_generic_detach(dev);
+
+ /* Release memory resource. */
+ if (sc->mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY,
+ rman_get_rid(sc->mem_res), sc->mem_res);
+
+ zy7_slcr_softc_p = NULL;
+ zynq7_cpu_reset = NULL;
+
+ ZSLCR_LOCK_DESTROY(sc);
+
+ return (0);
+}
+
+static device_method_t zy7_slcr_methods[] = {
+ /* device_if */
+ DEVMETHOD(device_probe, zy7_slcr_probe),
+ DEVMETHOD(device_attach, zy7_slcr_attach),
+ DEVMETHOD(device_detach, zy7_slcr_detach),
+
+ DEVMETHOD_END
+};
+
+static driver_t zy7_slcr_driver = {
+ "zy7_slcr",
+ zy7_slcr_methods,
+ sizeof(struct zy7_slcr_softc),
+};
+static devclass_t zy7_slcr_devclass;
+
+DRIVER_MODULE(zy7_slcr, simplebus, zy7_slcr_driver, zy7_slcr_devclass, 0, 0);
+MODULE_VERSION(zy7_slcr, 1);
diff --git a/sys/arm/xilinx/zy7_slcr.h b/sys/arm/xilinx/zy7_slcr.h
new file mode 100644
index 000000000000..d6ca043a9258
--- /dev/null
+++ b/sys/arm/xilinx/zy7_slcr.h
@@ -0,0 +1,329 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 Thomas Skibo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Defines for Zynq-7000 SLCR registers.
+ *
+ * Most of these registers are initialized by the First Stage Boot
+ * Loader and are not modified by the kernel.
+ *
+ * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual.
+ * (v1.4) November 16, 2012. Xilinx doc UG585. SLCR register definitions
+ * are in appendix B.28.
+ */
+
+#ifndef _ZY7_SLCR_H_
+#define _ZY7_SLCR_H_
+
+#define ZY7_SCLR_SCL 0x0000
+#define ZY7_SLCR_LOCK 0x0004
+#define ZY7_SLCR_LOCK_MAGIC 0x767b
+#define ZY7_SLCR_UNLOCK 0x0008
+#define ZY7_SLCR_UNLOCK_MAGIC 0xdf0d
+#define ZY7_SLCR_LOCKSTA 0x000c
+
+/* PLL controls. */
+#define ZY7_SLCR_ARM_PLL_CTRL 0x0100
+#define ZY7_SLCR_DDR_PLL_CTRL 0x0104
+#define ZY7_SLCR_IO_PLL_CTRL 0x0108
+#define ZY7_SLCR_PLL_CTRL_RESET (1 << 0)
+#define ZY7_SLCR_PLL_CTRL_PWRDWN (1 << 1)
+#define ZY7_SLCR_PLL_CTRL_BYPASS_QUAL (1 << 3)
+#define ZY7_SLCR_PLL_CTRL_BYPASS_FORCE (1 << 4)
+#define ZY7_SLCR_PLL_CTRL_FDIV_SHIFT 12
+#define ZY7_SLCR_PLL_CTRL_FDIV_MASK (0x7f << 12)
+#define ZY7_SLCR_PLL_STATUS 0x010c
+#define ZY7_SLCR_PLL_STAT_ARM_PLL_LOCK (1 << 0)
+#define ZY7_SLCR_PLL_STAT_DDR_PLL_LOCK (1 << 1)
+#define ZY7_SLCR_PLL_STAT_IO_PLL_LOCK (1 << 2)
+#define ZY7_SLCR_PLL_STAT_ARM_PLL_STABLE (1 << 3)
+#define ZY7_SLCR_PLL_STAT_DDR_PLL_STABLE (1 << 4)
+#define ZY7_SLCR_PLL_STAT_IO_PLL_STABLE (1 << 5)
+#define ZY7_SLCR_ARM_PLL_CFG 0x0110
+#define ZY7_SLCR_DDR_PLL_CFG 0x0114
+#define ZY7_SLCR_IO_PLL_CFG 0x0118
+#define ZY7_SLCR_PLL_CFG_RES_SHIFT 4
+#define ZY7_SLCR_PLL_CFG_RES_MASK (0xf << 4)
+#define ZY7_SLCR_PLL_CFG_PLL_CP_SHIFT 8
+#define ZY7_SLCR_PLL_CFG_PLL_CP_MASK (0xf << 8)
+#define ZY7_SLCR_PLL_CFG_LOCK_CNT_SHIFT 12
+#define ZY7_SLCR_PLL_CFG_LOCK_CNT_MASK (0x3ff << 12)
+
+/* Clock controls. */
+#define ZY7_SLCR_ARM_CLK_CTRL 0x0120
+#define ZY7_SLCR_ARM_CLK_CTRL_CPU_PERI_CLKACT (1 << 28)
+#define ZY7_SLCR_ARM_CLK_CTRL_CPU_1XCLKACT (1 << 27)
+#define ZY7_SLCR_ARM_CLK_CTRL_CPU_2XCLKACT (1 << 26)
+#define ZY7_SLCR_ARM_CLK_CTRL_CPU_3OR2XCLKACT (1 << 25)
+#define ZY7_SLCR_ARM_CLK_CTRL_CPU_6OR4XCLKACT (1 << 24)
+#define ZY7_SLCR_ARM_CLK_CTRL_SRCSEL_MASK (3 << 4)
+#define ZY7_SLCR_ARM_CLK_CTRL_SRCSEL_ARM_PLL (0 << 4)
+#define ZY7_SLCR_ARM_CLK_CTRL_SRCSEL_DDR_PLL (2 << 4)
+#define ZY7_SLCR_ARM_CLK_CTRL_SRCSEL_IO_PLL (3 << 4)
+#define ZY7_SLCR_ARM_CLK_CTRL_DIVISOR_SHIFT 8
+#define ZY7_SLCR_ARM_CLK_CTRL_DIVISOR_MASK (0x3f << 8)
+#define ZY7_SLCR_DDR_CLK_CTRL 0x0124
+#define ZY7_SLCR_DDR_CLK_CTRL_2XCLK_DIV_SHIFT 26
+#define ZY7_SLCR_DDR_CLK_CTRL_2XCLK_DIV_MASK (0x3f << 26)
+#define ZY7_SLCR_DDR_CLK_CTRL_3XCLK_DIV_SHIFT 20
+#define ZY7_SLCR_DDR_CLK_CTRL_3XCLK_DIV_MASK (0x3f << 20)
+#define ZY7_SLCR_DDR_CLK_CTRL_2XCLKACT (1 << 1)
+#define ZY7_SLCR_DDR_CLK_CTRL_3XCLKACT (1 << 0)
+#define ZY7_SLCR_DCI_CLK_CTRL 0x0128
+#define ZY7_SLCR_DCI_CLK_CTRL_DIVISOR1_SHIFT 20
+#define ZY7_SLCR_DCI_CLK_CTRL_DIVISOR1_MASK (0x3f << 20)
+#define ZY7_SLCR_DCI_CLK_CTRL_DIVISOR0_SHIFT 8
+#define ZY7_SLCR_DCI_CLK_CTRL_DIVISOR0_MASK (0x3f << 8)
+#define ZY7_SLCR_DCI_CLK_CTRL_CLKACT (1 << 0)
+#define ZY7_SLCR_APER_CLK_CTRL 0x012c /* amba periph clk ctrl */
+#define ZY7_SLCR_APER_CLK_CTRL_SMC_CPU_1XCLKACT (1 << 24)
+#define ZY7_SLCR_APER_CLK_CTRL_LQSPI_CPU_1XCLKACT (1 << 23)
+#define ZY7_SLCR_APER_CLK_CTRL_GPIO_CPU_1XCLKACT (1 << 22)
+#define ZY7_SLCR_APER_CLK_CTRL_UART1_CPU_1XCLKACT (1 << 21)
+#define ZY7_SLCR_APER_CLK_CTRL_UART0_CPU_1XCLKACT (1 << 20)
+#define ZY7_SLCR_APER_CLK_CTRL_I2C1_CPU_1XCLKACT (1 << 19)
+#define ZY7_SLCR_APER_CLK_CTRL_I2C0_CPU_1XCLKACT (1 << 18)
+#define ZY7_SLCR_APER_CLK_CTRL_CAN1_CPU_1XCLKACT (1 << 17)
+#define ZY7_SLCR_APER_CLK_CTRL_CAN0_CPU_1XCLKACT (1 << 16)
+#define ZY7_SLCR_APER_CLK_CTRL_SPI1_CPU_1XCLKACT (1 << 15)
+#define ZY7_SLCR_APER_CLK_CTRL_SPI0_CPU_1XCLKACT (1 << 14)
+#define ZY7_SLCR_APER_CLK_CTRL_SDI1_CPU_1XCLKACT (1 << 11)
+#define ZY7_SLCR_APER_CLK_CTRL_SDI0_CPU_1XCLKACT (1 << 10)
+#define ZY7_SLCR_APER_CLK_CTRL_GEM1_CPU_1XCLKACT (1 << 7)
+#define ZY7_SLCR_APER_CLK_CTRL_GEM0_CPU_1XCLKACT (1 << 6)
+#define ZY7_SLCR_APER_CLK_CTRL_USB1_CPU_1XCLKACT (1 << 3)
+#define ZY7_SLCR_APER_CLK_CTRL_USB0_CPU_1XCLKACT (1 << 2)
+#define ZY7_SLCR_APER_CLK_CTRL_DMA_CPU_1XCLKACT (1 << 0)
+#define ZY7_SLCR_USB0_CLK_CTRL 0x0130
+#define ZY7_SLCR_USB1_CLK_CTRL 0x0134
+#define ZY7_SLCR_GEM0_RCLK_CTRL 0x0138
+#define ZY7_SLCR_GEM1_RCLK_CTRL 0x013c
+#define ZY7_SLCR_GEM0_CLK_CTRL 0x0140
+#define ZY7_SLCR_GEM1_CLK_CTRL 0x0144
+#define ZY7_SLCR_GEM_CLK_CTRL_DIVISOR1_MASK (0x3f << 20)
+#define ZY7_SLCR_GEM_CLK_CTRL_DIVISOR1_SHIFT 20
+#define ZY7_SLCR_GEM_CLK_CTRL_DIVISOR1_MAX 0x3f
+#define ZY7_SLCR_GEM_CLK_CTRL_DIVISOR_MASK (0x3f << 8)
+#define ZY7_SLCR_GEM_CLK_CTRL_DIVISOR_SHIFT 8
+#define ZY7_SLCR_GEM_CLK_CTRL_DIVISOR_MAX 0x3f
+#define ZY7_SLCR_GEM_CLK_CTRL_SRCSEL_MASK (7 << 4)
+#define ZY7_SLCR_GEM_CLK_CTRL_SRCSEL_IO_PLL (0 << 4)
+#define ZY7_SLCR_GEM_CLK_CTRL_SRCSEL_ARM_PLL (2 << 4)
+#define ZY7_SLCR_GEM_CLK_CTRL_SRCSEL_DDR_PLL (3 << 4)
+#define ZY7_SLCR_GEM_CLK_CTRL_SRCSEL_EMIO_CLK (4 << 4)
+#define ZY7_SLCR_GEM_CLK_CTRL_CLKACT 1
+#define ZY7_SLCR_SMC_CLK_CTRL 0x0148
+#define ZY7_SLCR_LQSPI_CLK_CTRL 0x014c
+#define ZY7_SLCR_SDIO_CLK_CTRL 0x0150
+#define ZY7_SLCR_UART_CLK_CTRL 0x0154
+#define ZY7_SLCR_SPI_CLK_CTRL 0x0158
+#define ZY7_SLCR_CAN_CLK_CTRL 0x015c
+#define ZY7_SLCR_CAN_MIOCLK_CTRL 0x0160
+#define ZY7_SLCR_DBG_CLK_CTRL 0x0164
+#define ZY7_SLCR_PCAP_CLK_CTRL 0x0168
+#define ZY7_SLCR_TOPSW_CLK_CTRL 0x016c /* central intercnn clk ctrl */
+#define ZY7_SLCR_FPGA_CLK_CTRL(unit) (0x0170 + 0x10 * (unit))
+#define ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR1_SHIFT 20
+#define ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR1_MASK (0x3f << 20)
+#define ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR0_SHIFT 8
+#define ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR0_MASK (0x3f << 8)
+#define ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR_MAX 0x3f
+#define ZY7_SLCR_FPGA_CLK_CTRL_SRCSEL_SHIFT 4
+#define ZY7_SLCR_FPGA_CLK_CTRL_SRCSEL_MASK (3 << 4)
+#define ZY7_SLCR_FPGA_THR_CTRL(unit) (0x0174 + 0x10 * (unit))
+#define ZY7_SLCR_FPGA_THR_CTRL_CNT_RST (1 << 1)
+#define ZY7_SLCR_FPGA_THR_CTRL_CPU_START (1 << 0)
+#define ZY7_SLCR_FPGA_THR_CNT(unit) (0x0178 + 0x10 * (unit))
+#define ZY7_SLCR_FPGA_THR_STA(unit) (0x017c + 0x10 * (unit))
+#define ZY7_SLCR_CLK_621_TRUE 0x01c4 /* cpu clock ratio mode */
+
+/* Reset controls. */
+#define ZY7_SLCR_PSS_RST_CTRL 0x0200
+#define ZY7_SLCR_PSS_RST_CTRL_SOFT_RESET (1 << 0)
+#define ZY7_SLCR_DDR_RST_CTRL 0x0204
+#define ZY7_SLCR_TOPSW_RST_CTRL 0x0208
+#define ZY7_SLCR_DMAC_RST_CTRL 0x020c
+#define ZY7_SLCR_USB_RST_CTRL 0x0210
+#define ZY7_SLCR_GEM_RST_CTRL 0x0214
+#define ZY7_SLCR_SDIO_RST_CTRL 0x0218
+#define ZY7_SLCR_SPI_RST_CTRL 0x021c
+#define ZY7_SLCR_CAN_RST_CTRL 0x0220
+#define ZY7_SLCR_I2C_RST_CTRL 0x0224
+#define ZY7_SLCR_UART_RST_CTRL 0x0228
+#define ZY7_SLCR_GPIO_RST_CTRL 0x022c
+#define ZY7_SLCR_LQSPI_RST_CTRL 0x0230
+#define ZY7_SLCR_SMC_RST_CTRL 0x0234
+#define ZY7_SLCR_OCM_RST_CTRL 0x0238
+#define ZY7_SLCR_DEVCI_RST_CTRL 0x023c
+#define ZY7_SLCR_FPGA_RST_CTRL 0x0240
+#define ZY7_SLCR_FPGA_RST_CTRL_FPGA3_OUT_RST (1 << 3)
+#define ZY7_SLCR_FPGA_RST_CTRL_FPGA2_OUT_RST (1 << 2)
+#define ZY7_SLCR_FPGA_RST_CTRL_FPGA1_OUT_RST (1 << 1)
+#define ZY7_SLCR_FPGA_RST_CTRL_FPGA0_OUT_RST (1 << 0)
+#define ZY7_SLCR_FPGA_RST_CTRL_RST_ALL 0xf
+#define ZY7_SLCR_A9_CPU_RST_CTRL 0x0244
+#define ZY7_SLCR_RS_AWDT_CTRL 0x024c
+
+#define ZY7_SLCR_REBOOT_STAT 0x0258
+#define ZY7_SLCR_REBOOT_STAT_STATE_MASK (0xffU << 24)
+#define ZY7_SLCR_REBOOT_STAT_POR (1 << 22)
+#define ZY7_SLCR_REBOOT_STAT_SRST_B (1 << 21)
+#define ZY7_SLCR_REBOOT_STAT_DBG_RST (1 << 20)
+#define ZY7_SLCR_REBOOT_STAT_SLC_RST (1 << 19)
+#define ZY7_SLCR_REBOOT_STAT_AWDT1_RST (1 << 18)
+#define ZY7_SLCR_REBOOT_STAT_AWDT0_RST (1 << 17)
+#define ZY7_SLCR_REBOOT_STAT_SWDT_RST (1 << 16)
+#define ZY7_SLCR_REBOOT_STAT_BOOTROM_ERR_CODE_MASK (0xffff)
+#define ZY7_SLCR_BOOT_MODE 0x025c
+#define ZY7_SLCR_BOOT_MODE_PLL_BYPASS (1 << 4)
+#define ZY7_SLCR_BOOT_MODE_JTAG_INDEP (1 << 3)
+#define ZY7_SLCR_BOOT_MODE_BOOTDEV_MASK 7
+#define ZY7_SLCR_BOOT_MODE_BOOTDEV_JTAG 0
+#define ZY7_SLCR_BOOT_MODE_BOOTDEV_QUAD_SPI 1
+#define ZY7_SLCR_BOOT_MODE_BOOTDEV_NOR 2
+#define ZY7_SLCR_BOOT_MODE_BOOTDEV_NAND 4
+#define ZY7_SLCR_BOOT_MODE_BOOTDEV_SD_CARD 5
+#define ZY7_SLCR_APU_CTRL 0x0300
+#define ZY7_SLCR_WDT_CLK_SEL 0x0304
+
+#define ZY7_SLCR_PSS_IDCODE 0x0530
+#define ZY7_SLCR_PSS_IDCODE_REVISION_MASK (0xfU << 28)
+#define ZY7_SLCR_PSS_IDCODE_REVISION_SHIFT 28
+#define ZY7_SLCR_PSS_IDCODE_FAMILY_MASK (0x7f << 21)
+#define ZY7_SLCR_PSS_IDCODE_FAMILY_SHIFT 21
+#define ZY7_SLCR_PSS_IDCODE_SUB_FAMILY_MASK (0xf << 17)
+#define ZY7_SLCR_PSS_IDCODE_SUB_FAMILY_SHIFT 17
+#define ZY7_SLCR_PSS_IDCODE_DEVICE_MASK (0x1f << 12)
+#define ZY7_SLCR_PSS_IDCODE_DEVICE_7Z007S (0x03 << 12)
+#define ZY7_SLCR_PSS_IDCODE_DEVICE_7Z010 (0x02 << 12)
+#define ZY7_SLCR_PSS_IDCODE_DEVICE_7Z012S (0x1c << 12)
+#define ZY7_SLCR_PSS_IDCODE_DEVICE_7Z014S (0x08 << 12)
+#define ZY7_SLCR_PSS_IDCODE_DEVICE_7Z015 (0x1b << 12)
+#define ZY7_SLCR_PSS_IDCODE_DEVICE_7Z020 (0x07 << 12)
+#define ZY7_SLCR_PSS_IDCODE_DEVICE_7Z030 (0x0c << 12)
+#define ZY7_SLCR_PSS_IDCODE_DEVICE_7Z045 (0x11 << 12)
+#define ZY7_SLCR_PSS_IDCODE_DEVICE_7Z100 (0x16 << 12)
+#define ZY7_SLCR_PSS_IDCODE_DEVICE_SHIFT 12
+#define ZY7_SLCR_PSS_IDCODE_MNFR_ID_MASK (0x7ff << 1)
+#define ZY7_SLCR_PSS_IDCODE_MNFR_ID_SHIFT 1
+
+#define ZY7_SLCR_DDR_URGENT 0x0600
+#define ZY7_SLCR_DDR_CAL_START 0x060c
+#define ZY7_SLCR_DDR_REF_START 0x0614
+#define ZY7_SLCR_DDR_CMD_STA 0x0618
+#define ZY7_SLCR_DDR_URGENT_SEL 0x061c
+#define ZY7_SLCR_DDR_DFI_STATUS 0x0620
+
+/* MIO Pin controls */
+#define ZY7_SLCR_MIO_PIN(n) (0x0700 + (n) * 4) /* 0-53 */
+#define ZY7_SLCR_MIO_PIN_RCVR_DIS (1 << 13)
+#define ZY7_SLCR_MIO_PIN_PULLUP_EN (1 << 12)
+#define ZY7_SLCR_MIO_PIN_IO_TYPE_MASK (7 << 9)
+#define ZY7_SLCR_MIO_PIN_IO_TYPE_LVTTL (0 << 9)
+#define ZY7_SLCR_MIO_PIN_IO_TYPE_LVCMOS18 (1 << 9)
+#define ZY7_SLCR_MIO_PIN_IO_TYPE_LVCMOS25 (2 << 9)
+#define ZY7_SLCR_MIO_PIN_IO_TYPE_LVCMOS33 (3 << 9)
+#define ZY7_SLCR_MIO_PIN_IO_TYPE_HSTL (4 << 9)
+#define ZY7_SLCR_MIO_PIN_L2_SEL_MASK (3 << 3)
+#define ZY7_SLCR_MIO_PIN_L2_SEL_L3_MUX (0 << 3)
+#define ZY7_SLCR_MIO_PIN_L2_SEL_SRAM_NOR_CS0 (1 << 3)
+#define ZY7_SLCR_MIO_PIN_L2_SEL_NAND_CS (2 << 3)
+#define ZY7_SLCR_MIO_PIN_L2_SEL_SDIO0_PC (3 << 3)
+#define ZY7_SLCR_MIO_PIN_L1_SEL (1 << 2)
+#define ZY7_SLCR_MIO_PIN_L0_SEL (1 << 1)
+#define ZY7_SLCR_MIO_PIN_TRI_EN (1 << 0)
+
+#define ZY7_SLCR_MIO_LOOPBACK 0x0804
+#define ZY7_SLCR_MIO_LOOPBACK_I2C0_I2C1 (1 << 3)
+#define ZY7_SLCR_MIO_LOOPBACK_CAN0_CAN1 (1 << 2)
+#define ZY7_SLCR_MIO_LOOPBACK_UA0_UA1 (1 << 1)
+#define ZY7_SLCR_MIO_LOOPBACK_SPI0_SPI1 (1 << 0)
+#define ZY7_SLCR_MIO_MST_TRI0 0x080c
+#define ZY7_SLCR_MIO_MST_TRI1 0x0810
+#define ZY7_SLCR_SD0_WP_CD_SEL 0x0830
+#define ZY7_SLCR_SD1_WP_CD_SEL 0x0834
+
+/* PS-PL level shifter control. */
+#define ZY7_SLCR_LVL_SHFTR_EN 0x900
+#define ZY7_SLCR_LVL_SHFTR_EN_USER_LVL_IN_EN_0 (1 << 3) /* PL to PS */
+#define ZY7_SLCR_LVL_SHFTR_EN_USER_LVL_OUT_EN_0 (1 << 2) /* PS to PL */
+#define ZY7_SLCR_LVL_SHFTR_EN_USER_LVL_IN_EN_1 (1 << 1) /* PL to PS */
+#define ZY7_SLCR_LVL_SHFTR_EN_USER_LVL_OUT_EN_1 (1 << 0) /* PS to PL */
+#define ZY7_SLCR_LVL_SHFTR_EN_ALL 0xf
+
+#define ZY7_SLCR_OCM_CFG 0x0910
+
+#define ZY7_SLCR_GPIOB_CTRL 0x0b00
+#define ZY7_SLCR_GPIOB_CFG_CMOS18 0x0b04
+#define ZY7_SLCR_GPIOB_CFG_CMOS25 0x0b08
+#define ZY7_SLCR_GPIOB_CFG_CMOS33 0x0b0c
+#define ZY7_SLCR_GPIOB_CFG_LVTTL 0x0b10
+#define ZY7_SLCR_GPIOB_CFG_HSTL 0x0b14
+#define ZY7_SLCR_GPIOB_DRVR_BIAS_CTRL 0x0b18
+
+#define ZY7_SLCR_DDRIOB_ADDR0 0x0b40
+#define ZY7_SLCR_DDRIOB_ADDR1 0x0b44
+#define ZY7_SLCR_DDRIOB_DATA0 0x0b48
+#define ZY7_SLCR_DDRIOB_DATA1 0x0b4c
+#define ZY7_SLCR_DDRIOB_DIFF0 0x0b50
+#define ZY7_SLCR_DDRIOB_DIFF1 0x0b54
+#define ZY7_SLCR_DDRIOB_CLK 0x0b58
+#define ZY7_SLCR_DDRIOB_DRIVE_SLEW_ADDR 0x0b5c
+#define ZY7_SLCR_DDRIOB_DRIVE_SLEW_DATA 0x0b60
+#define ZY7_SLCR_DDRIOB_DRIVE_SLEW_DIFF 0x0b64
+#define ZY7_SLCR_DDRIOB_DRIVE_SLEW_CLK 0x0b68
+#define ZY7_SLCR_DDRIOB_DDR_CTRL 0x0b6c
+#define ZY7_SLCR_DDRIOB_DCI_CTRL 0x0b70
+#define ZY7_SLCR_DDRIOB_DCI_STATUS 0x0b74
+
+#ifdef _KERNEL
+extern void zy7_slcr_preload_pl(void);
+extern void zy7_slcr_postload_pl(int en_level_shifters);
+extern int cgem_set_ref_clk(int unit, int frequency);
+
+/* Should be consistent with SRCSEL field of FPGAx_CLK_CTRL */
+#define ZY7_PL_FCLK_SRC_IO 0
+#define ZY7_PL_FCLK_SRC_IO_ALT 1 /* ZY7_PL_FCLK_SRC_IO is b0x */
+#define ZY7_PL_FCLK_SRC_ARM 2
+#define ZY7_PL_FCLK_SRC_DDR 3
+
+int zy7_pl_fclk_set_source(int unit, int source);
+int zy7_pl_fclk_get_source(int unit);
+int zy7_pl_fclk_set_freq(int unit, int freq);
+int zy7_pl_fclk_get_freq(int unit);
+int zy7_pl_fclk_enable(int unit);
+int zy7_pl_fclk_disable(int unit);
+int zy7_pl_fclk_enabled(int unit);
+int zy7_pl_level_shifters_enabled(void);
+void zy7_pl_level_shifters_enable(void);
+void zy7_pl_level_shifters_disable(void);
+
+#endif
+#endif /* _ZY7_SLCR_H_ */
diff --git a/sys/arm/xilinx/zy7_spi.c b/sys/arm/xilinx/zy7_spi.c
new file mode 100644
index 000000000000..2f8edfedf7f1
--- /dev/null
+++ b/sys/arm/xilinx/zy7_spi.c
@@ -0,0 +1,596 @@
+/*-
+ * Copyright (c) 2018 Thomas Skibo <thomasskibo@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/uio.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/stdarg.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/spibus/spi.h>
+#include <dev/spibus/spibusvar.h>
+
+#include "spibus_if.h"
+
+static struct ofw_compat_data compat_data[] = {
+ {"xlnx,zy7_spi", 1},
+ {"xlnx,zynq-spi-1.0", 1},
+ {"cdns,spi-r1p6", 1},
+ {NULL, 0}
+};
+
+struct zy7_spi_softc {
+ device_t dev;
+ device_t child;
+ struct mtx sc_mtx;
+ struct resource *mem_res;
+ struct resource *irq_res;
+ void *intrhandle;
+
+ uint32_t cfg_reg_shadow;
+ uint32_t spi_clock;
+ uint32_t ref_clock;
+ unsigned int spi_clk_real_freq;
+ unsigned int rx_overflows;
+ unsigned int tx_underflows;
+ unsigned int interrupts;
+ unsigned int stray_ints;
+ struct spi_command *cmd;
+ int tx_bytes; /* tx_cmd_sz + tx_data_sz */
+ int tx_bytes_sent;
+ int rx_bytes; /* rx_cmd_sz + rx_data_sz */
+ int rx_bytes_rcvd;
+ int busy;
+};
+
+#define ZY7_SPI_DEFAULT_SPI_CLOCK 50000000
+
+#define SPI_SC_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
+#define SPI_SC_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
+#define SPI_SC_LOCK_INIT(sc) \
+ mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->dev), NULL, MTX_DEF)
+#define SPI_SC_LOCK_DESTROY(sc) mtx_destroy(&(sc)->sc_mtx)
+#define SPI_SC_ASSERT_LOCKED(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED)
+
+#define RD4(sc, off) (bus_read_4((sc)->mem_res, (off)))
+#define WR4(sc, off, val) (bus_write_4((sc)->mem_res, (off), (val)))
+
+/*
+ * SPI device registers.
+ * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual.
+ * (v1.12.1) December 6, 2017. Xilinx doc UG585.
+ */
+#define ZY7_SPI_CONFIG_REG 0x0000
+#define ZY7_SPI_CONFIG_MODEFAIL_GEN_EN (1 << 17)
+#define ZY7_SPI_CONFIG_MAN_STRT (1 << 16)
+#define ZY7_SPI_CONFIG_MAN_STRT_EN (1 << 15)
+#define ZY7_SPI_CONFIG_MAN_CS (1 << 14)
+#define ZY7_SPI_CONFIG_CS_MASK (0xf << 10)
+#define ZY7_SPI_CONFIG_CS(x) ((0xf ^ (1 << (x))) << 10)
+#define ZY7_SPI_CONFIG_PERI_SEL (1 << 9)
+#define ZY7_SPI_CONFIG_REF_CLK (1 << 8)
+#define ZY7_SPI_CONFIG_BAUD_RATE_DIV_MASK (7 << 3)
+#define ZY7_SPI_CONFIG_BAUD_RATE_DIV_SHIFT 3
+#define ZY7_SPI_CONFIG_BAUD_RATE_DIV(x) ((x) << 3) /* divide by 2<<x */
+#define ZY7_SPI_CONFIG_CLK_PH (1 << 2) /* clock phase */
+#define ZY7_SPI_CONFIG_CLK_POL (1 << 1) /* clock polatiry */
+#define ZY7_SPI_CONFIG_MODE_SEL (1 << 0) /* master enable */
+
+#define ZY7_SPI_INTR_STAT_REG 0x0004
+#define ZY7_SPI_INTR_EN_REG 0x0008
+#define ZY7_SPI_INTR_DIS_REG 0x000c
+#define ZY7_SPI_INTR_MASK_REG 0x0010
+#define ZY7_SPI_INTR_TX_FIFO_UNDERFLOW (1 << 6)
+#define ZY7_SPI_INTR_RX_FIFO_FULL (1 << 5)
+#define ZY7_SPI_INTR_RX_FIFO_NOT_EMPTY (1 << 4)
+#define ZY7_SPI_INTR_TX_FIFO_FULL (1 << 3)
+#define ZY7_SPI_INTR_TX_FIFO_NOT_FULL (1 << 2)
+#define ZY7_SPI_INTR_MODE_FAULT (1 << 1)
+#define ZY7_SPI_INTR_RX_OVERFLOW (1 << 0)
+
+#define ZY7_SPI_EN_REG 0x0014
+#define ZY7_SPI_ENABLE (1 << 0)
+
+#define ZY7_SPI_DELAY_CTRL_REG 0x0018
+#define ZY7_SPI_DELAY_CTRL_BTWN_MASK (0xff << 16)
+#define ZY7_SPI_DELAY_CTRL_BTWN_SHIFT 16
+#define ZY7_SPI_DELAY_CTRL_AFTER_MASK (0xff << 8)
+#define ZY7_SPI_DELAY_CTRL_AFTER_SHIFT 8
+#define ZY7_SPI_DELAY_CTRL_INIT_MASK (0xff << 0)
+#define ZY7_SPI_DELAY_CTRL_INIT_SHIFT 0
+
+#define ZY7_SPI_TX_DATA_REG 0x001c
+#define ZY7_SPI_RX_DATA_REG 0x0020
+
+#define ZY7_SPI_SLV_IDLE_COUNT_REG 0x0024
+
+#define ZY7_SPI_TX_THRESH_REG 0x0028
+#define ZY7_SPI_RX_THRESH_REG 0x002c
+
+/* Fill hardware fifo with command and data bytes. */
+static void
+zy7_spi_write_fifo(struct zy7_spi_softc *sc, int nbytes)
+{
+ uint8_t byte;
+
+ while (nbytes > 0) {
+ if (sc->tx_bytes_sent < sc->cmd->tx_cmd_sz)
+ /* Writing command. */
+ byte = *((uint8_t *)sc->cmd->tx_cmd +
+ sc->tx_bytes_sent);
+ else
+ /* Writing data. */
+ byte = *((uint8_t *)sc->cmd->tx_data +
+ (sc->tx_bytes_sent - sc->cmd->tx_cmd_sz));
+
+ WR4(sc, ZY7_SPI_TX_DATA_REG, (uint32_t)byte);
+
+ sc->tx_bytes_sent++;
+ nbytes--;
+ }
+}
+
+/* Read hardware fifo data into command response and data buffers. */
+static void
+zy7_spi_read_fifo(struct zy7_spi_softc *sc)
+{
+ uint8_t byte;
+
+ do {
+ byte = RD4(sc, ZY7_SPI_RX_DATA_REG) & 0xff;
+
+ if (sc->rx_bytes_rcvd < sc->cmd->rx_cmd_sz)
+ /* Reading command. */
+ *((uint8_t *)sc->cmd->rx_cmd + sc->rx_bytes_rcvd) =
+ byte;
+ else
+ /* Reading data. */
+ *((uint8_t *)sc->cmd->rx_data +
+ (sc->rx_bytes_rcvd - sc->cmd->rx_cmd_sz)) =
+ byte;
+
+ sc->rx_bytes_rcvd++;
+
+ } while (sc->rx_bytes_rcvd < sc->rx_bytes &&
+ (RD4(sc, ZY7_SPI_INTR_STAT_REG) &
+ ZY7_SPI_INTR_RX_FIFO_NOT_EMPTY) != 0);
+}
+
+/* End a transfer early by draining rx fifo and disabling interrupts. */
+static void
+zy7_spi_abort_transfer(struct zy7_spi_softc *sc)
+{
+ /* Drain receive fifo. */
+ while ((RD4(sc, ZY7_SPI_INTR_STAT_REG) &
+ ZY7_SPI_INTR_RX_FIFO_NOT_EMPTY) != 0)
+ (void)RD4(sc, ZY7_SPI_RX_DATA_REG);
+
+ /* Shut down interrupts. */
+ WR4(sc, ZY7_SPI_INTR_DIS_REG,
+ ZY7_SPI_INTR_RX_OVERFLOW |
+ ZY7_SPI_INTR_RX_FIFO_NOT_EMPTY |
+ ZY7_SPI_INTR_TX_FIFO_NOT_FULL);
+}
+
+static void
+zy7_spi_intr(void *arg)
+{
+ struct zy7_spi_softc *sc = (struct zy7_spi_softc *)arg;
+ uint32_t istatus;
+
+ SPI_SC_LOCK(sc);
+
+ sc->interrupts++;
+
+ istatus = RD4(sc, ZY7_SPI_INTR_STAT_REG);
+
+ /* Stray interrupts can happen if a transfer gets interrupted. */
+ if (!sc->busy) {
+ sc->stray_ints++;
+ SPI_SC_UNLOCK(sc);
+ return;
+ }
+
+ if ((istatus & ZY7_SPI_INTR_RX_OVERFLOW) != 0) {
+ device_printf(sc->dev, "rx fifo overflow!\n");
+ sc->rx_overflows++;
+
+ /* Clear status bit. */
+ WR4(sc, ZY7_SPI_INTR_STAT_REG,
+ ZY7_SPI_INTR_RX_OVERFLOW);
+ }
+
+ /* Empty receive fifo before any more transmit data is sent. */
+ if (sc->rx_bytes_rcvd < sc->rx_bytes &&
+ (istatus & ZY7_SPI_INTR_RX_FIFO_NOT_EMPTY) != 0) {
+ zy7_spi_read_fifo(sc);
+ if (sc->rx_bytes_rcvd == sc->rx_bytes)
+ /* Disable receive interrupts. */
+ WR4(sc, ZY7_SPI_INTR_DIS_REG,
+ ZY7_SPI_INTR_RX_FIFO_NOT_EMPTY |
+ ZY7_SPI_INTR_RX_OVERFLOW);
+ }
+
+ /* Count tx underflows. They probably shouldn't happen. */
+ if ((istatus & ZY7_SPI_INTR_TX_FIFO_UNDERFLOW) != 0) {
+ sc->tx_underflows++;
+
+ /* Clear status bit. */
+ WR4(sc, ZY7_SPI_INTR_STAT_REG,
+ ZY7_SPI_INTR_TX_FIFO_UNDERFLOW);
+ }
+
+ /* Fill transmit fifo. */
+ if (sc->tx_bytes_sent < sc->tx_bytes &&
+ (istatus & ZY7_SPI_INTR_TX_FIFO_NOT_FULL) != 0) {
+ zy7_spi_write_fifo(sc, MIN(96, sc->tx_bytes -
+ sc->tx_bytes_sent));
+
+ if (sc->tx_bytes_sent == sc->tx_bytes) {
+ /* Disable transmit FIFO interrupt, enable receive
+ * FIFO interrupt.
+ */
+ WR4(sc, ZY7_SPI_INTR_DIS_REG,
+ ZY7_SPI_INTR_TX_FIFO_NOT_FULL);
+ WR4(sc, ZY7_SPI_INTR_EN_REG,
+ ZY7_SPI_INTR_RX_FIFO_NOT_EMPTY);
+ }
+ }
+
+ /* Finished with transfer? */
+ if (sc->tx_bytes_sent == sc->tx_bytes &&
+ sc->rx_bytes_rcvd == sc->rx_bytes) {
+ /* De-assert CS. */
+ sc->cfg_reg_shadow &=
+ ~(ZY7_SPI_CONFIG_CLK_PH | ZY7_SPI_CONFIG_CLK_POL);
+ sc->cfg_reg_shadow |= ZY7_SPI_CONFIG_CS_MASK;
+ WR4(sc, ZY7_SPI_CONFIG_REG, sc->cfg_reg_shadow);
+
+ wakeup(sc->dev);
+ }
+
+ SPI_SC_UNLOCK(sc);
+}
+
+/* Initialize hardware. */
+static int
+zy7_spi_init_hw(struct zy7_spi_softc *sc)
+{
+ uint32_t baud_div;
+
+ /* Find best clock divider. Divide by 2 not supported. */
+ baud_div = 1;
+ while ((sc->ref_clock >> (baud_div + 1)) > sc->spi_clock &&
+ baud_div < 8)
+ baud_div++;
+ if (baud_div >= 8) {
+ device_printf(sc->dev, "cannot configure clock divider: ref=%d"
+ " spi=%d.\n", sc->ref_clock, sc->spi_clock);
+ return (EINVAL);
+ }
+ sc->spi_clk_real_freq = sc->ref_clock >> (baud_div + 1);
+
+ /* Set up configuration register. */
+ sc->cfg_reg_shadow =
+ ZY7_SPI_CONFIG_MAN_CS |
+ ZY7_SPI_CONFIG_CS_MASK |
+ ZY7_SPI_CONFIG_BAUD_RATE_DIV(baud_div) |
+ ZY7_SPI_CONFIG_MODE_SEL;
+ WR4(sc, ZY7_SPI_CONFIG_REG, sc->cfg_reg_shadow);
+
+ /* Set thresholds. */
+ WR4(sc, ZY7_SPI_TX_THRESH_REG, 32);
+ WR4(sc, ZY7_SPI_RX_THRESH_REG, 1);
+
+ /* Clear and disable all interrupts. */
+ WR4(sc, ZY7_SPI_INTR_STAT_REG, ~0);
+ WR4(sc, ZY7_SPI_INTR_DIS_REG, ~0);
+
+ /* Enable SPI. */
+ WR4(sc, ZY7_SPI_EN_REG, ZY7_SPI_ENABLE);
+
+ return (0);
+}
+
+static void
+zy7_spi_add_sysctls(device_t dev)
+{
+ struct zy7_spi_softc *sc = device_get_softc(dev);
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid_list *child;
+
+ ctx = device_get_sysctl_ctx(dev);
+ child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
+
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "spi_clk_real_freq", CTLFLAG_RD,
+ &sc->spi_clk_real_freq, 0, "SPI clock real frequency");
+
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_overflows", CTLFLAG_RD,
+ &sc->rx_overflows, 0, "RX FIFO overflow events");
+
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_underflows", CTLFLAG_RD,
+ &sc->tx_underflows, 0, "TX FIFO underflow events");
+
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "interrupts", CTLFLAG_RD,
+ &sc->interrupts, 0, "interrupt calls");
+
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "stray_ints", CTLFLAG_RD,
+ &sc->stray_ints, 0, "stray interrupts");
+}
+
+static int
+zy7_spi_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Zynq SPI Controller");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int zy7_spi_detach(device_t);
+
+static int
+zy7_spi_attach(device_t dev)
+{
+ struct zy7_spi_softc *sc;
+ int rid, err;
+ phandle_t node;
+ pcell_t cell;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ SPI_SC_LOCK_INIT(sc);
+
+ /* Get ref-clock and spi-clock properties. */
+ node = ofw_bus_get_node(dev);
+ if (OF_getprop(node, "ref-clock", &cell, sizeof(cell)) > 0)
+ sc->ref_clock = fdt32_to_cpu(cell);
+ else {
+ device_printf(dev, "must have ref-clock property\n");
+ return (ENXIO);
+ }
+ if (OF_getprop(node, "spi-clock", &cell, sizeof(cell)) > 0)
+ sc->spi_clock = fdt32_to_cpu(cell);
+ else
+ sc->spi_clock = ZY7_SPI_DEFAULT_SPI_CLOCK;
+
+ /* Get memory resource. */
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "could not allocate memory resources.\n");
+ zy7_spi_detach(dev);
+ return (ENOMEM);
+ }
+
+ /* Allocate IRQ. */
+ rid = 0;
+ sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (sc->irq_res == NULL) {
+ device_printf(dev, "could not allocate IRQ resource.\n");
+ zy7_spi_detach(dev);
+ return (ENOMEM);
+ }
+
+ /* Activate the interrupt. */
+ err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+ NULL, zy7_spi_intr, sc, &sc->intrhandle);
+ if (err) {
+ device_printf(dev, "could not setup IRQ.\n");
+ zy7_spi_detach(dev);
+ return (err);
+ }
+
+ /* Configure the device. */
+ err = zy7_spi_init_hw(sc);
+ if (err) {
+ zy7_spi_detach(dev);
+ return (err);
+ }
+
+ sc->child = device_add_child(dev, "spibus", -1);
+
+ zy7_spi_add_sysctls(dev);
+
+ /* Attach spibus driver as a child later when interrupts work. */
+ config_intrhook_oneshot((ich_func_t)bus_generic_attach, dev);
+
+ return (0);
+}
+
+static int
+zy7_spi_detach(device_t dev)
+{
+ struct zy7_spi_softc *sc = device_get_softc(dev);
+
+ if (device_is_attached(dev))
+ bus_generic_detach(dev);
+
+ /* Delete child bus. */
+ if (sc->child)
+ device_delete_child(dev, sc->child);
+
+ /* Disable hardware. */
+ if (sc->mem_res != NULL) {
+ /* Disable SPI. */
+ WR4(sc, ZY7_SPI_EN_REG, 0);
+
+ /* Clear and disable all interrupts. */
+ WR4(sc, ZY7_SPI_INTR_STAT_REG, ~0);
+ WR4(sc, ZY7_SPI_INTR_DIS_REG, ~0);
+ }
+
+ /* Teardown and release interrupt. */
+ if (sc->irq_res != NULL) {
+ if (sc->intrhandle)
+ bus_teardown_intr(dev, sc->irq_res, sc->intrhandle);
+ bus_release_resource(dev, SYS_RES_IRQ,
+ rman_get_rid(sc->irq_res), sc->irq_res);
+ }
+
+ /* Release memory resource. */
+ if (sc->mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY,
+ rman_get_rid(sc->mem_res), sc->mem_res);
+
+ SPI_SC_LOCK_DESTROY(sc);
+
+ return (0);
+}
+
+static phandle_t
+zy7_spi_get_node(device_t bus, device_t dev)
+{
+
+ return (ofw_bus_get_node(bus));
+}
+
+static int
+zy7_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
+{
+ struct zy7_spi_softc *sc = device_get_softc(dev);
+ uint32_t cs;
+ uint32_t mode;
+ int err = 0;
+
+ KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz,
+ ("TX/RX command sizes should be equal"));
+ KASSERT(cmd->tx_data_sz == cmd->rx_data_sz,
+ ("TX/RX data sizes should be equal"));
+
+ /* Get chip select and mode for this child. */
+ spibus_get_cs(child, &cs);
+ cs &= ~SPIBUS_CS_HIGH;
+ if (cs > 2) {
+ device_printf(dev, "Invalid chip select %d requested by %s",
+ cs, device_get_nameunit(child));
+ return (EINVAL);
+ }
+ spibus_get_mode(child, &mode);
+
+ SPI_SC_LOCK(sc);
+
+ /* Wait for controller available. */
+ while (sc->busy != 0) {
+ err = mtx_sleep(dev, &sc->sc_mtx, 0, "zspi0", 0);
+ if (err) {
+ SPI_SC_UNLOCK(sc);
+ return (err);
+ }
+ }
+
+ /* Start transfer. */
+ sc->busy = 1;
+ sc->cmd = cmd;
+ sc->tx_bytes = sc->cmd->tx_cmd_sz + sc->cmd->tx_data_sz;
+ sc->tx_bytes_sent = 0;
+ sc->rx_bytes = sc->cmd->rx_cmd_sz + sc->cmd->rx_data_sz;
+ sc->rx_bytes_rcvd = 0;
+
+ /* Enable interrupts. zy7_spi_intr() will handle transfer. */
+ WR4(sc, ZY7_SPI_INTR_EN_REG,
+ ZY7_SPI_INTR_TX_FIFO_NOT_FULL |
+ ZY7_SPI_INTR_RX_OVERFLOW);
+
+ /* Handle polarity and phase. */
+ if (mode == SPIBUS_MODE_CPHA || mode == SPIBUS_MODE_CPOL_CPHA)
+ sc->cfg_reg_shadow |= ZY7_SPI_CONFIG_CLK_PH;
+ if (mode == SPIBUS_MODE_CPOL || mode == SPIBUS_MODE_CPOL_CPHA)
+ sc->cfg_reg_shadow |= ZY7_SPI_CONFIG_CLK_POL;
+
+ /* Assert CS. */
+ sc->cfg_reg_shadow &= ~ZY7_SPI_CONFIG_CS_MASK;
+ sc->cfg_reg_shadow |= ZY7_SPI_CONFIG_CS(cs);
+ WR4(sc, ZY7_SPI_CONFIG_REG, sc->cfg_reg_shadow);
+
+ /* Wait for completion. */
+ err = mtx_sleep(dev, &sc->sc_mtx, 0, "zspi1", hz * 2);
+ if (err)
+ zy7_spi_abort_transfer(sc);
+
+ /* Release controller. */
+ sc->busy = 0;
+ wakeup_one(dev);
+
+ SPI_SC_UNLOCK(sc);
+
+ return (err);
+}
+
+static device_method_t zy7_spi_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, zy7_spi_probe),
+ DEVMETHOD(device_attach, zy7_spi_attach),
+ DEVMETHOD(device_detach, zy7_spi_detach),
+
+ /* SPI interface */
+ DEVMETHOD(spibus_transfer, zy7_spi_transfer),
+
+ /* ofw_bus interface */
+ DEVMETHOD(ofw_bus_get_node, zy7_spi_get_node),
+
+ DEVMETHOD_END
+};
+
+static driver_t zy7_spi_driver = {
+ "zy7_spi",
+ zy7_spi_methods,
+ sizeof(struct zy7_spi_softc),
+};
+static devclass_t zy7_spi_devclass;
+
+DRIVER_MODULE(zy7_spi, simplebus, zy7_spi_driver, zy7_spi_devclass, 0, 0);
+DRIVER_MODULE(ofw_spibus, zy7_spi, ofw_spibus_driver, ofw_spibus_devclass, 0, 0);
+SIMPLEBUS_PNP_INFO(compat_data);
+MODULE_DEPEND(zy7_spi, ofw_spibus, 1, 1, 1);